aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/brcm80211/sys
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/brcm80211/sys')
-rw-r--r--drivers/staging/brcm80211/sys/d11ucode_ext.h35
-rw-r--r--drivers/staging/brcm80211/sys/wl_dbg.h82
-rw-r--r--drivers/staging/brcm80211/sys/wl_export.h63
-rw-r--r--drivers/staging/brcm80211/sys/wl_mac80211.c2382
-rw-r--r--drivers/staging/brcm80211/sys/wl_mac80211.h161
-rw-r--r--drivers/staging/brcm80211/sys/wl_ucode.h37
-rw-r--r--drivers/staging/brcm80211/sys/wl_ucode_loader.c90
-rw-r--r--drivers/staging/brcm80211/sys/wlc_alloc.c373
-rw-r--r--drivers/staging/brcm80211/sys/wlc_alloc.h25
-rw-r--r--drivers/staging/brcm80211/sys/wlc_ampdu.c1411
-rw-r--r--drivers/staging/brcm80211/sys/wlc_ampdu.h40
-rw-r--r--drivers/staging/brcm80211/sys/wlc_antsel.c322
-rw-r--r--drivers/staging/brcm80211/sys/wlc_antsel.h28
-rw-r--r--drivers/staging/brcm80211/sys/wlc_bmac.c4206
-rw-r--r--drivers/staging/brcm80211/sys/wlc_bmac.h277
-rw-r--r--drivers/staging/brcm80211/sys/wlc_bsscfg.h152
-rw-r--r--drivers/staging/brcm80211/sys/wlc_cfg.h310
-rw-r--r--drivers/staging/brcm80211/sys/wlc_channel.c1599
-rw-r--r--drivers/staging/brcm80211/sys/wlc_channel.h159
-rw-r--r--drivers/staging/brcm80211/sys/wlc_event.c226
-rw-r--r--drivers/staging/brcm80211/sys/wlc_event.h51
-rw-r--r--drivers/staging/brcm80211/sys/wlc_key.h144
-rw-r--r--drivers/staging/brcm80211/sys/wlc_mac80211.c8675
-rw-r--r--drivers/staging/brcm80211/sys/wlc_mac80211.h1040
-rw-r--r--drivers/staging/brcm80211/sys/wlc_phy_shim.c243
-rw-r--r--drivers/staging/brcm80211/sys/wlc_phy_shim.h112
-rw-r--r--drivers/staging/brcm80211/sys/wlc_pub.h627
-rw-r--r--drivers/staging/brcm80211/sys/wlc_rate.c499
-rw-r--r--drivers/staging/brcm80211/sys/wlc_rate.h170
-rw-r--r--drivers/staging/brcm80211/sys/wlc_rpc.h527
-rw-r--r--drivers/staging/brcm80211/sys/wlc_rpctx.h71
-rw-r--r--drivers/staging/brcm80211/sys/wlc_scb.h84
-rw-r--r--drivers/staging/brcm80211/sys/wlc_stf.c593
-rw-r--r--drivers/staging/brcm80211/sys/wlc_stf.h42
-rw-r--r--drivers/staging/brcm80211/sys/wlc_types.h52
35 files changed, 24908 insertions, 0 deletions
diff --git a/drivers/staging/brcm80211/sys/d11ucode_ext.h b/drivers/staging/brcm80211/sys/d11ucode_ext.h
new file mode 100644
index 000000000000..c0c0d661e00e
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/d11ucode_ext.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+enum {
+ D11UCODE_NAMETAG_START = 0,
+ D11LCN0BSINITVALS24,
+ D11LCN0INITVALS24,
+ D11LCN1BSINITVALS24,
+ D11LCN1INITVALS24,
+ D11LCN2BSINITVALS24,
+ D11LCN2INITVALS24,
+ D11N0ABSINITVALS16,
+ D11N0BSINITVALS16,
+ D11N0INITVALS16,
+ D11UCODE_OVERSIGHT16_MIMO,
+ D11UCODE_OVERSIGHT16_MIMOSZ,
+ D11UCODE_OVERSIGHT24_LCN,
+ D11UCODE_OVERSIGHT24_LCNSZ,
+ D11UCODE_OVERSIGHT_BOMMAJOR,
+ D11UCODE_OVERSIGHT_BOMMINOR
+};
+#define UCODE_LOADER_API_VER 0
diff --git a/drivers/staging/brcm80211/sys/wl_dbg.h b/drivers/staging/brcm80211/sys/wl_dbg.h
new file mode 100644
index 000000000000..e63b27ebad5e
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wl_dbg.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wl_dbg_h_
+#define _wl_dbg_h_
+
+/* wl_msg_level is a bit vector with defs in wlioctl.h */
+extern u32 wl_msg_level;
+
+#define WL_PRINT(args) printf args
+#define WL_NONE(args)
+
+#ifdef BCMDBG
+
+#define WL_ERROR(args) do {if ((wl_msg_level & WL_ERROR_VAL)) WL_PRINT(args); } while (0)
+#define WL_TRACE(args) do {if (wl_msg_level & WL_TRACE_VAL) WL_PRINT(args); } while (0)
+#define WL_AMPDU(args) do {if (wl_msg_level & WL_AMPDU_VAL) WL_PRINT(args); } while (0)
+#define WL_FFPLD(args) do {if (wl_msg_level & WL_FFPLD_VAL) WL_PRINT(args); } while (0)
+
+#define WL_ERROR_ON() (wl_msg_level & WL_ERROR_VAL)
+
+/* Extra message control for AMPDU debugging */
+#define WL_AMPDU_UPDN_VAL 0x00000001 /* Config up/down related */
+#define WL_AMPDU_ERR_VAL 0x00000002 /* Calls to beaocn update */
+#define WL_AMPDU_TX_VAL 0x00000004 /* Transmit data path */
+#define WL_AMPDU_RX_VAL 0x00000008 /* Receive data path */
+#define WL_AMPDU_CTL_VAL 0x00000010 /* TSF-related items */
+#define WL_AMPDU_HW_VAL 0x00000020 /* AMPDU_HW */
+#define WL_AMPDU_HWTXS_VAL 0x00000040 /* AMPDU_HWTXS */
+#define WL_AMPDU_HWDBG_VAL 0x00000080 /* AMPDU_DBG */
+
+extern u32 wl_ampdu_dbg;
+
+#define WL_AMPDU_UPDN(args) do {if (wl_ampdu_dbg & WL_AMPDU_UPDN_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_RX(args) do {if (wl_ampdu_dbg & WL_AMPDU_RX_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_ERR(args) do {if (wl_ampdu_dbg & WL_AMPDU_ERR_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_TX(args) do {if (wl_ampdu_dbg & WL_AMPDU_TX_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_CTL(args) do {if (wl_ampdu_dbg & WL_AMPDU_CTL_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_HW(args) do {if (wl_ampdu_dbg & WL_AMPDU_HW_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_HWTXS(args) do {if (wl_ampdu_dbg & WL_AMPDU_HWTXS_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_HWDBG(args) do {if (wl_ampdu_dbg & WL_AMPDU_HWDBG_VAL) {WL_AMPDU(args); } } while (0)
+#define WL_AMPDU_ERR_ON() (wl_ampdu_dbg & WL_AMPDU_ERR_VAL)
+#define WL_AMPDU_HW_ON() (wl_ampdu_dbg & WL_AMPDU_HW_VAL)
+#define WL_AMPDU_HWTXS_ON() (wl_ampdu_dbg & WL_AMPDU_HWTXS_VAL)
+
+#else /* BCMDBG */
+
+#define WL_ERROR(args)
+#define WL_TRACE(args)
+#define WL_AMPDU(args)
+#define WL_FFPLD(args)
+
+#define WL_ERROR_ON() 0
+
+#define WL_AMPDU_UPDN(args)
+#define WL_AMPDU_RX(args)
+#define WL_AMPDU_ERR(args)
+#define WL_AMPDU_TX(args)
+#define WL_AMPDU_CTL(args)
+#define WL_AMPDU_HW(args)
+#define WL_AMPDU_HWTXS(args)
+#define WL_AMPDU_HWDBG(args)
+#define WL_AMPDU_ERR_ON() 0
+#define WL_AMPDU_HW_ON() 0
+#define WL_AMPDU_HWTXS_ON() 0
+
+#endif /* BCMDBG */
+
+#endif /* _wl_dbg_h_ */
diff --git a/drivers/staging/brcm80211/sys/wl_export.h b/drivers/staging/brcm80211/sys/wl_export.h
new file mode 100644
index 000000000000..08442f8e84e0
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wl_export.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wl_export_h_
+#define _wl_export_h_
+
+/* misc callbacks */
+struct wl_info;
+struct wl_if;
+struct wlc_if;
+extern void wl_init(struct wl_info *wl);
+extern uint wl_reset(struct wl_info *wl);
+extern void wl_intrson(struct wl_info *wl);
+extern u32 wl_intrsoff(struct wl_info *wl);
+extern void wl_intrsrestore(struct wl_info *wl, u32 macintmask);
+extern void wl_event(struct wl_info *wl, char *ifname, wlc_event_t *e);
+extern void wl_event_sendup(struct wl_info *wl, const wlc_event_t *e,
+ u8 *data, u32 len);
+extern int wl_up(struct wl_info *wl);
+extern void wl_down(struct wl_info *wl);
+extern void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
+ int prio);
+extern bool wl_alloc_dma_resources(struct wl_info *wl, uint dmaddrwidth);
+
+/* timer functions */
+struct wl_timer;
+extern struct wl_timer *wl_init_timer(struct wl_info *wl,
+ void (*fn) (void *arg), void *arg,
+ const char *name);
+extern void wl_free_timer(struct wl_info *wl, struct wl_timer *timer);
+extern void wl_add_timer(struct wl_info *wl, struct wl_timer *timer, uint ms,
+ int periodic);
+extern bool wl_del_timer(struct wl_info *wl, struct wl_timer *timer);
+
+extern uint wl_buf_to_pktcopy(osl_t *osh, void *p, unsigned char *buf, int len,
+ uint offset);
+extern void *wl_get_pktbuffer(osl_t *osh, int len);
+extern int wl_set_pktlen(osl_t *osh, void *p, int len);
+
+#define wl_sort_bsslist(a, b) false
+
+extern int wl_tkip_miccheck(struct wl_info *wl, void *p, int hdr_len,
+ bool group_key, int id);
+extern int wl_tkip_micadd(struct wl_info *wl, void *p, int hdr_len);
+extern int wl_tkip_encrypt(struct wl_info *wl, void *p, int hdr_len);
+extern int wl_tkip_decrypt(struct wl_info *wl, void *p, int hdr_len,
+ bool group_key);
+extern void wl_tkip_printstats(struct wl_info *wl, bool group_key);
+extern int wl_tkip_keyset(struct wl_info *wl, wsec_key_t *key);
+#endif /* _wl_export_h_ */
diff --git a/drivers/staging/brcm80211/sys/wl_mac80211.c b/drivers/staging/brcm80211/sys/wl_mac80211.c
new file mode 100644
index 000000000000..ad635ee7758e
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wl_mac80211.c
@@ -0,0 +1,2382 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define __UNDEF_NO_VERSION__
+
+#include <linux/kernel.h>
+#include <linux/etherdevice.h>
+#include <linux/string.h>
+#include <linux/pci_ids.h>
+#include <bcmdefs.h>
+#include <linuxver.h>
+#include <osl.h>
+#define WLC_MAXBSSCFG 1 /* single BSS configs */
+
+#include <wlc_cfg.h>
+#include <net/mac80211.h>
+#include <epivers.h>
+#ifndef WLC_HIGH_ONLY
+#include <phy_version.h>
+#endif
+#include <bcmutils.h>
+#include <pcicfg.h>
+#include <wlioctl.h>
+#include <wlc_key.h>
+#include <wlc_channel.h>
+#include <wlc_pub.h>
+#include <wlc_scb.h>
+#include <wl_dbg.h>
+#ifdef BCMSDIO
+#include <bcmsdh.h>
+#endif
+#include <wl_export.h>
+#ifdef WLC_HIGH_ONLY
+#include "dbus.h"
+#include "bcm_rpc_tp.h"
+#include "bcm_rpc.h"
+#include "bcm_xdr.h"
+#include "wlc_rpc.h"
+#endif
+
+#include <wl_mac80211.h>
+#include <linux/firmware.h>
+#ifndef WLC_HIGH_ONLY
+#include <wl_ucode.h>
+#include <d11ucode_ext.h>
+#endif
+
+#ifdef BCMSDIO
+extern struct device *sdiommc_dev;
+#endif
+
+extern void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg,
+ bool suspend);
+bool wlc_sendpkt_mac80211(wlc_info_t *wlc, void *sdu, struct ieee80211_hw *hw);
+void wlc_mac_bcn_promisc_change(wlc_info_t *wlc, bool promisc);
+void wlc_set_addrmatch(wlc_info_t *wlc, int match_reg_offset,
+ const struct ether_addr *addr);
+
+static void wl_timer(unsigned long data);
+static void _wl_timer(wl_timer_t *t);
+
+#ifdef WLC_HIGH_ONLY
+#define RPCQ_LOCK(_wl, _flags) spin_lock_irqsave(&(_wl)->rpcq_lock, (_flags))
+#define RPCQ_UNLOCK(_wl, _flags) spin_unlock_irqrestore(&(_wl)->rpcq_lock, (_flags))
+#define TXQ_LOCK(_wl, _flags) spin_lock_irqsave(&(_wl)->txq_lock, (_flags))
+#define TXQ_UNLOCK(_wl, _flags) spin_unlock_irqrestore(&(_wl)->txq_lock, (_flags))
+static void wl_rpc_down(void *wlh);
+static void wl_rpcq_free(wl_info_t *wl);
+static void wl_rpcq_dispatch(struct wl_task *task);
+static void wl_rpc_dispatch_schedule(void *ctx, struct rpc_buf *buf);
+static void wl_start_txqwork(struct wl_task *task);
+static void wl_txq_free(wl_info_t *wl);
+static void wl_timer_task(wl_task_t *task);
+static int wl_schedule_task(wl_info_t *wl, void (*fn) (struct wl_task *),
+ void *context);
+#endif /* WLC_HIGH_ONLY */
+
+static int ieee_hw_init(struct ieee80211_hw *hw);
+static int ieee_hw_rate_init(struct ieee80211_hw *hw);
+
+static int wl_linux_watchdog(void *ctx);
+
+/* Flags we support */
+#define MAC_FILTERS (FIF_PROMISC_IN_BSS | \
+ FIF_ALLMULTI | \
+ FIF_FCSFAIL | \
+ FIF_PLCPFAIL | \
+ FIF_CONTROL | \
+ FIF_OTHER_BSS | \
+ FIF_BCN_PRBRESP_PROMISC)
+
+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];
+};
+
+#ifndef WLC_HIGH_ONLY
+#define WL_DEV_IF(dev) ((wl_if_t *)netdev_priv(dev))
+#define WL_INFO(dev) ((wl_info_t *)(WL_DEV_IF(dev)->wl)) /* points to wl */
+static int wl_request_fw(wl_info_t *wl, struct pci_dev *pdev);
+static void wl_release_fw(wl_info_t *wl);
+#endif
+
+/* local prototypes */
+static int wl_start(struct sk_buff *skb, wl_info_t *wl);
+static int wl_start_int(wl_info_t *wl, struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+static void wl_dpc(unsigned long data);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
+MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#ifndef BCMSDIO
+/* recognized PCI IDs */
+static struct pci_device_id wl_id_table[] = {
+ {PCI_VENDOR_ID_BROADCOM, 0x4357, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 43225 2G */
+ {PCI_VENDOR_ID_BROADCOM, 0x4353, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 43224 DUAL */
+ {PCI_VENDOR_ID_BROADCOM, 0x4727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, /* 4313 DUAL */
+ {0}
+};
+
+MODULE_DEVICE_TABLE(pci, wl_id_table);
+static void wl_remove(struct pci_dev *pdev);
+#endif /* !BCMSDIO */
+
+#ifdef BCMSDIO
+static uint sd_drivestrength = 6;
+module_param(sd_drivestrength, uint, 0);
+#endif
+
+#ifdef BCMDBG
+static int msglevel = 0xdeadbeef;
+module_param(msglevel, int, 0);
+#ifndef WLC_HIGH_ONLY
+static int phymsglevel = 0xdeadbeef;
+module_param(phymsglevel, int, 0);
+#endif /* WLC_HIGH_ONLY */
+#endif /* BCMDBG */
+
+static int oneonly;
+module_param(oneonly, int, 0);
+
+static int piomode;
+module_param(piomode, int, 0);
+
+static int instance_base; /* Starting instance number */
+module_param(instance_base, int, 0);
+
+#if defined(BCMDBG)
+static char *macaddr;
+module_param(macaddr, charp, S_IRUGO);
+#endif
+
+static int nompc = 1;
+module_param(nompc, int, 0);
+
+static char name[IFNAMSIZ] = "eth%d";
+module_param_string(name, name, IFNAMSIZ, 0);
+
+#ifndef SRCBASE
+#define SRCBASE "."
+#endif
+
+#define WL_MAGIC 0xdeadbeef
+
+#define HW_TO_WL(hw) (hw->priv)
+#define WL_TO_HW(wl) (wl->pub->ieee_hw)
+#ifdef WLC_HIGH_ONLY
+static int wl_ops_tx_nl(struct ieee80211_hw *hw, struct sk_buff *skb);
+#else
+static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+#endif
+static int wl_ops_start(struct ieee80211_hw *hw);
+static void wl_ops_stop(struct ieee80211_hw *hw);
+static int wl_ops_add_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+static void wl_ops_remove_interface(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif);
+static int wl_ops_config(struct ieee80211_hw *hw, u32 changed);
+static void wl_ops_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed);
+static void wl_ops_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast);
+static int wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+ bool set);
+static void wl_ops_sw_scan_start(struct ieee80211_hw *hw);
+static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw);
+static void wl_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf);
+static int wl_ops_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats);
+static int wl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+static void wl_ops_sta_notify(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd,
+ struct ieee80211_sta *sta);
+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,
+ struct ieee80211_sta *sta);
+static int wl_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);
+
+#ifdef WLC_HIGH_ONLY
+static int wl_ops_tx_nl(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ int status;
+ wl_info_t *wl = hw->priv;
+ if (!wl->pub->up) {
+ WL_ERROR(("ops->tx called while down\n"));
+ status = -ENETDOWN;
+ goto done;
+ }
+ status = wl_start(skb, wl);
+ done:
+ return status;
+}
+#else
+static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ int status;
+ wl_info_t *wl = hw->priv;
+ WL_LOCK(wl);
+ if (!wl->pub->up) {
+ WL_ERROR(("ops->tx called while down\n"));
+ status = -ENETDOWN;
+ goto done;
+ }
+ status = wl_start(skb, wl);
+ done:
+ WL_UNLOCK(wl);
+ return status;
+}
+#endif /* WLC_HIGH_ONLY */
+
+static int wl_ops_start(struct ieee80211_hw *hw)
+{
+ wl_info_t *wl = hw->priv;
+ /* 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_UNLOCK(wl);
+
+ return 0;
+}
+
+static void wl_ops_stop(struct ieee80211_hw *hw)
+{
+ wl_info_t *wl = hw->priv;
+ ASSERT(wl);
+ WL_LOCK(wl);
+ wl_down(wl);
+ ieee80211_stop_queues(hw);
+ WL_UNLOCK(wl);
+
+ return;
+}
+
+static int
+wl_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ wl_info_t *wl;
+ int err;
+
+ /* Just STA for now */
+ if (vif->type != NL80211_IFTYPE_AP &&
+ vif->type != NL80211_IFTYPE_MESH_POINT &&
+ vif->type != NL80211_IFTYPE_STATION &&
+ vif->type != NL80211_IFTYPE_WDS &&
+ vif->type != NL80211_IFTYPE_ADHOC) {
+ WL_ERROR(("%s: Attempt to add type %d, only STA for now\n",
+ __func__, vif->type));
+ return -EOPNOTSUPP;
+ }
+
+ wl = HW_TO_WL(hw);
+ WL_LOCK(wl);
+ err = wl_up(wl);
+ WL_UNLOCK(wl);
+
+ 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;
+}
+
+static int
+ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
+ enum nl80211_channel_type type)
+{
+ wl_info_t *wl = HW_TO_WL(hw);
+ int err = 0;
+
+ 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__));
+ break;
+ }
+
+ if (err)
+ return -EIO;
+ return err;
+}
+
+static int wl_ops_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct ieee80211_conf *conf = &hw->conf;
+ wl_info_t *wl = HW_TO_WL(hw);
+ int err = 0;
+ int new_int;
+
+ 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",
+ __func__));
+ err = -EIO;
+ goto config_out;
+ }
+ wlc_iovar_getint(wl->wlc, "bcn_li_bcn", &new_int);
+ ASSERT(new_int == conf->listen_interval);
+ }
+ if (changed & IEEE80211_CONF_CHANGE_MONITOR)
+ WL_NONE(("Need to set monitor mode\n"));
+ if (changed & IEEE80211_CONF_CHANGE_PS)
+ WL_NONE(("Need to set Power-save mode\n"));
+
+ 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 = -EIO;
+ goto config_out;
+ }
+ wlc_iovar_getint(wl->wlc, "qtxpower", &new_int);
+ if (new_int != (conf->power_level * 4))
+ WL_ERROR(("%s: Power level req != actual, %d %d\n",
+ __func__, conf->power_level * 4, new_int));
+ }
+ if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+ 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) {
+ WL_ERROR(("%s: Error setting srl\n", __func__));
+ err = -EIO;
+ goto config_out;
+ }
+ if (wlc_set(wl->wlc, WLC_SET_LRL, conf->long_frame_max_tx_count)
+ < 0) {
+ WL_ERROR(("%s: Error setting lrl\n", __func__));
+ err = -EIO;
+ goto config_out;
+ }
+ }
+
+ config_out:
+ return err;
+}
+
+static void
+wl_ops_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info, u32 changed)
+{
+ wl_info_t *wl = HW_TO_WL(hw);
+ int val;
+
+#ifdef WLC_HIGH_ONLY
+ WL_LOCK(wl);
+#endif
+
+ 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.
+ */
+ }
+ 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 */
+ }
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+ WL_NONE(("Short preamble:\t%s Implement me\n",
+ info->use_short_preamble ? "True" : "False"));
+ /* preamble changed */
+ }
+ if (changed & BSS_CHANGED_ERP_SLOT) {
+ WL_NONE(("Changing short slot:\t%s\n",
+ info->use_short_slot ? "True" : "False"));
+ if (info->use_short_slot)
+ val = 1;
+ else
+ val = 0;
+ wlc_set(wl->wlc, WLC_SET_SHORTSLOT_OVERRIDE, val);
+ /* slot timing changed */
+ }
+
+ if (changed & BSS_CHANGED_HT) {
+ WL_NONE(("%s: HT mode - Implement me\n", __func__));
+ /* 802.11n parameters changed */
+ }
+ 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 */
+ }
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ WL_NONE(("Beacon Interval:\t%d Implement me\n",
+ info->beacon_int));
+ /* Beacon interval changed */
+ }
+ 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 */
+ wlc_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
+ (struct ether_addr *)info->bssid);
+ }
+ if (changed & BSS_CHANGED_BEACON) {
+ WL_ERROR(("BSS_CHANGED_BEACON\n"));
+ /* Beacon data changed, retrieve new beacon (beaconing modes) */
+ }
+ 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) */
+ }
+#ifdef WLC_HIGH_ONLY
+ WL_UNLOCK(wl);
+#endif
+ return;
+}
+
+static void
+wl_ops_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast)
+{
+#ifndef WLC_HIGH_ONLY
+ wl_info_t *wl = hw->priv;
+#endif
+
+ changed_flags &= MAC_FILTERS;
+ *total_flags &= MAC_FILTERS;
+ if (changed_flags & FIF_PROMISC_IN_BSS)
+ WL_ERROR(("FIF_PROMISC_IN_BSS\n"));
+ if (changed_flags & FIF_ALLMULTI)
+ WL_ERROR(("FIF_ALLMULTI\n"));
+ if (changed_flags & FIF_FCSFAIL)
+ WL_ERROR(("FIF_FCSFAIL\n"));
+ if (changed_flags & FIF_PLCPFAIL)
+ WL_ERROR(("FIF_PLCPFAIL\n"));
+ if (changed_flags & FIF_CONTROL)
+ WL_ERROR(("FIF_CONTROL\n"));
+ if (changed_flags & FIF_OTHER_BSS)
+ WL_ERROR(("FIF_OTHER_BSS\n"));
+ if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
+ WL_NONE(("FIF_BCN_PRBRESP_PROMISC\n"));
+#ifndef WLC_HIGH_ONLY
+ WL_LOCK(wl);
+ if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
+ wl->pub->mac80211_state |= MAC80211_PROMISC_BCNS;
+ wlc_mac_bcn_promisc_change(wl->wlc, 1);
+ } else {
+ wlc_mac_bcn_promisc_change(wl->wlc, 0);
+ wl->pub->mac80211_state &= ~MAC80211_PROMISC_BCNS;
+ }
+ WL_UNLOCK(wl);
+#endif
+ }
+ return;
+}
+
+static int
+wl_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
+{
+ WL_ERROR(("%s: Enter\n", __func__));
+ return 0;
+}
+
+static void wl_ops_sw_scan_start(struct ieee80211_hw *hw)
+{
+ WL_NONE(("Scan Start\n"));
+ return;
+}
+
+static void wl_ops_sw_scan_complete(struct ieee80211_hw *hw)
+{
+ WL_NONE(("Scan Complete\n"));
+ return;
+}
+
+static void wl_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+{
+ WL_ERROR(("%s: Enter\n", __func__));
+ return;
+}
+
+static int
+wl_ops_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ WL_ERROR(("%s: Enter\n", __func__));
+ return 0;
+}
+
+static int wl_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ WL_ERROR(("%s: Enter\n", __func__));
+ return 0;
+}
+
+static void
+wl_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+{
+ WL_NONE(("%s: Enter\n", __func__));
+ switch (cmd) {
+ default:
+ WL_ERROR(("%s: Uknown cmd = %d\n", __func__, cmd));
+ break;
+ }
+ return;
+}
+
+static int
+wl_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ const struct ieee80211_tx_queue_params *params)
+{
+ wl_info_t *wl = hw->priv;
+
+ WL_NONE(("%s: Enter (WME config)\n", __func__));
+ WL_NONE(("queue %d, txop %d, cwmin %d, cwmax %d, aifs %d\n", queue,
+ params->txop, params->cw_min, params->cw_max, params->aifs));
+
+ WL_LOCK(wl);
+ wlc_wme_setparams(wl->wlc, queue, (void *)params, true);
+ WL_UNLOCK(wl);
+
+ return 0;
+}
+
+static u64 wl_ops_get_tsf(struct ieee80211_hw *hw)
+{
+ WL_ERROR(("%s: Enter\n", __func__));
+ return 0;
+}
+
+static int
+wl_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct scb *scb;
+
+ int i;
+ wl_info_t *wl = hw->priv;
+
+ /* Init the scb */
+ scb = (struct scb *)sta->drv_priv;
+ bzero(scb, sizeof(struct scb));
+ for (i = 0; i < NUMPRIO; i++)
+ scb->seqctl[i] = 0xFFFF;
+ scb->seqctl_nonqos = 0xFFFF;
+ scb->magic = SCB_MAGIC;
+
+ wl->pub->global_scb = scb;
+ wl->pub->global_ampdu = &(scb->scb_ampdu);
+ wl->pub->global_ampdu->scb = scb;
+#ifdef WLC_HIGH_ONLY
+ wl->pub->global_ampdu->max_pdu = AMPDU_NUM_MPDU;
+#else
+ wl->pub->global_ampdu->max_pdu = 16;
+#endif
+ pktq_init(&scb->scb_ampdu.txq, AMPDU_MAX_SCB_TID,
+ AMPDU_MAX_SCB_TID * PKTQ_LEN_DEFAULT);
+
+ sta->ht_cap.ht_supported = true;
+#ifdef WLC_HIGH_ONLY
+ sta->ht_cap.ampdu_factor = AMPDU_RX_FACTOR_16K;
+#else
+ sta->ht_cap.ampdu_factor = AMPDU_RX_FACTOR_64K;
+#endif
+ sta->ht_cap.ampdu_density = AMPDU_DEF_MPDU_DENSITY;
+ sta->ht_cap.cap = IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+
+ /* minstrel_ht initiates addBA on our behalf by calling ieee80211_start_tx_ba_session() */
+ return 0;
+}
+
+static int
+wl_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)
+{
+#if defined(BCMDBG)
+ struct scb *scb = (struct scb *)sta->drv_priv;
+#endif
+ wl_info_t *wl = hw->priv;
+
+ ASSERT(scb->magic == SCB_MAGIC);
+ switch (action) {
+ case IEEE80211_AMPDU_RX_START:
+ WL_NONE(("%s: action = IEEE80211_AMPDU_RX_START\n", __func__));
+ break;
+ case IEEE80211_AMPDU_RX_STOP:
+ WL_NONE(("%s: action = IEEE80211_AMPDU_RX_STOP\n", __func__));
+ break;
+ case IEEE80211_AMPDU_TX_START:
+ if (!wlc_aggregatable(wl->wlc, tid)) {
+ /* WL_ERROR(("START: tid %d is not agg' able, return FAILURE to stack\n", tid)); */
+ return -1;
+ }
+ /* XXX: Use the starting sequence number provided ... */
+ *ssn = 0;
+ ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+
+ case IEEE80211_AMPDU_TX_STOP:
+ ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+ break;
+ case IEEE80211_AMPDU_TX_OPERATIONAL:
+ /* Not sure what to do here */
+ /* Power save wakeup */
+ WL_NONE(("%s: action = IEEE80211_AMPDU_TX_OPERATIONAL\n",
+ __func__));
+ break;
+ default:
+ WL_ERROR(("%s: Invalid command, ignoring\n", __func__));
+ }
+
+ return 0;
+}
+
+static const struct ieee80211_ops wl_ops = {
+#ifdef WLC_HIGH_ONLY
+ .tx = wl_ops_tx_nl,
+#else
+ .tx = wl_ops_tx,
+#endif
+ .start = wl_ops_start,
+ .stop = wl_ops_stop,
+ .add_interface = wl_ops_add_interface,
+ .remove_interface = wl_ops_remove_interface,
+ .config = wl_ops_config,
+ .bss_info_changed = wl_ops_bss_info_changed,
+ .configure_filter = wl_ops_configure_filter,
+ .set_tim = wl_ops_set_tim,
+ .sw_scan_start = wl_ops_sw_scan_start,
+ .sw_scan_complete = wl_ops_sw_scan_complete,
+ .set_tsf = wl_ops_set_tsf,
+ .get_stats = wl_ops_get_stats,
+ .set_rts_threshold = wl_ops_set_rts_threshold,
+ .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,
+};
+
+static int wl_set_hint(wl_info_t *wl, char *abbrev)
+{
+ WL_ERROR(("%s: Sending country code %c%c to MAC80211\n", __func__,
+ abbrev[0], abbrev[1]));
+ return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
+}
+
+/**
+ * attach to the WL device.
+ *
+ * Attach to the WL device identified by vendor and device parameters.
+ * regs is a host accessible memory address pointing to WL device registers.
+ *
+ * wl_attach is not defined as static because in the case where no bus
+ * 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.
+ */
+static wl_info_t *wl_attach(u16 vendor, u16 device, unsigned long regs,
+ uint bustype, void *btparam, uint irq)
+{
+ wl_info_t *wl;
+ osl_t *osh;
+ int unit, err;
+
+ unsigned long base_addr;
+ struct ieee80211_hw *hw;
+ u8 perm[ETH_ALEN];
+
+ unit = wl_found + instance_base;
+ err = 0;
+
+ if (unit < 0) {
+ WL_ERROR(("wl%d: unit number overflow, exiting\n", unit));
+ return NULL;
+ }
+
+ if (oneonly && (unit != instance_base)) {
+ WL_ERROR(("wl%d: wl_attach: oneonly is set, exiting\n", unit));
+ return NULL;
+ }
+
+ /* Requires pkttag feature */
+ osh = osl_attach(btparam, bustype, true);
+ ASSERT(osh);
+
+#ifdef WLC_HIGH_ONLY
+ hw = ieee80211_alloc_hw(sizeof(wl_info_t), &wl_ops);
+ if (!hw) {
+ WL_ERROR(("%s: ieee80211_alloc_hw failed\n", __func__));
+ ASSERT(0);
+ }
+
+ bzero(hw->priv, sizeof(*wl));
+ wl = hw->priv;
+#else
+ /* allocate private info */
+ hw = pci_get_drvdata(btparam); /* btparam == pdev */
+ wl = hw->priv;
+#endif
+ ASSERT(wl);
+
+ wl->magic = WL_MAGIC;
+ wl->osh = osh;
+ atomic_set(&wl->callbacks, 0);
+
+ /* setup the bottom half handler */
+ tasklet_init(&wl->tasklet, wl_dpc, (unsigned long) wl);
+
+#ifdef WLC_HIGH_ONLY
+ wl->rpc_th = bcm_rpc_tp_attach(osh, NULL);
+ if (wl->rpc_th == NULL) {
+ WL_ERROR(("wl%d: %s: bcm_rpc_tp_attach failed!\n", unit,
+ __func__));
+ goto fail;
+ }
+
+ wl->rpc = bcm_rpc_attach(NULL, osh, wl->rpc_th);
+ if (wl->rpc == NULL) {
+ WL_ERROR(("wl%d: %s: bcm_rpc_attach failed!\n", unit,
+ __func__));
+ goto fail;
+ }
+
+ /* init tx work queue for wl_start/send pkt; no need to destroy workitem */
+ INIT_WORK(&wl->txq_task.work, (work_func_t) wl_start_txqwork);
+ wl->txq_task.context = wl;
+#endif /* WLC_HIGH_ONLY */
+
+#ifdef BCMSDIO
+ SET_IEEE80211_DEV(hw, sdiommc_dev);
+#endif
+
+ base_addr = regs;
+
+ if (bustype == PCI_BUS) {
+ /* piomode can be overwritten by command argument */
+ wl->piomode = piomode;
+ WL_TRACE(("PCI/%s\n", wl->piomode ? "PIO" : "DMA"));
+ } else if (bustype == RPC_BUS) {
+ /* Do nothing */
+ } else {
+ bustype = PCI_BUS;
+ WL_TRACE(("force to PCI\n"));
+ }
+ wl->bcm_bustype = bustype;
+
+#ifdef WLC_HIGH_ONLY
+ if (wl->bcm_bustype == RPC_BUS) {
+ wl->regsva = (void *)0;
+ btparam = wl->rpc;
+ } else
+#endif
+ wl->regsva = ioremap_nocache(base_addr, PCI_BAR0_WINSZ);
+ if (wl->regsva == NULL) {
+ WL_ERROR(("wl%d: ioremap() failed\n", unit));
+ goto fail;
+ }
+#ifdef WLC_HIGH_ONLY
+ spin_lock_init(&wl->rpcq_lock);
+ spin_lock_init(&wl->txq_lock);
+
+ init_MUTEX(&wl->sem);
+#else
+ spin_lock_init(&wl->lock);
+ spin_lock_init(&wl->isr_lock);
+#endif
+
+#ifndef WLC_HIGH_ONLY
+ /* 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");
+ wl_release_fw(wl);
+ wl_remove((struct pci_dev *)btparam);
+ goto fail1;
+ }
+#endif
+
+ /* common load-time initialization */
+ wl->wlc = wlc_attach((void *)wl, vendor, device, unit, wl->piomode, osh,
+ wl->regsva, wl->bcm_bustype, btparam, &err);
+#ifndef WLC_HIGH_ONLY
+ wl_release_fw(wl);
+#endif
+ if (!wl->wlc) {
+ printf("%s: %s wlc_attach() failed with code %d\n",
+ KBUILD_MODNAME, EPI_VERSION_STR, err);
+ goto fail;
+ }
+ wl->pub = wlc_pub(wl->wlc);
+
+ wl->pub->ieee_hw = hw;
+ ASSERT(wl->pub->ieee_hw);
+ ASSERT(wl->pub->ieee_hw->priv == wl);
+
+#ifdef WLC_HIGH_ONLY
+ REGOPSSET(osh, (osl_rreg_fn_t) wlc_reg_read,
+ (osl_wreg_fn_t) wlc_reg_write, wl->wlc);
+ wl->rpc_dispatch_ctx.rpc = wl->rpc;
+ wl->rpc_dispatch_ctx.wlc = wl->wlc;
+ bcm_rpc_rxcb_init(wl->rpc, wl, wl_rpc_dispatch_schedule, wl,
+ wl_rpc_down, NULL, NULL);
+#endif /* WLC_HIGH_ONLY */
+
+ if (nompc) {
+ if (wlc_iovar_setint(wl->wlc, "mpc", 0)) {
+ WL_ERROR(("wl%d: Error setting MPC variable to 0\n",
+ unit));
+ }
+ }
+#ifdef BCMSDIO
+ /* Set SDIO drive strength */
+ wlc_iovar_setint(wl->wlc, "sd_drivestrength", sd_drivestrength);
+#endif
+
+#ifdef WLC_LOW
+ /* register our interrupt handler */
+ if (request_irq(irq, wl_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) {
+ WL_ERROR(("wl%d: request_irq() failed\n", unit));
+ goto fail;
+ }
+ wl->irq = irq;
+#endif /* WLC_LOW */
+
+ /* register module */
+ wlc_module_register(wl->pub, NULL, "linux", wl, NULL, wl_linux_watchdog,
+ NULL);
+
+ if (ieee_hw_init(hw)) {
+ WL_ERROR(("wl%d: %s: ieee_hw_init failed!\n", unit, __func__));
+ goto fail;
+ }
+
+ bcopy(&wl->pub->cur_etheraddr, perm, ETHER_ADDR_LEN);
+ ASSERT(is_valid_ether_addr(perm));
+ SET_IEEE80211_PERM_ADDR(hw, perm);
+
+ err = ieee80211_register_hw(hw);
+ if (err) {
+ WL_ERROR(("%s: ieee80211_register_hw failed, status %d\n",
+ __func__, err));
+ }
+
+ if (wl->pub->srom_ccode[0])
+ err = wl_set_hint(wl, wl->pub->srom_ccode);
+ else
+ err = wl_set_hint(wl, "US");
+ if (err) {
+ WL_ERROR(("%s: regulatory_hint failed, status %d\n", __func__,
+ err));
+ }
+#ifndef WLC_HIGH_ONLY
+ WL_ERROR(("wl%d: Broadcom BCM43xx 802.11 MAC80211 Driver "
+ EPI_VERSION_STR " (" PHY_VERSION_STR ")", unit));
+#else
+ WL_ERROR(("wl%d: Broadcom BCM43xx 802.11 MAC80211 Driver "
+ EPI_VERSION_STR, unit));
+#endif
+
+#ifdef BCMDBG
+ printf(" (Compiled in " SRCBASE " at " __TIME__ " on " __DATE__ ")");
+#endif /* BCMDBG */
+ printf("\n");
+
+ wl_found++;
+ return wl;
+
+ fail:
+ wl_free(wl);
+fail1:
+ return NULL;
+}
+
+#ifdef WLC_HIGH_ONLY
+static void *wl_dbus_probe_cb(void *arg, const char *desc, u32 bustype,
+ u32 hdrlen)
+{
+ wl_info_t *wl;
+ WL_ERROR(("%s:\n", __func__));
+
+ wl = wl_attach(BCM_DNGL_VID, BCM_DNGL_BDC_PID, (unsigned long) NULL, RPC_BUS,
+ NULL, 0);
+ if (!wl) {
+ WL_ERROR(("%s: wl_attach failed\n", __func__));
+ }
+
+ /* This is later passed to wl_dbus_disconnect_cb */
+ return wl;
+}
+
+static void wl_dbus_disconnect_cb(void *arg)
+{
+ wl_info_t *wl = arg;
+
+ WL_ERROR(("%s:\n", __func__));
+
+ if (wl) {
+#ifdef WLC_HIGH_ONLY
+ if (wl->pub->ieee_hw) {
+ ieee80211_unregister_hw(wl->pub->ieee_hw);
+ WL_ERROR(("%s: Back from down\n", __func__));
+ }
+ wlc_device_removed(wl->wlc);
+ wlc_bmac_dngl_reboot(wl->rpc);
+ bcm_rpc_down(wl->rpc);
+#endif
+ WL_LOCK(wl);
+ wl_down(wl);
+ WL_UNLOCK(wl);
+#ifdef WLC_HIGH_ONLY
+ if (wl->pub->ieee_hw) {
+ ieee80211_free_hw(wl->pub->ieee_hw);
+ WL_ERROR(("%s: Back from ieee80211_free_hw\n",
+ __func__));
+ wl->pub->ieee_hw = NULL;
+ }
+#endif
+ wl_free(wl);
+ }
+}
+#endif /* WLC_HIGH_ONLY */
+
+
+#define CHAN2GHZ(channel, freqency, chflags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (freqency), \
+ .hw_value = (channel), \
+ .flags = chflags, \
+ .max_antenna_gain = 0, \
+ .max_power = 19, \
+}
+
+static struct ieee80211_channel wl_2ghz_chantable[] = {
+ CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN2GHZ(5, 2432, 0),
+ CHAN2GHZ(6, 2437, 0),
+ CHAN2GHZ(7, 2442, 0),
+ CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(12, 2467,
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(13, 2472,
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN2GHZ(14, 2484,
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+};
+
+#define CHAN5GHZ(channel, chflags) { \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = 5000 + 5*(channel), \
+ .hw_value = (channel), \
+ .flags = chflags, \
+ .max_antenna_gain = 0, \
+ .max_power = 21, \
+}
+
+static struct ieee80211_channel wl_5ghz_nphy_chantable[] = {
+ /* UNII-1 */
+ CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
+ /* UNII-2 */
+ CHAN5GHZ(52,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(56,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(60,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(64,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ /* MID */
+ CHAN5GHZ(100,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(104,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(108,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(112,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(116,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(120,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(124,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(128,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(132,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(136,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(140,
+ IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+ IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS |
+ IEEE80211_CHAN_NO_HT40MINUS),
+ /* UNII-3 */
+ CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
+ CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
+ CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+};
+
+#define RATE(rate100m, _flags) { \
+ .bitrate = (rate100m), \
+ .flags = (_flags), \
+ .hw_value = (rate100m / 5), \
+}
+
+static struct ieee80211_rate wl_legacy_ratetable[] = {
+ RATE(10, 0),
+ RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
+ RATE(60, 0),
+ RATE(90, 0),
+ RATE(120, 0),
+ RATE(180, 0),
+ RATE(240, 0),
+ RATE(360, 0),
+ RATE(480, 0),
+ RATE(540, 0),
+};
+
+static struct ieee80211_supported_band wl_band_2GHz_nphy = {
+ .band = IEEE80211_BAND_2GHZ,
+ .channels = wl_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(wl_2ghz_chantable),
+ .bitrates = wl_legacy_ratetable,
+ .n_bitrates = ARRAY_SIZE(wl_legacy_ratetable),
+ .ht_cap = {
+ /* from include/linux/ieee80211.h */
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT,
+#ifdef WLC_HIGH_ONLY
+ .ht_supported = true,
+ .ampdu_factor = AMPDU_RX_FACTOR_16K,
+#else
+ .ht_supported = true,
+ .ampdu_factor = AMPDU_RX_FACTOR_64K,
+#endif
+ .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
+ .mcs = {
+ /* placeholders for now */
+#ifdef WLC_HIGH_ONLY
+ /*
+ * rx_mask[0] = 0xff by default
+ * rx_mask[1] = 0xff if number of rx chain >=2
+ * rx_mask[2] = 0xff if number of rx chain >=3
+ * rx_mask[4] = 1 if 40Mhz is supported
+ */
+ .rx_mask = {0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ .rx_highest = 72, /* max rate of single stream */
+#else
+ .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+ .rx_highest = 500,
+#endif
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
+ }
+};
+
+static struct ieee80211_supported_band wl_band_5GHz_nphy = {
+ .band = IEEE80211_BAND_5GHZ,
+ .channels = wl_5ghz_nphy_chantable,
+ .n_channels = ARRAY_SIZE(wl_5ghz_nphy_chantable),
+ .bitrates = wl_legacy_ratetable + 4,
+ .n_bitrates = ARRAY_SIZE(wl_legacy_ratetable) - 4,
+ .ht_cap = {
+ /* use IEEE80211_HT_CAP_* from include/linux/ieee80211.h */
+ .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT, /* No 40 mhz yet */
+ .ht_supported = true,
+ .ampdu_factor = AMPDU_RX_FACTOR_64K,
+ .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
+ .mcs = {
+ /* placeholders for now */
+ .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+ .rx_highest = 500,
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
+ }
+};
+
+static int ieee_hw_rate_init(struct ieee80211_hw *hw)
+{
+ wl_info_t *wl = HW_TO_WL(hw);
+ int has_5g;
+ char phy_list[4];
+
+ has_5g = 0;
+
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+ if (wlc_get(wl->wlc, WLC_GET_PHYLIST, (int *)&phy_list) < 0) {
+ WL_ERROR(("Phy list failed\n"));
+ }
+ WL_NONE(("%s: phylist = %c\n", __func__, phy_list[0]));
+
+#ifndef WLC_HIGH_ONLY
+ if (phy_list[0] == 'n' || phy_list[0] == 'c') {
+ if (phy_list[0] == 'c') {
+ /* Single stream */
+ wl_band_2GHz_nphy.ht_cap.mcs.rx_mask[1] = 0;
+ wl_band_2GHz_nphy.ht_cap.mcs.rx_highest = 72;
+ }
+#else
+ if (phy_list[0] == 's') {
+#endif
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &wl_band_2GHz_nphy;
+ } else {
+ BUG();
+ return -1;
+ }
+
+ /* Assume all bands use the same phy. True for 11n devices. */
+ if (NBANDS_PUB(wl->pub) > 1) {
+ has_5g++;
+#ifndef WLC_HIGH_ONLY
+ if (phy_list[0] == 'n' || phy_list[0] == 'c') {
+#else
+ if (phy_list[0] == 's') {
+#endif
+ hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+ &wl_band_5GHz_nphy;
+ } else {
+ return -1;
+ }
+ }
+
+ WL_NONE(("%s: 2ghz = %d, 5ghz = %d\n", __func__, 1, has_5g));
+
+ return 0;
+}
+
+static int ieee_hw_init(struct ieee80211_hw *hw)
+{
+ hw->flags = IEEE80211_HW_SIGNAL_DBM
+ /* | IEEE80211_HW_CONNECTION_MONITOR What is this? */
+ | IEEE80211_HW_REPORTS_TX_ACK_STATUS
+ | IEEE80211_HW_AMPDU_AGGREGATION;
+
+ hw->extra_tx_headroom = wlc_get_header_len();
+ /* FIXME: should get this from wlc->machwcap */
+ hw->queues = 4;
+ /* FIXME: this doesn't seem to be used properly in minstrel_ht.
+ * mac80211/status.c:ieee80211_tx_status() checks this value,
+ * but mac80211/rc80211_minstrel_ht.c:minstrel_ht_get_rate()
+ * appears to always set 3 rates
+ */
+ hw->max_rates = 2; /* Primary rate and 1 fallback rate */
+
+ hw->channel_change_time = 7 * 1000; /* channel change time is dependant on chip and band */
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+ hw->rate_control_algorithm = "minstrel_ht";
+
+ hw->sta_data_size = sizeof(struct scb);
+ return ieee_hw_rate_init(hw);
+}
+
+#ifndef BCMSDIO
+/**
+ * determines if a device is a WL device, and if so, attaches it.
+ *
+ * This function determines if a device pointed to by pdev is a WL device,
+ * and if so, performs a wl_attach() on it.
+ *
+ */
+int __devinit
+wl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int rc;
+ wl_info_t *wl;
+ struct ieee80211_hw *hw;
+ u32 val;
+
+ ASSERT(pdev);
+
+ WL_TRACE(("%s: bus %d slot %d func %d irq %d\n", __func__,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn), pdev->irq));
+
+ if ((pdev->vendor != PCI_VENDOR_ID_BROADCOM) ||
+ (((pdev->device & 0xff00) != 0x4300) &&
+ ((pdev->device & 0xff00) != 0x4700) &&
+ ((pdev->device < 43000) || (pdev->device > 43999))))
+ return -ENODEV;
+
+ rc = pci_enable_device(pdev);
+ if (rc) {
+ WL_ERROR(("%s: Cannot enable device %d-%d_%d\n", __func__,
+ pdev->bus->number, PCI_SLOT(pdev->devfn),
+ PCI_FUNC(pdev->devfn)));
+ return -ENODEV;
+ }
+ pci_set_master(pdev);
+
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ hw = ieee80211_alloc_hw(sizeof(wl_info_t), &wl_ops);
+ if (!hw) {
+ WL_ERROR(("%s: ieee80211_alloc_hw failed\n", __func__));
+ rc = -ENOMEM;
+ goto err_1;
+ }
+
+ SET_IEEE80211_DEV(hw, &pdev->dev);
+
+ pci_set_drvdata(pdev, hw);
+
+ bzero(hw->priv, sizeof(*wl));
+
+ wl = wl_attach(pdev->vendor, pdev->device, pci_resource_start(pdev, 0),
+ PCI_BUS, pdev, pdev->irq);
+
+ if (!wl) {
+ WL_ERROR(("%s: %s: wl_attach failed!\n",
+ KBUILD_MODNAME, __func__));
+ return -ENODEV;
+ }
+ return 0;
+ err_1:
+ WL_ERROR(("%s: err_1: Major hoarkage\n", __func__));
+ return 0;
+}
+
+#ifdef LINUXSTA_PS
+static int wl_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ wl_info_t *wl;
+ struct ieee80211_hw *hw;
+
+ WL_TRACE(("wl: wl_suspend\n"));
+
+ hw = pci_get_drvdata(pdev);
+ wl = HW_TO_WL(hw);
+ if (!wl) {
+ WL_ERROR(("wl: wl_suspend: pci_get_drvdata failed\n"));
+ return -ENODEV;
+ }
+
+ WL_LOCK(wl);
+ wl_down(wl);
+ wl->pub->hw_up = false;
+ WL_UNLOCK(wl);
+ pci_save_state(pdev, wl->pci_psstate);
+ pci_disable_device(pdev);
+ return pci_set_power_state(pdev, PCI_D3hot);
+}
+
+static int wl_resume(struct pci_dev *pdev)
+{
+ wl_info_t *wl;
+ struct ieee80211_hw *hw;
+ int err = 0;
+ u32 val;
+
+ WL_TRACE(("wl: wl_resume\n"));
+ hw = pci_get_drvdata(pdev);
+ wl = HW_TO_WL(hw);
+ if (!wl) {
+ WL_ERROR(("wl: wl_resume: pci_get_drvdata failed\n"));
+ return -ENODEV;
+ }
+
+ err = pci_set_power_state(pdev, PCI_D0);
+ if (err)
+ return err;
+
+ pci_restore_state(pdev, wl->pci_psstate);
+
+ err = pci_enable_device(pdev);
+ if (err)
+ return err;
+
+ pci_set_master(pdev);
+
+ pci_read_config_dword(pdev, 0x40, &val);
+ if ((val & 0x0000ff00) != 0)
+ pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+ WL_LOCK(wl);
+ err = wl_up(wl);
+ WL_UNLOCK(wl);
+
+ return err;
+}
+#endif /* LINUXSTA_PS */
+
+static void wl_remove(struct pci_dev *pdev)
+{
+ wl_info_t *wl;
+ struct ieee80211_hw *hw;
+
+ hw = pci_get_drvdata(pdev);
+ wl = HW_TO_WL(hw);
+ if (!wl) {
+ WL_ERROR(("wl: wl_remove: pci_get_drvdata failed\n"));
+ return;
+ }
+ if (!wlc_chipmatch(pdev->vendor, pdev->device)) {
+ WL_ERROR(("wl: wl_remove: wlc_chipmatch failed\n"));
+ return;
+ }
+ if (wl->wlc) {
+ ieee80211_unregister_hw(hw);
+ WL_LOCK(wl);
+ wl_down(wl);
+ WL_UNLOCK(wl);
+ WL_NONE(("%s: Down\n", __func__));
+ }
+ pci_disable_device(pdev);
+
+ wl_free(wl);
+
+ pci_set_drvdata(pdev, NULL);
+ ieee80211_free_hw(hw);
+}
+
+static struct pci_driver wl_pci_driver = {
+ .name = "brcm80211",
+ .probe = wl_pci_probe,
+#ifdef LINUXSTA_PS
+ .suspend = wl_suspend,
+ .resume = wl_resume,
+#endif /* LINUXSTA_PS */
+ .remove = __devexit_p(wl_remove),
+ .id_table = wl_id_table,
+};
+#endif /* !BCMSDIO */
+
+/**
+ * This is the main entry point for the WL driver.
+ *
+ * This function determines if a device pointed to by pdev is a WL device,
+ * and if so, performs a wl_attach() on it.
+ *
+ */
+static int __init wl_module_init(void)
+{
+ int error = -ENODEV;
+
+#ifdef BCMDBG
+ if (msglevel != 0xdeadbeef)
+ wl_msg_level = msglevel;
+ else {
+ char *var = getvar(NULL, "wl_msglevel");
+ if (var)
+ wl_msg_level = simple_strtoul(var, NULL, 0);
+ }
+#ifndef WLC_HIGH_ONLY
+ {
+ extern u32 phyhal_msg_level;
+
+ if (phymsglevel != 0xdeadbeef)
+ phyhal_msg_level = phymsglevel;
+ else {
+ char *var = getvar(NULL, "phy_msglevel");
+ if (var)
+ phyhal_msg_level = simple_strtoul(var, NULL, 0);
+ }
+ }
+#endif /* WLC_HIGH_ONLY */
+#endif /* BCMDBG */
+
+#ifndef BCMSDIO
+ error = pci_register_driver(&wl_pci_driver);
+ if (!error)
+ return 0;
+
+#endif /* !BCMSDIO */
+
+#ifdef WLC_HIGH_ONLY
+ /* BMAC_NOTE: define hardcode number, why NODEVICE is ok ? */
+ error =
+ dbus_register(BCM_DNGL_VID, 0, wl_dbus_probe_cb,
+ wl_dbus_disconnect_cb, NULL, NULL, NULL);
+ if (error == DBUS_ERR_NODEVICE) {
+ error = DBUS_OK;
+ }
+#endif /* WLC_HIGH_ONLY */
+
+ return error;
+}
+
+/**
+ * This function unloads the WL driver from the system.
+ *
+ * This function unconditionally unloads the WL driver module from the
+ * system.
+ *
+ */
+static void __exit wl_module_exit(void)
+{
+#ifndef BCMSDIO
+ pci_unregister_driver(&wl_pci_driver);
+#endif /* !BCMSDIO */
+
+#ifdef WLC_HIGH_ONLY
+ dbus_deregister();
+#endif /* WLC_HIGH_ONLY */
+}
+
+module_init(wl_module_init);
+module_exit(wl_module_exit);
+
+/**
+ * This function frees the WL per-device resources.
+ *
+ * This function frees resources owned by the WL device pointed to
+ * by the wl parameter.
+ *
+ */
+void wl_free(wl_info_t *wl)
+{
+ wl_timer_t *t, *next;
+ osl_t *osh;
+
+ ASSERT(wl);
+#ifndef WLC_HIGH_ONLY
+ /* free ucode data */
+ if (wl->fw.fw_cnt)
+ wl_ucode_data_free();
+ if (wl->irq)
+ free_irq(wl->irq, wl);
+#endif
+
+ /* kill dpc */
+ tasklet_kill(&wl->tasklet);
+
+ if (wl->pub) {
+ wlc_module_unregister(wl->pub, "linux", wl);
+ }
+
+ /* free common resources */
+ if (wl->wlc) {
+ wlc_detach(wl->wlc);
+ wl->wlc = NULL;
+ wl->pub = NULL;
+ }
+
+ /* virtual interface deletion is deferred so we cannot spinwait */
+
+ /* wait for all pending callbacks to complete */
+ while (atomic_read(&wl->callbacks) > 0)
+ schedule();
+
+ /* free timers */
+ for (t = wl->timers; t; t = next) {
+ next = t->next;
+#ifdef BCMDBG
+ if (t->name)
+ kfree(t->name);
+#endif
+ kfree(t);
+ }
+
+ osh = wl->osh;
+
+ /*
+ * unregister_netdev() calls get_stats() which may read chip registers
+ * so we cannot unmap the chip registers until after calling unregister_netdev() .
+ */
+ if (wl->regsva && BUSTYPE(wl->bcm_bustype) != SDIO_BUS &&
+ BUSTYPE(wl->bcm_bustype) != JTAG_BUS) {
+ iounmap((void *)wl->regsva);
+ }
+ wl->regsva = NULL;
+
+#ifdef WLC_HIGH_ONLY
+ wl_rpcq_free(wl);
+
+ wl_txq_free(wl);
+
+ if (wl->rpc) {
+ bcm_rpc_detach(wl->rpc);
+ wl->rpc = NULL;
+ }
+
+ if (wl->rpc_th) {
+ bcm_rpc_tp_detach(wl->rpc_th);
+ wl->rpc_th = NULL;
+ }
+#endif /* WLC_HIGH_ONLY */
+
+ osl_detach(osh);
+}
+
+#ifdef WLC_LOW
+/* transmit a packet */
+static int BCMFASTPATH wl_start(struct sk_buff *skb, wl_info_t *wl)
+{
+ if (!wl)
+ return -ENETDOWN;
+
+ return wl_start_int(wl, WL_TO_HW(wl), skb);
+}
+#endif /* WLC_LOW */
+
+static int BCMFASTPATH
+wl_start_int(wl_info_t *wl, struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+#ifdef WLC_HIGH_ONLY
+ WL_LOCK(wl);
+#endif
+ wlc_sendpkt_mac80211(wl->wlc, skb, hw);
+#ifdef WLC_HIGH_ONLY
+ WL_UNLOCK(wl);
+#endif
+ return NETDEV_TX_OK;
+}
+
+void wl_txflowcontrol(wl_info_t *wl, struct wl_if *wlif, bool state, int prio)
+{
+ WL_ERROR(("Shouldn't be here %s\n", __func__));
+}
+
+#if defined(WLC_HIGH_ONLY)
+/* Schedule a completion handler to run at safe time */
+static int
+wl_schedule_task(wl_info_t *wl, void (*fn) (struct wl_task *task),
+ void *context)
+{
+ wl_task_t *task;
+
+ WL_TRACE(("wl%d: wl_schedule_task\n", wl->pub->unit));
+
+ task = kmalloc(sizeof(wl_task_t), GFP_ATOMIC);
+ if (!task) {
+ WL_ERROR(("wl%d: wl_schedule_task: out of memory\n", wl->pub->unit));
+ return -ENOMEM;
+ }
+
+ INIT_WORK(&task->work, (work_func_t) fn);
+ task->context = context;
+
+ if (!schedule_work(&task->work)) {
+ WL_ERROR(("wl%d: schedule_work() failed\n", wl->pub->unit));
+ kfree(task);
+ return -ENOMEM;
+ }
+
+ atomic_inc(&wl->callbacks);
+
+ return 0;
+}
+#endif /* defined(WLC_HIGH_ONLY) */
+
+void wl_init(wl_info_t *wl)
+{
+ WL_TRACE(("wl%d: wl_init\n", wl->pub->unit));
+
+ wl_reset(wl);
+
+ wlc_init(wl->wlc);
+}
+
+uint wl_reset(wl_info_t *wl)
+{
+ WL_TRACE(("wl%d: wl_reset\n", wl->pub->unit));
+
+ wlc_reset(wl->wlc);
+
+ /* dpc will not be rescheduled */
+ wl->resched = 0;
+
+ return 0;
+}
+
+/*
+ * These are interrupt on/off entry points. Disable interrupts
+ * during interrupt state transition.
+ */
+void BCMFASTPATH wl_intrson(wl_info_t *wl)
+{
+#if defined(WLC_LOW)
+ unsigned long flags;
+
+ INT_LOCK(wl, flags);
+ wlc_intrson(wl->wlc);
+ INT_UNLOCK(wl, flags);
+#endif /* WLC_LOW */
+}
+
+bool wl_alloc_dma_resources(wl_info_t *wl, uint addrwidth)
+{
+ return true;
+}
+
+u32 BCMFASTPATH wl_intrsoff(wl_info_t *wl)
+{
+#if defined(WLC_LOW)
+ unsigned long flags;
+ u32 status;
+
+ INT_LOCK(wl, flags);
+ status = wlc_intrsoff(wl->wlc);
+ INT_UNLOCK(wl, flags);
+ return status;
+#else
+ return 0;
+#endif /* WLC_LOW */
+}
+
+void wl_intrsrestore(wl_info_t *wl, u32 macintmask)
+{
+#if defined(WLC_LOW)
+ unsigned long flags;
+
+ INT_LOCK(wl, flags);
+ wlc_intrsrestore(wl->wlc, macintmask);
+ INT_UNLOCK(wl, flags);
+#endif /* WLC_LOW */
+}
+
+int wl_up(wl_info_t *wl)
+{
+ int error = 0;
+
+ if (wl->pub->up)
+ return 0;
+
+ error = wlc_up(wl->wlc);
+
+ return error;
+}
+
+void wl_down(wl_info_t *wl)
+{
+ uint callbacks, ret_val = 0;
+
+ /* call common down function */
+ ret_val = wlc_down(wl->wlc);
+ callbacks = atomic_read(&wl->callbacks) - ret_val;
+
+ /* wait for down callbacks to complete */
+ WL_UNLOCK(wl);
+
+#ifndef WLC_HIGH_ONLY
+ /* For HIGH_only driver, it's important to actually schedule other work,
+ * not just spin wait since everything runs at schedule level
+ */
+ SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
+#endif /* WLC_HIGH_ONLY */
+
+ WL_LOCK(wl);
+}
+
+irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id)
+{
+#if defined(WLC_LOW)
+ wl_info_t *wl;
+ bool ours, wantdpc;
+ unsigned long flags;
+
+ wl = (wl_info_t *) dev_id;
+
+ WL_ISRLOCK(wl, flags);
+
+ /* call common first level interrupt handler */
+ ours = wlc_isr(wl->wlc, &wantdpc);
+ if (ours) {
+ /* if more to do... */
+ if (wantdpc) {
+
+ /* ...and call the second level interrupt handler */
+ /* schedule dpc */
+ ASSERT(wl->resched == false);
+ tasklet_schedule(&wl->tasklet);
+ }
+ }
+
+ WL_ISRUNLOCK(wl, flags);
+
+ return IRQ_RETVAL(ours);
+#else
+ return IRQ_RETVAL(0);
+#endif /* WLC_LOW */
+}
+
+static void BCMFASTPATH wl_dpc(unsigned long data)
+{
+#ifdef WLC_LOW
+ wl_info_t *wl;
+
+ wl = (wl_info_t *) data;
+
+ WL_LOCK(wl);
+
+ /* call the common second level interrupt handler */
+ if (wl->pub->up) {
+ if (wl->resched) {
+ unsigned long flags;
+
+ INT_LOCK(wl, flags);
+ wlc_intrsupd(wl->wlc);
+ INT_UNLOCK(wl, flags);
+ }
+
+ wl->resched = wlc_dpc(wl->wlc, true);
+ }
+
+ /* wlc_dpc() may bring the driver down */
+ if (!wl->pub->up)
+ goto done;
+
+ /* re-schedule dpc */
+ if (wl->resched)
+ tasklet_schedule(&wl->tasklet);
+ else {
+ /* re-enable interrupts */
+ wl_intrson(wl);
+ }
+
+ done:
+ WL_UNLOCK(wl);
+#endif /* WLC_LOW */
+}
+
+static void wl_link_up(wl_info_t *wl, char *ifname)
+{
+ WL_ERROR(("wl%d: link up (%s)\n", wl->pub->unit, ifname));
+}
+
+static void wl_link_down(wl_info_t *wl, char *ifname)
+{
+ WL_ERROR(("wl%d: link down (%s)\n", wl->pub->unit, ifname));
+}
+
+void wl_event(wl_info_t *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;
+ }
+}
+
+static void wl_timer(unsigned long data)
+{
+#ifndef WLC_HIGH_ONLY
+ _wl_timer((wl_timer_t *) data);
+#else
+ wl_timer_t *t = (wl_timer_t *) data;
+ wl_schedule_task(t->wl, wl_timer_task, t);
+#endif /* WLC_HIGH_ONLY */
+}
+
+static void _wl_timer(wl_timer_t *t)
+{
+ WL_LOCK(t->wl);
+
+ if (t->set) {
+ if (t->periodic) {
+ t->timer.expires = jiffies + t->ms * HZ / 1000;
+ atomic_inc(&t->wl->callbacks);
+ add_timer(&t->timer);
+ t->set = true;
+ } else
+ t->set = false;
+
+ t->fn(t->arg);
+ }
+
+ atomic_dec(&t->wl->callbacks);
+
+ WL_UNLOCK(t->wl);
+}
+
+wl_timer_t *wl_init_timer(wl_info_t *wl, void (*fn) (void *arg), void *arg,
+ const char *name)
+{
+ wl_timer_t *t;
+
+ t = kmalloc(sizeof(wl_timer_t), GFP_ATOMIC);
+ if (!t) {
+ WL_ERROR(("wl%d: wl_init_timer: out of memory\n", wl->pub->unit));
+ return 0;
+ }
+
+ bzero(t, sizeof(wl_timer_t));
+
+ init_timer(&t->timer);
+ t->timer.data = (unsigned long) t;
+ t->timer.function = wl_timer;
+ t->wl = wl;
+ t->fn = fn;
+ t->arg = arg;
+ t->next = wl->timers;
+ wl->timers = t;
+
+#ifdef BCMDBG
+ t->name = kmalloc(strlen(name) + 1, GFP_ATOMIC);
+ if (t->name)
+ strcpy(t->name, name);
+#endif
+
+ return 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
+ */
+void wl_add_timer(wl_info_t *wl, wl_timer_t *t, uint ms, int periodic)
+{
+#ifdef BCMDBG
+ if (t->set) {
+ WL_ERROR(("%s: Already set. Name: %s, per %d\n",
+ __func__, t->name, periodic));
+ }
+#endif
+ ASSERT(!t->set);
+
+ t->ms = ms;
+ t->periodic = (bool) periodic;
+ t->set = true;
+ t->timer.expires = jiffies + ms * HZ / 1000;
+
+ atomic_inc(&wl->callbacks);
+ add_timer(&t->timer);
+}
+
+/* return true if timer successfully deleted, false if still pending */
+bool wl_del_timer(wl_info_t *wl, wl_timer_t *t)
+{
+ if (t->set) {
+ t->set = false;
+ if (!del_timer(&t->timer)) {
+ return false;
+ }
+ atomic_dec(&wl->callbacks);
+ }
+
+ return true;
+}
+
+void wl_free_timer(wl_info_t *wl, wl_timer_t *t)
+{
+ wl_timer_t *tmp;
+
+ /* delete the timer in case it is active */
+ wl_del_timer(wl, t);
+
+ if (wl->timers == t) {
+ wl->timers = wl->timers->next;
+#ifdef BCMDBG
+ if (t->name)
+ kfree(t->name);
+#endif
+ kfree(t);
+ return;
+
+ }
+
+ tmp = wl->timers;
+ while (tmp) {
+ if (tmp->next == t) {
+ tmp->next = t->next;
+#ifdef BCMDBG
+ if (t->name)
+ kfree(t->name);
+#endif
+ kfree(t);
+ return;
+ }
+ tmp = tmp->next;
+ }
+
+}
+
+static int wl_linux_watchdog(void *ctx)
+{
+ wl_info_t *wl = (wl_info_t *) ctx;
+ struct net_device_stats *stats = NULL;
+ uint id;
+ /* refresh stats */
+ if (wl->pub->up) {
+ ASSERT(wl->stats_id < 2);
+
+ 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->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_frame_errors = 0;
+ stats->rx_fifo_errors = WLCNTVAL(wl->pub->_cnt->rxoflo);
+ stats->rx_missed_errors = 0;
+
+ stats->tx_fifo_errors = WLCNTVAL(wl->pub->_cnt->txuflo);
+
+ wl->stats_id = id;
+
+ }
+
+ return 0;
+}
+
+struct wl_fw_hdr {
+ u32 offset;
+ u32 len;
+ u32 idx;
+};
+
+#ifdef WLC_HIGH_ONLY
+static void wl_rpc_down(void *wlh)
+{
+ wl_info_t *wl = (wl_info_t *) (wlh);
+
+ wlc_device_removed(wl->wlc);
+
+ wl_rpcq_free(wl);
+}
+
+static int BCMFASTPATH wl_start(struct sk_buff *skb, wl_info_t *wl)
+{
+
+ unsigned long flags;
+
+ skb->prev = NULL;
+
+ /* Lock the queue as tasklet could be running at this time */
+ TXQ_LOCK(wl, flags);
+ if (wl->txq_head == NULL)
+ wl->txq_head = skb;
+ else {
+ wl->txq_tail->prev = skb;
+ }
+ wl->txq_tail = skb;
+
+ if (wl->txq_dispatched == false) {
+ wl->txq_dispatched = true;
+
+ if (schedule_work(&wl->txq_task.work)) {
+ atomic_inc(&wl->callbacks);
+ } else {
+ WL_ERROR(("wl%d: wl_start/schedule_work failed\n",
+ wl->pub->unit));
+ }
+ }
+
+ TXQ_UNLOCK(wl, flags);
+
+ return 0;
+
+}
+
+static void wl_start_txqwork(struct wl_task *task)
+{
+ wl_info_t *wl = (wl_info_t *) task->context;
+ struct sk_buff *skb;
+ unsigned long flags;
+ uint count = 0;
+
+ WL_TRACE(("wl%d: wl_start_txqwork\n", wl->pub->unit));
+
+ /* First remove an entry then go for execution */
+ TXQ_LOCK(wl, flags);
+ while (wl->txq_head) {
+ skb = wl->txq_head;
+ wl->txq_head = skb->prev;
+ skb->prev = NULL;
+ if (wl->txq_head == NULL)
+ wl->txq_tail = NULL;
+ TXQ_UNLOCK(wl, flags);
+
+ /* it has WL_LOCK/WL_UNLOCK inside */
+ wl_start_int(wl, WL_TO_HW(wl), skb);
+
+ /* bounded our execution, reshedule ourself next */
+ if (++count >= 10)
+ break;
+
+ TXQ_LOCK(wl, flags);
+ }
+
+ if (count >= 10) {
+ if (!schedule_work(&wl->txq_task.work)) {
+ WL_ERROR(("wl%d: wl_start/schedule_work failed\n",
+ wl->pub->unit));
+ atomic_dec(&wl->callbacks);
+ }
+ } else {
+ wl->txq_dispatched = false;
+ TXQ_UNLOCK(wl, flags);
+ atomic_dec(&wl->callbacks);
+ }
+
+ return;
+}
+
+static void wl_txq_free(wl_info_t *wl)
+{
+ struct sk_buff *skb;
+
+ if (wl->txq_head == NULL) {
+ ASSERT(wl->txq_tail == NULL);
+ return;
+ }
+
+ while (wl->txq_head) {
+ skb = wl->txq_head;
+ wl->txq_head = skb->prev;
+ PKTFREE(wl->osh, skb, true);
+ }
+
+ wl->txq_tail = NULL;
+}
+
+static void wl_rpcq_free(wl_info_t *wl)
+{
+ rpc_buf_t *buf;
+
+ if (wl->rpcq_head == NULL) {
+ ASSERT(wl->rpcq_tail == NULL);
+ return;
+ }
+
+ while (wl->rpcq_head) {
+ buf = wl->rpcq_head;
+ wl->rpcq_head = bcm_rpc_buf_next_get(wl->rpc_th, buf);
+ bcm_rpc_buf_free(wl->rpc_dispatch_ctx.rpc, buf);
+ }
+
+ wl->rpcq_tail = NULL;
+}
+
+static void wl_rpcq_dispatch(struct wl_task *task)
+{
+ wl_info_t *wl = (wl_info_t *) task->context;
+ rpc_buf_t *buf;
+ unsigned long flags;
+
+ /* First remove an entry then go for execution */
+ RPCQ_LOCK(wl, flags);
+ while (wl->rpcq_head) {
+ buf = wl->rpcq_head;
+ wl->rpcq_head = bcm_rpc_buf_next_get(wl->rpc_th, buf);
+
+ if (wl->rpcq_head == NULL)
+ wl->rpcq_tail = NULL;
+ RPCQ_UNLOCK(wl, flags);
+
+ WL_LOCK(wl);
+ wlc_rpc_high_dispatch(&wl->rpc_dispatch_ctx, buf);
+ WL_UNLOCK(wl);
+
+ RPCQ_LOCK(wl, flags);
+ }
+
+ wl->rpcq_dispatched = false;
+
+ RPCQ_UNLOCK(wl, flags);
+
+ kfree(task);
+ atomic_dec(&wl->callbacks);
+}
+
+static void wl_rpcq_add(wl_info_t *wl, rpc_buf_t *buf)
+{
+ unsigned long flags;
+
+ bcm_rpc_buf_next_set(wl->rpc_th, buf, NULL);
+
+ /* Lock the queue as tasklet could be running at this time */
+ RPCQ_LOCK(wl, flags);
+ if (wl->rpcq_head == NULL)
+ wl->rpcq_head = buf;
+ else
+ bcm_rpc_buf_next_set(wl->rpc_th, wl->rpcq_tail, buf);
+
+ wl->rpcq_tail = buf;
+
+ if (wl->rpcq_dispatched == false) {
+ wl->rpcq_dispatched = true;
+ wl_schedule_task(wl, wl_rpcq_dispatch, wl);
+ }
+
+ RPCQ_UNLOCK(wl, flags);
+}
+
+#if defined(BCMDBG)
+static const struct name_entry rpc_name_tbl[] = RPC_ID_TABLE;
+#endif /* BCMDBG */
+
+/* dongle-side rpc dispatch routine */
+static void wl_rpc_dispatch_schedule(void *ctx, struct rpc_buf *buf)
+{
+ bcm_xdr_buf_t b;
+ wl_info_t *wl = (wl_info_t *) ctx;
+ wlc_rpc_id_t rpc_id;
+ int err;
+
+ bcm_xdr_buf_init(&b, bcm_rpc_buf_data(wl->rpc_th, buf),
+ bcm_rpc_buf_len_get(wl->rpc_th, buf));
+
+ err = bcm_xdr_unpack_u32(&b, &rpc_id);
+ ASSERT(!err);
+ WL_TRACE(("%s: Dispatch id %s\n", __func__,
+ WLC_RPC_ID_LOOKUP(rpc_name_tbl, rpc_id)));
+
+ /* Handle few emergency ones */
+ switch (rpc_id) {
+ default:
+ wl_rpcq_add(wl, buf);
+ break;
+ }
+}
+
+static void wl_timer_task(wl_task_t *task)
+{
+ wl_timer_t *t = (wl_timer_t *) task->context;
+
+ _wl_timer(t);
+ kfree(task);
+
+ /* This dec is for the task_schedule. The timer related
+ * callback is decremented in _wl_timer
+ */
+ atomic_dec(&t->wl->callbacks);
+}
+#endif /* WLC_HIGH_ONLY */
+
+#ifndef WLC_HIGH_ONLY
+char *wl_firmwares[WL_MAX_FW] = {
+ "brcm/bcm43xx",
+ NULL
+};
+
+#ifdef WLC_LOW
+int wl_ucode_init_buf(wl_info_t *wl, void **pbuf, u32 idx)
+{
+ int i, entry;
+ const u8 *pdata;
+ struct wl_fw_hdr *hdr;
+ for (i = 0; i < wl->fw.fw_cnt; i++) {
+ hdr = (struct wl_fw_hdr *)wl->fw.fw_hdr[i]->data;
+ for (entry = 0; entry < wl->fw.hdr_num_entries[i];
+ entry++, hdr++) {
+ if (hdr->idx == 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);
+ }
+ bcopy(pdata, *pbuf, hdr->len);
+ return 0;
+ }
+ }
+ }
+ printf("ERROR: ucode buf tag:%d can not be found!\n", idx);
+ *pbuf = NULL;
+ return -1;
+}
+
+int wl_ucode_init_uint(wl_info_t *wl, u32 *data, u32 idx)
+{
+ int i, entry;
+ const u8 *pdata;
+ struct wl_fw_hdr *hdr;
+ for (i = 0; i < wl->fw.fw_cnt; i++) {
+ hdr = (struct wl_fw_hdr *)wl->fw.fw_hdr[i]->data;
+ for (entry = 0; entry < wl->fw.hdr_num_entries[i];
+ entry++, hdr++) {
+ if (hdr->idx == idx) {
+ pdata = wl->fw.fw_bin[i]->data + hdr->offset;
+ ASSERT(hdr->len == 4);
+ *data = *((u32 *) pdata);
+ return 0;
+ }
+ }
+ }
+ printf("ERROR: ucode tag:%d can not be found!\n", idx);
+ return -1;
+}
+#endif /* WLC_LOW */
+
+static int wl_request_fw(wl_info_t *wl, struct pci_dev *pdev)
+{
+ int status;
+ struct device *device = &pdev->dev;
+ char fw_name[100];
+ int i;
+
+ bzero((void *)&wl->fw, sizeof(struct wl_firmware));
+ for (i = 0; i < WL_MAX_FW; i++) {
+ if (wl_firmwares[i] == NULL)
+ break;
+ sprintf(fw_name, "%s-%d.fw", wl_firmwares[i],
+ UCODE_LOADER_API_VER);
+ 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);
+ return status;
+ }
+ WL_NONE(("request fw %s\n", fw_name));
+ sprintf(fw_name, "%s_hdr-%d.fw", wl_firmwares[i],
+ 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);
+ return status;
+ }
+ wl->fw.hdr_num_entries[i] =
+ wl->fw.fw_hdr[i]->size / (sizeof(struct wl_fw_hdr));
+ WL_NONE(("request fw %s find: %d entries\n", fw_name,
+ wl->fw.hdr_num_entries[i]));
+ }
+ wl->fw.fw_cnt = i;
+ wl_ucode_data_init(wl);
+ return 0;
+}
+
+#ifdef WLC_LOW
+void wl_ucode_free_buf(void *p)
+{
+ kfree(p);
+}
+#endif /* WLC_LOW */
+
+static void wl_release_fw(wl_info_t *wl)
+{
+ int i;
+ for (i = 0; i < WL_MAX_FW; i++) {
+ release_firmware(wl->fw.fw_bin[i]);
+ release_firmware(wl->fw.fw_hdr[i]);
+ }
+}
+#endif /* WLC_HIGH_ONLY */
diff --git a/drivers/staging/brcm80211/sys/wl_mac80211.h b/drivers/staging/brcm80211/sys/wl_mac80211.h
new file mode 100644
index 000000000000..78cee4454b0b
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wl_mac80211.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wl_mac80211_h_
+#define _wl_mac80211_h_
+
+#include <wlc_types.h>
+
+/* BMAC Note: High-only driver is no longer working in softirq context as it needs to block and
+ * sleep so perimeter lock has to be a semaphore instead of spinlock. This requires timers to be
+ * submitted to workqueue instead of being on kernel timer
+ */
+typedef struct wl_timer {
+ struct timer_list timer;
+ struct wl_info *wl;
+ void (*fn) (void *);
+ void *arg; /* argument to fn */
+ uint ms;
+ bool periodic;
+ bool set;
+ struct wl_timer *next;
+#ifdef BCMDBG
+ char *name; /* Description of the timer */
+#endif
+} wl_timer_t;
+
+/* contortion to call functions at safe time */
+/* In 2.6.20 kernels work functions get passed a pointer to the struct work, so things
+ * will continue to work as long as the work structure is the first component of the task structure.
+ */
+typedef struct wl_task {
+ struct work_struct work;
+ void *context;
+} wl_task_t;
+
+struct wl_if {
+ uint subunit; /* WDS/BSS unit */
+ struct pci_dev *pci_dev;
+};
+
+#define WL_MAX_FW 4
+struct wl_firmware {
+ u32 fw_cnt;
+ const struct firmware *fw_bin[WL_MAX_FW];
+ const struct firmware *fw_hdr[WL_MAX_FW];
+ u32 hdr_num_entries[WL_MAX_FW];
+};
+
+struct wl_info {
+ wlc_pub_t *pub; /* pointer to public wlc state */
+ void *wlc; /* pointer to private common os-independent data */
+ osl_t *osh; /* pointer to os handler */
+ u32 magic;
+
+ int irq;
+
+#ifdef WLC_HIGH_ONLY
+ struct semaphore sem; /* use semaphore to allow sleep */
+#else
+ spinlock_t lock; /* per-device perimeter lock */
+ spinlock_t isr_lock; /* per-device ISR synchronization lock */
+#endif
+ uint bcm_bustype; /* bus type */
+ bool piomode; /* set from insmod argument */
+ void *regsva; /* opaque chip registers virtual address */
+ atomic_t callbacks; /* # outstanding callback functions */
+ struct wl_timer *timers; /* timer cleanup queue */
+ struct tasklet_struct tasklet; /* dpc tasklet */
+#ifdef BCMSDIO
+ bcmsdh_info_t *sdh; /* pointer to sdio bus handler */
+ unsigned long flags; /* current irq flags */
+#endif /* BCMSDIO */
+ bool resched; /* dpc needs to be and is rescheduled */
+#ifdef LINUXSTA_PS
+ u32 pci_psstate[16]; /* pci ps-state save/restore */
+#endif
+ /* RPC, handle, lock, txq, workitem */
+#ifdef WLC_HIGH_ONLY
+ rpc_info_t *rpc; /* RPC handle */
+ rpc_tp_info_t *rpc_th; /* RPC transport handle */
+ wlc_rpc_ctx_t rpc_dispatch_ctx;
+
+ bool rpcq_dispatched; /* Avoid scheduling multiple tasks */
+ spinlock_t rpcq_lock; /* Lock for the queue */
+ rpc_buf_t *rpcq_head; /* RPC Q */
+ rpc_buf_t *rpcq_tail; /* Points to the last buf */
+
+ bool txq_dispatched; /* Avoid scheduling multiple tasks */
+ spinlock_t txq_lock; /* Lock for the queue */
+ struct sk_buff *txq_head; /* TX Q */
+ struct sk_buff *txq_tail; /* Points to the last buf */
+
+ wl_task_t txq_task; /* work queue for wl_start() */
+#endif /* WLC_HIGH_ONLY */
+ uint stats_id; /* the current set of stats */
+ /* ping-pong stats counters updated by Linux watchdog */
+ struct net_device_stats stats_watchdog[2];
+ struct wl_firmware fw;
+};
+
+#ifndef WLC_HIGH_ONLY
+#define WL_LOCK(wl) spin_lock_bh(&(wl)->lock)
+#define WL_UNLOCK(wl) spin_unlock_bh(&(wl)->lock)
+
+/* locking from inside wl_isr */
+#define WL_ISRLOCK(wl, flags) do {spin_lock(&(wl)->isr_lock); (void)(flags); } while (0)
+#define WL_ISRUNLOCK(wl, flags) do {spin_unlock(&(wl)->isr_lock); (void)(flags); } while (0)
+
+/* locking under WL_LOCK() to synchronize with wl_isr */
+#define INT_LOCK(wl, flags) spin_lock_irqsave(&(wl)->isr_lock, flags)
+#define INT_UNLOCK(wl, flags) spin_unlock_irqrestore(&(wl)->isr_lock, flags)
+#else /* BCMSDIO */
+
+#define WL_LOCK(wl) down(&(wl)->sem)
+#define WL_UNLOCK(wl) up(&(wl)->sem)
+
+#define WL_ISRLOCK(wl)
+#define WL_ISRUNLOCK(wl)
+#endif /* WLC_HIGH_ONLY */
+
+/* handle forward declaration */
+typedef struct wl_info wl_info_t;
+
+#ifndef PCI_D0
+#define PCI_D0 0
+#endif
+
+#ifndef PCI_D3hot
+#define PCI_D3hot 3
+#endif
+
+/* exported functions */
+
+extern irqreturn_t wl_isr(int irq, void *dev_id);
+
+extern int __devinit wl_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+extern void wl_free(wl_info_t *wl);
+extern int wl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+extern int wl_ucode_data_init(wl_info_t *wl);
+extern void wl_ucode_data_free(void);
+#ifdef WLC_LOW
+extern void wl_ucode_free_buf(void *);
+extern int wl_ucode_init_buf(wl_info_t *wl, void **pbuf, u32 idx);
+extern int wl_ucode_init_uint(wl_info_t *wl, u32 *data, u32 idx);
+#endif /* WLC_LOW */
+
+#endif /* _wl_mac80211_h_ */
diff --git a/drivers/staging/brcm80211/sys/wl_ucode.h b/drivers/staging/brcm80211/sys/wl_ucode.h
new file mode 100644
index 000000000000..a1ba37209f96
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wl_ucode.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+typedef struct d11init {
+ u16 addr;
+ u16 size;
+ u32 value;
+} d11init_t;
+
+extern d11init_t *d11lcn0bsinitvals24;
+extern d11init_t *d11lcn0initvals24;
+extern d11init_t *d11lcn1bsinitvals24;
+extern d11init_t *d11lcn1initvals24;
+extern d11init_t *d11lcn2bsinitvals24;
+extern d11init_t *d11lcn2initvals24;
+extern d11init_t *d11n0absinitvals16;
+extern d11init_t *d11n0bsinitvals16;
+extern d11init_t *d11n0initvals16;
+extern u32 *bcm43xx_16_mimo;
+extern u32 bcm43xx_16_mimosz;
+extern u32 *bcm43xx_24_lcn;
+extern u32 bcm43xx_24_lcnsz;
+extern u32 *bcm43xx_bommajor;
+extern u32 *bcm43xx_bomminor;
diff --git a/drivers/staging/brcm80211/sys/wl_ucode_loader.c b/drivers/staging/brcm80211/sys/wl_ucode_loader.c
new file mode 100644
index 000000000000..0b41a9cb1ec9
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wl_ucode_loader.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+typedef struct wl_info wl_info_t;
+#include <linux/types.h>
+#include <bcmdefs.h>
+#include <d11ucode_ext.h>
+#include <wl_ucode.h>
+
+extern int wl_ucode_init_buf(wl_info_t *wl, void **pbuf, unsigned int idx);
+extern int wl_ucode_init_uint(wl_info_t *wl, unsigned *data, unsigned int idx);
+extern int wl_ucode_data_init(wl_info_t *wl);
+extern void wl_ucode_data_free(void);
+extern void wl_ucode_free_buf(void *);
+
+d11init_t *d11lcn0bsinitvals24;
+d11init_t *d11lcn0initvals24;
+d11init_t *d11lcn1bsinitvals24;
+d11init_t *d11lcn1initvals24;
+d11init_t *d11lcn2bsinitvals24;
+d11init_t *d11lcn2initvals24;
+d11init_t *d11n0absinitvals16;
+d11init_t *d11n0bsinitvals16;
+d11init_t *d11n0initvals16;
+u32 *bcm43xx_16_mimo;
+u32 bcm43xx_16_mimosz;
+u32 *bcm43xx_24_lcn;
+u32 bcm43xx_24_lcnsz;
+u32 *bcm43xx_bommajor;
+u32 *bcm43xx_bomminor;
+
+int wl_ucode_data_init(wl_info_t *wl)
+{
+ wl_ucode_init_buf(wl, (void **)&d11lcn0bsinitvals24,
+ D11LCN0BSINITVALS24);
+ wl_ucode_init_buf(wl, (void **)&d11lcn0initvals24, D11LCN0INITVALS24);
+ wl_ucode_init_buf(wl, (void **)&d11lcn1bsinitvals24,
+ D11LCN1BSINITVALS24);
+ wl_ucode_init_buf(wl, (void **)&d11lcn1initvals24, D11LCN1INITVALS24);
+ wl_ucode_init_buf(wl, (void **)&d11lcn2bsinitvals24,
+ D11LCN2BSINITVALS24);
+ wl_ucode_init_buf(wl, (void **)&d11lcn2initvals24, D11LCN2INITVALS24);
+ wl_ucode_init_buf(wl, (void **)&d11n0absinitvals16, D11N0ABSINITVALS16);
+ wl_ucode_init_buf(wl, (void **)&d11n0bsinitvals16, D11N0BSINITVALS16);
+ wl_ucode_init_buf(wl, (void **)&d11n0initvals16, D11N0INITVALS16);
+ wl_ucode_init_buf(wl, (void **)&bcm43xx_16_mimo,
+ D11UCODE_OVERSIGHT16_MIMO);
+ wl_ucode_init_uint(wl, &bcm43xx_16_mimosz, D11UCODE_OVERSIGHT16_MIMOSZ);
+ wl_ucode_init_buf(wl, (void **)&bcm43xx_24_lcn,
+ D11UCODE_OVERSIGHT24_LCN);
+ wl_ucode_init_uint(wl, &bcm43xx_24_lcnsz, D11UCODE_OVERSIGHT24_LCNSZ);
+ wl_ucode_init_buf(wl, (void **)&bcm43xx_bommajor,
+ D11UCODE_OVERSIGHT_BOMMAJOR);
+ wl_ucode_init_buf(wl, (void **)&bcm43xx_bomminor,
+ D11UCODE_OVERSIGHT_BOMMINOR);
+
+ return 0;
+}
+
+void wl_ucode_data_free(void)
+{
+ wl_ucode_free_buf((void *)d11lcn0bsinitvals24);
+ wl_ucode_free_buf((void *)d11lcn0initvals24);
+ wl_ucode_free_buf((void *)d11lcn1bsinitvals24);
+ wl_ucode_free_buf((void *)d11lcn1initvals24);
+ wl_ucode_free_buf((void *)d11lcn2bsinitvals24);
+ wl_ucode_free_buf((void *)d11lcn2initvals24);
+ wl_ucode_free_buf((void *)d11n0absinitvals16);
+ wl_ucode_free_buf((void *)d11n0bsinitvals16);
+ wl_ucode_free_buf((void *)d11n0initvals16);
+ wl_ucode_free_buf((void *)bcm43xx_16_mimo);
+ wl_ucode_free_buf((void *)bcm43xx_24_lcn);
+ wl_ucode_free_buf((void *)bcm43xx_bommajor);
+ wl_ucode_free_buf((void *)bcm43xx_bomminor);
+
+ return;
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_alloc.c b/drivers/staging/brcm80211/sys/wlc_alloc.c
new file mode 100644
index 000000000000..2dc89f9c2635
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_alloc.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <bcmdefs.h>
+#include <wlc_cfg.h>
+#include <linuxver.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <wlioctl.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_mac80211.h>
+#include <wlc_alloc.h>
+
+static wlc_pub_t *wlc_pub_malloc(osl_t *osh, uint unit, uint *err,
+ uint devid);
+static void wlc_pub_mfree(osl_t *osh, wlc_pub_t *pub);
+static void wlc_tunables_init(wlc_tunables_t *tunables, uint devid);
+
+void *wlc_calloc(osl_t *osh, uint unit, uint size)
+{
+ void *item;
+
+ item = kzalloc(size, GFP_ATOMIC);
+ if (item == NULL)
+ WL_ERROR(("wl%d: %s: out of memory\n", unit, __func__));
+ return item;
+}
+
+void wlc_tunables_init(wlc_tunables_t *tunables, uint devid)
+{
+ tunables->ntxd = NTXD;
+ tunables->nrxd = NRXD;
+ tunables->rxbufsz = RXBUFSZ;
+ tunables->nrxbufpost = NRXBUFPOST;
+ tunables->maxscb = MAXSCB;
+ tunables->ampdunummpdu = AMPDU_NUM_MPDU;
+ tunables->maxpktcb = MAXPKTCB;
+ tunables->maxucodebss = WLC_MAX_UCODE_BSS;
+ tunables->maxucodebss4 = WLC_MAX_UCODE_BSS4;
+ tunables->maxbss = MAXBSS;
+ tunables->datahiwat = WLC_DATAHIWAT;
+ tunables->ampdudatahiwat = WLC_AMPDUDATAHIWAT;
+ tunables->rxbnd = RXBND;
+ tunables->txsbnd = TXSBND;
+#if defined(WLC_HIGH_ONLY) && defined(NTXD_USB_4319)
+ if (devid == BCM4319_CHIP_ID) {
+ tunables->ntxd = NTXD_USB_4319;
+ }
+#endif /* WLC_HIGH_ONLY */
+}
+
+static wlc_pub_t *wlc_pub_malloc(osl_t *osh, uint unit, uint *err, uint devid)
+{
+ wlc_pub_t *pub;
+
+ pub = (wlc_pub_t *) wlc_calloc(osh, unit, sizeof(wlc_pub_t));
+ if (pub == NULL) {
+ *err = 1001;
+ goto fail;
+ }
+
+ pub->tunables = (wlc_tunables_t *)wlc_calloc(osh, unit,
+ sizeof(wlc_tunables_t));
+ if (pub->tunables == NULL) {
+ *err = 1028;
+ goto fail;
+ }
+
+ /* need to init the tunables now */
+ wlc_tunables_init(pub->tunables, devid);
+
+ pub->multicast = (struct ether_addr *)wlc_calloc(osh, unit,
+ (sizeof(struct ether_addr) * MAXMULTILIST));
+ if (pub->multicast == NULL) {
+ *err = 1003;
+ goto fail;
+ }
+
+ return pub;
+
+ fail:
+ wlc_pub_mfree(osh, pub);
+ return NULL;
+}
+
+static void wlc_pub_mfree(osl_t *osh, wlc_pub_t *pub)
+{
+ if (pub == NULL)
+ return;
+
+ if (pub->multicast)
+ kfree(pub->multicast);
+ if (pub->tunables) {
+ kfree(pub->tunables);
+ pub->tunables = NULL;
+ }
+
+ kfree(pub);
+}
+
+wlc_bsscfg_t *wlc_bsscfg_malloc(osl_t *osh, uint unit)
+{
+ wlc_bsscfg_t *cfg;
+
+ cfg = (wlc_bsscfg_t *) wlc_calloc(osh, unit, sizeof(wlc_bsscfg_t));
+ if (cfg == NULL)
+ goto fail;
+
+ cfg->current_bss = (wlc_bss_info_t *)wlc_calloc(osh, unit,
+ sizeof(wlc_bss_info_t));
+ if (cfg->current_bss == NULL)
+ goto fail;
+
+ return cfg;
+
+ fail:
+ wlc_bsscfg_mfree(osh, cfg);
+ return NULL;
+}
+
+void wlc_bsscfg_mfree(osl_t *osh, wlc_bsscfg_t *cfg)
+{
+ if (cfg == NULL)
+ return;
+
+ if (cfg->maclist) {
+ kfree(cfg->maclist);
+ cfg->maclist = NULL;
+ }
+
+ if (cfg->current_bss != NULL) {
+ wlc_bss_info_t *current_bss = cfg->current_bss;
+ if (current_bss->bcn_prb != NULL)
+ kfree(current_bss->bcn_prb);
+ kfree(current_bss);
+ cfg->current_bss = NULL;
+ }
+
+ kfree(cfg);
+}
+
+void wlc_bsscfg_ID_assign(wlc_info_t *wlc, wlc_bsscfg_t *bsscfg)
+{
+ bsscfg->ID = wlc->next_bsscfg_ID;
+ wlc->next_bsscfg_ID++;
+}
+
+/*
+ * The common driver entry routine. Error codes should be unique
+ */
+wlc_info_t *wlc_attach_malloc(osl_t *osh, uint unit, uint *err, uint devid)
+{
+ wlc_info_t *wlc;
+
+ wlc = (wlc_info_t *) wlc_calloc(osh, unit, sizeof(wlc_info_t));
+ if (wlc == NULL) {
+ *err = 1002;
+ goto fail;
+ }
+
+ wlc->hwrxoff = WL_HWRXOFF;
+
+ /* allocate wlc_pub_t state structure */
+ wlc->pub = wlc_pub_malloc(osh, unit, err, devid);
+ if (wlc->pub == NULL) {
+ *err = 1003;
+ goto fail;
+ }
+ wlc->pub->wlc = wlc;
+
+ /* allocate wlc_hw_info_t state structure */
+
+ wlc->hw = (wlc_hw_info_t *)wlc_calloc(osh, unit,
+ sizeof(wlc_hw_info_t));
+ if (wlc->hw == NULL) {
+ *err = 1005;
+ goto fail;
+ }
+ wlc->hw->wlc = wlc;
+
+#ifdef WLC_LOW
+ wlc->hw->bandstate[0] = (wlc_hwband_t *)wlc_calloc(osh, unit,
+ (sizeof(wlc_hwband_t) * MAXBANDS));
+ if (wlc->hw->bandstate[0] == NULL) {
+ *err = 1006;
+ goto fail;
+ } else {
+ int i;
+
+ for (i = 1; i < MAXBANDS; i++) {
+ wlc->hw->bandstate[i] = (wlc_hwband_t *)
+ ((unsigned long)wlc->hw->bandstate[0] +
+ (sizeof(wlc_hwband_t) * i));
+ }
+ }
+#endif /* WLC_LOW */
+
+ wlc->modulecb = (modulecb_t *)wlc_calloc(osh, unit,
+ sizeof(modulecb_t) * WLC_MAXMODULES);
+ if (wlc->modulecb == NULL) {
+ *err = 1009;
+ goto fail;
+ }
+
+ wlc->default_bss = (wlc_bss_info_t *)wlc_calloc(osh, unit,
+ sizeof(wlc_bss_info_t));
+ if (wlc->default_bss == NULL) {
+ *err = 1010;
+ goto fail;
+ }
+
+ wlc->cfg = wlc_bsscfg_malloc(osh, unit);
+ if (wlc->cfg == NULL) {
+ *err = 1011;
+ goto fail;
+ }
+ wlc_bsscfg_ID_assign(wlc, wlc->cfg);
+
+ wlc->pkt_callback = (pkt_cb_t *)wlc_calloc(osh, unit,
+ (sizeof(pkt_cb_t) * (wlc->pub->tunables->maxpktcb + 1)));
+ if (wlc->pkt_callback == NULL) {
+ *err = 1013;
+ goto fail;
+ }
+
+ wlc->wsec_def_keys[0] = (wsec_key_t *)wlc_calloc(osh, unit,
+ (sizeof(wsec_key_t) * WLC_DEFAULT_KEYS));
+ if (wlc->wsec_def_keys[0] == NULL) {
+ *err = 1015;
+ goto fail;
+ } else {
+ int i;
+ for (i = 1; i < WLC_DEFAULT_KEYS; i++) {
+ wlc->wsec_def_keys[i] = (wsec_key_t *)
+ ((unsigned long)wlc->wsec_def_keys[0] +
+ (sizeof(wsec_key_t) * i));
+ }
+ }
+
+ wlc->protection = (wlc_protection_t *)wlc_calloc(osh, unit,
+ sizeof(wlc_protection_t));
+ if (wlc->protection == NULL) {
+ *err = 1016;
+ goto fail;
+ }
+
+ wlc->stf = (wlc_stf_t *)wlc_calloc(osh, unit, sizeof(wlc_stf_t));
+ if (wlc->stf == NULL) {
+ *err = 1017;
+ goto fail;
+ }
+
+ wlc->bandstate[0] = (wlcband_t *)wlc_calloc(osh, unit,
+ (sizeof(wlcband_t) * MAXBANDS));
+ if (wlc->bandstate[0] == NULL) {
+ *err = 1025;
+ goto fail;
+ } else {
+ int i;
+
+ for (i = 1; i < MAXBANDS; i++) {
+ wlc->bandstate[i] =
+ (wlcband_t *) ((unsigned long)wlc->bandstate[0] +
+ (sizeof(wlcband_t) * i));
+ }
+ }
+
+ wlc->corestate = (wlccore_t *)wlc_calloc(osh, unit, sizeof(wlccore_t));
+ if (wlc->corestate == NULL) {
+ *err = 1026;
+ goto fail;
+ }
+
+ wlc->corestate->macstat_snapshot =
+ (macstat_t *)wlc_calloc(osh, unit, sizeof(macstat_t));
+ if (wlc->corestate->macstat_snapshot == NULL) {
+ *err = 1027;
+ goto fail;
+ }
+
+ return wlc;
+
+ fail:
+ wlc_detach_mfree(wlc, osh);
+ return NULL;
+}
+
+void wlc_detach_mfree(wlc_info_t *wlc, osl_t *osh)
+{
+ if (wlc == NULL)
+ return;
+
+ if (wlc->modulecb) {
+ kfree(wlc->modulecb);
+ wlc->modulecb = NULL;
+ }
+
+ if (wlc->default_bss) {
+ kfree(wlc->default_bss);
+ wlc->default_bss = NULL;
+ }
+ if (wlc->cfg) {
+ wlc_bsscfg_mfree(osh, wlc->cfg);
+ wlc->cfg = NULL;
+ }
+
+ if (wlc->pkt_callback && wlc->pub && wlc->pub->tunables) {
+ kfree(wlc->pkt_callback);
+ wlc->pkt_callback = NULL;
+ }
+
+ if (wlc->wsec_def_keys[0])
+ kfree(wlc->wsec_def_keys[0]);
+ if (wlc->protection) {
+ kfree(wlc->protection);
+ wlc->protection = NULL;
+ }
+
+ if (wlc->stf) {
+ kfree(wlc->stf);
+ wlc->stf = NULL;
+ }
+
+ if (wlc->bandstate[0])
+ kfree(wlc->bandstate[0]);
+
+ if (wlc->corestate) {
+ if (wlc->corestate->macstat_snapshot) {
+ kfree(wlc->corestate->macstat_snapshot); wlc->corestate->macstat_snapshot = NULL;
+ }
+ kfree(wlc->corestate);
+ wlc->corestate = NULL;
+ }
+
+ if (wlc->pub) {
+ /* free pub struct */
+ wlc_pub_mfree(osh, wlc->pub);
+ wlc->pub = NULL;
+ }
+
+ if (wlc->hw) {
+#ifdef WLC_LOW
+ if (wlc->hw->bandstate[0]) {
+ kfree(wlc->hw->bandstate[0]);
+ wlc->hw->bandstate[0] = NULL;
+ }
+#endif
+
+ /* free hw struct */
+ kfree(wlc->hw);
+ wlc->hw = NULL;
+ }
+
+ /* free the wlc */
+ kfree(wlc);
+ wlc = NULL;
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_alloc.h b/drivers/staging/brcm80211/sys/wlc_alloc.h
new file mode 100644
index 000000000000..678a2b9784f8
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_alloc.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+extern void *wlc_calloc(osl_t *osh, uint unit, uint size);
+
+extern wlc_info_t *wlc_attach_malloc(osl_t *osh, uint unit, uint *err,
+ uint devid);
+extern void wlc_detach_mfree(wlc_info_t *wlc, osl_t *osh);
+
+struct wlc_bsscfg;
+extern struct wlc_bsscfg *wlc_bsscfg_malloc(osl_t *osh, uint unit);
+extern void wlc_bsscfg_mfree(osl_t *osh, struct wlc_bsscfg *cfg);
diff --git a/drivers/staging/brcm80211/sys/wlc_ampdu.c b/drivers/staging/brcm80211/sys/wlc_ampdu.c
new file mode 100644
index 000000000000..a4e49f3c1363
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_ampdu.c
@@ -0,0 +1,1411 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <wlc_cfg.h>
+#include <bcmdefs.h>
+#include <linuxver.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+#include <sbhnddma.h>
+#include <hnddma.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_mac80211.h>
+#include <wlc_phy_hal.h>
+#include <wlc_antsel.h>
+#include <wlc_scb.h>
+#include <net/mac80211.h>
+#include <wlc_ampdu.h>
+#include <wl_export.h>
+
+#ifdef WLC_HIGH_ONLY
+#include <bcm_rpc_tp.h>
+#include <wlc_rpctx.h>
+#endif
+
+#define AMPDU_MAX_MPDU 32 /* max number of mpdus in an ampdu */
+#define AMPDU_NUM_MPDU_LEGACY 16 /* max number of mpdus in an ampdu to a legacy */
+#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
+#define AMPDU_TX_BA_DEF_WSIZE 64 /* default Tx ba window size (in pdu) */
+#define AMPDU_RX_BA_DEF_WSIZE 64 /* max Rx ba window size (in pdu) */
+#define AMPDU_RX_BA_MAX_WSIZE 64 /* default Rx ba window size (in pdu) */
+#define AMPDU_MAX_DUR 5 /* max dur of tx ampdu (in msec) */
+#define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit */
+#define AMPDU_DEF_RR_RETRY_LIMIT 2 /* default tx retry limit at reg rate */
+#define AMPDU_DEF_TXPKT_WEIGHT 2 /* default weight of ampdu in txfifo */
+#define AMPDU_DEF_FFPLD_RSVD 2048 /* default ffpld reserved bytes */
+#define AMPDU_INI_FREE 10 /* # of inis to be freed on detach */
+#define AMPDU_SCB_MAX_RELEASE 20 /* max # of mpdus released at a time */
+
+#define NUM_FFPLD_FIFO 4 /* number of fifo concerned by pre-loading */
+#define FFPLD_TX_MAX_UNFL 200 /* default value of the average number of ampdu
+ * without underflows
+ */
+#define FFPLD_MPDU_SIZE 1800 /* estimate of maximum mpdu size */
+#define FFPLD_MAX_MCS 23 /* we don't deal with mcs 32 */
+#define FFPLD_PLD_INCR 1000 /* increments in bytes */
+#define FFPLD_MAX_AMPDU_CNT 5000 /* maximum number of ampdu we
+ * accumulate between resets.
+ */
+
+#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
+
+/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
+#define AMPDU_MAX_MPDU_OVERHEAD (DOT11_FCS_LEN + DOT11_ICV_AES_LEN + AMPDU_DELIMITER_LEN + 3 \
+ + DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
+
+#ifdef BCMDBG
+u32 wl_ampdu_dbg =
+ WL_AMPDU_UPDN_VAL |
+ WL_AMPDU_ERR_VAL |
+ WL_AMPDU_TX_VAL |
+ WL_AMPDU_RX_VAL |
+ WL_AMPDU_CTL_VAL |
+ WL_AMPDU_HW_VAL | WL_AMPDU_HWTXS_VAL | WL_AMPDU_HWDBG_VAL;
+#endif
+
+/* structure to hold tx fifo information and pre-loading state
+ * counters specific to tx underflows of ampdus
+ * some counters might be redundant with the ones in wlc or ampdu structures.
+ * This allows to maintain a specific state independantly of
+ * how often and/or when the wlc counters are updated.
+ */
+typedef struct wlc_fifo_info {
+ u16 ampdu_pld_size; /* number of bytes to be pre-loaded */
+ u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1]; /* per-mcs max # of mpdus in an ampdu */
+ u16 prev_txfunfl; /* num of underflows last read from the HW macstats counter */
+ u32 accum_txfunfl; /* num of underflows since we modified pld params */
+ u32 accum_txampdu; /* num of tx ampdu since we modified pld params */
+ u32 prev_txampdu; /* previous reading of tx ampdu */
+ u32 dmaxferrate; /* estimated dma avg xfer rate in kbits/sec */
+} wlc_fifo_info_t;
+
+/* AMPDU module specific state */
+struct ampdu_info {
+ wlc_info_t *wlc; /* pointer to main wlc structure */
+ int scb_handle; /* scb cubby handle to retrieve data from scb */
+ u8 ini_enable[AMPDU_MAX_SCB_TID]; /* per-tid initiator enable/disable of ampdu */
+ u8 ba_tx_wsize; /* Tx ba window size (in pdu) */
+ u8 ba_rx_wsize; /* Rx ba window size (in pdu) */
+ u8 retry_limit; /* mpdu transmit retry limit */
+ u8 rr_retry_limit; /* mpdu transmit retry limit at regular rate */
+ u8 retry_limit_tid[AMPDU_MAX_SCB_TID]; /* per-tid mpdu transmit retry limit */
+ /* per-tid mpdu transmit retry limit at regular rate */
+ u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
+ u8 mpdu_density; /* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
+ s8 max_pdu; /* max pdus allowed in ampdu */
+ u8 dur; /* max duration of an ampdu (in msec) */
+ u8 txpkt_weight; /* weight of ampdu in txfifo; reduces rate lag */
+ u8 rx_factor; /* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
+ u32 ffpld_rsvd; /* number of bytes to reserve for preload */
+ u32 max_txlen[MCS_TABLE_SIZE][2][2]; /* max size of ampdu per mcs, bw and sgi */
+ void *ini_free[AMPDU_INI_FREE]; /* array of ini's to be freed on detach */
+ bool mfbr; /* enable multiple fallback rate */
+ u32 tx_max_funl; /* underflows should be kept such that
+ * (tx_max_funfl*underflows) < tx frames
+ */
+ wlc_fifo_info_t fifo_tb[NUM_FFPLD_FIFO]; /* table of fifo infos */
+
+#ifdef WLC_HIGH_ONLY
+ void *p;
+ tx_status_t txs;
+ bool waiting_status; /* To help sanity checks */
+#endif
+};
+
+#define AMPDU_CLEANUPFLAG_RX (0x1)
+#define AMPDU_CLEANUPFLAG_TX (0x2)
+
+#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
+#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
+
+static void wlc_ffpld_init(ampdu_info_t *ampdu);
+static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int f);
+static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f);
+
+static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
+ scb_ampdu_t *scb_ampdu,
+ u8 tid, bool override);
+static void ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu,
+ u8 tid, bool force);
+static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur);
+static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb);
+static void scb_ampdu_update_config_all(ampdu_info_t *ampdu);
+
+#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
+
+static void wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb,
+ void *p, tx_status_t *txs,
+ u32 frmtxstatus,
+ u32 frmtxstatus2);
+
+static inline u16 pkt_txh_seqnum(wlc_info_t *wlc, void *p)
+{
+ d11txh_t *txh;
+ struct dot11_header *h;
+ txh = (d11txh_t *) PKTDATA(p);
+ h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+ return ltoh16(h->seq) >> SEQNUM_SHIFT;
+}
+
+ampdu_info_t *wlc_ampdu_attach(wlc_info_t *wlc)
+{
+ ampdu_info_t *ampdu;
+ int i;
+
+ /* some code depends on packed structures */
+ ASSERT(DOT11_MAXNUMFRAGS == NBITS(u16));
+ ASSERT(ISPOWEROF2(AMPDU_TX_BA_MAX_WSIZE));
+ ASSERT(ISPOWEROF2(AMPDU_RX_BA_MAX_WSIZE));
+ ASSERT(wlc->pub->tunables->ampdunummpdu <= AMPDU_MAX_MPDU);
+ ASSERT(wlc->pub->tunables->ampdunummpdu > 0);
+
+ ampdu = kzalloc(sizeof(ampdu_info_t), GFP_ATOMIC);
+ if (!ampdu) {
+ WL_ERROR(("wl%d: wlc_ampdu_attach: out of mem\n", wlc->pub->unit));
+ return NULL;
+ }
+ ampdu->wlc = wlc;
+
+ for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
+ ampdu->ini_enable[i] = true;
+ /* Disable ampdu for VO by default */
+ ampdu->ini_enable[PRIO_8021D_VO] = false;
+ ampdu->ini_enable[PRIO_8021D_NC] = false;
+
+ /* Disable ampdu for BK by default since not enough fifo space */
+ ampdu->ini_enable[PRIO_8021D_NONE] = false;
+ ampdu->ini_enable[PRIO_8021D_BK] = false;
+
+ ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
+ ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
+ ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
+ ampdu->max_pdu = AUTO;
+ ampdu->dur = AMPDU_MAX_DUR;
+ ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
+
+ ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
+ /* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
+ if (WLCISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
+ ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
+ else
+ ampdu->rx_factor = AMPDU_RX_FACTOR_64K;
+#ifdef WLC_HIGH_ONLY
+ /* Restrict to smaller rcv size for BMAC dongle */
+ ampdu->rx_factor = AMPDU_RX_FACTOR_32K;
+#endif
+ ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
+ ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
+
+ for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
+ ampdu->retry_limit_tid[i] = ampdu->retry_limit;
+ ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
+ }
+
+ ampdu_update_max_txlen(ampdu, ampdu->dur);
+ ampdu->mfbr = false;
+ /* try to set ampdu to the default value */
+ wlc_ampdu_set(ampdu, wlc->pub->_ampdu);
+
+ ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
+ wlc_ffpld_init(ampdu);
+
+ return ampdu;
+}
+
+void wlc_ampdu_detach(ampdu_info_t *ampdu)
+{
+ int i;
+
+ if (!ampdu)
+ return;
+
+ /* free all ini's which were to be freed on callbacks which were never called */
+ for (i = 0; i < AMPDU_INI_FREE; i++) {
+ if (ampdu->ini_free[i]) {
+ kfree(ampdu->ini_free[i]);
+ }
+ }
+
+ wlc_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
+ kfree(ampdu);
+}
+
+void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb)
+{
+ scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ u8 tid;
+
+ WL_AMPDU_UPDN(("scb_ampdu_cleanup: enter\n"));
+ ASSERT(scb_ampdu);
+
+ for (tid = 0; tid < AMPDU_MAX_SCB_TID; tid++) {
+ ampdu_cleanup_tid_ini(ampdu, scb_ampdu, tid, false);
+ }
+}
+
+/* reset the ampdu state machine so that it can gracefully handle packets that were
+ * freed from the dma and tx queues during reinit
+ */
+void wlc_ampdu_reset(ampdu_info_t *ampdu)
+{
+ WL_NONE(("%s: Entering\n", __func__));
+}
+
+static void scb_ampdu_update_config(ampdu_info_t *ampdu, struct scb *scb)
+{
+ scb_ampdu_t *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ int i;
+
+ scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
+
+ /* go back to legacy size if some preloading is occuring */
+ for (i = 0; i < NUM_FFPLD_FIFO; i++) {
+ if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
+ scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
+ }
+
+ /* apply user override */
+ if (ampdu->max_pdu != AUTO)
+ scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
+
+ scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
+
+ if (scb_ampdu->max_rxlen)
+ scb_ampdu->release =
+ min_t(u8, scb_ampdu->release, scb_ampdu->max_rxlen / 1600);
+
+ scb_ampdu->release = min(scb_ampdu->release,
+ ampdu->fifo_tb[TX_AC_BE_FIFO].
+ mcs2ampdu_table[FFPLD_MAX_MCS]);
+
+ ASSERT(scb_ampdu->release);
+}
+
+void scb_ampdu_update_config_all(ampdu_info_t *ampdu)
+{
+ scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
+}
+
+static void wlc_ffpld_init(ampdu_info_t *ampdu)
+{
+ int i, j;
+ wlc_fifo_info_t *fifo;
+
+ for (j = 0; j < NUM_FFPLD_FIFO; j++) {
+ fifo = (ampdu->fifo_tb + j);
+ fifo->ampdu_pld_size = 0;
+ for (i = 0; i <= FFPLD_MAX_MCS; i++)
+ fifo->mcs2ampdu_table[i] = 255;
+ fifo->dmaxferrate = 0;
+ fifo->accum_txampdu = 0;
+ fifo->prev_txfunfl = 0;
+ fifo->accum_txfunfl = 0;
+
+ }
+}
+
+/* evaluate the dma transfer rate using the tx underflows as feedback.
+ * If necessary, increase tx fifo preloading. If not enough,
+ * decrease maximum ampdu size for each mcs till underflows stop
+ * Return 1 if pre-loading not active, -1 if not an underflow event,
+ * 0 if pre-loading module took care of the event.
+ */
+static int wlc_ffpld_check_txfunfl(wlc_info_t *wlc, int fid)
+{
+ ampdu_info_t *ampdu = wlc->ampdu;
+ u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
+ u32 txunfl_ratio;
+ u8 max_mpdu;
+ u32 current_ampdu_cnt = 0;
+ u16 max_pld_size;
+ u32 new_txunfl;
+ wlc_fifo_info_t *fifo = (ampdu->fifo_tb + fid);
+ uint xmtfifo_sz;
+ u16 cur_txunfl;
+
+ /* return if we got here for a different reason than underflows */
+ cur_txunfl =
+ wlc_read_shm(wlc,
+ M_UCODE_MACSTAT + offsetof(macstat_t, txfunfl[fid]));
+ new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
+ if (new_txunfl == 0) {
+ WL_FFPLD(("check_txunfl : TX status FRAG set but no tx underflows\n"));
+ return -1;
+ }
+ fifo->prev_txfunfl = cur_txunfl;
+
+ if (!ampdu->tx_max_funl)
+ return 1;
+
+ /* check if fifo is big enough */
+ if (wlc_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz)) {
+ WL_FFPLD(("check_txunfl : get xmtfifo_sz failed.\n"));
+ return -1;
+ }
+
+ if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
+ return 1;
+
+ max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
+ fifo->accum_txfunfl += new_txunfl;
+
+ /* we need to wait for at least 10 underflows */
+ if (fifo->accum_txfunfl < 10)
+ return 0;
+
+ WL_FFPLD(("ampdu_count %d tx_underflows %d\n",
+ current_ampdu_cnt, fifo->accum_txfunfl));
+
+ /*
+ compute the current ratio of tx unfl per ampdu.
+ When the current ampdu count becomes too
+ big while the ratio remains small, we reset
+ the current count in order to not
+ introduce too big of a latency in detecting a
+ large amount of tx underflows later.
+ */
+
+ txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
+
+ if (txunfl_ratio > ampdu->tx_max_funl) {
+ if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
+ fifo->accum_txfunfl = 0;
+ }
+ return 0;
+ }
+ max_mpdu =
+ min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
+
+ /* In case max value max_pdu is already lower than
+ the fifo depth, there is nothing more we can do.
+ */
+
+ if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
+ WL_FFPLD(("tx fifo pld : max ampdu fits in fifo\n)"));
+ fifo->accum_txfunfl = 0;
+ return 0;
+ }
+
+ if (fifo->ampdu_pld_size < max_pld_size) {
+
+ /* increment by TX_FIFO_PLD_INC bytes */
+ fifo->ampdu_pld_size += FFPLD_PLD_INCR;
+ if (fifo->ampdu_pld_size > max_pld_size)
+ fifo->ampdu_pld_size = max_pld_size;
+
+ /* update scb release size */
+ scb_ampdu_update_config_all(ampdu);
+
+ /*
+ compute a new dma xfer rate for max_mpdu @ max mcs.
+ This is the minimum dma rate that
+ can acheive no unferflow condition for the current mpdu size.
+ */
+ /* note : we divide/multiply by 100 to avoid integer overflows */
+ fifo->dmaxferrate =
+ (((phy_rate / 100) *
+ (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
+ / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
+
+ WL_FFPLD(("DMA estimated transfer rate %d; pre-load size %d\n",
+ fifo->dmaxferrate, fifo->ampdu_pld_size));
+ } else {
+
+ /* decrease ampdu size */
+ if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
+ if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
+ fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
+ AMPDU_NUM_MPDU_LEGACY - 1;
+ else
+ fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
+
+ /* recompute the table */
+ wlc_ffpld_calc_mcs2ampdu_table(ampdu, fid);
+
+ /* update scb release size */
+ scb_ampdu_update_config_all(ampdu);
+ }
+ }
+ fifo->accum_txfunfl = 0;
+ return 0;
+}
+
+static void wlc_ffpld_calc_mcs2ampdu_table(ampdu_info_t *ampdu, int f)
+{
+ int i;
+ u32 phy_rate, dma_rate, tmp;
+ u8 max_mpdu;
+ wlc_fifo_info_t *fifo = (ampdu->fifo_tb + f);
+
+ /* recompute the dma rate */
+ /* note : we divide/multiply by 100 to avoid integer overflows */
+ max_mpdu =
+ min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
+ phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
+ dma_rate =
+ (((phy_rate / 100) *
+ (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
+ / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
+ fifo->dmaxferrate = dma_rate;
+
+ /* fill up the mcs2ampdu table; do not recalc the last mcs */
+ dma_rate = dma_rate >> 7;
+ for (i = 0; i < FFPLD_MAX_MCS; i++) {
+ /* shifting to keep it within integer range */
+ phy_rate = MCS_RATE(i, true, false) >> 7;
+ if (phy_rate > dma_rate) {
+ tmp = ((fifo->ampdu_pld_size * phy_rate) /
+ ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
+ tmp = min_t(u32, tmp, 255);
+ fifo->mcs2ampdu_table[i] = (u8) tmp;
+ }
+ }
+}
+
+static void BCMFASTPATH
+wlc_ampdu_agg(ampdu_info_t *ampdu, struct scb *scb, void *p, uint prec)
+{
+ scb_ampdu_t *scb_ampdu;
+ scb_ampdu_tid_ini_t *ini;
+ u8 tid = (u8) PKTPRIO(p);
+
+ scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+
+ /* initialize initiator on first packet; sends addba req */
+ ini = SCB_AMPDU_INI(scb_ampdu, tid);
+ if (ini->magic != INI_MAGIC) {
+ ini = wlc_ampdu_init_tid_ini(ampdu, scb_ampdu, tid, false);
+ }
+ return;
+}
+
+int BCMFASTPATH
+wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, void **pdu, int prec)
+{
+ wlc_info_t *wlc;
+ osl_t *osh;
+ void *p, *pkt[AMPDU_MAX_MPDU];
+ u8 tid, ndelim;
+ int err = 0;
+ u8 preamble_type = WLC_GF_PREAMBLE;
+ u8 fbr_preamble_type = WLC_GF_PREAMBLE;
+ u8 rts_preamble_type = WLC_LONG_PREAMBLE;
+ u8 rts_fbr_preamble_type = WLC_LONG_PREAMBLE;
+
+ bool rr = true, fbr = false;
+ uint i, count = 0, fifo, seg_cnt = 0;
+ u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
+ u32 ampdu_len, maxlen = 0;
+ d11txh_t *txh = NULL;
+ u8 *plcp;
+ struct dot11_header *h;
+ struct scb *scb;
+ scb_ampdu_t *scb_ampdu;
+ scb_ampdu_tid_ini_t *ini;
+ u8 mcs = 0;
+ bool use_rts = false, use_cts = false;
+ ratespec_t rspec = 0, rspec_fallback = 0;
+ ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
+ u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
+ struct dot11_rts_frame *rts;
+ u8 rr_retry_limit;
+ wlc_fifo_info_t *f;
+ bool fbr_iscck;
+ struct ieee80211_tx_info *tx_info;
+ u16 qlen;
+
+ wlc = ampdu->wlc;
+ osh = wlc->osh;
+ p = *pdu;
+
+ ASSERT(p);
+
+ tid = (u8) PKTPRIO(p);
+ ASSERT(tid < AMPDU_MAX_SCB_TID);
+
+ f = ampdu->fifo_tb + prio2fifo[tid];
+
+ scb = wlc->pub->global_scb;
+ ASSERT(scb->magic == SCB_MAGIC);
+
+ scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ ASSERT(scb_ampdu);
+ ini = &scb_ampdu->ini[tid];
+
+ /* Let pressure continue to build ... */
+ qlen = pktq_plen(&qi->q, prec);
+ if (ini->tx_in_transit > 0 && qlen < scb_ampdu->max_pdu) {
+ return BCME_BUSY;
+ }
+
+ wlc_ampdu_agg(ampdu, scb, p, tid);
+
+ if (wlc->block_datafifo) {
+ WL_ERROR(("%s: Fifo blocked\n", __func__));
+ return BCME_BUSY;
+ }
+ rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
+ ampdu_len = 0;
+ dma_len = 0;
+ while (p) {
+ struct ieee80211_tx_rate *txrate;
+
+ tx_info = IEEE80211_SKB_CB(p);
+ txrate = tx_info->status.rates;
+
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ err = wlc_prep_pdu(wlc, p, &fifo);
+ } else {
+ WL_ERROR(("%s: AMPDU flag is off!\n", __func__));
+ *pdu = NULL;
+ err = 0;
+ break;
+ }
+
+ if (err) {
+ if (err == BCME_BUSY) {
+ WL_ERROR(("wl%d: wlc_sendampdu: prep_xdu retry; seq 0x%x\n", wlc->pub->unit, seq));
+ WLCNTINCR(ampdu->cnt->sduretry);
+ *pdu = p;
+ break;
+ }
+
+ /* error in the packet; reject it */
+ WL_AMPDU_ERR(("wl%d: wlc_sendampdu: prep_xdu rejected; seq 0x%x\n", wlc->pub->unit, seq));
+ WLCNTINCR(ampdu->cnt->sdurejected);
+
+ *pdu = NULL;
+ break;
+ }
+
+ /* pkt is good to be aggregated */
+ ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
+ txh = (d11txh_t *) PKTDATA(p);
+ plcp = (u8 *) (txh + 1);
+ h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
+ seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
+ index = TX_SEQ_TO_INDEX(seq);
+
+ /* check mcl fields and test whether it can be agg'd */
+ mcl = ltoh16(txh->MacTxControlLow);
+ mcl &= ~TXC_AMPDU_MASK;
+ fbr_iscck = !(ltoh16(txh->XtraFrameTypes) & 0x3);
+ ASSERT(!fbr_iscck);
+ txh->PreloadSize = 0; /* always default to 0 */
+
+ /* Handle retry limits */
+ if (txrate[0].count <= rr_retry_limit) {
+ txrate[0].count++;
+ rr = true;
+ fbr = false;
+ ASSERT(!fbr);
+ } else {
+ fbr = true;
+ rr = false;
+ txrate[1].count++;
+ }
+
+ /* extract the length info */
+ len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
+ : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
+
+ /* retrieve null delimiter count */
+ ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
+ seg_cnt += 1;
+
+ WL_AMPDU_TX(("wl%d: wlc_sendampdu: mpdu %d plcp_len %d\n",
+ wlc->pub->unit, count, len));
+
+ /*
+ * aggregateable mpdu. For ucode/hw agg,
+ * test whether need to break or change the epoch
+ */
+ if (count == 0) {
+ u16 fc;
+ mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
+ /* refill the bits since might be a retx mpdu */
+ mcl |= TXC_STARTMSDU;
+ rts = (struct dot11_rts_frame *)&txh->rts_frame;
+ fc = ltoh16(rts->fc);
+ if ((fc & FC_KIND_MASK) == FC_RTS) {
+ mcl |= TXC_SENDRTS;
+ use_rts = true;
+ }
+ if ((fc & FC_KIND_MASK) == FC_CTS) {
+ mcl |= TXC_SENDCTS;
+ use_cts = true;
+ }
+ } else {
+ mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
+ mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
+ }
+
+ len = roundup(len, 4);
+ ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
+
+ dma_len += (u16) pkttotlen(osh, p);
+
+ WL_AMPDU_TX(("wl%d: wlc_sendampdu: ampdu_len %d seg_cnt %d null delim %d\n", wlc->pub->unit, ampdu_len, seg_cnt, ndelim));
+
+ txh->MacTxControlLow = htol16(mcl);
+
+ /* this packet is added */
+ pkt[count++] = p;
+
+ /* patch the first MPDU */
+ if (count == 1) {
+ u8 plcp0, plcp3, is40, sgi;
+ struct ieee80211_sta *sta;
+
+ sta = tx_info->control.sta;
+
+ if (rr) {
+ plcp0 = plcp[0];
+ plcp3 = plcp[3];
+ } else {
+ plcp0 = txh->FragPLCPFallback[0];
+ plcp3 = txh->FragPLCPFallback[3];
+
+ }
+ is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
+ sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
+ mcs = plcp0 & ~MIMO_PLCP_40MHZ;
+ ASSERT(mcs < MCS_TABLE_SIZE);
+ maxlen =
+ min(scb_ampdu->max_rxlen,
+ ampdu->max_txlen[mcs][is40][sgi]);
+
+ WL_NONE(("sendampdu: sgi %d, is40 %d, mcs %d\n", sgi,
+ is40, mcs));
+
+ maxlen = 64 * 1024; /* XXX Fix me to honor real max_rxlen */
+
+ if (is40)
+ mimo_ctlchbw =
+ CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
+ ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+
+ /* rebuild the rspec and rspec_fallback */
+ rspec = RSPEC_MIMORATE;
+ rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
+ if (plcp[0] & MIMO_PLCP_40MHZ)
+ rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
+
+ if (fbr_iscck) /* CCK */
+ rspec_fallback =
+ CCK_RSPEC(CCK_PHY2MAC_RATE
+ (txh->FragPLCPFallback[0]));
+ else { /* MIMO */
+ rspec_fallback = RSPEC_MIMORATE;
+ rspec_fallback |=
+ txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
+ if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
+ rspec_fallback |=
+ (PHY_TXC1_BW_40MHZ <<
+ RSPEC_BW_SHIFT);
+ }
+
+ if (use_rts || use_cts) {
+ rts_rspec =
+ wlc_rspec_to_rts_rspec(wlc, rspec, false,
+ mimo_ctlchbw);
+ rts_rspec_fallback =
+ wlc_rspec_to_rts_rspec(wlc, rspec_fallback,
+ false, mimo_ctlchbw);
+ }
+ }
+
+ /* if (first mpdu for host agg) */
+ /* test whether to add more */
+ if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
+ (count == f->mcs2ampdu_table[mcs])) {
+ WL_AMPDU_ERR(("wl%d: PR 37644: stopping ampdu at %d for mcs %d", wlc->pub->unit, count, mcs));
+ break;
+ }
+
+ if (count == scb_ampdu->max_pdu) {
+ WL_NONE(("Stop taking from q, reached %d deep\n",
+ scb_ampdu->max_pdu));
+ break;
+ }
+
+ /* check to see if the next pkt is a candidate for aggregation */
+ p = pktq_ppeek(&qi->q, prec);
+ tx_info = IEEE80211_SKB_CB(p); /* tx_info must be checked with current p */
+
+ if (p) {
+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
+ ((u8) PKTPRIO(p) == tid)) {
+
+ plen =
+ pkttotlen(osh, p) + AMPDU_MAX_MPDU_OVERHEAD;
+ plen = max(scb_ampdu->min_len, plen);
+
+ if ((plen + ampdu_len) > maxlen) {
+ p = NULL;
+ WL_ERROR(("%s: Bogus plen #1\n",
+ __func__));
+ ASSERT(3 == 4);
+ continue;
+ }
+
+ /* check if there are enough descriptors available */
+ if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
+ WL_ERROR(("%s: No fifo space !!!!!!\n", __func__));
+ p = NULL;
+ continue;
+ }
+ p = pktq_pdeq(&qi->q, prec);
+ ASSERT(p);
+ } else {
+ p = NULL;
+ }
+ }
+ } /* end while(p) */
+
+ ini->tx_in_transit += count;
+
+ if (count) {
+ WLCNTADD(ampdu->cnt->txmpdu, count);
+
+ /* patch up the last txh */
+ txh = (d11txh_t *) PKTDATA(pkt[count - 1]);
+ mcl = ltoh16(txh->MacTxControlLow);
+ mcl &= ~TXC_AMPDU_MASK;
+ mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
+ txh->MacTxControlLow = htol16(mcl);
+
+ /* remove the null delimiter after last mpdu */
+ ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
+ txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
+ ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
+
+ /* remove the pad len from last mpdu */
+ fbr_iscck = ((ltoh16(txh->XtraFrameTypes) & 0x3) == 0);
+ len = fbr_iscck ? WLC_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
+ : WLC_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
+ ampdu_len -= roundup(len, 4) - len;
+
+ /* patch up the first txh & plcp */
+ txh = (d11txh_t *) PKTDATA(pkt[0]);
+ plcp = (u8 *) (txh + 1);
+
+ WLC_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
+ /* mark plcp to indicate ampdu */
+ WLC_SET_MIMO_PLCP_AMPDU(plcp);
+
+ /* reset the mixed mode header durations */
+ if (txh->MModeLen) {
+ u16 mmodelen =
+ wlc_calc_lsig_len(wlc, rspec, ampdu_len);
+ txh->MModeLen = htol16(mmodelen);
+ preamble_type = WLC_MM_PREAMBLE;
+ }
+ if (txh->MModeFbrLen) {
+ u16 mmfbrlen =
+ wlc_calc_lsig_len(wlc, rspec_fallback, ampdu_len);
+ txh->MModeFbrLen = htol16(mmfbrlen);
+ fbr_preamble_type = WLC_MM_PREAMBLE;
+ }
+
+ /* set the preload length */
+ if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
+ dma_len = min(dma_len, f->ampdu_pld_size);
+ txh->PreloadSize = htol16(dma_len);
+ } else
+ txh->PreloadSize = 0;
+
+ mch = ltoh16(txh->MacTxControlHigh);
+
+ /* update RTS dur fields */
+ if (use_rts || use_cts) {
+ u16 durid;
+ rts = (struct dot11_rts_frame *)&txh->rts_frame;
+ if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
+ TXC_PREAMBLE_RTS_MAIN_SHORT)
+ rts_preamble_type = WLC_SHORT_PREAMBLE;
+
+ if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
+ TXC_PREAMBLE_RTS_FB_SHORT)
+ rts_fbr_preamble_type = WLC_SHORT_PREAMBLE;
+
+ durid =
+ wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec,
+ rspec, rts_preamble_type,
+ preamble_type, ampdu_len,
+ true);
+ rts->durid = htol16(durid);
+ durid = wlc_compute_rtscts_dur(wlc, use_cts,
+ rts_rspec_fallback,
+ rspec_fallback,
+ rts_fbr_preamble_type,
+ fbr_preamble_type,
+ ampdu_len, true);
+ txh->RTSDurFallback = htol16(durid);
+ /* set TxFesTimeNormal */
+ txh->TxFesTimeNormal = rts->durid;
+ /* set fallback rate version of TxFesTimeNormal */
+ txh->TxFesTimeFallback = txh->RTSDurFallback;
+ }
+
+ /* set flag and plcp for fallback rate */
+ if (fbr) {
+ WLCNTADD(ampdu->cnt->txfbr_mpdu, count);
+ WLCNTINCR(ampdu->cnt->txfbr_ampdu);
+ mch |= TXC_AMPDU_FBR;
+ txh->MacTxControlHigh = htol16(mch);
+ WLC_SET_MIMO_PLCP_AMPDU(plcp);
+ WLC_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
+ }
+
+ WL_AMPDU_TX(("wl%d: wlc_sendampdu: count %d ampdu_len %d\n",
+ wlc->pub->unit, count, ampdu_len));
+
+ /* inform rate_sel if it this is a rate probe pkt */
+ frameid = ltoh16(txh->TxFrameID);
+ if (frameid & TXFID_RATE_PROBE_MASK) {
+ WL_ERROR(("%s: XXX what to do with TXFID_RATE_PROBE_MASK!?\n", __func__));
+ }
+#ifdef WLC_HIGH_ONLY
+ if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
+ bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
+ BCM_RPC_TP_HOST_AGG_AMPDU, true);
+#endif
+ for (i = 0; i < count; i++)
+ wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
+ ampdu->txpkt_weight);
+#ifdef WLC_HIGH_ONLY
+ if (wlc->rpc_agg & BCM_RPC_TP_HOST_AGG_AMPDU)
+ bcm_rpc_tp_agg_set(bcm_rpc_tp_get(wlc->rpc),
+ BCM_RPC_TP_HOST_AGG_AMPDU, false);
+#endif
+
+ }
+ /* endif (count) */
+ return err;
+}
+
+void BCMFASTPATH
+wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, void *p,
+ tx_status_t *txs)
+{
+ scb_ampdu_t *scb_ampdu;
+ wlc_info_t *wlc = ampdu->wlc;
+ scb_ampdu_tid_ini_t *ini;
+ u32 s1 = 0, s2 = 0;
+ struct ieee80211_tx_info *tx_info;
+
+ tx_info = IEEE80211_SKB_CB(p);
+ ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
+ ASSERT(scb);
+ ASSERT(scb->magic == SCB_MAGIC);
+ ASSERT(txs->status & TX_STATUS_AMPDU);
+ scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ ASSERT(scb_ampdu);
+ ini = SCB_AMPDU_INI(scb_ampdu, PKTPRIO(p));
+ ASSERT(ini->scb == scb);
+
+ /* BMAC_NOTE: For the split driver, second level txstatus comes later
+ * So if the ACK was received then wait for the second level else just
+ * call the first one
+ */
+ if (txs->status & TX_STATUS_ACK_RCV) {
+#ifdef WLC_LOW
+ u8 status_delay = 0;
+
+ /* wait till the next 8 bytes of txstatus is available */
+ while (((s1 =
+ R_REG(wlc->osh,
+ &wlc->regs->frmtxstatus)) & TXS_V) == 0) {
+ udelay(1);
+ status_delay++;
+ if (status_delay > 10) {
+ ASSERT(status_delay <= 10);
+ return;
+ }
+ }
+
+ ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
+ ASSERT(s1 & TX_STATUS_AMPDU);
+ s2 = R_REG(wlc->osh, &wlc->regs->frmtxstatus2);
+#else /* WLC_LOW */
+
+ /* Store the relevant information in ampdu structure */
+ WL_AMPDU_TX(("wl%d: wlc_ampdu_dotxstatus: High Recvd\n",
+ wlc->pub->unit));
+
+ ASSERT(!ampdu->p);
+ ampdu->p = p;
+ bcopy(txs, &ampdu->txs, sizeof(tx_status_t));
+ ampdu->waiting_status = true;
+ return;
+#endif /* WLC_LOW */
+ }
+
+ wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
+ wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
+}
+
+#ifdef WLC_HIGH_ONLY
+void wlc_ampdu_txstatus_complete(ampdu_info_t *ampdu, u32 s1, u32 s2)
+{
+ WL_AMPDU_TX(("wl%d: wlc_ampdu_txstatus_complete: High Recvd 0x%x 0x%x p:%p\n", ampdu->wlc->pub->unit, s1, s2, ampdu->p));
+
+ ASSERT(ampdu->waiting_status);
+
+ /* The packet may have been freed if the SCB went away, if so, then still free the
+ * DMA chain
+ */
+ if (ampdu->p) {
+ struct ieee80211_tx_info *tx_info;
+ struct scb *scb;
+
+ tx_info = IEEE80211_SKB_CB(ampdu->p);
+ scb = (struct scb *)tx_info->control.sta->drv_priv;
+
+ wlc_ampdu_dotxstatus_complete(ampdu, scb, ampdu->p, &ampdu->txs,
+ s1, s2);
+ ampdu->p = NULL;
+ }
+
+ ampdu->waiting_status = false;
+}
+#endif /* WLC_HIGH_ONLY */
+void rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
+ tx_status_t *txs, u8 mcs);
+
+void
+rate_status(wlc_info_t *wlc, struct ieee80211_tx_info *tx_info,
+ tx_status_t *txs, u8 mcs)
+{
+ struct ieee80211_tx_rate *txrate = tx_info->status.rates;
+ int i;
+
+ /* clear the rest of the rates */
+ for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
+ txrate[i].idx = -1;
+ txrate[i].count = 0;
+ }
+}
+
+extern void wlc_txq_enq(wlc_info_t *wlc, struct scb *scb, void *sdu,
+ uint prec);
+
+#define SHORTNAME "AMPDU status"
+
+static void BCMFASTPATH
+wlc_ampdu_dotxstatus_complete(ampdu_info_t *ampdu, struct scb *scb, void *p,
+ tx_status_t *txs, u32 s1, u32 s2)
+{
+ scb_ampdu_t *scb_ampdu;
+ wlc_info_t *wlc = ampdu->wlc;
+ scb_ampdu_tid_ini_t *ini;
+ u8 bitmap[8], queue, tid;
+ d11txh_t *txh;
+ u8 *plcp;
+ struct dot11_header *h;
+ u16 seq, start_seq = 0, bindex, index, mcl;
+ u8 mcs = 0;
+ bool ba_recd = false, ack_recd = false;
+ u8 suc_mpdu = 0, tot_mpdu = 0;
+ uint supr_status;
+ bool update_rate = true, retry = true, tx_error = false;
+ u16 mimoantsel = 0;
+ u8 antselid = 0;
+ u8 retry_limit, rr_retry_limit;
+ struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
+
+#ifdef BCMDBG
+ u8 hole[AMPDU_MAX_MPDU];
+ bzero(hole, sizeof(hole));
+#endif
+
+ ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
+ ASSERT(txs->status & TX_STATUS_AMPDU);
+
+ scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ ASSERT(scb_ampdu);
+
+ tid = (u8) PKTPRIO(p);
+
+ ini = SCB_AMPDU_INI(scb_ampdu, tid);
+ retry_limit = ampdu->retry_limit_tid[tid];
+ rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
+
+ ASSERT(ini->scb == scb);
+
+ bzero(bitmap, sizeof(bitmap));
+ queue = txs->frameid & TXFID_QUEUE_MASK;
+ ASSERT(queue < AC_COUNT);
+
+ supr_status = txs->status & TX_STATUS_SUPR_MASK;
+
+ if (txs->status & TX_STATUS_ACK_RCV) {
+ if (TX_STATUS_SUPR_UF == supr_status) {
+ update_rate = false;
+ }
+
+ ASSERT(txs->status & TX_STATUS_INTERMEDIATE);
+ start_seq = txs->sequence >> SEQNUM_SHIFT;
+ bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
+ TX_STATUS_BA_BMAP03_SHIFT;
+
+ ASSERT(!(s1 & TX_STATUS_INTERMEDIATE));
+ ASSERT(s1 & TX_STATUS_AMPDU);
+
+ bitmap[0] |=
+ (s1 & TX_STATUS_BA_BMAP47_MASK) <<
+ TX_STATUS_BA_BMAP47_SHIFT;
+ bitmap[1] = (s1 >> 8) & 0xff;
+ bitmap[2] = (s1 >> 16) & 0xff;
+ bitmap[3] = (s1 >> 24) & 0xff;
+
+ bitmap[4] = s2 & 0xff;
+ bitmap[5] = (s2 >> 8) & 0xff;
+ bitmap[6] = (s2 >> 16) & 0xff;
+ bitmap[7] = (s2 >> 24) & 0xff;
+
+ ba_recd = true;
+ } else {
+ WLCNTINCR(ampdu->cnt->noba);
+ if (supr_status) {
+ update_rate = false;
+ if (supr_status == TX_STATUS_SUPR_BADCH) {
+ WL_ERROR(("%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
+ } else {
+ if (supr_status == TX_STATUS_SUPR_FRAG)
+ WL_NONE(("%s: AMPDU frag err\n",
+ __func__));
+ else
+ WL_ERROR(("%s: wlc_ampdu_dotxstatus: supr_status 0x%x\n", __func__, supr_status));
+ }
+ /* no need to retry for badch; will fail again */
+ if (supr_status == TX_STATUS_SUPR_BADCH ||
+ supr_status == TX_STATUS_SUPR_EXPTIME) {
+ retry = false;
+ WLCNTINCR(wlc->pub->_cnt->txchanrej);
+ } else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
+
+ WLCNTINCR(wlc->pub->_cnt->txexptime);
+
+ /* TX underflow : try tuning pre-loading or ampdu size */
+ } else if (supr_status == TX_STATUS_SUPR_FRAG) {
+ /* if there were underflows, but pre-loading is not active,
+ notify rate adaptation.
+ */
+ if (wlc_ffpld_check_txfunfl(wlc, prio2fifo[tid])
+ > 0) {
+ tx_error = true;
+#ifdef WLC_HIGH_ONLY
+ /* With BMAC, TX Underflows should not happen */
+ WL_ERROR(("wl%d: BMAC TX Underflow?",
+ wlc->pub->unit));
+#endif
+ }
+ }
+ } else if (txs->phyerr) {
+ update_rate = false;
+ WLCNTINCR(wlc->pub->_cnt->txphyerr);
+ WL_ERROR(("wl%d: wlc_ampdu_dotxstatus: tx phy error (0x%x)\n", wlc->pub->unit, txs->phyerr));
+
+#ifdef BCMDBG
+ if (WL_ERROR_ON()) {
+ prpkt("txpkt (AMPDU)", wlc->osh, p);
+ wlc_print_txdesc((d11txh_t *) PKTDATA(p));
+ wlc_print_txstatus(txs);
+ }
+#endif /* BCMDBG */
+ }
+ }
+
+ /* loop through all pkts and retry if not acked */
+ while (p) {
+ tx_info = IEEE80211_SKB_CB(p);
+ ASSERT(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
+ txh = (d11txh_t *) PKTDATA(p);
+ mcl = ltoh16(txh->MacTxControlLow);
+ plcp = (u8 *) (txh + 1);
+ h = (struct dot11_header *)(plcp + D11_PHY_HDR_LEN);
+ seq = ltoh16(h->seq) >> SEQNUM_SHIFT;
+
+ if (tot_mpdu == 0) {
+ mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
+ mimoantsel = ltoh16(txh->ABI_MimoAntSel);
+ }
+
+ index = TX_SEQ_TO_INDEX(seq);
+ ack_recd = false;
+ if (ba_recd) {
+ bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
+
+ WL_AMPDU_TX(("%s: tid %d seq is %d, start_seq is %d, "
+ "bindex is %d set %d, index %d\n",
+ __func__, tid, seq, start_seq, bindex,
+ isset(bitmap, bindex), index));
+
+ /* if acked then clear bit and free packet */
+ if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
+ && isset(bitmap, bindex)) {
+ ini->tx_in_transit--;
+ ini->txretry[index] = 0;
+
+ /* ampdu_ack_len: number of acked aggregated frames */
+ /* ampdu_ack_map: block ack bit map for the aggregation */
+ /* ampdu_len: number of aggregated frames */
+ rate_status(wlc, tx_info, txs, mcs);
+ tx_info->flags |= IEEE80211_TX_STAT_ACK;
+ tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
+
+ /* XXX TODO: Make these accurate. */
+ tx_info->status.ampdu_ack_len =
+ (txs->
+ status & TX_STATUS_FRM_RTX_MASK) >>
+ TX_STATUS_FRM_RTX_SHIFT;
+ tx_info->status.ampdu_len =
+ (txs->
+ status & TX_STATUS_FRM_RTX_MASK) >>
+ TX_STATUS_FRM_RTX_SHIFT;
+
+ PKTPULL(p, D11_PHY_HDR_LEN);
+ PKTPULL(p, D11_TXH_LEN);
+
+ ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
+ p);
+ ack_recd = true;
+ suc_mpdu++;
+ }
+ }
+ /* either retransmit or send bar if ack not recd */
+ if (!ack_recd) {
+ struct ieee80211_tx_rate *txrate =
+ tx_info->status.rates;
+ if (retry && (txrate[0].count < (int)retry_limit)) {
+ ini->txretry[index]++;
+ ini->tx_in_transit--;
+ /* Use high prededence for retransmit to give some punch */
+ /* wlc_txq_enq(wlc, scb, p, WLC_PRIO_TO_PREC(tid)); */
+ wlc_txq_enq(wlc, scb, p,
+ WLC_PRIO_TO_HI_PREC(tid));
+ } else {
+ /* Retry timeout */
+ ini->tx_in_transit--;
+ ieee80211_tx_info_clear_status(tx_info);
+ tx_info->flags |=
+ IEEE80211_TX_STAT_AMPDU_NO_BACK;
+ PKTPULL(p, D11_PHY_HDR_LEN);
+ PKTPULL(p, D11_TXH_LEN);
+ WL_ERROR(("%s: BA Timeout, seq %d, in_transit %d\n", SHORTNAME, seq, ini->tx_in_transit));
+ ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
+ p);
+ }
+ }
+ tot_mpdu++;
+
+ /* break out if last packet of ampdu */
+ if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
+ TXC_AMPDU_LAST)
+ break;
+
+ p = GETNEXTTXP(wlc, queue);
+ if (p == NULL) {
+ ASSERT(p);
+ break;
+ }
+ }
+ wlc_send_q(wlc, wlc->active_queue);
+
+ /* update rate state */
+ if (WLANTSEL_ENAB(wlc))
+ antselid = wlc_antsel_antsel2id(wlc->asi, mimoantsel);
+
+ wlc_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
+}
+
+static void
+ampdu_cleanup_tid_ini(ampdu_info_t *ampdu, scb_ampdu_t *scb_ampdu, u8 tid,
+ bool force)
+{
+ scb_ampdu_tid_ini_t *ini;
+ ini = SCB_AMPDU_INI(scb_ampdu, tid);
+ if (!ini)
+ return;
+
+ WL_AMPDU_CTL(("wl%d: ampdu_cleanup_tid_ini: tid %d\n",
+ ampdu->wlc->pub->unit, tid));
+
+ if (ini->tx_in_transit && !force)
+ return;
+
+ scb_ampdu = SCB_AMPDU_CUBBY(ampdu, ini->scb);
+ ASSERT(ini == &scb_ampdu->ini[ini->tid]);
+
+ /* free all buffered tx packets */
+ pktq_pflush(ampdu->wlc->osh, &scb_ampdu->txq, ini->tid, true, NULL, 0);
+}
+
+/* initialize the initiator code for tid */
+static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(ampdu_info_t *ampdu,
+ scb_ampdu_t *scb_ampdu,
+ u8 tid, bool override)
+{
+ scb_ampdu_tid_ini_t *ini;
+
+ ASSERT(scb_ampdu);
+ ASSERT(scb_ampdu->scb);
+ ASSERT(SCB_AMPDU(scb_ampdu->scb));
+ ASSERT(tid < AMPDU_MAX_SCB_TID);
+
+ /* check for per-tid control of ampdu */
+ if (!ampdu->ini_enable[tid]) {
+ WL_ERROR(("%s: Rejecting tid %d\n", __func__, tid));
+ return NULL;
+ }
+
+ ini = SCB_AMPDU_INI(scb_ampdu, tid);
+ ini->tid = tid;
+ ini->scb = scb_ampdu->scb;
+ ini->magic = INI_MAGIC;
+ WLCNTINCR(ampdu->cnt->txaddbareq);
+
+ return ini;
+}
+
+int wlc_ampdu_set(ampdu_info_t *ampdu, bool on)
+{
+ wlc_info_t *wlc = ampdu->wlc;
+
+ wlc->pub->_ampdu = false;
+
+ if (on) {
+ if (!N_ENAB(wlc->pub)) {
+ WL_AMPDU_ERR(("wl%d: driver not nmode enabled\n",
+ wlc->pub->unit));
+ return BCME_UNSUPPORTED;
+ }
+ if (!wlc_ampdu_cap(ampdu)) {
+ WL_AMPDU_ERR(("wl%d: device not ampdu capable\n",
+ wlc->pub->unit));
+ return BCME_UNSUPPORTED;
+ }
+ wlc->pub->_ampdu = on;
+ }
+
+ return 0;
+}
+
+bool wlc_ampdu_cap(ampdu_info_t *ampdu)
+{
+ if (WLC_PHY_11N_CAP(ampdu->wlc->band))
+ return true;
+ else
+ return false;
+}
+
+static void ampdu_update_max_txlen(ampdu_info_t *ampdu, u8 dur)
+{
+ u32 rate, mcs;
+
+ for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
+ /* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
+ /* 20MHz, No SGI */
+ rate = MCS_RATE(mcs, false, false);
+ ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
+ /* 40 MHz, No SGI */
+ rate = MCS_RATE(mcs, true, false);
+ ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
+ /* 20MHz, SGI */
+ rate = MCS_RATE(mcs, false, true);
+ ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
+ /* 40 MHz, SGI */
+ rate = MCS_RATE(mcs, true, true);
+ ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
+ }
+}
+
+u8 BCMFASTPATH
+wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
+ ratespec_t rspec, int phylen)
+{
+ scb_ampdu_t *scb_ampdu;
+ int bytes, cnt, tmp;
+ u8 tx_density;
+
+ ASSERT(scb);
+ ASSERT(SCB_AMPDU(scb));
+
+ scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+ ASSERT(scb_ampdu);
+
+ if (scb_ampdu->mpdu_density == 0)
+ return 0;
+
+ /* RSPEC2RATE is in kbps units ==> ~RSPEC2RATE/2^13 is in bytes/usec
+ density x is in 2^(x-4) usec
+ ==> # of bytes needed for req density = rate/2^(17-x)
+ ==> # of null delimiters = ceil(ceil(rate/2^(17-x)) - phylen)/4)
+ */
+
+ tx_density = scb_ampdu->mpdu_density;
+
+ ASSERT(tx_density <= AMPDU_MAX_MPDU_DENSITY);
+ tmp = 1 << (17 - tx_density);
+ bytes = CEIL(RSPEC2RATE(rspec), tmp);
+
+ if (bytes > phylen) {
+ cnt = CEIL(bytes - phylen, AMPDU_DELIMITER_LEN);
+ ASSERT(cnt <= 255);
+ return (u8) cnt;
+ } else
+ return 0;
+}
+
+void wlc_ampdu_macaddr_upd(wlc_info_t *wlc)
+{
+ char template[T_RAM_ACCESS_SZ * 2];
+
+ /* driver needs to write the ta in the template; ta is at offset 16 */
+ bzero(template, sizeof(template));
+ bcopy((char *)wlc->pub->cur_etheraddr.octet, template, ETHER_ADDR_LEN);
+ wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
+ template);
+}
+
+bool wlc_aggregatable(wlc_info_t *wlc, u8 tid)
+{
+ return wlc->ampdu->ini_enable[tid];
+}
+
+void wlc_ampdu_shm_upd(ampdu_info_t *ampdu)
+{
+ wlc_info_t *wlc = ampdu->wlc;
+
+ /* Extend ucode internal watchdog timer to match larger received frames */
+ if ((ampdu->rx_factor & HT_PARAMS_RX_FACTOR_MASK) ==
+ AMPDU_RX_FACTOR_64K) {
+ wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
+ wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
+ } else {
+ wlc_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
+ wlc_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
+ }
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_ampdu.h b/drivers/staging/brcm80211/sys/wlc_ampdu.h
new file mode 100644
index 000000000000..c721b16cc706
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_ampdu.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_ampdu_h_
+#define _wlc_ampdu_h_
+
+extern ampdu_info_t *wlc_ampdu_attach(wlc_info_t *wlc);
+extern void wlc_ampdu_detach(ampdu_info_t *ampdu);
+extern bool wlc_ampdu_cap(ampdu_info_t *ampdu);
+extern int wlc_ampdu_set(ampdu_info_t *ampdu, bool on);
+extern int wlc_sendampdu(ampdu_info_t *ampdu, wlc_txq_info_t *qi, void **aggp,
+ int prec);
+extern void wlc_ampdu_dotxstatus(ampdu_info_t *ampdu, struct scb *scb, void *p,
+ tx_status_t *txs);
+extern void wlc_ampdu_reset(ampdu_info_t *ampdu);
+extern void wlc_ampdu_macaddr_upd(wlc_info_t *wlc);
+extern void wlc_ampdu_shm_upd(ampdu_info_t *ampdu);
+
+extern u8 wlc_ampdu_null_delim_cnt(ampdu_info_t *ampdu, struct scb *scb,
+ ratespec_t rspec, int phylen);
+extern void scb_ampdu_cleanup(ampdu_info_t *ampdu, struct scb *scb);
+#ifdef WLC_HIGH_ONLY
+extern void wlc_ampdu_txstatus_complete(ampdu_info_t *ampdu, u32 s1,
+ u32 s2);
+#endif
+
+#endif /* _wlc_ampdu_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_antsel.c b/drivers/staging/brcm80211/sys/wlc_antsel.c
new file mode 100644
index 000000000000..5ff8831d2fa8
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_antsel.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <wlc_cfg.h>
+
+#ifdef WLANTSEL
+
+#include <linux/kernel.h>
+#include <linuxver.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <wlioctl.h>
+
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_key.h>
+#include <wlc_pub.h>
+#include <wl_dbg.h>
+#include <wlc_mac80211.h>
+#include <wlc_bmac.h>
+#include <wlc_phy_hal.h>
+#include <wl_export.h>
+#include <wlc_antsel.h>
+#include <wlc_phy_shim.h>
+
+/* useful macros */
+#define WLC_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
+#define WLC_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
+#define WLC_ANTIDX_11N(ant) (((WLC_ANTSEL_11N_0(ant)) << 2) + (WLC_ANTSEL_11N_1(ant)))
+#define WLC_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
+#define WLC_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)
+
+/* antenna switch */
+/* defines for no boardlevel antenna diversity */
+#define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */
+
+/* 2x3 antdiv defines and tables for GPIO communication */
+#define ANT_SELCFG_NUM_2x3 3
+#define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */
+
+/* 2x4 antdiv rev4 defines and tables for GPIO communication */
+#define ANT_SELCFG_NUM_2x4 4
+#define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */
+
+/* static functions */
+static int wlc_antsel_cfgupd(antsel_info_t *asi, wlc_antselcfg_t *antsel);
+static u8 wlc_antsel_id2antcfg(antsel_info_t *asi, u8 id);
+static u16 wlc_antsel_antcfg2antsel(antsel_info_t *asi, u8 ant_cfg);
+static void wlc_antsel_init_cfg(antsel_info_t *asi, wlc_antselcfg_t *antsel,
+ bool auto_sel);
+
+const u16 mimo_2x4_div_antselpat_tbl[] = {
+ 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */
+ 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */
+ 0, 0, 0, 0, /* n.a. */
+ 0, 0, 0, 0 /* n.a. */
+};
+
+const u8 mimo_2x4_div_antselid_tbl[16] = {
+ 0, 0, 0, 0, 0, 2, 3, 0,
+ 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */
+};
+
+const u16 mimo_2x3_div_antselpat_tbl[] = {
+ 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */
+ 16, 16, 16, 16, /* n.a. */
+ 16, 2, 16, 16, /* ant0: 2 ant1: 1 */
+ 16, 16, 16, 16 /* n.a. */
+};
+
+const u8 mimo_2x3_div_antselid_tbl[16] = {
+ 0, 1, 2, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */
+};
+
+antsel_info_t *wlc_antsel_attach(wlc_info_t *wlc, osl_t *osh,
+ wlc_pub_t *pub,
+ wlc_hw_info_t *wlc_hw) {
+ antsel_info_t *asi;
+
+ asi = kzalloc(sizeof(antsel_info_t), GFP_ATOMIC);
+ if (!asi) {
+ WL_ERROR(("wl%d: wlc_antsel_attach: out of mem\n", pub->unit));
+ return NULL;
+ }
+
+ asi->wlc = wlc;
+ asi->pub = pub;
+ asi->antsel_type = ANTSEL_NA;
+ asi->antsel_avail = false;
+ asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch");
+
+ if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
+ switch (asi->antsel_antswitch) {
+ case ANTSWITCH_TYPE_1:
+ case ANTSWITCH_TYPE_2:
+ case ANTSWITCH_TYPE_3:
+ /* 4321/2 board with 2x3 switch logic */
+ asi->antsel_type = ANTSEL_2x3;
+ /* Antenna selection availability */
+ if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) ||
+ ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) {
+ asi->antsel_avail = true;
+ } else
+ if (((u16) getintvar(asi->pub->vars, "aa2g") ==
+ 3)
+ || ((u16) getintvar(asi->pub->vars, "aa5g")
+ == 3)) {
+ asi->antsel_avail = false;
+ } else {
+ asi->antsel_avail = false;
+ WL_ERROR(("wlc_antsel_attach: 2o3 board cfg invalid\n"));
+ ASSERT(0);
+ }
+ break;
+ default:
+ break;
+ }
+ } else if ((asi->pub->sromrev == 4) &&
+ ((u16) getintvar(asi->pub->vars, "aa2g") == 7) &&
+ ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) {
+ /* hack to match old 4321CB2 cards with 2of3 antenna switch */
+ asi->antsel_type = ANTSEL_2x3;
+ asi->antsel_avail = true;
+ } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
+ asi->antsel_type = ANTSEL_2x4;
+ asi->antsel_avail = true;
+ }
+
+ /* Set the antenna selection type for the low driver */
+ wlc_bmac_antsel_type_set(wlc_hw, asi->antsel_type);
+
+ /* Init (auto/manual) antenna selection */
+ wlc_antsel_init_cfg(asi, &asi->antcfg_11n, true);
+ wlc_antsel_init_cfg(asi, &asi->antcfg_cur, true);
+
+ return asi;
+}
+
+void wlc_antsel_detach(antsel_info_t *asi)
+{
+ if (!asi)
+ return;
+
+ kfree(asi);
+}
+
+void wlc_antsel_init(antsel_info_t *asi)
+{
+ if ((asi->antsel_type == ANTSEL_2x3) ||
+ (asi->antsel_type == ANTSEL_2x4))
+ wlc_antsel_cfgupd(asi, &asi->antcfg_11n);
+}
+
+/* boardlevel antenna selection: init antenna selection structure */
+static void
+wlc_antsel_init_cfg(antsel_info_t *asi, wlc_antselcfg_t *antsel,
+ bool auto_sel)
+{
+ if (asi->antsel_type == ANTSEL_2x3) {
+ u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
+ ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
+ antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
+ antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
+ antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
+ antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
+ antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
+
+ } else if (asi->antsel_type == ANTSEL_2x4) {
+
+ antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
+ antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
+ antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
+ antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
+ antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
+
+ } else { /* no antenna selection available */
+
+ antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
+ antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
+ antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
+ antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
+ antsel->num_antcfg = 0;
+ }
+}
+
+void BCMFASTPATH
+wlc_antsel_antcfg_get(antsel_info_t *asi, bool usedef, bool sel,
+ u8 antselid, u8 fbantselid, u8 *antcfg,
+ u8 *fbantcfg)
+{
+ u8 ant;
+
+ /* if use default, assign it and return */
+ if (usedef) {
+ *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
+ *fbantcfg = *antcfg;
+ return;
+ }
+
+ if (!sel) {
+ *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
+ *fbantcfg = *antcfg;
+
+ } else {
+ ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
+ if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
+ *antcfg = wlc_antsel_id2antcfg(asi, antselid);
+ *fbantcfg = wlc_antsel_id2antcfg(asi, fbantselid);
+ } else {
+ *antcfg =
+ asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
+ *fbantcfg = *antcfg;
+ }
+ }
+ return;
+}
+
+/* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
+u8 wlc_antsel_antsel2id(antsel_info_t *asi, u16 antsel)
+{
+ u8 antselid = 0;
+
+ if (asi->antsel_type == ANTSEL_2x4) {
+ /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+ antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
+ return antselid;
+
+ } else if (asi->antsel_type == ANTSEL_2x3) {
+ /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+ antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
+ return antselid;
+ }
+
+ return antselid;
+}
+
+/* boardlevel antenna selection: convert id to ant_cfg */
+static u8 wlc_antsel_id2antcfg(antsel_info_t *asi, u8 id)
+{
+ u8 antcfg = ANT_SELCFG_DEF_2x2;
+
+ if (asi->antsel_type == ANTSEL_2x4) {
+ /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+ antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
+ return antcfg;
+
+ } else if (asi->antsel_type == ANTSEL_2x3) {
+ /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+ antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
+ return antcfg;
+ }
+
+ return antcfg;
+}
+
+/* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */
+static u16 wlc_antsel_antcfg2antsel(antsel_info_t *asi, u8 ant_cfg)
+{
+ u8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg));
+ u16 mimo_antsel = 0;
+
+ if (asi->antsel_type == ANTSEL_2x4) {
+ /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+ mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
+ return mimo_antsel;
+
+ } else if (asi->antsel_type == ANTSEL_2x3) {
+ /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+ mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
+ return mimo_antsel;
+ }
+
+ return mimo_antsel;
+}
+
+/* boardlevel antenna selection: ucode interface control */
+static int wlc_antsel_cfgupd(antsel_info_t *asi, wlc_antselcfg_t *antsel)
+{
+ wlc_info_t *wlc = asi->wlc;
+ u8 ant_cfg;
+ u16 mimo_antsel;
+
+ ASSERT(asi->antsel_type != ANTSEL_NA);
+
+ /* 1) Update TX antconfig for all frames that are not unicast data
+ * (aka default TX)
+ */
+ ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
+ mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
+ wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
+ /* Update driver stats for currently selected default tx/rx antenna config */
+ asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
+
+ /* 2) Update RX antconfig for all frames that are not unicast data
+ * (aka default RX)
+ */
+ ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
+ mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
+ wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
+ /* Update driver stats for currently selected default tx/rx antenna config */
+ asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
+
+ return 0;
+}
+
+#endif /* WLANTSEL */
diff --git a/drivers/staging/brcm80211/sys/wlc_antsel.h b/drivers/staging/brcm80211/sys/wlc_antsel.h
new file mode 100644
index 000000000000..1d048bbea946
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_antsel.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_antsel_h_
+#define _wlc_antsel_h_
+extern antsel_info_t *wlc_antsel_attach(wlc_info_t *wlc, osl_t *osh,
+ wlc_pub_t *pub,
+ wlc_hw_info_t *wlc_hw);
+extern void wlc_antsel_detach(antsel_info_t *asi);
+extern void wlc_antsel_init(antsel_info_t *asi);
+extern void wlc_antsel_antcfg_get(antsel_info_t *asi, bool usedef, bool sel,
+ u8 id, u8 fbid, u8 *antcfg,
+ u8 *fbantcfg);
+extern u8 wlc_antsel_antsel2id(antsel_info_t *asi, u16 antsel);
+#endif /* _wlc_antsel_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_bmac.c b/drivers/staging/brcm80211/sys/wlc_bmac.c
new file mode 100644
index 000000000000..b70f9d099233
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_bmac.c
@@ -0,0 +1,4206 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WLC_LOW
+#error "This file needs WLC_LOW"
+#endif
+
+#include <linux/kernel.h>
+#include <wlc_cfg.h>
+#include <linuxver.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <proto/802.11.h>
+#include <bcmwifi.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+#include <sbconfig.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <hnddma.h>
+#include <hndpmu.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_pub.h>
+#include <wlc_channel.h>
+#include <bcmsrom.h>
+#include <wlc_key.h>
+/* BMAC_NOTE: a WLC_HIGH compile include of wlc.h adds in more structures and type
+ * dependencies. Need to include these to files to allow a clean include of wlc.h
+ * with WLC_HIGH defined.
+ * At some point we may be able to skip the include of wlc.h and instead just
+ * define a stub wlc_info and band struct to allow rpc calls to get the rpc handle.
+ */
+#include <wlc_mac80211.h>
+#include <wlc_bmac.h>
+#include <wlc_phy_shim.h>
+#include <wlc_phy_hal.h>
+#include <wl_export.h>
+#include "wl_ucode.h"
+#include "d11ucode_ext.h"
+#ifdef BCMSDIO
+#include <bcmsdh.h>
+#endif
+#include <bcmotp.h>
+
+/* BMAC_NOTE: With WLC_HIGH defined, some fns in this file make calls to high level
+ * functions defined in the headers below. We should be eliminating those calls and
+ * will be able to delete these include lines.
+ */
+#include <wlc_antsel.h>
+
+#include <pcie_core.h>
+
+#include <wlc_alloc.h>
+
+#define TIMER_INTERVAL_WATCHDOG_BMAC 1000 /* watchdog timer, in unit of ms */
+
+#define SYNTHPU_DLY_APHY_US 3700 /* a phy synthpu_dly time in us */
+#define SYNTHPU_DLY_BPHY_US 1050 /* b/g phy synthpu_dly time in us, default */
+#define SYNTHPU_DLY_NPHY_US 2048 /* n phy REV3 synthpu_dly time in us, default */
+#define SYNTHPU_DLY_LPPHY_US 300 /* lpphy synthpu_dly time in us */
+
+#define SYNTHPU_DLY_PHY_US_QT 100 /* QT synthpu_dly time in us */
+
+#ifndef BMAC_DUP_TO_REMOVE
+#define WLC_RM_WAIT_TX_SUSPEND 4 /* Wait Tx Suspend */
+
+#define ANTCNT 10 /* vanilla M_MAX_ANTCNT value */
+
+#endif /* BMAC_DUP_TO_REMOVE */
+
+#define DMAREG(wlc_hw, direction, fifonum) (D11REV_LT(wlc_hw->corerev, 11) ? \
+ ((direction == DMA_TX) ? \
+ (void *)&(wlc_hw->regs->fifo.f32regs.dmaregs[fifonum].xmt) : \
+ (void *)&(wlc_hw->regs->fifo.f32regs.dmaregs[fifonum].rcv)) : \
+ ((direction == DMA_TX) ? \
+ (void *)&(wlc_hw->regs->fifo.f64regs[fifonum].dmaxmt) : \
+ (void *)&(wlc_hw->regs->fifo.f64regs[fifonum].dmarcv)))
+
+/*
+ * The following table lists the buffer memory allocated to xmt fifos in HW.
+ * the size is in units of 256bytes(one block), total size is HW dependent
+ * ucode has default fifo partition, sw can overwrite if necessary
+ *
+ * This is documented in twiki under the topic UcodeTxFifo. Please ensure
+ * the twiki is updated before making changes.
+ */
+
+#define XMTFIFOTBL_STARTREV 20 /* Starting corerev for the fifo size table */
+
+static u16 xmtfifo_sz[][NFIFO] = {
+ {20, 192, 192, 21, 17, 5}, /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */
+ {9, 58, 22, 14, 14, 5}, /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */
+ {20, 192, 192, 21, 17, 5}, /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */
+ {20, 192, 192, 21, 17, 5}, /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */
+ {9, 58, 22, 14, 14, 5}, /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */
+};
+
+static void wlc_clkctl_clk(wlc_hw_info_t *wlc, uint mode);
+static void wlc_coreinit(wlc_info_t *wlc);
+
+/* used by wlc_wakeucode_init() */
+static void wlc_write_inits(wlc_hw_info_t *wlc_hw, const d11init_t *inits);
+static void wlc_ucode_write(wlc_hw_info_t *wlc_hw, const u32 ucode[],
+ const uint nbytes);
+static void wlc_ucode_download(wlc_hw_info_t *wlc);
+static void wlc_ucode_txant_set(wlc_hw_info_t *wlc_hw);
+
+/* used by wlc_dpc() */
+static bool wlc_bmac_dotxstatus(wlc_hw_info_t *wlc, tx_status_t *txs,
+ u32 s2);
+static bool wlc_bmac_txstatus_corerev4(wlc_hw_info_t *wlc);
+static bool wlc_bmac_txstatus(wlc_hw_info_t *wlc, bool bound, bool *fatal);
+static bool wlc_bmac_recv(wlc_hw_info_t *wlc_hw, uint fifo, bool bound);
+
+/* used by wlc_down() */
+static void wlc_flushqueues(wlc_info_t *wlc);
+
+static void wlc_write_mhf(wlc_hw_info_t *wlc_hw, u16 *mhfs);
+static void wlc_mctrl_reset(wlc_hw_info_t *wlc_hw);
+static void wlc_corerev_fifofixup(wlc_hw_info_t *wlc_hw);
+
+/* Low Level Prototypes */
+static u16 wlc_bmac_read_objmem(wlc_hw_info_t *wlc_hw, uint offset,
+ u32 sel);
+static void wlc_bmac_write_objmem(wlc_hw_info_t *wlc_hw, uint offset, u16 v,
+ u32 sel);
+static bool wlc_bmac_attach_dmapio(wlc_info_t *wlc, uint j, bool wme);
+static void wlc_bmac_detach_dmapio(wlc_hw_info_t *wlc_hw);
+static void wlc_ucode_bsinit(wlc_hw_info_t *wlc_hw);
+static bool wlc_validboardtype(wlc_hw_info_t *wlc);
+static bool wlc_isgoodchip(wlc_hw_info_t *wlc_hw);
+static char *wlc_get_macaddr(wlc_hw_info_t *wlc_hw);
+static void wlc_mhfdef(wlc_info_t *wlc, u16 *mhfs, u16 mhf2_init);
+static void wlc_mctrl_write(wlc_hw_info_t *wlc_hw);
+static void wlc_ucode_mute_override_set(wlc_hw_info_t *wlc_hw);
+static void wlc_ucode_mute_override_clear(wlc_hw_info_t *wlc_hw);
+static u32 wlc_wlintrsoff(wlc_info_t *wlc);
+static void wlc_wlintrsrestore(wlc_info_t *wlc, u32 macintmask);
+static void wlc_gpio_init(wlc_info_t *wlc);
+static void wlc_write_hw_bcntemplate0(wlc_hw_info_t *wlc_hw, void *bcn,
+ int len);
+static void wlc_write_hw_bcntemplate1(wlc_hw_info_t *wlc_hw, void *bcn,
+ int len);
+static void wlc_bmac_bsinit(wlc_info_t *wlc, chanspec_t chanspec);
+static u32 wlc_setband_inact(wlc_info_t *wlc, uint bandunit);
+static void wlc_bmac_setband(wlc_hw_info_t *wlc_hw, uint bandunit,
+ chanspec_t chanspec);
+static void wlc_bmac_update_slot_timing(wlc_hw_info_t *wlc_hw, bool shortslot);
+static void wlc_upd_ofdm_pctl1_table(wlc_hw_info_t *wlc_hw);
+static u16 wlc_bmac_ofdm_ratetable_offset(wlc_hw_info_t *wlc_hw,
+ u8 rate);
+
+/* === Low Level functions === */
+
+void wlc_bmac_set_shortslot(wlc_hw_info_t *wlc_hw, bool shortslot)
+{
+ wlc_hw->shortslot = shortslot;
+
+ if (BAND_2G(wlc_hw->band->bandtype) && wlc_hw->up) {
+ wlc_suspend_mac_and_wait(wlc_hw->wlc);
+ wlc_bmac_update_slot_timing(wlc_hw, shortslot);
+ wlc_enable_mac(wlc_hw->wlc);
+ }
+}
+
+/*
+ * Update the slot timing for standard 11b/g (20us slots)
+ * or shortslot 11g (9us slots)
+ * The PSM needs to be suspended for this call.
+ */
+static void wlc_bmac_update_slot_timing(wlc_hw_info_t *wlc_hw, bool shortslot)
+{
+ osl_t *osh;
+ d11regs_t *regs;
+
+ osh = wlc_hw->osh;
+ regs = wlc_hw->regs;
+
+ if (shortslot) {
+ /* 11g short slot: 11a timing */
+ W_REG(osh, &regs->ifs_slot, 0x0207); /* APHY_SLOT_TIME */
+ wlc_bmac_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME);
+ } else {
+ /* 11g long slot: 11b timing */
+ W_REG(osh, &regs->ifs_slot, 0x0212); /* BPHY_SLOT_TIME */
+ wlc_bmac_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME);
+ }
+}
+
+static void WLBANDINITFN(wlc_ucode_bsinit) (wlc_hw_info_t *wlc_hw)
+{
+ /* init microcode host flags */
+ wlc_write_mhf(wlc_hw, wlc_hw->band->mhfs);
+
+ /* do band-specific ucode IHR, SHM, and SCR inits */
+ if (D11REV_IS(wlc_hw->corerev, 23)) {
+ if (WLCISNPHY(wlc_hw->band)) {
+ wlc_write_inits(wlc_hw, d11n0bsinitvals16);
+ } else {
+ WL_ERROR(("%s: wl%d: unsupported phy in corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ }
+ } else {
+ if (D11REV_IS(wlc_hw->corerev, 24)) {
+ if (WLCISLCNPHY(wlc_hw->band)) {
+ wlc_write_inits(wlc_hw, d11lcn0bsinitvals24);
+ } else
+ WL_ERROR(("%s: wl%d: unsupported phy in corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev));
+ } else {
+ WL_ERROR(("%s: wl%d: unsupported corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ }
+ }
+}
+
+/* switch to new band but leave it inactive */
+static u32 WLBANDINITFN(wlc_setband_inact) (wlc_info_t *wlc, uint bandunit)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ u32 macintmask;
+ u32 tmp;
+
+ WL_TRACE(("wl%d: wlc_setband_inact\n", wlc_hw->unit));
+
+ ASSERT(bandunit != wlc_hw->band->bandunit);
+ ASSERT(si_iscoreup(wlc_hw->sih));
+ ASSERT((R_REG(wlc_hw->osh, &wlc_hw->regs->maccontrol) & MCTL_EN_MAC) ==
+ 0);
+
+ /* disable interrupts */
+ macintmask = wl_intrsoff(wlc->wl);
+
+ /* radio off */
+ wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+
+ ASSERT(wlc_hw->clk);
+
+ if (D11REV_LT(wlc_hw->corerev, 17))
+ tmp = R_REG(wlc_hw->osh, &wlc_hw->regs->maccontrol);
+
+ wlc_bmac_core_phy_clk(wlc_hw, OFF);
+
+ wlc_setxband(wlc_hw, bandunit);
+
+ return macintmask;
+}
+
+/* Process received frames */
+/*
+ * Return true if more frames need to be processed. false otherwise.
+ * Param 'bound' indicates max. # frames to process before break out.
+ */
+static bool BCMFASTPATH
+wlc_bmac_recv(wlc_hw_info_t *wlc_hw, uint fifo, bool bound)
+{
+ void *p;
+ void *head = NULL;
+ void *tail = NULL;
+ uint n = 0;
+ uint bound_limit = bound ? wlc_hw->wlc->pub->tunables->rxbnd : -1;
+ u32 tsf_h, tsf_l;
+ wlc_d11rxhdr_t *wlc_rxhdr = NULL;
+
+ WL_TRACE(("wl%d: %s\n", wlc_hw->unit, __func__));
+ /* gather received frames */
+ while ((p = dma_rx(wlc_hw->di[fifo]))) {
+
+ if (!tail)
+ head = tail = p;
+ else {
+ PKTSETLINK(tail, p);
+ tail = p;
+ }
+
+ /* !give others some time to run! */
+ if (++n >= bound_limit)
+ break;
+ }
+
+ /* get the TSF REG reading */
+ wlc_bmac_read_tsf(wlc_hw, &tsf_l, &tsf_h);
+
+ /* post more rbufs */
+ dma_rxfill(wlc_hw->di[fifo]);
+
+ /* process each frame */
+ while ((p = head) != NULL) {
+ head = PKTLINK(head);
+ PKTSETLINK(p, NULL);
+
+ /* record the tsf_l in wlc_rxd11hdr */
+ wlc_rxhdr = (wlc_d11rxhdr_t *) PKTDATA(p);
+ wlc_rxhdr->tsf_l = htol32(tsf_l);
+
+ /* compute the RSSI from d11rxhdr and record it in wlc_rxd11hr */
+ wlc_phy_rssi_compute(wlc_hw->band->pi, wlc_rxhdr);
+
+ wlc_recv(wlc_hw->wlc, p);
+ }
+
+ return n >= bound_limit;
+}
+
+/* second-level interrupt processing
+ * Return true if another dpc needs to be re-scheduled. false otherwise.
+ * Param 'bounded' indicates if applicable loops should be bounded.
+ */
+bool BCMFASTPATH wlc_dpc(wlc_info_t *wlc, bool bounded)
+{
+ u32 macintstatus;
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ bool fatal = false;
+
+ if (DEVICEREMOVED(wlc)) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc_hw->unit, __func__));
+ wl_down(wlc->wl);
+ return false;
+ }
+
+ /* grab and clear the saved software intstatus bits */
+ macintstatus = wlc->macintstatus;
+ wlc->macintstatus = 0;
+
+ WL_TRACE(("wl%d: wlc_dpc: macintstatus 0x%x\n", wlc_hw->unit,
+ macintstatus));
+
+ if (macintstatus & MI_PRQ) {
+ /* Process probe request FIFO */
+ ASSERT(0 && "PRQ Interrupt in non-MBSS");
+ }
+
+ /* BCN template is available */
+ /* ZZZ: Use AP_ACTIVE ? */
+ if (AP_ENAB(wlc->pub) && (!APSTA_ENAB(wlc->pub) || wlc->aps_associated)
+ && (macintstatus & MI_BCNTPL)) {
+ wlc_update_beacon(wlc);
+ }
+
+ /* PMQ entry addition */
+ if (macintstatus & MI_PMQ) {
+ }
+
+ /* tx status */
+ if (macintstatus & MI_TFS) {
+ if (wlc_bmac_txstatus(wlc->hw, bounded, &fatal))
+ wlc->macintstatus |= MI_TFS;
+ if (fatal) {
+ WL_ERROR(("MI_TFS: fatal\n"));
+ goto fatal;
+ }
+ }
+
+ if (macintstatus & (MI_TBTT | MI_DTIM_TBTT))
+ wlc_tbtt(wlc, regs);
+
+ /* ATIM window end */
+ if (macintstatus & MI_ATIMWINEND) {
+ WL_TRACE(("wlc_isr: end of ATIM window\n"));
+
+ OR_REG(wlc_hw->osh, &regs->maccommand, wlc->qvalid);
+ wlc->qvalid = 0;
+ }
+
+ /* phy tx error */
+ if (macintstatus & MI_PHYTXERR) {
+ WLCNTINCR(wlc->pub->_cnt->txphyerr);
+ }
+
+ /* received data or control frame, MI_DMAINT is indication of RX_FIFO interrupt */
+ if (macintstatus & MI_DMAINT) {
+ if (wlc_bmac_recv(wlc_hw, RX_FIFO, bounded)) {
+ wlc->macintstatus |= MI_DMAINT;
+ }
+ }
+
+ /* TX FIFO suspend/flush completion */
+ if (macintstatus & MI_TXSTOP) {
+ if (wlc_bmac_tx_fifo_suspended(wlc_hw, TX_DATA_FIFO)) {
+ /* WL_ERROR(("dpc: fifo_suspend_comlete\n")); */
+ }
+ }
+
+ /* noise sample collected */
+ if (macintstatus & MI_BG_NOISE) {
+ wlc_phy_noise_sample_intr(wlc_hw->band->pi);
+ }
+
+ if (macintstatus & MI_GP0) {
+ WL_ERROR(("wl%d: PSM microcode watchdog fired at %d (seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now));
+
+ printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n",
+ __func__, CHIPID(wlc_hw->sih->chip),
+ CHIPREV(wlc_hw->sih->chiprev));
+
+ WLCNTINCR(wlc->pub->_cnt->psmwds);
+
+ /* big hammer */
+ wl_init(wlc->wl);
+ }
+
+ /* gptimer timeout */
+ if (macintstatus & MI_TO) {
+ W_REG(wlc_hw->osh, &regs->gptimer, 0);
+ }
+
+ if (macintstatus & MI_RFDISABLE) {
+#if defined(BCMDBG)
+ u32 rfd = R_REG(wlc_hw->osh, &regs->phydebug) & PDBG_RFD;
+#endif
+
+ WL_ERROR(("wl%d: MAC Detected a change on the RF Disable Input 0x%x\n", wlc_hw->unit, rfd));
+
+ WLCNTINCR(wlc->pub->_cnt->rfdisable);
+ }
+
+ /* send any enq'd tx packets. Just makes sure to jump start tx */
+ if (!pktq_empty(&wlc->active_queue->q))
+ wlc_send_q(wlc, wlc->active_queue);
+
+ ASSERT(wlc_ps_check(wlc));
+
+ /* make sure the bound indication and the implementation are in sync */
+ ASSERT(bounded == true || wlc->macintstatus == 0);
+
+ /* it isn't done and needs to be resched if macintstatus is non-zero */
+ return wlc->macintstatus != 0;
+
+ fatal:
+ wl_init(wlc->wl);
+ return wlc->macintstatus != 0;
+}
+
+/* common low-level watchdog code */
+void wlc_bmac_watchdog(void *arg)
+{
+ wlc_info_t *wlc = (wlc_info_t *) arg;
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+
+ WL_TRACE(("wl%d: wlc_bmac_watchdog\n", wlc_hw->unit));
+
+ if (!wlc_hw->up)
+ return;
+
+ /* increment second count */
+ wlc_hw->now++;
+
+ /* Check for FIFO error interrupts */
+ wlc_bmac_fifoerrors(wlc_hw);
+
+ /* make sure RX dma has buffers */
+ dma_rxfill(wlc->hw->di[RX_FIFO]);
+ if (D11REV_IS(wlc_hw->corerev, 4)) {
+ dma_rxfill(wlc->hw->di[RX_TXSTATUS_FIFO]);
+ }
+
+ wlc_phy_watchdog(wlc_hw->band->pi);
+}
+
+void
+wlc_bmac_set_chanspec(wlc_hw_info_t *wlc_hw, chanspec_t chanspec, bool mute,
+ struct txpwr_limits *txpwr)
+{
+ uint bandunit;
+
+ WL_TRACE(("wl%d: wlc_bmac_set_chanspec 0x%x\n", wlc_hw->unit,
+ chanspec));
+
+ wlc_hw->chanspec = chanspec;
+
+ /* Switch bands if necessary */
+ if (NBANDS_HW(wlc_hw) > 1) {
+ bandunit = CHSPEC_WLCBANDUNIT(chanspec);
+ if (wlc_hw->band->bandunit != bandunit) {
+ /* wlc_bmac_setband disables other bandunit,
+ * use light band switch if not up yet
+ */
+ if (wlc_hw->up) {
+ wlc_phy_chanspec_radio_set(wlc_hw->
+ bandstate[bandunit]->
+ pi, chanspec);
+ wlc_bmac_setband(wlc_hw, bandunit, chanspec);
+ } else {
+ wlc_setxband(wlc_hw, bandunit);
+ }
+ }
+ }
+
+ wlc_phy_initcal_enable(wlc_hw->band->pi, !mute);
+
+ if (!wlc_hw->up) {
+ if (wlc_hw->clk)
+ wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr,
+ chanspec);
+ wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec);
+ } else {
+ wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec);
+ wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec);
+
+ /* Update muting of the channel */
+ wlc_bmac_mute(wlc_hw, mute, 0);
+ }
+}
+
+int wlc_bmac_revinfo_get(wlc_hw_info_t *wlc_hw, wlc_bmac_revinfo_t *revinfo)
+{
+ si_t *sih = wlc_hw->sih;
+ uint idx;
+
+ revinfo->vendorid = wlc_hw->vendorid;
+ revinfo->deviceid = wlc_hw->deviceid;
+
+ revinfo->boardrev = wlc_hw->boardrev;
+ revinfo->corerev = wlc_hw->corerev;
+ revinfo->sromrev = wlc_hw->sromrev;
+ revinfo->chiprev = sih->chiprev;
+ revinfo->chip = sih->chip;
+ revinfo->chippkg = sih->chippkg;
+ revinfo->boardtype = sih->boardtype;
+ revinfo->boardvendor = sih->boardvendor;
+ revinfo->bustype = sih->bustype;
+ revinfo->buscoretype = sih->buscoretype;
+ revinfo->buscorerev = sih->buscorerev;
+ revinfo->issim = sih->issim;
+
+ revinfo->nbands = NBANDS_HW(wlc_hw);
+
+ for (idx = 0; idx < NBANDS_HW(wlc_hw); idx++) {
+ wlc_hwband_t *band = wlc_hw->bandstate[idx];
+ revinfo->band[idx].bandunit = band->bandunit;
+ revinfo->band[idx].bandtype = band->bandtype;
+ revinfo->band[idx].phytype = band->phytype;
+ revinfo->band[idx].phyrev = band->phyrev;
+ revinfo->band[idx].radioid = band->radioid;
+ revinfo->band[idx].radiorev = band->radiorev;
+ revinfo->band[idx].abgphy_encore = band->abgphy_encore;
+ revinfo->band[idx].anarev = 0;
+
+ }
+ return 0;
+}
+
+int wlc_bmac_state_get(wlc_hw_info_t *wlc_hw, wlc_bmac_state_t *state)
+{
+ state->machwcap = wlc_hw->machwcap;
+
+ return 0;
+}
+
+static bool wlc_bmac_attach_dmapio(wlc_info_t *wlc, uint j, bool wme)
+{
+ uint i;
+ char name[8];
+ /* ucode host flag 2 needed for pio mode, independent of band and fifo */
+ u16 pio_mhf2 = 0;
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ uint unit = wlc_hw->unit;
+ wlc_tunables_t *tune = wlc->pub->tunables;
+
+ /* name and offsets for dma_attach */
+ snprintf(name, sizeof(name), "wl%d", unit);
+
+ if (wlc_hw->di[0] == 0) { /* Init FIFOs */
+ uint addrwidth;
+ int dma_attach_err = 0;
+ osl_t *osh = wlc_hw->osh;
+
+ /* Find out the DMA addressing capability and let OS know
+ * All the channels within one DMA core have 'common-minimum' same
+ * capability
+ */
+ addrwidth =
+ dma_addrwidth(wlc_hw->sih, DMAREG(wlc_hw, DMA_TX, 0));
+ OSL_DMADDRWIDTH(osh, addrwidth);
+
+ if (!wl_alloc_dma_resources(wlc_hw->wlc->wl, addrwidth)) {
+ WL_ERROR(("wl%d: wlc_attach: alloc_dma_resources failed\n", unit));
+ return false;
+ }
+
+ /*
+ * FIFO 0
+ * TX: TX_AC_BK_FIFO (TX AC Background data packets)
+ * RX: RX_FIFO (RX data packets)
+ */
+ ASSERT(TX_AC_BK_FIFO == 0);
+ ASSERT(RX_FIFO == 0);
+ wlc_hw->di[0] = dma_attach(osh, name, wlc_hw->sih,
+ (wme ? DMAREG(wlc_hw, DMA_TX, 0) :
+ NULL), DMAREG(wlc_hw, DMA_RX, 0),
+ (wme ? tune->ntxd : 0), tune->nrxd,
+ tune->rxbufsz, -1, tune->nrxbufpost,
+ WL_HWRXOFF, &wl_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[0]);
+
+ /*
+ * FIFO 1
+ * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets)
+ * (legacy) TX_DATA_FIFO (TX data packets)
+ * RX: UNUSED
+ */
+ ASSERT(TX_AC_BE_FIFO == 1);
+ ASSERT(TX_DATA_FIFO == 1);
+ wlc_hw->di[1] = dma_attach(osh, name, wlc_hw->sih,
+ DMAREG(wlc_hw, DMA_TX, 1), NULL,
+ tune->ntxd, 0, 0, -1, 0, 0,
+ &wl_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[1]);
+
+ /*
+ * FIFO 2
+ * TX: TX_AC_VI_FIFO (TX AC Video data packets)
+ * RX: UNUSED
+ */
+ ASSERT(TX_AC_VI_FIFO == 2);
+ wlc_hw->di[2] = dma_attach(osh, name, wlc_hw->sih,
+ DMAREG(wlc_hw, DMA_TX, 2), NULL,
+ tune->ntxd, 0, 0, -1, 0, 0,
+ &wl_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[2]);
+ /*
+ * FIFO 3
+ * TX: TX_AC_VO_FIFO (TX AC Voice data packets)
+ * (legacy) TX_CTL_FIFO (TX control & mgmt packets)
+ * RX: RX_TXSTATUS_FIFO (transmit-status packets)
+ * for corerev < 5 only
+ */
+ ASSERT(TX_AC_VO_FIFO == 3);
+ ASSERT(TX_CTL_FIFO == 3);
+ if (D11REV_IS(wlc_hw->corerev, 4)) {
+ ASSERT(RX_TXSTATUS_FIFO == 3);
+ wlc_hw->di[3] = dma_attach(osh, name, wlc_hw->sih,
+ DMAREG(wlc_hw, DMA_TX, 3),
+ DMAREG(wlc_hw, DMA_RX, 3),
+ tune->ntxd, tune->nrxd,
+ sizeof(tx_status_t), -1,
+ tune->nrxbufpost, 0,
+ &wl_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[3]);
+ } else {
+ wlc_hw->di[3] = dma_attach(osh, name, wlc_hw->sih,
+ DMAREG(wlc_hw, DMA_TX, 3),
+ NULL, tune->ntxd, 0, 0, -1,
+ 0, 0, &wl_msg_level);
+ dma_attach_err |= (NULL == wlc_hw->di[3]);
+ }
+/* Cleaner to leave this as if with AP defined */
+
+ if (dma_attach_err) {
+ WL_ERROR(("wl%d: wlc_attach: dma_attach failed\n",
+ unit));
+ return false;
+ }
+
+ /* get pointer to dma engine tx flow control variable */
+ for (i = 0; i < NFIFO; i++)
+ if (wlc_hw->di[i])
+ wlc_hw->txavail[i] =
+ (uint *) dma_getvar(wlc_hw->di[i],
+ "&txavail");
+ }
+
+ /* initial ucode host flags */
+ wlc_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2);
+
+ return true;
+}
+
+static void wlc_bmac_detach_dmapio(wlc_hw_info_t *wlc_hw)
+{
+ uint j;
+
+ for (j = 0; j < NFIFO; j++) {
+ if (wlc_hw->di[j]) {
+ dma_detach(wlc_hw->di[j]);
+ wlc_hw->di[j] = NULL;
+ }
+ }
+}
+
+/* low level attach
+ * run backplane attach, init nvram
+ * run phy attach
+ * initialize software state for each core and band
+ * put the whole chip in reset(driver down state), no clock
+ */
+int wlc_bmac_attach(wlc_info_t *wlc, u16 vendor, u16 device, uint unit,
+ bool piomode, osl_t *osh, void *regsva, uint bustype,
+ void *btparam)
+{
+ wlc_hw_info_t *wlc_hw;
+ d11regs_t *regs;
+ char *macaddr = NULL;
+ char *vars;
+ uint err = 0;
+ uint j;
+ bool wme = false;
+ shared_phy_params_t sha_params;
+
+ WL_TRACE(("wl%d: wlc_bmac_attach: vendor 0x%x device 0x%x\n", unit,
+ vendor, device));
+
+ ASSERT(sizeof(wlc_d11rxhdr_t) <= WL_HWRXOFF);
+
+ wme = true;
+
+ wlc_hw = wlc->hw;
+ wlc_hw->wlc = wlc;
+ wlc_hw->unit = unit;
+ wlc_hw->osh = osh;
+ wlc_hw->band = wlc_hw->bandstate[0];
+ wlc_hw->_piomode = piomode;
+
+ /* populate wlc_hw_info_t with default values */
+ wlc_bmac_info_init(wlc_hw);
+
+ /*
+ * Do the hardware portion of the attach.
+ * Also initialize software state that depends on the particular hardware
+ * we are running.
+ */
+ wlc_hw->sih = si_attach((uint) device, osh, regsva, bustype, btparam,
+ &wlc_hw->vars, &wlc_hw->vars_size);
+ if (wlc_hw->sih == NULL) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: si_attach failed\n", unit));
+ err = 11;
+ goto fail;
+ }
+ vars = wlc_hw->vars;
+
+ /*
+ * Get vendid/devid nvram overwrites, which could be different
+ * than those the BIOS recognizes for devices on PCMCIA_BUS,
+ * SDIO_BUS, and SROMless devices on PCI_BUS.
+ */
+#ifdef BCMBUSTYPE
+ bustype = BCMBUSTYPE;
+#endif
+ if (bustype != SI_BUS) {
+ char *var;
+
+ var = getvar(vars, "vendid");
+ if (var) {
+ vendor = (u16) simple_strtoul(var, NULL, 0);
+ WL_ERROR(("Overriding vendor id = 0x%x\n", vendor));
+ }
+ var = getvar(vars, "devid");
+ if (var) {
+ u16 devid = (u16) simple_strtoul(var, NULL, 0);
+ if (devid != 0xffff) {
+ device = devid;
+ WL_ERROR(("Overriding device id = 0x%x\n",
+ device));
+ }
+ }
+
+ /* verify again the device is supported */
+ if (!wlc_chipmatch(vendor, device)) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: Unsupported vendor/device (0x%x/0x%x)\n", unit, vendor, device));
+ err = 12;
+ goto fail;
+ }
+ }
+
+ wlc_hw->vendorid = vendor;
+ wlc_hw->deviceid = device;
+
+ /* set bar0 window to point at D11 core */
+ wlc_hw->regs = (d11regs_t *) si_setcore(wlc_hw->sih, D11_CORE_ID, 0);
+ wlc_hw->corerev = si_corerev(wlc_hw->sih);
+
+ regs = wlc_hw->regs;
+
+ wlc->regs = wlc_hw->regs;
+
+ /* validate chip, chiprev and corerev */
+ if (!wlc_isgoodchip(wlc_hw)) {
+ err = 13;
+ goto fail;
+ }
+
+ /* initialize power control registers */
+ si_clkctl_init(wlc_hw->sih);
+
+ /* request fastclock and force fastclock for the rest of attach
+ * bring the d11 core out of reset.
+ * For PMU chips, the first wlc_clkctl_clk is no-op since core-clk is still false;
+ * But it will be called again inside wlc_corereset, after d11 is out of reset.
+ */
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+ wlc_bmac_corereset(wlc_hw, WLC_USE_COREFLAGS);
+
+ if (!wlc_bmac_validate_chip_access(wlc_hw)) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: validate_chip_access failed\n", unit));
+ err = 14;
+ goto fail;
+ }
+
+ /* get the board rev, used just below */
+ j = getintvar(vars, "boardrev");
+ /* promote srom boardrev of 0xFF to 1 */
+ if (j == BOARDREV_PROMOTABLE)
+ j = BOARDREV_PROMOTED;
+ wlc_hw->boardrev = (u16) j;
+ if (!wlc_validboardtype(wlc_hw)) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: Unsupported Broadcom board type (0x%x)" " or revision level (0x%x)\n", unit, wlc_hw->sih->boardtype, wlc_hw->boardrev));
+ err = 15;
+ goto fail;
+ }
+ wlc_hw->sromrev = (u8) getintvar(vars, "sromrev");
+ wlc_hw->boardflags = (u32) getintvar(vars, "boardflags");
+ wlc_hw->boardflags2 = (u32) getintvar(vars, "boardflags2");
+
+ if (D11REV_LE(wlc_hw->corerev, 4)
+ || (wlc_hw->boardflags & BFL_NOPLLDOWN))
+ wlc_bmac_pllreq(wlc_hw, true, WLC_PLLREQ_SHARED);
+
+ if ((BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ && (si_pci_war16165(wlc_hw->sih)))
+ wlc->war16165 = true;
+
+ /* check device id(srom, nvram etc.) to set bands */
+ if (wlc_hw->deviceid == BCM43224_D11N_ID) {
+ /* Dualband boards */
+ wlc_hw->_nbands = 2;
+ } else
+ wlc_hw->_nbands = 1;
+
+ if ((CHIPID(wlc_hw->sih->chip) == BCM43225_CHIP_ID))
+ wlc_hw->_nbands = 1;
+
+ /* BMAC_NOTE: remove init of pub values when wlc_attach() unconditionally does the
+ * init of these values
+ */
+ wlc->vendorid = wlc_hw->vendorid;
+ wlc->deviceid = wlc_hw->deviceid;
+ wlc->pub->sih = wlc_hw->sih;
+ wlc->pub->corerev = wlc_hw->corerev;
+ wlc->pub->sromrev = wlc_hw->sromrev;
+ wlc->pub->boardrev = wlc_hw->boardrev;
+ wlc->pub->boardflags = wlc_hw->boardflags;
+ wlc->pub->boardflags2 = wlc_hw->boardflags2;
+ wlc->pub->_nbands = wlc_hw->_nbands;
+
+ wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc);
+
+ if (wlc_hw->physhim == NULL) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: wlc_phy_shim_attach failed\n",
+ unit));
+ err = 25;
+ goto fail;
+ }
+
+ /* pass all the parameters to wlc_phy_shared_attach in one struct */
+ sha_params.osh = osh;
+ sha_params.sih = wlc_hw->sih;
+ sha_params.physhim = wlc_hw->physhim;
+ sha_params.unit = unit;
+ sha_params.corerev = wlc_hw->corerev;
+ sha_params.vars = vars;
+ sha_params.vid = wlc_hw->vendorid;
+ sha_params.did = wlc_hw->deviceid;
+ sha_params.chip = wlc_hw->sih->chip;
+ sha_params.chiprev = wlc_hw->sih->chiprev;
+ sha_params.chippkg = wlc_hw->sih->chippkg;
+ sha_params.sromrev = wlc_hw->sromrev;
+ sha_params.boardtype = wlc_hw->sih->boardtype;
+ sha_params.boardrev = wlc_hw->boardrev;
+ sha_params.boardvendor = wlc_hw->sih->boardvendor;
+ sha_params.boardflags = wlc_hw->boardflags;
+ sha_params.boardflags2 = wlc_hw->boardflags2;
+ sha_params.bustype = wlc_hw->sih->bustype;
+ sha_params.buscorerev = wlc_hw->sih->buscorerev;
+
+ /* alloc and save pointer to shared phy state area */
+ wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params);
+ if (!wlc_hw->phy_sh) {
+ err = 16;
+ goto fail;
+ }
+
+ /* initialize software state for each core and band */
+ for (j = 0; j < NBANDS_HW(wlc_hw); j++) {
+ /*
+ * band0 is always 2.4Ghz
+ * band1, if present, is 5Ghz
+ */
+
+ /* So if this is a single band 11a card, use band 1 */
+ if (IS_SINGLEBAND_5G(wlc_hw->deviceid))
+ j = BAND_5G_INDEX;
+
+ wlc_setxband(wlc_hw, j);
+
+ wlc_hw->band->bandunit = j;
+ wlc_hw->band->bandtype = j ? WLC_BAND_5G : WLC_BAND_2G;
+ wlc->band->bandunit = j;
+ wlc->band->bandtype = j ? WLC_BAND_5G : WLC_BAND_2G;
+ wlc->core->coreidx = si_coreidx(wlc_hw->sih);
+
+ if (D11REV_GE(wlc_hw->corerev, 13)) {
+ wlc_hw->machwcap = R_REG(wlc_hw->osh, &regs->machwcap);
+ wlc_hw->machwcap_backup = wlc_hw->machwcap;
+ }
+
+ /* init tx fifo size */
+ ASSERT((wlc_hw->corerev - XMTFIFOTBL_STARTREV) <
+ ARRAY_SIZE(xmtfifo_sz));
+ wlc_hw->xmtfifo_sz =
+ xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)];
+
+ /* Get a phy for this band */
+ wlc_hw->band->pi = wlc_phy_attach(wlc_hw->phy_sh,
+ (void *)regs, wlc_hw->band->bandtype, vars);
+ if (wlc_hw->band->pi == NULL) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: wlc_phy_attach failed\n", unit));
+ err = 17;
+ goto fail;
+ }
+
+ wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap);
+
+ wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype,
+ &wlc_hw->band->phyrev,
+ &wlc_hw->band->radioid,
+ &wlc_hw->band->radiorev);
+ wlc_hw->band->abgphy_encore =
+ wlc_phy_get_encore(wlc_hw->band->pi);
+ wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi);
+ wlc_hw->band->core_flags =
+ wlc_phy_get_coreflags(wlc_hw->band->pi);
+
+ /* verify good phy_type & supported phy revision */
+ if (WLCISNPHY(wlc_hw->band)) {
+ if (NCONF_HAS(wlc_hw->band->phyrev))
+ goto good_phy;
+ else
+ goto bad_phy;
+ } else if (WLCISLCNPHY(wlc_hw->band)) {
+ if (LCNCONF_HAS(wlc_hw->band->phyrev))
+ goto good_phy;
+ else
+ goto bad_phy;
+ } else {
+ bad_phy:
+ WL_ERROR(("wl%d: wlc_bmac_attach: unsupported phy type/rev (%d/%d)\n", unit, wlc_hw->band->phytype, wlc_hw->band->phyrev));
+ err = 18;
+ goto fail;
+ }
+
+ good_phy:
+ /* BMAC_NOTE: wlc->band->pi should not be set below and should be done in the
+ * high level attach. However we can not make that change until all low level access
+ * is changed to wlc_hw->band->pi. Instead do the wlc->band->pi init below, keeping
+ * wlc_hw->band->pi as well for incremental update of low level fns, and cut over
+ * low only init when all fns updated.
+ */
+ wlc->band->pi = wlc_hw->band->pi;
+ wlc->band->phytype = wlc_hw->band->phytype;
+ wlc->band->phyrev = wlc_hw->band->phyrev;
+ wlc->band->radioid = wlc_hw->band->radioid;
+ wlc->band->radiorev = wlc_hw->band->radiorev;
+
+ /* default contention windows size limits */
+ wlc_hw->band->CWmin = APHY_CWMIN;
+ wlc_hw->band->CWmax = PHY_CWMAX;
+
+ if (!wlc_bmac_attach_dmapio(wlc, j, wme)) {
+ err = 19;
+ goto fail;
+ }
+ }
+
+ /* disable core to match driver "down" state */
+ wlc_coredisable(wlc_hw);
+
+ /* Match driver "down" state */
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ si_pci_down(wlc_hw->sih);
+
+ /* register sb interrupt callback functions */
+ si_register_intr_callback(wlc_hw->sih, (void *)wlc_wlintrsoff,
+ (void *)wlc_wlintrsrestore, NULL, wlc);
+
+ /* turn off pll and xtal to match driver "down" state */
+ wlc_bmac_xtal(wlc_hw, OFF);
+
+ /* *********************************************************************
+ * The hardware is in the DOWN state at this point. D11 core
+ * or cores are in reset with clocks off, and the board PLLs
+ * are off if possible.
+ *
+ * Beyond this point, wlc->sbclk == false and chip registers
+ * should not be touched.
+ *********************************************************************
+ */
+
+ /* init etheraddr state variables */
+ macaddr = wlc_get_macaddr(wlc_hw);
+ if (macaddr == NULL) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: macaddr not found\n", unit));
+ err = 21;
+ goto fail;
+ }
+ bcm_ether_atoe(macaddr, &wlc_hw->etheraddr);
+ if (ETHER_ISBCAST((char *)&wlc_hw->etheraddr) ||
+ ETHER_ISNULLADDR((char *)&wlc_hw->etheraddr)) {
+ WL_ERROR(("wl%d: wlc_bmac_attach: bad macaddr %s\n", unit,
+ macaddr));
+ err = 22;
+ goto fail;
+ }
+
+ WL_ERROR(("%s:: deviceid 0x%x nbands %d board 0x%x macaddr: %s\n",
+ __func__, wlc_hw->deviceid, wlc_hw->_nbands,
+ wlc_hw->sih->boardtype, macaddr));
+
+ return err;
+
+ fail:
+ WL_ERROR(("wl%d: wlc_bmac_attach: failed with err %d\n", unit, err));
+ return err;
+}
+
+/*
+ * Initialize wlc_info default values ...
+ * may get overrides later in this function
+ * BMAC_NOTES, move low out and resolve the dangling ones
+ */
+void wlc_bmac_info_init(wlc_hw_info_t *wlc_hw)
+{
+ wlc_info_t *wlc = wlc_hw->wlc;
+
+ /* set default sw macintmask value */
+ wlc->defmacintmask = DEF_MACINTMASK;
+
+ /* various 802.11g modes */
+ wlc_hw->shortslot = false;
+
+ wlc_hw->SFBL = RETRY_SHORT_FB;
+ wlc_hw->LFBL = RETRY_LONG_FB;
+
+ /* default mac retry limits */
+ wlc_hw->SRL = RETRY_SHORT_DEF;
+ wlc_hw->LRL = RETRY_LONG_DEF;
+ wlc_hw->chanspec = CH20MHZ_CHSPEC(1);
+}
+
+/*
+ * low level detach
+ */
+int wlc_bmac_detach(wlc_info_t *wlc)
+{
+ uint i;
+ wlc_hwband_t *band;
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ int callbacks;
+
+ callbacks = 0;
+
+ if (wlc_hw->sih) {
+ /* detach interrupt sync mechanism since interrupt is disabled and per-port
+ * interrupt object may has been freed. this must be done before sb core switch
+ */
+ si_deregister_intr_callback(wlc_hw->sih);
+
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ si_pci_sleep(wlc_hw->sih);
+ }
+
+ wlc_bmac_detach_dmapio(wlc_hw);
+
+ band = wlc_hw->band;
+ for (i = 0; i < NBANDS_HW(wlc_hw); i++) {
+ if (band->pi) {
+ /* Detach this band's phy */
+ wlc_phy_detach(band->pi);
+ band->pi = NULL;
+ }
+ band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)];
+ }
+
+ /* Free shared phy state */
+ wlc_phy_shared_detach(wlc_hw->phy_sh);
+
+ wlc_phy_shim_detach(wlc_hw->physhim);
+
+ /* free vars */
+ if (wlc_hw->vars) {
+ kfree(wlc_hw->vars);
+ wlc_hw->vars = NULL;
+ }
+
+ if (wlc_hw->sih) {
+ si_detach(wlc_hw->sih);
+ wlc_hw->sih = NULL;
+ }
+
+ return callbacks;
+
+}
+
+void wlc_bmac_reset(wlc_hw_info_t *wlc_hw)
+{
+ WL_TRACE(("wl%d: wlc_bmac_reset\n", wlc_hw->unit));
+
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->reset);
+
+ /* reset the core */
+ if (!DEVICEREMOVED(wlc_hw->wlc))
+ wlc_bmac_corereset(wlc_hw, WLC_USE_COREFLAGS);
+
+ /* purge the dma rings */
+ wlc_flushqueues(wlc_hw->wlc);
+
+ wlc_reset_bmac_done(wlc_hw->wlc);
+}
+
+void
+wlc_bmac_init(wlc_hw_info_t *wlc_hw, chanspec_t chanspec,
+ bool mute) {
+ u32 macintmask;
+ bool fastclk;
+ wlc_info_t *wlc = wlc_hw->wlc;
+
+ WL_TRACE(("wl%d: wlc_bmac_init\n", wlc_hw->unit));
+
+ /* request FAST clock if not on */
+ fastclk = wlc_hw->forcefastclk;
+ if (!fastclk)
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+
+ /* disable interrupts */
+ macintmask = wl_intrsoff(wlc->wl);
+
+ /* set up the specified band and chanspec */
+ wlc_setxband(wlc_hw, CHSPEC_WLCBANDUNIT(chanspec));
+ wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec);
+
+ /* do one-time phy inits and calibration */
+ wlc_phy_cal_init(wlc_hw->band->pi);
+
+ /* core-specific initialization */
+ wlc_coreinit(wlc);
+
+ /* suspend the tx fifos and mute the phy for preism cac time */
+ if (mute)
+ wlc_bmac_mute(wlc_hw, ON, PHY_MUTE_FOR_PREISM);
+
+ /* band-specific inits */
+ wlc_bmac_bsinit(wlc, chanspec);
+
+ /* restore macintmask */
+ wl_intrsrestore(wlc->wl, macintmask);
+
+ /* seed wake_override with WLC_WAKE_OVERRIDE_MACSUSPEND since the mac is suspended
+ * and wlc_enable_mac() will clear this override bit.
+ */
+ mboolset(wlc_hw->wake_override, WLC_WAKE_OVERRIDE_MACSUSPEND);
+
+ /*
+ * initialize mac_suspend_depth to 1 to match ucode initial suspended state
+ */
+ wlc_hw->mac_suspend_depth = 1;
+
+ /* restore the clk */
+ if (!fastclk)
+ wlc_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+int wlc_bmac_up_prep(wlc_hw_info_t *wlc_hw)
+{
+ uint coremask;
+
+ WL_TRACE(("wl%d: %s:\n", wlc_hw->unit, __func__));
+
+ ASSERT(wlc_hw->wlc->pub->hw_up && wlc_hw->wlc->macintmask == 0);
+
+ /*
+ * Enable pll and xtal, initialize the power control registers,
+ * and force fastclock for the remainder of wlc_up().
+ */
+ wlc_bmac_xtal(wlc_hw, ON);
+ si_clkctl_init(wlc_hw->sih);
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+
+ /*
+ * Configure pci/pcmcia here instead of in wlc_attach()
+ * to allow mfg hotswap: down, hotswap (chip power cycle), up.
+ */
+ coremask = (1 << wlc_hw->wlc->core->coreidx);
+
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ si_pci_setup(wlc_hw->sih, coremask);
+
+ ASSERT(si_coreid(wlc_hw->sih) == D11_CORE_ID);
+
+ /*
+ * Need to read the hwradio status here to cover the case where the system
+ * is loaded with the hw radio disabled. We do not want to bring the driver up in this case.
+ */
+ if (wlc_bmac_radio_read_hwdisabled(wlc_hw)) {
+ /* put SB PCI in down state again */
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ si_pci_down(wlc_hw->sih);
+ wlc_bmac_xtal(wlc_hw, OFF);
+ return BCME_RADIOOFF;
+ }
+
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ si_pci_up(wlc_hw->sih);
+
+ /* reset the d11 core */
+ wlc_bmac_corereset(wlc_hw, WLC_USE_COREFLAGS);
+
+ return 0;
+}
+
+int wlc_bmac_up_finish(wlc_hw_info_t *wlc_hw)
+{
+ WL_TRACE(("wl%d: %s:\n", wlc_hw->unit, __func__));
+
+ wlc_hw->up = true;
+ wlc_phy_hw_state_upd(wlc_hw->band->pi, true);
+
+ /* FULLY enable dynamic power control and d11 core interrupt */
+ wlc_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+ ASSERT(wlc_hw->wlc->macintmask == 0);
+ wl_intrson(wlc_hw->wlc->wl);
+ return 0;
+}
+
+int wlc_bmac_down_prep(wlc_hw_info_t *wlc_hw)
+{
+ bool dev_gone;
+ uint callbacks = 0;
+
+ WL_TRACE(("wl%d: %s:\n", wlc_hw->unit, __func__));
+
+ if (!wlc_hw->up)
+ return callbacks;
+
+ dev_gone = DEVICEREMOVED(wlc_hw->wlc);
+
+ /* disable interrupts */
+ if (dev_gone)
+ wlc_hw->wlc->macintmask = 0;
+ else {
+ /* now disable interrupts */
+ wl_intrsoff(wlc_hw->wlc->wl);
+
+ /* ensure we're running on the pll clock again */
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+ }
+ /* down phy at the last of this stage */
+ callbacks += wlc_phy_down(wlc_hw->band->pi);
+
+ return callbacks;
+}
+
+int wlc_bmac_down_finish(wlc_hw_info_t *wlc_hw)
+{
+ uint callbacks = 0;
+ bool dev_gone;
+
+ WL_TRACE(("wl%d: %s:\n", wlc_hw->unit, __func__));
+
+ if (!wlc_hw->up)
+ return callbacks;
+
+ wlc_hw->up = false;
+ wlc_phy_hw_state_upd(wlc_hw->band->pi, false);
+
+ dev_gone = DEVICEREMOVED(wlc_hw->wlc);
+
+ if (dev_gone) {
+ wlc_hw->sbclk = false;
+ wlc_hw->clk = false;
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+
+ /* reclaim any posted packets */
+ wlc_flushqueues(wlc_hw->wlc);
+ } else {
+
+ /* Reset and disable the core */
+ if (si_iscoreup(wlc_hw->sih)) {
+ if (R_REG(wlc_hw->osh, &wlc_hw->regs->maccontrol) &
+ MCTL_EN_MAC)
+ wlc_suspend_mac_and_wait(wlc_hw->wlc);
+ callbacks += wl_reset(wlc_hw->wlc->wl);
+ wlc_coredisable(wlc_hw);
+ }
+
+ /* turn off primary xtal and pll */
+ if (!wlc_hw->noreset) {
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS)
+ si_pci_down(wlc_hw->sih);
+ wlc_bmac_xtal(wlc_hw, OFF);
+ }
+ }
+
+ return callbacks;
+}
+
+void wlc_bmac_wait_for_wake(wlc_hw_info_t *wlc_hw)
+{
+ if (D11REV_IS(wlc_hw->corerev, 4)) /* no slowclock */
+ udelay(5);
+ else {
+ /* delay before first read of ucode state */
+ udelay(40);
+
+ /* wait until ucode is no longer asleep */
+ SPINWAIT((wlc_bmac_read_shm(wlc_hw, M_UCODE_DBGST) ==
+ DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly);
+ }
+
+ ASSERT(wlc_bmac_read_shm(wlc_hw, M_UCODE_DBGST) != DBGST_ASLEEP);
+}
+
+void wlc_bmac_hw_etheraddr(wlc_hw_info_t *wlc_hw, struct ether_addr *ea)
+{
+ bcopy(&wlc_hw->etheraddr, ea, ETHER_ADDR_LEN);
+}
+
+void wlc_bmac_set_hw_etheraddr(wlc_hw_info_t *wlc_hw, struct ether_addr *ea)
+{
+ bcopy(ea, &wlc_hw->etheraddr, ETHER_ADDR_LEN);
+}
+
+int wlc_bmac_bandtype(wlc_hw_info_t *wlc_hw)
+{
+ return wlc_hw->band->bandtype;
+}
+
+void *wlc_cur_phy(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ return (void *)wlc_hw->band->pi;
+}
+
+/* control chip clock to save power, enable dynamic clock or force fast clock */
+static void wlc_clkctl_clk(wlc_hw_info_t *wlc_hw, uint mode)
+{
+ if (PMUCTL_ENAB(wlc_hw->sih)) {
+ /* new chips with PMU, CCS_FORCEHT will distribute the HT clock on backplane,
+ * but mac core will still run on ALP(not HT) when it enters powersave mode,
+ * which means the FCA bit may not be set.
+ * should wakeup mac if driver wants it to run on HT.
+ */
+
+ if (wlc_hw->clk) {
+ if (mode == CLK_FAST) {
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->clk_ctl_st,
+ CCS_FORCEHT);
+
+ udelay(64);
+
+ SPINWAIT(((R_REG
+ (wlc_hw->osh,
+ &wlc_hw->regs->
+ clk_ctl_st) & CCS_HTAVAIL) == 0),
+ PMU_MAX_TRANSITION_DLY);
+ ASSERT(R_REG
+ (wlc_hw->osh,
+ &wlc_hw->regs->
+ clk_ctl_st) & CCS_HTAVAIL);
+ } else {
+ if ((wlc_hw->sih->pmurev == 0) &&
+ (R_REG
+ (wlc_hw->osh,
+ &wlc_hw->regs->
+ clk_ctl_st) & (CCS_FORCEHT | CCS_HTAREQ)))
+ SPINWAIT(((R_REG
+ (wlc_hw->osh,
+ &wlc_hw->regs->
+ clk_ctl_st) & CCS_HTAVAIL)
+ == 0),
+ PMU_MAX_TRANSITION_DLY);
+ AND_REG(wlc_hw->osh, &wlc_hw->regs->clk_ctl_st,
+ ~CCS_FORCEHT);
+ }
+ }
+ wlc_hw->forcefastclk = (mode == CLK_FAST);
+ } else {
+ bool wakeup_ucode;
+
+ /* old chips w/o PMU, force HT through cc,
+ * then use FCA to verify mac is running fast clock
+ */
+
+ wakeup_ucode = D11REV_LT(wlc_hw->corerev, 9);
+
+ if (wlc_hw->up && wakeup_ucode)
+ wlc_ucode_wake_override_set(wlc_hw,
+ WLC_WAKE_OVERRIDE_CLKCTL);
+
+ wlc_hw->forcefastclk = si_clkctl_cc(wlc_hw->sih, mode);
+
+ if (D11REV_LT(wlc_hw->corerev, 11)) {
+ /* ucode WAR for old chips */
+ if (wlc_hw->forcefastclk)
+ wlc_bmac_mhf(wlc_hw, MHF1, MHF1_FORCEFASTCLK,
+ MHF1_FORCEFASTCLK, WLC_BAND_ALL);
+ else
+ wlc_bmac_mhf(wlc_hw, MHF1, MHF1_FORCEFASTCLK, 0,
+ WLC_BAND_ALL);
+ }
+
+ /* check fast clock is available (if core is not in reset) */
+ if (D11REV_GT(wlc_hw->corerev, 4) && wlc_hw->forcefastclk
+ && wlc_hw->clk)
+ ASSERT(si_core_sflags(wlc_hw->sih, 0, 0) & SISF_FCLKA);
+
+ /* keep the ucode wake bit on if forcefastclk is on
+ * since we do not want ucode to put us back to slow clock
+ * when it dozes for PM mode.
+ * Code below matches the wake override bit with current forcefastclk state
+ * Only setting bit in wake_override instead of waking ucode immediately
+ * since old code (wlc.c 1.4499) had this behavior. Older code set
+ * wlc->forcefastclk but only had the wake happen if the wakup_ucode work
+ * (protected by an up check) was executed just below.
+ */
+ if (wlc_hw->forcefastclk)
+ mboolset(wlc_hw->wake_override,
+ WLC_WAKE_OVERRIDE_FORCEFAST);
+ else
+ mboolclr(wlc_hw->wake_override,
+ WLC_WAKE_OVERRIDE_FORCEFAST);
+
+ /* ok to clear the wakeup now */
+ if (wlc_hw->up && wakeup_ucode)
+ wlc_ucode_wake_override_clear(wlc_hw,
+ WLC_WAKE_OVERRIDE_CLKCTL);
+ }
+}
+
+/* set initial host flags value */
+static void
+wlc_mhfdef(wlc_info_t *wlc, u16 *mhfs, u16 mhf2_init)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+
+ bzero(mhfs, sizeof(u16) * MHFMAX);
+
+ mhfs[MHF2] |= mhf2_init;
+
+ /* prohibit use of slowclock on multifunction boards */
+ if (wlc_hw->boardflags & BFL_NOPLLDOWN)
+ mhfs[MHF1] |= MHF1_FORCEFASTCLK;
+
+ if (WLCISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) {
+ mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR;
+ mhfs[MHF1] |= MHF1_IQSWAP_WAR;
+ }
+}
+
+/* set or clear ucode host flag bits
+ * it has an optimization for no-change write
+ * it only writes through shared memory when the core has clock;
+ * pre-CLK changes should use wlc_write_mhf to get around the optimization
+ *
+ *
+ * bands values are: WLC_BAND_AUTO <--- Current band only
+ * WLC_BAND_5G <--- 5G band only
+ * WLC_BAND_2G <--- 2G band only
+ * WLC_BAND_ALL <--- All bands
+ */
+void
+wlc_bmac_mhf(wlc_hw_info_t *wlc_hw, u8 idx, u16 mask, u16 val,
+ int bands)
+{
+ u16 save;
+ u16 addr[MHFMAX] = {
+ M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+ M_HOST_FLAGS5
+ };
+ wlc_hwband_t *band;
+
+ ASSERT((val & ~mask) == 0);
+ ASSERT(idx < MHFMAX);
+ ASSERT(ARRAY_SIZE(addr) == MHFMAX);
+
+ switch (bands) {
+ /* Current band only or all bands,
+ * then set the band to current band
+ */
+ case WLC_BAND_AUTO:
+ case WLC_BAND_ALL:
+ band = wlc_hw->band;
+ break;
+ case WLC_BAND_5G:
+ band = wlc_hw->bandstate[BAND_5G_INDEX];
+ break;
+ case WLC_BAND_2G:
+ band = wlc_hw->bandstate[BAND_2G_INDEX];
+ break;
+ default:
+ ASSERT(0);
+ band = NULL;
+ }
+
+ if (band) {
+ save = band->mhfs[idx];
+ band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val;
+
+ /* optimization: only write through if changed, and
+ * changed band is the current band
+ */
+ if (wlc_hw->clk && (band->mhfs[idx] != save)
+ && (band == wlc_hw->band))
+ wlc_bmac_write_shm(wlc_hw, addr[idx],
+ (u16) band->mhfs[idx]);
+ }
+
+ if (bands == WLC_BAND_ALL) {
+ wlc_hw->bandstate[0]->mhfs[idx] =
+ (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val;
+ wlc_hw->bandstate[1]->mhfs[idx] =
+ (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val;
+ }
+}
+
+u16 wlc_bmac_mhf_get(wlc_hw_info_t *wlc_hw, u8 idx, int bands)
+{
+ wlc_hwband_t *band;
+ ASSERT(idx < MHFMAX);
+
+ switch (bands) {
+ case WLC_BAND_AUTO:
+ band = wlc_hw->band;
+ break;
+ case WLC_BAND_5G:
+ band = wlc_hw->bandstate[BAND_5G_INDEX];
+ break;
+ case WLC_BAND_2G:
+ band = wlc_hw->bandstate[BAND_2G_INDEX];
+ break;
+ default:
+ ASSERT(0);
+ band = NULL;
+ }
+
+ if (!band)
+ return 0;
+
+ return band->mhfs[idx];
+}
+
+static void wlc_write_mhf(wlc_hw_info_t *wlc_hw, u16 *mhfs)
+{
+ u8 idx;
+ u16 addr[] = {
+ M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+ M_HOST_FLAGS5
+ };
+
+ ASSERT(ARRAY_SIZE(addr) == MHFMAX);
+
+ for (idx = 0; idx < MHFMAX; idx++) {
+ wlc_bmac_write_shm(wlc_hw, addr[idx], mhfs[idx]);
+ }
+}
+
+/* set the maccontrol register to desired reset state and
+ * initialize the sw cache of the register
+ */
+static void wlc_mctrl_reset(wlc_hw_info_t *wlc_hw)
+{
+ /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */
+ wlc_hw->maccontrol = 0;
+ wlc_hw->suspended_fifos = 0;
+ wlc_hw->wake_override = 0;
+ wlc_hw->mute_override = 0;
+ wlc_bmac_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE);
+}
+
+/* set or clear maccontrol bits */
+void wlc_bmac_mctrl(wlc_hw_info_t *wlc_hw, u32 mask, u32 val)
+{
+ u32 maccontrol;
+ u32 new_maccontrol;
+
+ ASSERT((val & ~mask) == 0);
+
+ maccontrol = wlc_hw->maccontrol;
+ new_maccontrol = (maccontrol & ~mask) | val;
+
+ /* if the new maccontrol value is the same as the old, nothing to do */
+ if (new_maccontrol == maccontrol)
+ return;
+
+ /* something changed, cache the new value */
+ wlc_hw->maccontrol = new_maccontrol;
+
+ /* write the new values with overrides applied */
+ wlc_mctrl_write(wlc_hw);
+}
+
+/* write the software state of maccontrol and overrides to the maccontrol register */
+static void wlc_mctrl_write(wlc_hw_info_t *wlc_hw)
+{
+ u32 maccontrol = wlc_hw->maccontrol;
+
+ /* OR in the wake bit if overridden */
+ if (wlc_hw->wake_override)
+ maccontrol |= MCTL_WAKE;
+
+ /* set AP and INFRA bits for mute if needed */
+ if (wlc_hw->mute_override) {
+ maccontrol &= ~(MCTL_AP);
+ maccontrol |= MCTL_INFRA;
+ }
+
+ W_REG(wlc_hw->osh, &wlc_hw->regs->maccontrol, maccontrol);
+}
+
+void wlc_ucode_wake_override_set(wlc_hw_info_t *wlc_hw, u32 override_bit)
+{
+ ASSERT((wlc_hw->wake_override & override_bit) == 0);
+
+ if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) {
+ mboolset(wlc_hw->wake_override, override_bit);
+ return;
+ }
+
+ mboolset(wlc_hw->wake_override, override_bit);
+
+ wlc_mctrl_write(wlc_hw);
+ wlc_bmac_wait_for_wake(wlc_hw);
+
+ return;
+}
+
+void wlc_ucode_wake_override_clear(wlc_hw_info_t *wlc_hw, u32 override_bit)
+{
+ ASSERT(wlc_hw->wake_override & override_bit);
+
+ mboolclr(wlc_hw->wake_override, override_bit);
+
+ if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE))
+ return;
+
+ wlc_mctrl_write(wlc_hw);
+
+ return;
+}
+
+/* When driver needs ucode to stop beaconing, it has to make sure that
+ * MCTL_AP is clear and MCTL_INFRA is set
+ * Mode MCTL_AP MCTL_INFRA
+ * AP 1 1
+ * STA 0 1 <--- This will ensure no beacons
+ * IBSS 0 0
+ */
+static void wlc_ucode_mute_override_set(wlc_hw_info_t *wlc_hw)
+{
+ wlc_hw->mute_override = 1;
+
+ /* if maccontrol already has AP == 0 and INFRA == 1 without this
+ * override, then there is no change to write
+ */
+ if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
+ return;
+
+ wlc_mctrl_write(wlc_hw);
+
+ return;
+}
+
+/* Clear the override on AP and INFRA bits */
+static void wlc_ucode_mute_override_clear(wlc_hw_info_t *wlc_hw)
+{
+ if (wlc_hw->mute_override == 0)
+ return;
+
+ wlc_hw->mute_override = 0;
+
+ /* if maccontrol already has AP == 0 and INFRA == 1 without this
+ * override, then there is no change to write
+ */
+ if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
+ return;
+
+ wlc_mctrl_write(wlc_hw);
+}
+
+/*
+ * Write a MAC address to the rcmta structure
+ */
+void
+wlc_bmac_set_rcmta(wlc_hw_info_t *wlc_hw, int idx,
+ const struct ether_addr *addr)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ volatile u16 *objdata16 = (volatile u16 *)&regs->objdata;
+ u32 mac_hm;
+ u16 mac_l;
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: %s\n", wlc_hw->unit, __func__));
+
+ ASSERT(wlc_hw->corerev > 4);
+
+ mac_hm =
+ (addr->octet[3] << 24) | (addr->octet[2] << 16) | (addr->
+ octet[1] << 8) |
+ addr->octet[0];
+ mac_l = (addr->octet[5] << 8) | addr->octet[4];
+
+ osh = wlc_hw->osh;
+
+ W_REG(osh, &regs->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, &regs->objdata, mac_hm);
+ W_REG(osh, &regs->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, objdata16, mac_l);
+}
+
+/*
+ * Write a MAC address to the given match reg offset in the RXE match engine.
+ */
+void
+wlc_bmac_set_addrmatch(wlc_hw_info_t *wlc_hw, int match_reg_offset,
+ const struct ether_addr *addr)
+{
+ d11regs_t *regs;
+ u16 mac_l;
+ u16 mac_m;
+ u16 mac_h;
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: wlc_bmac_set_addrmatch\n", wlc_hw->unit));
+
+ ASSERT((match_reg_offset < RCM_SIZE) || (wlc_hw->corerev == 4));
+
+ regs = wlc_hw->regs;
+ mac_l = addr->octet[0] | (addr->octet[1] << 8);
+ mac_m = addr->octet[2] | (addr->octet[3] << 8);
+ mac_h = addr->octet[4] | (addr->octet[5] << 8);
+
+ osh = wlc_hw->osh;
+
+ /* enter the MAC addr into the RXE match registers */
+ W_REG(osh, &regs->rcm_ctl, RCM_INC_DATA | match_reg_offset);
+ W_REG(osh, &regs->rcm_mat_data, mac_l);
+ W_REG(osh, &regs->rcm_mat_data, mac_m);
+ W_REG(osh, &regs->rcm_mat_data, mac_h);
+
+}
+
+void
+wlc_bmac_write_template_ram(wlc_hw_info_t *wlc_hw, int offset, int len,
+ void *buf)
+{
+ d11regs_t *regs;
+ u32 word;
+ bool be_bit;
+#ifdef IL_BIGENDIAN
+ volatile u16 *dptr = NULL;
+#endif /* IL_BIGENDIAN */
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: wlc_bmac_write_template_ram\n", wlc_hw->unit));
+
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ ASSERT(IS_ALIGNED(offset, sizeof(u32)));
+ ASSERT(IS_ALIGNED(len, sizeof(u32)));
+ ASSERT((offset & ~0xffff) == 0);
+
+ W_REG(osh, &regs->tplatewrptr, offset);
+
+ /* if MCTL_BIGEND bit set in mac control register,
+ * the chip swaps data in fifo, as well as data in
+ * template ram
+ */
+ be_bit = (R_REG(osh, &regs->maccontrol) & MCTL_BIGEND) != 0;
+
+ while (len > 0) {
+ bcopy((u8 *) buf, &word, sizeof(u32));
+
+ if (be_bit)
+ word = hton32(word);
+ else
+ word = htol32(word);
+
+ W_REG(osh, &regs->tplatewrdata, word);
+
+ buf = (u8 *) buf + sizeof(u32);
+ len -= sizeof(u32);
+ }
+}
+
+void wlc_bmac_set_cwmin(wlc_hw_info_t *wlc_hw, u16 newmin)
+{
+ osl_t *osh;
+
+ osh = wlc_hw->osh;
+ wlc_hw->band->CWmin = newmin;
+
+ W_REG(osh, &wlc_hw->regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_CWMIN);
+ (void)R_REG(osh, &wlc_hw->regs->objaddr);
+ W_REG(osh, &wlc_hw->regs->objdata, newmin);
+}
+
+void wlc_bmac_set_cwmax(wlc_hw_info_t *wlc_hw, u16 newmax)
+{
+ osl_t *osh;
+
+ osh = wlc_hw->osh;
+ wlc_hw->band->CWmax = newmax;
+
+ W_REG(osh, &wlc_hw->regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_CWMAX);
+ (void)R_REG(osh, &wlc_hw->regs->objaddr);
+ W_REG(osh, &wlc_hw->regs->objdata, newmax);
+}
+
+void wlc_bmac_bw_set(wlc_hw_info_t *wlc_hw, u16 bw)
+{
+ bool fastclk;
+ u32 tmp;
+
+ /* request FAST clock if not on */
+ fastclk = wlc_hw->forcefastclk;
+ if (!fastclk)
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+
+ wlc_phy_bw_state_set(wlc_hw->band->pi, bw);
+
+ ASSERT(wlc_hw->clk);
+ if (D11REV_LT(wlc_hw->corerev, 17))
+ tmp = R_REG(wlc_hw->osh, &wlc_hw->regs->maccontrol);
+
+ wlc_bmac_phy_reset(wlc_hw);
+ wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi));
+
+ /* restore the clk */
+ if (!fastclk)
+ wlc_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+static void
+wlc_write_hw_bcntemplate0(wlc_hw_info_t *wlc_hw, void *bcn, int len)
+{
+ d11regs_t *regs = wlc_hw->regs;
+
+ wlc_bmac_write_template_ram(wlc_hw, T_BCN0_TPL_BASE, (len + 3) & ~3,
+ bcn);
+ /* write beacon length to SCR */
+ ASSERT(len < 65536);
+ wlc_bmac_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len);
+ /* mark beacon0 valid */
+ OR_REG(wlc_hw->osh, &regs->maccommand, MCMD_BCN0VLD);
+}
+
+static void
+wlc_write_hw_bcntemplate1(wlc_hw_info_t *wlc_hw, void *bcn, int len)
+{
+ d11regs_t *regs = wlc_hw->regs;
+
+ wlc_bmac_write_template_ram(wlc_hw, T_BCN1_TPL_BASE, (len + 3) & ~3,
+ bcn);
+ /* write beacon length to SCR */
+ ASSERT(len < 65536);
+ wlc_bmac_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len);
+ /* mark beacon1 valid */
+ OR_REG(wlc_hw->osh, &regs->maccommand, MCMD_BCN1VLD);
+}
+
+/* mac is assumed to be suspended at this point */
+void
+wlc_bmac_write_hw_bcntemplates(wlc_hw_info_t *wlc_hw, void *bcn, int len,
+ bool both)
+{
+ d11regs_t *regs = wlc_hw->regs;
+
+ if (both) {
+ wlc_write_hw_bcntemplate0(wlc_hw, bcn, len);
+ wlc_write_hw_bcntemplate1(wlc_hw, bcn, len);
+ } else {
+ /* bcn 0 */
+ if (!(R_REG(wlc_hw->osh, &regs->maccommand) & MCMD_BCN0VLD))
+ wlc_write_hw_bcntemplate0(wlc_hw, bcn, len);
+ /* bcn 1 */
+ else if (!
+ (R_REG(wlc_hw->osh, &regs->maccommand) & MCMD_BCN1VLD))
+ wlc_write_hw_bcntemplate1(wlc_hw, bcn, len);
+ else /* one template should always have been available */
+ ASSERT(0);
+ }
+}
+
+static void WLBANDINITFN(wlc_bmac_upd_synthpu) (wlc_hw_info_t *wlc_hw)
+{
+ u16 v;
+ wlc_info_t *wlc = wlc_hw->wlc;
+ /* update SYNTHPU_DLY */
+
+ if (WLCISLCNPHY(wlc->band)) {
+ v = SYNTHPU_DLY_LPPHY_US;
+ } else if (WLCISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3))) {
+ v = SYNTHPU_DLY_NPHY_US;
+ } else {
+ v = SYNTHPU_DLY_BPHY_US;
+ }
+
+ wlc_bmac_write_shm(wlc_hw, M_SYNTHPU_DLY, v);
+}
+
+/* band-specific init */
+static void
+WLBANDINITFN(wlc_bmac_bsinit) (wlc_info_t *wlc, chanspec_t chanspec)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+
+ WL_TRACE(("wl%d: wlc_bmac_bsinit: bandunit %d\n", wlc_hw->unit,
+ wlc_hw->band->bandunit));
+
+ /* sanity check */
+ if (PHY_TYPE(R_REG(wlc_hw->osh, &wlc_hw->regs->phyversion)) !=
+ PHY_TYPE_LCNXN)
+ ASSERT((uint)
+ PHY_TYPE(R_REG(wlc_hw->osh, &wlc_hw->regs->phyversion))
+ == wlc_hw->band->phytype);
+
+ wlc_ucode_bsinit(wlc_hw);
+
+ wlc_phy_init(wlc_hw->band->pi, chanspec);
+
+ wlc_ucode_txant_set(wlc_hw);
+
+ /* cwmin is band-specific, update hardware with value for current band */
+ wlc_bmac_set_cwmin(wlc_hw, wlc_hw->band->CWmin);
+ wlc_bmac_set_cwmax(wlc_hw, wlc_hw->band->CWmax);
+
+ wlc_bmac_update_slot_timing(wlc_hw,
+ BAND_5G(wlc_hw->band->
+ bandtype) ? true : wlc_hw->
+ shortslot);
+
+ /* write phytype and phyvers */
+ wlc_bmac_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype);
+ wlc_bmac_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev);
+
+ /* initialize the txphyctl1 rate table since shmem is shared between bands */
+ wlc_upd_ofdm_pctl1_table(wlc_hw);
+
+ wlc_bmac_upd_synthpu(wlc_hw);
+}
+
+void wlc_bmac_core_phy_clk(wlc_hw_info_t *wlc_hw, bool clk)
+{
+ WL_TRACE(("wl%d: wlc_bmac_core_phy_clk: clk %d\n", wlc_hw->unit, clk));
+
+ wlc_hw->phyclk = clk;
+
+ if (OFF == clk) { /* clear gmode bit, put phy into reset */
+
+ si_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC | SICF_GMODE),
+ (SICF_PRST | SICF_FGC));
+ udelay(1);
+ si_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_PRST);
+ udelay(1);
+
+ } else { /* take phy out of reset */
+
+ si_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_FGC);
+ udelay(1);
+ si_core_cflags(wlc_hw->sih, (SICF_FGC), 0);
+ udelay(1);
+
+ }
+}
+
+/* Perform a soft reset of the PHY PLL */
+void wlc_bmac_core_phypll_reset(wlc_hw_info_t *wlc_hw)
+{
+ WL_TRACE(("wl%d: wlc_bmac_core_phypll_reset\n", wlc_hw->unit));
+
+ si_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(chipcregs_t, chipcontrol_addr), ~0, 0);
+ udelay(1);
+ si_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(chipcregs_t, chipcontrol_data), 0x4, 0);
+ udelay(1);
+ si_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(chipcregs_t, chipcontrol_data), 0x4, 4);
+ udelay(1);
+ si_corereg(wlc_hw->sih, SI_CC_IDX,
+ offsetof(chipcregs_t, chipcontrol_data), 0x4, 0);
+ udelay(1);
+}
+
+/* light way to turn on phy clock without reset for NPHY only
+ * refer to wlc_bmac_core_phy_clk for full version
+ */
+void wlc_bmac_phyclk_fgc(wlc_hw_info_t *wlc_hw, bool clk)
+{
+ /* support(necessary for NPHY and HYPHY) only */
+ if (!WLCISNPHY(wlc_hw->band))
+ return;
+
+ if (ON == clk)
+ si_core_cflags(wlc_hw->sih, SICF_FGC, SICF_FGC);
+ else
+ si_core_cflags(wlc_hw->sih, SICF_FGC, 0);
+
+}
+
+void wlc_bmac_macphyclk_set(wlc_hw_info_t *wlc_hw, bool clk)
+{
+ if (ON == clk)
+ si_core_cflags(wlc_hw->sih, SICF_MPCLKE, SICF_MPCLKE);
+ else
+ si_core_cflags(wlc_hw->sih, SICF_MPCLKE, 0);
+}
+
+void wlc_bmac_phy_reset(wlc_hw_info_t *wlc_hw)
+{
+ wlc_phy_t *pih = wlc_hw->band->pi;
+ u32 phy_bw_clkbits;
+ bool phy_in_reset = false;
+
+ WL_TRACE(("wl%d: wlc_bmac_phy_reset\n", wlc_hw->unit));
+
+ if (pih == NULL)
+ return;
+
+ phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi);
+
+ /* Specfic reset sequence required for NPHY rev 3 and 4 */
+ if (WLCISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) &&
+ NREV_LE(wlc_hw->band->phyrev, 4)) {
+ /* Set the PHY bandwidth */
+ si_core_cflags(wlc_hw->sih, SICF_BWMASK, phy_bw_clkbits);
+
+ udelay(1);
+
+ /* Perform a soft reset of the PHY PLL */
+ wlc_bmac_core_phypll_reset(wlc_hw);
+
+ /* reset the PHY */
+ si_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_PCLKE),
+ (SICF_PRST | SICF_PCLKE));
+ phy_in_reset = true;
+ } else {
+
+ si_core_cflags(wlc_hw->sih,
+ (SICF_PRST | SICF_PCLKE | SICF_BWMASK),
+ (SICF_PRST | SICF_PCLKE | phy_bw_clkbits));
+ }
+
+ udelay(2);
+ wlc_bmac_core_phy_clk(wlc_hw, ON);
+
+ if (pih)
+ wlc_phy_anacore(pih, ON);
+}
+
+/* switch to and initialize new band */
+static void
+WLBANDINITFN(wlc_bmac_setband) (wlc_hw_info_t *wlc_hw, uint bandunit,
+ chanspec_t chanspec) {
+ wlc_info_t *wlc = wlc_hw->wlc;
+ u32 macintmask;
+
+ ASSERT(NBANDS_HW(wlc_hw) > 1);
+ ASSERT(bandunit != wlc_hw->band->bandunit);
+
+ /* Enable the d11 core before accessing it */
+ if (!si_iscoreup(wlc_hw->sih)) {
+ si_core_reset(wlc_hw->sih, 0, 0);
+ ASSERT(si_iscoreup(wlc_hw->sih));
+ wlc_mctrl_reset(wlc_hw);
+ }
+
+ macintmask = wlc_setband_inact(wlc, bandunit);
+
+ if (!wlc_hw->up)
+ return;
+
+ wlc_bmac_core_phy_clk(wlc_hw, ON);
+
+ /* band-specific initializations */
+ wlc_bmac_bsinit(wlc, chanspec);
+
+ /*
+ * If there are any pending software interrupt bits,
+ * then replace these with a harmless nonzero value
+ * so wlc_dpc() will re-enable interrupts when done.
+ */
+ if (wlc->macintstatus)
+ wlc->macintstatus = MI_DMAINT;
+
+ /* restore macintmask */
+ wl_intrsrestore(wlc->wl, macintmask);
+
+ /* ucode should still be suspended.. */
+ ASSERT((R_REG(wlc_hw->osh, &wlc_hw->regs->maccontrol) & MCTL_EN_MAC) ==
+ 0);
+}
+
+/* low-level band switch utility routine */
+void WLBANDINITFN(wlc_setxband) (wlc_hw_info_t *wlc_hw, uint bandunit)
+{
+ WL_TRACE(("wl%d: wlc_setxband: bandunit %d\n", wlc_hw->unit, bandunit));
+
+ wlc_hw->band = wlc_hw->bandstate[bandunit];
+
+ /* BMAC_NOTE: until we eliminate need for wlc->band refs in low level code */
+ wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit];
+
+ /* set gmode core flag */
+ if (wlc_hw->sbclk && !wlc_hw->noreset) {
+ si_core_cflags(wlc_hw->sih, SICF_GMODE,
+ ((bandunit == 0) ? SICF_GMODE : 0));
+ }
+}
+
+static bool wlc_isgoodchip(wlc_hw_info_t *wlc_hw)
+{
+
+ /* reject unsupported corerev */
+ if (!VALID_COREREV(wlc_hw->corerev)) {
+ WL_ERROR(("unsupported core rev %d\n", wlc_hw->corerev));
+ return false;
+ }
+
+ return true;
+}
+
+static bool wlc_validboardtype(wlc_hw_info_t *wlc_hw)
+{
+ bool goodboard = true;
+ uint boardrev = wlc_hw->boardrev;
+
+ if (boardrev == 0)
+ goodboard = false;
+ else if (boardrev > 0xff) {
+ uint brt = (boardrev & 0xf000) >> 12;
+ uint b0 = (boardrev & 0xf00) >> 8;
+ uint b1 = (boardrev & 0xf0) >> 4;
+ uint b2 = boardrev & 0xf;
+
+ if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9)
+ || (b2 > 9))
+ goodboard = false;
+ }
+
+ if (wlc_hw->sih->boardvendor != VENDOR_BROADCOM)
+ return goodboard;
+
+ return goodboard;
+}
+
+static char *wlc_get_macaddr(wlc_hw_info_t *wlc_hw)
+{
+ const char *varname = "macaddr";
+ char *macaddr;
+
+ /* If macaddr exists, use it (Sromrev4, CIS, ...). */
+ macaddr = getvar(wlc_hw->vars, varname);
+ if (macaddr != NULL)
+ return macaddr;
+
+ if (NBANDS_HW(wlc_hw) > 1)
+ varname = "et1macaddr";
+ else
+ varname = "il0macaddr";
+
+ macaddr = getvar(wlc_hw->vars, varname);
+ if (macaddr == NULL) {
+ WL_ERROR(("wl%d: wlc_get_macaddr: macaddr getvar(%s) not found\n", wlc_hw->unit, varname));
+ }
+
+ return macaddr;
+}
+
+/*
+ * Return true if radio is disabled, otherwise false.
+ * hw radio disable signal is an external pin, users activate it asynchronously
+ * this function could be called when driver is down and w/o clock
+ * it operates on different registers depending on corerev and boardflag.
+ */
+bool wlc_bmac_radio_read_hwdisabled(wlc_hw_info_t *wlc_hw)
+{
+ bool v, clk, xtal;
+ u32 resetbits = 0, flags = 0;
+
+ xtal = wlc_hw->sbclk;
+ if (!xtal)
+ wlc_bmac_xtal(wlc_hw, ON);
+
+ /* may need to take core out of reset first */
+ clk = wlc_hw->clk;
+ if (!clk) {
+ if (D11REV_LE(wlc_hw->corerev, 11))
+ resetbits |= SICF_PCLKE;
+
+ /*
+ * corerev >= 18, mac no longer enables phyclk automatically when driver accesses
+ * phyreg throughput mac. This can be skipped since only mac reg is accessed below
+ */
+ if (D11REV_GE(wlc_hw->corerev, 18))
+ flags |= SICF_PCLKE;
+
+ /* AI chip doesn't restore bar0win2 on hibernation/resume, need sw fixup */
+ if ((CHIPID(wlc_hw->sih->chip) == BCM43224_CHIP_ID) ||
+ (CHIPID(wlc_hw->sih->chip) == BCM43225_CHIP_ID) ||
+ (CHIPID(wlc_hw->sih->chip) == BCM43421_CHIP_ID))
+ wlc_hw->regs =
+ (d11regs_t *) si_setcore(wlc_hw->sih, D11_CORE_ID,
+ 0);
+ si_core_reset(wlc_hw->sih, flags, resetbits);
+ wlc_mctrl_reset(wlc_hw);
+ }
+
+ v = ((R_REG(wlc_hw->osh, &wlc_hw->regs->phydebug) & PDBG_RFD) != 0);
+
+ /* put core back into reset */
+ if (!clk)
+ si_core_disable(wlc_hw->sih, 0);
+
+ if (!xtal)
+ wlc_bmac_xtal(wlc_hw, OFF);
+
+ return v;
+}
+
+/* Initialize just the hardware when coming out of POR or S3/S5 system states */
+void wlc_bmac_hw_up(wlc_hw_info_t *wlc_hw)
+{
+ if (wlc_hw->wlc->pub->hw_up)
+ return;
+
+ WL_TRACE(("wl%d: %s:\n", wlc_hw->unit, __func__));
+
+ /*
+ * Enable pll and xtal, initialize the power control registers,
+ * and force fastclock for the remainder of wlc_up().
+ */
+ wlc_bmac_xtal(wlc_hw, ON);
+ si_clkctl_init(wlc_hw->sih);
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+
+ if (BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS) {
+ si_pci_fixcfg(wlc_hw->sih);
+
+ /* AI chip doesn't restore bar0win2 on hibernation/resume, need sw fixup */
+ if ((CHIPID(wlc_hw->sih->chip) == BCM43224_CHIP_ID) ||
+ (CHIPID(wlc_hw->sih->chip) == BCM43225_CHIP_ID) ||
+ (CHIPID(wlc_hw->sih->chip) == BCM43421_CHIP_ID))
+ wlc_hw->regs =
+ (d11regs_t *) si_setcore(wlc_hw->sih, D11_CORE_ID,
+ 0);
+ }
+
+ /* Inform phy that a POR reset has occurred so it does a complete phy init */
+ wlc_phy_por_inform(wlc_hw->band->pi);
+
+ wlc_hw->ucode_loaded = false;
+ wlc_hw->wlc->pub->hw_up = true;
+
+ if ((wlc_hw->boardflags & BFL_FEM)
+ && (CHIPID(wlc_hw->sih->chip) == BCM4313_CHIP_ID)) {
+ if (!
+ (wlc_hw->boardrev >= 0x1250
+ && (wlc_hw->boardflags & BFL_FEM_BT)))
+ si_epa_4313war(wlc_hw->sih);
+ }
+}
+
+static bool wlc_dma_rxreset(wlc_hw_info_t *wlc_hw, uint fifo)
+{
+ hnddma_t *di = wlc_hw->di[fifo];
+ osl_t *osh;
+
+ if (D11REV_LT(wlc_hw->corerev, 12)) {
+ bool rxidle = true;
+ u16 rcv_frm_cnt = 0;
+
+ osh = wlc_hw->osh;
+
+ W_REG(osh, &wlc_hw->regs->rcv_fifo_ctl, fifo << 8);
+ SPINWAIT((!(rxidle = dma_rxidle(di))) &&
+ ((rcv_frm_cnt =
+ R_REG(osh, &wlc_hw->regs->rcv_frm_cnt)) != 0),
+ 50000);
+
+ if (!rxidle && (rcv_frm_cnt != 0))
+ WL_ERROR(("wl%d: %s: rxdma[%d] not idle && rcv_frm_cnt(%d) not zero\n", wlc_hw->unit, __func__, fifo, rcv_frm_cnt));
+ mdelay(2);
+ }
+
+ return dma_rxreset(di);
+}
+
+/* d11 core reset
+ * ensure fask clock during reset
+ * reset dma
+ * reset d11(out of reset)
+ * reset phy(out of reset)
+ * clear software macintstatus for fresh new start
+ * one testing hack wlc_hw->noreset will bypass the d11/phy reset
+ */
+void wlc_bmac_corereset(wlc_hw_info_t *wlc_hw, u32 flags)
+{
+ d11regs_t *regs;
+ uint i;
+ bool fastclk;
+ u32 resetbits = 0;
+
+ if (flags == WLC_USE_COREFLAGS)
+ flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0);
+
+ WL_TRACE(("wl%d: %s\n", wlc_hw->unit, __func__));
+
+ regs = wlc_hw->regs;
+
+ /* request FAST clock if not on */
+ fastclk = wlc_hw->forcefastclk;
+ if (!fastclk)
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+
+ /* reset the dma engines except first time thru */
+ if (si_iscoreup(wlc_hw->sih)) {
+ for (i = 0; i < NFIFO; i++)
+ if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) {
+ WL_ERROR(("wl%d: %s: dma_txreset[%d]: cannot stop dma\n", wlc_hw->unit, __func__, i));
+ }
+
+ if ((wlc_hw->di[RX_FIFO])
+ && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) {
+ WL_ERROR(("wl%d: %s: dma_rxreset[%d]: cannot stop dma\n", wlc_hw->unit, __func__, RX_FIFO));
+ }
+ if (D11REV_IS(wlc_hw->corerev, 4)
+ && wlc_hw->di[RX_TXSTATUS_FIFO]
+ && (!wlc_dma_rxreset(wlc_hw, RX_TXSTATUS_FIFO))) {
+ WL_ERROR(("wl%d: %s: dma_rxreset[%d]: cannot stop dma\n", wlc_hw->unit, __func__, RX_TXSTATUS_FIFO));
+ }
+ }
+ /* if noreset, just stop the psm and return */
+ if (wlc_hw->noreset) {
+ wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */
+ wlc_bmac_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0);
+ return;
+ }
+
+ if (D11REV_LE(wlc_hw->corerev, 11))
+ resetbits |= SICF_PCLKE;
+
+ /*
+ * corerev >= 18, mac no longer enables phyclk automatically when driver accesses phyreg
+ * throughput mac, AND phy_reset is skipped at early stage when band->pi is invalid
+ * need to enable PHY CLK
+ */
+ if (D11REV_GE(wlc_hw->corerev, 18))
+ flags |= SICF_PCLKE;
+
+ /* reset the core
+ * In chips with PMU, the fastclk request goes through d11 core reg 0x1e0, which
+ * is cleared by the core_reset. have to re-request it.
+ * This adds some delay and we can optimize it by also requesting fastclk through
+ * chipcommon during this period if necessary. But that has to work coordinate
+ * with other driver like mips/arm since they may touch chipcommon as well.
+ */
+ wlc_hw->clk = false;
+ si_core_reset(wlc_hw->sih, flags, resetbits);
+ wlc_hw->clk = true;
+ if (wlc_hw->band && wlc_hw->band->pi)
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true);
+
+ wlc_mctrl_reset(wlc_hw);
+
+ if (PMUCTL_ENAB(wlc_hw->sih))
+ wlc_clkctl_clk(wlc_hw, CLK_FAST);
+
+ wlc_bmac_phy_reset(wlc_hw);
+
+ /* turn on PHY_PLL */
+ wlc_bmac_core_phypll_ctl(wlc_hw, true);
+
+ /* clear sw intstatus */
+ wlc_hw->wlc->macintstatus = 0;
+
+ /* restore the clk setting */
+ if (!fastclk)
+ wlc_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+/* If the ucode that supports corerev 5 is used for corerev 9 and above,
+ * txfifo sizes needs to be modified(increased) since the newer cores
+ * have more memory.
+ */
+static void wlc_corerev_fifofixup(wlc_hw_info_t *wlc_hw)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ u16 fifo_nu;
+ u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk;
+ u16 txfifo_def, txfifo_def1;
+ u16 txfifo_cmd;
+ osl_t *osh;
+
+ if (D11REV_LT(wlc_hw->corerev, 9))
+ goto exit;
+
+ /* tx fifos start at TXFIFO_START_BLK from the Base address */
+ txfifo_startblk = TXFIFO_START_BLK;
+
+ osh = wlc_hw->osh;
+
+ /* sequence of operations: reset fifo, set fifo size, reset fifo */
+ for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) {
+
+ txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu];
+ txfifo_def = (txfifo_startblk & 0xff) |
+ (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT);
+ txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) |
+ ((((txfifo_endblk -
+ 1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT);
+ txfifo_cmd =
+ TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT);
+
+ W_REG(osh, &regs->xmtfifocmd, txfifo_cmd);
+ W_REG(osh, &regs->xmtfifodef, txfifo_def);
+ if (D11REV_GE(wlc_hw->corerev, 16))
+ W_REG(osh, &regs->xmtfifodef1, txfifo_def1);
+
+ W_REG(osh, &regs->xmtfifocmd, txfifo_cmd);
+
+ txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu];
+ }
+ exit:
+ /* need to propagate to shm location to be in sync since ucode/hw won't do this */
+ wlc_bmac_write_shm(wlc_hw, M_FIFOSIZE0,
+ wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]);
+ wlc_bmac_write_shm(wlc_hw, M_FIFOSIZE1,
+ wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]);
+ wlc_bmac_write_shm(wlc_hw, M_FIFOSIZE2,
+ ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw->
+ xmtfifo_sz[TX_AC_BK_FIFO]));
+ wlc_bmac_write_shm(wlc_hw, M_FIFOSIZE3,
+ ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw->
+ xmtfifo_sz[TX_BCMC_FIFO]));
+}
+
+/* d11 core init
+ * reset PSM
+ * download ucode/PCM
+ * let ucode run to suspended
+ * download ucode inits
+ * config other core registers
+ * init dma
+ */
+static void wlc_coreinit(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ d11regs_t *regs;
+ u32 sflags;
+ uint bcnint_us;
+ uint i = 0;
+ bool fifosz_fixup = false;
+ osl_t *osh;
+ int err = 0;
+ u16 buf[NFIFO];
+
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ WL_TRACE(("wl%d: wlc_coreinit\n", wlc_hw->unit));
+
+ /* reset PSM */
+ wlc_bmac_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE));
+
+ wlc_ucode_download(wlc_hw);
+ /*
+ * FIFOSZ fixup
+ * 1) core5-9 use ucode 5 to save space since the PSM is the same
+ * 2) newer chips, driver wants to controls the fifo allocation
+ */
+ if (D11REV_GE(wlc_hw->corerev, 4))
+ fifosz_fixup = true;
+
+ /* let the PSM run to the suspended state, set mode to BSS STA */
+ W_REG(osh, &regs->macintstatus, -1);
+ wlc_bmac_mctrl(wlc_hw, ~0,
+ (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE));
+
+ /* wait for ucode to self-suspend after auto-init */
+ SPINWAIT(((R_REG(osh, &regs->macintstatus) & MI_MACSSPNDD) == 0),
+ 1000 * 1000);
+ if ((R_REG(osh, &regs->macintstatus) & MI_MACSSPNDD) == 0)
+ WL_ERROR(("wl%d: wlc_coreinit: ucode did not self-suspend!\n",
+ wlc_hw->unit));
+
+ wlc_gpio_init(wlc);
+
+ sflags = si_core_sflags(wlc_hw->sih, 0, 0);
+
+ if (D11REV_IS(wlc_hw->corerev, 23)) {
+ if (WLCISNPHY(wlc_hw->band))
+ wlc_write_inits(wlc_hw, d11n0initvals16);
+ else
+ WL_ERROR(("%s: wl%d: unsupported phy in corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ } else if (D11REV_IS(wlc_hw->corerev, 24)) {
+ if (WLCISLCNPHY(wlc_hw->band)) {
+ wlc_write_inits(wlc_hw, d11lcn0initvals24);
+ } else {
+ WL_ERROR(("%s: wl%d: unsupported phy in corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ }
+ } else {
+ WL_ERROR(("%s: wl%d: unsupported corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ }
+
+ /* For old ucode, txfifo sizes needs to be modified(increased) for Corerev >= 9 */
+ if (fifosz_fixup == true) {
+ wlc_corerev_fifofixup(wlc_hw);
+ }
+
+ /* check txfifo allocations match between ucode and driver */
+ buf[TX_AC_BE_FIFO] = wlc_bmac_read_shm(wlc_hw, M_FIFOSIZE0);
+ if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) {
+ i = TX_AC_BE_FIFO;
+ err = -1;
+ }
+ buf[TX_AC_VI_FIFO] = wlc_bmac_read_shm(wlc_hw, M_FIFOSIZE1);
+ if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) {
+ i = TX_AC_VI_FIFO;
+ err = -1;
+ }
+ buf[TX_AC_BK_FIFO] = wlc_bmac_read_shm(wlc_hw, M_FIFOSIZE2);
+ buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff;
+ buf[TX_AC_BK_FIFO] &= 0xff;
+ if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) {
+ i = TX_AC_BK_FIFO;
+ err = -1;
+ }
+ if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) {
+ i = TX_AC_VO_FIFO;
+ err = -1;
+ }
+ buf[TX_BCMC_FIFO] = wlc_bmac_read_shm(wlc_hw, M_FIFOSIZE3);
+ buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff;
+ buf[TX_BCMC_FIFO] &= 0xff;
+ if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) {
+ i = TX_BCMC_FIFO;
+ err = -1;
+ }
+ if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) {
+ i = TX_ATIM_FIFO;
+ err = -1;
+ }
+ if (err != 0) {
+ WL_ERROR(("wlc_coreinit: txfifo mismatch: ucode size %d driver size %d index %d\n", buf[i], wlc_hw->xmtfifo_sz[i], i));
+ /* DO NOT ASSERT corerev < 4 even there is a mismatch
+ * shmem, since driver don't overwrite those chip and
+ * ucode initialize data will be used.
+ */
+ if (D11REV_GE(wlc_hw->corerev, 4))
+ ASSERT(0);
+ }
+
+ /* make sure we can still talk to the mac */
+ ASSERT(R_REG(osh, &regs->maccontrol) != 0xffffffff);
+
+ /* band-specific inits done by wlc_bsinit() */
+
+ /* Set up frame burst size and antenna swap threshold init values */
+ wlc_bmac_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST);
+ wlc_bmac_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT);
+
+ /* enable one rx interrupt per received frame */
+ W_REG(osh, &regs->intrcvlazy[0], (1 << IRL_FC_SHIFT));
+ if (D11REV_IS(wlc_hw->corerev, 4))
+ W_REG(osh, &regs->intrcvlazy[3], (1 << IRL_FC_SHIFT));
+
+ /* set the station mode (BSS STA) */
+ wlc_bmac_mctrl(wlc_hw,
+ (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP),
+ (MCTL_INFRA | MCTL_DISCARD_PMQ));
+
+ /* set up Beacon interval */
+ bcnint_us = 0x8000 << 10;
+ W_REG(osh, &regs->tsf_cfprep, (bcnint_us << CFPREP_CBI_SHIFT));
+ W_REG(osh, &regs->tsf_cfpstart, bcnint_us);
+ W_REG(osh, &regs->macintstatus, MI_GP1);
+
+ /* write interrupt mask */
+ W_REG(osh, &regs->intctrlregs[RX_FIFO].intmask, DEF_RXINTMASK);
+ if (D11REV_IS(wlc_hw->corerev, 4))
+ W_REG(osh, &regs->intctrlregs[RX_TXSTATUS_FIFO].intmask,
+ DEF_RXINTMASK);
+
+ /* allow the MAC to control the PHY clock (dynamic on/off) */
+ wlc_bmac_macphyclk_set(wlc_hw, ON);
+
+ /* program dynamic clock control fast powerup delay register */
+ if (D11REV_GT(wlc_hw->corerev, 4)) {
+ wlc->fastpwrup_dly = si_clkctl_fast_pwrup_delay(wlc_hw->sih);
+ W_REG(osh, &regs->scc_fastpwrup_dly, wlc->fastpwrup_dly);
+ }
+
+ /* tell the ucode the corerev */
+ wlc_bmac_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev);
+
+ /* tell the ucode MAC capabilities */
+ if (D11REV_GE(wlc_hw->corerev, 13)) {
+ wlc_bmac_write_shm(wlc_hw, M_MACHW_CAP_L,
+ (u16) (wlc_hw->machwcap & 0xffff));
+ wlc_bmac_write_shm(wlc_hw, M_MACHW_CAP_H,
+ (u16) ((wlc_hw->
+ machwcap >> 16) & 0xffff));
+ }
+
+ /* write retry limits to SCR, this done after PSM init */
+ W_REG(osh, &regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, &regs->objdata, wlc_hw->SRL);
+ W_REG(osh, &regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, &regs->objdata, wlc_hw->LRL);
+
+ /* write rate fallback retry limits */
+ wlc_bmac_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL);
+ wlc_bmac_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL);
+
+ if (D11REV_GE(wlc_hw->corerev, 16)) {
+ AND_REG(osh, &regs->ifs_ctl, 0x0FFF);
+ W_REG(osh, &regs->ifs_aifsn, EDCF_AIFSN_MIN);
+ }
+
+ /* dma initializations */
+ wlc->txpend16165war = 0;
+
+ /* init the tx dma engines */
+ for (i = 0; i < NFIFO; i++) {
+ if (wlc_hw->di[i])
+ dma_txinit(wlc_hw->di[i]);
+ }
+
+ /* init the rx dma engine(s) and post receive buffers */
+ dma_rxinit(wlc_hw->di[RX_FIFO]);
+ dma_rxfill(wlc_hw->di[RX_FIFO]);
+ if (D11REV_IS(wlc_hw->corerev, 4)) {
+ dma_rxinit(wlc_hw->di[RX_TXSTATUS_FIFO]);
+ dma_rxfill(wlc_hw->di[RX_TXSTATUS_FIFO]);
+ }
+}
+
+/* This function is used for changing the tsf frac register
+ * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz
+ * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz
+ * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz
+ * HTPHY Formula is 2^26/freq(MHz) e.g.
+ * For spuron2 - 126MHz -> 2^26/126 = 532610.0
+ * - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082
+ * For spuron: 123MHz -> 2^26/123 = 545600.5
+ * - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341
+ * For spur off: 120MHz -> 2^26/120 = 559240.5
+ * - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889
+ */
+
+void wlc_bmac_switch_macfreq(wlc_hw_info_t *wlc_hw, u8 spurmode)
+{
+ d11regs_t *regs;
+ osl_t *osh;
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ if ((CHIPID(wlc_hw->sih->chip) == BCM43224_CHIP_ID) ||
+ (CHIPID(wlc_hw->sih->chip) == BCM43225_CHIP_ID)) {
+ if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */
+ W_REG(osh, &regs->tsf_clk_frac_l, 0x2082);
+ W_REG(osh, &regs->tsf_clk_frac_h, 0x8);
+ } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */
+ W_REG(osh, &regs->tsf_clk_frac_l, 0x5341);
+ W_REG(osh, &regs->tsf_clk_frac_h, 0x8);
+ } else { /* 120Mhz */
+ W_REG(osh, &regs->tsf_clk_frac_l, 0x8889);
+ W_REG(osh, &regs->tsf_clk_frac_h, 0x8);
+ }
+ } else if (WLCISLCNPHY(wlc_hw->band)) {
+ if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */
+ W_REG(osh, &regs->tsf_clk_frac_l, 0x7CE0);
+ W_REG(osh, &regs->tsf_clk_frac_h, 0xC);
+ } else { /* 80Mhz */
+ W_REG(osh, &regs->tsf_clk_frac_l, 0xCCCD);
+ W_REG(osh, &regs->tsf_clk_frac_h, 0xC);
+ }
+ }
+}
+
+/* Initialize GPIOs that are controlled by D11 core */
+static void wlc_gpio_init(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ d11regs_t *regs;
+ u32 gc, gm;
+ osl_t *osh;
+
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ /* use GPIO select 0 to get all gpio signals from the gpio out reg */
+ wlc_bmac_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0);
+
+ /*
+ * Common GPIO setup:
+ * G0 = LED 0 = WLAN Activity
+ * G1 = LED 1 = WLAN 2.4 GHz Radio State
+ * G2 = LED 2 = WLAN 5 GHz Radio State
+ * G4 = radio disable input (HI enabled, LO disabled)
+ */
+
+ gc = gm = 0;
+
+ /* Allocate GPIOs for mimo antenna diversity feature */
+ if (WLANTSEL_ENAB(wlc)) {
+ if (wlc_hw->antsel_type == ANTSEL_2x3) {
+ /* Enable antenna diversity, use 2x3 mode */
+ wlc_bmac_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
+ MHF3_ANTSEL_EN, WLC_BAND_ALL);
+ wlc_bmac_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE,
+ MHF3_ANTSEL_MODE, WLC_BAND_ALL);
+
+ /* init superswitch control */
+ wlc_phy_antsel_init(wlc_hw->band->pi, false);
+
+ } else if (wlc_hw->antsel_type == ANTSEL_2x4) {
+ ASSERT((gm & BOARD_GPIO_12) == 0);
+ gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13);
+ /* The board itself is powered by these GPIOs (when not sending pattern)
+ * So set them high
+ */
+ OR_REG(osh, &regs->psm_gpio_oe,
+ (BOARD_GPIO_12 | BOARD_GPIO_13));
+ OR_REG(osh, &regs->psm_gpio_out,
+ (BOARD_GPIO_12 | BOARD_GPIO_13));
+
+ /* Enable antenna diversity, use 2x4 mode */
+ wlc_bmac_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
+ MHF3_ANTSEL_EN, WLC_BAND_ALL);
+ wlc_bmac_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0,
+ WLC_BAND_ALL);
+
+ /* Configure the desired clock to be 4Mhz */
+ wlc_bmac_write_shm(wlc_hw, M_ANTSEL_CLKDIV,
+ ANTSEL_CLKDIV_4MHZ);
+ }
+ }
+ /* gpio 9 controls the PA. ucode is responsible for wiggling out and oe */
+ if (wlc_hw->boardflags & BFL_PACTRL)
+ gm |= gc |= BOARD_GPIO_PACTRL;
+
+ /* apply to gpiocontrol register */
+ si_gpiocontrol(wlc_hw->sih, gm, gc, GPIO_DRV_PRIORITY);
+}
+
+static void wlc_ucode_download(wlc_hw_info_t *wlc_hw)
+{
+ wlc_info_t *wlc;
+ wlc = wlc_hw->wlc;
+
+ if (wlc_hw->ucode_loaded)
+ return;
+
+ if (D11REV_IS(wlc_hw->corerev, 23)) {
+ if (WLCISNPHY(wlc_hw->band)) {
+ wlc_ucode_write(wlc_hw, bcm43xx_16_mimo,
+ bcm43xx_16_mimosz);
+ wlc_hw->ucode_loaded = true;
+ } else
+ WL_ERROR(("%s: wl%d: unsupported phy in corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ } else if (D11REV_IS(wlc_hw->corerev, 24)) {
+ if (WLCISLCNPHY(wlc_hw->band)) {
+ wlc_ucode_write(wlc_hw, bcm43xx_24_lcn,
+ bcm43xx_24_lcnsz);
+ wlc_hw->ucode_loaded = true;
+ } else {
+ WL_ERROR(("%s: wl%d: unsupported phy in corerev %d\n",
+ __func__, wlc_hw->unit, wlc_hw->corerev));
+ }
+ }
+}
+
+static void wlc_ucode_write(wlc_hw_info_t *wlc_hw, const u32 ucode[],
+ const uint nbytes) {
+ osl_t *osh;
+ d11regs_t *regs = wlc_hw->regs;
+ uint i;
+ uint count;
+
+ osh = wlc_hw->osh;
+
+ WL_TRACE(("wl%d: wlc_ucode_write\n", wlc_hw->unit));
+
+ ASSERT(IS_ALIGNED(nbytes, sizeof(u32)));
+
+ count = (nbytes / sizeof(u32));
+
+ W_REG(osh, &regs->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
+ (void)R_REG(osh, &regs->objaddr);
+ for (i = 0; i < count; i++)
+ W_REG(osh, &regs->objdata, ucode[i]);
+}
+
+static void wlc_write_inits(wlc_hw_info_t *wlc_hw, const d11init_t *inits)
+{
+ int i;
+ osl_t *osh;
+ volatile u8 *base;
+
+ WL_TRACE(("wl%d: wlc_write_inits\n", wlc_hw->unit));
+
+ osh = wlc_hw->osh;
+ base = (volatile u8 *)wlc_hw->regs;
+
+ for (i = 0; inits[i].addr != 0xffff; i++) {
+ ASSERT((inits[i].size == 2) || (inits[i].size == 4));
+
+ if (inits[i].size == 2)
+ W_REG(osh, (u16 *)(base + inits[i].addr),
+ inits[i].value);
+ else if (inits[i].size == 4)
+ W_REG(osh, (u32 *)(base + inits[i].addr),
+ inits[i].value);
+ }
+}
+
+static void wlc_ucode_txant_set(wlc_hw_info_t *wlc_hw)
+{
+ u16 phyctl;
+ u16 phytxant = wlc_hw->bmac_phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
+
+ /* set the Probe Response frame phy control word */
+ phyctl = wlc_bmac_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS);
+ phyctl = (phyctl & ~mask) | phytxant;
+ wlc_bmac_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl);
+
+ /* set the Response (ACK/CTS) frame phy control word */
+ phyctl = wlc_bmac_read_shm(wlc_hw, M_RSP_PCTLWD);
+ phyctl = (phyctl & ~mask) | phytxant;
+ wlc_bmac_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl);
+}
+
+void wlc_bmac_txant_set(wlc_hw_info_t *wlc_hw, u16 phytxant)
+{
+ /* update sw state */
+ wlc_hw->bmac_phytxant = phytxant;
+
+ /* push to ucode if up */
+ if (!wlc_hw->up)
+ return;
+ wlc_ucode_txant_set(wlc_hw);
+
+}
+
+u16 wlc_bmac_get_txant(wlc_hw_info_t *wlc_hw)
+{
+ return (u16) wlc_hw->wlc->stf->txant;
+}
+
+void wlc_bmac_antsel_type_set(wlc_hw_info_t *wlc_hw, u8 antsel_type)
+{
+ wlc_hw->antsel_type = antsel_type;
+
+ /* Update the antsel type for phy module to use */
+ wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type);
+}
+
+void wlc_bmac_fifoerrors(wlc_hw_info_t *wlc_hw)
+{
+ bool fatal = false;
+ uint unit;
+ uint intstatus, idx;
+ d11regs_t *regs = wlc_hw->regs;
+
+ unit = wlc_hw->unit;
+
+ for (idx = 0; idx < NFIFO; idx++) {
+ /* read intstatus register and ignore any non-error bits */
+ intstatus =
+ R_REG(wlc_hw->osh,
+ &regs->intctrlregs[idx].intstatus) & I_ERRORS;
+ if (!intstatus)
+ continue;
+
+ WL_TRACE(("wl%d: wlc_bmac_fifoerrors: intstatus%d 0x%x\n", unit,
+ idx, intstatus));
+
+ if (intstatus & I_RO) {
+ WL_ERROR(("wl%d: fifo %d: receive fifo overflow\n",
+ unit, idx));
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->rxoflo);
+ fatal = true;
+ }
+
+ if (intstatus & I_PC) {
+ WL_ERROR(("wl%d: fifo %d: descriptor error\n", unit,
+ idx));
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->dmade);
+ fatal = true;
+ }
+
+ if (intstatus & I_PD) {
+ WL_ERROR(("wl%d: fifo %d: data error\n", unit, idx));
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->dmada);
+ fatal = true;
+ }
+
+ if (intstatus & I_DE) {
+ WL_ERROR(("wl%d: fifo %d: descriptor protocol error\n",
+ unit, idx));
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->dmape);
+ fatal = true;
+ }
+
+ if (intstatus & I_RU) {
+ WL_ERROR(("wl%d: fifo %d: receive descriptor underflow\n", unit, idx));
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->rxuflo[idx]);
+ }
+
+ if (intstatus & I_XU) {
+ WL_ERROR(("wl%d: fifo %d: transmit fifo underflow\n",
+ idx, unit));
+ WLCNTINCR(wlc_hw->wlc->pub->_cnt->txuflo);
+ fatal = true;
+ }
+
+ if (fatal) {
+ wlc_fatal_error(wlc_hw->wlc); /* big hammer */
+ break;
+ } else
+ W_REG(wlc_hw->osh, &regs->intctrlregs[idx].intstatus,
+ intstatus);
+ }
+}
+
+void wlc_intrson(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ ASSERT(wlc->defmacintmask);
+ wlc->macintmask = wlc->defmacintmask;
+ W_REG(wlc_hw->osh, &wlc_hw->regs->macintmask, wlc->macintmask);
+}
+
+/* callback for siutils.c, which has only wlc handler, no wl
+ * they both check up, not only because there is no need to off/restore d11 interrupt
+ * but also because per-port code may require sync with valid interrupt.
+ */
+
+static u32 wlc_wlintrsoff(wlc_info_t *wlc)
+{
+ if (!wlc->hw->up)
+ return 0;
+
+ return wl_intrsoff(wlc->wl);
+}
+
+static void wlc_wlintrsrestore(wlc_info_t *wlc, u32 macintmask)
+{
+ if (!wlc->hw->up)
+ return;
+
+ wl_intrsrestore(wlc->wl, macintmask);
+}
+
+u32 wlc_intrsoff(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ u32 macintmask;
+
+ if (!wlc_hw->clk)
+ return 0;
+
+ macintmask = wlc->macintmask; /* isr can still happen */
+
+ W_REG(wlc_hw->osh, &wlc_hw->regs->macintmask, 0);
+ (void)R_REG(wlc_hw->osh, &wlc_hw->regs->macintmask); /* sync readback */
+ udelay(1); /* ensure int line is no longer driven */
+ wlc->macintmask = 0;
+
+ /* return previous macintmask; resolve race between us and our isr */
+ return wlc->macintstatus ? 0 : macintmask;
+}
+
+void wlc_intrsrestore(wlc_info_t *wlc, u32 macintmask)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ if (!wlc_hw->clk)
+ return;
+
+ wlc->macintmask = macintmask;
+ W_REG(wlc_hw->osh, &wlc_hw->regs->macintmask, wlc->macintmask);
+}
+
+void wlc_bmac_mute(wlc_hw_info_t *wlc_hw, bool on, mbool flags)
+{
+ struct ether_addr null_ether_addr = { {0, 0, 0, 0, 0, 0} };
+
+ if (on) {
+ /* suspend tx fifos */
+ wlc_bmac_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO);
+ wlc_bmac_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO);
+ wlc_bmac_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO);
+ wlc_bmac_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO);
+
+ /* zero the address match register so we do not send ACKs */
+ wlc_bmac_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
+ &null_ether_addr);
+ } else {
+ /* resume tx fifos */
+ if (!wlc_hw->wlc->tx_suspended) {
+ wlc_bmac_tx_fifo_resume(wlc_hw, TX_DATA_FIFO);
+ }
+ wlc_bmac_tx_fifo_resume(wlc_hw, TX_CTL_FIFO);
+ wlc_bmac_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO);
+ wlc_bmac_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO);
+
+ /* Restore address */
+ wlc_bmac_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
+ &wlc_hw->etheraddr);
+ }
+
+ wlc_phy_mute_upd(wlc_hw->band->pi, on, flags);
+
+ if (on)
+ wlc_ucode_mute_override_set(wlc_hw);
+ else
+ wlc_ucode_mute_override_clear(wlc_hw);
+}
+
+void wlc_bmac_set_deaf(wlc_hw_info_t *wlc_hw, bool user_flag)
+{
+ wlc_phy_set_deaf(wlc_hw->band->pi, user_flag);
+}
+
+int wlc_bmac_xmtfifo_sz_get(wlc_hw_info_t *wlc_hw, uint fifo, uint *blocks)
+{
+ if (fifo >= NFIFO)
+ return BCME_RANGE;
+
+ *blocks = wlc_hw->xmtfifo_sz[fifo];
+
+ return 0;
+}
+
+int wlc_bmac_xmtfifo_sz_set(wlc_hw_info_t *wlc_hw, uint fifo, uint blocks)
+{
+ if (fifo >= NFIFO || blocks > 299)
+ return BCME_RANGE;
+
+ /* BMAC_NOTE, change blocks to u16 */
+ wlc_hw->xmtfifo_sz[fifo] = (u16) blocks;
+
+ return 0;
+}
+
+/* wlc_bmac_tx_fifo_suspended:
+ * Check the MAC's tx suspend status for a tx fifo.
+ *
+ * When the MAC acknowledges a tx suspend, it indicates that no more
+ * packets will be transmitted out the radio. This is independent of
+ * DMA channel suspension---the DMA may have finished suspending, or may still
+ * be pulling data into a tx fifo, by the time the MAC acks the suspend
+ * request.
+ */
+bool wlc_bmac_tx_fifo_suspended(wlc_hw_info_t *wlc_hw, uint tx_fifo)
+{
+ /* check that a suspend has been requested and is no longer pending */
+
+ /*
+ * for DMA mode, the suspend request is set in xmtcontrol of the DMA engine,
+ * and the tx fifo suspend at the lower end of the MAC is acknowledged in the
+ * chnstatus register.
+ * The tx fifo suspend completion is independent of the DMA suspend completion and
+ * may be acked before or after the DMA is suspended.
+ */
+ if (dma_txsuspended(wlc_hw->di[tx_fifo]) &&
+ (R_REG(wlc_hw->osh, &wlc_hw->regs->chnstatus) &
+ (1 << tx_fifo)) == 0)
+ return true;
+
+ return false;
+}
+
+void wlc_bmac_tx_fifo_suspend(wlc_hw_info_t *wlc_hw, uint tx_fifo)
+{
+ u8 fifo = 1 << tx_fifo;
+
+ /* Two clients of this code, 11h Quiet period and scanning. */
+
+ /* only suspend if not already suspended */
+ if ((wlc_hw->suspended_fifos & fifo) == fifo)
+ return;
+
+ /* force the core awake only if not already */
+ if (wlc_hw->suspended_fifos == 0)
+ wlc_ucode_wake_override_set(wlc_hw, WLC_WAKE_OVERRIDE_TXFIFO);
+
+ wlc_hw->suspended_fifos |= fifo;
+
+ if (wlc_hw->di[tx_fifo]) {
+ /* Suspending AMPDU transmissions in the middle can cause underflow
+ * which may result in mismatch between ucode and driver
+ * so suspend the mac before suspending the FIFO
+ */
+ if (WLC_PHY_11N_CAP(wlc_hw->band))
+ wlc_suspend_mac_and_wait(wlc_hw->wlc);
+
+ dma_txsuspend(wlc_hw->di[tx_fifo]);
+
+ if (WLC_PHY_11N_CAP(wlc_hw->band))
+ wlc_enable_mac(wlc_hw->wlc);
+ }
+}
+
+void wlc_bmac_tx_fifo_resume(wlc_hw_info_t *wlc_hw, uint tx_fifo)
+{
+ /* BMAC_NOTE: WLC_TX_FIFO_ENAB is done in wlc_dpc() for DMA case but need to be done
+ * here for PIO otherwise the watchdog will catch the inconsistency and fire
+ */
+ /* Two clients of this code, 11h Quiet period and scanning. */
+ if (wlc_hw->di[tx_fifo])
+ dma_txresume(wlc_hw->di[tx_fifo]);
+
+ /* allow core to sleep again */
+ if (wlc_hw->suspended_fifos == 0)
+ return;
+ else {
+ wlc_hw->suspended_fifos &= ~(1 << tx_fifo);
+ if (wlc_hw->suspended_fifos == 0)
+ wlc_ucode_wake_override_clear(wlc_hw,
+ WLC_WAKE_OVERRIDE_TXFIFO);
+ }
+}
+
+/*
+ * Read and clear macintmask and macintstatus and intstatus registers.
+ * This routine should be called with interrupts off
+ * Return:
+ * -1 if DEVICEREMOVED(wlc) evaluates to true;
+ * 0 if the interrupt is not for us, or we are in some special cases;
+ * device interrupt status bits otherwise.
+ */
+static inline u32 wlc_intstatus(wlc_info_t *wlc, bool in_isr)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ u32 macintstatus;
+ u32 intstatus_rxfifo, intstatus_txsfifo;
+ osl_t *osh;
+
+ osh = wlc_hw->osh;
+
+ /* macintstatus includes a DMA interrupt summary bit */
+ macintstatus = R_REG(osh, &regs->macintstatus);
+
+ WL_TRACE(("wl%d: macintstatus: 0x%x\n", wlc_hw->unit, macintstatus));
+
+ /* detect cardbus removed, in power down(suspend) and in reset */
+ if (DEVICEREMOVED(wlc))
+ return -1;
+
+ /* DEVICEREMOVED succeeds even when the core is still resetting,
+ * handle that case here.
+ */
+ if (macintstatus == 0xffffffff)
+ return 0;
+
+ /* defer unsolicited interrupts */
+ macintstatus &= (in_isr ? wlc->macintmask : wlc->defmacintmask);
+
+ /* if not for us */
+ if (macintstatus == 0)
+ return 0;
+
+ /* interrupts are already turned off for CFE build
+ * Caution: For CFE Turning off the interrupts again has some undesired
+ * consequences
+ */
+ /* turn off the interrupts */
+ W_REG(osh, &regs->macintmask, 0);
+#ifndef BCMSDIO
+ (void)R_REG(osh, &regs->macintmask); /* sync readback */
+#endif
+ wlc->macintmask = 0;
+
+ /* clear device interrupts */
+ W_REG(osh, &regs->macintstatus, macintstatus);
+
+ /* MI_DMAINT is indication of non-zero intstatus */
+ if (macintstatus & MI_DMAINT) {
+ if (D11REV_IS(wlc_hw->corerev, 4)) {
+ intstatus_rxfifo =
+ R_REG(osh, &regs->intctrlregs[RX_FIFO].intstatus);
+ intstatus_txsfifo =
+ R_REG(osh,
+ &regs->intctrlregs[RX_TXSTATUS_FIFO].
+ intstatus);
+ WL_TRACE(("wl%d: intstatus_rxfifo 0x%x, intstatus_txsfifo 0x%x\n", wlc_hw->unit, intstatus_rxfifo, intstatus_txsfifo));
+
+ /* defer unsolicited interrupt hints */
+ intstatus_rxfifo &= DEF_RXINTMASK;
+ intstatus_txsfifo &= DEF_RXINTMASK;
+
+ /* MI_DMAINT bit in macintstatus is indication of RX_FIFO interrupt */
+ /* clear interrupt hints */
+ if (intstatus_rxfifo)
+ W_REG(osh,
+ &regs->intctrlregs[RX_FIFO].intstatus,
+ intstatus_rxfifo);
+ else
+ macintstatus &= ~MI_DMAINT;
+
+ /* MI_TFS bit in macintstatus is encoding of RX_TXSTATUS_FIFO interrupt */
+ if (intstatus_txsfifo) {
+ W_REG(osh,
+ &regs->intctrlregs[RX_TXSTATUS_FIFO].
+ intstatus, intstatus_txsfifo);
+ macintstatus |= MI_TFS;
+ }
+ } else {
+ /*
+ * For corerevs >= 5, only fifo interrupt enabled is I_RI in RX_FIFO.
+ * If MI_DMAINT is set, assume it is set and clear the interrupt.
+ */
+ W_REG(osh, &regs->intctrlregs[RX_FIFO].intstatus,
+ DEF_RXINTMASK);
+ }
+ }
+
+ return macintstatus;
+}
+
+/* Update wlc->macintstatus and wlc->intstatus[]. */
+/* Return true if they are updated successfully. false otherwise */
+bool wlc_intrsupd(wlc_info_t *wlc)
+{
+ u32 macintstatus;
+
+ ASSERT(wlc->macintstatus != 0);
+
+ /* read and clear macintstatus and intstatus registers */
+ macintstatus = wlc_intstatus(wlc, false);
+
+ /* device is removed */
+ if (macintstatus == 0xffffffff)
+ return false;
+
+ /* update interrupt status in software */
+ wlc->macintstatus |= macintstatus;
+
+ return true;
+}
+
+/*
+ * First-level interrupt processing.
+ * Return true if this was our interrupt, false otherwise.
+ * *wantdpc will be set to true if further wlc_dpc() processing is required,
+ * false otherwise.
+ */
+bool BCMFASTPATH wlc_isr(wlc_info_t *wlc, bool *wantdpc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ u32 macintstatus;
+
+ *wantdpc = false;
+
+ if (!wlc_hw->up || !wlc->macintmask)
+ return false;
+
+ /* read and clear macintstatus and intstatus registers */
+ macintstatus = wlc_intstatus(wlc, true);
+
+ if (macintstatus == 0xffffffff)
+ WL_ERROR(("DEVICEREMOVED detected in the ISR code path.\n"));
+
+ /* it is not for us */
+ if (macintstatus == 0)
+ return false;
+
+ *wantdpc = true;
+
+ /* save interrupt status bits */
+ ASSERT(wlc->macintstatus == 0);
+ wlc->macintstatus = macintstatus;
+
+ return true;
+
+}
+
+/* process tx completion events for corerev < 5 */
+static bool wlc_bmac_txstatus_corerev4(wlc_hw_info_t *wlc_hw)
+{
+ void *status_p;
+ tx_status_t *txs;
+ osl_t *osh;
+ bool fatal = false;
+
+ WL_TRACE(("wl%d: wlc_txstatusrecv\n", wlc_hw->unit));
+
+ osh = wlc_hw->osh;
+
+ while (!fatal && (status_p = dma_rx(wlc_hw->di[RX_TXSTATUS_FIFO]))) {
+
+ txs = (tx_status_t *) PKTDATA(status_p);
+ /* MAC uses little endian only */
+ ltoh16_buf((void *)txs, sizeof(tx_status_t));
+
+ /* shift low bits for tx_status_t status compatibility */
+ txs->status = (txs->status & ~TXS_COMPAT_MASK)
+ | (((txs->status & TXS_COMPAT_MASK) << TXS_COMPAT_SHIFT));
+
+ fatal = wlc_bmac_dotxstatus(wlc_hw, txs, 0);
+
+ PKTFREE(osh, status_p, false);
+ }
+
+ if (fatal)
+ return true;
+
+ /* post more rbufs */
+ dma_rxfill(wlc_hw->di[RX_TXSTATUS_FIFO]);
+
+ return false;
+}
+
+static bool BCMFASTPATH
+wlc_bmac_dotxstatus(wlc_hw_info_t *wlc_hw, tx_status_t *txs, u32 s2)
+{
+ /* discard intermediate indications for ucode with one legitimate case:
+ * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, but the subsequent
+ * tx of DATA failed. so it will start rts/cts from the beginning (resetting the rts
+ * transmission count)
+ */
+ if (!(txs->status & TX_STATUS_AMPDU)
+ && (txs->status & TX_STATUS_INTERMEDIATE)) {
+ return false;
+ }
+
+ return wlc_dotxstatus(wlc_hw->wlc, txs, s2);
+}
+
+/* process tx completion events in BMAC
+ * Return true if more tx status need to be processed. false otherwise.
+ */
+static bool BCMFASTPATH
+wlc_bmac_txstatus(wlc_hw_info_t *wlc_hw, bool bound, bool *fatal)
+{
+ bool morepending = false;
+ wlc_info_t *wlc = wlc_hw->wlc;
+
+ WL_TRACE(("wl%d: wlc_bmac_txstatus\n", wlc_hw->unit));
+
+ if (D11REV_IS(wlc_hw->corerev, 4)) {
+ /* to retire soon */
+ *fatal = wlc_bmac_txstatus_corerev4(wlc->hw);
+
+ if (*fatal)
+ return 0;
+ } else {
+ /* corerev >= 5 */
+ d11regs_t *regs;
+ osl_t *osh;
+ tx_status_t txstatus, *txs;
+ u32 s1, s2;
+ uint n = 0;
+ /* Param 'max_tx_num' indicates max. # tx status to process before break out. */
+ uint max_tx_num = bound ? wlc->pub->tunables->txsbnd : -1;
+
+ txs = &txstatus;
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+ while (!(*fatal)
+ && (s1 = R_REG(osh, &regs->frmtxstatus)) & TXS_V) {
+
+ if (s1 == 0xffffffff) {
+ WL_ERROR(("wl%d: %s: dead chip\n",
+ wlc_hw->unit, __func__));
+ ASSERT(s1 != 0xffffffff);
+ return morepending;
+ }
+
+ s2 = R_REG(osh, &regs->frmtxstatus2);
+
+ txs->status = s1 & TXS_STATUS_MASK;
+ txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT;
+ txs->sequence = s2 & TXS_SEQ_MASK;
+ txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT;
+ txs->lasttxtime = 0;
+
+ *fatal = wlc_bmac_dotxstatus(wlc_hw, txs, s2);
+
+ /* !give others some time to run! */
+ if (++n >= max_tx_num)
+ break;
+ }
+
+ if (*fatal)
+ return 0;
+
+ if (n >= max_tx_num)
+ morepending = true;
+ }
+
+ if (!pktq_empty(&wlc->active_queue->q))
+ wlc_send_q(wlc, wlc->active_queue);
+
+ return morepending;
+}
+
+void wlc_suspend_mac_and_wait(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ u32 mc, mi;
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: wlc_suspend_mac_and_wait: bandunit %d\n", wlc_hw->unit,
+ wlc_hw->band->bandunit));
+
+ /*
+ * Track overlapping suspend requests
+ */
+ wlc_hw->mac_suspend_depth++;
+ if (wlc_hw->mac_suspend_depth > 1)
+ return;
+
+ osh = wlc_hw->osh;
+
+ /* force the core awake */
+ wlc_ucode_wake_override_set(wlc_hw, WLC_WAKE_OVERRIDE_MACSUSPEND);
+
+ mc = R_REG(osh, &regs->maccontrol);
+
+ if (mc == 0xffffffff) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc_hw->unit, __func__));
+ wl_down(wlc->wl);
+ return;
+ }
+ ASSERT(!(mc & MCTL_PSM_JMP_0));
+ ASSERT(mc & MCTL_PSM_RUN);
+ ASSERT(mc & MCTL_EN_MAC);
+
+ mi = R_REG(osh, &regs->macintstatus);
+ if (mi == 0xffffffff) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc_hw->unit, __func__));
+ wl_down(wlc->wl);
+ return;
+ }
+ ASSERT(!(mi & MI_MACSSPNDD));
+
+ wlc_bmac_mctrl(wlc_hw, MCTL_EN_MAC, 0);
+
+ SPINWAIT(!(R_REG(osh, &regs->macintstatus) & MI_MACSSPNDD),
+ WLC_MAX_MAC_SUSPEND);
+
+ if (!(R_REG(osh, &regs->macintstatus) & MI_MACSSPNDD)) {
+ WL_ERROR(("wl%d: wlc_suspend_mac_and_wait: waited %d uS and "
+ "MI_MACSSPNDD is still not on.\n",
+ wlc_hw->unit, WLC_MAX_MAC_SUSPEND));
+ WL_ERROR(("wl%d: psmdebug 0x%08x, phydebug 0x%08x, psm_brc 0x%04x\n", wlc_hw->unit, R_REG(osh, &regs->psmdebug), R_REG(osh, &regs->phydebug), R_REG(osh, &regs->psm_brc)));
+ }
+
+ mc = R_REG(osh, &regs->maccontrol);
+ if (mc == 0xffffffff) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc_hw->unit, __func__));
+ wl_down(wlc->wl);
+ return;
+ }
+ ASSERT(!(mc & MCTL_PSM_JMP_0));
+ ASSERT(mc & MCTL_PSM_RUN);
+ ASSERT(!(mc & MCTL_EN_MAC));
+}
+
+void wlc_enable_mac(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ u32 mc, mi;
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: wlc_enable_mac: bandunit %d\n", wlc_hw->unit,
+ wlc->band->bandunit));
+
+ /*
+ * Track overlapping suspend requests
+ */
+ ASSERT(wlc_hw->mac_suspend_depth > 0);
+ wlc_hw->mac_suspend_depth--;
+ if (wlc_hw->mac_suspend_depth > 0)
+ return;
+
+ osh = wlc_hw->osh;
+
+ mc = R_REG(osh, &regs->maccontrol);
+ ASSERT(!(mc & MCTL_PSM_JMP_0));
+ ASSERT(!(mc & MCTL_EN_MAC));
+ ASSERT(mc & MCTL_PSM_RUN);
+
+ wlc_bmac_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC);
+ W_REG(osh, &regs->macintstatus, MI_MACSSPNDD);
+
+ mc = R_REG(osh, &regs->maccontrol);
+ ASSERT(!(mc & MCTL_PSM_JMP_0));
+ ASSERT(mc & MCTL_EN_MAC);
+ ASSERT(mc & MCTL_PSM_RUN);
+
+ mi = R_REG(osh, &regs->macintstatus);
+ ASSERT(!(mi & MI_MACSSPNDD));
+
+ wlc_ucode_wake_override_clear(wlc_hw, WLC_WAKE_OVERRIDE_MACSUSPEND);
+}
+
+void wlc_bmac_ifsctl_edcrs_set(wlc_hw_info_t *wlc_hw, bool abie, bool isht)
+{
+ if (!(WLCISNPHY(wlc_hw->band) && (D11REV_GE(wlc_hw->corerev, 16))))
+ return;
+
+ if (isht) {
+ if (WLCISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 3)) {
+ AND_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ ~IFS_CTL1_EDCRS);
+ }
+ } else {
+ /* enable EDCRS for non-11n association */
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1, IFS_CTL1_EDCRS);
+ }
+
+ if (WLCISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3)) {
+ if (CHSPEC_IS20(wlc_hw->chanspec)) {
+ /* 20 mhz, use 20U ED only */
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ IFS_CTL1_EDCRS);
+ AND_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ ~IFS_CTL1_EDCRS_20L);
+ AND_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ ~IFS_CTL1_EDCRS_40);
+ } else {
+ /* 40 mhz, use 20U 20L and 40 ED */
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ IFS_CTL1_EDCRS);
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ IFS_CTL1_EDCRS_20L);
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->ifs_ctl1,
+ IFS_CTL1_EDCRS_40);
+ }
+ }
+}
+
+static void wlc_upd_ofdm_pctl1_table(wlc_hw_info_t *wlc_hw)
+{
+ u8 rate;
+ u8 rates[8] = {
+ WLC_RATE_6M, WLC_RATE_9M, WLC_RATE_12M, WLC_RATE_18M,
+ WLC_RATE_24M, WLC_RATE_36M, WLC_RATE_48M, WLC_RATE_54M
+ };
+ u16 entry_ptr;
+ u16 pctl1;
+ uint i;
+
+ if (!WLC_PHY_11N_CAP(wlc_hw->band))
+ return;
+
+ /* walk the phy rate table and update the entries */
+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ rate = rates[i];
+
+ entry_ptr = wlc_bmac_ofdm_ratetable_offset(wlc_hw, rate);
+
+ /* read the SHM Rate Table entry OFDM PCTL1 values */
+ pctl1 =
+ wlc_bmac_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS);
+
+ /* modify the value */
+ pctl1 &= ~PHY_TXC1_MODE_MASK;
+ pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT);
+
+ /* Update the SHM Rate Table entry OFDM PCTL1 values */
+ wlc_bmac_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS,
+ pctl1);
+ }
+}
+
+static u16 wlc_bmac_ofdm_ratetable_offset(wlc_hw_info_t *wlc_hw, u8 rate)
+{
+ uint i;
+ u8 plcp_rate = 0;
+ struct plcp_signal_rate_lookup {
+ u8 rate;
+ u8 signal_rate;
+ };
+ /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */
+ const struct plcp_signal_rate_lookup rate_lookup[] = {
+ {WLC_RATE_6M, 0xB},
+ {WLC_RATE_9M, 0xF},
+ {WLC_RATE_12M, 0xA},
+ {WLC_RATE_18M, 0xE},
+ {WLC_RATE_24M, 0x9},
+ {WLC_RATE_36M, 0xD},
+ {WLC_RATE_48M, 0x8},
+ {WLC_RATE_54M, 0xC}
+ };
+
+ for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) {
+ if (rate == rate_lookup[i].rate) {
+ plcp_rate = rate_lookup[i].signal_rate;
+ break;
+ }
+ }
+
+ /* Find the SHM pointer to the rate table entry by looking in the
+ * Direct-map Table
+ */
+ return 2 * wlc_bmac_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2));
+}
+
+void wlc_bmac_band_stf_ss_set(wlc_hw_info_t *wlc_hw, u8 stf_mode)
+{
+ wlc_hw->hw_stf_ss_opmode = stf_mode;
+
+ if (wlc_hw->clk)
+ wlc_upd_ofdm_pctl1_table(wlc_hw);
+}
+
+void BCMFASTPATH
+wlc_bmac_read_tsf(wlc_hw_info_t *wlc_hw, u32 *tsf_l_ptr,
+ u32 *tsf_h_ptr)
+{
+ d11regs_t *regs = wlc_hw->regs;
+
+ /* read the tsf timer low, then high to get an atomic read */
+ *tsf_l_ptr = R_REG(wlc_hw->osh, &regs->tsf_timerlow);
+ *tsf_h_ptr = R_REG(wlc_hw->osh, &regs->tsf_timerhigh);
+
+ return;
+}
+
+bool wlc_bmac_validate_chip_access(wlc_hw_info_t *wlc_hw)
+{
+ d11regs_t *regs;
+ u32 w, val;
+ volatile u16 *reg16;
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: validate_chip_access\n", wlc_hw->unit));
+
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ /* Validate dchip register access */
+
+ W_REG(osh, &regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, &regs->objaddr);
+ w = R_REG(osh, &regs->objdata);
+
+ /* Can we write and read back a 32bit register? */
+ W_REG(osh, &regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, &regs->objdata, (u32) 0xaa5555aa);
+
+ W_REG(osh, &regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, &regs->objaddr);
+ val = R_REG(osh, &regs->objdata);
+ if (val != (u32) 0xaa5555aa) {
+ WL_ERROR(("wl%d: validate_chip_access: SHM = 0x%x, expected 0xaa5555aa\n", wlc_hw->unit, val));
+ return false;
+ }
+
+ W_REG(osh, &regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, &regs->objdata, (u32) 0x55aaaa55);
+
+ W_REG(osh, &regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, &regs->objaddr);
+ val = R_REG(osh, &regs->objdata);
+ if (val != (u32) 0x55aaaa55) {
+ WL_ERROR(("wl%d: validate_chip_access: SHM = 0x%x, expected 0x55aaaa55\n", wlc_hw->unit, val));
+ return false;
+ }
+
+ W_REG(osh, &regs->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, &regs->objaddr);
+ W_REG(osh, &regs->objdata, w);
+
+ if (D11REV_LT(wlc_hw->corerev, 11)) {
+ /* if 32 bit writes are split into 16 bit writes, are they in the correct order
+ * for our interface, low to high
+ */
+ reg16 = (volatile u16 *)&regs->tsf_cfpstart;
+
+ /* write the CFPStart register low half explicitly, starting a buffered write */
+ W_REG(osh, reg16, 0xAAAA);
+
+ /* Write a 32 bit value to CFPStart to test the 16 bit split order.
+ * If the low 16 bits are written first, followed by the high 16 bits then the
+ * 32 bit value 0xCCCCBBBB should end up in the register.
+ * If the order is reversed, then the write to the high half will trigger a buffered
+ * write of 0xCCCCAAAA.
+ * If the bus is 32 bits, then this is not much of a test, and the reg should
+ * have the correct value 0xCCCCBBBB.
+ */
+ W_REG(osh, &regs->tsf_cfpstart, 0xCCCCBBBB);
+
+ /* verify with the 16 bit registers that have no side effects */
+ val = R_REG(osh, &regs->tsf_cfpstrt_l);
+ if (val != (uint) 0xBBBB) {
+ WL_ERROR(("wl%d: validate_chip_access: tsf_cfpstrt_l = 0x%x, expected" " 0x%x\n", wlc_hw->unit, val, 0xBBBB));
+ return false;
+ }
+ val = R_REG(osh, &regs->tsf_cfpstrt_h);
+ if (val != (uint) 0xCCCC) {
+ WL_ERROR(("wl%d: validate_chip_access: tsf_cfpstrt_h = 0x%x, expected" " 0x%x\n", wlc_hw->unit, val, 0xCCCC));
+ return false;
+ }
+
+ }
+
+ /* clear CFPStart */
+ W_REG(osh, &regs->tsf_cfpstart, 0);
+
+ w = R_REG(osh, &regs->maccontrol);
+ if ((w != (MCTL_IHR_EN | MCTL_WAKE)) &&
+ (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) {
+ WL_ERROR(("wl%d: validate_chip_access: maccontrol = 0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w, (MCTL_IHR_EN | MCTL_WAKE), (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE)));
+ return false;
+ }
+
+ return true;
+}
+
+#define PHYPLL_WAIT_US 100000
+
+void wlc_bmac_core_phypll_ctl(wlc_hw_info_t *wlc_hw, bool on)
+{
+ d11regs_t *regs;
+ osl_t *osh;
+ u32 tmp;
+
+ WL_TRACE(("wl%d: wlc_bmac_core_phypll_ctl\n", wlc_hw->unit));
+
+ tmp = 0;
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ if (D11REV_LE(wlc_hw->corerev, 16) || D11REV_IS(wlc_hw->corerev, 20))
+ return;
+
+ if (on) {
+ if ((wlc_hw->sih->chip == BCM4313_CHIP_ID)) {
+ OR_REG(osh, &regs->clk_ctl_st,
+ (CCS_ERSRC_REQ_HT | CCS_ERSRC_REQ_D11PLL |
+ CCS_ERSRC_REQ_PHYPLL));
+ SPINWAIT((R_REG(osh, &regs->clk_ctl_st) &
+ (CCS_ERSRC_AVAIL_HT)) != (CCS_ERSRC_AVAIL_HT),
+ PHYPLL_WAIT_US);
+
+ tmp = R_REG(osh, &regs->clk_ctl_st);
+ if ((tmp & (CCS_ERSRC_AVAIL_HT)) !=
+ (CCS_ERSRC_AVAIL_HT)) {
+ WL_ERROR(("%s: turn on PHY PLL failed\n",
+ __func__));
+ ASSERT(0);
+ }
+ } else {
+ OR_REG(osh, &regs->clk_ctl_st,
+ (CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL));
+ SPINWAIT((R_REG(osh, &regs->clk_ctl_st) &
+ (CCS_ERSRC_AVAIL_D11PLL |
+ CCS_ERSRC_AVAIL_PHYPLL)) !=
+ (CCS_ERSRC_AVAIL_D11PLL |
+ CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US);
+
+ tmp = R_REG(osh, &regs->clk_ctl_st);
+ if ((tmp &
+ (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL))
+ !=
+ (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) {
+ WL_ERROR(("%s: turn on PHY PLL failed\n",
+ __func__));
+ ASSERT(0);
+ }
+ }
+ } else {
+ /* Since the PLL may be shared, other cores can still be requesting it;
+ * so we'll deassert the request but not wait for status to comply.
+ */
+ AND_REG(osh, &regs->clk_ctl_st, ~CCS_ERSRC_REQ_PHYPLL);
+ tmp = R_REG(osh, &regs->clk_ctl_st);
+ }
+}
+
+void wlc_coredisable(wlc_hw_info_t *wlc_hw)
+{
+ bool dev_gone;
+
+ WL_TRACE(("wl%d: %s\n", wlc_hw->unit, __func__));
+
+ ASSERT(!wlc_hw->up);
+
+ dev_gone = DEVICEREMOVED(wlc_hw->wlc);
+
+ if (dev_gone)
+ return;
+
+ if (wlc_hw->noreset)
+ return;
+
+ /* radio off */
+ wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+
+ /* turn off analog core */
+ wlc_phy_anacore(wlc_hw->band->pi, OFF);
+
+ /* turn off PHYPLL to save power */
+ wlc_bmac_core_phypll_ctl(wlc_hw, false);
+
+ /* No need to set wlc->pub->radio_active = OFF
+ * because this function needs down capability and
+ * radio_active is designed for BCMNODOWN.
+ */
+
+ /* remove gpio controls */
+ if (wlc_hw->ucode_dbgsel)
+ si_gpiocontrol(wlc_hw->sih, ~0, 0, GPIO_DRV_PRIORITY);
+
+ wlc_hw->clk = false;
+ si_core_disable(wlc_hw->sih, 0);
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+}
+
+/* power both the pll and external oscillator on/off */
+void wlc_bmac_xtal(wlc_hw_info_t *wlc_hw, bool want)
+{
+ WL_TRACE(("wl%d: wlc_bmac_xtal: want %d\n", wlc_hw->unit, want));
+
+ /* dont power down if plldown is false or we must poll hw radio disable */
+ if (!want && wlc_hw->pllreq)
+ return;
+
+ if (wlc_hw->sih)
+ si_clkctl_xtal(wlc_hw->sih, XTAL | PLL, want);
+
+ wlc_hw->sbclk = want;
+ if (!wlc_hw->sbclk) {
+ wlc_hw->clk = false;
+ if (wlc_hw->band && wlc_hw->band->pi)
+ wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+ }
+}
+
+static void wlc_flushqueues(wlc_info_t *wlc)
+{
+ wlc_hw_info_t *wlc_hw = wlc->hw;
+ uint i;
+
+ wlc->txpend16165war = 0;
+
+ /* free any posted tx packets */
+ for (i = 0; i < NFIFO; i++)
+ if (wlc_hw->di[i]) {
+ dma_txreclaim(wlc_hw->di[i], HNDDMA_RANGE_ALL);
+ TXPKTPENDCLR(wlc, i);
+ WL_TRACE(("wlc_flushqueues: pktpend fifo %d cleared\n",
+ i));
+ }
+
+ /* free any posted rx packets */
+ dma_rxreclaim(wlc_hw->di[RX_FIFO]);
+ if (D11REV_IS(wlc_hw->corerev, 4))
+ dma_rxreclaim(wlc_hw->di[RX_TXSTATUS_FIFO]);
+}
+
+u16 wlc_bmac_read_shm(wlc_hw_info_t *wlc_hw, uint offset)
+{
+ return wlc_bmac_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
+}
+
+void wlc_bmac_write_shm(wlc_hw_info_t *wlc_hw, uint offset, u16 v)
+{
+ wlc_bmac_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL);
+}
+
+/* Set a range of shared memory to a value.
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ */
+void wlc_bmac_set_shm(wlc_hw_info_t *wlc_hw, uint offset, u16 v, int len)
+{
+ int i;
+
+ /* offset and len need to be even */
+ ASSERT((offset & 1) == 0);
+ ASSERT((len & 1) == 0);
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i += 2) {
+ wlc_bmac_write_objmem(wlc_hw, offset + i, v, OBJADDR_SHM_SEL);
+ }
+}
+
+static u16
+wlc_bmac_read_objmem(wlc_hw_info_t *wlc_hw, uint offset, u32 sel)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ volatile u16 *objdata_lo = (volatile u16 *)&regs->objdata;
+ volatile u16 *objdata_hi = objdata_lo + 1;
+ u16 v;
+
+ ASSERT((offset & 1) == 0);
+
+ W_REG(wlc_hw->osh, &regs->objaddr, sel | (offset >> 2));
+ (void)R_REG(wlc_hw->osh, &regs->objaddr);
+ if (offset & 2) {
+ v = R_REG(wlc_hw->osh, objdata_hi);
+ } else {
+ v = R_REG(wlc_hw->osh, objdata_lo);
+ }
+
+ return v;
+}
+
+static void
+wlc_bmac_write_objmem(wlc_hw_info_t *wlc_hw, uint offset, u16 v, u32 sel)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ volatile u16 *objdata_lo = (volatile u16 *)&regs->objdata;
+ volatile u16 *objdata_hi = objdata_lo + 1;
+
+ ASSERT((offset & 1) == 0);
+
+ W_REG(wlc_hw->osh, &regs->objaddr, sel | (offset >> 2));
+ (void)R_REG(wlc_hw->osh, &regs->objaddr);
+ if (offset & 2) {
+ W_REG(wlc_hw->osh, objdata_hi, v);
+ } else {
+ W_REG(wlc_hw->osh, objdata_lo, v);
+ }
+}
+
+/* Copy a buffer to shared memory of specified type .
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ * 'sel' selects the type of memory
+ */
+void
+wlc_bmac_copyto_objmem(wlc_hw_info_t *wlc_hw, uint offset, const void *buf,
+ int len, u32 sel)
+{
+ u16 v;
+ const u8 *p = (const u8 *)buf;
+ int i;
+
+ /* offset and len need to be even */
+ ASSERT((offset & 1) == 0);
+ ASSERT((len & 1) == 0);
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i += 2) {
+ v = p[i] | (p[i + 1] << 8);
+ wlc_bmac_write_objmem(wlc_hw, offset + i, v, sel);
+ }
+}
+
+/* Copy a piece of shared memory of specified type to a buffer .
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ * 'sel' selects the type of memory
+ */
+void
+wlc_bmac_copyfrom_objmem(wlc_hw_info_t *wlc_hw, uint offset, void *buf,
+ int len, u32 sel)
+{
+ u16 v;
+ u8 *p = (u8 *) buf;
+ int i;
+
+ /* offset and len need to be even */
+ ASSERT((offset & 1) == 0);
+ ASSERT((len & 1) == 0);
+
+ if (len <= 0)
+ return;
+
+ for (i = 0; i < len; i += 2) {
+ v = wlc_bmac_read_objmem(wlc_hw, offset + i, sel);
+ p[i] = v & 0xFF;
+ p[i + 1] = (v >> 8) & 0xFF;
+ }
+}
+
+void wlc_bmac_copyfrom_vars(wlc_hw_info_t *wlc_hw, char **buf, uint *len)
+{
+ WL_TRACE(("wlc_bmac_copyfrom_vars, nvram vars totlen=%d\n",
+ wlc_hw->vars_size));
+
+ *buf = wlc_hw->vars;
+ *len = wlc_hw->vars_size;
+}
+
+void wlc_bmac_retrylimit_upd(wlc_hw_info_t *wlc_hw, u16 SRL, u16 LRL)
+{
+ wlc_hw->SRL = SRL;
+ wlc_hw->LRL = LRL;
+
+ /* write retry limit to SCR, shouldn't need to suspend */
+ if (wlc_hw->up) {
+ W_REG(wlc_hw->osh, &wlc_hw->regs->objaddr,
+ OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+ (void)R_REG(wlc_hw->osh, &wlc_hw->regs->objaddr);
+ W_REG(wlc_hw->osh, &wlc_hw->regs->objdata, wlc_hw->SRL);
+ W_REG(wlc_hw->osh, &wlc_hw->regs->objaddr,
+ OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+ (void)R_REG(wlc_hw->osh, &wlc_hw->regs->objaddr);
+ W_REG(wlc_hw->osh, &wlc_hw->regs->objdata, wlc_hw->LRL);
+ }
+}
+
+void wlc_bmac_set_noreset(wlc_hw_info_t *wlc_hw, bool noreset_flag)
+{
+ wlc_hw->noreset = noreset_flag;
+}
+
+void wlc_bmac_set_ucode_loaded(wlc_hw_info_t *wlc_hw, bool ucode_loaded)
+{
+ wlc_hw->ucode_loaded = ucode_loaded;
+}
+
+void wlc_bmac_pllreq(wlc_hw_info_t *wlc_hw, bool set, mbool req_bit)
+{
+ ASSERT(req_bit);
+
+ if (set) {
+ if (mboolisset(wlc_hw->pllreq, req_bit))
+ return;
+
+ mboolset(wlc_hw->pllreq, req_bit);
+
+ if (mboolisset(wlc_hw->pllreq, WLC_PLLREQ_FLIP)) {
+ if (!wlc_hw->sbclk) {
+ wlc_bmac_xtal(wlc_hw, ON);
+ }
+ }
+ } else {
+ if (!mboolisset(wlc_hw->pllreq, req_bit))
+ return;
+
+ mboolclr(wlc_hw->pllreq, req_bit);
+
+ if (mboolisset(wlc_hw->pllreq, WLC_PLLREQ_FLIP)) {
+ if (wlc_hw->sbclk) {
+ wlc_bmac_xtal(wlc_hw, OFF);
+ }
+ }
+ }
+
+ return;
+}
+
+void wlc_bmac_set_clk(wlc_hw_info_t *wlc_hw, bool on)
+{
+ if (on) {
+ /* power up pll and oscillator */
+ wlc_bmac_xtal(wlc_hw, ON);
+
+ /* enable core(s), ignore bandlocked
+ * Leave with the same band selected as we entered
+ */
+ wlc_bmac_corereset(wlc_hw, WLC_USE_COREFLAGS);
+ } else {
+ /* if already down, must skip the core disable */
+ if (wlc_hw->clk) {
+ /* disable core(s), ignore bandlocked */
+ wlc_coredisable(wlc_hw);
+ }
+ /* power down pll and oscillator */
+ wlc_bmac_xtal(wlc_hw, OFF);
+ }
+}
+
+/* this will be true for all ai chips */
+bool wlc_bmac_taclear(wlc_hw_info_t *wlc_hw, bool ta_ok)
+{
+ return true;
+}
+
+/* Lower down relevant GPIOs like LED when going down w/o
+ * doing PCI config cycles or touching interrupts
+ */
+void wlc_gpio_fast_deinit(wlc_hw_info_t *wlc_hw)
+{
+ if ((wlc_hw == NULL) || (wlc_hw->sih == NULL))
+ return;
+
+ /* Only chips with internal bus or PCIE cores or certain PCI cores
+ * are able to switch cores w/o disabling interrupts
+ */
+ if (!((BUSTYPE(wlc_hw->sih->bustype) == SI_BUS) ||
+ ((BUSTYPE(wlc_hw->sih->bustype) == PCI_BUS) &&
+ ((wlc_hw->sih->buscoretype == PCIE_CORE_ID) ||
+ (wlc_hw->sih->buscorerev >= 13)))))
+ return;
+
+ WL_TRACE(("wl%d: %s\n", wlc_hw->unit, __func__));
+ return;
+}
+
+bool wlc_bmac_radio_hw(wlc_hw_info_t *wlc_hw, bool enable)
+{
+ /* Do not access Phy registers if core is not up */
+ if (si_iscoreup(wlc_hw->sih) == false)
+ return false;
+
+ if (enable) {
+ if (PMUCTL_ENAB(wlc_hw->sih)) {
+ AND_REG(wlc_hw->osh, &wlc_hw->regs->clk_ctl_st,
+ ~CCS_FORCEHWREQOFF);
+ si_pmu_radio_enable(wlc_hw->sih, true);
+ }
+
+ wlc_phy_anacore(wlc_hw->band->pi, ON);
+ wlc_phy_switch_radio(wlc_hw->band->pi, ON);
+
+ /* resume d11 core */
+ wlc_enable_mac(wlc_hw->wlc);
+ } else {
+ /* suspend d11 core */
+ wlc_suspend_mac_and_wait(wlc_hw->wlc);
+
+ wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+ wlc_phy_anacore(wlc_hw->band->pi, OFF);
+
+ if (PMUCTL_ENAB(wlc_hw->sih)) {
+ si_pmu_radio_enable(wlc_hw->sih, false);
+ OR_REG(wlc_hw->osh, &wlc_hw->regs->clk_ctl_st,
+ CCS_FORCEHWREQOFF);
+ }
+ }
+
+ return true;
+}
+
+u16 wlc_bmac_rate_shm_offset(wlc_hw_info_t *wlc_hw, u8 rate)
+{
+ u16 table_ptr;
+ u8 phy_rate, index;
+
+ /* get the phy specific rate encoding for the PLCP SIGNAL field */
+ /* XXX4321 fixup needed ? */
+ if (IS_OFDM(rate))
+ table_ptr = M_RT_DIRMAP_A;
+ else
+ table_ptr = M_RT_DIRMAP_B;
+
+ /* for a given rate, the LS-nibble of the PLCP SIGNAL field is
+ * the index into the rate table.
+ */
+ phy_rate = rate_info[rate] & RATE_MASK;
+ index = phy_rate & 0xf;
+
+ /* Find the SHM pointer to the rate table entry by looking in the
+ * Direct-map Table
+ */
+ return 2 * wlc_bmac_read_shm(wlc_hw, table_ptr + (index * 2));
+}
+
+void wlc_bmac_set_txpwr_percent(wlc_hw_info_t *wlc_hw, u8 val)
+{
+ wlc_phy_txpwr_percent_set(wlc_hw->band->pi, val);
+}
+
+void wlc_bmac_antsel_set(wlc_hw_info_t *wlc_hw, u32 antsel_avail)
+{
+ wlc_hw->antsel_avail = antsel_avail;
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_bmac.h b/drivers/staging/brcm80211/sys/wlc_bmac.h
new file mode 100644
index 000000000000..872bc8d866d2
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_bmac.h
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* XXXXX this interface is under wlc.c by design
+ * http://hwnbu-twiki.broadcom.com/bin/view/Mwgroup/WlBmacDesign
+ *
+ * high driver files(e.g. wlc_ampdu.c etc)
+ * wlc.h/wlc.c
+ * wlc_bmac.h/wlc_bmac.c
+ *
+ * So don't include this in files other than wlc.c, wlc_bmac* wl_rte.c(dongle port) and wl_phy.c
+ * create wrappers in wlc.c if needed
+ */
+
+/* Revision and other info required from BMAC driver for functioning of high ONLY driver */
+typedef struct wlc_bmac_revinfo {
+ uint vendorid; /* PCI vendor id */
+ uint deviceid; /* device id of chip */
+
+ uint boardrev; /* version # of particular board */
+ uint corerev; /* core revision */
+ uint sromrev; /* srom revision */
+ uint chiprev; /* chip revision */
+ uint chip; /* chip number */
+ uint chippkg; /* chip package */
+ uint boardtype; /* board type */
+ uint boardvendor; /* board vendor */
+ uint bustype; /* SB_BUS, PCI_BUS */
+ uint buscoretype; /* PCI_CORE_ID, PCIE_CORE_ID, PCMCIA_CORE_ID */
+ uint buscorerev; /* buscore rev */
+ u32 issim; /* chip is in simulation or emulation */
+
+ uint nbands;
+
+ struct band_info {
+ uint bandunit; /* To match on both sides */
+ uint bandtype; /* To match on both sides */
+ uint radiorev;
+ uint phytype;
+ uint phyrev;
+ uint anarev;
+ uint radioid;
+ bool abgphy_encore;
+ } band[MAXBANDS];
+} wlc_bmac_revinfo_t;
+
+/* dup state between BMAC(wlc_hw_info_t) and HIGH(wlc_info_t) driver */
+typedef struct wlc_bmac_state {
+ u32 machwcap; /* mac hw capibility */
+ u32 preamble_ovr; /* preamble override */
+} wlc_bmac_state_t;
+
+enum {
+ IOV_BMAC_DIAG,
+ IOV_BMAC_SBGPIOTIMERVAL,
+ IOV_BMAC_SBGPIOOUT,
+ IOV_BMAC_CCGPIOCTRL, /* CC GPIOCTRL REG */
+ IOV_BMAC_CCGPIOOUT, /* CC GPIOOUT REG */
+ IOV_BMAC_CCGPIOOUTEN, /* CC GPIOOUTEN REG */
+ IOV_BMAC_CCGPIOIN, /* CC GPIOIN REG */
+ IOV_BMAC_WPSGPIO, /* WPS push button GPIO pin */
+ IOV_BMAC_OTPDUMP,
+ IOV_BMAC_OTPSTAT,
+ IOV_BMAC_PCIEASPM, /* obfuscation clkreq/aspm control */
+ IOV_BMAC_PCIEADVCORRMASK, /* advanced correctable error mask */
+ IOV_BMAC_PCIECLKREQ, /* PCIE 1.1 clockreq enab support */
+ IOV_BMAC_PCIELCREG, /* PCIE LCREG */
+ IOV_BMAC_SBGPIOTIMERMASK,
+ IOV_BMAC_RFDISABLEDLY,
+ IOV_BMAC_PCIEREG, /* PCIE REG */
+ IOV_BMAC_PCICFGREG, /* PCI Config register */
+ IOV_BMAC_PCIESERDESREG, /* PCIE SERDES REG (dev, 0}offset) */
+ IOV_BMAC_PCIEGPIOOUT, /* PCIEOUT REG */
+ IOV_BMAC_PCIEGPIOOUTEN, /* PCIEOUTEN REG */
+ IOV_BMAC_PCIECLKREQENCTRL, /* clkreqenctrl REG (PCIE REV > 6.0 */
+ IOV_BMAC_DMALPBK,
+ IOV_BMAC_CCREG,
+ IOV_BMAC_COREREG,
+ IOV_BMAC_SDCIS,
+ IOV_BMAC_SDIO_DRIVE,
+ IOV_BMAC_OTPW,
+ IOV_BMAC_NVOTPW,
+ IOV_BMAC_SROM,
+ IOV_BMAC_SRCRC,
+ IOV_BMAC_CIS_SOURCE,
+ IOV_BMAC_CISVAR,
+ IOV_BMAC_OTPLOCK,
+ IOV_BMAC_OTP_CHIPID,
+ IOV_BMAC_CUSTOMVAR1,
+ IOV_BMAC_BOARDFLAGS,
+ IOV_BMAC_BOARDFLAGS2,
+ IOV_BMAC_WPSLED,
+ IOV_BMAC_NVRAM_SOURCE,
+ IOV_BMAC_OTP_RAW_READ,
+ IOV_BMAC_LAST
+};
+
+typedef enum {
+ BMAC_DUMP_GPIO_ID,
+ BMAC_DUMP_SI_ID,
+ BMAC_DUMP_SIREG_ID,
+ BMAC_DUMP_SICLK_ID,
+ BMAC_DUMP_CCREG_ID,
+ BMAC_DUMP_PCIEREG_ID,
+ BMAC_DUMP_PHYREG_ID,
+ BMAC_DUMP_PHYTBL_ID,
+ BMAC_DUMP_PHYTBL2_ID,
+ BMAC_DUMP_PHY_RADIOREG_ID,
+ BMAC_DUMP_LAST
+} wlc_bmac_dump_id_t;
+
+typedef enum {
+ WLCHW_STATE_ATTACH,
+ WLCHW_STATE_CLK,
+ WLCHW_STATE_UP,
+ WLCHW_STATE_ASSOC,
+ WLCHW_STATE_LAST
+} wlc_bmac_state_id_t;
+
+extern int wlc_bmac_attach(wlc_info_t *wlc, u16 vendor, u16 device,
+ uint unit, bool piomode, osl_t *osh, void *regsva,
+ uint bustype, void *btparam);
+extern int wlc_bmac_detach(wlc_info_t *wlc);
+extern void wlc_bmac_watchdog(void *arg);
+extern void wlc_bmac_info_init(wlc_hw_info_t *wlc_hw);
+
+/* up/down, reset, clk */
+#ifdef WLC_LOW
+extern void wlc_bmac_xtal(wlc_hw_info_t *wlc_hw, bool want);
+#endif
+
+extern void wlc_bmac_copyto_objmem(wlc_hw_info_t *wlc_hw,
+ uint offset, const void *buf, int len,
+ u32 sel);
+extern void wlc_bmac_copyfrom_objmem(wlc_hw_info_t *wlc_hw, uint offset,
+ void *buf, int len, u32 sel);
+#define wlc_bmac_copyfrom_shm(wlc_hw, offset, buf, len) \
+ wlc_bmac_copyfrom_objmem(wlc_hw, offset, buf, len, OBJADDR_SHM_SEL)
+#define wlc_bmac_copyto_shm(wlc_hw, offset, buf, len) \
+ wlc_bmac_copyto_objmem(wlc_hw, offset, buf, len, OBJADDR_SHM_SEL)
+
+extern void wlc_bmac_core_phy_clk(wlc_hw_info_t *wlc_hw, bool clk);
+extern void wlc_bmac_core_phypll_reset(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_core_phypll_ctl(wlc_hw_info_t *wlc_hw, bool on);
+extern void wlc_bmac_phyclk_fgc(wlc_hw_info_t *wlc_hw, bool clk);
+extern void wlc_bmac_macphyclk_set(wlc_hw_info_t *wlc_hw, bool clk);
+extern void wlc_bmac_phy_reset(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_corereset(wlc_hw_info_t *wlc_hw, u32 flags);
+extern void wlc_bmac_reset(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_init(wlc_hw_info_t *wlc_hw, chanspec_t chanspec,
+ bool mute);
+extern int wlc_bmac_up_prep(wlc_hw_info_t *wlc_hw);
+extern int wlc_bmac_up_finish(wlc_hw_info_t *wlc_hw);
+extern int wlc_bmac_down_prep(wlc_hw_info_t *wlc_hw);
+extern int wlc_bmac_down_finish(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_corereset(wlc_hw_info_t *wlc_hw, u32 flags);
+extern void wlc_bmac_switch_macfreq(wlc_hw_info_t *wlc_hw, u8 spurmode);
+
+/* chanspec, ucode interface */
+extern int wlc_bmac_bandtype(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_set_chanspec(wlc_hw_info_t *wlc_hw, chanspec_t chanspec,
+ bool mute, struct txpwr_limits *txpwr);
+
+extern void wlc_bmac_txfifo(wlc_hw_info_t *wlc_hw, uint fifo, void *p,
+ bool commit, u16 frameid, u8 txpktpend);
+extern int wlc_bmac_xmtfifo_sz_get(wlc_hw_info_t *wlc_hw, uint fifo,
+ uint *blocks);
+extern void wlc_bmac_mhf(wlc_hw_info_t *wlc_hw, u8 idx, u16 mask,
+ u16 val, int bands);
+extern void wlc_bmac_mctrl(wlc_hw_info_t *wlc_hw, u32 mask, u32 val);
+extern u16 wlc_bmac_mhf_get(wlc_hw_info_t *wlc_hw, u8 idx, int bands);
+extern int wlc_bmac_xmtfifo_sz_set(wlc_hw_info_t *wlc_hw, uint fifo,
+ uint blocks);
+extern void wlc_bmac_txant_set(wlc_hw_info_t *wlc_hw, u16 phytxant);
+extern u16 wlc_bmac_get_txant(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_antsel_type_set(wlc_hw_info_t *wlc_hw, u8 antsel_type);
+extern int wlc_bmac_revinfo_get(wlc_hw_info_t *wlc_hw,
+ wlc_bmac_revinfo_t *revinfo);
+extern int wlc_bmac_state_get(wlc_hw_info_t *wlc_hw, wlc_bmac_state_t *state);
+extern void wlc_bmac_write_shm(wlc_hw_info_t *wlc_hw, uint offset, u16 v);
+extern u16 wlc_bmac_read_shm(wlc_hw_info_t *wlc_hw, uint offset);
+extern void wlc_bmac_set_shm(wlc_hw_info_t *wlc_hw, uint offset, u16 v,
+ int len);
+extern void wlc_bmac_write_template_ram(wlc_hw_info_t *wlc_hw, int offset,
+ int len, void *buf);
+extern void wlc_bmac_copyfrom_vars(wlc_hw_info_t *wlc_hw, char **buf,
+ uint *len);
+
+extern void wlc_bmac_process_ps_switch(wlc_hw_info_t *wlc,
+ struct ether_addr *ea, s8 ps_on);
+extern void wlc_bmac_hw_etheraddr(wlc_hw_info_t *wlc_hw,
+ struct ether_addr *ea);
+extern void wlc_bmac_set_hw_etheraddr(wlc_hw_info_t *wlc_hw,
+ struct ether_addr *ea);
+extern bool wlc_bmac_validate_chip_access(wlc_hw_info_t *wlc_hw);
+
+extern bool wlc_bmac_radio_read_hwdisabled(wlc_hw_info_t *wlc_hw);
+extern void wlc_bmac_set_shortslot(wlc_hw_info_t *wlc_hw, bool shortslot);
+extern void wlc_bmac_mute(wlc_hw_info_t *wlc_hw, bool want, mbool flags);
+extern void wlc_bmac_set_deaf(wlc_hw_info_t *wlc_hw, bool user_flag);
+extern void wlc_bmac_band_stf_ss_set(wlc_hw_info_t *wlc_hw, u8 stf_mode);
+
+extern void wlc_bmac_wait_for_wake(wlc_hw_info_t *wlc_hw);
+extern bool wlc_bmac_tx_fifo_suspended(wlc_hw_info_t *wlc_hw, uint tx_fifo);
+extern void wlc_bmac_tx_fifo_suspend(wlc_hw_info_t *wlc_hw, uint tx_fifo);
+extern void wlc_bmac_tx_fifo_resume(wlc_hw_info_t *wlc_hw, uint tx_fifo);
+
+extern void wlc_ucode_wake_override_set(wlc_hw_info_t *wlc_hw,
+ u32 override_bit);
+extern void wlc_ucode_wake_override_clear(wlc_hw_info_t *wlc_hw,
+ u32 override_bit);
+
+extern void wlc_bmac_set_rcmta(wlc_hw_info_t *wlc_hw, int idx,
+ const struct ether_addr *addr);
+extern void wlc_bmac_set_addrmatch(wlc_hw_info_t *wlc_hw, int match_reg_offset,
+ const struct ether_addr *addr);
+extern void wlc_bmac_write_hw_bcntemplates(wlc_hw_info_t *wlc_hw, void *bcn,
+ int len, bool both);
+
+extern void wlc_bmac_read_tsf(wlc_hw_info_t *wlc_hw, u32 *tsf_l_ptr,
+ u32 *tsf_h_ptr);
+extern void wlc_bmac_set_cwmin(wlc_hw_info_t *wlc_hw, u16 newmin);
+extern void wlc_bmac_set_cwmax(wlc_hw_info_t *wlc_hw, u16 newmax);
+extern void wlc_bmac_set_noreset(wlc_hw_info_t *wlc, bool noreset_flag);
+extern void wlc_bmac_set_ucode_loaded(wlc_hw_info_t *wlc, bool ucode_loaded);
+
+extern void wlc_bmac_retrylimit_upd(wlc_hw_info_t *wlc_hw, u16 SRL,
+ u16 LRL);
+
+extern void wlc_bmac_fifoerrors(wlc_hw_info_t *wlc_hw);
+
+#ifdef WLC_HIGH_ONLY
+extern void wlc_bmac_dngl_reboot(rpc_info_t *);
+extern void wlc_bmac_dngl_rpc_agg(rpc_info_t *, u16 agg);
+extern void wlc_bmac_dngl_rpc_msglevel(rpc_info_t *, u16 level);
+extern void wlc_bmac_dngl_rpc_txq_wm_set(rpc_info_t *rpc, u32 wm);
+extern void wlc_bmac_dngl_rpc_txq_wm_get(rpc_info_t *rpc, u32 *wm);
+extern void wlc_bmac_dngl_rpc_agg_limit_set(rpc_info_t *rpc, u32 val);
+extern void wlc_bmac_dngl_rpc_agg_limit_get(rpc_info_t *rpc, u32 *pval);
+extern int wlc_bmac_debug_template(wlc_hw_info_t *wlc_hw);
+#endif
+
+/* API for BMAC driver (e.g. wlc_phy.c etc) */
+
+extern void wlc_bmac_bw_set(wlc_hw_info_t *wlc_hw, u16 bw);
+extern void wlc_bmac_pllreq(wlc_hw_info_t *wlc_hw, bool set, mbool req_bit);
+extern void wlc_bmac_set_clk(wlc_hw_info_t *wlc_hw, bool on);
+extern bool wlc_bmac_taclear(wlc_hw_info_t *wlc_hw, bool ta_ok);
+extern void wlc_bmac_hw_up(struct wlc_hw_info *wlc_hw);
+
+extern void wlc_bmac_dump(wlc_hw_info_t *wlc_hw, struct bcmstrbuf *b,
+ wlc_bmac_dump_id_t dump_id);
+extern void wlc_gpio_fast_deinit(wlc_hw_info_t *wlc_hw);
+
+extern bool wlc_bmac_radio_hw(wlc_hw_info_t *wlc_hw, bool enable);
+extern u16 wlc_bmac_rate_shm_offset(wlc_hw_info_t *wlc_hw, u8 rate);
+
+extern void wlc_bmac_assert_type_set(wlc_hw_info_t *wlc_hw, u32 type);
+extern void wlc_bmac_set_txpwr_percent(wlc_hw_info_t *wlc_hw, u8 val);
+extern void wlc_bmac_blink_sync(wlc_hw_info_t *wlc_hw, u32 led_pins);
+extern void wlc_bmac_ifsctl_edcrs_set(wlc_hw_info_t *wlc_hw, bool abie,
+ bool isht);
+
+extern void wlc_bmac_antsel_set(wlc_hw_info_t *wlc_hw, u32 antsel_avail);
diff --git a/drivers/staging/brcm80211/sys/wlc_bsscfg.h b/drivers/staging/brcm80211/sys/wlc_bsscfg.h
new file mode 100644
index 000000000000..ae5542ab0334
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_bsscfg.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WLC_BSSCFG_H_
+#define _WLC_BSSCFG_H_
+
+#include <wlc_types.h>
+
+/* Check if a particular BSS config is AP or STA */
+#define BSSCFG_AP(cfg) (0)
+#define BSSCFG_STA(cfg) (1)
+
+#define BSSCFG_IBSS(cfg) (!(cfg)->BSS)
+
+/* forward declarations */
+typedef struct wlc_bsscfg wlc_bsscfg_t;
+
+#include <wlc_rate.h>
+
+#define NTXRATE 64 /* # tx MPDUs rate is reported for */
+#define MAXMACLIST 64 /* max # source MAC matches */
+#define BCN_TEMPLATE_COUNT 2
+
+/* Iterator for "associated" STA bss configs: (wlc_info_t *wlc, int idx, wlc_bsscfg_t *cfg) */
+#define FOREACH_AS_STA(wlc, idx, cfg) \
+ for (idx = 0; (int) idx < WLC_MAXBSSCFG; idx++) \
+ if ((cfg = (wlc)->bsscfg[idx]) && BSSCFG_STA(cfg) && cfg->associated)
+
+/* As above for all non-NULL BSS configs */
+#define FOREACH_BSS(wlc, idx, cfg) \
+ for (idx = 0; (int) idx < WLC_MAXBSSCFG; idx++) \
+ if ((cfg = (wlc)->bsscfg[idx]))
+
+/* BSS configuration state */
+struct wlc_bsscfg {
+ struct wlc_info *wlc; /* wlc to which this bsscfg belongs to. */
+ bool up; /* is this configuration up operational */
+ bool enable; /* is this configuration enabled */
+ bool associated; /* is BSS in ASSOCIATED state */
+ bool BSS; /* infraustructure or adhac */
+ bool dtim_programmed;
+#ifdef LATER
+ bool _ap; /* is this configuration an AP */
+ struct wlc_if *wlcif; /* virtual interface, NULL for primary bsscfg */
+ void *sup; /* pointer to supplicant state */
+ s8 sup_type; /* type of supplicant */
+ bool sup_enable_wpa; /* supplicant WPA on/off */
+ void *authenticator; /* pointer to authenticator state */
+ bool sup_auth_pending; /* flag for auth timeout */
+#endif
+ u8 SSID_len; /* the length of SSID */
+ u8 SSID[DOT11_MAX_SSID_LEN]; /* SSID string */
+ struct scb *bcmc_scb[MAXBANDS]; /* one bcmc_scb per band */
+ s8 _idx; /* the index of this bsscfg,
+ * assigned at wlc_bsscfg_alloc()
+ */
+ /* MAC filter */
+ uint nmac; /* # of entries on maclist array */
+ int macmode; /* allow/deny stations on maclist array */
+ struct ether_addr *maclist; /* list of source MAC addrs to match */
+
+ /* security */
+ u32 wsec; /* wireless security bitvec */
+ s16 auth; /* 802.11 authentication: Open, Shared Key, WPA */
+ s16 openshared; /* try Open auth first, then Shared Key */
+ bool wsec_restrict; /* drop unencrypted packets if wsec is enabled */
+ bool eap_restrict; /* restrict data until 802.1X auth succeeds */
+ u16 WPA_auth; /* WPA: authenticated key management */
+ bool wpa2_preauth; /* default is true, wpa_cap sets value */
+ bool wsec_portopen; /* indicates keys are plumbed */
+ wsec_iv_t wpa_none_txiv; /* global txiv for WPA_NONE, tkip and aes */
+ int wsec_index; /* 0-3: default tx key, -1: not set */
+ wsec_key_t *bss_def_keys[WLC_DEFAULT_KEYS]; /* default key storage */
+
+ /* TKIP countermeasures */
+ bool tkip_countermeasures; /* flags TKIP no-assoc period */
+ u32 tk_cm_dt; /* detect timer */
+ u32 tk_cm_bt; /* blocking timer */
+ u32 tk_cm_bt_tmstmp; /* Timestamp when TKIP BT is activated */
+ bool tk_cm_activate; /* activate countermeasures after EAPOL-Key sent */
+
+ struct ether_addr BSSID; /* BSSID (associated) */
+ struct ether_addr cur_etheraddr; /* h/w address */
+ u16 bcmc_fid; /* the last BCMC FID queued to TX_BCMC_FIFO */
+ u16 bcmc_fid_shm; /* the last BCMC FID written to shared mem */
+
+ u32 flags; /* WLC_BSSCFG flags; see below */
+
+ u8 *bcn; /* AP beacon */
+ uint bcn_len; /* AP beacon length */
+ bool ar_disassoc; /* disassociated in associated recreation */
+
+ int auth_atmptd; /* auth type (open/shared) attempted */
+
+ pmkid_cand_t pmkid_cand[MAXPMKID]; /* PMKID candidate list */
+ uint npmkid_cand; /* num PMKID candidates */
+ pmkid_t pmkid[MAXPMKID]; /* PMKID cache */
+ uint npmkid; /* num cached PMKIDs */
+
+ wlc_bss_info_t *target_bss; /* BSS parms during tran. to ASSOCIATED state */
+ wlc_bss_info_t *current_bss; /* BSS parms in ASSOCIATED state */
+
+ /* PM states */
+ bool PMawakebcn; /* bcn recvd during current waking state */
+ bool PMpending; /* waiting for tx status with PM indicated set */
+ bool priorPMstate; /* Detecting PM state transitions */
+ bool PSpoll; /* whether there is an outstanding PS-Poll frame */
+
+ /* BSSID entry in RCMTA, use the wsec key management infrastructure to
+ * manage the RCMTA entries.
+ */
+ wsec_key_t *rcmta;
+
+ /* 'unique' ID of this bsscfg, assigned at bsscfg allocation */
+ u16 ID;
+
+ uint txrspecidx; /* index into tx rate circular buffer */
+ ratespec_t txrspec[NTXRATE][2]; /* circular buffer of prev MPDUs tx rates */
+};
+
+#define WLC_BSSCFG_11N_DISABLE 0x1000 /* Do not advertise .11n IEs for this BSS */
+#define WLC_BSSCFG_HW_BCN 0x20 /* The BSS is generating beacons in HW */
+
+#define HWBCN_ENAB(cfg) (((cfg)->flags & WLC_BSSCFG_HW_BCN) != 0)
+#define HWPRB_ENAB(cfg) (((cfg)->flags & WLC_BSSCFG_HW_PRB) != 0)
+
+extern void wlc_bsscfg_ID_assign(struct wlc_info *wlc, wlc_bsscfg_t * bsscfg);
+
+/* Extend N_ENAB to per-BSS */
+#define BSS_N_ENAB(wlc, cfg) \
+ (N_ENAB((wlc)->pub) && !((cfg)->flags & WLC_BSSCFG_11N_DISABLE))
+
+#define MBSS_BCN_ENAB(cfg) 0
+#define MBSS_PRB_ENAB(cfg) 0
+#define SOFTBCN_ENAB(pub) (0)
+#define SOFTPRB_ENAB(pub) (0)
+#define wlc_bsscfg_tx_check(a) do { } while (0);
+
+#endif /* _WLC_BSSCFG_H_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_cfg.h b/drivers/staging/brcm80211/sys/wlc_cfg.h
new file mode 100644
index 000000000000..a415e1fd2c05
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_cfg.h
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_cfg_h_
+#define _wlc_cfg_h_
+
+#define NBANDS(wlc) ((wlc)->pub->_nbands)
+#define NBANDS_PUB(pub) ((pub)->_nbands)
+#define NBANDS_HW(hw) ((hw)->_nbands)
+
+#define IS_SINGLEBAND_5G(device) 0
+
+/* Keep WLC_HIGH_ONLY, WLC_SPLIT for USB extension later on */
+#if !defined(WLC_LOW)
+#define WLC_HIGH_ONLY
+#endif
+#if !defined(WLC_LOW)
+#define WLC_SPLIT
+#endif
+
+/* **** Core type/rev defaults **** */
+#define D11_DEFAULT 0x0fffffb0 /* Supported D11 revs: 4, 5, 7-27
+ * also need to update wlc.h MAXCOREREV
+ */
+
+#define NPHY_DEFAULT 0x000001ff /* Supported nphy revs:
+ * 0 4321a0
+ * 1 4321a1
+ * 2 4321b0/b1/c0/c1
+ * 3 4322a0
+ * 4 4322a1
+ * 5 4716a0
+ * 6 43222a0, 43224a0
+ * 7 43226a0
+ * 8 5357a0, 43236a0
+ */
+
+#define LCNPHY_DEFAULT 0x00000007 /* Supported lcnphy revs:
+ * 0 4313a0, 4336a0, 4330a0
+ * 1
+ * 2 4330a0
+ */
+
+#define SSLPNPHY_DEFAULT 0x0000000f /* Supported sslpnphy revs:
+ * 0 4329a0/k0
+ * 1 4329b0/4329C0
+ * 2 4319a0
+ * 3 5356a0
+ */
+
+#ifdef BCMSDIO
+#define D11CONF 0x100000
+#define SSLPNCONF 2
+#define GCCONF 0
+#define ACCONF 0
+#define NCONF 0
+#define LPCONF 0
+#define LCNCONF 0
+#define NTXD 32
+#define NRXD 16
+#define NRXBUFPOST 8
+#define WLC_DATAHIWAT 32
+#define RXBND 8
+#define MAXPKTCB 64
+#define AMPDU_NUM_MPDU 8
+#endif
+
+/* For undefined values, use defaults */
+#ifndef D11CONF
+#define D11CONF D11_DEFAULT
+#endif
+#ifndef NCONF
+#define NCONF NPHY_DEFAULT
+#endif
+#ifndef LCNCONF
+#define LCNCONF LCNPHY_DEFAULT
+#endif
+
+#ifndef SSLPNCONF
+#define SSLPNCONF SSLPNPHY_DEFAULT
+#endif
+
+#define BAND2G
+#define BAND5G
+#define WLANTSEL 1
+
+/********************************************************************
+ * Phy/Core Configuration. Defines macros to to check core phy/rev *
+ * compile-time configuration. Defines default core support. *
+ * ******************************************************************
+ */
+
+/* Basic macros to check a configuration bitmask */
+
+#define CONF_HAS(config, val) ((config) & (1 << (val)))
+#define CONF_MSK(config, mask) ((config) & (mask))
+#define MSK_RANGE(low, hi) ((1 << ((hi)+1)) - (1 << (low)))
+#define CONF_RANGE(config, low, hi) (CONF_MSK(config, MSK_RANGE(low, high)))
+
+#define CONF_IS(config, val) ((config) == (1 << (val)))
+#define CONF_GE(config, val) ((config) & (0-(1 << (val))))
+#define CONF_GT(config, val) ((config) & (0-2*(1 << (val))))
+#define CONF_LT(config, val) ((config) & ((1 << (val))-1))
+#define CONF_LE(config, val) ((config) & (2*(1 << (val))-1))
+
+/* Wrappers for some of the above, specific to config constants */
+
+#define NCONF_HAS(val) CONF_HAS(NCONF, val)
+#define NCONF_MSK(mask) CONF_MSK(NCONF, mask)
+#define NCONF_IS(val) CONF_IS(NCONF, val)
+#define NCONF_GE(val) CONF_GE(NCONF, val)
+#define NCONF_GT(val) CONF_GT(NCONF, val)
+#define NCONF_LT(val) CONF_LT(NCONF, val)
+#define NCONF_LE(val) CONF_LE(NCONF, val)
+
+#define LCNCONF_HAS(val) CONF_HAS(LCNCONF, val)
+#define LCNCONF_MSK(mask) CONF_MSK(LCNCONF, mask)
+#define LCNCONF_IS(val) CONF_IS(LCNCONF, val)
+#define LCNCONF_GE(val) CONF_GE(LCNCONF, val)
+#define LCNCONF_GT(val) CONF_GT(LCNCONF, val)
+#define LCNCONF_LT(val) CONF_LT(LCNCONF, val)
+#define LCNCONF_LE(val) CONF_LE(LCNCONF, val)
+
+#define D11CONF_HAS(val) CONF_HAS(D11CONF, val)
+#define D11CONF_MSK(mask) CONF_MSK(D11CONF, mask)
+#define D11CONF_IS(val) CONF_IS(D11CONF, val)
+#define D11CONF_GE(val) CONF_GE(D11CONF, val)
+#define D11CONF_GT(val) CONF_GT(D11CONF, val)
+#define D11CONF_LT(val) CONF_LT(D11CONF, val)
+#define D11CONF_LE(val) CONF_LE(D11CONF, val)
+
+#define PHYCONF_HAS(val) CONF_HAS(PHYTYPE, val)
+#define PHYCONF_IS(val) CONF_IS(PHYTYPE, val)
+
+#define NREV_IS(var, val) (NCONF_HAS(val) && (NCONF_IS(val) || ((var) == (val))))
+#define NREV_GE(var, val) (NCONF_GE(val) && (!NCONF_LT(val) || ((var) >= (val))))
+#define NREV_GT(var, val) (NCONF_GT(val) && (!NCONF_LE(val) || ((var) > (val))))
+#define NREV_LT(var, val) (NCONF_LT(val) && (!NCONF_GE(val) || ((var) < (val))))
+#define NREV_LE(var, val) (NCONF_LE(val) && (!NCONF_GT(val) || ((var) <= (val))))
+
+#define LCNREV_IS(var, val) (LCNCONF_HAS(val) && (LCNCONF_IS(val) || ((var) == (val))))
+#define LCNREV_GE(var, val) (LCNCONF_GE(val) && (!LCNCONF_LT(val) || ((var) >= (val))))
+#define LCNREV_GT(var, val) (LCNCONF_GT(val) && (!LCNCONF_LE(val) || ((var) > (val))))
+#define LCNREV_LT(var, val) (LCNCONF_LT(val) && (!LCNCONF_GE(val) || ((var) < (val))))
+#define LCNREV_LE(var, val) (LCNCONF_LE(val) && (!LCNCONF_GT(val) || ((var) <= (val))))
+
+#define D11REV_IS(var, val) (D11CONF_HAS(val) && (D11CONF_IS(val) || ((var) == (val))))
+#define D11REV_GE(var, val) (D11CONF_GE(val) && (!D11CONF_LT(val) || ((var) >= (val))))
+#define D11REV_GT(var, val) (D11CONF_GT(val) && (!D11CONF_LE(val) || ((var) > (val))))
+#define D11REV_LT(var, val) (D11CONF_LT(val) && (!D11CONF_GE(val) || ((var) < (val))))
+#define D11REV_LE(var, val) (D11CONF_LE(val) && (!D11CONF_GT(val) || ((var) <= (val))))
+
+#define PHYTYPE_IS(var, val) (PHYCONF_HAS(val) && (PHYCONF_IS(val) || ((var) == (val))))
+
+/* Finally, early-exit from switch case if anyone wants it... */
+
+#define CASECHECK(config, val) if (!(CONF_HAS(config, val))) break
+#define CASEMSK(config, mask) if (!(CONF_MSK(config, mask))) break
+
+#if (D11CONF ^ (D11CONF & D11_DEFAULT))
+#error "Unsupported MAC revision configured"
+#endif
+#if (NCONF ^ (NCONF & NPHY_DEFAULT))
+#error "Unsupported NPHY revision configured"
+#endif
+#if (LCNCONF ^ (LCNCONF & LCNPHY_DEFAULT))
+#error "Unsupported LPPHY revision configured"
+#endif
+
+/* *** Consistency checks *** */
+#if !D11CONF
+#error "No MAC revisions configured!"
+#endif
+
+#if !NCONF && !LCNCONF && !SSLPNCONF
+#error "No PHY configured!"
+#endif
+
+/* Set up PHYTYPE automatically: (depends on PHY_TYPE_X, from d11.h) */
+
+#define _PHYCONF_N (1 << PHY_TYPE_N)
+
+#if LCNCONF
+#define _PHYCONF_LCN (1 << PHY_TYPE_LCN)
+#else
+#define _PHYCONF_LCN 0
+#endif /* LCNCONF */
+
+#if SSLPNCONF
+#define _PHYCONF_SSLPN (1 << PHY_TYPE_SSN)
+#else
+#define _PHYCONF_SSLPN 0
+#endif /* SSLPNCONF */
+
+#define PHYTYPE (_PHYCONF_N | _PHYCONF_LCN | _PHYCONF_SSLPN)
+
+/* Utility macro to identify 802.11n (HT) capable PHYs */
+#define PHYTYPE_11N_CAP(phytype) \
+ (PHYTYPE_IS(phytype, PHY_TYPE_N) || \
+ PHYTYPE_IS(phytype, PHY_TYPE_LCN) || \
+ PHYTYPE_IS(phytype, PHY_TYPE_SSN))
+
+/* Last but not least: shorter wlc-specific var checks */
+#define WLCISNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_N)
+#define WLCISLCNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_LCN)
+#define WLCISSSLPNPHY(band) PHYTYPE_IS((band)->phytype, PHY_TYPE_SSN)
+
+#define WLC_PHY_11N_CAP(band) PHYTYPE_11N_CAP((band)->phytype)
+
+/**********************************************************************
+ * ------------- End of Core phy/rev configuration. ----------------- *
+ * ********************************************************************
+ */
+
+/*************************************************
+ * Defaults for tunables (e.g. sizing constants)
+ *
+ * For each new tunable, add a member to the end
+ * of wlc_tunables_t in wlc_pub.h to enable
+ * runtime checks of tunable values. (Directly
+ * using the macros in code invalidates ROM code)
+ *
+ * ***********************************************
+ */
+#ifndef NTXD
+#define NTXD 256 /* Max # of entries in Tx FIFO based on 4kb page size */
+#endif /* NTXD */
+#ifndef NRXD
+#define NRXD 256 /* Max # of entries in Rx FIFO based on 4kb page size */
+#endif /* NRXD */
+
+#ifndef NRXBUFPOST
+#define NRXBUFPOST 32 /* try to keep this # rbufs posted to the chip */
+#endif /* NRXBUFPOST */
+
+#ifndef MAXSCB /* station control blocks in cache */
+#define MAXSCB 32 /* Maximum SCBs in cache for STA */
+#endif /* MAXSCB */
+
+#ifndef AMPDU_NUM_MPDU
+#define AMPDU_NUM_MPDU 16 /* max allowed number of mpdus in an ampdu (2 streams) */
+#endif /* AMPDU_NUM_MPDU */
+
+#ifndef AMPDU_NUM_MPDU_3STREAMS
+#define AMPDU_NUM_MPDU_3STREAMS 32 /* max allowed number of mpdus in an ampdu for 3+ streams */
+#endif /* AMPDU_NUM_MPDU_3STREAMS */
+
+/* Count of packet callback structures. either of following
+ * 1. Set to the number of SCBs since a STA
+ * can queue up a rate callback for each IBSS STA it knows about, and an AP can
+ * queue up an "are you there?" Null Data callback for each associated STA
+ * 2. controlled by tunable config file
+ */
+#ifndef MAXPKTCB
+#define MAXPKTCB MAXSCB /* Max number of packet callbacks */
+#endif /* MAXPKTCB */
+
+#ifndef CTFPOOLSZ
+#define CTFPOOLSZ 128
+#endif /* CTFPOOLSZ */
+
+/* NetBSD also needs to keep track of this */
+#define WLC_MAX_UCODE_BSS (16) /* Number of BSS handled in ucode bcn/prb */
+#define WLC_MAX_UCODE_BSS4 (4) /* Number of BSS handled in sw bcn/prb */
+#ifndef WLC_MAXBSSCFG
+#define WLC_MAXBSSCFG (1) /* max # BSS configs */
+#endif /* WLC_MAXBSSCFG */
+
+#ifndef MAXBSS
+#define MAXBSS 64 /* max # available networks */
+#endif /* MAXBSS */
+
+#ifndef WLC_DATAHIWAT
+#define WLC_DATAHIWAT 50 /* data msg txq hiwat mark */
+#endif /* WLC_DATAHIWAT */
+
+#ifndef WLC_AMPDUDATAHIWAT
+#define WLC_AMPDUDATAHIWAT 255
+#endif /* WLC_AMPDUDATAHIWAT */
+
+/* bounded rx loops */
+#ifndef RXBND
+#define RXBND 8 /* max # frames to process in wlc_recv() */
+#endif /* RXBND */
+#ifndef TXSBND
+#define TXSBND 8 /* max # tx status to process in wlc_txstatus() */
+#endif /* TXSBND */
+
+#define BAND_5G(bt) ((bt) == WLC_BAND_5G)
+#define BAND_2G(bt) ((bt) == WLC_BAND_2G)
+
+#define WLBANDINITDATA(_data) _data
+#define WLBANDINITFN(_fn) _fn
+
+#define WLANTSEL_ENAB(wlc) 1
+
+#endif /* _wlc_cfg_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_channel.c b/drivers/staging/brcm80211/sys/wlc_channel.c
new file mode 100644
index 000000000000..509280337e34
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_channel.c
@@ -0,0 +1,1599 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <bcmdefs.h>
+#include <wlc_cfg.h>
+#include <osl.h>
+#include <linuxver.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <wlioctl.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_mac80211.h>
+#include <wlc_bmac.h>
+#include <wlc_stf.h>
+#include <wlc_channel.h>
+
+typedef struct wlc_cm_band {
+ u8 locale_flags; /* locale_info_t flags */
+ chanvec_t valid_channels; /* List of valid channels in the country */
+ const chanvec_t *restricted_channels; /* List of restricted use channels */
+ const chanvec_t *radar_channels; /* List of radar sensitive channels */
+ u8 PAD[8];
+} wlc_cm_band_t;
+
+struct wlc_cm_info {
+ wlc_pub_t *pub;
+ wlc_info_t *wlc;
+ char srom_ccode[WLC_CNTRY_BUF_SZ]; /* Country Code in SROM */
+ uint srom_regrev; /* Regulatory Rev for the SROM ccode */
+ const country_info_t *country; /* current country def */
+ char ccode[WLC_CNTRY_BUF_SZ]; /* current internal Country Code */
+ uint regrev; /* current Regulatory Revision */
+ char country_abbrev[WLC_CNTRY_BUF_SZ]; /* current advertised ccode */
+ wlc_cm_band_t bandstate[MAXBANDS]; /* per-band state (one per phy/radio) */
+ /* quiet channels currently for radar sensitivity or 11h support */
+ chanvec_t quiet_channels; /* channels on which we cannot transmit */
+};
+
+static int wlc_channels_init(wlc_cm_info_t *wlc_cm,
+ const country_info_t *country);
+static void wlc_set_country_common(wlc_cm_info_t *wlc_cm,
+ const char *country_abbrev,
+ const char *ccode, uint regrev,
+ const country_info_t *country);
+static int wlc_country_aggregate_map(wlc_cm_info_t *wlc_cm, const char *ccode,
+ char *mapped_ccode, uint *mapped_regrev);
+static const country_info_t *wlc_country_lookup_direct(const char *ccode,
+ uint regrev);
+static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
+ const char *ccode,
+ char *mapped_ccode,
+ uint *mapped_regrev);
+static void wlc_channels_commit(wlc_cm_info_t *wlc_cm);
+static bool wlc_japan_ccode(const char *ccode);
+static void wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *
+ wlc_cm,
+ struct
+ txpwr_limits
+ *txpwr,
+ u8
+ local_constraint_qdbm);
+void wlc_locale_add_channels(chanvec_t *target, const chanvec_t *channels);
+static const locale_mimo_info_t *wlc_get_mimo_2g(u8 locale_idx);
+static const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx);
+
+/* QDB() macro takes a dB value and converts to a quarter dB value */
+#ifdef QDB
+#undef QDB
+#endif
+#define QDB(n) ((n) * WLC_TXPWR_DB_FACTOR)
+
+/* Regulatory Matrix Spreadsheet (CLM) MIMO v3.7.9 */
+
+/*
+ * Some common channel sets
+ */
+
+/* No channels */
+static const chanvec_t chanvec_none = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* All 2.4 GHz HW channels */
+const chanvec_t chanvec_all_2G = {
+ {0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* All 5 GHz HW channels */
+const chanvec_t chanvec_all_5G = {
+ {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x11, 0x11,
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x20, 0x22, 0x22, 0x00, 0x00, 0x11,
+ 0x11, 0x11, 0x11, 0x01}
+};
+
+/*
+ * Radar channel sets
+ */
+
+/* No radar */
+#define radar_set_none chanvec_none
+
+static const chanvec_t radar_set1 = { /* Channels 52 - 64, 100 - 140 */
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, /* 52 - 60 */
+ 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11, /* 64, 100 - 124 */
+ 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128 - 140 */
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/*
+ * Restricted channel sets
+ */
+
+#define restricted_set_none chanvec_none
+
+/* Channels 34, 38, 42, 46 */
+static const chanvec_t restricted_set_japan_legacy = {
+ {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channels 12, 13 */
+static const chanvec_t restricted_set_2g_short = {
+ {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channel 165 */
+static const chanvec_t restricted_chan_165 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channels 36 - 48 & 149 - 165 */
+static const chanvec_t restricted_low_hi = {
+ {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x22, 0x22, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channels 12 - 14 */
+static const chanvec_t restricted_set_12_13_14 = {
+ {0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+#define LOCALE_CHAN_01_11 (1<<0)
+#define LOCALE_CHAN_12_13 (1<<1)
+#define LOCALE_CHAN_14 (1<<2)
+#define LOCALE_SET_5G_LOW_JP1 (1<<3) /* 34-48, step 2 */
+#define LOCALE_SET_5G_LOW_JP2 (1<<4) /* 34-46, step 4 */
+#define LOCALE_SET_5G_LOW1 (1<<5) /* 36-48, step 4 */
+#define LOCALE_SET_5G_LOW2 (1<<6) /* 52 */
+#define LOCALE_SET_5G_LOW3 (1<<7) /* 56-64, step 4 */
+#define LOCALE_SET_5G_MID1 (1<<8) /* 100-116, step 4 */
+#define LOCALE_SET_5G_MID2 (1<<9) /* 120-124, step 4 */
+#define LOCALE_SET_5G_MID3 (1<<10) /* 128 */
+#define LOCALE_SET_5G_HIGH1 (1<<11) /* 132-140, step 4 */
+#define LOCALE_SET_5G_HIGH2 (1<<12) /* 149-161, step 4 */
+#define LOCALE_SET_5G_HIGH3 (1<<13) /* 165 */
+#define LOCALE_CHAN_52_140_ALL (1<<14)
+#define LOCALE_SET_5G_HIGH4 (1<<15) /* 184-216 */
+
+#define LOCALE_CHAN_36_64 (LOCALE_SET_5G_LOW1 | LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
+#define LOCALE_CHAN_52_64 (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
+#define LOCALE_CHAN_100_124 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2)
+#define LOCALE_CHAN_100_140 \
+ (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1)
+#define LOCALE_CHAN_149_165 (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3)
+#define LOCALE_CHAN_184_216 LOCALE_SET_5G_HIGH4
+
+#define LOCALE_CHAN_01_14 (LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13 | LOCALE_CHAN_14)
+
+#define LOCALE_RADAR_SET_NONE 0
+#define LOCALE_RADAR_SET_1 1
+
+#define LOCALE_RESTRICTED_NONE 0
+#define LOCALE_RESTRICTED_SET_2G_SHORT 1
+#define LOCALE_RESTRICTED_CHAN_165 2
+#define LOCALE_CHAN_ALL_5G 3
+#define LOCALE_RESTRICTED_JAPAN_LEGACY 4
+#define LOCALE_RESTRICTED_11D_2G 5
+#define LOCALE_RESTRICTED_11D_5G 6
+#define LOCALE_RESTRICTED_LOW_HI 7
+#define LOCALE_RESTRICTED_12_13_14 8
+
+/* global memory to provide working buffer for expanded locale */
+
+static const chanvec_t *g_table_radar_set[] = {
+ &chanvec_none,
+ &radar_set1
+};
+
+static const chanvec_t *g_table_restricted_chan[] = {
+ &chanvec_none, /* restricted_set_none */
+ &restricted_set_2g_short,
+ &restricted_chan_165,
+ &chanvec_all_5G,
+ &restricted_set_japan_legacy,
+ &chanvec_all_2G, /* restricted_set_11d_2G */
+ &chanvec_all_5G, /* restricted_set_11d_5G */
+ &restricted_low_hi,
+ &restricted_set_12_13_14
+};
+
+static const chanvec_t locale_2g_01_11 = {
+ {0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_2g_12_13 = {
+ {0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_2g_14 = {
+ {0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW_JP1 = {
+ {0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW_JP2 = {
+ {0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW1 = {
+ {0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW2 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW3 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_MID1 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_MID2 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_MID3 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH1 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH2 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x20, 0x22, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH3 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_52_140_ALL = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x11, 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH4 = {
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+ 0x11, 0x11, 0x11, 0x11}
+};
+
+static const chanvec_t *g_table_locale_base[] = {
+ &locale_2g_01_11,
+ &locale_2g_12_13,
+ &locale_2g_14,
+ &locale_5g_LOW_JP1,
+ &locale_5g_LOW_JP2,
+ &locale_5g_LOW1,
+ &locale_5g_LOW2,
+ &locale_5g_LOW3,
+ &locale_5g_MID1,
+ &locale_5g_MID2,
+ &locale_5g_MID3,
+ &locale_5g_HIGH1,
+ &locale_5g_HIGH2,
+ &locale_5g_HIGH3,
+ &locale_5g_52_140_ALL,
+ &locale_5g_HIGH4
+};
+
+void wlc_locale_add_channels(chanvec_t *target, const chanvec_t *channels)
+{
+ u8 i;
+ for (i = 0; i < sizeof(chanvec_t); i++) {
+ target->vec[i] |= channels->vec[i];
+ }
+}
+
+void wlc_locale_get_channels(const locale_info_t *locale, chanvec_t *channels)
+{
+ u8 i;
+
+ bzero(channels, sizeof(chanvec_t));
+
+ for (i = 0; i < ARRAY_SIZE(g_table_locale_base); i++) {
+ if (locale->valid_channels & (1 << i)) {
+ wlc_locale_add_channels(channels,
+ g_table_locale_base[i]);
+ }
+ }
+}
+
+/*
+ * Locale Definitions - 2.4 GHz
+ */
+static const locale_info_t locale_i = { /* locale i. channel 1 - 13 */
+ LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13,
+ LOCALE_RADAR_SET_NONE,
+ LOCALE_RESTRICTED_SET_2G_SHORT,
+ {QDB(19), QDB(19), QDB(19),
+ QDB(19), QDB(19), QDB(19)},
+ {20, 20, 20, 0},
+ WLC_EIRP
+};
+
+/*
+ * Locale Definitions - 5 GHz
+ */
+static const locale_info_t locale_11 = {
+ /* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */
+ LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165,
+ LOCALE_RADAR_SET_1,
+ LOCALE_RESTRICTED_NONE,
+ {QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)},
+ {23, 23, 23, 30, 30},
+ WLC_EIRP | WLC_DFS_EU
+};
+
+#define LOCALE_2G_IDX_i 0
+static const locale_info_t *g_locale_2g_table[] = {
+ &locale_i
+};
+
+#define LOCALE_5G_IDX_11 0
+static const locale_info_t *g_locale_5g_table[] = {
+ &locale_11
+};
+
+/*
+ * MIMO Locale Definitions - 2.4 GHz
+ */
+static const locale_mimo_info_t locale_bn = {
+ {QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+ QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+ QDB(13), QDB(13), QDB(13)},
+ {0, 0, QDB(13), QDB(13), QDB(13),
+ QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+ QDB(13), 0, 0},
+ 0
+};
+
+/* locale mimo 2g indexes */
+#define LOCALE_MIMO_IDX_bn 0
+
+static const locale_mimo_info_t *g_mimo_2g_table[] = {
+ &locale_bn
+};
+
+/*
+ * MIMO Locale Definitions - 5 GHz
+ */
+static const locale_mimo_info_t locale_11n = {
+ { /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
+ {QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
+ 0
+};
+
+#define LOCALE_MIMO_IDX_11n 0
+static const locale_mimo_info_t *g_mimo_5g_table[] = {
+ &locale_11n
+};
+
+#ifdef LC
+#undef LC
+#endif
+#define LC(id) LOCALE_MIMO_IDX_ ## id
+
+#ifdef LC_2G
+#undef LC_2G
+#endif
+#define LC_2G(id) LOCALE_2G_IDX_ ## id
+
+#ifdef LC_5G
+#undef LC_5G
+#endif
+#define LC_5G(id) LOCALE_5G_IDX_ ## id
+
+#define LOCALES(band2, band5, mimo2, mimo5) {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)}
+
+static const struct {
+ char abbrev[WLC_CNTRY_BUF_SZ]; /* country abbreviation */
+ country_info_t country;
+} cntry_locales[] = {
+ {
+ "X2", LOCALES(i, 11, bn, 11n)}, /* Worldwide RoW 2 */
+};
+
+#ifdef SUPPORT_40MHZ
+/* 20MHz channel info for 40MHz pairing support */
+struct chan20_info {
+ u8 sb;
+ u8 adj_sbs;
+};
+
+/* indicates adjacent channels that are allowed for a 40 Mhz channel and
+ * those that permitted by the HT
+ */
+struct chan20_info chan20_info[] = {
+ /* 11b/11g */
+/* 0 */ {1, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 1 */ {2, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 2 */ {3, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 3 */ {4, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 4 */ {5, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 5 */ {6, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 6 */ {7, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 7 */ {8, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 8 */ {9, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 9 */ {10, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 10 */ {11, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 11 */ {12, (CH_LOWER_SB)},
+/* 12 */ {13, (CH_LOWER_SB)},
+/* 13 */ {14, (CH_LOWER_SB)},
+
+/* 11a japan high */
+/* 14 */ {34, (CH_UPPER_SB)},
+/* 15 */ {38, (CH_LOWER_SB)},
+/* 16 */ {42, (CH_LOWER_SB)},
+/* 17 */ {46, (CH_LOWER_SB)},
+
+/* 11a usa low */
+/* 18 */ {36, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 19 */ {40, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 20 */ {44, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 21 */ {48, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 22 */ {52, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 23 */ {56, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 24 */ {60, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 25 */ {64, (CH_LOWER_SB | CH_EWA_VALID)},
+
+/* 11a Europe */
+/* 26 */ {100, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 27 */ {104, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 28 */ {108, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 29 */ {112, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 30 */ {116, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 31 */ {120, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 32 */ {124, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 33 */ {128, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 34 */ {132, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 35 */ {136, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 36 */ {140, (CH_LOWER_SB)},
+
+/* 11a usa high, ref5 only */
+/* The 0x80 bit in pdiv means these are REF5, other entries are REF20 */
+/* 37 */ {149, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 38 */ {153, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 39 */ {157, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 40 */ {161, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 41 */ {165, (CH_LOWER_SB)},
+
+/* 11a japan */
+/* 42 */ {184, (CH_UPPER_SB)},
+/* 43 */ {188, (CH_LOWER_SB)},
+/* 44 */ {192, (CH_UPPER_SB)},
+/* 45 */ {196, (CH_LOWER_SB)},
+/* 46 */ {200, (CH_UPPER_SB)},
+/* 47 */ {204, (CH_LOWER_SB)},
+/* 48 */ {208, (CH_UPPER_SB)},
+/* 49 */ {212, (CH_LOWER_SB)},
+/* 50 */ {216, (CH_LOWER_SB)}
+};
+#endif /* SUPPORT_40MHZ */
+
+const locale_info_t *wlc_get_locale_2g(u8 locale_idx)
+{
+ if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) {
+ WL_ERROR(("%s: locale 2g index size out of range %d\n",
+ __func__, locale_idx));
+ ASSERT(locale_idx < ARRAY_SIZE(g_locale_2g_table));
+ return NULL;
+ }
+ return g_locale_2g_table[locale_idx];
+}
+
+const locale_info_t *wlc_get_locale_5g(u8 locale_idx)
+{
+ if (locale_idx >= ARRAY_SIZE(g_locale_5g_table)) {
+ WL_ERROR(("%s: locale 5g index size out of range %d\n",
+ __func__, locale_idx));
+ ASSERT(locale_idx < ARRAY_SIZE(g_locale_5g_table));
+ return NULL;
+ }
+ return g_locale_5g_table[locale_idx];
+}
+
+const locale_mimo_info_t *wlc_get_mimo_2g(u8 locale_idx)
+{
+ if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) {
+ WL_ERROR(("%s: mimo 2g index size out of range %d\n", __func__,
+ locale_idx));
+ return NULL;
+ }
+ return g_mimo_2g_table[locale_idx];
+}
+
+const locale_mimo_info_t *wlc_get_mimo_5g(u8 locale_idx)
+{
+ if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) {
+ WL_ERROR(("%s: mimo 5g index size out of range %d\n", __func__,
+ locale_idx));
+ return NULL;
+ }
+ return g_mimo_5g_table[locale_idx];
+}
+
+wlc_cm_info_t *wlc_channel_mgr_attach(wlc_info_t *wlc)
+{
+ wlc_cm_info_t *wlc_cm;
+ char country_abbrev[WLC_CNTRY_BUF_SZ];
+ const country_info_t *country;
+ wlc_pub_t *pub = wlc->pub;
+ char *ccode;
+
+ WL_TRACE(("wl%d: wlc_channel_mgr_attach\n", wlc->pub->unit));
+
+ wlc_cm = kzalloc(sizeof(wlc_cm_info_t), GFP_ATOMIC);
+ if (wlc_cm == NULL) {
+ WL_ERROR(("wl%d: %s: out of memory", pub->unit, __func__));
+ return NULL;
+ }
+ wlc_cm->pub = pub;
+ wlc_cm->wlc = wlc;
+ wlc->cmi = wlc_cm;
+
+ /* store the country code for passing up as a regulatory hint */
+ ccode = getvar(wlc->pub->vars, "ccode");
+ if (ccode) {
+ strncpy(wlc->pub->srom_ccode, ccode, WLC_CNTRY_BUF_SZ - 1);
+ WL_NONE(("%s: SROM country code is %c%c\n", __func__,
+ wlc->pub->srom_ccode[0], wlc->pub->srom_ccode[1]));
+ }
+
+ /* internal country information which must match regulatory constraints in firmware */
+ bzero(country_abbrev, WLC_CNTRY_BUF_SZ);
+ strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1);
+ country = wlc_country_lookup(wlc, country_abbrev);
+
+ ASSERT(country != NULL);
+
+ /* save default country for exiting 11d regulatory mode */
+ strncpy(wlc->country_default, country_abbrev, WLC_CNTRY_BUF_SZ - 1);
+
+ /* initialize autocountry_default to driver default */
+ strncpy(wlc->autocountry_default, "X2", WLC_CNTRY_BUF_SZ - 1);
+
+ wlc_set_countrycode(wlc_cm, country_abbrev);
+
+ return wlc_cm;
+}
+
+void wlc_channel_mgr_detach(wlc_cm_info_t *wlc_cm)
+{
+ if (wlc_cm)
+ kfree(wlc_cm);
+}
+
+const char *wlc_channel_country_abbrev(wlc_cm_info_t *wlc_cm)
+{
+ return wlc_cm->country_abbrev;
+}
+
+u8 wlc_channel_locale_flags(wlc_cm_info_t *wlc_cm)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+
+ return wlc_cm->bandstate[wlc->band->bandunit].locale_flags;
+}
+
+u8 wlc_channel_locale_flags_in_band(wlc_cm_info_t *wlc_cm, uint bandunit)
+{
+ return wlc_cm->bandstate[bandunit].locale_flags;
+}
+
+/* return chanvec for a given country code and band */
+bool
+wlc_channel_get_chanvec(struct wlc_info *wlc, const char *country_abbrev,
+ int bandtype, chanvec_t *channels)
+{
+ const country_info_t *country;
+ const locale_info_t *locale = NULL;
+
+ country = wlc_country_lookup(wlc, country_abbrev);
+ if (country == NULL)
+ return false;
+
+ if (bandtype == WLC_BAND_2G)
+ locale = wlc_get_locale_2g(country->locale_2G);
+ else if (bandtype == WLC_BAND_5G)
+ locale = wlc_get_locale_5g(country->locale_5G);
+ if (locale == NULL)
+ return false;
+
+ wlc_locale_get_channels(locale, channels);
+ return true;
+}
+
+/* set the driver's current country and regulatory information using a country code
+ * as the source. Lookup built in country information found with the country code.
+ */
+int wlc_set_countrycode(wlc_cm_info_t *wlc_cm, const char *ccode)
+{
+ char country_abbrev[WLC_CNTRY_BUF_SZ];
+ strncpy(country_abbrev, ccode, WLC_CNTRY_BUF_SZ);
+ return wlc_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1);
+}
+
+int
+wlc_set_countrycode_rev(wlc_cm_info_t *wlc_cm,
+ const char *country_abbrev,
+ const char *ccode, int regrev)
+{
+ const country_info_t *country;
+ char mapped_ccode[WLC_CNTRY_BUF_SZ];
+ uint mapped_regrev;
+
+ WL_NONE(("%s: (country_abbrev \"%s\", ccode \"%s\", regrev %d) SPROM \"%s\"/%u\n", __func__, country_abbrev, ccode, regrev, wlc_cm->srom_ccode, wlc_cm->srom_regrev));
+
+ /* if regrev is -1, lookup the mapped country code,
+ * otherwise use the ccode and regrev directly
+ */
+ if (regrev == -1) {
+ /* map the country code to a built-in country code, regrev, and country_info */
+ country =
+ wlc_countrycode_map(wlc_cm, ccode, mapped_ccode,
+ &mapped_regrev);
+ } else {
+ /* find the matching built-in country definition */
+ ASSERT(0);
+ country = wlc_country_lookup_direct(ccode, regrev);
+ strncpy(mapped_ccode, ccode, WLC_CNTRY_BUF_SZ);
+ mapped_regrev = regrev;
+ }
+
+ if (country == NULL)
+ return BCME_BADARG;
+
+ /* set the driver state for the country */
+ wlc_set_country_common(wlc_cm, country_abbrev, mapped_ccode,
+ mapped_regrev, country);
+
+ return 0;
+}
+
+/* set the driver's current country and regulatory information using a country code
+ * as the source. Look up built in country information found with the country code.
+ */
+static void
+wlc_set_country_common(wlc_cm_info_t *wlc_cm,
+ const char *country_abbrev,
+ const char *ccode, uint regrev,
+ const country_info_t *country)
+{
+ const locale_mimo_info_t *li_mimo;
+ const locale_info_t *locale;
+ wlc_info_t *wlc = wlc_cm->wlc;
+ char prev_country_abbrev[WLC_CNTRY_BUF_SZ];
+
+ ASSERT(country != NULL);
+
+ /* save current country state */
+ wlc_cm->country = country;
+
+ bzero(&prev_country_abbrev, WLC_CNTRY_BUF_SZ);
+ strncpy(prev_country_abbrev, wlc_cm->country_abbrev,
+ WLC_CNTRY_BUF_SZ - 1);
+
+ strncpy(wlc_cm->country_abbrev, country_abbrev, WLC_CNTRY_BUF_SZ - 1);
+ strncpy(wlc_cm->ccode, ccode, WLC_CNTRY_BUF_SZ - 1);
+ wlc_cm->regrev = regrev;
+
+ /* disable/restore nmode based on country regulations */
+ li_mimo = wlc_get_mimo_2g(country->locale_mimo_2G);
+ if (li_mimo && (li_mimo->flags & WLC_NO_MIMO)) {
+ wlc_set_nmode(wlc, OFF);
+ wlc->stf->no_cddstbc = true;
+ } else {
+ wlc->stf->no_cddstbc = false;
+ if (N_ENAB(wlc->pub) != wlc->protection->nmode_user)
+ wlc_set_nmode(wlc, wlc->protection->nmode_user);
+ }
+
+ wlc_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ wlc_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+ /* set or restore gmode as required by regulatory */
+ locale = wlc_get_locale_2g(country->locale_2G);
+ if (locale && (locale->flags & WLC_NO_OFDM)) {
+ wlc_set_gmode(wlc, GMODE_LEGACY_B, false);
+ } else {
+ wlc_set_gmode(wlc, wlc->protection->gmode_user, false);
+ }
+
+ wlc_channels_init(wlc_cm, country);
+
+ return;
+}
+
+/* Lookup a country info structure from a null terminated country code
+ * The lookup is case sensitive.
+ */
+const country_info_t *wlc_country_lookup(struct wlc_info *wlc,
+ const char *ccode)
+{
+ const country_info_t *country;
+ char mapped_ccode[WLC_CNTRY_BUF_SZ];
+ uint mapped_regrev;
+
+ /* map the country code to a built-in country code, regrev, and country_info struct */
+ country =
+ wlc_countrycode_map(wlc->cmi, ccode, mapped_ccode, &mapped_regrev);
+
+ return country;
+}
+
+static const country_info_t *wlc_countrycode_map(wlc_cm_info_t *wlc_cm,
+ const char *ccode,
+ char *mapped_ccode,
+ uint *mapped_regrev)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ const country_info_t *country;
+ uint srom_regrev = wlc_cm->srom_regrev;
+ const char *srom_ccode = wlc_cm->srom_ccode;
+ int mapped;
+
+ /* check for currently supported ccode size */
+ if (strlen(ccode) > (WLC_CNTRY_BUF_SZ - 1)) {
+ WL_ERROR(("wl%d: %s: ccode \"%s\" too long for match\n",
+ wlc->pub->unit, __func__, ccode));
+ return NULL;
+ }
+
+ /* default mapping is the given ccode and regrev 0 */
+ strncpy(mapped_ccode, ccode, WLC_CNTRY_BUF_SZ);
+ *mapped_regrev = 0;
+
+ /* If the desired country code matches the srom country code,
+ * then the mapped country is the srom regulatory rev.
+ * Otherwise look for an aggregate mapping.
+ */
+ if (!strcmp(srom_ccode, ccode)) {
+ *mapped_regrev = srom_regrev;
+ mapped = 0;
+ WL_ERROR(("srom_code == ccode %s\n", __func__));
+ ASSERT(0);
+ } else {
+ mapped =
+ wlc_country_aggregate_map(wlc_cm, ccode, mapped_ccode,
+ mapped_regrev);
+ }
+
+ /* find the matching built-in country definition */
+ country = wlc_country_lookup_direct(mapped_ccode, *mapped_regrev);
+
+ /* if there is not an exact rev match, default to rev zero */
+ if (country == NULL && *mapped_regrev != 0) {
+ *mapped_regrev = 0;
+ ASSERT(0);
+ country =
+ wlc_country_lookup_direct(mapped_ccode, *mapped_regrev);
+ }
+
+ return country;
+}
+
+static int
+wlc_country_aggregate_map(wlc_cm_info_t *wlc_cm, const char *ccode,
+ char *mapped_ccode, uint *mapped_regrev)
+{
+ return false;
+}
+
+/* Lookup a country info structure from a null terminated country
+ * abbreviation and regrev directly with no translation.
+ */
+static const country_info_t *wlc_country_lookup_direct(const char *ccode,
+ uint regrev)
+{
+ uint size, i;
+
+ /* Should just return 0 for single locale driver. */
+ /* Keep it this way in case we add more locales. (for now anyway) */
+
+ /* all other country def arrays are for regrev == 0, so if regrev is non-zero, fail */
+ if (regrev > 0)
+ return NULL;
+
+ /* find matched table entry from country code */
+ size = ARRAY_SIZE(cntry_locales);
+ for (i = 0; i < size; i++) {
+ if (strcmp(ccode, cntry_locales[i].abbrev) == 0) {
+ return &cntry_locales[i].country;
+ }
+ }
+
+ WL_ERROR(("%s: Returning NULL\n", __func__));
+ ASSERT(0);
+ return NULL;
+}
+
+static int
+wlc_channels_init(wlc_cm_info_t *wlc_cm, const country_info_t *country)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ uint i, j;
+ wlcband_t *band;
+ const locale_info_t *li;
+ chanvec_t sup_chan;
+ const locale_mimo_info_t *li_mimo;
+
+ band = wlc->band;
+ for (i = 0; i < NBANDS(wlc);
+ i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
+
+ li = BAND_5G(band->bandtype) ?
+ wlc_get_locale_5g(country->locale_5G) :
+ wlc_get_locale_2g(country->locale_2G);
+ ASSERT(li);
+ wlc_cm->bandstate[band->bandunit].locale_flags = li->flags;
+ li_mimo = BAND_5G(band->bandtype) ?
+ wlc_get_mimo_5g(country->locale_mimo_5G) :
+ wlc_get_mimo_2g(country->locale_mimo_2G);
+ ASSERT(li_mimo);
+
+ /* merge the mimo non-mimo locale flags */
+ wlc_cm->bandstate[band->bandunit].locale_flags |=
+ li_mimo->flags;
+
+ wlc_cm->bandstate[band->bandunit].restricted_channels =
+ g_table_restricted_chan[li->restricted_channels];
+ wlc_cm->bandstate[band->bandunit].radar_channels =
+ g_table_radar_set[li->radar_channels];
+
+ /* set the channel availability,
+ * masking out the channels that may not be supported on this phy
+ */
+ wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
+ &sup_chan);
+ wlc_locale_get_channels(li,
+ &wlc_cm->bandstate[band->bandunit].
+ valid_channels);
+ for (j = 0; j < sizeof(chanvec_t); j++)
+ wlc_cm->bandstate[band->bandunit].valid_channels.
+ vec[j] &= sup_chan.vec[j];
+ }
+
+ wlc_quiet_channels_reset(wlc_cm);
+ wlc_channels_commit(wlc_cm);
+
+ return 0;
+}
+
+/* Update the radio state (enable/disable) and tx power targets
+ * based on a new set of channel/regulatory information
+ */
+static void wlc_channels_commit(wlc_cm_info_t *wlc_cm)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ uint chan;
+ struct txpwr_limits txpwr;
+
+ /* search for the existence of any valid channel */
+ for (chan = 0; chan < MAXCHANNEL; chan++) {
+ if (VALID_CHANNEL20_DB(wlc, chan)) {
+ break;
+ }
+ }
+ if (chan == MAXCHANNEL)
+ chan = INVCHANNEL;
+
+ /* based on the channel search above, set or clear WL_RADIO_COUNTRY_DISABLE */
+ if (chan == INVCHANNEL) {
+ /* country/locale with no valid channels, set the radio disable bit */
+ mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+ WL_ERROR(("wl%d: %s: no valid channel for \"%s\" nbands %d bandlocked %d\n", wlc->pub->unit, __func__, wlc_cm->country_abbrev, NBANDS(wlc), wlc->bandlocked));
+ } else
+ if (mboolisset(wlc->pub->radio_disabled,
+ WL_RADIO_COUNTRY_DISABLE)) {
+ /* country/locale with valid channel, clear the radio disable bit */
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+ }
+
+ /* Now that the country abbreviation is set, if the radio supports 2G, then
+ * set channel 14 restrictions based on the new locale.
+ */
+ if (NBANDS(wlc) > 1 || BAND_2G(wlc->band->bandtype)) {
+ wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
+ wlc_japan(wlc) ? true :
+ false);
+ }
+
+ if (wlc->pub->up && chan != INVCHANNEL) {
+ wlc_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
+ wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm,
+ &txpwr,
+ WLC_TXPWR_MAX);
+ wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
+ }
+}
+
+/* reset the quiet channels vector to the union of the restricted and radar channel sets */
+void wlc_quiet_channels_reset(wlc_cm_info_t *wlc_cm)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ uint i, j;
+ wlcband_t *band;
+ const chanvec_t *chanvec;
+
+ bzero(&wlc_cm->quiet_channels, sizeof(chanvec_t));
+
+ band = wlc->band;
+ for (i = 0; i < NBANDS(wlc);
+ i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
+
+ /* initialize quiet channels for restricted channels */
+ chanvec = wlc_cm->bandstate[band->bandunit].restricted_channels;
+ for (j = 0; j < sizeof(chanvec_t); j++)
+ wlc_cm->quiet_channels.vec[j] |= chanvec->vec[j];
+
+ }
+}
+
+bool wlc_quiet_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
+{
+ return N_ENAB(wlc_cm->wlc->pub) && CHSPEC_IS40(chspec) ?
+ (isset
+ (wlc_cm->quiet_channels.vec,
+ LOWER_20_SB(CHSPEC_CHANNEL(chspec)))
+ || isset(wlc_cm->quiet_channels.vec,
+ UPPER_20_SB(CHSPEC_CHANNEL(chspec)))) : isset(wlc_cm->
+ quiet_channels.
+ vec,
+ CHSPEC_CHANNEL
+ (chspec));
+}
+
+/* Is the channel valid for the current locale? (but don't consider channels not
+ * available due to bandlocking)
+ */
+bool wlc_valid_channel20_db(wlc_cm_info_t *wlc_cm, uint val)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+
+ return VALID_CHANNEL20(wlc, val) ||
+ (!wlc->bandlocked
+ && VALID_CHANNEL20_IN_BAND(wlc, OTHERBANDUNIT(wlc), val));
+}
+
+/* Is the channel valid for the current locale and specified band? */
+bool
+wlc_valid_channel20_in_band(wlc_cm_info_t *wlc_cm, uint bandunit, uint val)
+{
+ return ((val < MAXCHANNEL)
+ && isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val));
+}
+
+/* Is the channel valid for the current locale and current band? */
+bool wlc_valid_channel20(wlc_cm_info_t *wlc_cm, uint val)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+
+ return ((val < MAXCHANNEL) &&
+ isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec,
+ val));
+}
+
+/* Is the 40 MHz allowed for the current locale and specified band? */
+bool wlc_valid_40chanspec_in_band(wlc_cm_info_t *wlc_cm, uint bandunit)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+
+ return (((wlc_cm->bandstate[bandunit].
+ locale_flags & (WLC_NO_MIMO | WLC_NO_40MHZ)) == 0)
+ && wlc->bandstate[bandunit]->mimo_cap_40);
+}
+
+static void
+wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm_info_t *wlc_cm,
+ struct txpwr_limits *txpwr,
+ u8
+ local_constraint_qdbm)
+{
+ int j;
+
+ /* CCK Rates */
+ for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) {
+ txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
+ }
+
+ /* 20 MHz Legacy OFDM SISO */
+ for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) {
+ txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
+ }
+
+ /* 20 MHz Legacy OFDM CDD */
+ for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
+ txpwr->ofdm_cdd[j] =
+ min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
+ }
+
+ /* 40 MHz Legacy OFDM SISO */
+ for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
+ txpwr->ofdm_40_siso[j] =
+ min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
+ }
+
+ /* 40 MHz Legacy OFDM CDD */
+ for (j = 0; j < WLC_NUM_RATES_OFDM; j++) {
+ txpwr->ofdm_40_cdd[j] =
+ min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
+ }
+
+ /* 20MHz MCS 0-7 SISO */
+ for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ txpwr->mcs_20_siso[j] =
+ min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
+ }
+
+ /* 20MHz MCS 0-7 CDD */
+ for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ txpwr->mcs_20_cdd[j] =
+ min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
+ }
+
+ /* 20MHz MCS 0-7 STBC */
+ for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ txpwr->mcs_20_stbc[j] =
+ min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
+ }
+
+ /* 20MHz MCS 8-15 MIMO */
+ for (j = 0; j < WLC_NUM_RATES_MCS_2_STREAM; j++)
+ txpwr->mcs_20_mimo[j] =
+ min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 0-7 SISO */
+ for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ txpwr->mcs_40_siso[j] =
+ min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
+ }
+
+ /* 40MHz MCS 0-7 CDD */
+ for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ txpwr->mcs_40_cdd[j] =
+ min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
+ }
+
+ /* 40MHz MCS 0-7 STBC */
+ for (j = 0; j < WLC_NUM_RATES_MCS_1_STREAM; j++) {
+ txpwr->mcs_40_stbc[j] =
+ min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
+ }
+
+ /* 40MHz MCS 8-15 MIMO */
+ for (j = 0; j < WLC_NUM_RATES_MCS_2_STREAM; j++)
+ txpwr->mcs_40_mimo[j] =
+ min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
+
+ /* 40MHz MCS 32 */
+ txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm);
+
+}
+
+void
+wlc_channel_set_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
+ u8 local_constraint_qdbm)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ struct txpwr_limits txpwr;
+
+ wlc_channel_reg_limits(wlc_cm, chanspec, &txpwr);
+
+ wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm, &txpwr,
+ local_constraint_qdbm);
+
+ wlc_bmac_set_chanspec(wlc->hw, chanspec,
+ (wlc_quiet_chanspec(wlc_cm, chanspec) != 0),
+ &txpwr);
+}
+
+int
+wlc_channel_set_txpower_limit(wlc_cm_info_t *wlc_cm,
+ u8 local_constraint_qdbm)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ struct txpwr_limits txpwr;
+
+ wlc_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
+
+ wlc_channel_min_txpower_limits_with_local_constraint(wlc_cm, &txpwr,
+ local_constraint_qdbm);
+
+ wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
+
+ return 0;
+}
+
+#ifdef POWER_DBG
+static void wlc_phy_txpower_limits_dump(txpwr_limits_t *txpwr)
+{
+ int i;
+ char fraction[4][4] = { " ", ".25", ".5 ", ".75" };
+
+ printf("CCK ");
+ for (i = 0; i < WLC_NUM_RATES_CCK; i++) {
+ printf(" %2d%s", txpwr->cck[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->cck[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("20 MHz OFDM SISO ");
+ for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ printf(" %2d%s", txpwr->ofdm[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("20 MHz OFDM CDD ");
+ for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ printf(" %2d%s", txpwr->ofdm_cdd[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm_cdd[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("40 MHz OFDM SISO ");
+ for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ printf(" %2d%s", txpwr->ofdm_40_siso[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm_40_siso[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("40 MHz OFDM CDD ");
+ for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ printf(" %2d%s", txpwr->ofdm_40_cdd[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->ofdm_40_cdd[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("20 MHz MCS0-7 SISO ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_20_siso[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_siso[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("20 MHz MCS0-7 CDD ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_20_cdd[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_cdd[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("20 MHz MCS0-7 STBC ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_20_stbc[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_stbc[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("20 MHz MCS8-15 SDM ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_20_mimo[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_20_mimo[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("40 MHz MCS0-7 SISO ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_40_siso[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_siso[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("40 MHz MCS0-7 CDD ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_40_cdd[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_cdd[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("40 MHz MCS0-7 STBC ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_40_stbc[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_stbc[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("40 MHz MCS8-15 SDM ");
+ for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
+ printf(" %2d%s", txpwr->mcs_40_mimo[i] / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs_40_mimo[i] % WLC_TXPWR_DB_FACTOR]);
+ }
+ printf("\n");
+
+ printf("MCS32 %2d%s\n",
+ txpwr->mcs32 / WLC_TXPWR_DB_FACTOR,
+ fraction[txpwr->mcs32 % WLC_TXPWR_DB_FACTOR]);
+}
+#endif /* POWER_DBG */
+
+void
+wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm, chanspec_t chanspec,
+ txpwr_limits_t *txpwr)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ uint i;
+ uint chan;
+ int maxpwr;
+ int delta;
+ const country_info_t *country;
+ wlcband_t *band;
+ const locale_info_t *li;
+ int conducted_max;
+ int conducted_ofdm_max;
+ const locale_mimo_info_t *li_mimo;
+ int maxpwr20, maxpwr40;
+ int maxpwr_idx;
+ uint j;
+
+ bzero(txpwr, sizeof(txpwr_limits_t));
+
+ if (!wlc_valid_chanspec_db(wlc_cm, chanspec)) {
+ country = wlc_country_lookup(wlc, wlc->autocountry_default);
+ if (country == NULL)
+ return;
+ } else {
+ country = wlc_cm->country;
+ }
+
+ chan = CHSPEC_CHANNEL(chanspec);
+ band = wlc->bandstate[CHSPEC_WLCBANDUNIT(chanspec)];
+ li = BAND_5G(band->bandtype) ?
+ wlc_get_locale_5g(country->locale_5G) :
+ wlc_get_locale_2g(country->locale_2G);
+
+ li_mimo = BAND_5G(band->bandtype) ?
+ wlc_get_mimo_5g(country->locale_mimo_5G) :
+ wlc_get_mimo_2g(country->locale_mimo_2G);
+
+ if (li->flags & WLC_EIRP) {
+ delta = band->antgain;
+ } else {
+ delta = 0;
+ if (band->antgain > QDB(6))
+ delta = band->antgain - QDB(6); /* Excess over 6 dB */
+ }
+
+ if (li == &locale_i) {
+ conducted_max = QDB(22);
+ conducted_ofdm_max = QDB(22);
+ }
+
+ /* CCK txpwr limits for 2.4G band */
+ if (BAND_2G(band->bandtype)) {
+ maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_CCK(chan)];
+
+ maxpwr = maxpwr - delta;
+ maxpwr = max(maxpwr, 0);
+ maxpwr = min(maxpwr, conducted_max);
+
+ for (i = 0; i < WLC_NUM_RATES_CCK; i++)
+ txpwr->cck[i] = (u8) maxpwr;
+ }
+
+ /* OFDM txpwr limits for 2.4G or 5G bands */
+ if (BAND_2G(band->bandtype)) {
+ maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_OFDM(chan)];
+
+ } else {
+ maxpwr = li->maxpwr[CHANNEL_POWER_IDX_5G(chan)];
+ }
+
+ maxpwr = maxpwr - delta;
+ maxpwr = max(maxpwr, 0);
+ maxpwr = min(maxpwr, conducted_ofdm_max);
+
+ /* Keep OFDM lmit below CCK limit */
+ if (BAND_2G(band->bandtype))
+ maxpwr = min_t(int, maxpwr, txpwr->cck[0]);
+
+ for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ txpwr->ofdm[i] = (u8) maxpwr;
+ }
+
+ for (i = 0; i < WLC_NUM_RATES_OFDM; i++) {
+ /* OFDM 40 MHz SISO has the same power as the corresponding MCS0-7 rate unless
+ * overriden by the locale specific code. We set this value to 0 as a
+ * flag (presumably 0 dBm isn't a possibility) and then copy the MCS0-7 value
+ * to the 40 MHz value if it wasn't explicitly set.
+ */
+ txpwr->ofdm_40_siso[i] = 0;
+
+ txpwr->ofdm_cdd[i] = (u8) maxpwr;
+
+ txpwr->ofdm_40_cdd[i] = 0;
+ }
+
+ /* MIMO/HT specific limits */
+ if (li_mimo->flags & WLC_EIRP) {
+ delta = band->antgain;
+ } else {
+ delta = 0;
+ if (band->antgain > QDB(6))
+ delta = band->antgain - QDB(6); /* Excess over 6 dB */
+ }
+
+ if (BAND_2G(band->bandtype))
+ maxpwr_idx = (chan - 1);
+ else
+ maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
+
+ maxpwr20 = li_mimo->maxpwr20[maxpwr_idx];
+ maxpwr40 = li_mimo->maxpwr40[maxpwr_idx];
+
+ maxpwr20 = maxpwr20 - delta;
+ maxpwr20 = max(maxpwr20, 0);
+ maxpwr40 = maxpwr40 - delta;
+ maxpwr40 = max(maxpwr40, 0);
+
+ /* Fill in the MCS 0-7 (SISO) rates */
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+
+ /* 20 MHz has the same power as the corresponding OFDM rate unless
+ * overriden by the locale specific code.
+ */
+ txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
+ txpwr->mcs_40_siso[i] = 0;
+ }
+
+ /* Fill in the MCS 0-7 CDD rates */
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
+ txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
+ }
+
+ /* These locales have SISO expressed in the table and override CDD later */
+ if (li_mimo == &locale_bn) {
+ if (li_mimo == &locale_bn) {
+ maxpwr20 = QDB(16);
+ maxpwr40 = 0;
+
+ if (chan >= 3 && chan <= 11) {
+ maxpwr40 = QDB(16);
+ }
+ }
+
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ txpwr->mcs_20_siso[i] = (u8) maxpwr20;
+ txpwr->mcs_40_siso[i] = (u8) maxpwr40;
+ }
+ }
+
+ /* Fill in the MCS 0-7 STBC rates */
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ txpwr->mcs_20_stbc[i] = 0;
+ txpwr->mcs_40_stbc[i] = 0;
+ }
+
+ /* Fill in the MCS 8-15 SDM rates */
+ for (i = 0; i < WLC_NUM_RATES_MCS_2_STREAM; i++) {
+ txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
+ txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
+ }
+
+ /* Fill in MCS32 */
+ txpwr->mcs32 = (u8) maxpwr40;
+
+ for (i = 0, j = 0; i < WLC_NUM_RATES_OFDM; i++, j++) {
+ if (txpwr->ofdm_40_cdd[i] == 0)
+ txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
+ if (i == 0) {
+ i = i + 1;
+ if (txpwr->ofdm_40_cdd[i] == 0)
+ txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
+ }
+ }
+
+ /* Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO value if it wasn't
+ * provided explicitly.
+ */
+
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ if (txpwr->mcs_40_siso[i] == 0)
+ txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
+ }
+
+ for (i = 0, j = 0; i < WLC_NUM_RATES_OFDM; i++, j++) {
+ if (txpwr->ofdm_40_siso[i] == 0)
+ txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
+ if (i == 0) {
+ i = i + 1;
+ if (txpwr->ofdm_40_siso[i] == 0)
+ txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
+ }
+ }
+
+ /* Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding STBC values if they weren't
+ * provided explicitly.
+ */
+ for (i = 0; i < WLC_NUM_RATES_MCS_1_STREAM; i++) {
+ if (txpwr->mcs_20_stbc[i] == 0)
+ txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
+
+ if (txpwr->mcs_40_stbc[i] == 0)
+ txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i];
+ }
+
+#ifdef POWER_DBG
+ wlc_phy_txpower_limits_dump(txpwr);
+#endif
+ return;
+}
+
+/* Returns true if currently set country is Japan or variant */
+bool wlc_japan(struct wlc_info *wlc)
+{
+ return wlc_japan_ccode(wlc->cmi->country_abbrev);
+}
+
+/* JP, J1 - J10 are Japan ccodes */
+static bool wlc_japan_ccode(const char *ccode)
+{
+ return (ccode[0] == 'J' &&
+ (ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
+}
+
+/*
+ * Validate the chanspec for this locale, for 40MHZ we need to also check that the sidebands
+ * are valid 20MZH channels in this locale and they are also a legal HT combination
+ */
+static bool
+wlc_valid_chanspec_ext(wlc_cm_info_t *wlc_cm, chanspec_t chspec, bool dualband)
+{
+ wlc_info_t *wlc = wlc_cm->wlc;
+ u8 channel = CHSPEC_CHANNEL(chspec);
+
+ /* check the chanspec */
+ if (wf_chspec_malformed(chspec)) {
+ WL_ERROR(("wl%d: malformed chanspec 0x%x\n", wlc->pub->unit,
+ chspec));
+ ASSERT(0);
+ return false;
+ }
+
+ if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
+ CHSPEC_WLCBANDUNIT(chspec))
+ return false;
+
+ /* Check a 20Mhz channel */
+ if (CHSPEC_IS20(chspec)) {
+ if (dualband)
+ return VALID_CHANNEL20_DB(wlc_cm->wlc, channel);
+ else
+ return VALID_CHANNEL20(wlc_cm->wlc, channel);
+ }
+#ifdef SUPPORT_40MHZ
+ /* We know we are now checking a 40MHZ channel, so we should only be here
+ * for NPHYS
+ */
+ if (WLCISNPHY(wlc->band) || WLCISSSLPNPHY(wlc->band)) {
+ u8 upper_sideband = 0, idx;
+ u8 num_ch20_entries =
+ sizeof(chan20_info) / sizeof(struct chan20_info);
+
+ if (!VALID_40CHANSPEC_IN_BAND(wlc, CHSPEC_WLCBANDUNIT(chspec)))
+ return false;
+
+ if (dualband) {
+ if (!VALID_CHANNEL20_DB(wlc, LOWER_20_SB(channel)) ||
+ !VALID_CHANNEL20_DB(wlc, UPPER_20_SB(channel)))
+ return false;
+ } else {
+ if (!VALID_CHANNEL20(wlc, LOWER_20_SB(channel)) ||
+ !VALID_CHANNEL20(wlc, UPPER_20_SB(channel)))
+ return false;
+ }
+
+ /* find the lower sideband info in the sideband array */
+ for (idx = 0; idx < num_ch20_entries; idx++) {
+ if (chan20_info[idx].sb == LOWER_20_SB(channel))
+ upper_sideband = chan20_info[idx].adj_sbs;
+ }
+ /* check that the lower sideband allows an upper sideband */
+ if ((upper_sideband & (CH_UPPER_SB | CH_EWA_VALID)) ==
+ (CH_UPPER_SB | CH_EWA_VALID))
+ return true;
+ return false;
+ }
+#endif /* 40 MHZ */
+
+ return false;
+}
+
+bool wlc_valid_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
+{
+ return wlc_valid_chanspec_ext(wlc_cm, chspec, false);
+}
+
+bool wlc_valid_chanspec_db(wlc_cm_info_t *wlc_cm, chanspec_t chspec)
+{
+ return wlc_valid_chanspec_ext(wlc_cm, chspec, true);
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_channel.h b/drivers/staging/brcm80211/sys/wlc_channel.h
new file mode 100644
index 000000000000..1f170aff68fd
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_channel.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WLC_CHANNEL_H_
+#define _WLC_CHANNEL_H_
+
+#include <wlc_phy_hal.h>
+
+#define WLC_TXPWR_DB_FACTOR 4 /* conversion for phy txpwr cacluations that use .25 dB units */
+
+struct wlc_info;
+
+/* maxpwr mapping to 5GHz band channels:
+ * maxpwr[0] - channels [34-48]
+ * maxpwr[1] - channels [52-60]
+ * maxpwr[2] - channels [62-64]
+ * maxpwr[3] - channels [100-140]
+ * maxpwr[4] - channels [149-165]
+ */
+#define BAND_5G_PWR_LVLS 5 /* 5 power levels for 5G */
+
+/* power level in group of 2.4GHz band channels:
+ * maxpwr[0] - CCK channels [1]
+ * maxpwr[1] - CCK channels [2-10]
+ * maxpwr[2] - CCK channels [11-14]
+ * maxpwr[3] - OFDM channels [1]
+ * maxpwr[4] - OFDM channels [2-10]
+ * maxpwr[5] - OFDM channels [11-14]
+ */
+
+/* macro to get 2.4 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_2G_CCK(c) (((c) < 2) ? 0 : (((c) < 11) ? 1 : 2)) /* cck index */
+#define CHANNEL_POWER_IDX_2G_OFDM(c) (((c) < 2) ? 3 : (((c) < 11) ? 4 : 5)) /* ofdm index */
+
+/* macro to get 5 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_5G(c) \
+ (((c) < 52) ? 0 : (((c) < 62) ? 1 : (((c) < 100) ? 2 : (((c) < 149) ? 3 : 4))))
+
+#define WLC_MAXPWR_TBL_SIZE 6 /* max of BAND_5G_PWR_LVLS and 6 for 2.4 GHz */
+#define WLC_MAXPWR_MIMO_TBL_SIZE 14 /* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */
+
+/* locale channel and power info. */
+typedef struct {
+ u32 valid_channels;
+ u8 radar_channels; /* List of radar sensitive channels */
+ u8 restricted_channels; /* List of channels used only if APs are detected */
+ s8 maxpwr[WLC_MAXPWR_TBL_SIZE]; /* Max tx pwr in qdBm for each sub-band */
+ s8 pub_maxpwr[BAND_5G_PWR_LVLS]; /* Country IE advertised max tx pwr in dBm
+ * per sub-band
+ */
+ u8 flags;
+} locale_info_t;
+
+/* bits for locale_info flags */
+#define WLC_PEAK_CONDUCTED 0x00 /* Peak for locals */
+#define WLC_EIRP 0x01 /* Flag for EIRP */
+#define WLC_DFS_TPC 0x02 /* Flag for DFS TPC */
+#define WLC_NO_OFDM 0x04 /* Flag for No OFDM */
+#define WLC_NO_40MHZ 0x08 /* Flag for No MIMO 40MHz */
+#define WLC_NO_MIMO 0x10 /* Flag for No MIMO, 20 or 40 MHz */
+#define WLC_RADAR_TYPE_EU 0x20 /* Flag for EU */
+#define WLC_DFS_FCC WLC_DFS_TPC /* Flag for DFS FCC */
+#define WLC_DFS_EU (WLC_DFS_TPC | WLC_RADAR_TYPE_EU) /* Flag for DFS EU */
+
+#define ISDFS_EU(fl) (((fl) & WLC_DFS_EU) == WLC_DFS_EU)
+
+/* locale per-channel tx power limits for MIMO frames
+ * maxpwr arrays are index by channel for 2.4 GHz limits, and
+ * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel)
+ */
+typedef struct {
+ s8 maxpwr20[WLC_MAXPWR_MIMO_TBL_SIZE]; /* tx 20 MHz power limits, qdBm units */
+ s8 maxpwr40[WLC_MAXPWR_MIMO_TBL_SIZE]; /* tx 40 MHz power limits, qdBm units */
+ u8 flags;
+} locale_mimo_info_t;
+
+extern const chanvec_t chanvec_all_2G;
+extern const chanvec_t chanvec_all_5G;
+
+/*
+ * Country names and abbreviations with locale defined from ISO 3166
+ */
+struct country_info {
+ const u8 locale_2G; /* 2.4G band locale */
+ const u8 locale_5G; /* 5G band locale */
+ const u8 locale_mimo_2G; /* 2.4G mimo info */
+ const u8 locale_mimo_5G; /* 5G mimo info */
+};
+
+typedef struct country_info country_info_t;
+
+typedef struct wlc_cm_info wlc_cm_info_t;
+
+extern wlc_cm_info_t *wlc_channel_mgr_attach(struct wlc_info *wlc);
+extern void wlc_channel_mgr_detach(wlc_cm_info_t *wlc_cm);
+
+extern int wlc_set_countrycode(wlc_cm_info_t *wlc_cm, const char *ccode);
+extern int wlc_set_countrycode_rev(wlc_cm_info_t *wlc_cm,
+ const char *country_abbrev,
+ const char *ccode, int regrev);
+
+extern const char *wlc_channel_country_abbrev(wlc_cm_info_t *wlc_cm);
+extern u8 wlc_channel_locale_flags(wlc_cm_info_t *wlc_cm);
+extern u8 wlc_channel_locale_flags_in_band(wlc_cm_info_t *wlc_cm,
+ uint bandunit);
+
+extern void wlc_quiet_channels_reset(wlc_cm_info_t *wlc_cm);
+extern bool wlc_quiet_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec);
+
+#define VALID_CHANNEL20_DB(wlc, val) wlc_valid_channel20_db((wlc)->cmi, val)
+#define VALID_CHANNEL20_IN_BAND(wlc, bandunit, val) \
+ wlc_valid_channel20_in_band((wlc)->cmi, bandunit, val)
+#define VALID_CHANNEL20(wlc, val) wlc_valid_channel20((wlc)->cmi, val)
+#define VALID_40CHANSPEC_IN_BAND(wlc, bandunit) wlc_valid_40chanspec_in_band((wlc)->cmi, bandunit)
+
+extern bool wlc_valid_chanspec(wlc_cm_info_t *wlc_cm, chanspec_t chspec);
+extern bool wlc_valid_chanspec_db(wlc_cm_info_t *wlc_cm, chanspec_t chspec);
+extern bool wlc_valid_channel20_db(wlc_cm_info_t *wlc_cm, uint val);
+extern bool wlc_valid_channel20_in_band(wlc_cm_info_t *wlc_cm, uint bandunit,
+ uint val);
+extern bool wlc_valid_channel20(wlc_cm_info_t *wlc_cm, uint val);
+extern bool wlc_valid_40chanspec_in_band(wlc_cm_info_t *wlc_cm, uint bandunit);
+
+extern void wlc_channel_reg_limits(wlc_cm_info_t *wlc_cm,
+ chanspec_t chanspec,
+ struct txpwr_limits *txpwr);
+extern void wlc_channel_set_chanspec(wlc_cm_info_t *wlc_cm,
+ chanspec_t chanspec,
+ u8 local_constraint_qdbm);
+extern int wlc_channel_set_txpower_limit(wlc_cm_info_t *wlc_cm,
+ u8 local_constraint_qdbm);
+
+extern const country_info_t *wlc_country_lookup(struct wlc_info *wlc,
+ const char *ccode);
+extern void wlc_locale_get_channels(const locale_info_t *locale,
+ chanvec_t *valid_channels);
+extern const locale_info_t *wlc_get_locale_2g(u8 locale_idx);
+extern const locale_info_t *wlc_get_locale_5g(u8 locale_idx);
+extern bool wlc_japan(struct wlc_info *wlc);
+
+extern u8 wlc_get_regclass(wlc_cm_info_t *wlc_cm, chanspec_t chanspec);
+extern bool wlc_channel_get_chanvec(struct wlc_info *wlc,
+ const char *country_abbrev, int bandtype,
+ chanvec_t *channels);
+
+#endif /* _WLC_CHANNEL_H */
diff --git a/drivers/staging/brcm80211/sys/wlc_event.c b/drivers/staging/brcm80211/sys/wlc_event.c
new file mode 100644
index 000000000000..7e1bf0e2ecdd
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_event.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <bcmdefs.h>
+#include <linuxver.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <wlioctl.h>
+#include <wlc_cfg.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wl_export.h>
+#include <wlc_event.h>
+
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_mac80211.h>
+#ifdef MSGTRACE
+#include <msgtrace.h>
+#endif
+
+/* Local prototypes */
+static void wlc_timer_cb(void *arg);
+
+/* Private data structures */
+struct wlc_eventq {
+ wlc_event_t *head;
+ wlc_event_t *tail;
+ struct wlc_info *wlc;
+ void *wl;
+ wlc_pub_t *pub;
+ bool tpending;
+ bool workpending;
+ struct wl_timer *timer;
+ wlc_eventq_cb_t cb;
+ u8 event_inds_mask[broken_roundup(WLC_E_LAST, NBBY) / NBBY];
+};
+
+/*
+ * Export functions
+ */
+wlc_eventq_t *wlc_eventq_attach(wlc_pub_t *pub, struct wlc_info *wlc, void *wl,
+ wlc_eventq_cb_t cb)
+{
+ wlc_eventq_t *eq;
+
+ eq = kzalloc(sizeof(wlc_eventq_t), GFP_ATOMIC);
+ if (eq == NULL)
+ return NULL;
+
+ eq->cb = cb;
+ eq->wlc = wlc;
+ eq->wl = wl;
+ eq->pub = pub;
+
+ eq->timer = wl_init_timer(eq->wl, wlc_timer_cb, eq, "eventq");
+ if (!eq->timer) {
+ WL_ERROR(("wl%d: wlc_eventq_attach: timer failed\n",
+ pub->unit));
+ kfree(eq);
+ return NULL;
+ }
+
+ return eq;
+}
+
+int wlc_eventq_detach(wlc_eventq_t *eq)
+{
+ /* Clean up pending events */
+ wlc_eventq_down(eq);
+
+ if (eq->timer) {
+ if (eq->tpending) {
+ wl_del_timer(eq->wl, eq->timer);
+ eq->tpending = false;
+ }
+ wl_free_timer(eq->wl, eq->timer);
+ eq->timer = NULL;
+ }
+
+ ASSERT(wlc_eventq_avail(eq) == false);
+ kfree(eq);
+ return 0;
+}
+
+int wlc_eventq_down(wlc_eventq_t *eq)
+{
+ int callbacks = 0;
+ if (eq->tpending && !eq->workpending) {
+ if (!wl_del_timer(eq->wl, eq->timer))
+ callbacks++;
+
+ ASSERT(wlc_eventq_avail(eq) == true);
+ ASSERT(eq->workpending == false);
+ eq->workpending = true;
+ if (eq->cb)
+ eq->cb(eq->wlc);
+
+ ASSERT(eq->workpending == true);
+ eq->workpending = false;
+ eq->tpending = false;
+ } else {
+ ASSERT(eq->workpending || wlc_eventq_avail(eq) == false);
+ }
+ return callbacks;
+}
+
+wlc_event_t *wlc_event_alloc(wlc_eventq_t *eq)
+{
+ wlc_event_t *e;
+
+ e = kzalloc(sizeof(wlc_event_t), GFP_ATOMIC);
+
+ if (e == NULL)
+ return NULL;
+
+ return e;
+}
+
+void wlc_event_free(wlc_eventq_t *eq, wlc_event_t *e)
+{
+ ASSERT(e->data == NULL);
+ ASSERT(e->next == NULL);
+ kfree(e);
+}
+
+void wlc_eventq_enq(wlc_eventq_t *eq, wlc_event_t *e)
+{
+ ASSERT(e->next == NULL);
+ e->next = NULL;
+
+ if (eq->tail) {
+ eq->tail->next = e;
+ eq->tail = e;
+ } else
+ eq->head = eq->tail = e;
+
+ if (!eq->tpending) {
+ eq->tpending = true;
+ /* Use a zero-delay timer to trigger
+ * delayed processing of the event.
+ */
+ wl_add_timer(eq->wl, eq->timer, 0, 0);
+ }
+}
+
+wlc_event_t *wlc_eventq_deq(wlc_eventq_t *eq)
+{
+ wlc_event_t *e;
+
+ e = eq->head;
+ if (e) {
+ eq->head = e->next;
+ e->next = NULL;
+
+ if (eq->head == NULL)
+ eq->tail = eq->head;
+ }
+ return e;
+}
+
+wlc_event_t *wlc_eventq_next(wlc_eventq_t *eq, wlc_event_t *e)
+{
+#ifdef BCMDBG
+ wlc_event_t *etmp;
+
+ for (etmp = eq->head; etmp; etmp = etmp->next) {
+ if (etmp == e)
+ break;
+ }
+ ASSERT(etmp != NULL);
+#endif
+
+ return e->next;
+}
+
+int wlc_eventq_cnt(wlc_eventq_t *eq)
+{
+ wlc_event_t *etmp;
+ int cnt = 0;
+
+ for (etmp = eq->head; etmp; etmp = etmp->next)
+ cnt++;
+
+ return cnt;
+}
+
+bool wlc_eventq_avail(wlc_eventq_t *eq)
+{
+ return (eq->head != NULL);
+}
+
+/*
+ * Local Functions
+ */
+static void wlc_timer_cb(void *arg)
+{
+ struct wlc_eventq *eq = (struct wlc_eventq *)arg;
+
+ ASSERT(eq->tpending == true);
+ ASSERT(wlc_eventq_avail(eq) == true);
+ ASSERT(eq->workpending == false);
+ eq->workpending = true;
+
+ if (eq->cb)
+ eq->cb(eq->wlc);
+
+ ASSERT(wlc_eventq_avail(eq) == false);
+ ASSERT(eq->tpending == true);
+ eq->workpending = false;
+ eq->tpending = false;
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_event.h b/drivers/staging/brcm80211/sys/wlc_event.h
new file mode 100644
index 000000000000..e443dae258b7
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_event.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WLC_EVENT_H_
+#define _WLC_EVENT_H_
+
+typedef struct wlc_eventq wlc_eventq_t;
+
+typedef void (*wlc_eventq_cb_t) (void *arg);
+
+extern wlc_eventq_t *wlc_eventq_attach(wlc_pub_t *pub, struct wlc_info *wlc,
+ void *wl, wlc_eventq_cb_t cb);
+extern int wlc_eventq_detach(wlc_eventq_t *eq);
+extern int wlc_eventq_down(wlc_eventq_t *eq);
+extern void wlc_event_free(wlc_eventq_t *eq, wlc_event_t *e);
+extern wlc_event_t *wlc_eventq_next(wlc_eventq_t *eq, wlc_event_t *e);
+extern int wlc_eventq_cnt(wlc_eventq_t *eq);
+extern bool wlc_eventq_avail(wlc_eventq_t *eq);
+extern wlc_event_t *wlc_eventq_deq(wlc_eventq_t *eq);
+extern void wlc_eventq_enq(wlc_eventq_t *eq, wlc_event_t *e);
+extern wlc_event_t *wlc_event_alloc(wlc_eventq_t *eq);
+
+extern int wlc_eventq_register_ind(wlc_eventq_t *eq, void *bitvect);
+extern int wlc_eventq_query_ind(wlc_eventq_t *eq, void *bitvect);
+extern int wlc_eventq_test_ind(wlc_eventq_t *eq, int et);
+extern int wlc_eventq_set_ind(wlc_eventq_t *eq, uint et, bool on);
+extern void wlc_eventq_flush(wlc_eventq_t *eq);
+extern void wlc_assign_event_msg(wlc_info_t *wlc, wl_event_msg_t *msg,
+ const wlc_event_t *e, u8 *data,
+ u32 len);
+
+#ifdef MSGTRACE
+extern void wlc_event_sendup_trace(struct wlc_info *wlc, hndrte_dev_t *bus,
+ u8 *hdr, u16 hdrlen, u8 *buf,
+ u16 buflen);
+#endif
+
+#endif /* _WLC_EVENT_H_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_key.h b/drivers/staging/brcm80211/sys/wlc_key.h
new file mode 100644
index 000000000000..6678c69f1e1c
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_key.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_key_h_
+#define _wlc_key_h_
+
+struct scb;
+struct wlc_info;
+struct wlc_bsscfg;
+/* Maximum # of keys that wl driver supports in S/W.
+ * Keys supported in H/W is less than or equal to WSEC_MAX_KEYS.
+ */
+#define WSEC_MAX_KEYS 54 /* Max # of keys (50 + 4 default keys) */
+#define WLC_DEFAULT_KEYS 4 /* Default # of keys */
+
+#define WSEC_MAX_WOWL_KEYS 5 /* Max keys in WOWL mode (1 + 4 default keys) */
+
+#define WPA2_GTK_MAX 3
+
+/*
+* Max # of keys currently supported:
+*
+* s/w keys if WSEC_SW(wlc->wsec).
+* h/w keys otherwise.
+*/
+#define WLC_MAX_WSEC_KEYS(wlc) WSEC_MAX_KEYS
+
+/* number of 802.11 default (non-paired, group keys) */
+#define WSEC_MAX_DEFAULT_KEYS 4 /* # of default keys */
+
+/* Max # of hardware keys supported */
+#define WLC_MAX_WSEC_HW_KEYS(wlc) WSEC_MAX_RCMTA_KEYS
+
+/* Max # of hardware TKIP MIC keys supported */
+#define WLC_MAX_TKMIC_HW_KEYS(wlc) ((D11REV_GE((wlc)->pub->corerev, 13)) ? \
+ WSEC_MAX_TKMIC_ENGINE_KEYS : 0)
+
+#define WSEC_HW_TKMIC_KEY(wlc, key, bsscfg) \
+ (((D11REV_GE((wlc)->pub->corerev, 13)) && ((wlc)->machwcap & MCAP_TKIPMIC)) && \
+ (key) && ((key)->algo == CRYPTO_ALGO_TKIP) && \
+ !WSEC_SOFTKEY(wlc, key, bsscfg) && \
+ WSEC_KEY_INDEX(wlc, key) >= WLC_DEFAULT_KEYS && \
+ (WSEC_KEY_INDEX(wlc, key) < WSEC_MAX_TKMIC_ENGINE_KEYS))
+
+/* index of key in key table */
+#define WSEC_KEY_INDEX(wlc, key) ((key)->idx)
+
+#define WSEC_SOFTKEY(wlc, key, bsscfg) (WLC_SW_KEYS(wlc, bsscfg) || \
+ WSEC_KEY_INDEX(wlc, key) >= WLC_MAX_WSEC_HW_KEYS(wlc))
+
+/* get a key, non-NULL only if key allocated and not clear */
+#define WSEC_KEY(wlc, i) (((wlc)->wsec_keys[i] && (wlc)->wsec_keys[i]->len) ? \
+ (wlc)->wsec_keys[i] : NULL)
+
+#define WSEC_SCB_KEY_VALID(scb) (((scb)->key && (scb)->key->len) ? true : false)
+
+/* default key */
+#define WSEC_BSS_DEFAULT_KEY(bsscfg) (((bsscfg)->wsec_index == -1) ? \
+ (struct wsec_key *)NULL:(bsscfg)->bss_def_keys[(bsscfg)->wsec_index])
+
+/* Macros for key management in IBSS mode */
+#define WSEC_IBSS_MAX_PEERS 16 /* Max # of IBSS Peers */
+#define WSEC_IBSS_RCMTA_INDEX(idx) \
+ (((idx - WSEC_MAX_DEFAULT_KEYS) % WSEC_IBSS_MAX_PEERS) + WSEC_MAX_DEFAULT_KEYS)
+
+/* contiguous # key slots for infrastructure mode STA */
+#define WSEC_BSS_STA_KEY_GROUP_SIZE 5
+
+typedef struct wsec_iv {
+ u32 hi; /* upper 32 bits of IV */
+ u16 lo; /* lower 16 bits of IV */
+} wsec_iv_t;
+
+#define WLC_NUMRXIVS 16 /* # rx IVs (one per 802.11e TID) */
+
+typedef struct wsec_key {
+ struct ether_addr ea; /* per station */
+ u8 idx; /* key index in wsec_keys array */
+ u8 id; /* key ID [0-3] */
+ u8 algo; /* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+ u8 rcmta; /* rcmta entry index, same as idx by default */
+ u16 flags; /* misc flags */
+ u8 algo_hw; /* cache for hw register */
+ u8 aes_mode; /* cache for hw register */
+ s8 iv_len; /* IV length */
+ s8 icv_len; /* ICV length */
+ u32 len; /* key length..don't move this var */
+ /* data is 4byte aligned */
+ u8 data[DOT11_MAX_KEY_SIZE]; /* key data */
+ wsec_iv_t rxiv[WLC_NUMRXIVS]; /* Rx IV (one per TID) */
+ wsec_iv_t txiv; /* Tx IV */
+
+} wsec_key_t;
+
+#define broken_roundup(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
+typedef struct {
+ u8 vec[broken_roundup(WSEC_MAX_KEYS, NBBY) / NBBY]; /* bitvec of wsec_key indexes */
+} wsec_key_vec_t;
+
+/* For use with wsec_key_t.flags */
+
+#define WSEC_BS_UPDATE (1 << 0) /* Indicates hw needs key update on BS switch */
+#define WSEC_PRIMARY_KEY (1 << 1) /* Indicates this key is the primary (ie tx) key */
+#define WSEC_TKIP_ERROR (1 << 2) /* Provoke deliberate MIC error */
+#define WSEC_REPLAY_ERROR (1 << 3) /* Provoke deliberate replay */
+#define WSEC_IBSS_PEER_GROUP_KEY (1 << 7) /* Flag: group key for a IBSS PEER */
+#define WSEC_ICV_ERROR (1 << 8) /* Provoke deliberate ICV error */
+
+#define wlc_key_insert(a, b, c, d, e, f, g, h, i, j) (BCME_ERROR)
+#define wlc_key_update(a, b, c) do {} while (0)
+#define wlc_key_remove(a, b, c) do {} while (0)
+#define wlc_key_remove_all(a, b) do {} while (0)
+#define wlc_key_delete(a, b, c) do {} while (0)
+#define wlc_scb_key_delete(a, b) do {} while (0)
+#define wlc_key_lookup(a, b, c, d, e) (NULL)
+#define wlc_key_hw_init_all(a) do {} while (0)
+#define wlc_key_hw_init(a, b, c) do {} while (0)
+#define wlc_key_hw_wowl_init(a, b, c, d) do {} while (0)
+#define wlc_key_sw_wowl_update(a, b, c, d, e) do {} while (0)
+#define wlc_key_sw_wowl_create(a, b, c) (BCME_ERROR)
+#define wlc_key_iv_update(a, b, c, d, e) do {(void)e; } while (0)
+#define wlc_key_iv_init(a, b, c) do {} while (0)
+#define wlc_key_set_error(a, b, c) (BCME_ERROR)
+#define wlc_key_dump_hw(a, b) (BCME_ERROR)
+#define wlc_key_dump_sw(a, b) (BCME_ERROR)
+#define wlc_key_defkeyflag(a) (0)
+#define wlc_rcmta_add_bssid(a, b) do {} while (0)
+#define wlc_rcmta_del_bssid(a, b) do {} while (0)
+#define wlc_key_scb_delete(a, b) do {} while (0)
+
+#endif /* _wlc_key_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_mac80211.c b/drivers/staging/brcm80211/sys/wlc_mac80211.c
new file mode 100644
index 000000000000..feaffcc64ec6
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_mac80211.c
@@ -0,0 +1,8675 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/ctype.h>
+#include <bcmdefs.h>
+#include <wlc_cfg.h>
+#include <linuxver.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <bcmwifi.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <proto/wpa.h>
+#include <pcicfg.h>
+#include <bcmsrom.h>
+#include <wlioctl.h>
+#include <epivers.h>
+#include <sbhnddma.h>
+#include <hnddma.h>
+#include <hndpmu.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_bsscfg.h>
+#include <wlc_channel.h>
+#include <wlc_mac80211.h>
+#include <wlc_bmac.h>
+#include <wlc_scb.h>
+#include <wlc_phy_hal.h>
+#include <wlc_phy_shim.h>
+#include <wlc_antsel.h>
+#include <wlc_stf.h>
+#include <wlc_ampdu.h>
+#include <wlc_event.h>
+#include <wl_export.h>
+#ifdef BCMSDIO
+#include <bcmsdh.h>
+#else
+#include "d11ucode_ext.h"
+#endif
+#ifdef WLC_HIGH_ONLY
+#include <bcm_rpc_tp.h>
+#include <bcm_rpc.h>
+#include <bcm_xdr.h>
+#include <wlc_rpc.h>
+#include <wlc_rpctx.h>
+#endif /* WLC_HIGH_ONLY */
+#include <wlc_alloc.h>
+#include <net/mac80211.h>
+
+#ifdef WLC_HIGH_ONLY
+#undef R_REG
+#undef W_REG
+#define R_REG(osh, r) RPC_READ_REG(osh, r)
+#define W_REG(osh, r, v) RPC_WRITE_REG(osh, r, v)
+#endif
+
+/*
+ * buffer length needed for wlc_format_ssid
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1)
+
+#define TIMER_INTERVAL_WATCHDOG 1000 /* watchdog timer, in unit of ms */
+#define TIMER_INTERVAL_RADIOCHK 800 /* radio monitor timer, in unit of ms */
+
+#ifndef WLC_MPC_MAX_DELAYCNT
+#define WLC_MPC_MAX_DELAYCNT 10 /* Max MPC timeout, in unit of watchdog */
+#endif
+#define WLC_MPC_MIN_DELAYCNT 1 /* Min MPC timeout, in unit of watchdog */
+#define WLC_MPC_THRESHOLD 3 /* MPC count threshold level */
+
+#define BEACON_INTERVAL_DEFAULT 100 /* beacon interval, in unit of 1024TU */
+#define DTIM_INTERVAL_DEFAULT 3 /* DTIM interval, in unit of beacon interval */
+
+/* Scale down delays to accommodate QT slow speed */
+#define BEACON_INTERVAL_DEF_QT 20 /* beacon interval, in unit of 1024TU */
+#define DTIM_INTERVAL_DEF_QT 1 /* DTIM interval, in unit of beacon interval */
+
+#define TBTT_ALIGN_LEEWAY_US 100 /* min leeway before first TBTT in us */
+
+/*
+ * driver maintains internal 'tick'(wlc->pub->now) which increments in 1s OS timer(soft
+ * watchdog) it is not a wall clock and won't increment when driver is in "down" state
+ * this low resolution driver tick can be used for maintenance tasks such as phy
+ * calibration and scb update
+ */
+
+/* watchdog trigger mode: OSL timer or TBTT */
+#define WLC_WATCHDOG_TBTT(wlc) \
+ (wlc->stas_associated > 0 && wlc->PM != PM_OFF && wlc->pub->align_wd_tbtt)
+
+/* To inform the ucode of the last mcast frame posted so that it can clear moredata bit */
+#define BCMCFID(wlc, fid) wlc_bmac_write_shm((wlc)->hw, M_BCMC_FID, (fid))
+
+#ifndef WLC_HIGH_ONLY
+#define WLC_WAR16165(wlc) (BUSTYPE(wlc->pub->sih->bustype) == PCI_BUS && \
+ (!AP_ENAB(wlc->pub)) && (wlc->war16165))
+#else
+#define WLC_WAR16165(wlc) (false)
+#endif /* WLC_HIGH_ONLY */
+
+/* debug/trace */
+uint wl_msg_level =
+#if defined(BCMDBG)
+ WL_ERROR_VAL;
+#else
+ 0;
+#endif /* BCMDBG */
+
+/* Find basic rate for a given rate */
+#define WLC_BASIC_RATE(wlc, rspec) (IS_MCS(rspec) ? \
+ (wlc)->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK].leg_ofdm] : \
+ (wlc)->band->basic_rate[rspec & RSPEC_RATE_MASK])
+
+#define FRAMETYPE(r, mimoframe) (IS_MCS(r) ? mimoframe : (IS_CCK(r) ? FT_CCK : FT_OFDM))
+
+#define RFDISABLE_DEFAULT 10000000 /* rfdisable delay timer 500 ms, runs of ALP clock */
+
+#define WLC_TEMPSENSE_PERIOD 10 /* 10 second timeout */
+
+#define SCAN_IN_PROGRESS(x) 0
+
+#ifdef BCMDBG
+/* pointer to most recently allocated wl/wlc */
+static wlc_info_t *wlc_info_dbg = (wlc_info_t *) (NULL);
+#endif
+
+/* IOVar table */
+
+/* Parameter IDs, for use only internally to wlc -- in the wlc_iovars
+ * table and by the wlc_doiovar() function. No ordering is imposed:
+ * the table is keyed by name, and the function uses a switch.
+ */
+enum {
+ IOV_MPC = 1,
+ IOV_QTXPOWER,
+ IOV_BCN_LI_BCN, /* Beacon listen interval in # of beacons */
+ IOV_LAST /* In case of a need to check max ID number */
+};
+
+const bcm_iovar_t wlc_iovars[] = {
+ {"mpc", IOV_MPC, (IOVF_OPEN_ALLOW), IOVT_BOOL, 0},
+ {"qtxpower", IOV_QTXPOWER, (IOVF_WHL | IOVF_OPEN_ALLOW), IOVT_UINT32,
+ 0},
+ {"bcn_li_bcn", IOV_BCN_LI_BCN, 0, IOVT_UINT8, 0},
+ {NULL, 0, 0, 0, 0}
+};
+
+const u8 prio2fifo[NUMPRIO] = {
+ TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */
+ TX_AC_BK_FIFO, /* 1 BK AC_BK Background */
+ TX_AC_BK_FIFO, /* 2 -- AC_BK Background */
+ TX_AC_BE_FIFO, /* 3 EE AC_BE Best Effort */
+ TX_AC_VI_FIFO, /* 4 CL AC_VI Video */
+ TX_AC_VI_FIFO, /* 5 VI AC_VI Video */
+ TX_AC_VO_FIFO, /* 6 VO AC_VO Voice */
+ TX_AC_VO_FIFO /* 7 NC AC_VO Voice */
+};
+
+/* precedences numbers for wlc queues. These are twice as may levels as
+ * 802.1D priorities.
+ * Odd numbers are used for HI priority traffic at same precedence levels
+ * These constants are used ONLY by wlc_prio2prec_map. Do not use them elsewhere.
+ */
+#define _WLC_PREC_NONE 0 /* None = - */
+#define _WLC_PREC_BK 2 /* BK - Background */
+#define _WLC_PREC_BE 4 /* BE - Best-effort */
+#define _WLC_PREC_EE 6 /* EE - Excellent-effort */
+#define _WLC_PREC_CL 8 /* CL - Controlled Load */
+#define _WLC_PREC_VI 10 /* Vi - Video */
+#define _WLC_PREC_VO 12 /* Vo - Voice */
+#define _WLC_PREC_NC 14 /* NC - Network Control */
+
+/* 802.1D Priority to precedence queue mapping */
+const u8 wlc_prio2prec_map[] = {
+ _WLC_PREC_BE, /* 0 BE - Best-effort */
+ _WLC_PREC_BK, /* 1 BK - Background */
+ _WLC_PREC_NONE, /* 2 None = - */
+ _WLC_PREC_EE, /* 3 EE - Excellent-effort */
+ _WLC_PREC_CL, /* 4 CL - Controlled Load */
+ _WLC_PREC_VI, /* 5 Vi - Video */
+ _WLC_PREC_VO, /* 6 Vo - Voice */
+ _WLC_PREC_NC, /* 7 NC - Network Control */
+};
+
+/* Sanity check for tx_prec_map and fifo synchup
+ * Either there are some packets pending for the fifo, else if fifo is empty then
+ * all the corresponding precmap bits should be set
+ */
+#define WLC_TX_FIFO_CHECK(wlc, fifo) (TXPKTPENDGET((wlc), (fifo)) || \
+ (TXPKTPENDGET((wlc), (fifo)) == 0 && \
+ ((wlc)->tx_prec_map & (wlc)->fifo2prec_map[(fifo)]) == \
+ (wlc)->fifo2prec_map[(fifo)]))
+
+/* TX FIFO number to WME/802.1E Access Category */
+const u8 wme_fifo2ac[] = { AC_BK, AC_BE, AC_VI, AC_VO, AC_BE, AC_BE };
+
+/* WME/802.1E Access Category to TX FIFO number */
+static const u8 wme_ac2fifo[] = { 1, 0, 2, 3 };
+
+static bool in_send_q = false;
+
+/* Shared memory location index for various AC params */
+#define wme_shmemacindex(ac) wme_ac2fifo[ac]
+
+#ifdef BCMDBG
+static const char *fifo_names[] = {
+ "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" };
+const char *aci_names[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO" };
+#endif
+
+static const u8 acbitmap2maxprio[] = {
+ PRIO_8021D_BE, PRIO_8021D_BE, PRIO_8021D_BK, PRIO_8021D_BK,
+ PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI,
+ PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO,
+ PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO
+};
+
+/* currently the best mechanism for determining SIFS is the band in use */
+#define SIFS(band) ((band)->bandtype == WLC_BAND_5G ? APHY_SIFS_TIME : BPHY_SIFS_TIME);
+
+/* value for # replay counters currently supported */
+#define WLC_REPLAY_CNTRS_VALUE WPA_CAP_16_REPLAY_CNTRS
+
+/* local prototypes */
+extern void wlc_txq_enq(void *ctx, struct scb *scb, void *sdu, uint prec);
+static u16 BCMFASTPATH wlc_d11hdrs_mac80211(wlc_info_t *wlc,
+ struct ieee80211_hw *hw, void *p,
+ struct scb *scb, uint frag,
+ uint nfrags, uint queue,
+ uint next_frag_len,
+ wsec_key_t *key,
+ ratespec_t rspec_override);
+bool wlc_sendpkt_mac80211(wlc_info_t *wlc, void *sdu, struct ieee80211_hw *hw);
+void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg, bool suspend);
+static void wlc_bss_default_init(wlc_info_t *wlc);
+static void wlc_ucode_mac_upd(wlc_info_t *wlc);
+static ratespec_t mac80211_wlc_set_nrate(wlc_info_t *wlc, wlcband_t *cur_band,
+ u32 int_val);
+static void wlc_tx_prec_map_init(wlc_info_t *wlc);
+static void wlc_watchdog(void *arg);
+static void wlc_watchdog_by_timer(void *arg);
+static int wlc_set_rateset(wlc_info_t *wlc, wlc_rateset_t *rs_arg);
+static int wlc_iovar_rangecheck(wlc_info_t *wlc, u32 val,
+ const bcm_iovar_t *vi);
+static u8 wlc_local_constraint_qdbm(wlc_info_t *wlc);
+
+/* send and receive */
+static wlc_txq_info_t *wlc_txq_alloc(wlc_info_t *wlc, osl_t *osh);
+static void wlc_txq_free(wlc_info_t *wlc, osl_t *osh, wlc_txq_info_t *qi);
+static void wlc_txflowcontrol_signal(wlc_info_t *wlc, wlc_txq_info_t *qi,
+ bool on, int prio);
+static void wlc_txflowcontrol_reset(wlc_info_t *wlc);
+static u16 wlc_compute_airtime(wlc_info_t *wlc, ratespec_t rspec,
+ uint length);
+static void wlc_compute_cck_plcp(ratespec_t rate, uint length, u8 *plcp);
+static void wlc_compute_ofdm_plcp(ratespec_t rate, uint length, u8 *plcp);
+static void wlc_compute_mimo_plcp(ratespec_t rate, uint length, u8 *plcp);
+static u16 wlc_compute_frame_dur(wlc_info_t *wlc, ratespec_t rate,
+ u8 preamble_type, uint next_frag_len);
+static void wlc_recvctl(wlc_info_t *wlc, osl_t *osh, d11rxhdr_t *rxh,
+ void *p);
+static uint wlc_calc_frame_len(wlc_info_t *wlc, ratespec_t rate,
+ u8 preamble_type, uint dur);
+static uint wlc_calc_ack_time(wlc_info_t *wlc, ratespec_t rate,
+ u8 preamble_type);
+static uint wlc_calc_cts_time(wlc_info_t *wlc, ratespec_t rate,
+ u8 preamble_type);
+/* interrupt, up/down, band */
+static void wlc_setband(wlc_info_t *wlc, uint bandunit);
+static chanspec_t wlc_init_chanspec(wlc_info_t *wlc);
+static void wlc_bandinit_ordered(wlc_info_t *wlc, chanspec_t chanspec);
+static void wlc_bsinit(wlc_info_t *wlc);
+static int wlc_duty_cycle_set(wlc_info_t *wlc, int duty_cycle, bool isOFDM,
+ bool writeToShm);
+static void wlc_radio_hwdisable_upd(wlc_info_t *wlc);
+static bool wlc_radio_monitor_start(wlc_info_t *wlc);
+static void wlc_radio_timer(void *arg);
+static void wlc_radio_enable(wlc_info_t *wlc);
+static void wlc_radio_upd(wlc_info_t *wlc);
+
+/* scan, association, BSS */
+static uint wlc_calc_ba_time(wlc_info_t *wlc, ratespec_t rate,
+ u8 preamble_type);
+static void wlc_update_mimo_band_bwcap(wlc_info_t *wlc, u8 bwcap);
+static void wlc_ht_update_sgi_rx(wlc_info_t *wlc, int val);
+void wlc_ht_mimops_cap_update(wlc_info_t *wlc, u8 mimops_mode);
+static void wlc_ht_update_ldpc(wlc_info_t *wlc, s8 val);
+static void wlc_war16165(wlc_info_t *wlc, bool tx);
+
+static void wlc_process_eventq(void *arg);
+static void wlc_wme_retries_write(wlc_info_t *wlc);
+static bool wlc_attach_stf_ant_init(wlc_info_t *wlc);
+static uint wlc_attach_module(wlc_info_t *wlc);
+static void wlc_detach_module(wlc_info_t *wlc);
+static void wlc_timers_deinit(wlc_info_t *wlc);
+static void wlc_down_led_upd(wlc_info_t *wlc);
+static uint wlc_down_del_timer(wlc_info_t *wlc);
+static void wlc_ofdm_rateset_war(wlc_info_t *wlc);
+static int _wlc_ioctl(wlc_info_t *wlc, int cmd, void *arg, int len,
+ struct wlc_if *wlcif);
+
+#if defined(BCMDBG)
+void wlc_get_rcmta(wlc_info_t *wlc, int idx, struct ether_addr *addr)
+{
+ d11regs_t *regs = wlc->regs;
+ u32 v32;
+ osl_t *osh;
+
+ WL_TRACE(("wl%d: %s\n", WLCWLUNIT(wlc), __func__));
+
+ ASSERT(wlc->pub->corerev > 4);
+
+ osh = wlc->osh;
+
+ W_REG(osh, &regs->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
+ (void)R_REG(osh, &regs->objaddr);
+ v32 = R_REG(osh, &regs->objdata);
+ addr->octet[0] = (u8) v32;
+ addr->octet[1] = (u8) (v32 >> 8);
+ addr->octet[2] = (u8) (v32 >> 16);
+ addr->octet[3] = (u8) (v32 >> 24);
+ W_REG(osh, &regs->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
+ (void)R_REG(osh, &regs->objaddr);
+ v32 = R_REG(osh, (volatile u16 *)&regs->objdata);
+ addr->octet[4] = (u8) v32;
+ addr->octet[5] = (u8) (v32 >> 8);
+}
+#endif /* defined(BCMDBG) */
+
+/* keep the chip awake if needed */
+bool wlc_stay_awake(wlc_info_t *wlc)
+{
+ return true;
+}
+
+/* conditions under which the PM bit should be set in outgoing frames and STAY_AWAKE is meaningful
+ */
+bool wlc_ps_allowed(wlc_info_t *wlc)
+{
+ int idx;
+ wlc_bsscfg_t *cfg;
+
+ /* disallow PS when one of the following global conditions meets */
+ if (!wlc->pub->associated || !wlc->PMenabled || wlc->PM_override)
+ return false;
+
+ /* disallow PS when one of these meets when not scanning */
+ if (!wlc->PMblocked) {
+ if (AP_ACTIVE(wlc) || wlc->monitor)
+ return false;
+ }
+
+ FOREACH_AS_STA(wlc, idx, cfg) {
+ /* disallow PS when one of the following bsscfg specific conditions meets */
+ if (!cfg->BSS || !WLC_PORTOPEN(cfg))
+ return false;
+
+ if (!cfg->dtim_programmed)
+ return false;
+ }
+
+ return true;
+}
+
+void wlc_reset(wlc_info_t *wlc)
+{
+ WL_TRACE(("wl%d: wlc_reset\n", wlc->pub->unit));
+
+ wlc->check_for_unaligned_tbtt = false;
+
+ /* slurp up hw mac counters before core reset */
+ if (WLC_UPDATE_STATS(wlc)) {
+ wlc_statsupd(wlc);
+
+ /* reset our snapshot of macstat counters */
+ bzero((char *)wlc->core->macstat_snapshot, sizeof(macstat_t));
+ }
+
+ wlc_bmac_reset(wlc->hw);
+ wlc_ampdu_reset(wlc->ampdu);
+ wlc->txretried = 0;
+
+#ifdef WLC_HIGH_ONLY
+ /* Need to set a flag(to be cleared asynchronously by BMAC driver with high call)
+ * in order to prevent wlc_rpctx_txreclaim() from screwing wlc_rpctx_getnexttxp(),
+ * which could be invoked by already QUEUED high call(s) from BMAC driver before
+ * wlc_bmac_reset() finishes.
+ * It's not needed before in monolithic driver model because d11core interrupts would
+ * have been cleared instantly in wlc_bmac_reset() and no txstatus interrupt
+ * will come to driver to fetch those flushed dma pkt pointers.
+ */
+ wlc->reset_bmac_pending = true;
+
+ wlc_rpctx_txreclaim(wlc->rpctx);
+
+ wlc_stf_phy_txant_upd(wlc);
+ wlc_phy_ant_rxdiv_set(wlc->band->pi, wlc->stf->ant_rx_ovr);
+#endif
+}
+
+void wlc_fatal_error(wlc_info_t *wlc)
+{
+ WL_ERROR(("wl%d: fatal error, reinitializing\n", wlc->pub->unit));
+ wl_init(wlc->wl);
+}
+
+/* Return the channel the driver should initialize during wlc_init.
+ * the channel may have to be changed from the currently configured channel
+ * if other configurations are in conflict (bandlocked, 11n mode disabled,
+ * invalid channel for current country, etc.)
+ */
+static chanspec_t wlc_init_chanspec(wlc_info_t *wlc)
+{
+ chanspec_t chanspec =
+ 1 | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE |
+ WL_CHANSPEC_BAND_2G;
+
+ /* make sure the channel is on the supported band if we are band-restricted */
+ if (wlc->bandlocked || NBANDS(wlc) == 1) {
+ ASSERT(CHSPEC_WLCBANDUNIT(chanspec) == wlc->band->bandunit);
+ }
+ ASSERT(wlc_valid_chanspec_db(wlc->cmi, chanspec));
+ return chanspec;
+}
+
+struct scb global_scb;
+
+static void wlc_init_scb(wlc_info_t *wlc, struct scb *scb)
+{
+ int i;
+ scb->flags = SCB_WMECAP | SCB_HTCAP;
+ for (i = 0; i < NUMPRIO; i++)
+ scb->seqnum[i] = 0;
+}
+
+void wlc_init(wlc_info_t *wlc)
+{
+ d11regs_t *regs;
+ chanspec_t chanspec;
+ int i;
+ wlc_bsscfg_t *bsscfg;
+ bool mute = false;
+
+ WL_TRACE(("wl%d: wlc_init\n", wlc->pub->unit));
+
+ regs = wlc->regs;
+
+ /* This will happen if a big-hammer was executed. In that case, we want to go back
+ * to the channel that we were on and not new channel
+ */
+ if (wlc->pub->associated)
+ chanspec = wlc->home_chanspec;
+ else
+ chanspec = wlc_init_chanspec(wlc);
+
+ wlc_bmac_init(wlc->hw, chanspec, mute);
+
+ wlc->seckeys = wlc_bmac_read_shm(wlc->hw, M_SECRXKEYS_PTR) * 2;
+ if (D11REV_GE(wlc->pub->corerev, 15) && (wlc->machwcap & MCAP_TKIPMIC))
+ wlc->tkmickeys =
+ wlc_bmac_read_shm(wlc->hw, M_TKMICKEYS_PTR) * 2;
+
+ /* update beacon listen interval */
+ wlc_bcn_li_upd(wlc);
+ wlc->bcn_wait_prd =
+ (u8) (wlc_bmac_read_shm(wlc->hw, M_NOSLPZNATDTIM) >> 10);
+ ASSERT(wlc->bcn_wait_prd > 0);
+
+ /* the world is new again, so is our reported rate */
+ wlc_reprate_init(wlc);
+
+ /* write ethernet address to core */
+ FOREACH_BSS(wlc, i, bsscfg) {
+ wlc_set_mac(bsscfg);
+ wlc_set_bssid(bsscfg);
+ }
+
+ /* Update tsf_cfprep if associated and up */
+ if (wlc->pub->associated) {
+ FOREACH_BSS(wlc, i, bsscfg) {
+ if (bsscfg->up) {
+ u32 bi;
+
+ /* get beacon period from bsscfg and convert to uS */
+ bi = bsscfg->current_bss->beacon_period << 10;
+ /* update the tsf_cfprep register */
+ /* since init path would reset to default value */
+ W_REG(wlc->osh, &regs->tsf_cfprep,
+ (bi << CFPREP_CBI_SHIFT));
+
+ /* Update maccontrol PM related bits */
+ wlc_set_ps_ctrl(wlc);
+
+ break;
+ }
+ }
+ }
+
+ wlc_key_hw_init_all(wlc);
+
+ wlc_bandinit_ordered(wlc, chanspec);
+
+ wlc_init_scb(wlc, &global_scb);
+
+ /* init probe response timeout */
+ wlc_write_shm(wlc, M_PRS_MAXTIME, wlc->prb_resp_timeout);
+
+ /* init max burst txop (framebursting) */
+ wlc_write_shm(wlc, M_MBURST_TXOP,
+ (wlc->
+ _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP));
+
+ /* initialize maximum allowed duty cycle */
+ wlc_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true);
+ wlc_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true);
+
+ /* Update some shared memory locations related to max AMPDU size allowed to received */
+ wlc_ampdu_shm_upd(wlc->ampdu);
+
+ /* band-specific inits */
+ wlc_bsinit(wlc);
+
+ /* Enable EDCF mode (while the MAC is suspended) */
+ if (EDCF_ENAB(wlc->pub)) {
+ OR_REG(wlc->osh, &regs->ifs_ctl, IFS_USEEDCF);
+ wlc_edcf_setparams(wlc->cfg, false);
+ }
+
+ /* Init precedence maps for empty FIFOs */
+ wlc_tx_prec_map_init(wlc);
+
+ /* read the ucode version if we have not yet done so */
+ if (wlc->ucode_rev == 0) {
+ wlc->ucode_rev =
+ wlc_read_shm(wlc, M_BOM_REV_MAJOR) << NBITS(u16);
+ wlc->ucode_rev |= wlc_read_shm(wlc, M_BOM_REV_MINOR);
+ }
+
+ /* ..now really unleash hell (allow the MAC out of suspend) */
+ wlc_enable_mac(wlc);
+
+ /* clear tx flow control */
+ wlc_txflowcontrol_reset(wlc);
+
+ /* clear tx data fifo suspends */
+ wlc->tx_suspended = false;
+
+ /* enable the RF Disable Delay timer */
+ if (D11REV_GE(wlc->pub->corerev, 10))
+ W_REG(wlc->osh, &wlc->regs->rfdisabledly, RFDISABLE_DEFAULT);
+
+ /* initialize mpc delay */
+ wlc->mpc_delay_off = wlc->mpc_dlycnt = WLC_MPC_MIN_DELAYCNT;
+
+ /*
+ * Initialize WME parameters; if they haven't been set by some other
+ * mechanism (IOVar, etc) then read them from the hardware.
+ */
+ if (WLC_WME_RETRY_SHORT_GET(wlc, 0) == 0) { /* Unintialized; read from HW */
+ int ac;
+
+ ASSERT(wlc->clk);
+ for (ac = 0; ac < AC_COUNT; ac++) {
+ wlc->wme_retries[ac] =
+ wlc_read_shm(wlc, M_AC_TXLMT_ADDR(ac));
+ }
+ }
+}
+
+void wlc_mac_bcn_promisc_change(wlc_info_t *wlc, bool promisc)
+{
+ wlc->bcnmisc_monitor = promisc;
+ wlc_mac_bcn_promisc(wlc);
+}
+
+void wlc_mac_bcn_promisc(wlc_info_t *wlc)
+{
+ if ((AP_ENAB(wlc->pub) && (N_ENAB(wlc->pub) || wlc->band->gmode)) ||
+ wlc->bcnmisc_ibss || wlc->bcnmisc_scan || wlc->bcnmisc_monitor)
+ wlc_mctrl(wlc, MCTL_BCNS_PROMISC, MCTL_BCNS_PROMISC);
+ else
+ wlc_mctrl(wlc, MCTL_BCNS_PROMISC, 0);
+}
+
+/* set or clear maccontrol bits MCTL_PROMISC and MCTL_KEEPCONTROL */
+void wlc_mac_promisc(wlc_info_t *wlc)
+{
+ u32 promisc_bits = 0;
+
+ /* promiscuous mode just sets MCTL_PROMISC
+ * Note: APs get all BSS traffic without the need to set the MCTL_PROMISC bit
+ * since all BSS data traffic is directed at the AP
+ */
+ if (PROMISC_ENAB(wlc->pub) && !AP_ENAB(wlc->pub) && !wlc->wet)
+ promisc_bits |= MCTL_PROMISC;
+
+ /* monitor mode needs both MCTL_PROMISC and MCTL_KEEPCONTROL
+ * Note: monitor mode also needs MCTL_BCNS_PROMISC, but that is
+ * handled in wlc_mac_bcn_promisc()
+ */
+ if (MONITOR_ENAB(wlc))
+ promisc_bits |= MCTL_PROMISC | MCTL_KEEPCONTROL;
+
+ wlc_mctrl(wlc, MCTL_PROMISC | MCTL_KEEPCONTROL, promisc_bits);
+}
+
+/* check if hps and wake states of sw and hw are in sync */
+bool wlc_ps_check(wlc_info_t *wlc)
+{
+ bool res = true;
+ bool hps, wake;
+ bool wake_ok;
+
+ if (!AP_ACTIVE(wlc)) {
+ volatile u32 tmp;
+ tmp = R_REG(wlc->osh, &wlc->regs->maccontrol);
+
+ /* If deviceremoved is detected, then don't take any action as this can be called
+ * in any context. Assume that caller will take care of the condition. This is just
+ * to avoid assert
+ */
+ if (tmp == 0xffffffff) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit,
+ __func__));
+ return DEVICEREMOVED(wlc);
+ }
+
+ hps = PS_ALLOWED(wlc);
+
+ if (hps != ((tmp & MCTL_HPS) != 0)) {
+ int idx;
+ wlc_bsscfg_t *cfg;
+ WL_ERROR(("wl%d: hps not sync, sw %d, maccontrol 0x%x\n", wlc->pub->unit, hps, tmp));
+ FOREACH_BSS(wlc, idx, cfg) {
+ if (!BSSCFG_STA(cfg))
+ continue;
+ }
+
+ res = false;
+ }
+#ifdef WLC_LOW
+ /* For a monolithic build the wake check can be exact since it looks at wake
+ * override bits. The MCTL_WAKE bit should match the 'wake' value.
+ */
+ wake = STAY_AWAKE(wlc) || wlc->hw->wake_override;
+ wake_ok = (wake == ((tmp & MCTL_WAKE) != 0));
+#else
+ /* For a split build we will not have access to any wake overrides from the low
+ * level. The check can only make sure the MCTL_WAKE bit is on if the high
+ * level 'wake' value is true. If the high level 'wake' is false, the MCTL_WAKE
+ * may be either true or false due to the low level override.
+ */
+ wake = STAY_AWAKE(wlc);
+ wake_ok = (wake && ((tmp & MCTL_WAKE) != 0)) || !wake;
+#endif
+ if (hps && !wake_ok) {
+ WL_ERROR(("wl%d: wake not sync, sw %d maccontrol 0x%x\n", wlc->pub->unit, wake, tmp));
+ res = false;
+ }
+ }
+ ASSERT(res);
+ return res;
+}
+
+/* push sw hps and wake state through hardware */
+void wlc_set_ps_ctrl(wlc_info_t *wlc)
+{
+ u32 v1, v2;
+ bool hps, wake;
+ bool awake_before;
+
+ hps = PS_ALLOWED(wlc);
+ wake = hps ? (STAY_AWAKE(wlc)) : true;
+
+ WL_TRACE(("wl%d: wlc_set_ps_ctrl: hps %d wake %d\n", wlc->pub->unit,
+ hps, wake));
+
+ v1 = R_REG(wlc->osh, &wlc->regs->maccontrol);
+ v2 = 0;
+ if (hps)
+ v2 |= MCTL_HPS;
+ if (wake)
+ v2 |= MCTL_WAKE;
+
+ wlc_mctrl(wlc, MCTL_WAKE | MCTL_HPS, v2);
+
+ awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0));
+
+ if (wake && !awake_before)
+ wlc_bmac_wait_for_wake(wlc->hw);
+
+}
+
+/*
+ * Write this BSS config's MAC address to core.
+ * Updates RXE match engine.
+ */
+int wlc_set_mac(wlc_bsscfg_t *cfg)
+{
+ int err = 0;
+ wlc_info_t *wlc = cfg->wlc;
+
+ if (cfg == wlc->cfg) {
+ /* enter the MAC addr into the RXE match registers */
+ wlc_set_addrmatch(wlc, RCM_MAC_OFFSET, &cfg->cur_etheraddr);
+ }
+
+ wlc_ampdu_macaddr_upd(wlc);
+
+ return err;
+}
+
+/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl).
+ * Updates RXE match engine.
+ */
+void wlc_set_bssid(wlc_bsscfg_t *cfg)
+{
+ wlc_info_t *wlc = cfg->wlc;
+
+ /* if primary config, we need to update BSSID in RXE match registers */
+ if (cfg == wlc->cfg) {
+ wlc_set_addrmatch(wlc, RCM_BSSID_OFFSET, &cfg->BSSID);
+ }
+#ifdef SUPPORT_HWKEYS
+ else if (BSSCFG_STA(cfg) && cfg->BSS) {
+ wlc_rcmta_add_bssid(wlc, cfg);
+ }
+#endif
+}
+
+/*
+ * Suspend the the MAC and update the slot timing
+ * for standard 11b/g (20us slots) or shortslot 11g (9us slots).
+ */
+void wlc_switch_shortslot(wlc_info_t *wlc, bool shortslot)
+{
+ int idx;
+ wlc_bsscfg_t *cfg;
+
+ ASSERT(wlc->band->gmode);
+
+ /* use the override if it is set */
+ if (wlc->shortslot_override != WLC_SHORTSLOT_AUTO)
+ shortslot = (wlc->shortslot_override == WLC_SHORTSLOT_ON);
+
+ if (wlc->shortslot == shortslot)
+ return;
+
+ wlc->shortslot = shortslot;
+
+ /* update the capability based on current shortslot mode */
+ FOREACH_BSS(wlc, idx, cfg) {
+ if (!cfg->associated)
+ continue;
+ cfg->current_bss->capability &= ~DOT11_CAP_SHORTSLOT;
+ if (wlc->shortslot)
+ cfg->current_bss->capability |= DOT11_CAP_SHORTSLOT;
+ }
+
+ wlc_bmac_set_shortslot(wlc->hw, shortslot);
+}
+
+static u8 wlc_local_constraint_qdbm(wlc_info_t *wlc)
+{
+ u8 local;
+ s16 local_max;
+
+ local = WLC_TXPWR_MAX;
+ if (wlc->pub->associated &&
+ (wf_chspec_ctlchan(wlc->chanspec) ==
+ wf_chspec_ctlchan(wlc->home_chanspec))) {
+
+ /* get the local power constraint if we are on the AP's
+ * channel [802.11h, 7.3.2.13]
+ */
+ /* Clamp the value between 0 and WLC_TXPWR_MAX w/o overflowing the target */
+ local_max =
+ (wlc->txpwr_local_max -
+ wlc->txpwr_local_constraint) * WLC_TXPWR_DB_FACTOR;
+ if (local_max > 0 && local_max < WLC_TXPWR_MAX)
+ return (u8) local_max;
+ if (local_max < 0)
+ return 0;
+ }
+
+ return local;
+}
+
+/* propagate home chanspec to all bsscfgs in case bsscfg->current_bss->chanspec is referenced */
+void wlc_set_home_chanspec(wlc_info_t *wlc, chanspec_t chanspec)
+{
+ if (wlc->home_chanspec != chanspec) {
+ int idx;
+ wlc_bsscfg_t *cfg;
+
+ wlc->home_chanspec = chanspec;
+
+ FOREACH_BSS(wlc, idx, cfg) {
+ if (!cfg->associated)
+ continue;
+ cfg->target_bss->chanspec = chanspec;
+ cfg->current_bss->chanspec = chanspec;
+ }
+
+ }
+}
+
+static void wlc_set_phy_chanspec(wlc_info_t *wlc, chanspec_t chanspec)
+{
+ /* Save our copy of the chanspec */
+ wlc->chanspec = chanspec;
+
+ /* Set the chanspec and power limits for this locale after computing
+ * any 11h local tx power constraints.
+ */
+ wlc_channel_set_chanspec(wlc->cmi, chanspec,
+ wlc_local_constraint_qdbm(wlc));
+
+ if (wlc->stf->ss_algosel_auto)
+ wlc_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel,
+ chanspec);
+
+ wlc_stf_ss_update(wlc, wlc->band);
+
+}
+
+void wlc_set_chanspec(wlc_info_t *wlc, chanspec_t chanspec)
+{
+ uint bandunit;
+ bool switchband = false;
+ chanspec_t old_chanspec = wlc->chanspec;
+
+ if (!wlc_valid_chanspec_db(wlc->cmi, chanspec)) {
+ WL_ERROR(("wl%d: %s: Bad channel %d\n",
+ wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)));
+ ASSERT(wlc_valid_chanspec_db(wlc->cmi, chanspec));
+ return;
+ }
+
+ /* Switch bands if necessary */
+ if (NBANDS(wlc) > 1) {
+ bandunit = CHSPEC_WLCBANDUNIT(chanspec);
+ if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) {
+ switchband = true;
+ if (wlc->bandlocked) {
+ WL_ERROR(("wl%d: %s: chspec %d band is locked!\n", wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)));
+ return;
+ }
+ /* BMAC_NOTE: should the setband call come after the wlc_bmac_chanspec() ?
+ * if the setband updates (wlc_bsinit) use low level calls to inspect and
+ * set state, the state inspected may be from the wrong band, or the
+ * following wlc_bmac_set_chanspec() may undo the work.
+ */
+ wlc_setband(wlc, bandunit);
+ }
+ }
+
+ ASSERT(N_ENAB(wlc->pub) || !CHSPEC_IS40(chanspec));
+
+ /* sync up phy/radio chanspec */
+ wlc_set_phy_chanspec(wlc, chanspec);
+
+ /* init antenna selection */
+ if (CHSPEC_WLC_BW(old_chanspec) != CHSPEC_WLC_BW(chanspec)) {
+ if (WLANTSEL_ENAB(wlc))
+ wlc_antsel_init(wlc->asi);
+
+ /* Fix the hardware rateset based on bw.
+ * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz
+ */
+ wlc_rateset_bw_mcs_filter(&wlc->band->hw_rateset,
+ wlc->band->
+ mimo_cap_40 ? CHSPEC_WLC_BW(chanspec)
+ : 0);
+ }
+
+ /* update some mac configuration since chanspec changed */
+ wlc_ucode_mac_upd(wlc);
+}
+
+#if defined(BCMDBG)
+static int wlc_get_current_txpwr(wlc_info_t *wlc, void *pwr, uint len)
+{
+ txpwr_limits_t txpwr;
+ tx_power_t power;
+ tx_power_legacy_t *old_power = NULL;
+ int r, c;
+ uint qdbm;
+ bool override;
+
+ if (len == sizeof(tx_power_legacy_t))
+ old_power = (tx_power_legacy_t *) pwr;
+ else if (len < sizeof(tx_power_t))
+ return BCME_BUFTOOSHORT;
+
+ bzero(&power, sizeof(tx_power_t));
+
+ power.chanspec = WLC_BAND_PI_RADIO_CHANSPEC;
+ if (wlc->pub->associated)
+ power.local_chanspec = wlc->home_chanspec;
+
+ /* Return the user target tx power limits for the various rates. Note wlc_phy.c's
+ * public interface only implements getting and setting a single value for all of
+ * rates, so we need to fill the array ourselves.
+ */
+ wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override);
+ for (r = 0; r < WL_TX_POWER_RATES; r++) {
+ power.user_limit[r] = (u8) qdbm;
+ }
+
+ power.local_max = wlc->txpwr_local_max * WLC_TXPWR_DB_FACTOR;
+ power.local_constraint =
+ wlc->txpwr_local_constraint * WLC_TXPWR_DB_FACTOR;
+
+ power.antgain[0] = wlc->bandstate[BAND_2G_INDEX]->antgain;
+ power.antgain[1] = wlc->bandstate[BAND_5G_INDEX]->antgain;
+
+ wlc_channel_reg_limits(wlc->cmi, power.chanspec, &txpwr);
+
+#if WL_TX_POWER_CCK_NUM != WLC_NUM_RATES_CCK
+#error "WL_TX_POWER_CCK_NUM != WLC_NUM_RATES_CCK"
+#endif
+
+ /* CCK tx power limits */
+ for (c = 0, r = WL_TX_POWER_CCK_FIRST; c < WL_TX_POWER_CCK_NUM;
+ c++, r++)
+ power.reg_limit[r] = txpwr.cck[c];
+
+#if WL_TX_POWER_OFDM_NUM != WLC_NUM_RATES_OFDM
+#error "WL_TX_POWER_OFDM_NUM != WLC_NUM_RATES_OFDM"
+#endif
+
+ /* 20 MHz OFDM SISO tx power limits */
+ for (c = 0, r = WL_TX_POWER_OFDM_FIRST; c < WL_TX_POWER_OFDM_NUM;
+ c++, r++)
+ power.reg_limit[r] = txpwr.ofdm[c];
+
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+
+ /* 20 MHz OFDM CDD tx power limits */
+ for (c = 0, r = WL_TX_POWER_OFDM20_CDD_FIRST;
+ c < WL_TX_POWER_OFDM_NUM; c++, r++)
+ power.reg_limit[r] = txpwr.ofdm_cdd[c];
+
+ /* 40 MHz OFDM SISO tx power limits */
+ for (c = 0, r = WL_TX_POWER_OFDM40_SISO_FIRST;
+ c < WL_TX_POWER_OFDM_NUM; c++, r++)
+ power.reg_limit[r] = txpwr.ofdm_40_siso[c];
+
+ /* 40 MHz OFDM CDD tx power limits */
+ for (c = 0, r = WL_TX_POWER_OFDM40_CDD_FIRST;
+ c < WL_TX_POWER_OFDM_NUM; c++, r++)
+ power.reg_limit[r] = txpwr.ofdm_40_cdd[c];
+
+#if WL_TX_POWER_MCS_1_STREAM_NUM != WLC_NUM_RATES_MCS_1_STREAM
+#error "WL_TX_POWER_MCS_1_STREAM_NUM != WLC_NUM_RATES_MCS_1_STREAM"
+#endif
+
+ /* 20MHz MCS0-7 SISO tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS20_SISO_FIRST;
+ c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_20_siso[c];
+
+ /* 20MHz MCS0-7 CDD tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS20_CDD_FIRST;
+ c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_20_cdd[c];
+
+ /* 20MHz MCS0-7 STBC tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS20_STBC_FIRST;
+ c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_20_stbc[c];
+
+ /* 40MHz MCS0-7 SISO tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS40_SISO_FIRST;
+ c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_40_siso[c];
+
+ /* 40MHz MCS0-7 CDD tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS40_CDD_FIRST;
+ c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_40_cdd[c];
+
+ /* 40MHz MCS0-7 STBC tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS40_STBC_FIRST;
+ c < WLC_NUM_RATES_MCS_1_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_40_stbc[c];
+
+#if WL_TX_POWER_MCS_2_STREAM_NUM != WLC_NUM_RATES_MCS_2_STREAM
+#error "WL_TX_POWER_MCS_2_STREAM_NUM != WLC_NUM_RATES_MCS_2_STREAM"
+#endif
+
+ /* 20MHz MCS8-15 SDM tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS20_SDM_FIRST;
+ c < WLC_NUM_RATES_MCS_2_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_20_mimo[c];
+
+ /* 40MHz MCS8-15 SDM tx power limits */
+ for (c = 0, r = WL_TX_POWER_MCS40_SDM_FIRST;
+ c < WLC_NUM_RATES_MCS_2_STREAM; c++, r++)
+ power.reg_limit[r] = txpwr.mcs_40_mimo[c];
+
+ /* MCS 32 */
+ power.reg_limit[WL_TX_POWER_MCS_32] = txpwr.mcs32;
+ }
+
+ wlc_phy_txpower_get_current(wlc->band->pi, &power,
+ CHSPEC_CHANNEL(power.chanspec));
+
+ /* copy the tx_power_t struct to the return buffer,
+ * or convert to a tx_power_legacy_t struct
+ */
+ if (!old_power) {
+ bcopy(&power, pwr, sizeof(tx_power_t));
+ } else {
+ int band_idx = CHSPEC_IS2G(power.chanspec) ? 0 : 1;
+
+ bzero(old_power, sizeof(tx_power_legacy_t));
+
+ old_power->txpwr_local_max = power.local_max;
+ old_power->txpwr_local_constraint = power.local_constraint;
+ if (CHSPEC_IS2G(power.chanspec)) {
+ old_power->txpwr_chan_reg_max = txpwr.cck[0];
+ old_power->txpwr_est_Pout[band_idx] =
+ power.est_Pout_cck;
+ old_power->txpwr_est_Pout_gofdm = power.est_Pout[0];
+ } else {
+ old_power->txpwr_chan_reg_max = txpwr.ofdm[0];
+ old_power->txpwr_est_Pout[band_idx] = power.est_Pout[0];
+ }
+ old_power->txpwr_antgain[0] = power.antgain[0];
+ old_power->txpwr_antgain[1] = power.antgain[1];
+
+ for (r = 0; r < NUM_PWRCTRL_RATES; r++) {
+ old_power->txpwr_band_max[r] = power.user_limit[r];
+ old_power->txpwr_limit[r] = power.reg_limit[r];
+ old_power->txpwr_target[band_idx][r] = power.target[r];
+ if (CHSPEC_IS2G(power.chanspec))
+ old_power->txpwr_bphy_cck_max[r] =
+ power.board_limit[r];
+ else
+ old_power->txpwr_aphy_max[r] =
+ power.board_limit[r];
+ }
+ }
+
+ return 0;
+}
+#endif /* defined(BCMDBG) */
+
+static u32 wlc_watchdog_backup_bi(wlc_info_t *wlc)
+{
+ u32 bi;
+ bi = 2 * wlc->cfg->current_bss->dtim_period *
+ wlc->cfg->current_bss->beacon_period;
+ if (wlc->bcn_li_dtim)
+ bi *= wlc->bcn_li_dtim;
+ else if (wlc->bcn_li_bcn)
+ /* recalculate bi based on bcn_li_bcn */
+ bi = 2 * wlc->bcn_li_bcn * wlc->cfg->current_bss->beacon_period;
+
+ if (bi < 2 * TIMER_INTERVAL_WATCHDOG)
+ bi = 2 * TIMER_INTERVAL_WATCHDOG;
+ return bi;
+}
+
+/* Change to run the watchdog either from a periodic timer or from tbtt handler.
+ * Call watchdog from tbtt handler if tbtt is true, watchdog timer otherwise.
+ */
+void wlc_watchdog_upd(wlc_info_t *wlc, bool tbtt)
+{
+ /* make sure changing watchdog driver is allowed */
+ if (!wlc->pub->up || !wlc->pub->align_wd_tbtt)
+ return;
+ if (!tbtt && wlc->WDarmed) {
+ wl_del_timer(wlc->wl, wlc->wdtimer);
+ wlc->WDarmed = false;
+ }
+
+ /* stop watchdog timer and use tbtt interrupt to drive watchdog */
+ if (tbtt && wlc->WDarmed) {
+ wl_del_timer(wlc->wl, wlc->wdtimer);
+ wlc->WDarmed = false;
+ wlc->WDlast = OSL_SYSUPTIME();
+ }
+ /* arm watchdog timer and drive the watchdog there */
+ else if (!tbtt && !wlc->WDarmed) {
+ wl_add_timer(wlc->wl, wlc->wdtimer, TIMER_INTERVAL_WATCHDOG,
+ true);
+ wlc->WDarmed = true;
+ }
+ if (tbtt && !wlc->WDarmed) {
+ wl_add_timer(wlc->wl, wlc->wdtimer, wlc_watchdog_backup_bi(wlc),
+ true);
+ wlc->WDarmed = true;
+ }
+}
+
+ratespec_t wlc_lowest_basic_rspec(wlc_info_t *wlc, wlc_rateset_t *rs)
+{
+ ratespec_t lowest_basic_rspec;
+ uint i;
+
+ /* Use the lowest basic rate */
+ lowest_basic_rspec = rs->rates[0] & RATE_MASK;
+ for (i = 0; i < rs->count; i++) {
+ if (rs->rates[i] & WLC_RATE_FLAG) {
+ lowest_basic_rspec = rs->rates[i] & RATE_MASK;
+ break;
+ }
+ }
+#if NCONF
+ /* pick siso/cdd as default for OFDM (note no basic rate MCSs are supported yet) */
+ if (IS_OFDM(lowest_basic_rspec)) {
+ lowest_basic_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
+ }
+#endif
+
+ return lowest_basic_rspec;
+}
+
+/* This function changes the phytxctl for beacon based on current beacon ratespec AND txant
+ * setting as per this table:
+ * ratespec CCK ant = wlc->stf->txant
+ * OFDM ant = 3
+ */
+void wlc_beacon_phytxctl_txant_upd(wlc_info_t *wlc, ratespec_t bcn_rspec)
+{
+ u16 phyctl;
+ u16 phytxant = wlc->stf->phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
+
+ /* for non-siso rates or default setting, use the available chains */
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+ phytxant = wlc_stf_phytxchain_sel(wlc, bcn_rspec);
+ }
+
+ phyctl = wlc_read_shm(wlc, M_BCN_PCTLWD);
+ phyctl = (phyctl & ~mask) | phytxant;
+ wlc_write_shm(wlc, M_BCN_PCTLWD, phyctl);
+}
+
+/* centralized protection config change function to simplify debugging, no consistency checking
+ * this should be called only on changes to avoid overhead in periodic function
+*/
+void wlc_protection_upd(wlc_info_t *wlc, uint idx, int val)
+{
+ WL_TRACE(("wlc_protection_upd: idx %d, val %d\n", idx, val));
+
+ switch (idx) {
+ case WLC_PROT_G_SPEC:
+ wlc->protection->_g = (bool) val;
+ break;
+ case WLC_PROT_G_OVR:
+ wlc->protection->g_override = (s8) val;
+ break;
+ case WLC_PROT_G_USER:
+ wlc->protection->gmode_user = (u8) val;
+ break;
+ case WLC_PROT_OVERLAP:
+ wlc->protection->overlap = (s8) val;
+ break;
+ case WLC_PROT_N_USER:
+ wlc->protection->nmode_user = (s8) val;
+ break;
+ case WLC_PROT_N_CFG:
+ wlc->protection->n_cfg = (s8) val;
+ break;
+ case WLC_PROT_N_CFG_OVR:
+ wlc->protection->n_cfg_override = (s8) val;
+ break;
+ case WLC_PROT_N_NONGF:
+ wlc->protection->nongf = (bool) val;
+ break;
+ case WLC_PROT_N_NONGF_OVR:
+ wlc->protection->nongf_override = (s8) val;
+ break;
+ case WLC_PROT_N_PAM_OVR:
+ wlc->protection->n_pam_override = (s8) val;
+ break;
+ case WLC_PROT_N_OBSS:
+ wlc->protection->n_obss = (bool) val;
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+}
+
+static void wlc_ht_update_sgi_rx(wlc_info_t *wlc, int val)
+{
+ wlc->ht_cap.cap &= ~(HT_CAP_SHORT_GI_20 | HT_CAP_SHORT_GI_40);
+ wlc->ht_cap.cap |= (val & WLC_N_SGI_20) ? HT_CAP_SHORT_GI_20 : 0;
+ wlc->ht_cap.cap |= (val & WLC_N_SGI_40) ? HT_CAP_SHORT_GI_40 : 0;
+
+ if (wlc->pub->up) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ }
+}
+
+static void wlc_ht_update_ldpc(wlc_info_t *wlc, s8 val)
+{
+ wlc->stf->ldpc = val;
+
+ wlc->ht_cap.cap &= ~HT_CAP_LDPC_CODING;
+ if (wlc->stf->ldpc != OFF)
+ wlc->ht_cap.cap |= HT_CAP_LDPC_CODING;
+
+ if (wlc->pub->up) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false));
+ }
+}
+
+/*
+ * ucode, hwmac update
+ * Channel dependent updates for ucode and hw
+ */
+static void wlc_ucode_mac_upd(wlc_info_t *wlc)
+{
+ /* enable or disable any active IBSSs depending on whether or not
+ * we are on the home channel
+ */
+ if (wlc->home_chanspec == WLC_BAND_PI_RADIO_CHANSPEC) {
+ if (wlc->pub->associated) {
+ /* BMAC_NOTE: This is something that should be fixed in ucode inits.
+ * I think that the ucode inits set up the bcn templates and shm values
+ * with a bogus beacon. This should not be done in the inits. If ucode needs
+ * to set up a beacon for testing, the test routines should write it down,
+ * not expect the inits to populate a bogus beacon.
+ */
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+ wlc_write_shm(wlc, M_BCN_TXTSF_OFFSET,
+ wlc->band->bcntsfoff);
+ }
+ }
+ } else {
+ /* disable an active IBSS if we are not on the home channel */
+ }
+
+ /* update the various promisc bits */
+ wlc_mac_bcn_promisc(wlc);
+ wlc_mac_promisc(wlc);
+}
+
+static void wlc_bandinit_ordered(wlc_info_t *wlc, chanspec_t chanspec)
+{
+ wlc_rateset_t default_rateset;
+ uint parkband;
+ uint i, band_order[2];
+
+ WL_TRACE(("wl%d: wlc_bandinit_ordered\n", wlc->pub->unit));
+ /*
+ * We might have been bandlocked during down and the chip power-cycled (hibernate).
+ * figure out the right band to park on
+ */
+ if (wlc->bandlocked || NBANDS(wlc) == 1) {
+ ASSERT(CHSPEC_WLCBANDUNIT(chanspec) == wlc->band->bandunit);
+
+ parkband = wlc->band->bandunit; /* updated in wlc_bandlock() */
+ band_order[0] = band_order[1] = parkband;
+ } else {
+ /* park on the band of the specified chanspec */
+ parkband = CHSPEC_WLCBANDUNIT(chanspec);
+
+ /* order so that parkband initialize last */
+ band_order[0] = parkband ^ 1;
+ band_order[1] = parkband;
+ }
+
+ /* make each band operational, software state init */
+ for (i = 0; i < NBANDS(wlc); i++) {
+ uint j = band_order[i];
+
+ wlc->band = wlc->bandstate[j];
+
+ wlc_default_rateset(wlc, &default_rateset);
+
+ /* fill in hw_rate */
+ wlc_rateset_filter(&default_rateset, &wlc->band->hw_rateset,
+ false, WLC_RATES_CCK_OFDM, RATE_MASK,
+ (bool) N_ENAB(wlc->pub));
+
+ /* init basic rate lookup */
+ wlc_rate_lookup_init(wlc, &default_rateset);
+ }
+
+ /* sync up phy/radio chanspec */
+ wlc_set_phy_chanspec(wlc, chanspec);
+}
+
+/* band-specific init */
+static void WLBANDINITFN(wlc_bsinit) (wlc_info_t *wlc)
+{
+ WL_TRACE(("wl%d: wlc_bsinit: bandunit %d\n", wlc->pub->unit,
+ wlc->band->bandunit));
+
+ /* write ucode ACK/CTS rate table */
+ wlc_set_ratetable(wlc);
+
+ /* update some band specific mac configuration */
+ wlc_ucode_mac_upd(wlc);
+
+ /* init antenna selection */
+ if (WLANTSEL_ENAB(wlc))
+ wlc_antsel_init(wlc->asi);
+
+}
+
+/* switch to and initialize new band */
+static void WLBANDINITFN(wlc_setband) (wlc_info_t *wlc, uint bandunit)
+{
+ int idx;
+ wlc_bsscfg_t *cfg;
+
+ ASSERT(NBANDS(wlc) > 1);
+ ASSERT(!wlc->bandlocked);
+ ASSERT(bandunit != wlc->band->bandunit || wlc->bandinit_pending);
+
+ wlc->band = wlc->bandstate[bandunit];
+
+ if (!wlc->pub->up)
+ return;
+
+ /* wait for at least one beacon before entering sleeping state */
+ wlc->PMawakebcn = true;
+ FOREACH_AS_STA(wlc, idx, cfg)
+ cfg->PMawakebcn = true;
+ wlc_set_ps_ctrl(wlc);
+
+ /* band-specific initializations */
+ wlc_bsinit(wlc);
+}
+
+/* Initialize a WME Parameter Info Element with default STA parameters from WMM Spec, Table 12 */
+void wlc_wme_initparams_sta(wlc_info_t *wlc, wme_param_ie_t *pe)
+{
+ static const wme_param_ie_t stadef = {
+ WME_OUI,
+ WME_TYPE,
+ WME_SUBTYPE_PARAM_IE,
+ WME_VER,
+ 0,
+ 0,
+ {
+ {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA,
+ HTOL16(EDCF_AC_BE_TXOP_STA)},
+ {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA,
+ HTOL16(EDCF_AC_BK_TXOP_STA)},
+ {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA,
+ HTOL16(EDCF_AC_VI_TXOP_STA)},
+ {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA,
+ HTOL16(EDCF_AC_VO_TXOP_STA)}
+ }
+ };
+
+ ASSERT(sizeof(*pe) == WME_PARAM_IE_LEN);
+ memcpy(pe, &stadef, sizeof(*pe));
+}
+
+void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg, bool suspend)
+{
+ int i;
+ shm_acparams_t acp_shm;
+ u16 *shm_entry;
+ struct ieee80211_tx_queue_params *params = arg;
+
+ ASSERT(wlc);
+
+ /* Only apply params if the core is out of reset and has clocks */
+ if (!wlc->clk) {
+ WL_ERROR(("wl%d: %s : no-clock\n", wlc->pub->unit, __func__));
+ return;
+ }
+
+ /*
+ * AP uses AC params from wme_param_ie_ap.
+ * AP advertises AC params from wme_param_ie.
+ * STA uses AC params from wme_param_ie.
+ */
+
+ wlc->wme_admctl = 0;
+
+ do {
+ bzero((char *)&acp_shm, sizeof(shm_acparams_t));
+ /* find out which ac this set of params applies to */
+ ASSERT(aci < AC_COUNT);
+ /* set the admission control policy for this AC */
+ /* wlc->wme_admctl |= 1 << aci; *//* should be set ?? seems like off by default */
+
+ /* fill in shm ac params struct */
+ acp_shm.txop = ltoh16(params->txop);
+ /* convert from units of 32us to us for ucode */
+ wlc->edcf_txop[aci & 0x3] = acp_shm.txop =
+ EDCF_TXOP2USEC(acp_shm.txop);
+ acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK);
+
+ if (aci == AC_VI && acp_shm.txop == 0
+ && acp_shm.aifs < EDCF_AIFSN_MAX)
+ acp_shm.aifs++;
+
+ if (acp_shm.aifs < EDCF_AIFSN_MIN
+ || acp_shm.aifs > EDCF_AIFSN_MAX) {
+ WL_ERROR(("wl%d: wlc_edcf_setparams: bad aifs %d\n",
+ wlc->pub->unit, acp_shm.aifs));
+ continue;
+ }
+
+ acp_shm.cwmin = params->cw_min;
+ acp_shm.cwmax = params->cw_max;
+ acp_shm.cwcur = acp_shm.cwmin;
+ acp_shm.bslots =
+ R_REG(wlc->osh, &wlc->regs->tsf_random) & acp_shm.cwcur;
+ acp_shm.reggap = acp_shm.bslots + acp_shm.aifs;
+ /* Indicate the new params to the ucode */
+ acp_shm.status = wlc_read_shm(wlc, (M_EDCF_QINFO +
+ wme_shmemacindex(aci) *
+ M_EDCF_QLEN +
+ M_EDCF_STATUS_OFF));
+ acp_shm.status |= WME_STATUS_NEWAC;
+
+ /* Fill in shm acparam table */
+ shm_entry = (u16 *) &acp_shm;
+ for (i = 0; i < (int)sizeof(shm_acparams_t); i += 2)
+ wlc_write_shm(wlc,
+ M_EDCF_QINFO +
+ wme_shmemacindex(aci) * M_EDCF_QLEN + i,
+ *shm_entry++);
+
+ } while (0);
+
+ if (suspend)
+ wlc_suspend_mac_and_wait(wlc);
+
+ if (suspend)
+ wlc_enable_mac(wlc);
+
+}
+
+void wlc_edcf_setparams(wlc_bsscfg_t *cfg, bool suspend)
+{
+ wlc_info_t *wlc = cfg->wlc;
+ uint aci, i, j;
+ edcf_acparam_t *edcf_acp;
+ shm_acparams_t acp_shm;
+ u16 *shm_entry;
+
+ ASSERT(cfg);
+ ASSERT(wlc);
+
+ /* Only apply params if the core is out of reset and has clocks */
+ if (!wlc->clk)
+ return;
+
+ /*
+ * AP uses AC params from wme_param_ie_ap.
+ * AP advertises AC params from wme_param_ie.
+ * STA uses AC params from wme_param_ie.
+ */
+
+ edcf_acp = (edcf_acparam_t *) &wlc->wme_param_ie.acparam[0];
+
+ wlc->wme_admctl = 0;
+
+ for (i = 0; i < AC_COUNT; i++, edcf_acp++) {
+ bzero((char *)&acp_shm, sizeof(shm_acparams_t));
+ /* find out which ac this set of params applies to */
+ aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT;
+ ASSERT(aci < AC_COUNT);
+ /* set the admission control policy for this AC */
+ if (edcf_acp->ACI & EDCF_ACM_MASK) {
+ wlc->wme_admctl |= 1 << aci;
+ }
+
+ /* fill in shm ac params struct */
+ acp_shm.txop = ltoh16(edcf_acp->TXOP);
+ /* convert from units of 32us to us for ucode */
+ wlc->edcf_txop[aci] = acp_shm.txop =
+ EDCF_TXOP2USEC(acp_shm.txop);
+ acp_shm.aifs = (edcf_acp->ACI & EDCF_AIFSN_MASK);
+
+ if (aci == AC_VI && acp_shm.txop == 0
+ && acp_shm.aifs < EDCF_AIFSN_MAX)
+ acp_shm.aifs++;
+
+ if (acp_shm.aifs < EDCF_AIFSN_MIN
+ || acp_shm.aifs > EDCF_AIFSN_MAX) {
+ WL_ERROR(("wl%d: wlc_edcf_setparams: bad aifs %d\n",
+ wlc->pub->unit, acp_shm.aifs));
+ continue;
+ }
+
+ /* CWmin = 2^(ECWmin) - 1 */
+ acp_shm.cwmin = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK);
+ /* CWmax = 2^(ECWmax) - 1 */
+ acp_shm.cwmax = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK)
+ >> EDCF_ECWMAX_SHIFT);
+ acp_shm.cwcur = acp_shm.cwmin;
+ acp_shm.bslots =
+ R_REG(wlc->osh, &wlc->regs->tsf_random) & acp_shm.cwcur;
+ acp_shm.reggap = acp_shm.bslots + acp_shm.aifs;
+ /* Indicate the new params to the ucode */
+ acp_shm.status = wlc_read_shm(wlc, (M_EDCF_QINFO +
+ wme_shmemacindex(aci) *
+ M_EDCF_QLEN +
+ M_EDCF_STATUS_OFF));
+ acp_shm.status |= WME_STATUS_NEWAC;
+
+ /* Fill in shm acparam table */
+ shm_entry = (u16 *) &acp_shm;
+ for (j = 0; j < (int)sizeof(shm_acparams_t); j += 2)
+ wlc_write_shm(wlc,
+ M_EDCF_QINFO +
+ wme_shmemacindex(aci) * M_EDCF_QLEN + j,
+ *shm_entry++);
+ }
+
+ if (suspend)
+ wlc_suspend_mac_and_wait(wlc);
+
+ if (AP_ENAB(wlc->pub) && WME_ENAB(wlc->pub)) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, false);
+ }
+
+ if (suspend)
+ wlc_enable_mac(wlc);
+
+}
+
+bool wlc_timers_init(wlc_info_t *wlc, int unit)
+{
+ wlc->wdtimer = wl_init_timer(wlc->wl, wlc_watchdog_by_timer,
+ wlc, "watchdog");
+ if (!wlc->wdtimer) {
+ WL_ERROR(("wl%d: wl_init_timer for wdtimer failed\n", unit));
+ goto fail;
+ }
+
+ wlc->radio_timer = wl_init_timer(wlc->wl, wlc_radio_timer,
+ wlc, "radio");
+ if (!wlc->radio_timer) {
+ WL_ERROR(("wl%d: wl_init_timer for radio_timer failed\n",
+ unit));
+ goto fail;
+ }
+
+ return true;
+
+ fail:
+ return false;
+}
+
+/*
+ * Initialize wlc_info default values ...
+ * may get overrides later in this function
+ */
+void wlc_info_init(wlc_info_t *wlc, int unit)
+{
+ int i;
+ /* Assume the device is there until proven otherwise */
+ wlc->device_present = true;
+
+ /* set default power output percentage to 100 percent */
+ wlc->txpwr_percent = 100;
+
+ /* Save our copy of the chanspec */
+ wlc->chanspec = CH20MHZ_CHSPEC(1);
+
+ /* initialize CCK preamble mode to unassociated state */
+ wlc->shortpreamble = false;
+
+ wlc->legacy_probe = true;
+
+ /* various 802.11g modes */
+ wlc->shortslot = false;
+ wlc->shortslot_override = WLC_SHORTSLOT_AUTO;
+
+ wlc->barker_overlap_control = true;
+ wlc->barker_preamble = WLC_BARKER_SHORT_ALLOWED;
+ wlc->txburst_limit_override = AUTO;
+
+ wlc_protection_upd(wlc, WLC_PROT_G_OVR, WLC_PROTECTION_AUTO);
+ wlc_protection_upd(wlc, WLC_PROT_G_SPEC, false);
+
+ wlc_protection_upd(wlc, WLC_PROT_N_CFG_OVR, WLC_PROTECTION_AUTO);
+ wlc_protection_upd(wlc, WLC_PROT_N_CFG, WLC_N_PROTECTION_OFF);
+ wlc_protection_upd(wlc, WLC_PROT_N_NONGF_OVR, WLC_PROTECTION_AUTO);
+ wlc_protection_upd(wlc, WLC_PROT_N_NONGF, false);
+ wlc_protection_upd(wlc, WLC_PROT_N_PAM_OVR, AUTO);
+
+ wlc_protection_upd(wlc, WLC_PROT_OVERLAP, WLC_PROTECTION_CTL_OVERLAP);
+
+ /* 802.11g draft 4.0 NonERP elt advertisement */
+ wlc->include_legacy_erp = true;
+
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF;
+ wlc->stf->txant = ANT_TX_DEF;
+
+ wlc->prb_resp_timeout = WLC_PRB_RESP_TIMEOUT;
+
+ wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN;
+ for (i = 0; i < NFIFO; i++)
+ wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN;
+ wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN;
+
+ /* default rate fallback retry limits */
+ wlc->SFBL = RETRY_SHORT_FB;
+ wlc->LFBL = RETRY_LONG_FB;
+
+ /* default mac retry limits */
+ wlc->SRL = RETRY_SHORT_DEF;
+ wlc->LRL = RETRY_LONG_DEF;
+
+ /* init PM state */
+ wlc->PM = PM_OFF; /* User's setting of PM mode through IOCTL */
+ wlc->PM_override = false; /* Prevents from going to PM if our AP is 'ill' */
+ wlc->PMenabled = false; /* Current PM state */
+ wlc->PMpending = false; /* Tracks whether STA indicated PM in the last attempt */
+ wlc->PMblocked = false; /* To allow blocking going into PM during RM and scans */
+
+ /* In WMM Auto mode, PM is allowed if association is a UAPSD association */
+ wlc->WME_PM_blocked = false;
+
+ /* Init wme queuing method */
+ wlc->wme_prec_queuing = false;
+
+ /* Overrides for the core to stay awake under zillion conditions Look for STAY_AWAKE */
+ wlc->wake = false;
+ /* Are we waiting for a response to PS-Poll that we sent */
+ wlc->PSpoll = false;
+
+ /* APSD defaults */
+ wlc->wme_apsd = true;
+ wlc->apsd_sta_usp = false;
+ wlc->apsd_trigger_timeout = 0; /* disable the trigger timer */
+ wlc->apsd_trigger_ac = AC_BITMAP_ALL;
+
+ /* Set flag to indicate that hw keys should be used when available. */
+ wlc->wsec_swkeys = false;
+
+ /* init the 4 static WEP default keys */
+ for (i = 0; i < WSEC_MAX_DEFAULT_KEYS; i++) {
+ wlc->wsec_keys[i] = wlc->wsec_def_keys[i];
+ wlc->wsec_keys[i]->idx = (u8) i;
+ }
+
+ wlc->_regulatory_domain = false; /* 802.11d */
+
+ /* WME QoS mode is Auto by default */
+ wlc->pub->_wme = AUTO;
+
+#ifdef BCMSDIODEV_ENABLED
+ wlc->pub->_priofc = true; /* enable priority flow control for sdio dongle */
+#endif
+
+ wlc->pub->_ampdu = AMPDU_AGG_HOST;
+ wlc->pub->bcmerror = 0;
+ wlc->ibss_allowed = true;
+ wlc->ibss_coalesce_allowed = true;
+ wlc->pub->_coex = ON;
+
+ /* intialize mpc delay */
+ wlc->mpc_delay_off = wlc->mpc_dlycnt = WLC_MPC_MIN_DELAYCNT;
+
+ wlc->pr80838_war = true;
+}
+
+static bool wlc_state_bmac_sync(wlc_info_t *wlc)
+{
+ wlc_bmac_state_t state_bmac;
+
+ if (wlc_bmac_state_get(wlc->hw, &state_bmac) != 0)
+ return false;
+
+ wlc->machwcap = state_bmac.machwcap;
+ wlc_protection_upd(wlc, WLC_PROT_N_PAM_OVR,
+ (s8) state_bmac.preamble_ovr);
+
+ return true;
+}
+
+static uint wlc_attach_module(wlc_info_t *wlc)
+{
+ uint err = 0;
+ uint unit;
+ unit = wlc->pub->unit;
+
+ wlc->asi = wlc_antsel_attach(wlc, wlc->osh, wlc->pub, wlc->hw);
+ if (wlc->asi == NULL) {
+ WL_ERROR(("wl%d: wlc_attach: wlc_antsel_attach failed\n",
+ unit));
+ err = 44;
+ goto fail;
+ }
+
+ wlc->ampdu = wlc_ampdu_attach(wlc);
+ if (wlc->ampdu == NULL) {
+ WL_ERROR(("wl%d: wlc_attach: wlc_ampdu_attach failed\n", unit));
+ err = 50;
+ goto fail;
+ }
+
+ /* Initialize event queue; needed before following calls */
+ wlc->eventq =
+ wlc_eventq_attach(wlc->pub, wlc, wlc->wl, wlc_process_eventq);
+ if (wlc->eventq == NULL) {
+ WL_ERROR(("wl%d: wlc_attach: wlc_eventq_attachfailed\n", unit));
+ err = 57;
+ goto fail;
+ }
+
+ if ((wlc_stf_attach(wlc) != 0)) {
+ WL_ERROR(("wl%d: wlc_attach: wlc_stf_attach failed\n", unit));
+ err = 68;
+ goto fail;
+ }
+ fail:
+ return err;
+}
+
+wlc_pub_t *wlc_pub(void *wlc)
+{
+ return ((wlc_info_t *) wlc)->pub;
+}
+
+#define CHIP_SUPPORTS_11N(wlc) 1
+
+/*
+ * The common driver entry routine. Error codes should be unique
+ */
+void *wlc_attach(void *wl, u16 vendor, u16 device, uint unit, bool piomode,
+ osl_t *osh, void *regsva, uint bustype, void *btparam,
+ uint *perr)
+{
+ wlc_info_t *wlc;
+ uint err = 0;
+ uint j;
+ wlc_pub_t *pub;
+ wlc_txq_info_t *qi;
+ uint n_disabled;
+
+ WL_NONE(("wl%d: %s: vendor 0x%x device 0x%x\n", unit, __func__, vendor,
+ device));
+
+ ASSERT(WSEC_MAX_RCMTA_KEYS <= WSEC_MAX_KEYS);
+ ASSERT(WSEC_MAX_DEFAULT_KEYS == WLC_DEFAULT_KEYS);
+
+ /* some code depends on packed structures */
+ ASSERT(sizeof(struct ether_addr) == ETHER_ADDR_LEN);
+ ASSERT(sizeof(struct ether_header) == ETHER_HDR_LEN);
+ ASSERT(sizeof(d11regs_t) == SI_CORE_SIZE);
+ ASSERT(sizeof(ofdm_phy_hdr_t) == D11_PHY_HDR_LEN);
+ ASSERT(sizeof(cck_phy_hdr_t) == D11_PHY_HDR_LEN);
+ ASSERT(sizeof(d11txh_t) == D11_TXH_LEN);
+ ASSERT(sizeof(d11rxhdr_t) == RXHDR_LEN);
+ ASSERT(sizeof(struct dot11_header) == DOT11_A4_HDR_LEN);
+ ASSERT(sizeof(struct dot11_rts_frame) == DOT11_RTS_LEN);
+ ASSERT(sizeof(struct dot11_management_header) == DOT11_MGMT_HDR_LEN);
+ ASSERT(sizeof(struct dot11_bcn_prb) == DOT11_BCN_PRB_LEN);
+ ASSERT(sizeof(tx_status_t) == TXSTATUS_LEN);
+ ASSERT(sizeof(ht_cap_ie_t) == HT_CAP_IE_LEN);
+ ASSERT(offsetof(wl_scan_params_t, channel_list) ==
+ WL_SCAN_PARAMS_FIXED_SIZE);
+ ASSERT(IS_ALIGNED(offsetof(wsec_key_t, data), sizeof(u32)));
+ ASSERT(ISPOWEROF2(MA_WINDOW_SZ));
+
+ ASSERT(sizeof(wlc_d11rxhdr_t) <= WL_HWRXOFF);
+
+ /*
+ * Number of replay counters value used in WPA IE must match # rxivs
+ * supported in wsec_key_t struct. See 802.11i/D3.0 sect. 7.3.2.17
+ * 'RSN Information Element' figure 8 for this mapping.
+ */
+ ASSERT((WPA_CAP_16_REPLAY_CNTRS == WLC_REPLAY_CNTRS_VALUE
+ && 16 == WLC_NUMRXIVS)
+ || (WPA_CAP_4_REPLAY_CNTRS == WLC_REPLAY_CNTRS_VALUE
+ && 4 == WLC_NUMRXIVS));
+
+ /* allocate wlc_info_t state and its substructures */
+ wlc = (wlc_info_t *) wlc_attach_malloc(osh, unit, &err, device);
+ if (wlc == NULL)
+ goto fail;
+ wlc->osh = osh;
+ pub = wlc->pub;
+
+#if defined(BCMDBG)
+ wlc_info_dbg = wlc;
+#endif
+
+ wlc->band = wlc->bandstate[0];
+ wlc->core = wlc->corestate;
+ wlc->wl = wl;
+ pub->unit = unit;
+ pub->osh = osh;
+ wlc->btparam = btparam;
+ pub->_piomode = piomode;
+ wlc->bandinit_pending = false;
+ /* By default restrict TKIP associations from 11n STA's */
+ wlc->ht_wsec_restriction = WLC_HT_TKIP_RESTRICT;
+
+ /* populate wlc_info_t with default values */
+ wlc_info_init(wlc, unit);
+
+ /* update sta/ap related parameters */
+ wlc_ap_upd(wlc);
+
+ /* 11n_disable nvram */
+ n_disabled = getintvar(pub->vars, "11n_disable");
+
+ /* register a module (to handle iovars) */
+ wlc_module_register(wlc->pub, wlc_iovars, "wlc_iovars", wlc,
+ wlc_doiovar, NULL, NULL);
+
+ /* low level attach steps(all hw accesses go inside, no more in rest of the attach) */
+ err = wlc_bmac_attach(wlc, vendor, device, unit, piomode, osh, regsva,
+ bustype, btparam);
+ if (err)
+ goto fail;
+
+ /* for some states, due to different info pointer(e,g, wlc, wlc_hw) or master/slave split,
+ * HIGH driver(both monolithic and HIGH_ONLY) needs to sync states FROM BMAC portion driver
+ */
+ if (!wlc_state_bmac_sync(wlc)) {
+ err = 20;
+ goto fail;
+ }
+
+ pub->phy_11ncapable = WLC_PHY_11N_CAP(wlc->band);
+
+ /* propagate *vars* from BMAC driver to high driver */
+ wlc_bmac_copyfrom_vars(wlc->hw, &pub->vars, &wlc->vars_size);
+
+#ifdef WLC_HIGH_ONLY
+ WL_TRACE(("nvram : vars %p , vars_size %d\n", pub->vars,
+ wlc->vars_size));
+#endif
+
+ /* set maximum allowed duty cycle */
+ wlc->tx_duty_cycle_ofdm =
+ (u16) getintvar(pub->vars, "tx_duty_cycle_ofdm");
+ wlc->tx_duty_cycle_cck =
+ (u16) getintvar(pub->vars, "tx_duty_cycle_cck");
+
+ wlc_stf_phy_chain_calc(wlc);
+
+ /* txchain 1: txant 0, txchain 2: txant 1 */
+ if (WLCISNPHY(wlc->band) && (wlc->stf->txstreams == 1))
+ wlc->stf->txant = wlc->stf->hw_txchain - 1;
+
+ /* push to BMAC driver */
+ wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain,
+ wlc->stf->hw_rxchain);
+
+#ifdef WLC_LOW
+ /* pull up some info resulting from the low attach */
+ {
+ int i;
+ for (i = 0; i < NFIFO; i++)
+ wlc->core->txavail[i] = wlc->hw->txavail[i];
+ }
+#endif /* WLC_LOW */
+
+ wlc_bmac_hw_etheraddr(wlc->hw, &wlc->perm_etheraddr);
+
+ bcopy((char *)&wlc->perm_etheraddr, (char *)&pub->cur_etheraddr,
+ ETHER_ADDR_LEN);
+
+ for (j = 0; j < NBANDS(wlc); j++) {
+ /* Use band 1 for single band 11a */
+ if (IS_SINGLEBAND_5G(wlc->deviceid))
+ j = BAND_5G_INDEX;
+
+ wlc->band = wlc->bandstate[j];
+
+ if (!wlc_attach_stf_ant_init(wlc)) {
+ err = 24;
+ goto fail;
+ }
+
+ /* default contention windows size limits */
+ wlc->band->CWmin = APHY_CWMIN;
+ wlc->band->CWmax = PHY_CWMAX;
+
+ /* init gmode value */
+ if (BAND_2G(wlc->band->bandtype)) {
+ wlc->band->gmode = GMODE_AUTO;
+ wlc_protection_upd(wlc, WLC_PROT_G_USER,
+ wlc->band->gmode);
+ }
+
+ /* init _n_enab supported mode */
+ if (WLC_PHY_11N_CAP(wlc->band) && CHIP_SUPPORTS_11N(wlc)) {
+ if (n_disabled & WLFEATURE_DISABLE_11N) {
+ pub->_n_enab = OFF;
+ wlc_protection_upd(wlc, WLC_PROT_N_USER, OFF);
+ } else {
+ pub->_n_enab = SUPPORT_11N;
+ wlc_protection_upd(wlc, WLC_PROT_N_USER,
+ ((pub->_n_enab ==
+ SUPPORT_11N) ? WL_11N_2x2 :
+ WL_11N_3x3));
+ }
+ }
+
+ /* init per-band default rateset, depend on band->gmode */
+ wlc_default_rateset(wlc, &wlc->band->defrateset);
+
+ /* fill in hw_rateset (used early by WLC_SET_RATESET) */
+ wlc_rateset_filter(&wlc->band->defrateset,
+ &wlc->band->hw_rateset, false,
+ WLC_RATES_CCK_OFDM, RATE_MASK,
+ (bool) N_ENAB(wlc->pub));
+ }
+
+ /* update antenna config due to wlc->stf->txant/txchain/ant_rx_ovr change */
+ wlc_stf_phy_txant_upd(wlc);
+
+ /* attach each modules */
+ err = wlc_attach_module(wlc);
+ if (err != 0)
+ goto fail;
+
+ if (!wlc_timers_init(wlc, unit)) {
+ WL_ERROR(("wl%d: %s: wlc_init_timer failed\n", unit, __func__));
+ err = 32;
+ goto fail;
+ }
+
+ /* depend on rateset, gmode */
+ wlc->cmi = wlc_channel_mgr_attach(wlc);
+ if (!wlc->cmi) {
+ WL_ERROR(("wl%d: %s: wlc_channel_mgr_attach failed\n", unit,
+ __func__));
+ err = 33;
+ goto fail;
+ }
+
+ /* init default when all parameters are ready, i.e. ->rateset */
+ wlc_bss_default_init(wlc);
+
+ /*
+ * Complete the wlc default state initializations..
+ */
+
+ /* allocate our initial queue */
+ qi = wlc_txq_alloc(wlc, osh);
+ if (qi == NULL) {
+ WL_ERROR(("wl%d: %s: failed to malloc tx queue\n", unit,
+ __func__));
+ err = 100;
+ goto fail;
+ }
+ wlc->active_queue = qi;
+
+ wlc->bsscfg[0] = wlc->cfg;
+ wlc->cfg->_idx = 0;
+ wlc->cfg->wlc = wlc;
+ pub->txmaxpkts = MAXTXPKTS;
+
+ WLCNTSET(pub->_cnt->version, WL_CNT_T_VERSION);
+ WLCNTSET(pub->_cnt->length, sizeof(wl_cnt_t));
+
+ WLCNTSET(pub->_wme_cnt->version, WL_WME_CNT_VERSION);
+ WLCNTSET(pub->_wme_cnt->length, sizeof(wl_wme_cnt_t));
+
+ wlc_wme_initparams_sta(wlc, &wlc->wme_param_ie);
+
+ wlc->mimoft = FT_HT;
+ wlc->ht_cap.cap = HT_CAP;
+ if (HT_ENAB(wlc->pub))
+ wlc->stf->ldpc = AUTO;
+
+ wlc->mimo_40txbw = AUTO;
+ wlc->ofdm_40txbw = AUTO;
+ wlc->cck_40txbw = AUTO;
+ wlc_update_mimo_band_bwcap(wlc, WLC_N_BW_20IN2G_40IN5G);
+
+ /* Enable setting the RIFS Mode bit by default in HT Info IE */
+ wlc->rifs_advert = AUTO;
+
+ /* Set default values of SGI */
+ if (WLC_SGI_CAP_PHY(wlc)) {
+ wlc_ht_update_sgi_rx(wlc, (WLC_N_SGI_20 | WLC_N_SGI_40));
+ wlc->sgi_tx = AUTO;
+ } else if (WLCISSSLPNPHY(wlc->band)) {
+ wlc_ht_update_sgi_rx(wlc, (WLC_N_SGI_20 | WLC_N_SGI_40));
+ wlc->sgi_tx = AUTO;
+ } else {
+ wlc_ht_update_sgi_rx(wlc, 0);
+ wlc->sgi_tx = OFF;
+ }
+
+ /* *******nvram 11n config overrides Start ********* */
+
+ /* apply the sgi override from nvram conf */
+ if (n_disabled & WLFEATURE_DISABLE_11N_SGI_TX)
+ wlc->sgi_tx = OFF;
+
+ if (n_disabled & WLFEATURE_DISABLE_11N_SGI_RX)
+ wlc_ht_update_sgi_rx(wlc, 0);
+
+ /* apply the stbc override from nvram conf */
+ if (n_disabled & WLFEATURE_DISABLE_11N_STBC_TX) {
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF;
+ wlc->ht_cap.cap &= ~HT_CAP_TX_STBC;
+ }
+ if (n_disabled & WLFEATURE_DISABLE_11N_STBC_RX)
+ wlc_stf_stbc_rx_set(wlc, HT_CAP_RX_STBC_NO);
+
+ /* apply the GF override from nvram conf */
+ if (n_disabled & WLFEATURE_DISABLE_11N_GF)
+ wlc->ht_cap.cap &= ~HT_CAP_GF;
+
+ /* initialize radio_mpc_disable according to wlc->mpc */
+ wlc_radio_mpc_upd(wlc);
+
+ if (WLANTSEL_ENAB(wlc)) {
+ if ((CHIPID(wlc->pub->sih->chip)) == BCM43235_CHIP_ID) {
+ if ((getintvar(wlc->pub->vars, "aa2g") == 7) ||
+ (getintvar(wlc->pub->vars, "aa5g") == 7)) {
+ wlc_bmac_antsel_set(wlc->hw, 1);
+ }
+ } else {
+ wlc_bmac_antsel_set(wlc->hw, wlc->asi->antsel_avail);
+ }
+ }
+
+ if (perr)
+ *perr = 0;
+
+ return (void *)wlc;
+
+ fail:
+ WL_ERROR(("wl%d: %s: failed with err %d\n", unit, __func__, err));
+ if (wlc)
+ wlc_detach(wlc);
+
+ if (perr)
+ *perr = err;
+ return NULL;
+}
+
+static void wlc_attach_antgain_init(wlc_info_t *wlc)
+{
+ uint unit;
+ unit = wlc->pub->unit;
+
+ if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) {
+ /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */
+ wlc->band->antgain = 8;
+ } else if (wlc->band->antgain == -1) {
+ WL_ERROR(("wl%d: %s: Invalid antennas available in srom, using 2dB\n", unit, __func__));
+ wlc->band->antgain = 8;
+ } else {
+ s8 gain, fract;
+ /* Older sroms specified gain in whole dbm only. In order
+ * be able to specify qdbm granularity and remain backward compatible
+ * the whole dbms are now encoded in only low 6 bits and remaining qdbms
+ * are encoded in the hi 2 bits. 6 bit signed number ranges from
+ * -32 - 31. Examples: 0x1 = 1 db,
+ * 0xc1 = 1.75 db (1 + 3 quarters),
+ * 0x3f = -1 (-1 + 0 quarters),
+ * 0x7f = -.75 (-1 in low 6 bits + 1 quarters in hi 2 bits) = -3 qdbm.
+ * 0xbf = -.50 (-1 in low 6 bits + 2 quarters in hi 2 bits) = -2 qdbm.
+ */
+ gain = wlc->band->antgain & 0x3f;
+ gain <<= 2; /* Sign extend */
+ gain >>= 2;
+ fract = (wlc->band->antgain & 0xc0) >> 6;
+ wlc->band->antgain = 4 * gain + fract;
+ }
+}
+
+static bool wlc_attach_stf_ant_init(wlc_info_t *wlc)
+{
+ int aa;
+ uint unit;
+ char *vars;
+ int bandtype;
+
+ unit = wlc->pub->unit;
+ vars = wlc->pub->vars;
+ bandtype = wlc->band->bandtype;
+
+ /* get antennas available */
+ aa = (s8) getintvar(vars, (BAND_5G(bandtype) ? "aa5g" : "aa2g"));
+ if (aa == 0)
+ aa = (s8) getintvar(vars,
+ (BAND_5G(bandtype) ? "aa1" : "aa0"));
+ if ((aa < 1) || (aa > 15)) {
+ WL_ERROR(("wl%d: %s: Invalid antennas available in srom (0x%x), using 3.\n", unit, __func__, aa));
+ aa = 3;
+ }
+
+ /* reset the defaults if we have a single antenna */
+ if (aa == 1) {
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0;
+ wlc->stf->txant = ANT_TX_FORCE_0;
+ } else if (aa == 2) {
+ wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1;
+ wlc->stf->txant = ANT_TX_FORCE_1;
+ } else {
+ }
+
+ /* Compute Antenna Gain */
+ wlc->band->antgain =
+ (s8) getintvar(vars, (BAND_5G(bandtype) ? "ag1" : "ag0"));
+ wlc_attach_antgain_init(wlc);
+
+ return true;
+}
+
+#ifdef WLC_HIGH_ONLY
+/* HIGH_ONLY bmac_attach, which sync over LOW_ONLY bmac_attach states */
+int wlc_bmac_attach(wlc_info_t *wlc, u16 vendor, u16 device, uint unit,
+ bool piomode, osl_t *osh, void *regsva, uint bustype,
+ void *btparam)
+{
+ wlc_bmac_revinfo_t revinfo;
+ uint idx = 0;
+ rpc_info_t *rpc = (rpc_info_t *) btparam;
+
+ ASSERT(bustype == RPC_BUS);
+
+ /* install the rpc handle in the various state structures used by stub RPC functions */
+ wlc->rpc = rpc;
+ wlc->hw->rpc = rpc;
+ wlc->hw->osh = osh;
+
+ wlc->regs = 0;
+
+ wlc->rpctx = wlc_rpctx_attach(wlc->pub, wlc);
+ if (wlc->rpctx == NULL)
+ return -1;
+
+ /*
+ * FIFO 0
+ * TX: TX_AC_BK_FIFO (TX AC Background data packets)
+ */
+ /* Always initialized */
+ ASSERT(NRPCTXBUFPOST <= NTXD);
+ wlc_rpctx_fifoinit(wlc->rpctx, TX_DATA_FIFO, NRPCTXBUFPOST);
+ wlc_rpctx_fifoinit(wlc->rpctx, TX_CTL_FIFO, NRPCTXBUFPOST);
+ wlc_rpctx_fifoinit(wlc->rpctx, TX_BCMC_FIFO, NRPCTXBUFPOST);
+
+ /* VI and BK inited only if WME */
+ if (WME_ENAB(wlc->pub)) {
+ wlc_rpctx_fifoinit(wlc->rpctx, TX_AC_BK_FIFO, NRPCTXBUFPOST);
+ wlc_rpctx_fifoinit(wlc->rpctx, TX_AC_VI_FIFO, NRPCTXBUFPOST);
+ }
+
+ /* Allocate SB handle */
+ wlc->pub->sih = osl_malloc(wlc->osh, sizeof(si_t));
+ if (!wlc->pub->sih)
+ return -1;
+ bzero(wlc->pub->sih, sizeof(si_t));
+
+ /* sync up revinfo with BMAC */
+ bzero(&revinfo, sizeof(wlc_bmac_revinfo_t));
+ if (wlc_bmac_revinfo_get(wlc->hw, &revinfo) != 0)
+ return -1;
+ wlc->vendorid = (u16) revinfo.vendorid;
+ wlc->deviceid = (u16) revinfo.deviceid;
+
+ wlc->pub->boardrev = (u16) revinfo.boardrev;
+ wlc->pub->corerev = revinfo.corerev;
+ wlc->pub->sromrev = (u8) revinfo.sromrev;
+ wlc->pub->sih->chiprev = revinfo.chiprev;
+ wlc->pub->sih->chip = revinfo.chip;
+ wlc->pub->sih->chippkg = revinfo.chippkg;
+ wlc->pub->sih->boardtype = revinfo.boardtype;
+ wlc->pub->sih->boardvendor = revinfo.boardvendor;
+ wlc->pub->sih->bustype = revinfo.bustype;
+ wlc->pub->sih->buscoretype = revinfo.buscoretype;
+ wlc->pub->sih->buscorerev = revinfo.buscorerev;
+ wlc->pub->sih->issim = (bool) revinfo.issim;
+ wlc->pub->sih->rpc = rpc;
+
+ if (revinfo.nbands == 0 || revinfo.nbands > 2)
+ return -1;
+ wlc->pub->_nbands = revinfo.nbands;
+
+ for (idx = 0; idx < wlc->pub->_nbands; idx++) {
+ uint bandunit, bandtype; /* To access bandstate */
+ wlc_phy_t *pi = osl_malloc(wlc->osh, sizeof(wlc_phy_t));
+
+ if (!pi)
+ return -1;
+ bzero(pi, sizeof(wlc_phy_t));
+ pi->rpc = rpc;
+
+ bandunit = revinfo.band[idx].bandunit;
+ bandtype = revinfo.band[idx].bandtype;
+ wlc->bandstate[bandunit]->radiorev =
+ (u8) revinfo.band[idx].radiorev;
+ wlc->bandstate[bandunit]->phytype =
+ (u16) revinfo.band[idx].phytype;
+ wlc->bandstate[bandunit]->phyrev =
+ (u16) revinfo.band[idx].phyrev;
+ wlc->bandstate[bandunit]->radioid =
+ (u16) revinfo.band[idx].radioid;
+ wlc->bandstate[bandunit]->abgphy_encore =
+ revinfo.band[idx].abgphy_encore;
+
+ wlc->bandstate[bandunit]->pi = pi;
+ wlc->bandstate[bandunit]->bandunit = bandunit;
+ wlc->bandstate[bandunit]->bandtype = bandtype;
+ }
+
+ /* misc stuff */
+
+ return 0;
+}
+
+/* Free the convenience handles */
+int wlc_bmac_detach(wlc_info_t *wlc)
+{
+ uint idx;
+
+ if (wlc->pub->sih) {
+ osl_mfree(wlc->osh, (void *)wlc->pub->sih, sizeof(si_t));
+ wlc->pub->sih = NULL;
+ }
+
+ for (idx = 0; idx < MAXBANDS; idx++)
+ if (wlc->bandstate[idx]->pi) {
+ kfree(wlc->bandstate[idx]->pi);
+ wlc->bandstate[idx]->pi = NULL;
+ }
+
+ if (wlc->rpctx) {
+ wlc_rpctx_detach(wlc->rpctx);
+ wlc->rpctx = NULL;
+ }
+
+ return 0;
+
+}
+
+#endif /* WLC_HIGH_ONLY */
+
+static void wlc_timers_deinit(wlc_info_t *wlc)
+{
+ /* free timer state */
+ if (wlc->wdtimer) {
+ wl_free_timer(wlc->wl, wlc->wdtimer);
+ wlc->wdtimer = NULL;
+ }
+ if (wlc->radio_timer) {
+ wl_free_timer(wlc->wl, wlc->radio_timer);
+ wlc->radio_timer = NULL;
+ }
+}
+
+static void wlc_detach_module(wlc_info_t *wlc)
+{
+ if (wlc->asi) {
+ wlc_antsel_detach(wlc->asi);
+ wlc->asi = NULL;
+ }
+
+ if (wlc->ampdu) {
+ wlc_ampdu_detach(wlc->ampdu);
+ wlc->ampdu = NULL;
+ }
+
+ wlc_stf_detach(wlc);
+}
+
+/*
+ * Return a count of the number of driver callbacks still pending.
+ *
+ * General policy is that wlc_detach can only dealloc/free software states. It can NOT
+ * touch hardware registers since the d11core may be in reset and clock may not be available.
+ * One exception is sb register access, which is possible if crystal is turned on
+ * After "down" state, driver should avoid software timer with the exception of radio_monitor.
+ */
+uint wlc_detach(wlc_info_t *wlc)
+{
+ uint i;
+ uint callbacks = 0;
+
+ if (wlc == NULL)
+ return 0;
+
+ WL_TRACE(("wl%d: %s\n", wlc->pub->unit, __func__));
+
+ ASSERT(!wlc->pub->up);
+
+ callbacks += wlc_bmac_detach(wlc);
+
+ /* delete software timers */
+ if (!wlc_radio_monitor_stop(wlc))
+ callbacks++;
+
+ if (wlc->eventq) {
+ wlc_eventq_detach(wlc->eventq);
+ wlc->eventq = NULL;
+ }
+
+ wlc_channel_mgr_detach(wlc->cmi);
+
+ wlc_timers_deinit(wlc);
+
+ wlc_detach_module(wlc);
+
+ /* free other state */
+
+#ifdef WLC_HIGH_ONLY
+ /* High-Only driver has an allocated copy of vars, monolithic just
+ * references the wlc->hw->vars which is freed in wlc_bmac_detach()
+ */
+ if (wlc->pub->vars) {
+ kfree(wlc->pub->vars);
+ wlc->pub->vars = NULL;
+ }
+#endif
+
+#ifdef BCMDBG
+ if (wlc->country_ie_override) {
+ kfree(wlc->country_ie_override);
+ wlc->country_ie_override = NULL;
+ }
+#endif /* BCMDBG */
+
+ {
+ /* free dumpcb list */
+ dumpcb_t *prev, *ptr;
+ prev = ptr = wlc->dumpcb_head;
+ while (ptr) {
+ ptr = prev->next;
+ kfree(prev);
+ prev = ptr;
+ }
+ wlc->dumpcb_head = NULL;
+ }
+
+ /* Detach from iovar manager */
+ wlc_module_unregister(wlc->pub, "wlc_iovars", wlc);
+
+ /*
+ if (wlc->ap) {
+ wlc_ap_detach(wlc->ap);
+ wlc->ap = NULL;
+ }
+ */
+
+ while (wlc->tx_queues != NULL) {
+ wlc_txq_free(wlc, wlc->osh, wlc->tx_queues);
+ }
+
+ /*
+ * consistency check: wlc_module_register/wlc_module_unregister calls
+ * should match therefore nothing should be left here.
+ */
+ for (i = 0; i < WLC_MAXMODULES; i++)
+ ASSERT(wlc->modulecb[i].name[0] == '\0');
+
+ wlc_detach_mfree(wlc, wlc->osh);
+ return callbacks;
+}
+
+/* update state that depends on the current value of "ap" */
+void wlc_ap_upd(wlc_info_t *wlc)
+{
+ if (AP_ENAB(wlc->pub))
+ wlc->PLCPHdr_override = WLC_PLCP_AUTO; /* AP: short not allowed, but not enforced */
+ else
+ wlc->PLCPHdr_override = WLC_PLCP_SHORT; /* STA-BSS; short capable */
+
+ /* disable vlan_mode on AP since some legacy STAs cannot rx tagged pkts */
+ wlc->vlan_mode = AP_ENAB(wlc->pub) ? OFF : AUTO;
+
+ /* fixup mpc */
+ wlc->mpc = true;
+}
+
+/* read hwdisable state and propagate to wlc flag */
+static void wlc_radio_hwdisable_upd(wlc_info_t *wlc)
+{
+ if (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO || wlc->pub->hw_off)
+ return;
+
+ if (wlc_bmac_radio_read_hwdisabled(wlc->hw)) {
+ mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
+ } else {
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE);
+ }
+}
+
+/* return true if Minimum Power Consumption should be entered, false otherwise */
+bool wlc_is_non_delay_mpc(wlc_info_t *wlc)
+{
+ return false;
+}
+
+bool wlc_ismpc(wlc_info_t *wlc)
+{
+ return (wlc->mpc_delay_off == 0) && (wlc_is_non_delay_mpc(wlc));
+}
+
+void wlc_radio_mpc_upd(wlc_info_t *wlc)
+{
+ bool mpc_radio, radio_state;
+
+ /*
+ * Clear the WL_RADIO_MPC_DISABLE bit when mpc feature is disabled
+ * in case the WL_RADIO_MPC_DISABLE bit was set. Stop the radio
+ * monitor also when WL_RADIO_MPC_DISABLE is the only reason that
+ * the radio is going down.
+ */
+ if (!wlc->mpc) {
+ if (!wlc->pub->radio_disabled)
+ return;
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE);
+ wlc_radio_upd(wlc);
+ if (!wlc->pub->radio_disabled)
+ wlc_radio_monitor_stop(wlc);
+ return;
+ }
+
+ /*
+ * sync ismpc logic with WL_RADIO_MPC_DISABLE bit in wlc->pub->radio_disabled
+ * to go ON, always call radio_upd synchronously
+ * to go OFF, postpone radio_upd to later when context is safe(e.g. watchdog)
+ */
+ radio_state =
+ (mboolisset(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE) ? OFF :
+ ON);
+ mpc_radio = (wlc_ismpc(wlc) == true) ? OFF : ON;
+
+ if (radio_state == ON && mpc_radio == OFF)
+ wlc->mpc_delay_off = wlc->mpc_dlycnt;
+ else if (radio_state == OFF && mpc_radio == ON) {
+ mboolclr(wlc->pub->radio_disabled, WL_RADIO_MPC_DISABLE);
+ wlc_radio_upd(wlc);
+ if (wlc->mpc_offcnt < WLC_MPC_THRESHOLD) {
+ wlc->mpc_dlycnt = WLC_MPC_MAX_DELAYCNT;
+ } else
+ wlc->mpc_dlycnt = WLC_MPC_MIN_DELAYCNT;
+ wlc->mpc_dur += OSL_SYSUPTIME() - wlc->mpc_laston_ts;
+ }
+ /* Below logic is meant to capture the transition from mpc off to mpc on for reasons
+ * other than wlc->mpc_delay_off keeping the mpc off. In that case reset
+ * wlc->mpc_delay_off to wlc->mpc_dlycnt, so that we restart the countdown of mpc_delay_off
+ */
+ if ((wlc->prev_non_delay_mpc == false) &&
+ (wlc_is_non_delay_mpc(wlc) == true) && wlc->mpc_delay_off) {
+ wlc->mpc_delay_off = wlc->mpc_dlycnt;
+ }
+ wlc->prev_non_delay_mpc = wlc_is_non_delay_mpc(wlc);
+}
+
+/*
+ * centralized radio disable/enable function,
+ * invoke radio enable/disable after updating hwradio status
+ */
+static void wlc_radio_upd(wlc_info_t *wlc)
+{
+ if (wlc->pub->radio_disabled)
+ wlc_radio_disable(wlc);
+ else
+ wlc_radio_enable(wlc);
+}
+
+/* maintain LED behavior in down state */
+static void wlc_down_led_upd(wlc_info_t *wlc)
+{
+ ASSERT(!wlc->pub->up);
+
+ /* maintain LEDs while in down state, turn on sbclk if not available yet */
+ /* turn on sbclk if necessary */
+ if (!AP_ENAB(wlc->pub)) {
+ wlc_pllreq(wlc, true, WLC_PLLREQ_FLIP);
+
+ wlc_pllreq(wlc, false, WLC_PLLREQ_FLIP);
+ }
+}
+
+void wlc_radio_disable(wlc_info_t *wlc)
+{
+ if (!wlc->pub->up) {
+ wlc_down_led_upd(wlc);
+ return;
+ }
+
+ wlc_radio_monitor_start(wlc);
+ wl_down(wlc->wl);
+}
+
+static void wlc_radio_enable(wlc_info_t *wlc)
+{
+ if (wlc->pub->up)
+ return;
+
+ if (DEVICEREMOVED(wlc))
+ return;
+
+ if (!wlc->down_override) { /* imposed by wl down/out ioctl */
+ wl_up(wlc->wl);
+ }
+}
+
+/* periodical query hw radio button while driver is "down" */
+static void wlc_radio_timer(void *arg)
+{
+ wlc_info_t *wlc = (wlc_info_t *) arg;
+
+ if (DEVICEREMOVED(wlc)) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit, __func__));
+ wl_down(wlc->wl);
+ return;
+ }
+
+ /* cap mpc off count */
+ if (wlc->mpc_offcnt < WLC_MPC_MAX_DELAYCNT)
+ wlc->mpc_offcnt++;
+
+ /* validate all the reasons driver could be down and running this radio_timer */
+ ASSERT(wlc->pub->radio_disabled || wlc->down_override);
+ wlc_radio_hwdisable_upd(wlc);
+ wlc_radio_upd(wlc);
+}
+
+static bool wlc_radio_monitor_start(wlc_info_t *wlc)
+{
+ /* Don't start the timer if HWRADIO feature is disabled */
+ if (wlc->radio_monitor || (wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO))
+ return true;
+
+ wlc->radio_monitor = true;
+ wlc_pllreq(wlc, true, WLC_PLLREQ_RADIO_MON);
+ wl_add_timer(wlc->wl, wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true);
+ return true;
+}
+
+bool wlc_radio_monitor_stop(wlc_info_t *wlc)
+{
+ if (!wlc->radio_monitor)
+ return true;
+
+ ASSERT((wlc->pub->wlfeatureflag & WL_SWFL_NOHWRADIO) !=
+ WL_SWFL_NOHWRADIO);
+
+ wlc->radio_monitor = false;
+ wlc_pllreq(wlc, false, WLC_PLLREQ_RADIO_MON);
+ return wl_del_timer(wlc->wl, wlc->radio_timer);
+}
+
+/* bring the driver down, but don't reset hardware */
+void wlc_out(wlc_info_t *wlc)
+{
+ wlc_bmac_set_noreset(wlc->hw, true);
+ wlc_radio_upd(wlc);
+ wl_down(wlc->wl);
+ wlc_bmac_set_noreset(wlc->hw, false);
+
+ /* core clk is true in BMAC driver due to noreset, need to mirror it in HIGH */
+ wlc->clk = true;
+
+ /* This will make sure that when 'up' is done
+ * after 'out' it'll restore hardware (especially gpios)
+ */
+ wlc->pub->hw_up = false;
+}
+
+#if defined(BCMDBG)
+/* Verify the sanity of wlc->tx_prec_map. This can be done only by making sure that
+ * if there is no packet pending for the FIFO, then the corresponding prec bits should be set
+ * in prec_map. Of course, ignore this rule when block_datafifo is set
+ */
+static bool wlc_tx_prec_map_verify(wlc_info_t *wlc)
+{
+ /* For non-WME, both fifos have overlapping prec_map. So it's an error only if both
+ * fail the check.
+ */
+ if (!EDCF_ENAB(wlc->pub)) {
+ if (!(WLC_TX_FIFO_CHECK(wlc, TX_DATA_FIFO) ||
+ WLC_TX_FIFO_CHECK(wlc, TX_CTL_FIFO)))
+ return false;
+ else
+ return true;
+ }
+
+ return WLC_TX_FIFO_CHECK(wlc, TX_AC_BK_FIFO)
+ && WLC_TX_FIFO_CHECK(wlc, TX_AC_BE_FIFO)
+ && WLC_TX_FIFO_CHECK(wlc, TX_AC_VI_FIFO)
+ && WLC_TX_FIFO_CHECK(wlc, TX_AC_VO_FIFO);
+}
+#endif /* BCMDBG */
+
+static void wlc_watchdog_by_timer(void *arg)
+{
+ wlc_info_t *wlc = (wlc_info_t *) arg;
+ wlc_watchdog(arg);
+ if (WLC_WATCHDOG_TBTT(wlc)) {
+ /* set to normal osl watchdog period */
+ wl_del_timer(wlc->wl, wlc->wdtimer);
+ wl_add_timer(wlc->wl, wlc->wdtimer, TIMER_INTERVAL_WATCHDOG,
+ true);
+ }
+}
+
+/* common watchdog code */
+static void wlc_watchdog(void *arg)
+{
+ wlc_info_t *wlc = (wlc_info_t *) arg;
+ int i;
+ wlc_bsscfg_t *cfg;
+
+ WL_TRACE(("wl%d: wlc_watchdog\n", wlc->pub->unit));
+
+ if (!wlc->pub->up)
+ return;
+
+ if (DEVICEREMOVED(wlc)) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit, __func__));
+ wl_down(wlc->wl);
+ return;
+ }
+
+ /* increment second count */
+ wlc->pub->now++;
+
+ /* delay radio disable */
+ if (wlc->mpc_delay_off) {
+ if (--wlc->mpc_delay_off == 0) {
+ mboolset(wlc->pub->radio_disabled,
+ WL_RADIO_MPC_DISABLE);
+ if (wlc->mpc && wlc_ismpc(wlc))
+ wlc->mpc_offcnt = 0;
+ wlc->mpc_laston_ts = OSL_SYSUPTIME();
+ }
+ }
+
+ /* mpc sync */
+ wlc_radio_mpc_upd(wlc);
+ /* radio sync: sw/hw/mpc --> radio_disable/radio_enable */
+ wlc_radio_hwdisable_upd(wlc);
+ wlc_radio_upd(wlc);
+ /* if ismpc, driver should be in down state if up/down is allowed */
+ if (wlc->mpc && wlc_ismpc(wlc))
+ ASSERT(!wlc->pub->up);
+ /* if radio is disable, driver may be down, quit here */
+ if (wlc->pub->radio_disabled)
+ return;
+
+#ifdef WLC_LOW
+ wlc_bmac_watchdog(wlc);
+#endif
+#ifdef WLC_HIGH_ONLY
+ /* maintenance */
+ wlc_bmac_rpc_watchdog(wlc);
+#endif
+
+ /* occasionally sample mac stat counters to detect 16-bit counter wrap */
+ if ((WLC_UPDATE_STATS(wlc))
+ && (!(wlc->pub->now % SW_TIMER_MAC_STAT_UPD)))
+ wlc_statsupd(wlc);
+
+ /* Manage TKIP countermeasures timers */
+ FOREACH_BSS(wlc, i, cfg) {
+ if (cfg->tk_cm_dt) {
+ cfg->tk_cm_dt--;
+ }
+ if (cfg->tk_cm_bt) {
+ cfg->tk_cm_bt--;
+ }
+ }
+
+ /* Call any registered watchdog handlers */
+ for (i = 0; i < WLC_MAXMODULES; i++) {
+ if (wlc->modulecb[i].watchdog_fn)
+ wlc->modulecb[i].watchdog_fn(wlc->modulecb[i].hdl);
+ }
+
+ if (WLCISNPHY(wlc->band) && !wlc->pub->tempsense_disable &&
+ ((wlc->pub->now - wlc->tempsense_lasttime) >=
+ WLC_TEMPSENSE_PERIOD)) {
+ wlc->tempsense_lasttime = wlc->pub->now;
+ wlc_tempsense_upd(wlc);
+ }
+#ifdef WLC_LOW
+ /* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
+ ASSERT(wlc_bmac_taclear(wlc->hw, true));
+#endif
+
+ /* Verify that tx_prec_map and fifos are in sync to avoid lock ups */
+ ASSERT(wlc_tx_prec_map_verify(wlc));
+
+ ASSERT(wlc_ps_check(wlc));
+}
+
+/* make interface operational */
+int wlc_up(wlc_info_t *wlc)
+{
+ WL_TRACE(("wl%d: %s:\n", wlc->pub->unit, __func__));
+
+ /* HW is turned off so don't try to access it */
+ if (wlc->pub->hw_off || DEVICEREMOVED(wlc))
+ return BCME_RADIOOFF;
+
+ if (!wlc->pub->hw_up) {
+ wlc_bmac_hw_up(wlc->hw);
+ wlc->pub->hw_up = true;
+ }
+
+ if ((wlc->pub->boardflags & BFL_FEM)
+ && (CHIPID(wlc->pub->sih->chip) == BCM4313_CHIP_ID)) {
+ if (wlc->pub->boardrev >= 0x1250
+ && (wlc->pub->boardflags & BFL_FEM_BT)) {
+ wlc_mhf(wlc, MHF5, MHF5_4313_GPIOCTRL,
+ MHF5_4313_GPIOCTRL, WLC_BAND_ALL);
+ } else {
+ wlc_mhf(wlc, MHF4, MHF4_EXTPA_ENABLE, MHF4_EXTPA_ENABLE,
+ WLC_BAND_ALL);
+ }
+ }
+
+ /*
+ * Need to read the hwradio status here to cover the case where the system
+ * is loaded with the hw radio disabled. We do not want to bring the driver up in this case.
+ * if radio is disabled, abort up, lower power, start radio timer and return 0(for NDIS)
+ * don't call radio_update to avoid looping wlc_up.
+ *
+ * wlc_bmac_up_prep() returns either 0 or BCME_RADIOOFF only
+ */
+ if (!wlc->pub->radio_disabled) {
+ int status = wlc_bmac_up_prep(wlc->hw);
+ if (status == BCME_RADIOOFF) {
+ if (!mboolisset
+ (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) {
+ int idx;
+ wlc_bsscfg_t *bsscfg;
+ mboolset(wlc->pub->radio_disabled,
+ WL_RADIO_HW_DISABLE);
+
+ FOREACH_BSS(wlc, idx, bsscfg) {
+ if (!BSSCFG_STA(bsscfg)
+ || !bsscfg->enable || !bsscfg->BSS)
+ continue;
+ WL_ERROR(("wl%d.%d: wlc_up: rfdisable -> " "wlc_bsscfg_disable()\n", wlc->pub->unit, idx));
+ }
+ }
+ } else
+ ASSERT(!status);
+ }
+
+ if (wlc->pub->radio_disabled) {
+ wlc_radio_monitor_start(wlc);
+ return 0;
+ }
+
+ /* wlc_bmac_up_prep has done wlc_corereset(). so clk is on, set it */
+ wlc->clk = true;
+
+ wlc_radio_monitor_stop(wlc);
+
+ /* Set EDCF hostflags */
+ if (EDCF_ENAB(wlc->pub)) {
+ wlc_mhf(wlc, MHF1, MHF1_EDCF, MHF1_EDCF, WLC_BAND_ALL);
+ } else {
+ wlc_mhf(wlc, MHF1, MHF1_EDCF, 0, WLC_BAND_ALL);
+ }
+
+ if (WLC_WAR16165(wlc))
+ wlc_mhf(wlc, MHF2, MHF2_PCISLOWCLKWAR, MHF2_PCISLOWCLKWAR,
+ WLC_BAND_ALL);
+
+ wl_init(wlc->wl);
+ wlc->pub->up = true;
+
+ if (wlc->bandinit_pending) {
+ wlc_suspend_mac_and_wait(wlc);
+ wlc_set_chanspec(wlc, wlc->default_bss->chanspec);
+ wlc->bandinit_pending = false;
+ wlc_enable_mac(wlc);
+ }
+
+ wlc_bmac_up_finish(wlc->hw);
+
+ /* other software states up after ISR is running */
+ /* start APs that were to be brought up but are not up yet */
+ /* if (AP_ENAB(wlc->pub)) wlc_restart_ap(wlc->ap); */
+
+ /* Program the TX wme params with the current settings */
+ wlc_wme_retries_write(wlc);
+
+ /* start one second watchdog timer */
+ ASSERT(!wlc->WDarmed);
+ wl_add_timer(wlc->wl, wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true);
+ wlc->WDarmed = true;
+
+ /* ensure antenna config is up to date */
+ wlc_stf_phy_txant_upd(wlc);
+ /* ensure LDPC config is in sync */
+ wlc_ht_update_ldpc(wlc, wlc->stf->ldpc);
+
+ return 0;
+}
+
+/* Initialize the base precedence map for dequeueing from txq based on WME settings */
+static void wlc_tx_prec_map_init(wlc_info_t *wlc)
+{
+ wlc->tx_prec_map = WLC_PREC_BMP_ALL;
+ bzero(wlc->fifo2prec_map, sizeof(u16) * NFIFO);
+
+ /* For non-WME, both fifos have overlapping MAXPRIO. So just disable all precedences
+ * if either is full.
+ */
+ if (!EDCF_ENAB(wlc->pub)) {
+ wlc->fifo2prec_map[TX_DATA_FIFO] = WLC_PREC_BMP_ALL;
+ wlc->fifo2prec_map[TX_CTL_FIFO] = WLC_PREC_BMP_ALL;
+ } else {
+ wlc->fifo2prec_map[TX_AC_BK_FIFO] = WLC_PREC_BMP_AC_BK;
+ wlc->fifo2prec_map[TX_AC_BE_FIFO] = WLC_PREC_BMP_AC_BE;
+ wlc->fifo2prec_map[TX_AC_VI_FIFO] = WLC_PREC_BMP_AC_VI;
+ wlc->fifo2prec_map[TX_AC_VO_FIFO] = WLC_PREC_BMP_AC_VO;
+ }
+}
+
+static uint wlc_down_del_timer(wlc_info_t *wlc)
+{
+ uint callbacks = 0;
+
+ return callbacks;
+}
+
+/*
+ * Mark the interface nonoperational, stop the software mechanisms,
+ * disable the hardware, free any transient buffer state.
+ * Return a count of the number of driver callbacks still pending.
+ */
+uint wlc_down(wlc_info_t *wlc)
+{
+
+ uint callbacks = 0;
+ int i;
+ bool dev_gone = false;
+ wlc_txq_info_t *qi;
+
+ WL_TRACE(("wl%d: %s:\n", wlc->pub->unit, __func__));
+
+ /* check if we are already in the going down path */
+ if (wlc->going_down) {
+ WL_ERROR(("wl%d: %s: Driver going down so return\n",
+ wlc->pub->unit, __func__));
+ return 0;
+ }
+ if (!wlc->pub->up)
+ return callbacks;
+
+ /* in between, mpc could try to bring down again.. */
+ wlc->going_down = true;
+
+ callbacks += wlc_bmac_down_prep(wlc->hw);
+
+ dev_gone = DEVICEREMOVED(wlc);
+
+ /* Call any registered down handlers */
+ for (i = 0; i < WLC_MAXMODULES; i++) {
+ if (wlc->modulecb[i].down_fn)
+ callbacks +=
+ wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl);
+ }
+
+ /* cancel the watchdog timer */
+ if (wlc->WDarmed) {
+ if (!wl_del_timer(wlc->wl, wlc->wdtimer))
+ callbacks++;
+ wlc->WDarmed = false;
+ }
+ /* cancel all other timers */
+ callbacks += wlc_down_del_timer(wlc);
+
+ /* interrupt must have been blocked */
+ ASSERT((wlc->macintmask == 0) || !wlc->pub->up);
+
+ wlc->pub->up = false;
+
+ wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL);
+
+ /* clear txq flow control */
+ wlc_txflowcontrol_reset(wlc);
+
+ /* flush tx queues */
+ for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
+ pktq_flush(wlc->osh, &qi->q, true, NULL, 0);
+ ASSERT(pktq_empty(&qi->q));
+ }
+
+ /* flush event queue.
+ * Should be the last thing done after all the events are generated
+ * Just delivers the events synchronously instead of waiting for a timer
+ */
+ callbacks += wlc_eventq_down(wlc->eventq);
+
+ callbacks += wlc_bmac_down_finish(wlc->hw);
+
+ /* wlc_bmac_down_finish has done wlc_coredisable(). so clk is off */
+ wlc->clk = false;
+
+#ifdef WLC_HIGH_ONLY
+ wlc_rpctx_txreclaim(wlc->rpctx);
+#endif
+
+ /* Verify all packets are flushed from the driver */
+ if (PKTALLOCED(wlc->osh) != 0) {
+ WL_ERROR(("%d packets not freed at wlc_down!!!!!!\n",
+ PKTALLOCED(wlc->osh)));
+ }
+#ifdef BCMDBG
+ /* Since all the packets should have been freed,
+ * all callbacks should have been called
+ */
+ for (i = 1; i <= wlc->pub->tunables->maxpktcb; i++)
+ ASSERT(wlc->pkt_callback[i].fn == NULL);
+#endif
+ wlc->going_down = false;
+ return callbacks;
+}
+
+/* Set the current gmode configuration */
+int wlc_set_gmode(wlc_info_t *wlc, u8 gmode, bool config)
+{
+ int ret = 0;
+ uint i;
+ wlc_rateset_t rs;
+ /* Default to 54g Auto */
+ s8 shortslot = WLC_SHORTSLOT_AUTO; /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */
+ bool shortslot_restrict = false; /* Restrict association to stations that support shortslot
+ */
+ bool ignore_bcns = true; /* Ignore legacy beacons on the same channel */
+ bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */
+ int preamble = WLC_PLCP_LONG; /* Advertise and use short preambles (-1/0/1 Auto/Off/On) */
+ bool preamble_restrict = false; /* Restrict association to stations that support short
+ * preambles
+ */
+ wlcband_t *band;
+
+ /* if N-support is enabled, allow Gmode set as long as requested
+ * Gmode is not GMODE_LEGACY_B
+ */
+ if (N_ENAB(wlc->pub) && gmode == GMODE_LEGACY_B)
+ return BCME_UNSUPPORTED;
+
+ /* verify that we are dealing with 2G band and grab the band pointer */
+ if (wlc->band->bandtype == WLC_BAND_2G)
+ band = wlc->band;
+ else if ((NBANDS(wlc) > 1) &&
+ (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == WLC_BAND_2G))
+ band = wlc->bandstate[OTHERBANDUNIT(wlc)];
+ else
+ return BCME_BADBAND;
+
+ /* Legacy or bust when no OFDM is supported by regulatory */
+ if ((wlc_channel_locale_flags_in_band(wlc->cmi, band->bandunit) &
+ WLC_NO_OFDM) && (gmode != GMODE_LEGACY_B))
+ return BCME_RANGE;
+
+ /* update configuration value */
+ if (config == true)
+ wlc_protection_upd(wlc, WLC_PROT_G_USER, gmode);
+
+ /* Clear supported rates filter */
+ bzero(&wlc->sup_rates_override, sizeof(wlc_rateset_t));
+
+ /* Clear rateset override */
+ bzero(&rs, sizeof(wlc_rateset_t));
+
+ switch (gmode) {
+ case GMODE_LEGACY_B:
+ shortslot = WLC_SHORTSLOT_OFF;
+ wlc_rateset_copy(&gphy_legacy_rates, &rs);
+
+ break;
+
+ case GMODE_LRS:
+ if (AP_ENAB(wlc->pub))
+ wlc_rateset_copy(&cck_rates, &wlc->sup_rates_override);
+ break;
+
+ case GMODE_AUTO:
+ /* Accept defaults */
+ break;
+
+ case GMODE_ONLY:
+ ofdm_basic = true;
+ preamble = WLC_PLCP_SHORT;
+ preamble_restrict = true;
+ break;
+
+ case GMODE_PERFORMANCE:
+ if (AP_ENAB(wlc->pub)) /* Put all rates into the Supported Rates element */
+ wlc_rateset_copy(&cck_ofdm_rates,
+ &wlc->sup_rates_override);
+
+ shortslot = WLC_SHORTSLOT_ON;
+ shortslot_restrict = true;
+ ofdm_basic = true;
+ preamble = WLC_PLCP_SHORT;
+ preamble_restrict = true;
+ break;
+
+ default:
+ /* Error */
+ WL_ERROR(("wl%d: %s: invalid gmode %d\n", wlc->pub->unit,
+ __func__, gmode));
+ return BCME_UNSUPPORTED;
+ }
+
+ /*
+ * If we are switching to gmode == GMODE_LEGACY_B,
+ * clean up rate info that may refer to OFDM rates.
+ */
+ if ((gmode == GMODE_LEGACY_B) && (band->gmode != GMODE_LEGACY_B)) {
+ band->gmode = gmode;
+ if (band->rspec_override && !IS_CCK(band->rspec_override)) {
+ band->rspec_override = 0;
+ wlc_reprate_init(wlc);
+ }
+ if (band->mrspec_override && !IS_CCK(band->mrspec_override)) {
+ band->mrspec_override = 0;
+ }
+ }
+
+ band->gmode = gmode;
+
+ wlc->ignore_bcns = ignore_bcns;
+
+ wlc->shortslot_override = shortslot;
+
+ if (AP_ENAB(wlc->pub)) {
+ /* wlc->ap->shortslot_restrict = shortslot_restrict; */
+ wlc->PLCPHdr_override =
+ (preamble !=
+ WLC_PLCP_LONG) ? WLC_PLCP_SHORT : WLC_PLCP_AUTO;
+ }
+
+ if ((AP_ENAB(wlc->pub) && preamble != WLC_PLCP_LONG)
+ || preamble == WLC_PLCP_SHORT)
+ wlc->default_bss->capability |= DOT11_CAP_SHORT;
+ else
+ wlc->default_bss->capability &= ~DOT11_CAP_SHORT;
+
+ /* Update shortslot capability bit for AP and IBSS */
+ if ((AP_ENAB(wlc->pub) && shortslot == WLC_SHORTSLOT_AUTO) ||
+ shortslot == WLC_SHORTSLOT_ON)
+ wlc->default_bss->capability |= DOT11_CAP_SHORTSLOT;
+ else
+ wlc->default_bss->capability &= ~DOT11_CAP_SHORTSLOT;
+
+ /* Use the default 11g rateset */
+ if (!rs.count)
+ wlc_rateset_copy(&cck_ofdm_rates, &rs);
+
+ if (ofdm_basic) {
+ for (i = 0; i < rs.count; i++) {
+ if (rs.rates[i] == WLC_RATE_6M
+ || rs.rates[i] == WLC_RATE_12M
+ || rs.rates[i] == WLC_RATE_24M)
+ rs.rates[i] |= WLC_RATE_FLAG;
+ }
+ }
+
+ /* Set default bss rateset */
+ wlc->default_bss->rateset.count = rs.count;
+ bcopy((char *)rs.rates, (char *)wlc->default_bss->rateset.rates,
+ sizeof(wlc->default_bss->rateset.rates));
+
+ return ret;
+}
+
+static int wlc_nmode_validate(wlc_info_t *wlc, s32 nmode)
+{
+ int err = 0;
+
+ switch (nmode) {
+
+ case OFF:
+ break;
+
+ case AUTO:
+ case WL_11N_2x2:
+ case WL_11N_3x3:
+ if (!(WLC_PHY_11N_CAP(wlc->band)))
+ err = BCME_BADBAND;
+ break;
+
+ default:
+ err = BCME_RANGE;
+ break;
+ }
+
+ return err;
+}
+
+int wlc_set_nmode(wlc_info_t *wlc, s32 nmode)
+{
+ uint i;
+ int err;
+
+ err = wlc_nmode_validate(wlc, nmode);
+ ASSERT(err == 0);
+ if (err)
+ return err;
+
+ switch (nmode) {
+ case OFF:
+ wlc->pub->_n_enab = OFF;
+ wlc->default_bss->flags &= ~WLC_BSS_HT;
+ /* delete the mcs rates from the default and hw ratesets */
+ wlc_rateset_mcs_clear(&wlc->default_bss->rateset);
+ for (i = 0; i < NBANDS(wlc); i++) {
+ memset(wlc->bandstate[i]->hw_rateset.mcs, 0,
+ MCSSET_LEN);
+ if (IS_MCS(wlc->band->rspec_override)) {
+ wlc->bandstate[i]->rspec_override = 0;
+ wlc_reprate_init(wlc);
+ }
+ if (IS_MCS(wlc->band->mrspec_override))
+ wlc->bandstate[i]->mrspec_override = 0;
+ }
+ break;
+
+ case AUTO:
+ if (wlc->stf->txstreams == WL_11N_3x3)
+ nmode = WL_11N_3x3;
+ else
+ nmode = WL_11N_2x2;
+ case WL_11N_2x2:
+ case WL_11N_3x3:
+ ASSERT(WLC_PHY_11N_CAP(wlc->band));
+ /* force GMODE_AUTO if NMODE is ON */
+ wlc_set_gmode(wlc, GMODE_AUTO, true);
+ if (nmode == WL_11N_3x3)
+ wlc->pub->_n_enab = SUPPORT_HT;
+ else
+ wlc->pub->_n_enab = SUPPORT_11N;
+ wlc->default_bss->flags |= WLC_BSS_HT;
+ /* add the mcs rates to the default and hw ratesets */
+ wlc_rateset_mcs_build(&wlc->default_bss->rateset,
+ wlc->stf->txstreams);
+ for (i = 0; i < NBANDS(wlc); i++)
+ memcpy(wlc->bandstate[i]->hw_rateset.mcs,
+ wlc->default_bss->rateset.mcs, MCSSET_LEN);
+ break;
+
+ default:
+ ASSERT(0);
+ break;
+ }
+
+ return err;
+}
+
+static int wlc_set_rateset(wlc_info_t *wlc, wlc_rateset_t *rs_arg)
+{
+ wlc_rateset_t rs, new;
+ uint bandunit;
+
+ bcopy((char *)rs_arg, (char *)&rs, sizeof(wlc_rateset_t));
+
+ /* check for bad count value */
+ if ((rs.count == 0) || (rs.count > WLC_NUMRATES))
+ return BCME_BADRATESET;
+
+ /* try the current band */
+ bandunit = wlc->band->bandunit;
+ bcopy((char *)&rs, (char *)&new, sizeof(wlc_rateset_t));
+ if (wlc_rate_hwrs_filter_sort_validate
+ (&new, &wlc->bandstate[bandunit]->hw_rateset, true,
+ wlc->stf->txstreams))
+ goto good;
+
+ /* try the other band */
+ if (IS_MBAND_UNLOCKED(wlc)) {
+ bandunit = OTHERBANDUNIT(wlc);
+ bcopy((char *)&rs, (char *)&new, sizeof(wlc_rateset_t));
+ if (wlc_rate_hwrs_filter_sort_validate(&new,
+ &wlc->
+ bandstate[bandunit]->
+ hw_rateset, true,
+ wlc->stf->txstreams))
+ goto good;
+ }
+
+ return BCME_ERROR;
+
+ good:
+ /* apply new rateset */
+ bcopy((char *)&new, (char *)&wlc->default_bss->rateset,
+ sizeof(wlc_rateset_t));
+ bcopy((char *)&new, (char *)&wlc->bandstate[bandunit]->defrateset,
+ sizeof(wlc_rateset_t));
+ return 0;
+}
+
+/* simplified integer set interface for common ioctl handler */
+int wlc_set(wlc_info_t *wlc, int cmd, int arg)
+{
+ return wlc_ioctl(wlc, cmd, (void *)&arg, sizeof(arg), NULL);
+}
+
+/* simplified integer get interface for common ioctl handler */
+int wlc_get(wlc_info_t *wlc, int cmd, int *arg)
+{
+ return wlc_ioctl(wlc, cmd, arg, sizeof(int), NULL);
+}
+
+static void wlc_ofdm_rateset_war(wlc_info_t *wlc)
+{
+ u8 r;
+ bool war = false;
+
+ if (wlc->cfg->associated)
+ r = wlc->cfg->current_bss->rateset.rates[0];
+ else
+ r = wlc->default_bss->rateset.rates[0];
+
+ wlc_phy_ofdm_rateset_war(wlc->band->pi, war);
+
+ return;
+}
+
+int
+wlc_ioctl(wlc_info_t *wlc, int cmd, void *arg, int len, struct wlc_if *wlcif)
+{
+ return _wlc_ioctl(wlc, cmd, arg, len, wlcif);
+}
+
+/* common ioctl handler. return: 0=ok, -1=error, positive=particular error */
+static int
+_wlc_ioctl(wlc_info_t *wlc, int cmd, void *arg, int len, struct wlc_if *wlcif)
+{
+ int val, *pval;
+ bool bool_val;
+ int bcmerror;
+ d11regs_t *regs;
+ uint i;
+ struct scb *nextscb;
+ bool ta_ok;
+ uint band;
+ rw_reg_t *r;
+ wlc_bsscfg_t *bsscfg;
+ osl_t *osh;
+ wlc_bss_info_t *current_bss;
+
+ /* update bsscfg pointer */
+ bsscfg = NULL; /* XXX: Hack bsscfg to be size one and use this globally */
+ current_bss = NULL;
+
+ /* initialize the following to get rid of compiler warning */
+ nextscb = NULL;
+ ta_ok = false;
+ band = 0;
+ r = NULL;
+
+ /* If the device is turned off, then it's not "removed" */
+ if (!wlc->pub->hw_off && DEVICEREMOVED(wlc)) {
+ WL_ERROR(("wl%d: %s: dead chip\n", wlc->pub->unit, __func__));
+ wl_down(wlc->wl);
+ return BCME_ERROR;
+ }
+
+ ASSERT(!(wlc->pub->hw_off && wlc->pub->up));
+
+ /* default argument is generic integer */
+ pval = arg ? (int *)arg:NULL;
+
+ /* This will prevent the misaligned access */
+ if (pval && (u32) len >= sizeof(val))
+ bcopy(pval, &val, sizeof(val));
+ else
+ val = 0;
+
+ /* bool conversion to avoid duplication below */
+ bool_val = val != 0;
+
+ if (cmd != WLC_SET_CHANNEL)
+ WL_NONE(("WLC_IOCTL: cmd %d val 0x%x (%d) len %d\n", cmd,
+ (uint) val, val, len));
+
+ bcmerror = 0;
+ regs = wlc->regs;
+ osh = wlc->osh;
+
+ /* A few commands don't need any arguments; all the others do. */
+ switch (cmd) {
+ case WLC_UP:
+ case WLC_OUT:
+ case WLC_DOWN:
+ case WLC_DISASSOC:
+ case WLC_RESTART:
+ case WLC_REBOOT:
+ case WLC_START_CHANNEL_QA:
+ case WLC_INIT:
+ break;
+
+ default:
+ if ((arg == NULL) || (len <= 0)) {
+ WL_ERROR(("wl%d: %s: Command %d needs arguments\n",
+ wlc->pub->unit, __func__, cmd));
+ bcmerror = BCME_BADARG;
+ goto done;
+ }
+ }
+
+ switch (cmd) {
+
+#if defined(BCMDBG)
+ case WLC_GET_MSGLEVEL:
+ *pval = wl_msg_level;
+ break;
+
+ case WLC_SET_MSGLEVEL:
+ wl_msg_level = val;
+ break;
+#endif
+
+ case WLC_GET_INSTANCE:
+ *pval = wlc->pub->unit;
+ break;
+
+ case WLC_GET_CHANNEL:{
+ channel_info_t *ci = (channel_info_t *) arg;
+
+ ASSERT(len > (int)sizeof(ci));
+
+ ci->hw_channel =
+ CHSPEC_CHANNEL(WLC_BAND_PI_RADIO_CHANSPEC);
+ ci->target_channel =
+ CHSPEC_CHANNEL(wlc->default_bss->chanspec);
+ ci->scan_channel = 0;
+
+ break;
+ }
+
+ case WLC_SET_CHANNEL:{
+ chanspec_t chspec = CH20MHZ_CHSPEC(val);
+
+ if (val < 0 || val > MAXCHANNEL) {
+ bcmerror = BCME_OUTOFRANGECHAN;
+ break;
+ }
+
+ if (!wlc_valid_chanspec_db(wlc->cmi, chspec)) {
+ bcmerror = BCME_BADCHAN;
+ break;
+ }
+
+ if (!wlc->pub->up && IS_MBAND_UNLOCKED(wlc)) {
+ if (wlc->band->bandunit !=
+ CHSPEC_WLCBANDUNIT(chspec))
+ wlc->bandinit_pending = true;
+ else
+ wlc->bandinit_pending = false;
+ }
+
+ wlc->default_bss->chanspec = chspec;
+ /* wlc_BSSinit() will sanitize the rateset before using it.. */
+ if (wlc->pub->up && !wlc->pub->associated &&
+ (WLC_BAND_PI_RADIO_CHANSPEC != chspec)) {
+ wlc_set_home_chanspec(wlc, chspec);
+ wlc_suspend_mac_and_wait(wlc);
+ wlc_set_chanspec(wlc, chspec);
+ wlc_enable_mac(wlc);
+ }
+#ifdef WLC_HIGH_ONLY
+ /* delay for channel change */
+ msleep(50);
+#endif
+ break;
+ }
+
+#if defined(BCMDBG)
+ case WLC_GET_UCFLAGS:
+ if (!wlc->pub->up) {
+ bcmerror = BCME_NOTUP;
+ break;
+ }
+
+ /* optional band is stored in the second integer of incoming buffer */
+ band =
+ (len <
+ (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
+
+ /* bcmerror checking */
+ bcmerror = wlc_iocregchk(wlc, band);
+ if (bcmerror)
+ break;
+
+ if (val >= MHFMAX) {
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ *pval = wlc_bmac_mhf_get(wlc->hw, (u8) val, WLC_BAND_AUTO);
+ break;
+
+ case WLC_SET_UCFLAGS:
+ if (!wlc->pub->up) {
+ bcmerror = BCME_NOTUP;
+ break;
+ }
+
+ /* optional band is stored in the second integer of incoming buffer */
+ band =
+ (len <
+ (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
+
+ /* bcmerror checking */
+ bcmerror = wlc_iocregchk(wlc, band);
+ if (bcmerror)
+ break;
+
+ i = (u16) val;
+ if (i >= MHFMAX) {
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ wlc_mhf(wlc, (u8) i, 0xffff, (u16) (val >> NBITS(u16)),
+ WLC_BAND_AUTO);
+ break;
+
+ case WLC_GET_SHMEM:
+ ta_ok = true;
+
+ /* optional band is stored in the second integer of incoming buffer */
+ band =
+ (len <
+ (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
+
+ /* bcmerror checking */
+ bcmerror = wlc_iocregchk(wlc, band);
+ if (bcmerror)
+ break;
+
+ if (val & 1) {
+ bcmerror = BCME_BADADDR;
+ break;
+ }
+
+ *pval = wlc_read_shm(wlc, (u16) val);
+ break;
+
+ case WLC_SET_SHMEM:
+ ta_ok = true;
+
+ /* optional band is stored in the second integer of incoming buffer */
+ band =
+ (len <
+ (int)(2 * sizeof(int))) ? WLC_BAND_AUTO : ((int *)arg)[1];
+
+ /* bcmerror checking */
+ bcmerror = wlc_iocregchk(wlc, band);
+ if (bcmerror)
+ break;
+
+ if (val & 1) {
+ bcmerror = BCME_BADADDR;
+ break;
+ }
+
+ wlc_write_shm(wlc, (u16) val,
+ (u16) (val >> NBITS(u16)));
+ break;
+
+ case WLC_R_REG: /* MAC registers */
+ ta_ok = true;
+ r = (rw_reg_t *) arg;
+ band = WLC_BAND_AUTO;
+
+ if (len < (int)(sizeof(rw_reg_t) - sizeof(uint))) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ if (len >= (int)sizeof(rw_reg_t))
+ band = r->band;
+
+ /* bcmerror checking */
+ bcmerror = wlc_iocregchk(wlc, band);
+ if (bcmerror)
+ break;
+
+ if ((r->byteoff + r->size) > sizeof(d11regs_t)) {
+ bcmerror = BCME_BADADDR;
+ break;
+ }
+ if (r->size == sizeof(u32))
+ r->val =
+ R_REG(osh,
+ (u32 *)((unsigned char *)(unsigned long)regs +
+ r->byteoff));
+ else if (r->size == sizeof(u16))
+ r->val =
+ R_REG(osh,
+ (u16 *)((unsigned char *)(unsigned long)regs +
+ r->byteoff));
+ else
+ bcmerror = BCME_BADADDR;
+ break;
+
+ case WLC_W_REG:
+ ta_ok = true;
+ r = (rw_reg_t *) arg;
+ band = WLC_BAND_AUTO;
+
+ if (len < (int)(sizeof(rw_reg_t) - sizeof(uint))) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ if (len >= (int)sizeof(rw_reg_t))
+ band = r->band;
+
+ /* bcmerror checking */
+ bcmerror = wlc_iocregchk(wlc, band);
+ if (bcmerror)
+ break;
+
+ if (r->byteoff + r->size > sizeof(d11regs_t)) {
+ bcmerror = BCME_BADADDR;
+ break;
+ }
+ if (r->size == sizeof(u32))
+ W_REG(osh,
+ (u32 *)((unsigned char *)(unsigned long) regs +
+ r->byteoff), r->val);
+ else if (r->size == sizeof(u16))
+ W_REG(osh,
+ (u16 *)((unsigned char *)(unsigned long) regs +
+ r->byteoff), r->val);
+ else
+ bcmerror = BCME_BADADDR;
+ break;
+#endif /* BCMDBG */
+
+ case WLC_GET_TXANT:
+ *pval = wlc->stf->txant;
+ break;
+
+ case WLC_SET_TXANT:
+ bcmerror = wlc_stf_ant_txant_validate(wlc, (s8) val);
+ if (bcmerror < 0)
+ break;
+
+ wlc->stf->txant = (s8) val;
+
+ /* if down, we are done */
+ if (!wlc->pub->up)
+ break;
+
+ wlc_suspend_mac_and_wait(wlc);
+
+ wlc_stf_phy_txant_upd(wlc);
+ wlc_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
+
+ wlc_enable_mac(wlc);
+
+ break;
+
+ case WLC_GET_ANTDIV:{
+ u8 phy_antdiv;
+
+ /* return configured value if core is down */
+ if (!wlc->pub->up) {
+ *pval = wlc->stf->ant_rx_ovr;
+
+ } else {
+ if (wlc_phy_ant_rxdiv_get
+ (wlc->band->pi, &phy_antdiv))
+ *pval = (int)phy_antdiv;
+ else
+ *pval = (int)wlc->stf->ant_rx_ovr;
+ }
+
+ break;
+ }
+ case WLC_SET_ANTDIV:
+ /* values are -1=driver default, 0=force0, 1=force1, 2=start1, 3=start0 */
+ if ((val < -1) || (val > 3)) {
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ if (val == -1)
+ val = ANT_RX_DIV_DEF;
+
+ wlc->stf->ant_rx_ovr = (u8) val;
+ wlc_phy_ant_rxdiv_set(wlc->band->pi, (u8) val);
+ break;
+
+ case WLC_GET_RX_ANT:{ /* get latest used rx antenna */
+ u16 rxstatus;
+
+ if (!wlc->pub->up) {
+ bcmerror = BCME_NOTUP;
+ break;
+ }
+
+ rxstatus = R_REG(wlc->osh, &wlc->regs->phyrxstatus0);
+ if (rxstatus == 0xdead || rxstatus == (u16) -1) {
+ bcmerror = BCME_ERROR;
+ break;
+ }
+ *pval = (rxstatus & PRXS0_RXANT_UPSUBBAND) ? 1 : 0;
+ break;
+ }
+
+#if defined(BCMDBG)
+ case WLC_GET_UCANTDIV:
+ if (!wlc->clk) {
+ bcmerror = BCME_NOCLK;
+ break;
+ }
+
+ *pval =
+ (wlc_bmac_mhf_get(wlc->hw, MHF1, WLC_BAND_AUTO) &
+ MHF1_ANTDIV);
+ break;
+
+ case WLC_SET_UCANTDIV:{
+ if (!wlc->pub->up) {
+ bcmerror = BCME_NOTUP;
+ break;
+ }
+
+ /* if multiband, band must be locked */
+ if (IS_MBAND_UNLOCKED(wlc)) {
+ bcmerror = BCME_NOTBANDLOCKED;
+ break;
+ }
+
+ /* 4322 supports antdiv in phy, no need to set it to ucode */
+ if (WLCISNPHY(wlc->band)
+ && D11REV_IS(wlc->pub->corerev, 16)) {
+ WL_ERROR(("wl%d: can't set ucantdiv for 4322\n",
+ wlc->pub->unit));
+ bcmerror = BCME_UNSUPPORTED;
+ } else
+ wlc_mhf(wlc, MHF1, MHF1_ANTDIV,
+ (val ? MHF1_ANTDIV : 0), WLC_BAND_AUTO);
+ break;
+ }
+#endif /* defined(BCMDBG) */
+
+ case WLC_GET_SRL:
+ *pval = wlc->SRL;
+ break;
+
+ case WLC_SET_SRL:
+ if (val >= 1 && val <= RETRY_SHORT_MAX) {
+ int ac;
+ wlc->SRL = (u16) val;
+
+ wlc_bmac_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);
+
+ for (ac = 0; ac < AC_COUNT; ac++) {
+ WLC_WME_RETRY_SHORT_SET(wlc, ac, wlc->SRL);
+ }
+ wlc_wme_retries_write(wlc);
+ } else
+ bcmerror = BCME_RANGE;
+ break;
+
+ case WLC_GET_LRL:
+ *pval = wlc->LRL;
+ break;
+
+ case WLC_SET_LRL:
+ if (val >= 1 && val <= 255) {
+ int ac;
+ wlc->LRL = (u16) val;
+
+ wlc_bmac_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL);
+
+ for (ac = 0; ac < AC_COUNT; ac++) {
+ WLC_WME_RETRY_LONG_SET(wlc, ac, wlc->LRL);
+ }
+ wlc_wme_retries_write(wlc);
+ } else
+ bcmerror = BCME_RANGE;
+ break;
+
+ case WLC_GET_CWMIN:
+ *pval = wlc->band->CWmin;
+ break;
+
+ case WLC_SET_CWMIN:
+ if (!wlc->clk) {
+ bcmerror = BCME_NOCLK;
+ break;
+ }
+
+ if (val >= 1 && val <= 255) {
+ wlc_set_cwmin(wlc, (u16) val);
+ } else
+ bcmerror = BCME_RANGE;
+ break;
+
+ case WLC_GET_CWMAX:
+ *pval = wlc->band->CWmax;
+ break;
+
+ case WLC_SET_CWMAX:
+ if (!wlc->clk) {
+ bcmerror = BCME_NOCLK;
+ break;
+ }
+
+ if (val >= 255 && val <= 2047) {
+ wlc_set_cwmax(wlc, (u16) val);
+ } else
+ bcmerror = BCME_RANGE;
+ break;
+
+ case WLC_GET_RADIO: /* use mask if don't want to expose some internal bits */
+ *pval = wlc->pub->radio_disabled;
+ break;
+
+ case WLC_SET_RADIO:{ /* 32 bits input, higher 16 bits are mask, lower 16 bits are value to
+ * set
+ */
+ u16 radiomask, radioval;
+ uint validbits =
+ WL_RADIO_SW_DISABLE | WL_RADIO_HW_DISABLE;
+ mbool new = 0;
+
+ radiomask = (val & 0xffff0000) >> 16;
+ radioval = val & 0x0000ffff;
+
+ if ((radiomask == 0) || (radiomask & ~validbits)
+ || (radioval & ~validbits)
+ || ((radioval & ~radiomask) != 0)) {
+ WL_ERROR(("SET_RADIO with wrong bits 0x%x\n",
+ val));
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ new =
+ (wlc->pub->radio_disabled & ~radiomask) | radioval;
+ wlc->pub->radio_disabled = new;
+
+ wlc_radio_hwdisable_upd(wlc);
+ wlc_radio_upd(wlc);
+ break;
+ }
+
+ case WLC_GET_PHYTYPE:
+ *pval = WLC_PHYTYPE(wlc->band->phytype);
+ break;
+
+#if defined(BCMDBG)
+ case WLC_GET_KEY:
+ if ((val >= 0) && (val < WLC_MAX_WSEC_KEYS(wlc))) {
+ wl_wsec_key_t key;
+
+ wsec_key_t *src_key = wlc->wsec_keys[val];
+
+ if (len < (int)sizeof(key)) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ bzero((char *)&key, sizeof(key));
+ if (src_key) {
+ key.index = src_key->id;
+ key.len = src_key->len;
+ bcopy(src_key->data, key.data, key.len);
+ key.algo = src_key->algo;
+ if (WSEC_SOFTKEY(wlc, src_key, bsscfg))
+ key.flags |= WL_SOFT_KEY;
+ if (src_key->flags & WSEC_PRIMARY_KEY)
+ key.flags |= WL_PRIMARY_KEY;
+
+ bcopy(src_key->ea.octet, key.ea.octet,
+ ETHER_ADDR_LEN);
+ }
+
+ bcopy((char *)&key, arg, sizeof(key));
+ } else
+ bcmerror = BCME_BADKEYIDX;
+ break;
+#endif /* defined(BCMDBG) */
+
+ case WLC_SET_KEY:
+ bcmerror =
+ wlc_iovar_op(wlc, "wsec_key", NULL, 0, arg, len, IOV_SET,
+ wlcif);
+ break;
+
+ case WLC_GET_KEY_SEQ:{
+ wsec_key_t *key;
+
+ if (len < DOT11_WPA_KEY_RSC_LEN) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ /* Return the key's tx iv as an EAPOL sequence counter.
+ * This will be used to supply the RSC value to a supplicant.
+ * The format is 8 bytes, with least significant in seq[0].
+ */
+
+ key = WSEC_KEY(wlc, val);
+ if ((val >= 0) && (val < WLC_MAX_WSEC_KEYS(wlc)) &&
+ (key != NULL)) {
+ u8 seq[DOT11_WPA_KEY_RSC_LEN];
+ u16 lo;
+ u32 hi;
+ /* group keys in WPA-NONE (IBSS only, AES and TKIP) use a global TXIV */
+ if ((bsscfg->WPA_auth & WPA_AUTH_NONE)
+ && ETHER_ISNULLADDR(&key->ea)) {
+ lo = bsscfg->wpa_none_txiv.lo;
+ hi = bsscfg->wpa_none_txiv.hi;
+ } else {
+ lo = key->txiv.lo;
+ hi = key->txiv.hi;
+ }
+
+ /* format the buffer, low to high */
+ seq[0] = lo & 0xff;
+ seq[1] = (lo >> 8) & 0xff;
+ seq[2] = hi & 0xff;
+ seq[3] = (hi >> 8) & 0xff;
+ seq[4] = (hi >> 16) & 0xff;
+ seq[5] = (hi >> 24) & 0xff;
+ seq[6] = 0;
+ seq[7] = 0;
+
+ bcopy((char *)seq, arg, sizeof(seq));
+ } else {
+ bcmerror = BCME_BADKEYIDX;
+ }
+ break;
+ }
+
+ case WLC_GET_CURR_RATESET:{
+ wl_rateset_t *ret_rs = (wl_rateset_t *) arg;
+ wlc_rateset_t *rs;
+
+ if (bsscfg->associated)
+ rs = &current_bss->rateset;
+ else
+ rs = &wlc->default_bss->rateset;
+
+ if (len < (int)(rs->count + sizeof(rs->count))) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ /* Copy only legacy rateset section */
+ ret_rs->count = rs->count;
+ bcopy(&rs->rates, &ret_rs->rates, rs->count);
+ break;
+ }
+
+ case WLC_GET_RATESET:{
+ wlc_rateset_t rs;
+ wl_rateset_t *ret_rs = (wl_rateset_t *) arg;
+
+ bzero(&rs, sizeof(wlc_rateset_t));
+ wlc_default_rateset(wlc, (wlc_rateset_t *) &rs);
+
+ if (len < (int)(rs.count + sizeof(rs.count))) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ /* Copy only legacy rateset section */
+ ret_rs->count = rs.count;
+ bcopy(&rs.rates, &ret_rs->rates, rs.count);
+ break;
+ }
+
+ case WLC_SET_RATESET:{
+ wlc_rateset_t rs;
+ wl_rateset_t *in_rs = (wl_rateset_t *) arg;
+
+ if (len < (int)(in_rs->count + sizeof(in_rs->count))) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ if (in_rs->count > WLC_NUMRATES) {
+ bcmerror = BCME_BUFTOOLONG;
+ break;
+ }
+
+ bzero(&rs, sizeof(wlc_rateset_t));
+
+ /* Copy only legacy rateset section */
+ rs.count = in_rs->count;
+ bcopy(&in_rs->rates, &rs.rates, rs.count);
+
+ /* merge rateset coming in with the current mcsset */
+ if (N_ENAB(wlc->pub)) {
+ if (bsscfg->associated)
+ bcopy(&current_bss->rateset.mcs[0],
+ rs.mcs, MCSSET_LEN);
+ else
+ bcopy(&wlc->default_bss->rateset.mcs[0],
+ rs.mcs, MCSSET_LEN);
+ }
+
+ bcmerror = wlc_set_rateset(wlc, &rs);
+
+ if (!bcmerror)
+ wlc_ofdm_rateset_war(wlc);
+
+ break;
+ }
+
+ case WLC_GET_BCNPRD:
+ if (BSSCFG_STA(bsscfg) && bsscfg->BSS && bsscfg->associated)
+ *pval = current_bss->beacon_period;
+ else
+ *pval = wlc->default_bss->beacon_period;
+ break;
+
+ case WLC_SET_BCNPRD:
+ /* range [1, 0xffff] */
+ if (val >= DOT11_MIN_BEACON_PERIOD
+ && val <= DOT11_MAX_BEACON_PERIOD) {
+ wlc->default_bss->beacon_period = (u16) val;
+ } else
+ bcmerror = BCME_RANGE;
+ break;
+
+ case WLC_GET_DTIMPRD:
+ if (BSSCFG_STA(bsscfg) && bsscfg->BSS && bsscfg->associated)
+ *pval = current_bss->dtim_period;
+ else
+ *pval = wlc->default_bss->dtim_period;
+ break;
+
+ case WLC_SET_DTIMPRD:
+ /* range [1, 0xff] */
+ if (val >= DOT11_MIN_DTIM_PERIOD
+ && val <= DOT11_MAX_DTIM_PERIOD) {
+ wlc->default_bss->dtim_period = (u8) val;
+ } else
+ bcmerror = BCME_RANGE;
+ break;
+
+#ifdef SUPPORT_PS
+ case WLC_GET_PM:
+ *pval = wlc->PM;
+ break;
+
+ case WLC_SET_PM:
+ if ((val >= PM_OFF) && (val <= PM_MAX)) {
+ wlc->PM = (u8) val;
+ if (wlc->pub->up) {
+ }
+ /* Change watchdog driver to align watchdog with tbtt if possible */
+ wlc_watchdog_upd(wlc, PS_ALLOWED(wlc));
+ } else
+ bcmerror = BCME_ERROR;
+ break;
+#endif /* SUPPORT_PS */
+
+#ifdef SUPPORT_PS
+#ifdef BCMDBG
+ case WLC_GET_WAKE:
+ if (AP_ENAB(wlc->pub)) {
+ bcmerror = BCME_NOTSTA;
+ break;
+ }
+ *pval = wlc->wake;
+ break;
+
+ case WLC_SET_WAKE:
+ if (AP_ENAB(wlc->pub)) {
+ bcmerror = BCME_NOTSTA;
+ break;
+ }
+
+ wlc->wake = val ? true : false;
+
+ /* if down, we're done */
+ if (!wlc->pub->up)
+ break;
+
+ /* apply to the mac */
+ wlc_set_ps_ctrl(wlc);
+ break;
+#endif /* BCMDBG */
+#endif /* SUPPORT_PS */
+
+ case WLC_GET_REVINFO:
+ bcmerror = wlc_get_revision_info(wlc, arg, (uint) len);
+ break;
+
+ case WLC_GET_AP:
+ *pval = (int)AP_ENAB(wlc->pub);
+ break;
+
+ case WLC_GET_ATIM:
+ if (bsscfg->associated)
+ *pval = (int)current_bss->atim_window;
+ else
+ *pval = (int)wlc->default_bss->atim_window;
+ break;
+
+ case WLC_SET_ATIM:
+ wlc->default_bss->atim_window = (u32) val;
+ break;
+
+ case WLC_GET_PKTCNTS:{
+ get_pktcnt_t *pktcnt = (get_pktcnt_t *) pval;
+ if (WLC_UPDATE_STATS(wlc))
+ wlc_statsupd(wlc);
+ pktcnt->rx_good_pkt = WLCNTVAL(wlc->pub->_cnt->rxframe);
+ pktcnt->rx_bad_pkt = WLCNTVAL(wlc->pub->_cnt->rxerror);
+ pktcnt->tx_good_pkt =
+ WLCNTVAL(wlc->pub->_cnt->txfrmsnt);
+ pktcnt->tx_bad_pkt =
+ WLCNTVAL(wlc->pub->_cnt->txerror) +
+ WLCNTVAL(wlc->pub->_cnt->txfail);
+ if (len >= (int)sizeof(get_pktcnt_t)) {
+ /* Be backward compatible - only if buffer is large enough */
+ pktcnt->rx_ocast_good_pkt =
+ WLCNTVAL(wlc->pub->_cnt->rxmfrmocast);
+ }
+ break;
+ }
+
+#ifdef SUPPORT_HWKEY
+ case WLC_GET_WSEC:
+ bcmerror =
+ wlc_iovar_op(wlc, "wsec", NULL, 0, arg, len, IOV_GET,
+ wlcif);
+ break;
+
+ case WLC_SET_WSEC:
+ bcmerror =
+ wlc_iovar_op(wlc, "wsec", NULL, 0, arg, len, IOV_SET,
+ wlcif);
+ break;
+
+ case WLC_GET_WPA_AUTH:
+ *pval = (int)bsscfg->WPA_auth;
+ break;
+
+ case WLC_SET_WPA_AUTH:
+ /* change of WPA_Auth modifies the PS_ALLOWED state */
+ if (BSSCFG_STA(bsscfg)) {
+ bsscfg->WPA_auth = (u16) val;
+ } else
+ bsscfg->WPA_auth = (u16) val;
+ break;
+#endif /* SUPPORT_HWKEY */
+
+ case WLC_GET_BANDLIST:
+ /* count of number of bands, followed by each band type */
+ *pval++ = NBANDS(wlc);
+ *pval++ = wlc->band->bandtype;
+ if (NBANDS(wlc) > 1)
+ *pval++ = wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype;
+ break;
+
+ case WLC_GET_BAND:
+ *pval = wlc->bandlocked ? wlc->band->bandtype : WLC_BAND_AUTO;
+ break;
+
+ case WLC_GET_PHYLIST:
+ {
+ unsigned char *cp = arg;
+ if (len < 3) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+
+ if (WLCISNPHY(wlc->band)) {
+ *cp++ = 'n';
+ } else if (WLCISLCNPHY(wlc->band)) {
+ *cp++ = 'c';
+ } else if (WLCISSSLPNPHY(wlc->band)) {
+ *cp++ = 's';
+ }
+ *cp = '\0';
+ break;
+ }
+
+ case WLC_GET_SHORTSLOT:
+ *pval = wlc->shortslot;
+ break;
+
+ case WLC_GET_SHORTSLOT_OVERRIDE:
+ *pval = wlc->shortslot_override;
+ break;
+
+ case WLC_SET_SHORTSLOT_OVERRIDE:
+ if ((val != WLC_SHORTSLOT_AUTO) &&
+ (val != WLC_SHORTSLOT_OFF) && (val != WLC_SHORTSLOT_ON)) {
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ wlc->shortslot_override = (s8) val;
+
+ /* shortslot is an 11g feature, so no more work if we are
+ * currently on the 5G band
+ */
+ if (BAND_5G(wlc->band->bandtype))
+ break;
+
+ if (wlc->pub->up && wlc->pub->associated) {
+ /* let watchdog or beacon processing update shortslot */
+ } else if (wlc->pub->up) {
+ /* unassociated shortslot is off */
+ wlc_switch_shortslot(wlc, false);
+ } else {
+ /* driver is down, so just update the wlc_info value */
+ if (wlc->shortslot_override == WLC_SHORTSLOT_AUTO) {
+ wlc->shortslot = false;
+ } else {
+ wlc->shortslot =
+ (wlc->shortslot_override ==
+ WLC_SHORTSLOT_ON);
+ }
+ }
+
+ break;
+
+ case WLC_GET_LEGACY_ERP:
+ *pval = wlc->include_legacy_erp;
+ break;
+
+ case WLC_SET_LEGACY_ERP:
+ if (wlc->include_legacy_erp == bool_val)
+ break;
+
+ wlc->include_legacy_erp = bool_val;
+
+ if (AP_ENAB(wlc->pub) && wlc->clk) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ }
+ break;
+
+ case WLC_GET_GMODE:
+ if (wlc->band->bandtype == WLC_BAND_2G)
+ *pval = wlc->band->gmode;
+ else if (NBANDS(wlc) > 1)
+ *pval = wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode;
+ break;
+
+ case WLC_SET_GMODE:
+ if (!wlc->pub->associated)
+ bcmerror = wlc_set_gmode(wlc, (u8) val, true);
+ else {
+ bcmerror = BCME_ASSOCIATED;
+ break;
+ }
+ break;
+
+ case WLC_GET_GMODE_PROTECTION:
+ *pval = wlc->protection->_g;
+ break;
+
+ case WLC_GET_PROTECTION_CONTROL:
+ *pval = wlc->protection->overlap;
+ break;
+
+ case WLC_SET_PROTECTION_CONTROL:
+ if ((val != WLC_PROTECTION_CTL_OFF) &&
+ (val != WLC_PROTECTION_CTL_LOCAL) &&
+ (val != WLC_PROTECTION_CTL_OVERLAP)) {
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ wlc_protection_upd(wlc, WLC_PROT_OVERLAP, (s8) val);
+
+ /* Current g_protection will sync up to the specified control alg in watchdog
+ * if the driver is up and associated.
+ * If the driver is down or not associated, the control setting has no effect.
+ */
+ break;
+
+ case WLC_GET_GMODE_PROTECTION_OVERRIDE:
+ *pval = wlc->protection->g_override;
+ break;
+
+ case WLC_SET_GMODE_PROTECTION_OVERRIDE:
+ if ((val != WLC_PROTECTION_AUTO) &&
+ (val != WLC_PROTECTION_OFF) && (val != WLC_PROTECTION_ON)) {
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ wlc_protection_upd(wlc, WLC_PROT_G_OVR, (s8) val);
+
+ break;
+
+ case WLC_SET_SUP_RATESET_OVERRIDE:{
+ wlc_rateset_t rs, new;
+
+ /* copyin */
+ if (len < (int)sizeof(wlc_rateset_t)) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+ bcopy((char *)arg, (char *)&rs, sizeof(wlc_rateset_t));
+
+ /* check for bad count value */
+ if (rs.count > WLC_NUMRATES) {
+ bcmerror = BCME_BADRATESET; /* invalid rateset */
+ break;
+ }
+
+ /* this command is only appropriate for gmode operation */
+ if (!(wlc->band->gmode ||
+ ((NBANDS(wlc) > 1)
+ && wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode))) {
+ bcmerror = BCME_BADBAND; /* gmode only command when not in gmode */
+ break;
+ }
+
+ /* check for an empty rateset to clear the override */
+ if (rs.count == 0) {
+ bzero(&wlc->sup_rates_override,
+ sizeof(wlc_rateset_t));
+ break;
+ }
+
+ /* validate rateset by comparing pre and post sorted against 11g hw rates */
+ wlc_rateset_filter(&rs, &new, false, WLC_RATES_CCK_OFDM,
+ RATE_MASK, BSS_N_ENAB(wlc, bsscfg));
+ wlc_rate_hwrs_filter_sort_validate(&new,
+ &cck_ofdm_rates,
+ false,
+ wlc->stf->txstreams);
+ if (rs.count != new.count) {
+ bcmerror = BCME_BADRATESET; /* invalid rateset */
+ break;
+ }
+
+ /* apply new rateset to the override */
+ bcopy((char *)&new, (char *)&wlc->sup_rates_override,
+ sizeof(wlc_rateset_t));
+
+ /* update bcn and probe resp if needed */
+ if (wlc->pub->up && AP_ENAB(wlc->pub)
+ && wlc->pub->associated) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ }
+ break;
+ }
+
+ case WLC_GET_SUP_RATESET_OVERRIDE:
+ /* this command is only appropriate for gmode operation */
+ if (!(wlc->band->gmode ||
+ ((NBANDS(wlc) > 1)
+ && wlc->bandstate[OTHERBANDUNIT(wlc)]->gmode))) {
+ bcmerror = BCME_BADBAND; /* gmode only command when not in gmode */
+ break;
+ }
+ if (len < (int)sizeof(wlc_rateset_t)) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+ bcopy((char *)&wlc->sup_rates_override, (char *)arg,
+ sizeof(wlc_rateset_t));
+
+ break;
+
+ case WLC_GET_PRB_RESP_TIMEOUT:
+ *pval = wlc->prb_resp_timeout;
+ break;
+
+ case WLC_SET_PRB_RESP_TIMEOUT:
+ if (wlc->pub->up) {
+ bcmerror = BCME_NOTDOWN;
+ break;
+ }
+ if (val < 0 || val >= 0xFFFF) {
+ bcmerror = BCME_RANGE; /* bad value */
+ break;
+ }
+ wlc->prb_resp_timeout = (u16) val;
+ break;
+
+ case WLC_GET_KEY_PRIMARY:{
+ wsec_key_t *key;
+
+ /* treat the 'val' parm as the key id */
+ key = WSEC_BSS_DEFAULT_KEY(bsscfg);
+ if (key != NULL) {
+ *pval = key->id == val ? true : false;
+ } else {
+ bcmerror = BCME_BADKEYIDX;
+ }
+ break;
+ }
+
+ case WLC_SET_KEY_PRIMARY:{
+ wsec_key_t *key, *old_key;
+
+ bcmerror = BCME_BADKEYIDX;
+
+ /* treat the 'val' parm as the key id */
+ for (i = 0; i < WSEC_MAX_DEFAULT_KEYS; i++) {
+ key = bsscfg->bss_def_keys[i];
+ if (key != NULL && key->id == val) {
+ old_key = WSEC_BSS_DEFAULT_KEY(bsscfg);
+ if (old_key != NULL)
+ old_key->flags &=
+ ~WSEC_PRIMARY_KEY;
+ key->flags |= WSEC_PRIMARY_KEY;
+ bsscfg->wsec_index = i;
+ bcmerror = BCME_OK;
+ }
+ }
+ break;
+ }
+
+#ifdef BCMDBG
+ case WLC_INIT:
+ wl_init(wlc->wl);
+ break;
+#endif
+
+ case WLC_SET_VAR:
+ case WLC_GET_VAR:{
+ char *name;
+ /* validate the name value */
+ name = (char *)arg;
+ for (i = 0; i < (uint) len && *name != '\0';
+ i++, name++)
+ ;
+
+ if (i == (uint) len) {
+ bcmerror = BCME_BUFTOOSHORT;
+ break;
+ }
+ i++; /* include the null in the string length */
+
+ if (cmd == WLC_GET_VAR) {
+ bcmerror =
+ wlc_iovar_op(wlc, arg,
+ (void *)((s8 *) arg + i),
+ len - i, arg, len, IOV_GET,
+ wlcif);
+ } else
+ bcmerror =
+ wlc_iovar_op(wlc, arg, NULL, 0,
+ (void *)((s8 *) arg + i),
+ len - i, IOV_SET, wlcif);
+
+ break;
+ }
+
+ case WLC_SET_WSEC_PMK:
+ bcmerror = BCME_UNSUPPORTED;
+ break;
+
+#if defined(BCMDBG)
+ case WLC_CURRENT_PWR:
+ if (!wlc->pub->up)
+ bcmerror = BCME_NOTUP;
+ else
+ bcmerror = wlc_get_current_txpwr(wlc, arg, len);
+ break;
+#endif
+
+ case WLC_LAST:
+ WL_ERROR(("%s: WLC_LAST\n", __func__));
+ }
+ done:
+
+ if (bcmerror) {
+ if (VALID_BCMERROR(bcmerror))
+ wlc->pub->bcmerror = bcmerror;
+ else {
+ bcmerror = 0;
+ }
+
+ }
+#ifdef WLC_LOW
+ /* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
+ /* In hw_off condition, IOCTLs that reach here are deemed safe but taclear would
+ * certainly result in getting -1 for register reads. So skip ta_clear altogether
+ */
+ if (!(wlc->pub->hw_off))
+ ASSERT(wlc_bmac_taclear(wlc->hw, ta_ok) || !ta_ok);
+#endif
+
+ return bcmerror;
+}
+
+#if defined(BCMDBG)
+/* consolidated register access ioctl error checking */
+int wlc_iocregchk(wlc_info_t *wlc, uint band)
+{
+ /* if band is specified, it must be the current band */
+ if ((band != WLC_BAND_AUTO) && (band != (uint) wlc->band->bandtype))
+ return BCME_BADBAND;
+
+ /* if multiband and band is not specified, band must be locked */
+ if ((band == WLC_BAND_AUTO) && IS_MBAND_UNLOCKED(wlc))
+ return BCME_NOTBANDLOCKED;
+
+ /* must have core clocks */
+ if (!wlc->clk)
+ return BCME_NOCLK;
+
+ return 0;
+}
+#endif /* defined(BCMDBG) */
+
+#if defined(BCMDBG)
+/* For some ioctls, make sure that the pi pointer matches the current phy */
+int wlc_iocpichk(wlc_info_t *wlc, uint phytype)
+{
+ if (wlc->band->phytype != phytype)
+ return BCME_BADBAND;
+ return 0;
+}
+#endif
+
+/* Look up the given var name in the given table */
+static const bcm_iovar_t *wlc_iovar_lookup(const bcm_iovar_t *table,
+ const char *name)
+{
+ const bcm_iovar_t *vi;
+ const char *lookup_name;
+
+ /* skip any ':' delimited option prefixes */
+ lookup_name = strrchr(name, ':');
+ if (lookup_name != NULL)
+ lookup_name++;
+ else
+ lookup_name = name;
+
+ ASSERT(table != NULL);
+
+ for (vi = table; vi->name; vi++) {
+ if (!strcmp(vi->name, lookup_name))
+ return vi;
+ }
+ /* ran to end of table */
+
+ return NULL; /* var name not found */
+}
+
+/* simplified integer get interface for common WLC_GET_VAR ioctl handler */
+int wlc_iovar_getint(wlc_info_t *wlc, const char *name, int *arg)
+{
+ return wlc_iovar_op(wlc, name, NULL, 0, arg, sizeof(s32), IOV_GET,
+ NULL);
+}
+
+/* simplified integer set interface for common WLC_SET_VAR ioctl handler */
+int wlc_iovar_setint(wlc_info_t *wlc, const char *name, int arg)
+{
+ return wlc_iovar_op(wlc, name, NULL, 0, (void *)&arg, sizeof(arg),
+ IOV_SET, NULL);
+}
+
+/* simplified s8 get interface for common WLC_GET_VAR ioctl handler */
+int wlc_iovar_gets8(wlc_info_t *wlc, const char *name, s8 *arg)
+{
+ int iovar_int;
+ int err;
+
+ err =
+ wlc_iovar_op(wlc, name, NULL, 0, &iovar_int, sizeof(iovar_int),
+ IOV_GET, NULL);
+ if (!err)
+ *arg = (s8) iovar_int;
+
+ return err;
+}
+
+/*
+ * register iovar table, watchdog and down handlers.
+ * calling function must keep 'iovars' until wlc_module_unregister is called.
+ * 'iovar' must have the last entry's name field being NULL as terminator.
+ */
+int wlc_module_register(wlc_pub_t *pub, const bcm_iovar_t *iovars,
+ const char *name, void *hdl, iovar_fn_t i_fn,
+ watchdog_fn_t w_fn, down_fn_t d_fn)
+{
+ wlc_info_t *wlc = (wlc_info_t *) pub->wlc;
+ int i;
+
+ ASSERT(name != NULL);
+ ASSERT(i_fn != NULL || w_fn != NULL || d_fn != NULL);
+
+ /* find an empty entry and just add, no duplication check! */
+ for (i = 0; i < WLC_MAXMODULES; i++) {
+ if (wlc->modulecb[i].name[0] == '\0') {
+ strncpy(wlc->modulecb[i].name, name,
+ sizeof(wlc->modulecb[i].name) - 1);
+ wlc->modulecb[i].iovars = iovars;
+ wlc->modulecb[i].hdl = hdl;
+ wlc->modulecb[i].iovar_fn = i_fn;
+ wlc->modulecb[i].watchdog_fn = w_fn;
+ wlc->modulecb[i].down_fn = d_fn;
+ return 0;
+ }
+ }
+
+ /* it is time to increase the capacity */
+ ASSERT(i < WLC_MAXMODULES);
+ return BCME_NORESOURCE;
+}
+
+/* unregister module callbacks */
+int wlc_module_unregister(wlc_pub_t *pub, const char *name, void *hdl)
+{
+ wlc_info_t *wlc = (wlc_info_t *) pub->wlc;
+ int i;
+
+ if (wlc == NULL)
+ return BCME_NOTFOUND;
+
+ ASSERT(name != NULL);
+
+ for (i = 0; i < WLC_MAXMODULES; i++) {
+ if (!strcmp(wlc->modulecb[i].name, name) &&
+ (wlc->modulecb[i].hdl == hdl)) {
+ bzero(&wlc->modulecb[i], sizeof(modulecb_t));
+ return 0;
+ }
+ }
+
+ /* table not found! */
+ return BCME_NOTFOUND;
+}
+
+/* Write WME tunable parameters for retransmit/max rate from wlc struct to ucode */
+static void wlc_wme_retries_write(wlc_info_t *wlc)
+{
+ int ac;
+
+ /* Need clock to do this */
+ if (!wlc->clk)
+ return;
+
+ for (ac = 0; ac < AC_COUNT; ac++) {
+ wlc_write_shm(wlc, M_AC_TXLMT_ADDR(ac), wlc->wme_retries[ac]);
+ }
+}
+
+/* Get or set an iovar. The params/p_len pair specifies any additional
+ * qualifying parameters (e.g. an "element index") for a get, while the
+ * arg/len pair is the buffer for the value to be set or retrieved.
+ * Operation (get/set) is specified by the last argument.
+ * interface context provided by wlcif
+ *
+ * All pointers may point into the same buffer.
+ */
+int
+wlc_iovar_op(wlc_info_t *wlc, const char *name,
+ void *params, int p_len, void *arg, int len,
+ bool set, struct wlc_if *wlcif)
+{
+ int err = 0;
+ int val_size;
+ const bcm_iovar_t *vi = NULL;
+ u32 actionid;
+ int i;
+
+ ASSERT(name != NULL);
+
+ ASSERT(len >= 0);
+
+ /* Get MUST have return space */
+ ASSERT(set || (arg && len));
+
+ ASSERT(!(wlc->pub->hw_off && wlc->pub->up));
+
+ /* Set does NOT take qualifiers */
+ ASSERT(!set || (!params && !p_len));
+
+ if (!set && (len == sizeof(int)) &&
+ !(IS_ALIGNED((unsigned long)(arg), (uint) sizeof(int)))) {
+ WL_ERROR(("wl%d: %s unaligned get ptr for %s\n",
+ wlc->pub->unit, __func__, name));
+ ASSERT(0);
+ }
+
+ /* find the given iovar name */
+ for (i = 0; i < WLC_MAXMODULES; i++) {
+ if (!wlc->modulecb[i].iovars)
+ continue;
+ vi = wlc_iovar_lookup(wlc->modulecb[i].iovars, name);
+ if (vi)
+ break;
+ }
+ /* iovar name not found */
+ if (i >= WLC_MAXMODULES) {
+ err = BCME_UNSUPPORTED;
+#ifdef WLC_HIGH_ONLY
+ err =
+ bcmsdh_iovar_op(wlc->btparam, name, params, p_len, arg, len,
+ set);
+#endif
+ goto exit;
+ }
+
+ /* set up 'params' pointer in case this is a set command so that
+ * the convenience int and bool code can be common to set and get
+ */
+ if (params == NULL) {
+ params = arg;
+ p_len = len;
+ }
+
+ if (vi->type == IOVT_VOID)
+ val_size = 0;
+ else if (vi->type == IOVT_BUFFER)
+ val_size = len;
+ else
+ /* all other types are integer sized */
+ val_size = sizeof(int);
+
+ actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+
+ /* Do the actual parameter implementation */
+ err = wlc->modulecb[i].iovar_fn(wlc->modulecb[i].hdl, vi, actionid,
+ name, params, p_len, arg, len, val_size,
+ wlcif);
+
+ exit:
+ return err;
+}
+
+int
+wlc_iovar_check(wlc_pub_t *pub, const bcm_iovar_t *vi, void *arg, int len,
+ bool set)
+{
+ wlc_info_t *wlc = (wlc_info_t *) pub->wlc;
+ int err = 0;
+ s32 int_val = 0;
+
+ /* check generic condition flags */
+ if (set) {
+ if (((vi->flags & IOVF_SET_DOWN) && wlc->pub->up) ||
+ ((vi->flags & IOVF_SET_UP) && !wlc->pub->up)) {
+ err = (wlc->pub->up ? BCME_NOTDOWN : BCME_NOTUP);
+ } else if ((vi->flags & IOVF_SET_BAND)
+ && IS_MBAND_UNLOCKED(wlc)) {
+ err = BCME_NOTBANDLOCKED;
+ } else if ((vi->flags & IOVF_SET_CLK) && !wlc->clk) {
+ err = BCME_NOCLK;
+ }
+ } else {
+ if (((vi->flags & IOVF_GET_DOWN) && wlc->pub->up) ||
+ ((vi->flags & IOVF_GET_UP) && !wlc->pub->up)) {
+ err = (wlc->pub->up ? BCME_NOTDOWN : BCME_NOTUP);
+ } else if ((vi->flags & IOVF_GET_BAND)
+ && IS_MBAND_UNLOCKED(wlc)) {
+ err = BCME_NOTBANDLOCKED;
+ } else if ((vi->flags & IOVF_GET_CLK) && !wlc->clk) {
+ err = BCME_NOCLK;
+ }
+ }
+
+ if (err)
+ goto exit;
+
+ /* length check on io buf */
+ err = bcm_iovar_lencheck(vi, arg, len, set);
+ if (err)
+ goto exit;
+
+ /* On set, check value ranges for integer types */
+ if (set) {
+ switch (vi->type) {
+ case IOVT_BOOL:
+ case IOVT_INT8:
+ case IOVT_INT16:
+ case IOVT_INT32:
+ case IOVT_UINT8:
+ case IOVT_UINT16:
+ case IOVT_UINT32:
+ bcopy(arg, &int_val, sizeof(int));
+ err = wlc_iovar_rangecheck(wlc, int_val, vi);
+ break;
+ }
+ }
+ exit:
+ return err;
+}
+
+/* handler for iovar table wlc_iovars */
+/*
+ * IMPLEMENTATION NOTE: In order to avoid checking for get/set in each
+ * iovar case, the switch statement maps the iovar id into separate get
+ * and set values. If you add a new iovar to the switch you MUST use
+ * IOV_GVAL and/or IOV_SVAL in the case labels to avoid conflict with
+ * another case.
+ * Please use params for additional qualifying parameters.
+ */
+int
+wlc_doiovar(void *hdl, const bcm_iovar_t *vi, u32 actionid,
+ const char *name, void *params, uint p_len, void *arg, int len,
+ int val_size, struct wlc_if *wlcif)
+{
+ wlc_info_t *wlc = hdl;
+ wlc_bsscfg_t *bsscfg;
+ int err = 0;
+ s32 int_val = 0;
+ s32 int_val2 = 0;
+ s32 *ret_int_ptr;
+ bool bool_val;
+ bool bool_val2;
+ wlc_bss_info_t *current_bss;
+
+ WL_TRACE(("wl%d: %s\n", wlc->pub->unit, __func__));
+
+ bsscfg = NULL;
+ current_bss = NULL;
+
+ err = wlc_iovar_check(wlc->pub, vi, arg, len, IOV_ISSET(actionid));
+ if (err != 0)
+ return err;
+
+ /* convenience int and bool vals for first 8 bytes of buffer */
+ if (p_len >= (int)sizeof(int_val))
+ bcopy(params, &int_val, sizeof(int_val));
+
+ if (p_len >= (int)sizeof(int_val) * 2)
+ bcopy((void *)((unsigned long)params + sizeof(int_val)), &int_val2,
+ sizeof(int_val));
+
+ /* convenience int ptr for 4-byte gets (requires int aligned arg) */
+ ret_int_ptr = (s32 *) arg;
+
+ bool_val = (int_val != 0) ? true : false;
+ bool_val2 = (int_val2 != 0) ? true : false;
+
+ WL_TRACE(("wl%d: %s: id %d\n", wlc->pub->unit, __func__,
+ IOV_ID(actionid)));
+ /* Do the actual parameter implementation */
+ switch (actionid) {
+
+ case IOV_GVAL(IOV_QTXPOWER):{
+ uint qdbm;
+ bool override;
+
+ err = wlc_phy_txpower_get(wlc->band->pi, &qdbm,
+ &override);
+ if (err != BCME_OK)
+ return err;
+
+ /* Return qdbm units */
+ *ret_int_ptr =
+ qdbm | (override ? WL_TXPWR_OVERRIDE : 0);
+ break;
+ }
+
+ /* As long as override is false, this only sets the *user* targets.
+ User can twiddle this all he wants with no harm.
+ wlc_phy_txpower_set() explicitly sets override to false if
+ not internal or test.
+ */
+ case IOV_SVAL(IOV_QTXPOWER):{
+ u8 qdbm;
+ bool override;
+
+ /* Remove override bit and clip to max qdbm value */
+ qdbm = (u8)min_t(u32, (int_val & ~WL_TXPWR_OVERRIDE), 0xff);
+ /* Extract override setting */
+ override = (int_val & WL_TXPWR_OVERRIDE) ? true : false;
+ err =
+ wlc_phy_txpower_set(wlc->band->pi, qdbm, override);
+ break;
+ }
+
+ case IOV_GVAL(IOV_MPC):
+ *ret_int_ptr = (s32) wlc->mpc;
+ break;
+
+ case IOV_SVAL(IOV_MPC):
+ wlc->mpc = bool_val;
+ wlc_radio_mpc_upd(wlc);
+
+ break;
+
+ case IOV_GVAL(IOV_BCN_LI_BCN):
+ *ret_int_ptr = wlc->bcn_li_bcn;
+ break;
+
+ case IOV_SVAL(IOV_BCN_LI_BCN):
+ wlc->bcn_li_bcn = (u8) int_val;
+ if (wlc->pub->up)
+ wlc_bcn_li_upd(wlc);
+ break;
+
+ default:
+ WL_ERROR(("wl%d: %s: unsupported\n", wlc->pub->unit, __func__));
+ err = BCME_UNSUPPORTED;
+ break;
+ }
+
+ goto exit; /* avoid unused label warning */
+
+ exit:
+ return err;
+}
+
+static int
+wlc_iovar_rangecheck(wlc_info_t *wlc, u32 val, const bcm_iovar_t *vi)
+{
+ int err = 0;
+ u32 min_val = 0;
+ u32 max_val = 0;
+
+ /* Only ranged integers are checked */
+ switch (vi->type) {
+ case IOVT_INT32:
+ max_val |= 0x7fffffff;
+ /* fall through */
+ case IOVT_INT16:
+ max_val |= 0x00007fff;
+ /* fall through */
+ case IOVT_INT8:
+ max_val |= 0x0000007f;
+ min_val = ~max_val;
+ if (vi->flags & IOVF_NTRL)
+ min_val = 1;
+ else if (vi->flags & IOVF_WHL)
+ min_val = 0;
+ /* Signed values are checked against max_val and min_val */
+ if ((s32) val < (s32) min_val
+ || (s32) val > (s32) max_val)
+ err = BCME_RANGE;
+ break;
+
+ case IOVT_UINT32:
+ max_val |= 0xffffffff;
+ /* fall through */
+ case IOVT_UINT16:
+ max_val |= 0x0000ffff;
+ /* fall through */
+ case IOVT_UINT8:
+ max_val |= 0x000000ff;
+ if (vi->flags & IOVF_NTRL)
+ min_val = 1;
+ if ((val < min_val) || (val > max_val))
+ err = BCME_RANGE;
+ break;
+ }
+
+ return err;
+}
+
+#ifdef BCMDBG
+static const char *supr_reason[] = {
+ "None", "PMQ Entry", "Flush request",
+ "Previous frag failure", "Channel mismatch",
+ "Lifetime Expiry", "Underflow"
+};
+
+static void wlc_print_txs_status(u16 s)
+{
+ printf("[15:12] %d frame attempts\n", (s & TX_STATUS_FRM_RTX_MASK) >>
+ TX_STATUS_FRM_RTX_SHIFT);
+ printf(" [11:8] %d rts attempts\n", (s & TX_STATUS_RTS_RTX_MASK) >>
+ TX_STATUS_RTS_RTX_SHIFT);
+ printf(" [7] %d PM mode indicated\n",
+ ((s & TX_STATUS_PMINDCTD) ? 1 : 0));
+ printf(" [6] %d intermediate status\n",
+ ((s & TX_STATUS_INTERMEDIATE) ? 1 : 0));
+ printf(" [5] %d AMPDU\n", (s & TX_STATUS_AMPDU) ? 1 : 0);
+ printf(" [4:2] %d Frame Suppressed Reason (%s)\n",
+ ((s & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT),
+ supr_reason[(s & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT]);
+ printf(" [1] %d acked\n", ((s & TX_STATUS_ACK_RCV) ? 1 : 0));
+}
+#endif /* BCMDBG */
+
+void wlc_print_txstatus(tx_status_t *txs)
+{
+#if defined(BCMDBG)
+ u16 s = txs->status;
+ u16 ackphyrxsh = txs->ackphyrxsh;
+
+ printf("\ntxpkt (MPDU) Complete\n");
+
+ printf("FrameID: %04x ", txs->frameid);
+ printf("TxStatus: %04x", s);
+ printf("\n");
+#ifdef BCMDBG
+ wlc_print_txs_status(s);
+#endif
+ printf("LastTxTime: %04x ", txs->lasttxtime);
+ printf("Seq: %04x ", txs->sequence);
+ printf("PHYTxStatus: %04x ", txs->phyerr);
+ printf("RxAckRSSI: %04x ",
+ (ackphyrxsh & PRXS1_JSSI_MASK) >> PRXS1_JSSI_SHIFT);
+ printf("RxAckSQ: %04x", (ackphyrxsh & PRXS1_SQ_MASK) >> PRXS1_SQ_SHIFT);
+ printf("\n");
+#endif /* defined(BCMDBG) */
+}
+
+#define MACSTATUPD(name) \
+ wlc_ctrupd_cache(macstats.name, &wlc->core->macstat_snapshot->name, &wlc->pub->_cnt->name)
+
+void wlc_statsupd(wlc_info_t *wlc)
+{
+ int i;
+#ifdef BCMDBG
+ u16 delta;
+ u16 rxf0ovfl;
+ u16 txfunfl[NFIFO];
+#endif /* BCMDBG */
+
+ /* if driver down, make no sense to update stats */
+ if (!wlc->pub->up)
+ return;
+
+#ifdef BCMDBG
+ /* save last rx fifo 0 overflow count */
+ rxf0ovfl = wlc->core->macstat_snapshot->rxf0ovfl;
+
+ /* save last tx fifo underflow count */
+ for (i = 0; i < NFIFO; i++)
+ txfunfl[i] = wlc->core->macstat_snapshot->txfunfl[i];
+#endif /* BCMDBG */
+
+#ifdef BCMDBG
+ /* check for rx fifo 0 overflow */
+ delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl);
+ if (delta)
+ WL_ERROR(("wl%d: %u rx fifo 0 overflows!\n", wlc->pub->unit,
+ delta));
+
+ /* check for tx fifo underflows */
+ for (i = 0; i < NFIFO; i++) {
+ delta =
+ (u16) (wlc->core->macstat_snapshot->txfunfl[i] -
+ txfunfl[i]);
+ if (delta)
+ WL_ERROR(("wl%d: %u tx fifo %d underflows!\n",
+ wlc->pub->unit, delta, i));
+ }
+#endif /* BCMDBG */
+
+ /* dot11 counter update */
+
+ WLCNTSET(wlc->pub->_cnt->txrts,
+ (wlc->pub->_cnt->rxctsucast -
+ wlc->pub->_cnt->d11cnt_txrts_off));
+ WLCNTSET(wlc->pub->_cnt->rxcrc,
+ (wlc->pub->_cnt->rxbadfcs - wlc->pub->_cnt->d11cnt_rxcrc_off));
+ WLCNTSET(wlc->pub->_cnt->txnocts,
+ ((wlc->pub->_cnt->txrtsfrm - wlc->pub->_cnt->rxctsucast) -
+ wlc->pub->_cnt->d11cnt_txnocts_off));
+
+ /* merge counters from dma module */
+ for (i = 0; i < NFIFO; i++) {
+ if (wlc->hw->di[i]) {
+ WLCNTADD(wlc->pub->_cnt->txnobuf,
+ (wlc->hw->di[i])->txnobuf);
+ WLCNTADD(wlc->pub->_cnt->rxnobuf,
+ (wlc->hw->di[i])->rxnobuf);
+ WLCNTADD(wlc->pub->_cnt->rxgiant,
+ (wlc->hw->di[i])->rxgiants);
+ dma_counterreset(wlc->hw->di[i]);
+ }
+ }
+
+ /*
+ * Aggregate transmit and receive errors that probably resulted
+ * in the loss of a frame are computed on the fly.
+ */
+ WLCNTSET(wlc->pub->_cnt->txerror,
+ wlc->pub->_cnt->txnobuf + wlc->pub->_cnt->txnoassoc +
+ wlc->pub->_cnt->txuflo + wlc->pub->_cnt->txrunt +
+ wlc->pub->_cnt->dmade + wlc->pub->_cnt->dmada +
+ wlc->pub->_cnt->dmape);
+ WLCNTSET(wlc->pub->_cnt->rxerror,
+ wlc->pub->_cnt->rxoflo + wlc->pub->_cnt->rxnobuf +
+ wlc->pub->_cnt->rxfragerr + wlc->pub->_cnt->rxrunt +
+ wlc->pub->_cnt->rxgiant + wlc->pub->_cnt->rxnoscb +
+ wlc->pub->_cnt->rxbadsrcmac);
+ for (i = 0; i < NFIFO; i++)
+ WLCNTADD(wlc->pub->_cnt->rxerror, wlc->pub->_cnt->rxuflo[i]);
+}
+
+bool wlc_chipmatch(u16 vendor, u16 device)
+{
+ if (vendor != VENDOR_BROADCOM) {
+ WL_ERROR(("wlc_chipmatch: unknown vendor id %04x\n", vendor));
+ return false;
+ }
+
+ if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID))
+ return true;
+
+ if (device == BCM4313_D11N2G_ID)
+ return true;
+ if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID))
+ return true;
+
+ WL_ERROR(("wlc_chipmatch: unknown device id %04x\n", device));
+ return false;
+}
+
+#if defined(BCMDBG)
+void wlc_print_txdesc(d11txh_t *txh)
+{
+ u16 mtcl = ltoh16(txh->MacTxControlLow);
+ u16 mtch = ltoh16(txh->MacTxControlHigh);
+ u16 mfc = ltoh16(txh->MacFrameControl);
+ u16 tfest = ltoh16(txh->TxFesTimeNormal);
+ u16 ptcw = ltoh16(txh->PhyTxControlWord);
+ u16 ptcw_1 = ltoh16(txh->PhyTxControlWord_1);
+ u16 ptcw_1_Fbr = ltoh16(txh->PhyTxControlWord_1_Fbr);
+ u16 ptcw_1_Rts = ltoh16(txh->PhyTxControlWord_1_Rts);
+ u16 ptcw_1_FbrRts = ltoh16(txh->PhyTxControlWord_1_FbrRts);
+ u16 mainrates = ltoh16(txh->MainRates);
+ u16 xtraft = ltoh16(txh->XtraFrameTypes);
+ u8 *iv = txh->IV;
+ u8 *ra = txh->TxFrameRA;
+ u16 tfestfb = ltoh16(txh->TxFesTimeFallback);
+ u8 *rtspfb = txh->RTSPLCPFallback;
+ u16 rtsdfb = ltoh16(txh->RTSDurFallback);
+ u8 *fragpfb = txh->FragPLCPFallback;
+ u16 fragdfb = ltoh16(txh->FragDurFallback);
+ u16 mmodelen = ltoh16(txh->MModeLen);
+ u16 mmodefbrlen = ltoh16(txh->MModeFbrLen);
+ u16 tfid = ltoh16(txh->TxFrameID);
+ u16 txs = ltoh16(txh->TxStatus);
+ u16 mnmpdu = ltoh16(txh->MaxNMpdus);
+ u16 mabyte = ltoh16(txh->MaxABytes_MRT);
+ u16 mabyte_f = ltoh16(txh->MaxABytes_FBR);
+ u16 mmbyte = ltoh16(txh->MinMBytes);
+
+ u8 *rtsph = txh->RTSPhyHeader;
+ struct dot11_rts_frame rts = txh->rts_frame;
+ char hexbuf[256];
+
+ /* add plcp header along with txh descriptor */
+ prhex("Raw TxDesc + plcp header", (unsigned char *) txh, sizeof(d11txh_t) + 48);
+
+ printf("TxCtlLow: %04x ", mtcl);
+ printf("TxCtlHigh: %04x ", mtch);
+ printf("FC: %04x ", mfc);
+ printf("FES Time: %04x\n", tfest);
+ printf("PhyCtl: %04x%s ", ptcw,
+ (ptcw & PHY_TXC_SHORT_HDR) ? " short" : "");
+ printf("PhyCtl_1: %04x ", ptcw_1);
+ printf("PhyCtl_1_Fbr: %04x\n", ptcw_1_Fbr);
+ printf("PhyCtl_1_Rts: %04x ", ptcw_1_Rts);
+ printf("PhyCtl_1_Fbr_Rts: %04x\n", ptcw_1_FbrRts);
+ printf("MainRates: %04x ", mainrates);
+ printf("XtraFrameTypes: %04x ", xtraft);
+ printf("\n");
+
+ bcm_format_hex(hexbuf, iv, sizeof(txh->IV));
+ printf("SecIV: %s\n", hexbuf);
+ bcm_format_hex(hexbuf, ra, sizeof(txh->TxFrameRA));
+ printf("RA: %s\n", hexbuf);
+
+ printf("Fb FES Time: %04x ", tfestfb);
+ bcm_format_hex(hexbuf, rtspfb, sizeof(txh->RTSPLCPFallback));
+ printf("RTS PLCP: %s ", hexbuf);
+ printf("RTS DUR: %04x ", rtsdfb);
+ bcm_format_hex(hexbuf, fragpfb, sizeof(txh->FragPLCPFallback));
+ printf("PLCP: %s ", hexbuf);
+ printf("DUR: %04x", fragdfb);
+ printf("\n");
+
+ printf("MModeLen: %04x ", mmodelen);
+ printf("MModeFbrLen: %04x\n", mmodefbrlen);
+
+ printf("FrameID: %04x\n", tfid);
+ printf("TxStatus: %04x\n", txs);
+
+ printf("MaxNumMpdu: %04x\n", mnmpdu);
+ printf("MaxAggbyte: %04x\n", mabyte);
+ printf("MaxAggbyte_fb: %04x\n", mabyte_f);
+ printf("MinByte: %04x\n", mmbyte);
+
+ bcm_format_hex(hexbuf, rtsph, sizeof(txh->RTSPhyHeader));
+ printf("RTS PLCP: %s ", hexbuf);
+ bcm_format_hex(hexbuf, (u8 *) &rts, sizeof(txh->rts_frame));
+ printf("RTS Frame: %s", hexbuf);
+ printf("\n");
+
+}
+#endif /* defined(BCMDBG) */
+
+#if defined(BCMDBG)
+void wlc_print_rxh(d11rxhdr_t *rxh)
+{
+ u16 len = rxh->RxFrameSize;
+ u16 phystatus_0 = rxh->PhyRxStatus_0;
+ u16 phystatus_1 = rxh->PhyRxStatus_1;
+ u16 phystatus_2 = rxh->PhyRxStatus_2;
+ u16 phystatus_3 = rxh->PhyRxStatus_3;
+ u16 macstatus1 = rxh->RxStatus1;
+ u16 macstatus2 = rxh->RxStatus2;
+ char flagstr[64];
+ char lenbuf[20];
+ static const bcm_bit_desc_t macstat_flags[] = {
+ {RXS_FCSERR, "FCSErr"},
+ {RXS_RESPFRAMETX, "Reply"},
+ {RXS_PBPRES, "PADDING"},
+ {RXS_DECATMPT, "DeCr"},
+ {RXS_DECERR, "DeCrErr"},
+ {RXS_BCNSENT, "Bcn"},
+ {0, NULL}
+ };
+
+ prhex("Raw RxDesc", (unsigned char *) rxh, sizeof(d11rxhdr_t));
+
+ bcm_format_flags(macstat_flags, macstatus1, flagstr, 64);
+
+ snprintf(lenbuf, sizeof(lenbuf), "0x%x", len);
+
+ printf("RxFrameSize: %6s (%d)%s\n", lenbuf, len,
+ (rxh->PhyRxStatus_0 & PRXS0_SHORTH) ? " short preamble" : "");
+ printf("RxPHYStatus: %04x %04x %04x %04x\n",
+ phystatus_0, phystatus_1, phystatus_2, phystatus_3);
+ printf("RxMACStatus: %x %s\n", macstatus1, flagstr);
+ printf("RXMACaggtype: %x\n", (macstatus2 & RXS_AGGTYPE_MASK));
+ printf("RxTSFTime: %04x\n", rxh->RxTSFTime);
+}
+#endif /* defined(BCMDBG) */
+
+#if defined(BCMDBG)
+int wlc_format_ssid(char *buf, const unsigned char ssid[], uint ssid_len)
+{
+ uint i, c;
+ char *p = buf;
+ char *endp = buf + SSID_FMT_BUF_LEN;
+
+ if (ssid_len > DOT11_MAX_SSID_LEN)
+ ssid_len = DOT11_MAX_SSID_LEN;
+
+ for (i = 0; i < ssid_len; i++) {
+ c = (uint) ssid[i];
+ if (c == '\\') {
+ *p++ = '\\';
+ *p++ = '\\';
+ } else if (isprint((unsigned char) c)) {
+ *p++ = (char)c;
+ } else {
+ p += snprintf(p, (endp - p), "\\x%02X", c);
+ }
+ }
+ *p = '\0';
+ ASSERT(p < endp);
+
+ return (int)(p - buf);
+}
+#endif /* defined(BCMDBG) */
+
+u16 wlc_rate_shm_offset(wlc_info_t *wlc, u8 rate)
+{
+ return wlc_bmac_rate_shm_offset(wlc->hw, rate);
+}
+
+/* Callback for device removed */
+#if defined(WLC_HIGH_ONLY)
+void wlc_device_removed(void *arg)
+{
+ wlc_info_t *wlc = (wlc_info_t *) arg;
+
+ wlc->device_present = false;
+}
+#endif /* WLC_HIGH_ONLY */
+
+/*
+ * Attempts to queue a packet onto a multiple-precedence queue,
+ * if necessary evicting a lower precedence packet from the queue.
+ *
+ * 'prec' is the precedence number that has already been mapped
+ * from the packet priority.
+ *
+ * Returns true if packet consumed (queued), false if not.
+ */
+bool BCMFASTPATH
+wlc_prec_enq(wlc_info_t *wlc, struct pktq *q, void *pkt, int prec)
+{
+ return wlc_prec_enq_head(wlc, q, pkt, prec, false);
+}
+
+bool BCMFASTPATH
+wlc_prec_enq_head(wlc_info_t *wlc, struct pktq *q, void *pkt, int prec,
+ bool head)
+{
+ void *p;
+ int eprec = -1; /* precedence to evict from */
+
+ /* Determine precedence from which to evict packet, if any */
+ if (pktq_pfull(q, prec))
+ eprec = prec;
+ else if (pktq_full(q)) {
+ p = pktq_peek_tail(q, &eprec);
+ ASSERT(p != NULL);
+ if (eprec > prec) {
+ WL_ERROR(("%s: Failing: eprec %d > prec %d\n", __func__,
+ eprec, prec));
+ return false;
+ }
+ }
+
+ /* Evict if needed */
+ if (eprec >= 0) {
+ bool discard_oldest;
+
+ /* Detect queueing to unconfigured precedence */
+ ASSERT(!pktq_pempty(q, eprec));
+
+ discard_oldest = AC_BITMAP_TST(wlc->wme_dp, eprec);
+
+ /* Refuse newer packet unless configured to discard oldest */
+ if (eprec == prec && !discard_oldest) {
+ WL_ERROR(("%s: No where to go, prec == %d\n", __func__,
+ prec));
+ return false;
+ }
+
+ /* Evict packet according to discard policy */
+ p = discard_oldest ? pktq_pdeq(q, eprec) : pktq_pdeq_tail(q,
+ eprec);
+ ASSERT(p != NULL);
+
+ /* Increment wme stats */
+ if (WME_ENAB(wlc->pub)) {
+ WLCNTINCR(wlc->pub->_wme_cnt->
+ tx_failed[WME_PRIO2AC(PKTPRIO(p))].packets);
+ WLCNTADD(wlc->pub->_wme_cnt->
+ tx_failed[WME_PRIO2AC(PKTPRIO(p))].bytes,
+ pkttotlen(wlc->osh, p));
+ }
+
+ ASSERT(0);
+ PKTFREE(wlc->osh, p, true);
+ WLCNTINCR(wlc->pub->_cnt->txnobuf);
+ }
+
+ /* Enqueue */
+ if (head)
+ p = pktq_penq_head(q, prec, pkt);
+ else
+ p = pktq_penq(q, prec, pkt);
+ ASSERT(p != NULL);
+
+ return true;
+}
+
+void BCMFASTPATH wlc_txq_enq(void *ctx, struct scb *scb, void *sdu, uint prec)
+{
+ wlc_info_t *wlc = (wlc_info_t *) ctx;
+ wlc_txq_info_t *qi = wlc->active_queue; /* Check me */
+ struct pktq *q = &qi->q;
+ int prio;
+
+ prio = PKTPRIO(sdu);
+
+ ASSERT(pktq_max(q) >= wlc->pub->tunables->datahiwat);
+
+ if (!wlc_prec_enq(wlc, q, sdu, prec)) {
+ if (!EDCF_ENAB(wlc->pub)
+ || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL))
+ WL_ERROR(("wl%d: wlc_txq_enq: txq overflow\n",
+ wlc->pub->unit));
+
+ /* ASSERT(9 == 8); *//* XXX we might hit this condtion in case packet flooding from mac80211 stack */
+ PKTFREE(wlc->osh, sdu, true);
+ WLCNTINCR(wlc->pub->_cnt->txnobuf);
+ }
+
+ /* Check if flow control needs to be turned on after enqueuing the packet
+ * Don't turn on flow control if EDCF is enabled. Driver would make the decision on what
+ * to drop instead of relying on stack to make the right decision
+ */
+ if (!EDCF_ENAB(wlc->pub)
+ || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL)) {
+ if (pktq_len(q) >= wlc->pub->tunables->datahiwat) {
+ wlc_txflowcontrol(wlc, qi, ON, ALLPRIO);
+ }
+ } else if (wlc->pub->_priofc) {
+ if (pktq_plen(q, wlc_prio2prec_map[prio]) >=
+ wlc->pub->tunables->datahiwat) {
+ wlc_txflowcontrol(wlc, qi, ON, prio);
+ }
+ }
+}
+
+bool BCMFASTPATH
+wlc_sendpkt_mac80211(wlc_info_t *wlc, void *sdu, struct ieee80211_hw *hw)
+{
+ u8 prio;
+ uint fifo;
+ void *pkt;
+ struct scb *scb = &global_scb;
+ struct dot11_header *d11_header = (struct dot11_header *)PKTDATA(sdu);
+ u16 type, fc;
+
+ ASSERT(sdu);
+
+ fc = ltoh16(d11_header->fc);
+ type = FC_TYPE(fc);
+
+ /* 802.11 standard requires management traffic to go at highest priority */
+ prio = (type == FC_TYPE_DATA ? PKTPRIO(sdu) : MAXPRIO);
+ fifo = prio2fifo[prio];
+
+ ASSERT((uint) PKTHEADROOM(sdu) >= TXOFF);
+ ASSERT(!PKTSHARED(sdu));
+ ASSERT(!PKTNEXT(sdu));
+ ASSERT(!PKTLINK(sdu));
+ ASSERT(fifo < NFIFO);
+
+ pkt = sdu;
+ if (unlikely
+ (wlc_d11hdrs_mac80211(wlc, hw, pkt, scb, 0, 1, fifo, 0, NULL, 0)))
+ return -EINVAL;
+ wlc_txq_enq(wlc, scb, pkt, WLC_PRIO_TO_PREC(prio));
+ wlc_send_q(wlc, wlc->active_queue);
+
+ WLCNTINCR(wlc->pub->_cnt->ieee_tx);
+ return 0;
+}
+
+void BCMFASTPATH wlc_send_q(wlc_info_t *wlc, wlc_txq_info_t *qi)
+{
+ void *pkt[DOT11_MAXNUMFRAGS];
+ int prec;
+ u16 prec_map;
+ int err = 0, i, count;
+ uint fifo;
+ struct pktq *q = &qi->q;
+ struct ieee80211_tx_info *tx_info;
+
+ /* only do work for the active queue */
+ if (qi != wlc->active_queue)
+ return;
+
+ if (in_send_q)
+ return;
+ else
+ in_send_q = true;
+
+ prec_map = wlc->tx_prec_map;
+
+ /* Send all the enq'd pkts that we can.
+ * Dequeue packets with precedence with empty HW fifo only
+ */
+ while (prec_map && (pkt[0] = pktq_mdeq(q, prec_map, &prec))) {
+ tx_info = IEEE80211_SKB_CB(pkt[0]);
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ err = wlc_sendampdu(wlc->ampdu, qi, pkt, prec);
+ } else {
+ count = 1;
+ err = wlc_prep_pdu(wlc, pkt[0], &fifo);
+ if (!err) {
+ for (i = 0; i < count; i++) {
+ wlc_txfifo(wlc, fifo, pkt[i], true, 1);
+ }
+ }
+ }
+
+ if (err == BCME_BUSY) {
+ pktq_penq_head(q, prec, pkt[0]);
+ /* If send failed due to any other reason than a change in
+ * HW FIFO condition, quit. Otherwise, read the new prec_map!
+ */
+ if (prec_map == wlc->tx_prec_map)
+ break;
+ prec_map = wlc->tx_prec_map;
+ }
+ }
+
+ /* Check if flow control needs to be turned off after sending the packet */
+ if (!EDCF_ENAB(wlc->pub)
+ || (wlc->pub->wlfeatureflag & WL_SWFL_FLOWCONTROL)) {
+ if (wlc_txflowcontrol_prio_isset(wlc, qi, ALLPRIO)
+ && (pktq_len(q) < wlc->pub->tunables->datahiwat / 2)) {
+ wlc_txflowcontrol(wlc, qi, OFF, ALLPRIO);
+ }
+ } else if (wlc->pub->_priofc) {
+ int prio;
+ for (prio = MAXPRIO; prio >= 0; prio--) {
+ if (wlc_txflowcontrol_prio_isset(wlc, qi, prio) &&
+ (pktq_plen(q, wlc_prio2prec_map[prio]) <
+ wlc->pub->tunables->datahiwat / 2)) {
+ wlc_txflowcontrol(wlc, qi, OFF, prio);
+ }
+ }
+ }
+ in_send_q = false;
+}
+
+/*
+ * bcmc_fid_generate:
+ * Generate frame ID for a BCMC packet. The frag field is not used
+ * for MC frames so is used as part of the sequence number.
+ */
+static inline u16
+bcmc_fid_generate(wlc_info_t *wlc, wlc_bsscfg_t *bsscfg, d11txh_t *txh)
+{
+ u16 frameid;
+
+ frameid = ltoh16(txh->TxFrameID) & ~(TXFID_SEQ_MASK | TXFID_QUEUE_MASK);
+ frameid |=
+ (((wlc->
+ mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
+ TX_BCMC_FIFO;
+
+ return frameid;
+}
+
+void BCMFASTPATH
+wlc_txfifo(wlc_info_t *wlc, uint fifo, void *p, bool commit, s8 txpktpend)
+{
+ u16 frameid = INVALIDFID;
+ d11txh_t *txh;
+
+ ASSERT(fifo < NFIFO);
+ txh = (d11txh_t *) PKTDATA(p);
+
+ /* When a BC/MC frame is being committed to the BCMC fifo via DMA (NOT PIO), update
+ * ucode or BSS info as appropriate.
+ */
+ if (fifo == TX_BCMC_FIFO) {
+ frameid = ltoh16(txh->TxFrameID);
+
+ }
+
+ if (WLC_WAR16165(wlc))
+ wlc_war16165(wlc, true);
+
+#ifdef WLC_HIGH_ONLY
+ if (RPCTX_ENAB(wlc->pub)) {
+ (void)wlc_rpctx_tx(wlc->rpctx, fifo, p, commit, frameid,
+ txpktpend);
+ return;
+ }
+#else
+
+ /* Bump up pending count for if not using rpc. If rpc is used, this will be handled
+ * in wlc_bmac_txfifo()
+ */
+ if (commit) {
+ TXPKTPENDINC(wlc, fifo, txpktpend);
+ WL_TRACE(("wlc_txfifo, pktpend inc %d to %d\n", txpktpend,
+ TXPKTPENDGET(wlc, fifo)));
+ }
+
+ /* Commit BCMC sequence number in the SHM frame ID location */
+ if (frameid != INVALIDFID)
+ BCMCFID(wlc, frameid);
+
+ if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0) {
+ WL_ERROR(("wlc_txfifo: fatal, toss frames !!!\n"));
+ }
+#endif /* WLC_HIGH_ONLY */
+}
+
+static u16
+wlc_compute_airtime(wlc_info_t *wlc, ratespec_t rspec, uint length)
+{
+ u16 usec = 0;
+ uint mac_rate = RSPEC2RATE(rspec);
+ uint nsyms;
+
+ if (IS_MCS(rspec)) {
+ /* not supported yet */
+ ASSERT(0);
+ } else if (IS_OFDM(rspec)) {
+ /* nsyms = Ceiling(Nbits / (Nbits/sym))
+ *
+ * Nbits = length * 8
+ * Nbits/sym = Mbps * 4 = mac_rate * 2
+ */
+ nsyms = CEIL((length * 8), (mac_rate * 2));
+
+ /* usec = symbols * usec/symbol */
+ usec = (u16) (nsyms * APHY_SYMBOL_TIME);
+ return usec;
+ } else {
+ switch (mac_rate) {
+ case WLC_RATE_1M:
+ usec = length << 3;
+ break;
+ case WLC_RATE_2M:
+ usec = length << 2;
+ break;
+ case WLC_RATE_5M5:
+ usec = (length << 4) / 11;
+ break;
+ case WLC_RATE_11M:
+ usec = (length << 3) / 11;
+ break;
+ default:
+ WL_ERROR(("wl%d: wlc_compute_airtime: unsupported rspec 0x%x\n", wlc->pub->unit, rspec));
+ ASSERT((const char *)"Bad phy_rate" == NULL);
+ break;
+ }
+ }
+
+ return usec;
+}
+
+void BCMFASTPATH
+wlc_compute_plcp(wlc_info_t *wlc, ratespec_t rspec, uint length, u8 *plcp)
+{
+ if (IS_MCS(rspec)) {
+ wlc_compute_mimo_plcp(rspec, length, plcp);
+ } else if (IS_OFDM(rspec)) {
+ wlc_compute_ofdm_plcp(rspec, length, plcp);
+ } else {
+ wlc_compute_cck_plcp(rspec, length, plcp);
+ }
+ return;
+}
+
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void wlc_compute_mimo_plcp(ratespec_t rspec, uint length, u8 *plcp)
+{
+ u8 mcs = (u8) (rspec & RSPEC_RATE_MASK);
+ ASSERT(IS_MCS(rspec));
+ plcp[0] = mcs;
+ if (RSPEC_IS40MHZ(rspec) || (mcs == 32))
+ plcp[0] |= MIMO_PLCP_40MHZ;
+ WLC_SET_MIMO_PLCP_LEN(plcp, length);
+ plcp[3] = RSPEC_MIMOPLCP3(rspec); /* rspec already holds this byte */
+ plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */
+ plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */
+ plcp[5] = 0;
+}
+
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void BCMFASTPATH
+wlc_compute_ofdm_plcp(ratespec_t rspec, u32 length, u8 *plcp)
+{
+ u8 rate_signal;
+ u32 tmp = 0;
+ int rate = RSPEC2RATE(rspec);
+
+ ASSERT(IS_OFDM(rspec));
+
+ /* encode rate per 802.11a-1999 sec 17.3.4.1, with lsb transmitted first */
+ rate_signal = rate_info[rate] & RATE_MASK;
+ ASSERT(rate_signal != 0);
+
+ bzero(plcp, D11_PHY_HDR_LEN);
+ D11A_PHY_HDR_SRATE((ofdm_phy_hdr_t *) plcp, rate_signal);
+
+ tmp = (length & 0xfff) << 5;
+ plcp[2] |= (tmp >> 16) & 0xff;
+ plcp[1] |= (tmp >> 8) & 0xff;
+ plcp[0] |= tmp & 0xff;
+
+ return;
+}
+
+/*
+ * Compute PLCP, but only requires actual rate and length of pkt.
+ * Rate is given in the driver standard multiple of 500 kbps.
+ * le is set for 11 Mbps rate if necessary.
+ * Broken out for PRQ.
+ */
+
+static void wlc_cck_plcp_set(int rate_500, uint length, u8 *plcp)
+{
+ u16 usec = 0;
+ u8 le = 0;
+
+ switch (rate_500) {
+ case WLC_RATE_1M:
+ usec = length << 3;
+ break;
+ case WLC_RATE_2M:
+ usec = length << 2;
+ break;
+ case WLC_RATE_5M5:
+ usec = (length << 4) / 11;
+ if ((length << 4) - (usec * 11) > 0)
+ usec++;
+ break;
+ case WLC_RATE_11M:
+ usec = (length << 3) / 11;
+ if ((length << 3) - (usec * 11) > 0) {
+ usec++;
+ if ((usec * 11) - (length << 3) >= 8)
+ le = D11B_PLCP_SIGNAL_LE;
+ }
+ break;
+
+ default:
+ WL_ERROR(("wlc_cck_plcp_set: unsupported rate %d\n", rate_500));
+ rate_500 = WLC_RATE_1M;
+ usec = length << 3;
+ break;
+ }
+ /* PLCP signal byte */
+ plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */
+ /* PLCP service byte */
+ plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED);
+ /* PLCP length u16, little endian */
+ plcp[2] = usec & 0xff;
+ plcp[3] = (usec >> 8) & 0xff;
+ /* PLCP CRC16 */
+ plcp[4] = 0;
+ plcp[5] = 0;
+}
+
+/* Rate: 802.11 rate code, length: PSDU length in octets */
+static void wlc_compute_cck_plcp(ratespec_t rspec, uint length, u8 *plcp)
+{
+ int rate = RSPEC2RATE(rspec);
+
+ ASSERT(IS_CCK(rspec));
+
+ wlc_cck_plcp_set(rate, length, plcp);
+}
+
+/* wlc_compute_frame_dur()
+ *
+ * Calculate the 802.11 MAC header DUR field for MPDU
+ * DUR for a single frame = 1 SIFS + 1 ACK
+ * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time
+ *
+ * rate MPDU rate in unit of 500kbps
+ * next_frag_len next MPDU length in bytes
+ * preamble_type use short/GF or long/MM PLCP header
+ */
+static u16 BCMFASTPATH
+wlc_compute_frame_dur(wlc_info_t *wlc, ratespec_t rate, u8 preamble_type,
+ uint next_frag_len)
+{
+ u16 dur, sifs;
+
+ sifs = SIFS(wlc->band);
+
+ dur = sifs;
+ dur += (u16) wlc_calc_ack_time(wlc, rate, preamble_type);
+
+ if (next_frag_len) {
+ /* Double the current DUR to get 2 SIFS + 2 ACKs */
+ dur *= 2;
+ /* add another SIFS and the frag time */
+ dur += sifs;
+ dur +=
+ (u16) wlc_calc_frame_time(wlc, rate, preamble_type,
+ next_frag_len);
+ }
+ return dur;
+}
+
+/* wlc_compute_rtscts_dur()
+ *
+ * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame
+ * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK
+ * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK
+ *
+ * cts cts-to-self or rts/cts
+ * rts_rate rts or cts rate in unit of 500kbps
+ * rate next MPDU rate in unit of 500kbps
+ * frame_len next MPDU frame length in bytes
+ */
+u16 BCMFASTPATH
+wlc_compute_rtscts_dur(wlc_info_t *wlc, bool cts_only, ratespec_t rts_rate,
+ ratespec_t frame_rate, u8 rts_preamble_type,
+ u8 frame_preamble_type, uint frame_len, bool ba)
+{
+ u16 dur, sifs;
+
+ sifs = SIFS(wlc->band);
+
+ if (!cts_only) { /* RTS/CTS */
+ dur = 3 * sifs;
+ dur +=
+ (u16) wlc_calc_cts_time(wlc, rts_rate,
+ rts_preamble_type);
+ } else { /* CTS-TO-SELF */
+ dur = 2 * sifs;
+ }
+
+ dur +=
+ (u16) wlc_calc_frame_time(wlc, frame_rate, frame_preamble_type,
+ frame_len);
+ if (ba)
+ dur +=
+ (u16) wlc_calc_ba_time(wlc, frame_rate,
+ WLC_SHORT_PREAMBLE);
+ else
+ dur +=
+ (u16) wlc_calc_ack_time(wlc, frame_rate,
+ frame_preamble_type);
+ return dur;
+}
+
+static bool wlc_phy_rspec_check(wlc_info_t *wlc, u16 bw, ratespec_t rspec)
+{
+ if (IS_MCS(rspec)) {
+ uint mcs = rspec & RSPEC_RATE_MASK;
+
+ if (mcs < 8) {
+ ASSERT(RSPEC_STF(rspec) < PHY_TXC1_MODE_SDM);
+ } else if ((mcs >= 8) && (mcs <= 23)) {
+ ASSERT(RSPEC_STF(rspec) == PHY_TXC1_MODE_SDM);
+ } else if (mcs == 32) {
+ ASSERT(RSPEC_STF(rspec) < PHY_TXC1_MODE_SDM);
+ ASSERT(bw == PHY_TXC1_BW_40MHZ_DUP);
+ }
+ } else if (IS_OFDM(rspec)) {
+ ASSERT(RSPEC_STF(rspec) < PHY_TXC1_MODE_STBC);
+ } else {
+ ASSERT(IS_CCK(rspec));
+
+ ASSERT((bw == PHY_TXC1_BW_20MHZ)
+ || (bw == PHY_TXC1_BW_20MHZ_UP));
+ ASSERT(RSPEC_STF(rspec) == PHY_TXC1_MODE_SISO);
+ }
+
+ return true;
+}
+
+u16 BCMFASTPATH wlc_phytxctl1_calc(wlc_info_t *wlc, ratespec_t rspec)
+{
+ u16 phyctl1 = 0;
+ u16 bw;
+
+ if (WLCISLCNPHY(wlc->band)) {
+ bw = PHY_TXC1_BW_20MHZ;
+ } else {
+ bw = RSPEC_GET_BW(rspec);
+ /* 10Mhz is not supported yet */
+ if (bw < PHY_TXC1_BW_20MHZ) {
+ WL_ERROR(("wlc_phytxctl1_calc: bw %d is not supported yet, set to 20L\n", bw));
+ bw = PHY_TXC1_BW_20MHZ;
+ }
+
+ wlc_phy_rspec_check(wlc, bw, rspec);
+ }
+
+ if (IS_MCS(rspec)) {
+ uint mcs = rspec & RSPEC_RATE_MASK;
+
+ /* bw, stf, coding-type is part of RSPEC_PHYTXBYTE2 returns */
+ phyctl1 = RSPEC_PHYTXBYTE2(rspec);
+ /* set the upper byte of phyctl1 */
+ phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8);
+ } else if (IS_CCK(rspec) && !WLCISLCNPHY(wlc->band)
+ && !WLCISSSLPNPHY(wlc->band)) {
+ /* In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate */
+ /* Eventually MIMOPHY would also be converted to this format */
+ /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
+ phyctl1 = (bw | (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
+ } else { /* legacy OFDM/CCK */
+ s16 phycfg;
+ /* get the phyctl byte from rate phycfg table */
+ phycfg = wlc_rate_legacy_phyctl(RSPEC2RATE(rspec));
+ if (phycfg == -1) {
+ WL_ERROR(("wlc_phytxctl1_calc: wrong legacy OFDM/CCK rate\n"));
+ ASSERT(0);
+ phycfg = 0;
+ }
+ /* set the upper byte of phyctl1 */
+ phyctl1 =
+ (bw | (phycfg << 8) |
+ (RSPEC_STF(rspec) << PHY_TXC1_MODE_SHIFT));
+ }
+
+#ifdef BCMDBG
+ /* phy clock must support 40Mhz if tx descriptor uses it */
+ if ((phyctl1 & PHY_TXC1_BW_MASK) >= PHY_TXC1_BW_40MHZ) {
+ ASSERT(CHSPEC_WLC_BW(wlc->chanspec) == WLC_40_MHZ);
+#ifndef WLC_HIGH_ONLY
+ ASSERT(wlc->chanspec == wlc_phy_chanspec_get(wlc->band->pi));
+#endif
+ }
+#endif /* BCMDBG */
+ return phyctl1;
+}
+
+ratespec_t BCMFASTPATH
+wlc_rspec_to_rts_rspec(wlc_info_t *wlc, ratespec_t rspec, bool use_rspec,
+ u16 mimo_ctlchbw)
+{
+ ratespec_t rts_rspec = 0;
+
+ if (use_rspec) {
+ /* use frame rate as rts rate */
+ rts_rspec = rspec;
+
+ } else if (wlc->band->gmode && wlc->protection->_g && !IS_CCK(rspec)) {
+ /* Use 11Mbps as the g protection RTS target rate and fallback.
+ * Use the WLC_BASIC_RATE() lookup to find the best basic rate under the
+ * target in case 11 Mbps is not Basic.
+ * 6 and 9 Mbps are not usually selected by rate selection, but even
+ * if the OFDM rate we are protecting is 6 or 9 Mbps, 11 is more robust.
+ */
+ rts_rspec = WLC_BASIC_RATE(wlc, WLC_RATE_11M);
+ } else {
+ /* calculate RTS rate and fallback rate based on the frame rate
+ * RTS must be sent at a basic rate since it is a
+ * control frame, sec 9.6 of 802.11 spec
+ */
+ rts_rspec = WLC_BASIC_RATE(wlc, rspec);
+ }
+
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+ /* set rts txbw to correct side band */
+ rts_rspec &= ~RSPEC_BW_MASK;
+
+ /* if rspec/rspec_fallback is 40MHz, then send RTS on both 20MHz channel
+ * (DUP), otherwise send RTS on control channel
+ */
+ if (RSPEC_IS40MHZ(rspec) && !IS_CCK(rts_rspec))
+ rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT);
+ else
+ rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
+
+ /* pick siso/cdd as default for ofdm */
+ if (IS_OFDM(rts_rspec)) {
+ rts_rspec &= ~RSPEC_STF_MASK;
+ rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT);
+ }
+ }
+ return rts_rspec;
+}
+
+/*
+ * Add d11txh_t, cck_phy_hdr_t.
+ *
+ * 'p' data must start with 802.11 MAC header
+ * 'p' must allow enough bytes of local headers to be "pushed" onto the packet
+ *
+ * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes)
+ *
+ */
+static u16 BCMFASTPATH
+wlc_d11hdrs_mac80211(wlc_info_t *wlc, struct ieee80211_hw *hw,
+ void *p, struct scb *scb, uint frag,
+ uint nfrags, uint queue, uint next_frag_len,
+ wsec_key_t *key, ratespec_t rspec_override)
+{
+ struct dot11_header *h;
+ d11txh_t *txh;
+ u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN];
+ osl_t *osh;
+ int len, phylen, rts_phylen;
+ u16 fc, type, frameid, mch, phyctl, xfts, mainrates;
+ u16 seq = 0, mcl = 0, status = 0;
+ ratespec_t rspec[2] = { WLC_RATE_1M, WLC_RATE_1M }, rts_rspec[2] = {
+ WLC_RATE_1M, WLC_RATE_1M};
+ bool use_rts = false;
+ bool use_cts = false;
+ bool use_rifs = false;
+ bool short_preamble[2] = { false, false };
+ u8 preamble_type[2] = { WLC_LONG_PREAMBLE, WLC_LONG_PREAMBLE };
+ u8 rts_preamble_type[2] = { WLC_LONG_PREAMBLE, WLC_LONG_PREAMBLE };
+ u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN];
+ struct dot11_rts_frame *rts = NULL;
+ bool qos;
+ uint ac;
+ u32 rate_val[2];
+ bool hwtkmic = false;
+ u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
+#ifdef WLANTSEL
+#define ANTCFG_NONE 0xFF
+ u8 antcfg = ANTCFG_NONE;
+ u8 fbantcfg = ANTCFG_NONE;
+#endif
+ uint phyctl1_stf = 0;
+ u16 durid = 0;
+ struct ieee80211_tx_rate *txrate[2];
+ int k;
+ struct ieee80211_tx_info *tx_info;
+ bool is_mcs[2];
+ u16 mimo_txbw;
+ u8 mimo_preamble_type;
+
+ frameid = 0;
+
+ ASSERT(queue < NFIFO);
+
+ osh = wlc->osh;
+
+ /* locate 802.11 MAC header */
+ h = (struct dot11_header *)PKTDATA(p);
+ fc = ltoh16(h->fc);
+ type = FC_TYPE(fc);
+
+ qos = (type == FC_TYPE_DATA && FC_SUBTYPE_ANY_QOS(FC_SUBTYPE(fc)));
+
+ /* compute length of frame in bytes for use in PLCP computations */
+ len = pkttotlen(osh, p);
+ phylen = len + DOT11_FCS_LEN;
+
+ /* If WEP enabled, add room in phylen for the additional bytes of
+ * ICV which MAC generates. We do NOT add the additional bytes to
+ * the packet itself, thus phylen = packet length + ICV_LEN + FCS_LEN
+ * in this case
+ */
+ if (key) {
+ phylen += key->icv_len;
+ }
+
+ /* Get tx_info */
+ tx_info = IEEE80211_SKB_CB(p);
+ ASSERT(tx_info);
+
+ /* add PLCP */
+ plcp = PKTPUSH(p, D11_PHY_HDR_LEN);
+
+ /* add Broadcom tx descriptor header */
+ txh = (d11txh_t *) PKTPUSH(p, D11_TXH_LEN);
+ bzero((char *)txh, D11_TXH_LEN);
+
+ /* setup frameid */
+ if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ /* non-AP STA should never use BCMC queue */
+ ASSERT(queue != TX_BCMC_FIFO);
+ if (queue == TX_BCMC_FIFO) {
+ WL_ERROR(("wl%d: %s: ASSERT queue == TX_BCMC!\n",
+ WLCWLUNIT(wlc), __func__));
+ frameid = bcmc_fid_generate(wlc, NULL, txh);
+ } else {
+ /* Increment the counter for first fragment */
+ if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) {
+ SCB_SEQNUM(scb, PKTPRIO(p))++;
+ }
+
+ /* extract fragment number from frame first */
+ seq = ltoh16(seq) & FRAGNUM_MASK;
+ seq |= (SCB_SEQNUM(scb, PKTPRIO(p)) << SEQNUM_SHIFT);
+ h->seq = htol16(seq);
+
+ frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) |
+ (queue & TXFID_QUEUE_MASK);
+ }
+ }
+ frameid |= queue & TXFID_QUEUE_MASK;
+
+ /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */
+ if (SCB_PS(scb) || ((fc & FC_KIND_MASK) == FC_BEACON))
+ mcl |= TXC_IGNOREPMQ;
+
+ ASSERT(hw->max_rates <= IEEE80211_TX_MAX_RATES);
+ ASSERT(hw->max_rates == 2);
+
+ txrate[0] = tx_info->control.rates;
+ txrate[1] = txrate[0] + 1;
+
+ ASSERT(txrate[0]->idx >= 0);
+ /* if rate control algorithm didn't give us a fallback rate, use the primary rate */
+ if (txrate[1]->idx < 0) {
+ txrate[1] = txrate[0];
+ }
+#ifdef WLC_HIGH_ONLY
+ /* Double protection , just in case */
+ if (txrate[0]->idx > HIGHEST_SINGLE_STREAM_MCS)
+ txrate[0]->idx = HIGHEST_SINGLE_STREAM_MCS;
+ if (txrate[1]->idx > HIGHEST_SINGLE_STREAM_MCS)
+ txrate[1]->idx = HIGHEST_SINGLE_STREAM_MCS;
+#endif
+
+ for (k = 0; k < hw->max_rates; k++) {
+ is_mcs[k] =
+ txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false;
+ if (!is_mcs[k]) {
+ ASSERT(!(tx_info->flags & IEEE80211_TX_CTL_AMPDU));
+ if ((txrate[k]->idx >= 0)
+ && (txrate[k]->idx <
+ hw->wiphy->bands[tx_info->band]->n_bitrates)) {
+ rate_val[k] =
+ hw->wiphy->bands[tx_info->band]->
+ bitrates[txrate[k]->idx].hw_value;
+ short_preamble[k] =
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE ?
+ true : false;
+ } else {
+ ASSERT((txrate[k]->idx >= 0) &&
+ (txrate[k]->idx <
+ hw->wiphy->bands[tx_info->band]->
+ n_bitrates));
+ rate_val[k] = WLC_RATE_1M;
+ }
+ } else {
+ rate_val[k] = txrate[k]->idx;
+ }
+ /* Currently only support same setting for primay and fallback rates.
+ * Unify flags for each rate into a single value for the frame
+ */
+ use_rts |=
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false;
+ use_cts |=
+ txrate[k]->
+ flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false;
+
+ if (is_mcs[k])
+ rate_val[k] |= NRATE_MCS_INUSE;
+
+ rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, rate_val[k]);
+
+ /* (1) RATE: determine and validate primary rate and fallback rates */
+ if (!RSPEC_ACTIVE(rspec[k])) {
+ ASSERT(RSPEC_ACTIVE(rspec[k]));
+ rspec[k] = WLC_RATE_1M;
+ } else {
+ if (WLANTSEL_ENAB(wlc) && !ETHER_ISMULTI(&h->a1)) {
+ /* set tx antenna config */
+ wlc_antsel_antcfg_get(wlc->asi, false, false, 0,
+ 0, &antcfg, &fbantcfg);
+ }
+ }
+ }
+
+ phyctl1_stf = wlc->stf->ss_opmode;
+
+ if (N_ENAB(wlc->pub)) {
+ for (k = 0; k < hw->max_rates; k++) {
+ /* apply siso/cdd to single stream mcs's or ofdm if rspec is auto selected */
+ if (((IS_MCS(rspec[k]) &&
+ IS_SINGLE_STREAM(rspec[k] & RSPEC_RATE_MASK)) ||
+ IS_OFDM(rspec[k]))
+ && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY)
+ || !(rspec[k] & RSPEC_OVERRIDE))) {
+ rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK);
+
+ /* For SISO MCS use STBC if possible */
+ if (IS_MCS(rspec[k])
+ && WLC_STF_SS_STBC_TX(wlc, scb)) {
+ u8 stc;
+
+ ASSERT(WLC_STBC_CAP_PHY(wlc));
+ stc = 1; /* Nss for single stream is always 1 */
+ rspec[k] |=
+ (PHY_TXC1_MODE_STBC <<
+ RSPEC_STF_SHIFT) | (stc <<
+ RSPEC_STC_SHIFT);
+ } else
+ rspec[k] |=
+ (phyctl1_stf << RSPEC_STF_SHIFT);
+ }
+
+ /* Is the phy configured to use 40MHZ frames? If so then pick the desired txbw */
+ if (CHSPEC_WLC_BW(wlc->chanspec) == WLC_40_MHZ) {
+ /* default txbw is 20in40 SB */
+ mimo_ctlchbw = mimo_txbw =
+ CHSPEC_SB_UPPER(WLC_BAND_PI_RADIO_CHANSPEC)
+ ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+
+ if (IS_MCS(rspec[k])) {
+ /* mcs 32 must be 40b/w DUP */
+ if ((rspec[k] & RSPEC_RATE_MASK) == 32) {
+ mimo_txbw =
+ PHY_TXC1_BW_40MHZ_DUP;
+ /* use override */
+ } else if (wlc->mimo_40txbw != AUTO)
+ mimo_txbw = wlc->mimo_40txbw;
+ /* else check if dst is using 40 Mhz */
+ else if (scb->flags & SCB_IS40)
+ mimo_txbw = PHY_TXC1_BW_40MHZ;
+ } else if (IS_OFDM(rspec[k])) {
+ if (wlc->ofdm_40txbw != AUTO)
+ mimo_txbw = wlc->ofdm_40txbw;
+ } else {
+ ASSERT(IS_CCK(rspec[k]));
+ if (wlc->cck_40txbw != AUTO)
+ mimo_txbw = wlc->cck_40txbw;
+ }
+ } else {
+ /* mcs32 is 40 b/w only.
+ * This is possible for probe packets on a STA during SCAN
+ */
+ if ((rspec[k] & RSPEC_RATE_MASK) == 32) {
+ /* mcs 0 */
+ rspec[k] = RSPEC_MIMORATE;
+ }
+ mimo_txbw = PHY_TXC1_BW_20MHZ;
+ }
+
+ /* Set channel width */
+ rspec[k] &= ~RSPEC_BW_MASK;
+ if ((k == 0) || ((k > 0) && IS_MCS(rspec[k])))
+ rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT);
+ else
+ rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT);
+
+ /* Set Short GI */
+#ifdef NOSGIYET
+ if (IS_MCS(rspec[k])
+ && (txrate[k]->flags & IEEE80211_TX_RC_SHORT_GI))
+ rspec[k] |= RSPEC_SHORT_GI;
+ else if (!(txrate[k]->flags & IEEE80211_TX_RC_SHORT_GI))
+ rspec[k] &= ~RSPEC_SHORT_GI;
+#else
+ rspec[k] &= ~RSPEC_SHORT_GI;
+#endif
+
+ mimo_preamble_type = WLC_MM_PREAMBLE;
+ if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD)
+ mimo_preamble_type = WLC_GF_PREAMBLE;
+
+ if ((txrate[k]->flags & IEEE80211_TX_RC_MCS)
+ && (!IS_MCS(rspec[k]))) {
+ WL_ERROR(("wl%d: %s: IEEE80211_TX_RC_MCS != IS_MCS(rspec)\n", WLCWLUNIT(wlc), __func__));
+ ASSERT(0 && "Rate mismatch");
+ }
+
+ if (IS_MCS(rspec[k])) {
+ preamble_type[k] = mimo_preamble_type;
+
+ /* if SGI is selected, then forced mm for single stream */
+ if ((rspec[k] & RSPEC_SHORT_GI)
+ && IS_SINGLE_STREAM(rspec[k] &
+ RSPEC_RATE_MASK)) {
+ preamble_type[k] = WLC_MM_PREAMBLE;
+ }
+ }
+
+ /* mimo bw field MUST now be valid in the rspec (it affects duration calculations) */
+ ASSERT(VALID_RATE_DBG(wlc, rspec[0]));
+
+ /* should be better conditionalized */
+ if (!IS_MCS(rspec[0])
+ && (tx_info->control.rates[0].
+ flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE))
+ preamble_type[k] = WLC_SHORT_PREAMBLE;
+
+ ASSERT(!IS_MCS(rspec[0])
+ || WLC_IS_MIMO_PREAMBLE(preamble_type[k]));
+ }
+ } else {
+ for (k = 0; k < hw->max_rates; k++) {
+ /* Set ctrlchbw as 20Mhz */
+ ASSERT(!IS_MCS(rspec[k]));
+ rspec[k] &= ~RSPEC_BW_MASK;
+ rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT);
+
+ /* for nphy, stf of ofdm frames must follow policies */
+ if (WLCISNPHY(wlc->band) && IS_OFDM(rspec[k])) {
+ rspec[k] &= ~RSPEC_STF_MASK;
+ rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT;
+ }
+ }
+ }
+
+ /* Reset these for use with AMPDU's */
+ txrate[0]->count = 0;
+ txrate[1]->count = 0;
+
+ /* (3) PLCP: determine PLCP header and MAC duration, fill d11txh_t */
+ wlc_compute_plcp(wlc, rspec[0], phylen, plcp);
+ wlc_compute_plcp(wlc, rspec[1], phylen, plcp_fallback);
+ bcopy(plcp_fallback, (char *)&txh->FragPLCPFallback,
+ sizeof(txh->FragPLCPFallback));
+
+ /* Length field now put in CCK FBR CRC field */
+ if (IS_CCK(rspec[1])) {
+ txh->FragPLCPFallback[4] = phylen & 0xff;
+ txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8;
+ }
+
+ /* MIMO-RATE: need validation ?? */
+ mainrates =
+ IS_OFDM(rspec[0]) ? D11A_PHY_HDR_GRATE((ofdm_phy_hdr_t *) plcp) :
+ plcp[0];
+
+ /* DUR field for main rate */
+ if ((fc != FC_PS_POLL) && !ETHER_ISMULTI(&h->a1) && !use_rifs) {
+ durid =
+ wlc_compute_frame_dur(wlc, rspec[0], preamble_type[0],
+ next_frag_len);
+ h->durid = htol16(durid);
+ } else if (use_rifs) {
+ /* NAV protect to end of next max packet size */
+ durid =
+ (u16) wlc_calc_frame_time(wlc, rspec[0],
+ preamble_type[0],
+ DOT11_MAX_FRAG_LEN);
+ durid += RIFS_11N_TIME;
+ h->durid = htol16(durid);
+ }
+
+ /* DUR field for fallback rate */
+ if (fc == FC_PS_POLL)
+ txh->FragDurFallback = h->durid;
+ else if (ETHER_ISMULTI(&h->a1) || use_rifs)
+ txh->FragDurFallback = 0;
+ else {
+ durid = wlc_compute_frame_dur(wlc, rspec[1],
+ preamble_type[1], next_frag_len);
+ txh->FragDurFallback = htol16(durid);
+ }
+
+ /* (4) MAC-HDR: MacTxControlLow */
+ if (frag == 0)
+ mcl |= TXC_STARTMSDU;
+
+ if (!ETHER_ISMULTI(&h->a1))
+ mcl |= TXC_IMMEDACK;
+
+ if (BAND_5G(wlc->band->bandtype))
+ mcl |= TXC_FREQBAND_5G;
+
+ if (CHSPEC_IS40(WLC_BAND_PI_RADIO_CHANSPEC))
+ mcl |= TXC_BW_40;
+
+ /* set AMIC bit if using hardware TKIP MIC */
+ if (hwtkmic)
+ mcl |= TXC_AMIC;
+
+ txh->MacTxControlLow = htol16(mcl);
+
+ /* MacTxControlHigh */
+ mch = 0;
+
+ /* Set fallback rate preamble type */
+ if ((preamble_type[1] == WLC_SHORT_PREAMBLE) ||
+ (preamble_type[1] == WLC_GF_PREAMBLE)) {
+ ASSERT((preamble_type[1] == WLC_GF_PREAMBLE) ||
+ (!IS_MCS(rspec[1])));
+ if (RSPEC2RATE(rspec[1]) != WLC_RATE_1M)
+ mch |= TXC_PREAMBLE_DATA_FB_SHORT;
+ }
+
+ /* MacFrameControl */
+ bcopy((char *)&h->fc, (char *)&txh->MacFrameControl, sizeof(u16));
+
+ txh->TxFesTimeNormal = htol16(0);
+
+ txh->TxFesTimeFallback = htol16(0);
+
+ /* TxFrameRA */
+ bcopy((char *)&h->a1, (char *)&txh->TxFrameRA, ETHER_ADDR_LEN);
+
+ /* TxFrameID */
+ txh->TxFrameID = htol16(frameid);
+
+ /* TxStatus, Note the case of recreating the first frag of a suppressed frame
+ * then we may need to reset the retry cnt's via the status reg
+ */
+ txh->TxStatus = htol16(status);
+
+ if (D11REV_GE(wlc->pub->corerev, 16)) {
+ /* extra fields for ucode AMPDU aggregation, the new fields are added to
+ * the END of previous structure so that it's compatible in driver.
+ * In old rev ucode, these fields should be ignored
+ */
+ txh->MaxNMpdus = htol16(0);
+ txh->MaxABytes_MRT = htol16(0);
+ txh->MaxABytes_FBR = htol16(0);
+ txh->MinMBytes = htol16(0);
+ }
+
+ /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, furnish d11txh_t */
+ /* RTS PLCP header and RTS frame */
+ if (use_rts || use_cts) {
+ if (use_rts && use_cts)
+ use_cts = false;
+
+ for (k = 0; k < 2; k++) {
+ rts_rspec[k] = wlc_rspec_to_rts_rspec(wlc, rspec[k],
+ false,
+ mimo_ctlchbw);
+ }
+
+ if (!IS_OFDM(rts_rspec[0]) &&
+ !((RSPEC2RATE(rts_rspec[0]) == WLC_RATE_1M) ||
+ (wlc->PLCPHdr_override == WLC_PLCP_LONG))) {
+ rts_preamble_type[0] = WLC_SHORT_PREAMBLE;
+ mch |= TXC_PREAMBLE_RTS_MAIN_SHORT;
+ }
+
+ if (!IS_OFDM(rts_rspec[1]) &&
+ !((RSPEC2RATE(rts_rspec[1]) == WLC_RATE_1M) ||
+ (wlc->PLCPHdr_override == WLC_PLCP_LONG))) {
+ rts_preamble_type[1] = WLC_SHORT_PREAMBLE;
+ mch |= TXC_PREAMBLE_RTS_FB_SHORT;
+ }
+
+ /* RTS/CTS additions to MacTxControlLow */
+ if (use_cts) {
+ txh->MacTxControlLow |= htol16(TXC_SENDCTS);
+ } else {
+ txh->MacTxControlLow |= htol16(TXC_SENDRTS);
+ txh->MacTxControlLow |= htol16(TXC_LONGFRAME);
+ }
+
+ /* RTS PLCP header */
+ ASSERT(IS_ALIGNED((unsigned long)txh->RTSPhyHeader, sizeof(u16)));
+ rts_plcp = txh->RTSPhyHeader;
+ if (use_cts)
+ rts_phylen = DOT11_CTS_LEN + DOT11_FCS_LEN;
+ else
+ rts_phylen = DOT11_RTS_LEN + DOT11_FCS_LEN;
+
+ wlc_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp);
+
+ /* fallback rate version of RTS PLCP header */
+ wlc_compute_plcp(wlc, rts_rspec[1], rts_phylen,
+ rts_plcp_fallback);
+ bcopy(rts_plcp_fallback, (char *)&txh->RTSPLCPFallback,
+ sizeof(txh->RTSPLCPFallback));
+
+ /* RTS frame fields... */
+ rts = (struct dot11_rts_frame *)&txh->rts_frame;
+
+ durid = wlc_compute_rtscts_dur(wlc, use_cts, rts_rspec[0],
+ rspec[0], rts_preamble_type[0],
+ preamble_type[0], phylen, false);
+ rts->durid = htol16(durid);
+ /* fallback rate version of RTS DUR field */
+ durid = wlc_compute_rtscts_dur(wlc, use_cts,
+ rts_rspec[1], rspec[1],
+ rts_preamble_type[1],
+ preamble_type[1], phylen, false);
+ txh->RTSDurFallback = htol16(durid);
+
+ if (use_cts) {
+ rts->fc = htol16(FC_CTS);
+ bcopy((char *)&h->a2, (char *)&rts->ra, ETHER_ADDR_LEN);
+ } else {
+ rts->fc = htol16((u16) FC_RTS);
+ bcopy((char *)&h->a1, (char *)&rts->ra,
+ 2 * ETHER_ADDR_LEN);
+ }
+
+ /* mainrate
+ * low 8 bits: main frag rate/mcs,
+ * high 8 bits: rts/cts rate/mcs
+ */
+ mainrates |= (IS_OFDM(rts_rspec[0]) ?
+ D11A_PHY_HDR_GRATE((ofdm_phy_hdr_t *) rts_plcp) :
+ rts_plcp[0]) << 8;
+ } else {
+ bzero((char *)txh->RTSPhyHeader, D11_PHY_HDR_LEN);
+ bzero((char *)&txh->rts_frame, sizeof(struct dot11_rts_frame));
+ bzero((char *)txh->RTSPLCPFallback,
+ sizeof(txh->RTSPLCPFallback));
+ txh->RTSDurFallback = 0;
+ }
+
+#ifdef SUPPORT_40MHZ
+ /* add null delimiter count */
+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && IS_MCS(rspec)) {
+ txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] =
+ wlc_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen);
+ }
+#endif
+
+ /* Now that RTS/RTS FB preamble types are updated, write the final value */
+ txh->MacTxControlHigh = htol16(mch);
+
+ /* MainRates (both the rts and frag plcp rates have been calculated now) */
+ txh->MainRates = htol16(mainrates);
+
+ /* XtraFrameTypes */
+ xfts = FRAMETYPE(rspec[1], wlc->mimoft);
+ xfts |= (FRAMETYPE(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT);
+ xfts |= (FRAMETYPE(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT);
+ xfts |=
+ CHSPEC_CHANNEL(WLC_BAND_PI_RADIO_CHANSPEC) << XFTS_CHANNEL_SHIFT;
+ txh->XtraFrameTypes = htol16(xfts);
+
+ /* PhyTxControlWord */
+ phyctl = FRAMETYPE(rspec[0], wlc->mimoft);
+ if ((preamble_type[0] == WLC_SHORT_PREAMBLE) ||
+ (preamble_type[0] == WLC_GF_PREAMBLE)) {
+ ASSERT((preamble_type[0] == WLC_GF_PREAMBLE)
+ || !IS_MCS(rspec[0]));
+ if (RSPEC2RATE(rspec[0]) != WLC_RATE_1M)
+ phyctl |= PHY_TXC_SHORT_HDR;
+ WLCNTINCR(wlc->pub->_cnt->txprshort);
+ }
+
+ /* phytxant is properly bit shifted */
+ phyctl |= wlc_stf_d11hdrs_phyctl_txant(wlc, rspec[0]);
+ txh->PhyTxControlWord = htol16(phyctl);
+
+ /* PhyTxControlWord_1 */
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+ u16 phyctl1 = 0;
+
+ phyctl1 = wlc_phytxctl1_calc(wlc, rspec[0]);
+ txh->PhyTxControlWord_1 = htol16(phyctl1);
+ phyctl1 = wlc_phytxctl1_calc(wlc, rspec[1]);
+ txh->PhyTxControlWord_1_Fbr = htol16(phyctl1);
+
+ if (use_rts || use_cts) {
+ phyctl1 = wlc_phytxctl1_calc(wlc, rts_rspec[0]);
+ txh->PhyTxControlWord_1_Rts = htol16(phyctl1);
+ phyctl1 = wlc_phytxctl1_calc(wlc, rts_rspec[1]);
+ txh->PhyTxControlWord_1_FbrRts = htol16(phyctl1);
+ }
+
+ /*
+ * For mcs frames, if mixedmode(overloaded with long preamble) is going to be set,
+ * fill in non-zero MModeLen and/or MModeFbrLen
+ * it will be unnecessary if they are separated
+ */
+ if (IS_MCS(rspec[0]) && (preamble_type[0] == WLC_MM_PREAMBLE)) {
+ u16 mmodelen =
+ wlc_calc_lsig_len(wlc, rspec[0], phylen);
+ txh->MModeLen = htol16(mmodelen);
+ }
+
+ if (IS_MCS(rspec[1]) && (preamble_type[1] == WLC_MM_PREAMBLE)) {
+ u16 mmodefbrlen =
+ wlc_calc_lsig_len(wlc, rspec[1], phylen);
+ txh->MModeFbrLen = htol16(mmodefbrlen);
+ }
+ }
+
+ if (IS_MCS(rspec[0]))
+ ASSERT(IS_MCS(rspec[1]));
+
+ ASSERT(!IS_MCS(rspec[0]) ||
+ ((preamble_type[0] == WLC_MM_PREAMBLE) == (txh->MModeLen != 0)));
+ ASSERT(!IS_MCS(rspec[1]) ||
+ ((preamble_type[1] == WLC_MM_PREAMBLE) ==
+ (txh->MModeFbrLen != 0)));
+
+ ac = wme_fifo2ac[queue];
+ if (SCB_WME(scb) && qos && wlc->edcf_txop[ac]) {
+ uint frag_dur, dur, dur_fallback;
+
+ ASSERT(!ETHER_ISMULTI(&h->a1));
+
+ /* WME: Update TXOP threshold */
+ if ((!(tx_info->flags & IEEE80211_TX_CTL_AMPDU)) && (frag == 0)) {
+ frag_dur =
+ wlc_calc_frame_time(wlc, rspec[0], preamble_type[0],
+ phylen);
+
+ if (rts) {
+ /* 1 RTS or CTS-to-self frame */
+ dur =
+ wlc_calc_cts_time(wlc, rts_rspec[0],
+ rts_preamble_type[0]);
+ dur_fallback =
+ wlc_calc_cts_time(wlc, rts_rspec[1],
+ rts_preamble_type[1]);
+ /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */
+ dur += ltoh16(rts->durid);
+ dur_fallback += ltoh16(txh->RTSDurFallback);
+ } else if (use_rifs) {
+ dur = frag_dur;
+ dur_fallback = 0;
+ } else {
+ /* frame + SIFS + ACK */
+ dur = frag_dur;
+ dur +=
+ wlc_compute_frame_dur(wlc, rspec[0],
+ preamble_type[0], 0);
+
+ dur_fallback =
+ wlc_calc_frame_time(wlc, rspec[1],
+ preamble_type[1],
+ phylen);
+ dur_fallback +=
+ wlc_compute_frame_dur(wlc, rspec[1],
+ preamble_type[1], 0);
+ }
+ /* NEED to set TxFesTimeNormal (hard) */
+ txh->TxFesTimeNormal = htol16((u16) dur);
+ /* NEED to set fallback rate version of TxFesTimeNormal (hard) */
+ txh->TxFesTimeFallback = htol16((u16) dur_fallback);
+
+ /* update txop byte threshold (txop minus intraframe overhead) */
+ if (wlc->edcf_txop[ac] >= (dur - frag_dur)) {
+ {
+ uint newfragthresh;
+
+ newfragthresh =
+ wlc_calc_frame_len(wlc, rspec[0],
+ preamble_type[0],
+ (wlc->
+ edcf_txop[ac] -
+ (dur -
+ frag_dur)));
+ /* range bound the fragthreshold */
+ if (newfragthresh < DOT11_MIN_FRAG_LEN)
+ newfragthresh =
+ DOT11_MIN_FRAG_LEN;
+ else if (newfragthresh >
+ wlc->usr_fragthresh)
+ newfragthresh =
+ wlc->usr_fragthresh;
+ /* update the fragthresh and do txc update */
+ if (wlc->fragthresh[queue] !=
+ (u16) newfragthresh) {
+ wlc->fragthresh[queue] =
+ (u16) newfragthresh;
+ }
+ }
+ } else
+ WL_ERROR(("wl%d: %s txop invalid for rate %d\n",
+ wlc->pub->unit, fifo_names[queue],
+ RSPEC2RATE(rspec[0])));
+
+ if (dur > wlc->edcf_txop[ac])
+ WL_ERROR(("wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n", wlc->pub->unit, __func__, fifo_names[queue], phylen, wlc->fragthresh[queue], dur, wlc->edcf_txop[ac]));
+ }
+ }
+
+ return 0;
+}
+
+void wlc_tbtt(wlc_info_t *wlc, d11regs_t *regs)
+{
+ wlc_bsscfg_t *cfg = wlc->cfg;
+
+ WLCNTINCR(wlc->pub->_cnt->tbtt);
+
+ if (BSSCFG_STA(cfg)) {
+ /* run watchdog here if the watchdog timer is not armed */
+ if (WLC_WATCHDOG_TBTT(wlc)) {
+ u32 cur, delta;
+ if (wlc->WDarmed) {
+ wl_del_timer(wlc->wl, wlc->wdtimer);
+ wlc->WDarmed = false;
+ }
+
+ cur = OSL_SYSUPTIME();
+ delta = cur > wlc->WDlast ? cur - wlc->WDlast :
+ (u32) ~0 - wlc->WDlast + cur + 1;
+ if (delta >= TIMER_INTERVAL_WATCHDOG) {
+ wlc_watchdog((void *)wlc);
+ wlc->WDlast = cur;
+ }
+
+ wl_add_timer(wlc->wl, wlc->wdtimer,
+ wlc_watchdog_backup_bi(wlc), true);
+ wlc->WDarmed = true;
+ }
+ }
+
+ if (!cfg->BSS) {
+ /* DirFrmQ is now valid...defer setting until end of ATIM window */
+ wlc->qvalid |= MCMD_DIRFRMQVAL;
+ }
+}
+
+/* GP timer is a freerunning 32 bit counter, decrements at 1 us rate */
+void wlc_hwtimer_gptimer_set(wlc_info_t *wlc, uint us)
+{
+ ASSERT(wlc->pub->corerev >= 3); /* no gptimer in earlier revs */
+ W_REG(wlc->osh, &wlc->regs->gptimer, us);
+}
+
+void wlc_hwtimer_gptimer_abort(wlc_info_t *wlc)
+{
+ ASSERT(wlc->pub->corerev >= 3);
+ W_REG(wlc->osh, &wlc->regs->gptimer, 0);
+}
+
+static void wlc_hwtimer_gptimer_cb(wlc_info_t *wlc)
+{
+ /* when interrupt is generated, the counter is loaded with last value
+ * written and continue to decrement. So it has to be cleaned first
+ */
+ W_REG(wlc->osh, &wlc->regs->gptimer, 0);
+}
+
+/*
+ * This fn has all the high level dpc processing from wlc_dpc.
+ * POLICY: no macinstatus change, no bounding loop.
+ * All dpc bounding should be handled in BMAC dpc, like txstatus and rxint
+ */
+void wlc_high_dpc(wlc_info_t *wlc, u32 macintstatus)
+{
+ d11regs_t *regs = wlc->regs;
+#ifdef BCMDBG
+ char flagstr[128];
+ static const bcm_bit_desc_t int_flags[] = {
+ {MI_MACSSPNDD, "MACSSPNDD"},
+ {MI_BCNTPL, "BCNTPL"},
+ {MI_TBTT, "TBTT"},
+ {MI_BCNSUCCESS, "BCNSUCCESS"},
+ {MI_BCNCANCLD, "BCNCANCLD"},
+ {MI_ATIMWINEND, "ATIMWINEND"},
+ {MI_PMQ, "PMQ"},
+ {MI_NSPECGEN_0, "NSPECGEN_0"},
+ {MI_NSPECGEN_1, "NSPECGEN_1"},
+ {MI_MACTXERR, "MACTXERR"},
+ {MI_NSPECGEN_3, "NSPECGEN_3"},
+ {MI_PHYTXERR, "PHYTXERR"},
+ {MI_PME, "PME"},
+ {MI_GP0, "GP0"},
+ {MI_GP1, "GP1"},
+ {MI_DMAINT, "DMAINT"},
+ {MI_TXSTOP, "TXSTOP"},
+ {MI_CCA, "CCA"},
+ {MI_BG_NOISE, "BG_NOISE"},
+ {MI_DTIM_TBTT, "DTIM_TBTT"},
+ {MI_PRQ, "PRQ"},
+ {MI_PWRUP, "PWRUP"},
+ {MI_RFDISABLE, "RFDISABLE"},
+ {MI_TFS, "TFS"},
+ {MI_PHYCHANGED, "PHYCHANGED"},
+ {MI_TO, "TO"},
+ {0, NULL}
+ };
+
+ if (macintstatus & ~(MI_TBTT | MI_TXSTOP)) {
+ bcm_format_flags(int_flags, macintstatus, flagstr,
+ sizeof(flagstr));
+ WL_TRACE(("wl%d: macintstatus 0x%x %s\n", wlc->pub->unit,
+ macintstatus, flagstr));
+ }
+#endif /* BCMDBG */
+
+ if (macintstatus & MI_PRQ) {
+ /* Process probe request FIFO */
+ ASSERT(0 && "PRQ Interrupt in non-MBSS");
+ }
+
+ /* TBTT indication */
+ /* ucode only gives either TBTT or DTIM_TBTT, not both */
+ if (macintstatus & (MI_TBTT | MI_DTIM_TBTT))
+ wlc_tbtt(wlc, regs);
+
+ if (macintstatus & MI_GP0) {
+ WL_ERROR(("wl%d: PSM microcode watchdog fired at %d (seconds). Resetting.\n", wlc->pub->unit, wlc->pub->now));
+
+ printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n",
+ __func__, CHIPID(wlc->pub->sih->chip),
+ CHIPREV(wlc->pub->sih->chiprev));
+
+ WLCNTINCR(wlc->pub->_cnt->psmwds);
+
+ /* big hammer */
+ wl_init(wlc->wl);
+ }
+
+ /* gptimer timeout */
+ if (macintstatus & MI_TO) {
+ wlc_hwtimer_gptimer_cb(wlc);
+ }
+
+ if (macintstatus & MI_RFDISABLE) {
+ WL_ERROR(("wl%d: MAC Detected a change on the RF Disable Input 0x%x\n", wlc->pub->unit, R_REG(wlc->osh, &regs->phydebug) & PDBG_RFD));
+ /* delay the cleanup to wl_down in IBSS case */
+ if ((R_REG(wlc->osh, &regs->phydebug) & PDBG_RFD)) {
+ int idx;
+ wlc_bsscfg_t *bsscfg;
+ FOREACH_BSS(wlc, idx, bsscfg) {
+ if (!BSSCFG_STA(bsscfg) || !bsscfg->enable
+ || !bsscfg->BSS)
+ continue;
+ WL_ERROR(("wl%d: wlc_dpc: rfdisable -> wlc_bsscfg_disable()\n", wlc->pub->unit));
+ }
+ }
+ }
+
+ /* send any enq'd tx packets. Just makes sure to jump start tx */
+ if (!pktq_empty(&wlc->active_queue->q))
+ wlc_send_q(wlc, wlc->active_queue);
+
+#ifndef WLC_HIGH_ONLY
+ ASSERT(wlc_ps_check(wlc));
+#endif
+}
+
+static void *wlc_15420war(wlc_info_t *wlc, uint queue)
+{
+ hnddma_t *di;
+ void *p;
+
+ ASSERT(queue < NFIFO);
+
+ if ((D11REV_IS(wlc->pub->corerev, 4))
+ || (D11REV_GT(wlc->pub->corerev, 6)))
+ return NULL;
+
+ di = wlc->hw->di[queue];
+ ASSERT(di != NULL);
+
+ /* get next packet, ignoring XmtStatus.Curr */
+ p = dma_getnexttxp(di, HNDDMA_RANGE_ALL);
+
+ /* sw block tx dma */
+ dma_txblock(di);
+
+ /* if tx ring is now empty, reset and re-init the tx dma channel */
+ if (dma_txactive(wlc->hw->di[queue]) == 0) {
+ WLCNTINCR(wlc->pub->_cnt->txdmawar);
+ if (!dma_txreset(di))
+ WL_ERROR(("wl%d: %s: dma_txreset[%d]: cannot stop dma\n", wlc->pub->unit, __func__, queue));
+ dma_txinit(di);
+ }
+ return p;
+}
+
+static void wlc_war16165(wlc_info_t *wlc, bool tx)
+{
+ if (tx) {
+ /* the post-increment is used in STAY_AWAKE macro */
+ if (wlc->txpend16165war++ == 0)
+ wlc_set_ps_ctrl(wlc);
+ } else {
+ wlc->txpend16165war--;
+ if (wlc->txpend16165war == 0)
+ wlc_set_ps_ctrl(wlc);
+ }
+}
+
+/* process an individual tx_status_t */
+/* WLC_HIGH_API */
+bool BCMFASTPATH
+wlc_dotxstatus(wlc_info_t *wlc, tx_status_t *txs, u32 frm_tx2)
+{
+ void *p;
+ uint queue;
+ d11txh_t *txh;
+ struct scb *scb = NULL;
+ bool free_pdu;
+ osl_t *osh;
+ int tx_rts, tx_frame_count, tx_rts_count;
+ uint totlen, supr_status;
+ bool lastframe;
+ struct dot11_header *h;
+ u16 fc;
+ u16 mcl;
+ struct ieee80211_tx_info *tx_info;
+ struct ieee80211_tx_rate *txrate;
+ int i;
+
+ (void)(frm_tx2); /* Compiler reference to avoid unused variable warning */
+
+ /* discard intermediate indications for ucode with one legitimate case:
+ * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, but the subsequent
+ * tx of DATA failed. so it will start rts/cts from the beginning (resetting the rts
+ * transmission count)
+ */
+ if (!(txs->status & TX_STATUS_AMPDU)
+ && (txs->status & TX_STATUS_INTERMEDIATE)) {
+ WLCNTADD(wlc->pub->_cnt->txnoack,
+ ((txs->
+ status & TX_STATUS_FRM_RTX_MASK) >>
+ TX_STATUS_FRM_RTX_SHIFT));
+ WL_ERROR(("%s: INTERMEDIATE but not AMPDU\n", __func__));
+ return false;
+ }
+
+ osh = wlc->osh;
+ queue = txs->frameid & TXFID_QUEUE_MASK;
+ ASSERT(queue < NFIFO);
+ if (queue >= NFIFO) {
+ p = NULL;
+ goto fatal;
+ }
+
+ p = GETNEXTTXP(wlc, queue);
+ if (WLC_WAR16165(wlc))
+ wlc_war16165(wlc, false);
+ if (p == NULL)
+ p = wlc_15420war(wlc, queue);
+ ASSERT(p != NULL);
+ if (p == NULL)
+ goto fatal;
+
+ txh = (d11txh_t *) PKTDATA(p);
+ mcl = ltoh16(txh->MacTxControlLow);
+
+ if (txs->phyerr) {
+ WL_ERROR(("phyerr 0x%x, rate 0x%x\n", txs->phyerr,
+ txh->MainRates));
+ wlc_print_txdesc(txh);
+ wlc_print_txstatus(txs);
+ }
+
+ ASSERT(txs->frameid == htol16(txh->TxFrameID));
+ if (txs->frameid != htol16(txh->TxFrameID))
+ goto fatal;
+
+ tx_info = IEEE80211_SKB_CB(p);
+ h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+ fc = ltoh16(h->fc);
+
+ scb = (struct scb *)tx_info->control.sta->drv_priv;
+
+ if (N_ENAB(wlc->pub)) {
+ u8 *plcp = (u8 *) (txh + 1);
+ if (PLCP3_ISSGI(plcp[3]))
+ WLCNTINCR(wlc->pub->_cnt->txmpdu_sgi);
+ if (PLCP3_ISSTBC(plcp[3]))
+ WLCNTINCR(wlc->pub->_cnt->txmpdu_stbc);
+ }
+
+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+ ASSERT((mcl & TXC_AMPDU_MASK) != TXC_AMPDU_NONE);
+ wlc_ampdu_dotxstatus(wlc->ampdu, scb, p, txs);
+ return false;
+ }
+
+ supr_status = txs->status & TX_STATUS_SUPR_MASK;
+ if (supr_status == TX_STATUS_SUPR_BADCH)
+ WL_NONE(("%s: Pkt tx suppressed, possibly channel %d\n",
+ __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)));
+
+ tx_rts = htol16(txh->MacTxControlLow) & TXC_SENDRTS;
+ tx_frame_count =
+ (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT;
+ tx_rts_count =
+ (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT;
+
+ lastframe = (fc & FC_MOREFRAG) == 0;
+
+ if (!lastframe) {
+ WL_ERROR(("Not last frame!\n"));
+ } else {
+ u16 sfbl, lfbl;
+ ieee80211_tx_info_clear_status(tx_info);
+ if (queue < AC_COUNT) {
+ sfbl = WLC_WME_RETRY_SFB_GET(wlc, wme_fifo2ac[queue]);
+ lfbl = WLC_WME_RETRY_LFB_GET(wlc, wme_fifo2ac[queue]);
+ } else {
+ sfbl = wlc->SFBL;
+ lfbl = wlc->LFBL;
+ }
+
+ txrate = tx_info->status.rates;
+ /* FIXME: this should use a combination of sfbl, lfbl depending on frame length and RTS setting */
+ if ((tx_frame_count > sfbl) && (txrate[1].idx >= 0)) {
+ /* rate selection requested a fallback rate and we used it */
+ txrate->count = lfbl;
+ txrate[1].count = tx_frame_count - lfbl;
+ } else {
+ /* rate selection did not request fallback rate, or we didn't need it */
+ txrate->count = tx_frame_count;
+ /* rc80211_minstrel.c:minstrel_tx_status() expects unused rates to be marked with idx = -1 */
+ txrate[1].idx = -1;
+ txrate[1].count = 0;
+ }
+
+ /* clear the rest of the rates */
+ for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
+ txrate[i].idx = -1;
+ txrate[i].count = 0;
+ }
+
+ if (txs->status & TX_STATUS_ACK_RCV)
+ tx_info->flags |= IEEE80211_TX_STAT_ACK;
+ }
+
+ totlen = pkttotlen(osh, p);
+ free_pdu = true;
+
+ wlc_txfifo_complete(wlc, queue, 1);
+
+ if (lastframe) {
+ PKTSETNEXT(p, NULL);
+ PKTSETLINK(p, NULL);
+ wlc->txretried = 0;
+ /* remove PLCP & Broadcom tx descriptor header */
+ PKTPULL(p, D11_PHY_HDR_LEN);
+ PKTPULL(p, D11_TXH_LEN);
+ ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p);
+ WLCNTINCR(wlc->pub->_cnt->ieee_tx_status);
+ } else {
+ WL_ERROR(("%s: Not last frame => not calling tx_status\n",
+ __func__));
+ }
+
+ return false;
+
+ fatal:
+ ASSERT(0);
+ if (p)
+ PKTFREE(osh, p, true);
+
+#ifdef WLC_HIGH_ONLY
+ /* If this is a split driver, do the big-hammer here.
+ * If this is a monolithic driver, wlc_bmac.c:wlc_dpc() will do the big-hammer.
+ */
+ wl_init(wlc->wl);
+#endif
+ return true;
+
+}
+
+void BCMFASTPATH
+wlc_txfifo_complete(wlc_info_t *wlc, uint fifo, s8 txpktpend)
+{
+ TXPKTPENDDEC(wlc, fifo, txpktpend);
+ WL_TRACE(("wlc_txfifo_complete, pktpend dec %d to %d\n", txpktpend,
+ TXPKTPENDGET(wlc, fifo)));
+
+ /* There is more room; mark precedences related to this FIFO sendable */
+ WLC_TX_FIFO_ENAB(wlc, fifo);
+ ASSERT(TXPKTPENDGET(wlc, fifo) >= 0);
+
+ if (!TXPKTPENDTOT(wlc)) {
+ if (wlc->block_datafifo & DATA_BLOCK_TX_SUPR)
+ wlc_bsscfg_tx_check(wlc);
+ }
+
+ /* Clear MHF2_TXBCMC_NOW flag if BCMC fifo has drained */
+ if (AP_ENAB(wlc->pub) &&
+ wlc->bcmcfifo_drain && !TXPKTPENDGET(wlc, TX_BCMC_FIFO)) {
+ wlc->bcmcfifo_drain = false;
+ wlc_mhf(wlc, MHF2, MHF2_TXBCMC_NOW, 0, WLC_BAND_AUTO);
+ }
+
+ /* figure out which bsscfg is being worked on... */
+}
+
+/* Given the beacon interval in kus, and a 64 bit TSF in us,
+ * return the offset (in us) of the TSF from the last TBTT
+ */
+u32 wlc_calc_tbtt_offset(u32 bp, u32 tsf_h, u32 tsf_l)
+{
+ u32 k, btklo, btkhi, offset;
+
+ /* TBTT is always an even multiple of the beacon_interval,
+ * so the TBTT less than or equal to the beacon timestamp is
+ * the beacon timestamp minus the beacon timestamp modulo
+ * the beacon interval.
+ *
+ * TBTT = BT - (BT % BIu)
+ * = (BTk - (BTk % BP)) * 2^10
+ *
+ * BT = beacon timestamp (usec, 64bits)
+ * BTk = beacon timestamp (Kusec, 54bits)
+ * BP = beacon interval (Kusec, 16bits)
+ * BIu = BP * 2^10 = beacon interval (usec, 26bits)
+ *
+ * To keep the calculations in u32s, the modulo operation
+ * on the high part of BT needs to be done in parts using the
+ * relations:
+ * X*Y mod Z = ((X mod Z) * (Y mod Z)) mod Z
+ * and
+ * (X + Y) mod Z = ((X mod Z) + (Y mod Z)) mod Z
+ *
+ * So, if BTk[n] = u16 n [0,3] of BTk.
+ * BTk % BP = SUM((BTk[n] * 2^16n) % BP , 0<=n<4) % BP
+ * and the SUM term can be broken down:
+ * (BTk[n] * 2^16n) % BP
+ * (BTk[n] * (2^16n % BP)) % BP
+ *
+ * Create a set of power of 2 mod BP constants:
+ * K[n] = 2^(16n) % BP
+ * = (K[n-1] * 2^16) % BP
+ * K[2] = 2^32 % BP = ((2^16 % BP) * 2^16) % BP
+ *
+ * BTk % BP = BTk[0-1] % BP +
+ * (BTk[2] * K[2]) % BP +
+ * (BTk[3] * K[3]) % BP
+ *
+ * Since K[n] < 2^16 and BTk[n] is < 2^16, then BTk[n] * K[n] < 2^32
+ */
+
+ /* BTk = BT >> 10, btklo = BTk[0-3], bkthi = BTk[4-6] */
+ btklo = (tsf_h << 22) | (tsf_l >> 10);
+ btkhi = tsf_h >> 10;
+
+ /* offset = BTk % BP */
+ offset = btklo % bp;
+
+ /* K[2] = ((2^16 % BP) * 2^16) % BP */
+ k = (u32) (1 << 16) % bp;
+ k = (u32) (k * 1 << 16) % (u32) bp;
+
+ /* offset += (BTk[2] * K[2]) % BP */
+ offset += ((btkhi & 0xffff) * k) % bp;
+
+ /* BTk[3] */
+ btkhi = btkhi >> 16;
+
+ /* k[3] = (K[2] * 2^16) % BP */
+ k = (k << 16) % bp;
+
+ /* offset += (BTk[3] * K[3]) % BP */
+ offset += ((btkhi & 0xffff) * k) % bp;
+
+ offset = offset % bp;
+
+ /* convert offset from kus to us by shifting up 10 bits and
+ * add in the low 10 bits of tsf that we ignored
+ */
+ offset = (offset << 10) + (tsf_l & 0x3FF);
+
+ return offset;
+}
+
+/* Update beacon listen interval in shared memory */
+void wlc_bcn_li_upd(wlc_info_t *wlc)
+{
+ if (AP_ENAB(wlc->pub))
+ return;
+
+ /* wake up every DTIM is the default */
+ if (wlc->bcn_li_dtim == 1)
+ wlc_write_shm(wlc, M_BCN_LI, 0);
+ else
+ wlc_write_shm(wlc, M_BCN_LI,
+ (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn);
+}
+
+static void
+prep_mac80211_status(wlc_info_t *wlc, d11rxhdr_t *rxh, void *p,
+ struct ieee80211_rx_status *rx_status)
+{
+ u32 tsf_l, tsf_h;
+ wlc_d11rxhdr_t *wlc_rxh = (wlc_d11rxhdr_t *) rxh;
+ int preamble;
+ int channel;
+ ratespec_t rspec;
+ unsigned char *plcp;
+
+ wlc_read_tsf(wlc, &tsf_l, &tsf_h); /* mactime */
+ rx_status->mactime = tsf_h;
+ rx_status->mactime <<= 32;
+ rx_status->mactime |= tsf_l;
+ rx_status->flag |= RX_FLAG_TSFT;
+
+ channel = WLC_CHAN_CHANNEL(rxh->RxChan);
+
+ /* XXX Channel/badn needs to be filtered against whether we are single/dual band card */
+ if (channel > 14) {
+ rx_status->band = IEEE80211_BAND_5GHZ;
+ rx_status->freq = wf_channel2mhz(channel, WF_CHAN_FACTOR_5_G);
+ } else {
+ rx_status->band = IEEE80211_BAND_2GHZ;
+ rx_status->freq = wf_channel2mhz(channel, WF_CHAN_FACTOR_2_4_G);
+ }
+
+ rx_status->signal = wlc_rxh->rssi; /* signal */
+
+ /* noise */
+ /* qual */
+ rx_status->antenna = (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; /* ant */
+
+ plcp = PKTDATA(p);
+
+ rspec = wlc_compute_rspec(rxh, plcp);
+ if (IS_MCS(rspec)) {
+ rx_status->rate_idx = rspec & RSPEC_RATE_MASK;
+ rx_status->flag |= RX_FLAG_HT;
+ if (RSPEC_IS40MHZ(rspec))
+ rx_status->flag |= RX_FLAG_40MHZ;
+ } else {
+ switch (RSPEC2RATE(rspec)) {
+ case WLC_RATE_1M:
+ rx_status->rate_idx = 0;
+ break;
+ case WLC_RATE_2M:
+ rx_status->rate_idx = 1;
+ break;
+ case WLC_RATE_5M5:
+ rx_status->rate_idx = 2;
+ break;
+ case WLC_RATE_11M:
+ rx_status->rate_idx = 3;
+ break;
+ case WLC_RATE_6M:
+ rx_status->rate_idx = 4;
+ break;
+ case WLC_RATE_9M:
+ rx_status->rate_idx = 5;
+ break;
+ case WLC_RATE_12M:
+ rx_status->rate_idx = 6;
+ break;
+ case WLC_RATE_18M:
+ rx_status->rate_idx = 7;
+ break;
+ case WLC_RATE_24M:
+ rx_status->rate_idx = 8;
+ break;
+ case WLC_RATE_36M:
+ rx_status->rate_idx = 9;
+ break;
+ case WLC_RATE_48M:
+ rx_status->rate_idx = 10;
+ break;
+ case WLC_RATE_54M:
+ rx_status->rate_idx = 11;
+ break;
+ default:
+ WL_ERROR(("%s: Unknown rate\n", __func__));
+ }
+
+ /* Determine short preamble and rate_idx */
+ preamble = 0;
+ if (IS_CCK(rspec)) {
+ if (rxh->PhyRxStatus_0 & PRXS0_SHORTH)
+ WL_ERROR(("Short CCK\n"));
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ } else if (IS_OFDM(rspec)) {
+ rx_status->flag |= RX_FLAG_SHORTPRE;
+ } else {
+ WL_ERROR(("%s: Unknown modulation\n", __func__));
+ }
+ }
+
+ if (PLCP3_ISSGI(plcp[3]))
+ rx_status->flag |= RX_FLAG_SHORT_GI;
+
+ if (rxh->RxStatus1 & RXS_DECERR) {
+ rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ WL_ERROR(("%s: RX_FLAG_FAILED_PLCP_CRC\n", __func__));
+ }
+ if (rxh->RxStatus1 & RXS_FCSERR) {
+ rx_status->flag |= RX_FLAG_FAILED_FCS_CRC;
+ WL_ERROR(("%s: RX_FLAG_FAILED_FCS_CRC\n", __func__));
+ }
+}
+
+static void
+wlc_recvctl(wlc_info_t *wlc, osl_t *osh, d11rxhdr_t *rxh, void *p)
+{
+ int len_mpdu;
+ struct ieee80211_rx_status rx_status;
+#if defined(BCMDBG)
+ struct sk_buff *skb = p;
+#endif /* BCMDBG */
+ /* Todo:
+ * Cache plcp for first MPDU of AMPD and use chacched version for INTERMEDIATE.
+ * Test for INTERMEDIATE like so:
+ * if (!(plcp[0] | plcp[1] | plcp[2]))
+ */
+
+ memset(&rx_status, 0, sizeof(rx_status));
+ prep_mac80211_status(wlc, rxh, p, &rx_status);
+
+ /* mac header+body length, exclude CRC and plcp header */
+ len_mpdu = PKTLEN(p) - D11_PHY_HDR_LEN - DOT11_FCS_LEN;
+ PKTPULL(p, D11_PHY_HDR_LEN);
+ PKTSETLEN(p, len_mpdu);
+
+ ASSERT(!PKTNEXT(p));
+ ASSERT(!PKTLINK(p));
+
+ ASSERT(IS_ALIGNED((unsigned long)skb->data, 2));
+
+ memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p);
+
+ WLCNTINCR(wlc->pub->_cnt->ieee_rx);
+ PKTUNALLOC(osh);
+ return;
+}
+
+void wlc_bss_list_free(wlc_info_t *wlc, wlc_bss_list_t *bss_list)
+{
+ uint index;
+ wlc_bss_info_t *bi;
+
+ if (!bss_list) {
+ WL_ERROR(("%s: Attempting to free NULL list\n", __func__));
+ return;
+ }
+ /* inspect all BSS descriptor */
+ for (index = 0; index < bss_list->count; index++) {
+ bi = bss_list->ptrs[index];
+ if (bi) {
+ if (bi->bcn_prb) {
+ kfree(bi->bcn_prb);
+ }
+ kfree(bi);
+ bss_list->ptrs[index] = NULL;
+ }
+ }
+ bss_list->count = 0;
+}
+
+/* Process received frames */
+/*
+ * Return true if more frames need to be processed. false otherwise.
+ * Param 'bound' indicates max. # frames to process before break out.
+ */
+/* WLC_HIGH_API */
+void BCMFASTPATH wlc_recv(wlc_info_t *wlc, void *p)
+{
+ d11rxhdr_t *rxh;
+ struct dot11_header *h;
+ osl_t *osh;
+ u16 fc;
+ uint len;
+ bool is_amsdu;
+
+ WL_TRACE(("wl%d: wlc_recv\n", wlc->pub->unit));
+
+ osh = wlc->osh;
+
+ /* frame starts with rxhdr */
+ rxh = (d11rxhdr_t *) PKTDATA(p);
+
+ /* strip off rxhdr */
+ PKTPULL(p, wlc->hwrxoff);
+
+ /* fixup rx header endianness */
+ ltoh16_buf((void *)rxh, sizeof(d11rxhdr_t));
+
+ /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */
+ if (rxh->RxStatus1 & RXS_PBPRES) {
+ if (PKTLEN(p) < 2) {
+ WLCNTINCR(wlc->pub->_cnt->rxrunt);
+ WL_ERROR(("wl%d: wlc_recv: rcvd runt of len %d\n",
+ wlc->pub->unit, PKTLEN(p)));
+ goto toss;
+ }
+ PKTPULL(p, 2);
+ }
+
+ h = (struct dot11_header *)(PKTDATA(p) + D11_PHY_HDR_LEN);
+ len = PKTLEN(p);
+
+ if (rxh->RxStatus1 & RXS_FCSERR) {
+ if (wlc->pub->mac80211_state & MAC80211_PROMISC_BCNS) {
+ WL_ERROR(("FCSERR while scanning******* - tossing\n"));
+ goto toss;
+ } else {
+ WL_ERROR(("RCSERR!!!\n"));
+ goto toss;
+ }
+ }
+
+ /* check received pkt has at least frame control field */
+ if (len >= D11_PHY_HDR_LEN + sizeof(h->fc)) {
+ fc = ltoh16(h->fc);
+ } else {
+ WLCNTINCR(wlc->pub->_cnt->rxrunt);
+ goto toss;
+ }
+
+ is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK;
+
+ /* explicitly test bad src address to avoid sending bad deauth */
+ if (!is_amsdu) {
+ /* CTS and ACK CTL frames are w/o a2 */
+ if (FC_TYPE(fc) == FC_TYPE_DATA || FC_TYPE(fc) == FC_TYPE_MNG) {
+ if ((ETHER_ISNULLADDR(&h->a2) || ETHER_ISMULTI(&h->a2))) {
+ WL_ERROR(("wl%d: %s: dropping a frame with "
+ "invalid src mac address, a2: %pM\n",
+ wlc->pub->unit, __func__, &h->a2));
+ WLCNTINCR(wlc->pub->_cnt->rxbadsrcmac);
+ goto toss;
+ }
+ WLCNTINCR(wlc->pub->_cnt->rxfrag);
+ }
+ }
+
+ /* due to sheer numbers, toss out probe reqs for now */
+ if (FC_TYPE(fc) == FC_TYPE_MNG) {
+ if ((fc & FC_KIND_MASK) == FC_PROBE_REQ)
+ goto toss;
+ }
+
+ if (is_amsdu) {
+ WL_ERROR(("%s: is_amsdu causing toss\n", __func__));
+ goto toss;
+ }
+
+ wlc_recvctl(wlc, osh, rxh, p);
+ return;
+
+ toss:
+ PKTFREE(osh, p, false);
+}
+
+/* calculate frame duration for Mixed-mode L-SIG spoofing, return
+ * number of bytes goes in the length field
+ *
+ * Formula given by HT PHY Spec v 1.13
+ * len = 3(nsyms + nstream + 3) - 3
+ */
+u16 BCMFASTPATH
+wlc_calc_lsig_len(wlc_info_t *wlc, ratespec_t ratespec, uint mac_len)
+{
+ uint nsyms, len = 0, kNdps;
+
+ WL_TRACE(("wl%d: wlc_calc_lsig_len: rate %d, len%d\n", wlc->pub->unit,
+ RSPEC2RATE(ratespec), mac_len));
+
+ if (IS_MCS(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ /* MCS_TXS(mcs) returns num tx streams - 1 */
+ int tot_streams = (MCS_TXS(mcs) + 1) + RSPEC_STC(ratespec);
+
+ ASSERT(WLC_PHY_11N_CAP(wlc->band));
+ /* the payload duration calculation matches that of regular ofdm */
+ /* 1000Ndbps = kbps * 4 */
+ kNdps =
+ MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
+ RSPEC_ISSGI(ratespec)) * 4;
+
+ if (RSPEC_STC(ratespec) == 0)
+ /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
+ nsyms =
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, kNdps);
+ else
+ /* STBC needs to have even number of symbols */
+ nsyms =
+ 2 *
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, 2 * kNdps);
+
+ nsyms += (tot_streams + 3); /* (+3) account for HT-SIG(2) and HT-STF(1) */
+ /* 3 bytes/symbol @ legacy 6Mbps rate */
+ len = (3 * nsyms) - 3; /* (-3) excluding service bits and tail bits */
+ }
+
+ return (u16) len;
+}
+
+/* calculate frame duration of a given rate and length, return time in usec unit */
+uint BCMFASTPATH
+wlc_calc_frame_time(wlc_info_t *wlc, ratespec_t ratespec, u8 preamble_type,
+ uint mac_len)
+{
+ uint nsyms, dur = 0, Ndps, kNdps;
+ uint rate = RSPEC2RATE(ratespec);
+
+ if (rate == 0) {
+ ASSERT(0);
+ WL_ERROR(("wl%d: WAR: using rate of 1 mbps\n", wlc->pub->unit));
+ rate = WLC_RATE_1M;
+ }
+
+ WL_TRACE(("wl%d: wlc_calc_frame_time: rspec 0x%x, preamble_type %d, len%d\n", wlc->pub->unit, ratespec, preamble_type, mac_len));
+
+ if (IS_MCS(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ int tot_streams = MCS_TXS(mcs) + RSPEC_STC(ratespec);
+ ASSERT(WLC_PHY_11N_CAP(wlc->band));
+ ASSERT(WLC_IS_MIMO_PREAMBLE(preamble_type));
+
+ dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
+ if (preamble_type == WLC_MM_PREAMBLE)
+ dur += PREN_MM_EXT;
+ /* 1000Ndbps = kbps * 4 */
+ kNdps =
+ MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
+ RSPEC_ISSGI(ratespec)) * 4;
+
+ if (RSPEC_STC(ratespec) == 0)
+ /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
+ nsyms =
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, kNdps);
+ else
+ /* STBC needs to have even number of symbols */
+ nsyms =
+ 2 *
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
+ APHY_TAIL_NBITS) * 1000, 2 * kNdps);
+
+ dur += APHY_SYMBOL_TIME * nsyms;
+ if (BAND_2G(wlc->band->bandtype))
+ dur += DOT11_OFDM_SIGNAL_EXTENSION;
+ } else if (IS_OFDM(rate)) {
+ dur = APHY_PREAMBLE_TIME;
+ dur += APHY_SIGNAL_TIME;
+ /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
+ Ndps = rate * 2;
+ /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
+ nsyms =
+ CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS),
+ Ndps);
+ dur += APHY_SYMBOL_TIME * nsyms;
+ if (BAND_2G(wlc->band->bandtype))
+ dur += DOT11_OFDM_SIGNAL_EXTENSION;
+ } else {
+ /* calc # bits * 2 so factor of 2 in rate (1/2 mbps) will divide out */
+ mac_len = mac_len * 8 * 2;
+ /* calc ceiling of bits/rate = microseconds of air time */
+ dur = (mac_len + rate - 1) / rate;
+ if (preamble_type & WLC_SHORT_PREAMBLE)
+ dur += BPHY_PLCP_SHORT_TIME;
+ else
+ dur += BPHY_PLCP_TIME;
+ }
+ return dur;
+}
+
+/* The opposite of wlc_calc_frame_time */
+static uint
+wlc_calc_frame_len(wlc_info_t *wlc, ratespec_t ratespec, u8 preamble_type,
+ uint dur)
+{
+ uint nsyms, mac_len, Ndps, kNdps;
+ uint rate = RSPEC2RATE(ratespec);
+
+ WL_TRACE(("wl%d: wlc_calc_frame_len: rspec 0x%x, preamble_type %d, dur %d\n", wlc->pub->unit, ratespec, preamble_type, dur));
+
+ if (IS_MCS(ratespec)) {
+ uint mcs = ratespec & RSPEC_RATE_MASK;
+ int tot_streams = MCS_TXS(mcs) + RSPEC_STC(ratespec);
+ ASSERT(WLC_PHY_11N_CAP(wlc->band));
+ dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
+ /* payload calculation matches that of regular ofdm */
+ if (BAND_2G(wlc->band->bandtype))
+ dur -= DOT11_OFDM_SIGNAL_EXTENSION;
+ /* kNdbps = kbps * 4 */
+ kNdps =
+ MCS_RATE(mcs, RSPEC_IS40MHZ(ratespec),
+ RSPEC_ISSGI(ratespec)) * 4;
+ nsyms = dur / APHY_SYMBOL_TIME;
+ mac_len =
+ ((nsyms * kNdps) -
+ ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000;
+ } else if (IS_OFDM(ratespec)) {
+ dur -= APHY_PREAMBLE_TIME;
+ dur -= APHY_SIGNAL_TIME;
+ /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
+ Ndps = rate * 2;
+ nsyms = dur / APHY_SYMBOL_TIME;
+ mac_len =
+ ((nsyms * Ndps) -
+ (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8;
+ } else {
+ if (preamble_type & WLC_SHORT_PREAMBLE)
+ dur -= BPHY_PLCP_SHORT_TIME;
+ else
+ dur -= BPHY_PLCP_TIME;
+ mac_len = dur * rate;
+ /* divide out factor of 2 in rate (1/2 mbps) */
+ mac_len = mac_len / 8 / 2;
+ }
+ return mac_len;
+}
+
+static uint
+wlc_calc_ba_time(wlc_info_t *wlc, ratespec_t rspec, u8 preamble_type)
+{
+ WL_TRACE(("wl%d: wlc_calc_ba_time: rspec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type));
+ /* Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that is less than
+ * or equal to the rate of the immediately previous frame in the FES
+ */
+ rspec = WLC_BASIC_RATE(wlc, rspec);
+ ASSERT(VALID_RATE_DBG(wlc, rspec));
+
+ /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */
+ return wlc_calc_frame_time(wlc, rspec, preamble_type,
+ (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN +
+ DOT11_FCS_LEN));
+}
+
+static uint BCMFASTPATH
+wlc_calc_ack_time(wlc_info_t *wlc, ratespec_t rspec, u8 preamble_type)
+{
+ uint dur = 0;
+
+ WL_TRACE(("wl%d: wlc_calc_ack_time: rspec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type));
+ /* Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that is less than
+ * or equal to the rate of the immediately previous frame in the FES
+ */
+ rspec = WLC_BASIC_RATE(wlc, rspec);
+ ASSERT(VALID_RATE_DBG(wlc, rspec));
+
+ /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */
+ dur =
+ wlc_calc_frame_time(wlc, rspec, preamble_type,
+ (DOT11_ACK_LEN + DOT11_FCS_LEN));
+ return dur;
+}
+
+static uint
+wlc_calc_cts_time(wlc_info_t *wlc, ratespec_t rspec, u8 preamble_type)
+{
+ WL_TRACE(("wl%d: wlc_calc_cts_time: ratespec 0x%x, preamble_type %d\n",
+ wlc->pub->unit, rspec, preamble_type));
+ return wlc_calc_ack_time(wlc, rspec, preamble_type);
+}
+
+/* derive wlc->band->basic_rate[] table from 'rateset' */
+void wlc_rate_lookup_init(wlc_info_t *wlc, wlc_rateset_t *rateset)
+{
+ u8 rate;
+ u8 mandatory;
+ u8 cck_basic = 0;
+ u8 ofdm_basic = 0;
+ u8 *br = wlc->band->basic_rate;
+ uint i;
+
+ /* incoming rates are in 500kbps units as in 802.11 Supported Rates */
+ bzero(br, WLC_MAXRATE + 1);
+
+ /* For each basic rate in the rates list, make an entry in the
+ * best basic lookup.
+ */
+ for (i = 0; i < rateset->count; i++) {
+ /* only make an entry for a basic rate */
+ if (!(rateset->rates[i] & WLC_RATE_FLAG))
+ continue;
+
+ /* mask off basic bit */
+ rate = (rateset->rates[i] & RATE_MASK);
+
+ if (rate > WLC_MAXRATE) {
+ WL_ERROR(("wlc_rate_lookup_init: invalid rate 0x%X in rate set\n", rateset->rates[i]));
+ continue;
+ }
+
+ br[rate] = rate;
+ }
+
+ /* The rate lookup table now has non-zero entries for each
+ * basic rate, equal to the basic rate: br[basicN] = basicN
+ *
+ * To look up the best basic rate corresponding to any
+ * particular rate, code can use the basic_rate table
+ * like this
+ *
+ * basic_rate = wlc->band->basic_rate[tx_rate]
+ *
+ * Make sure there is a best basic rate entry for
+ * every rate by walking up the table from low rates
+ * to high, filling in holes in the lookup table
+ */
+
+ for (i = 0; i < wlc->band->hw_rateset.count; i++) {
+ rate = wlc->band->hw_rateset.rates[i];
+ ASSERT(rate <= WLC_MAXRATE);
+
+ if (br[rate] != 0) {
+ /* This rate is a basic rate.
+ * Keep track of the best basic rate so far by
+ * modulation type.
+ */
+ if (IS_OFDM(rate))
+ ofdm_basic = rate;
+ else
+ cck_basic = rate;
+
+ continue;
+ }
+
+ /* This rate is not a basic rate so figure out the
+ * best basic rate less than this rate and fill in
+ * the hole in the table
+ */
+
+ br[rate] = IS_OFDM(rate) ? ofdm_basic : cck_basic;
+
+ if (br[rate] != 0)
+ continue;
+
+ if (IS_OFDM(rate)) {
+ /* In 11g and 11a, the OFDM mandatory rates are 6, 12, and 24 Mbps */
+ if (rate >= WLC_RATE_24M)
+ mandatory = WLC_RATE_24M;
+ else if (rate >= WLC_RATE_12M)
+ mandatory = WLC_RATE_12M;
+ else
+ mandatory = WLC_RATE_6M;
+ } else {
+ /* In 11b, all the CCK rates are mandatory 1 - 11 Mbps */
+ mandatory = rate;
+ }
+
+ br[rate] = mandatory;
+ }
+}
+
+static void wlc_write_rate_shm(wlc_info_t *wlc, u8 rate, u8 basic_rate)
+{
+ u8 phy_rate, index;
+ u8 basic_phy_rate, basic_index;
+ u16 dir_table, basic_table;
+ u16 basic_ptr;
+
+ /* Shared memory address for the table we are reading */
+ dir_table = IS_OFDM(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B;
+
+ /* Shared memory address for the table we are writing */
+ basic_table = IS_OFDM(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B;
+
+ /*
+ * for a given rate, the LS-nibble of the PLCP SIGNAL field is
+ * the index into the rate table.
+ */
+ phy_rate = rate_info[rate] & RATE_MASK;
+ basic_phy_rate = rate_info[basic_rate] & RATE_MASK;
+ index = phy_rate & 0xf;
+ basic_index = basic_phy_rate & 0xf;
+
+ /* Find the SHM pointer to the ACK rate entry by looking in the
+ * Direct-map Table
+ */
+ basic_ptr = wlc_read_shm(wlc, (dir_table + basic_index * 2));
+
+ /* Update the SHM BSS-basic-rate-set mapping table with the pointer
+ * to the correct basic rate for the given incoming rate
+ */
+ wlc_write_shm(wlc, (basic_table + index * 2), basic_ptr);
+}
+
+static const wlc_rateset_t *wlc_rateset_get_hwrs(wlc_info_t *wlc)
+{
+ const wlc_rateset_t *rs_dflt;
+
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+ if (BAND_5G(wlc->band->bandtype))
+ rs_dflt = &ofdm_mimo_rates;
+ else
+ rs_dflt = &cck_ofdm_mimo_rates;
+ } else if (wlc->band->gmode)
+ rs_dflt = &cck_ofdm_rates;
+ else
+ rs_dflt = &cck_rates;
+
+ return rs_dflt;
+}
+
+void wlc_set_ratetable(wlc_info_t *wlc)
+{
+ const wlc_rateset_t *rs_dflt;
+ wlc_rateset_t rs;
+ u8 rate, basic_rate;
+ uint i;
+
+ rs_dflt = wlc_rateset_get_hwrs(wlc);
+ ASSERT(rs_dflt != NULL);
+
+ wlc_rateset_copy(rs_dflt, &rs);
+ wlc_rateset_mcs_upd(&rs, wlc->stf->txstreams);
+
+ /* walk the phy rate table and update SHM basic rate lookup table */
+ for (i = 0; i < rs.count; i++) {
+ rate = rs.rates[i] & RATE_MASK;
+
+ /* for a given rate WLC_BASIC_RATE returns the rate at
+ * which a response ACK/CTS should be sent.
+ */
+ basic_rate = WLC_BASIC_RATE(wlc, rate);
+ if (basic_rate == 0) {
+ /* This should only happen if we are using a
+ * restricted rateset.
+ */
+ basic_rate = rs.rates[0] & RATE_MASK;
+ }
+
+ wlc_write_rate_shm(wlc, rate, basic_rate);
+ }
+}
+
+/*
+ * Return true if the specified rate is supported by the specified band.
+ * WLC_BAND_AUTO indicates the current band.
+ */
+bool wlc_valid_rate(wlc_info_t *wlc, ratespec_t rspec, int band, bool verbose)
+{
+ wlc_rateset_t *hw_rateset;
+ uint i;
+
+ if ((band == WLC_BAND_AUTO) || (band == wlc->band->bandtype)) {
+ hw_rateset = &wlc->band->hw_rateset;
+ } else if (NBANDS(wlc) > 1) {
+ hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset;
+ } else {
+ /* other band specified and we are a single band device */
+ return false;
+ }
+
+ /* check if this is a mimo rate */
+ if (IS_MCS(rspec)) {
+ if (!VALID_MCS((rspec & RSPEC_RATE_MASK)))
+ goto error;
+
+ return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK));
+ }
+
+ for (i = 0; i < hw_rateset->count; i++)
+ if (hw_rateset->rates[i] == RSPEC2RATE(rspec))
+ return true;
+ error:
+ if (verbose) {
+ WL_ERROR(("wl%d: wlc_valid_rate: rate spec 0x%x not in hw_rateset\n", wlc->pub->unit, rspec));
+ }
+
+ return false;
+}
+
+static void wlc_update_mimo_band_bwcap(wlc_info_t *wlc, u8 bwcap)
+{
+ uint i;
+ wlcband_t *band;
+
+ for (i = 0; i < NBANDS(wlc); i++) {
+ if (IS_SINGLEBAND_5G(wlc->deviceid))
+ i = BAND_5G_INDEX;
+ band = wlc->bandstate[i];
+ if (band->bandtype == WLC_BAND_5G) {
+ if ((bwcap == WLC_N_BW_40ALL)
+ || (bwcap == WLC_N_BW_20IN2G_40IN5G))
+ band->mimo_cap_40 = true;
+ else
+ band->mimo_cap_40 = false;
+ } else {
+ ASSERT(band->bandtype == WLC_BAND_2G);
+ if (bwcap == WLC_N_BW_40ALL)
+ band->mimo_cap_40 = true;
+ else
+ band->mimo_cap_40 = false;
+ }
+ }
+
+ wlc->mimo_band_bwcap = bwcap;
+}
+
+void wlc_mod_prb_rsp_rate_table(wlc_info_t *wlc, uint frame_len)
+{
+ const wlc_rateset_t *rs_dflt;
+ wlc_rateset_t rs;
+ u8 rate;
+ u16 entry_ptr;
+ u8 plcp[D11_PHY_HDR_LEN];
+ u16 dur, sifs;
+ uint i;
+
+ sifs = SIFS(wlc->band);
+
+ rs_dflt = wlc_rateset_get_hwrs(wlc);
+ ASSERT(rs_dflt != NULL);
+
+ wlc_rateset_copy(rs_dflt, &rs);
+ wlc_rateset_mcs_upd(&rs, wlc->stf->txstreams);
+
+ /* walk the phy rate table and update MAC core SHM basic rate table entries */
+ for (i = 0; i < rs.count; i++) {
+ rate = rs.rates[i] & RATE_MASK;
+
+ entry_ptr = wlc_rate_shm_offset(wlc, rate);
+
+ /* Calculate the Probe Response PLCP for the given rate */
+ wlc_compute_plcp(wlc, rate, frame_len, plcp);
+
+ /* Calculate the duration of the Probe Response frame plus SIFS for the MAC */
+ dur =
+ (u16) wlc_calc_frame_time(wlc, rate, WLC_LONG_PREAMBLE,
+ frame_len);
+ dur += sifs;
+
+ /* Update the SHM Rate Table entry Probe Response values */
+ wlc_write_shm(wlc, entry_ptr + M_RT_PRS_PLCP_POS,
+ (u16) (plcp[0] + (plcp[1] << 8)));
+ wlc_write_shm(wlc, entry_ptr + M_RT_PRS_PLCP_POS + 2,
+ (u16) (plcp[2] + (plcp[3] << 8)));
+ wlc_write_shm(wlc, entry_ptr + M_RT_PRS_DUR_POS, dur);
+ }
+}
+
+u16
+wlc_compute_bcntsfoff(wlc_info_t *wlc, ratespec_t rspec, bool short_preamble,
+ bool phydelay)
+{
+ uint bcntsfoff = 0;
+
+ if (IS_MCS(rspec)) {
+ WL_ERROR(("wl%d: recd beacon with mcs rate; rspec 0x%x\n",
+ wlc->pub->unit, rspec));
+ } else if (IS_OFDM(rspec)) {
+ /* tx delay from MAC through phy to air (2.1 usec) +
+ * phy header time (preamble + PLCP SIGNAL == 20 usec) +
+ * PLCP SERVICE + MAC header time (SERVICE + FC + DUR + A1 + A2 + A3 + SEQ == 26
+ * bytes at beacon rate)
+ */
+ bcntsfoff += phydelay ? D11A_PHY_TX_DELAY : 0;
+ bcntsfoff += APHY_PREAMBLE_TIME + APHY_SIGNAL_TIME;
+ bcntsfoff +=
+ wlc_compute_airtime(wlc, rspec,
+ APHY_SERVICE_NBITS / 8 +
+ DOT11_MAC_HDR_LEN);
+ } else {
+ /* tx delay from MAC through phy to air (3.4 usec) +
+ * phy header time (long preamble + PLCP == 192 usec) +
+ * MAC header time (FC + DUR + A1 + A2 + A3 + SEQ == 24 bytes at beacon rate)
+ */
+ bcntsfoff += phydelay ? D11B_PHY_TX_DELAY : 0;
+ bcntsfoff +=
+ short_preamble ? D11B_PHY_SPREHDR_TIME :
+ D11B_PHY_LPREHDR_TIME;
+ bcntsfoff += wlc_compute_airtime(wlc, rspec, DOT11_MAC_HDR_LEN);
+ }
+ return (u16) (bcntsfoff);
+}
+
+/* Max buffering needed for beacon template/prb resp template is 142 bytes.
+ *
+ * PLCP header is 6 bytes.
+ * 802.11 A3 header is 24 bytes.
+ * Max beacon frame body template length is 112 bytes.
+ * Max probe resp frame body template length is 110 bytes.
+ *
+ * *len on input contains the max length of the packet available.
+ *
+ * The *len value is set to the number of bytes in buf used, and starts with the PLCP
+ * and included up to, but not including, the 4 byte FCS.
+ */
+static void
+wlc_bcn_prb_template(wlc_info_t *wlc, uint type, ratespec_t bcn_rspec,
+ wlc_bsscfg_t *cfg, u16 *buf, int *len)
+{
+ cck_phy_hdr_t *plcp;
+ struct dot11_management_header *h;
+ int hdr_len, body_len;
+
+ ASSERT(*len >= 142);
+ ASSERT(type == FC_BEACON || type == FC_PROBE_RESP);
+
+ if (MBSS_BCN_ENAB(cfg) && type == FC_BEACON)
+ hdr_len = DOT11_MAC_HDR_LEN;
+ else
+ hdr_len = D11_PHY_HDR_LEN + DOT11_MAC_HDR_LEN;
+ body_len = *len - hdr_len; /* calc buffer size provided for frame body */
+
+ *len = hdr_len + body_len; /* return actual size */
+
+ /* format PHY and MAC headers */
+ bzero((char *)buf, hdr_len);
+
+ plcp = (cck_phy_hdr_t *) buf;
+
+ /* PLCP for Probe Response frames are filled in from core's rate table */
+ if (type == FC_BEACON && !MBSS_BCN_ENAB(cfg)) {
+ /* fill in PLCP */
+ wlc_compute_plcp(wlc, bcn_rspec,
+ (DOT11_MAC_HDR_LEN + body_len + DOT11_FCS_LEN),
+ (u8 *) plcp);
+
+ }
+ /* "Regular" and 16 MBSS but not for 4 MBSS */
+ /* Update the phytxctl for the beacon based on the rspec */
+ if (!SOFTBCN_ENAB(cfg))
+ wlc_beacon_phytxctl_txant_upd(wlc, bcn_rspec);
+
+ if (MBSS_BCN_ENAB(cfg) && type == FC_BEACON)
+ h = (struct dot11_management_header *)&plcp[0];
+ else
+ h = (struct dot11_management_header *)&plcp[1];
+
+ /* fill in 802.11 header */
+ h->fc = htol16((u16) type);
+
+ /* DUR is 0 for multicast bcn, or filled in by MAC for prb resp */
+ /* A1 filled in by MAC for prb resp, broadcast for bcn */
+ if (type == FC_BEACON)
+ bcopy((const char *)&ether_bcast, (char *)&h->da,
+ ETHER_ADDR_LEN);
+ bcopy((char *)&cfg->cur_etheraddr, (char *)&h->sa, ETHER_ADDR_LEN);
+ bcopy((char *)&cfg->BSSID, (char *)&h->bssid, ETHER_ADDR_LEN);
+
+ /* SEQ filled in by MAC */
+
+ return;
+}
+
+int wlc_get_header_len()
+{
+ return TXOFF;
+}
+
+/* Update a beacon for a particular BSS
+ * For MBSS, this updates the software template and sets "latest" to the index of the
+ * template updated.
+ * Otherwise, it updates the hardware template.
+ */
+void wlc_bss_update_beacon(wlc_info_t *wlc, wlc_bsscfg_t *cfg)
+{
+ int len = BCN_TMPL_LEN;
+
+ /* Clear the soft intmask */
+ wlc->defmacintmask &= ~MI_BCNTPL;
+
+ if (!cfg->up) { /* Only allow updates on an UP bss */
+ return;
+ }
+
+ if (MBSS_BCN_ENAB(cfg)) { /* Optimize: Some of if/else could be combined */
+ } else if (HWBCN_ENAB(cfg)) { /* Hardware beaconing for this config */
+ u16 bcn[BCN_TMPL_LEN / 2];
+ u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD;
+ d11regs_t *regs = wlc->regs;
+ osl_t *osh = NULL;
+
+ osh = wlc->osh;
+
+ /* Check if both templates are in use, if so sched. an interrupt
+ * that will call back into this routine
+ */
+ if ((R_REG(osh, &regs->maccommand) & both_valid) == both_valid) {
+ /* clear any previous status */
+ W_REG(osh, &regs->macintstatus, MI_BCNTPL);
+ }
+ /* Check that after scheduling the interrupt both of the
+ * templates are still busy. if not clear the int. & remask
+ */
+ if ((R_REG(osh, &regs->maccommand) & both_valid) == both_valid) {
+ wlc->defmacintmask |= MI_BCNTPL;
+ return;
+ }
+
+ wlc->bcn_rspec =
+ wlc_lowest_basic_rspec(wlc, &cfg->current_bss->rateset);
+ ASSERT(wlc_valid_rate
+ (wlc, wlc->bcn_rspec,
+ CHSPEC_IS2G(cfg->current_bss->
+ chanspec) ? WLC_BAND_2G : WLC_BAND_5G,
+ true));
+
+ /* update the template and ucode shm */
+ wlc_bcn_prb_template(wlc, FC_BEACON, wlc->bcn_rspec, cfg, bcn,
+ &len);
+ wlc_write_hw_bcntemplates(wlc, bcn, len, false);
+ }
+}
+
+/*
+ * Update all beacons for the system.
+ */
+void wlc_update_beacon(wlc_info_t *wlc)
+{
+ int idx;
+ wlc_bsscfg_t *bsscfg;
+
+ /* update AP or IBSS beacons */
+ FOREACH_BSS(wlc, idx, bsscfg) {
+ if (bsscfg->up && (BSSCFG_AP(bsscfg) || !bsscfg->BSS))
+ wlc_bss_update_beacon(wlc, bsscfg);
+ }
+}
+
+/* Write ssid into shared memory */
+void wlc_shm_ssid_upd(wlc_info_t *wlc, wlc_bsscfg_t *cfg)
+{
+ u8 *ssidptr = cfg->SSID;
+ u16 base = M_SSID;
+ u8 ssidbuf[DOT11_MAX_SSID_LEN];
+
+ /* padding the ssid with zero and copy it into shm */
+ bzero(ssidbuf, DOT11_MAX_SSID_LEN);
+ bcopy(ssidptr, ssidbuf, cfg->SSID_len);
+
+ wlc_copyto_shm(wlc, base, ssidbuf, DOT11_MAX_SSID_LEN);
+
+ if (!MBSS_BCN_ENAB(cfg))
+ wlc_write_shm(wlc, M_SSIDLEN, (u16) cfg->SSID_len);
+}
+
+void wlc_update_probe_resp(wlc_info_t *wlc, bool suspend)
+{
+ int idx;
+ wlc_bsscfg_t *bsscfg;
+
+ /* update AP or IBSS probe responses */
+ FOREACH_BSS(wlc, idx, bsscfg) {
+ if (bsscfg->up && (BSSCFG_AP(bsscfg) || !bsscfg->BSS))
+ wlc_bss_update_probe_resp(wlc, bsscfg, suspend);
+ }
+}
+
+void
+wlc_bss_update_probe_resp(wlc_info_t *wlc, wlc_bsscfg_t *cfg, bool suspend)
+{
+ u16 prb_resp[BCN_TMPL_LEN / 2];
+ int len = BCN_TMPL_LEN;
+
+ /* write the probe response to hardware, or save in the config structure */
+ if (!MBSS_PRB_ENAB(cfg)) {
+
+ /* create the probe response template */
+ wlc_bcn_prb_template(wlc, FC_PROBE_RESP, 0, cfg, prb_resp,
+ &len);
+
+ if (suspend)
+ wlc_suspend_mac_and_wait(wlc);
+
+ /* write the probe response into the template region */
+ wlc_bmac_write_template_ram(wlc->hw, T_PRS_TPL_BASE,
+ (len + 3) & ~3, prb_resp);
+
+ /* write the length of the probe response frame (+PLCP/-FCS) */
+ wlc_write_shm(wlc, M_PRB_RESP_FRM_LEN, (u16) len);
+
+ /* write the SSID and SSID length */
+ wlc_shm_ssid_upd(wlc, cfg);
+
+ /*
+ * Write PLCP headers and durations for probe response frames at all rates.
+ * Use the actual frame length covered by the PLCP header for the call to
+ * wlc_mod_prb_rsp_rate_table() by subtracting the PLCP len and adding the FCS.
+ */
+ len += (-D11_PHY_HDR_LEN + DOT11_FCS_LEN);
+ wlc_mod_prb_rsp_rate_table(wlc, (u16) len);
+
+ if (suspend)
+ wlc_enable_mac(wlc);
+ } else { /* Generating probe resp in sw; update local template */
+ ASSERT(0 && "No software probe response support without MBSS");
+ }
+}
+
+/* prepares pdu for transmission. returns BCM error codes */
+int wlc_prep_pdu(wlc_info_t *wlc, void *pdu, uint *fifop)
+{
+ osl_t *osh;
+ uint fifo;
+ d11txh_t *txh;
+ struct dot11_header *h;
+ struct scb *scb;
+ u16 fc;
+
+ osh = wlc->osh;
+
+ ASSERT(pdu);
+ txh = (d11txh_t *) PKTDATA(pdu);
+ ASSERT(txh);
+ h = (struct dot11_header *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+ ASSERT(h);
+ fc = ltoh16(h->fc);
+
+ /* get the pkt queue info. This was put at wlc_sendctl or wlc_send for PDU */
+ fifo = ltoh16(txh->TxFrameID) & TXFID_QUEUE_MASK;
+
+ scb = NULL;
+
+ *fifop = fifo;
+
+ /* return if insufficient dma resources */
+ if (TXAVAIL(wlc, fifo) < MAX_DMA_SEGS) {
+ /* Mark precedences related to this FIFO, unsendable */
+ WLC_TX_FIFO_CLEAR(wlc, fifo);
+ return BCME_BUSY;
+ }
+
+ if (FC_TYPE(ltoh16(txh->MacFrameControl)) != FC_TYPE_DATA)
+ WLCNTINCR(wlc->pub->_cnt->txctl);
+
+ return 0;
+}
+
+/* init tx reported rate mechanism */
+void wlc_reprate_init(wlc_info_t *wlc)
+{
+ int i;
+ wlc_bsscfg_t *bsscfg;
+
+ FOREACH_BSS(wlc, i, bsscfg) {
+ wlc_bsscfg_reprate_init(bsscfg);
+ }
+}
+
+/* per bsscfg init tx reported rate mechanism */
+void wlc_bsscfg_reprate_init(wlc_bsscfg_t *bsscfg)
+{
+ bsscfg->txrspecidx = 0;
+ bzero((char *)bsscfg->txrspec, sizeof(bsscfg->txrspec));
+}
+
+/* Retrieve a consolidated set of revision information,
+ * typically for the WLC_GET_REVINFO ioctl
+ */
+int wlc_get_revision_info(wlc_info_t *wlc, void *buf, uint len)
+{
+ wlc_rev_info_t *rinfo = (wlc_rev_info_t *) buf;
+
+ if (len < WL_REV_INFO_LEGACY_LENGTH)
+ return BCME_BUFTOOSHORT;
+
+ rinfo->vendorid = wlc->vendorid;
+ rinfo->deviceid = wlc->deviceid;
+ rinfo->radiorev = (wlc->band->radiorev << IDCODE_REV_SHIFT) |
+ (wlc->band->radioid << IDCODE_ID_SHIFT);
+ rinfo->chiprev = wlc->pub->sih->chiprev;
+ rinfo->corerev = wlc->pub->corerev;
+ rinfo->boardid = wlc->pub->sih->boardtype;
+ rinfo->boardvendor = wlc->pub->sih->boardvendor;
+ rinfo->boardrev = wlc->pub->boardrev;
+ rinfo->ucoderev = wlc->ucode_rev;
+ rinfo->driverrev = EPI_VERSION_NUM;
+ rinfo->bus = wlc->pub->sih->bustype;
+ rinfo->chipnum = wlc->pub->sih->chip;
+
+ if (len >= (offsetof(wlc_rev_info_t, chippkg))) {
+ rinfo->phytype = wlc->band->phytype;
+ rinfo->phyrev = wlc->band->phyrev;
+ rinfo->anarev = 0; /* obsolete stuff, suppress */
+ }
+
+ if (len >= sizeof(*rinfo)) {
+ rinfo->chippkg = wlc->pub->sih->chippkg;
+ }
+
+ return BCME_OK;
+}
+
+void wlc_default_rateset(wlc_info_t *wlc, wlc_rateset_t *rs)
+{
+ wlc_rateset_default(rs, NULL, wlc->band->phytype, wlc->band->bandtype,
+ false, RATE_MASK_FULL, (bool) N_ENAB(wlc->pub),
+ CHSPEC_WLC_BW(wlc->default_bss->chanspec),
+ wlc->stf->txstreams);
+}
+
+static void wlc_bss_default_init(wlc_info_t *wlc)
+{
+ chanspec_t chanspec;
+ wlcband_t *band;
+ wlc_bss_info_t *bi = wlc->default_bss;
+
+ /* init default and target BSS with some sane initial values */
+ bzero((char *)(bi), sizeof(wlc_bss_info_t));
+ bi->beacon_period = ISSIM_ENAB(wlc->pub->sih) ? BEACON_INTERVAL_DEF_QT :
+ BEACON_INTERVAL_DEFAULT;
+ bi->dtim_period = ISSIM_ENAB(wlc->pub->sih) ? DTIM_INTERVAL_DEF_QT :
+ DTIM_INTERVAL_DEFAULT;
+
+ /* fill the default channel as the first valid channel
+ * starting from the 2G channels
+ */
+ chanspec = CH20MHZ_CHSPEC(1);
+ ASSERT(chanspec != INVCHANSPEC);
+
+ wlc->home_chanspec = bi->chanspec = chanspec;
+
+ /* find the band of our default channel */
+ band = wlc->band;
+ if (NBANDS(wlc) > 1 && band->bandunit != CHSPEC_WLCBANDUNIT(chanspec))
+ band = wlc->bandstate[OTHERBANDUNIT(wlc)];
+
+ /* init bss rates to the band specific default rate set */
+ wlc_rateset_default(&bi->rateset, NULL, band->phytype, band->bandtype,
+ false, RATE_MASK_FULL, (bool) N_ENAB(wlc->pub),
+ CHSPEC_WLC_BW(chanspec), wlc->stf->txstreams);
+
+ if (N_ENAB(wlc->pub))
+ bi->flags |= WLC_BSS_HT;
+}
+
+/* Deferred event processing */
+static void wlc_process_eventq(void *arg)
+{
+ wlc_info_t *wlc = (wlc_info_t *) arg;
+ wlc_event_t *etmp;
+
+ while ((etmp = wlc_eventq_deq(wlc->eventq))) {
+ /* Perform OS specific event processing */
+ wl_event(wlc->wl, etmp->event.ifname, etmp);
+ if (etmp->data) {
+ kfree(etmp->data);
+ etmp->data = NULL;
+ }
+ wlc_event_free(wlc->eventq, etmp);
+ }
+}
+
+void
+wlc_uint64_sub(u32 *a_high, u32 *a_low, u32 b_high, u32 b_low)
+{
+ if (b_low > *a_low) {
+ /* low half needs a carry */
+ b_high += 1;
+ }
+ *a_low -= b_low;
+ *a_high -= b_high;
+}
+
+static ratespec_t
+mac80211_wlc_set_nrate(wlc_info_t *wlc, wlcband_t *cur_band, u32 int_val)
+{
+ u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT;
+ u8 rate = int_val & NRATE_RATE_MASK;
+ ratespec_t rspec;
+ bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE);
+ bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT);
+ bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY)
+ == NRATE_OVERRIDE_MCS_ONLY);
+ int bcmerror = 0;
+
+ if (!ismcs) {
+ return (ratespec_t) rate;
+ }
+
+ /* validate the combination of rate/mcs/stf is allowed */
+ if (N_ENAB(wlc->pub) && ismcs) {
+ /* mcs only allowed when nmode */
+ if (stf > PHY_TXC1_MODE_SDM) {
+ WL_ERROR(("wl%d: %s: Invalid stf\n", WLCWLUNIT(wlc),
+ __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+
+ /* mcs 32 is a special case, DUP mode 40 only */
+ if (rate == 32) {
+ if (!CHSPEC_IS40(wlc->home_chanspec) ||
+ ((stf != PHY_TXC1_MODE_SISO)
+ && (stf != PHY_TXC1_MODE_CDD))) {
+ WL_ERROR(("wl%d: %s: Invalid mcs 32\n",
+ WLCWLUNIT(wlc), __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+ /* mcs > 7 must use stf SDM */
+ } else if (rate > HIGHEST_SINGLE_STREAM_MCS) {
+ /* mcs > 7 must use stf SDM */
+ if (stf != PHY_TXC1_MODE_SDM) {
+ WL_TRACE(("wl%d: %s: enabling SDM mode for mcs %d\n", WLCWLUNIT(wlc), __func__, rate));
+ stf = PHY_TXC1_MODE_SDM;
+ }
+ } else {
+ /* MCS 0-7 may use SISO, CDD, and for phy_rev >= 3 STBC */
+ if ((stf > PHY_TXC1_MODE_STBC) ||
+ (!WLC_STBC_CAP_PHY(wlc)
+ && (stf == PHY_TXC1_MODE_STBC))) {
+ WL_ERROR(("wl%d: %s: Invalid STBC\n",
+ WLCWLUNIT(wlc), __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+ }
+ } else if (IS_OFDM(rate)) {
+ if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) {
+ WL_ERROR(("wl%d: %s: Invalid OFDM\n", WLCWLUNIT(wlc),
+ __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+ } else if (IS_CCK(rate)) {
+ if ((cur_band->bandtype != WLC_BAND_2G)
+ || (stf != PHY_TXC1_MODE_SISO)) {
+ WL_ERROR(("wl%d: %s: Invalid CCK\n", WLCWLUNIT(wlc),
+ __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+ } else {
+ WL_ERROR(("wl%d: %s: Unknown rate type\n", WLCWLUNIT(wlc),
+ __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+ /* make sure multiple antennae are available for non-siso rates */
+ if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) {
+ WL_ERROR(("wl%d: %s: SISO antenna but !SISO request\n",
+ WLCWLUNIT(wlc), __func__));
+ bcmerror = BCME_RANGE;
+ goto done;
+ }
+
+ rspec = rate;
+ if (ismcs) {
+ rspec |= RSPEC_MIMORATE;
+ /* For STBC populate the STC field of the ratespec */
+ if (stf == PHY_TXC1_MODE_STBC) {
+ u8 stc;
+ stc = 1; /* Nss for single stream is always 1 */
+ rspec |= (stc << RSPEC_STC_SHIFT);
+ }
+ }
+
+ rspec |= (stf << RSPEC_STF_SHIFT);
+
+ if (override_mcs_only)
+ rspec |= RSPEC_OVERRIDE_MCS_ONLY;
+
+ if (issgi)
+ rspec |= RSPEC_SHORT_GI;
+
+ if ((rate != 0)
+ && !wlc_valid_rate(wlc, rspec, cur_band->bandtype, true)) {
+ return rate;
+ }
+
+ return rspec;
+ done:
+ WL_ERROR(("Hoark\n"));
+ return rate;
+}
+
+/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */
+static int
+wlc_duty_cycle_set(wlc_info_t *wlc, int duty_cycle, bool isOFDM,
+ bool writeToShm)
+{
+ int idle_busy_ratio_x_16 = 0;
+ uint offset =
+ isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM :
+ M_TX_IDLE_BUSY_RATIO_X_16_CCK;
+ if (duty_cycle > 100 || duty_cycle < 0) {
+ WL_ERROR(("wl%d: duty cycle value off limit\n",
+ wlc->pub->unit));
+ return BCME_RANGE;
+ }
+ if (duty_cycle)
+ idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle;
+ /* Only write to shared memory when wl is up */
+ if (writeToShm)
+ wlc_write_shm(wlc, offset, (u16) idle_busy_ratio_x_16);
+
+ if (isOFDM)
+ wlc->tx_duty_cycle_ofdm = (u16) duty_cycle;
+ else
+ wlc->tx_duty_cycle_cck = (u16) duty_cycle;
+
+ return BCME_OK;
+}
+
+/* Read a single u16 from shared memory.
+ * SHM 'offset' needs to be an even address
+ */
+u16 wlc_read_shm(wlc_info_t *wlc, uint offset)
+{
+ return wlc_bmac_read_shm(wlc->hw, offset);
+}
+
+/* Write a single u16 to shared memory.
+ * SHM 'offset' needs to be an even address
+ */
+void wlc_write_shm(wlc_info_t *wlc, uint offset, u16 v)
+{
+ wlc_bmac_write_shm(wlc->hw, offset, v);
+}
+
+/* Set a range of shared memory to a value.
+ * SHM 'offset' needs to be an even address and
+ * Range length 'len' must be an even number of bytes
+ */
+void wlc_set_shm(wlc_info_t *wlc, uint offset, u16 v, int len)
+{
+ /* offset and len need to be even */
+ ASSERT((offset & 1) == 0);
+ ASSERT((len & 1) == 0);
+
+ if (len <= 0)
+ return;
+
+ wlc_bmac_set_shm(wlc->hw, offset, v, len);
+}
+
+/* Copy a buffer to shared memory.
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ */
+void wlc_copyto_shm(wlc_info_t *wlc, uint offset, const void *buf, int len)
+{
+ /* offset and len need to be even */
+ ASSERT((offset & 1) == 0);
+ ASSERT((len & 1) == 0);
+
+ if (len <= 0)
+ return;
+ wlc_bmac_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL);
+
+}
+
+/* Copy from shared memory to a buffer.
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ */
+void wlc_copyfrom_shm(wlc_info_t *wlc, uint offset, void *buf, int len)
+{
+ /* offset and len need to be even */
+ ASSERT((offset & 1) == 0);
+ ASSERT((len & 1) == 0);
+
+ if (len <= 0)
+ return;
+
+ wlc_bmac_copyfrom_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL);
+}
+
+/* wrapper BMAC functions to for HIGH driver access */
+void wlc_mctrl(wlc_info_t *wlc, u32 mask, u32 val)
+{
+ wlc_bmac_mctrl(wlc->hw, mask, val);
+}
+
+void wlc_corereset(wlc_info_t *wlc, u32 flags)
+{
+ wlc_bmac_corereset(wlc->hw, flags);
+}
+
+void wlc_mhf(wlc_info_t *wlc, u8 idx, u16 mask, u16 val, int bands)
+{
+ wlc_bmac_mhf(wlc->hw, idx, mask, val, bands);
+}
+
+u16 wlc_mhf_get(wlc_info_t *wlc, u8 idx, int bands)
+{
+ return wlc_bmac_mhf_get(wlc->hw, idx, bands);
+}
+
+int wlc_xmtfifo_sz_get(wlc_info_t *wlc, uint fifo, uint *blocks)
+{
+ return wlc_bmac_xmtfifo_sz_get(wlc->hw, fifo, blocks);
+}
+
+void wlc_write_template_ram(wlc_info_t *wlc, int offset, int len, void *buf)
+{
+ wlc_bmac_write_template_ram(wlc->hw, offset, len, buf);
+}
+
+void wlc_write_hw_bcntemplates(wlc_info_t *wlc, void *bcn, int len, bool both)
+{
+ wlc_bmac_write_hw_bcntemplates(wlc->hw, bcn, len, both);
+}
+
+void
+wlc_set_addrmatch(wlc_info_t *wlc, int match_reg_offset,
+ const struct ether_addr *addr)
+{
+ wlc_bmac_set_addrmatch(wlc->hw, match_reg_offset, addr);
+}
+
+void wlc_set_rcmta(wlc_info_t *wlc, int idx, const struct ether_addr *addr)
+{
+ wlc_bmac_set_rcmta(wlc->hw, idx, addr);
+}
+
+void wlc_read_tsf(wlc_info_t *wlc, u32 *tsf_l_ptr, u32 *tsf_h_ptr)
+{
+ wlc_bmac_read_tsf(wlc->hw, tsf_l_ptr, tsf_h_ptr);
+}
+
+void wlc_set_cwmin(wlc_info_t *wlc, u16 newmin)
+{
+ wlc->band->CWmin = newmin;
+ wlc_bmac_set_cwmin(wlc->hw, newmin);
+}
+
+void wlc_set_cwmax(wlc_info_t *wlc, u16 newmax)
+{
+ wlc->band->CWmax = newmax;
+ wlc_bmac_set_cwmax(wlc->hw, newmax);
+}
+
+void wlc_fifoerrors(wlc_info_t *wlc)
+{
+
+ wlc_bmac_fifoerrors(wlc->hw);
+}
+
+/* Search mem rw utilities */
+
+void wlc_pllreq(wlc_info_t *wlc, bool set, mbool req_bit)
+{
+ wlc_bmac_pllreq(wlc->hw, set, req_bit);
+}
+
+void wlc_reset_bmac_done(wlc_info_t *wlc)
+{
+#ifdef WLC_HIGH_ONLY
+ wlc->reset_bmac_pending = false;
+#endif
+}
+
+void wlc_ht_mimops_cap_update(wlc_info_t *wlc, u8 mimops_mode)
+{
+ wlc->ht_cap.cap &= ~HT_CAP_MIMO_PS_MASK;
+ wlc->ht_cap.cap |= (mimops_mode << HT_CAP_MIMO_PS_SHIFT);
+
+ if (AP_ENAB(wlc->pub) && wlc->clk) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ }
+}
+
+/* check for the particular priority flow control bit being set */
+bool
+wlc_txflowcontrol_prio_isset(wlc_info_t *wlc, wlc_txq_info_t *q, int prio)
+{
+ uint prio_mask;
+
+ if (prio == ALLPRIO) {
+ prio_mask = TXQ_STOP_FOR_PRIOFC_MASK;
+ } else {
+ ASSERT(prio >= 0 && prio <= MAXPRIO);
+ prio_mask = NBITVAL(prio);
+ }
+
+ return (q->stopped & prio_mask) == prio_mask;
+}
+
+/* propogate the flow control to all interfaces using the given tx queue */
+void wlc_txflowcontrol(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on, int prio)
+{
+ uint prio_bits;
+ uint cur_bits;
+
+ WL_ERROR(("%s: flow contro kicks in\n", __func__));
+
+ if (prio == ALLPRIO) {
+ prio_bits = TXQ_STOP_FOR_PRIOFC_MASK;
+ } else {
+ ASSERT(prio >= 0 && prio <= MAXPRIO);
+ prio_bits = NBITVAL(prio);
+ }
+
+ cur_bits = qi->stopped & prio_bits;
+
+ /* Check for the case of no change and return early
+ * Otherwise update the bit and continue
+ */
+ if (on) {
+ if (cur_bits == prio_bits) {
+ return;
+ }
+ mboolset(qi->stopped, prio_bits);
+ } else {
+ if (cur_bits == 0) {
+ return;
+ }
+ mboolclr(qi->stopped, prio_bits);
+ }
+
+ /* If there is a flow control override we will not change the external
+ * flow control state.
+ */
+ if (qi->stopped & ~TXQ_STOP_FOR_PRIOFC_MASK) {
+ return;
+ }
+
+ wlc_txflowcontrol_signal(wlc, qi, on, prio);
+}
+
+void
+wlc_txflowcontrol_override(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on,
+ uint override)
+{
+ uint prev_override;
+
+ ASSERT(override != 0);
+ ASSERT((override & TXQ_STOP_FOR_PRIOFC_MASK) == 0);
+
+ prev_override = (qi->stopped & ~TXQ_STOP_FOR_PRIOFC_MASK);
+
+ /* Update the flow control bits and do an early return if there is
+ * no change in the external flow control state.
+ */
+ if (on) {
+ mboolset(qi->stopped, override);
+ /* if there was a previous override bit on, then setting this
+ * makes no difference.
+ */
+ if (prev_override) {
+ return;
+ }
+
+ wlc_txflowcontrol_signal(wlc, qi, ON, ALLPRIO);
+ } else {
+ mboolclr(qi->stopped, override);
+ /* clearing an override bit will only make a difference for
+ * flow control if it was the only bit set. For any other
+ * override setting, just return
+ */
+ if (prev_override != override) {
+ return;
+ }
+
+ if (qi->stopped == 0) {
+ wlc_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
+ } else {
+ int prio;
+
+ for (prio = MAXPRIO; prio >= 0; prio--) {
+ if (!mboolisset(qi->stopped, NBITVAL(prio)))
+ wlc_txflowcontrol_signal(wlc, qi, OFF,
+ prio);
+ }
+ }
+ }
+}
+
+static void wlc_txflowcontrol_reset(wlc_info_t *wlc)
+{
+ wlc_txq_info_t *qi;
+
+ for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) {
+ if (qi->stopped) {
+ wlc_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO);
+ qi->stopped = 0;
+ }
+ }
+}
+
+static void
+wlc_txflowcontrol_signal(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on,
+ int prio)
+{
+ wlc_if_t *wlcif;
+
+ for (wlcif = wlc->wlcif_list; wlcif != NULL; wlcif = wlcif->next) {
+ if (wlcif->qi == qi && wlcif->flags & WLC_IF_LINKED)
+ wl_txflowcontrol(wlc->wl, wlcif->wlif, on, prio);
+ }
+}
+
+static wlc_txq_info_t *wlc_txq_alloc(wlc_info_t *wlc, osl_t *osh)
+{
+ wlc_txq_info_t *qi, *p;
+
+ qi = (wlc_txq_info_t *) wlc_calloc(osh, wlc->pub->unit,
+ sizeof(wlc_txq_info_t));
+ if (qi == NULL) {
+ return NULL;
+ }
+
+ /* Have enough room for control packets along with HI watermark */
+ /* Also, add room to txq for total psq packets if all the SCBs leave PS mode */
+ /* The watermark for flowcontrol to OS packets will remain the same */
+ pktq_init(&qi->q, WLC_PREC_COUNT,
+ (2 * wlc->pub->tunables->datahiwat) + PKTQ_LEN_DEFAULT +
+ wlc->pub->psq_pkts_total);
+
+ /* add this queue to the the global list */
+ p = wlc->tx_queues;
+ if (p == NULL) {
+ wlc->tx_queues = qi;
+ } else {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = qi;
+ }
+
+ return qi;
+}
+
+static void wlc_txq_free(wlc_info_t *wlc, osl_t *osh, wlc_txq_info_t *qi)
+{
+ wlc_txq_info_t *p;
+
+ if (qi == NULL)
+ return;
+
+ /* remove the queue from the linked list */
+ p = wlc->tx_queues;
+ if (p == qi)
+ wlc->tx_queues = p->next;
+ else {
+ while (p != NULL && p->next != qi)
+ p = p->next;
+ ASSERT(p->next == qi);
+ if (p != NULL)
+ p->next = p->next->next;
+ }
+
+ kfree(qi);
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_mac80211.h b/drivers/staging/brcm80211/sys/wlc_mac80211.h
new file mode 100644
index 000000000000..6a77591234b7
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_mac80211.h
@@ -0,0 +1,1040 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_h_
+#define _wlc_h_
+
+#include <wlc_types.h>
+
+#include <wl_dbg.h>
+#include <wlioctl.h>
+#include <wlc_event.h>
+#include <wlc_phy_hal.h>
+#include <wlc_channel.h>
+#ifdef WLC_SPLIT
+#include <bcm_rpc.h>
+#endif
+
+#include <wlc_bsscfg.h>
+
+#include <wlc_scb.h>
+
+#define MA_WINDOW_SZ 8 /* moving average window size */
+#define WL_HWRXOFF 38 /* chip rx buffer offset */
+#define INVCHANNEL 255 /* invalid channel */
+#define MAXCOREREV 28 /* max # supported core revisions (0 .. MAXCOREREV - 1) */
+#define WLC_MAXMODULES 22 /* max # wlc_module_register() calls */
+
+/* network protection config */
+#define WLC_PROT_G_SPEC 1 /* SPEC g protection */
+#define WLC_PROT_G_OVR 2 /* SPEC g prot override */
+#define WLC_PROT_G_USER 3 /* gmode specified by user */
+#define WLC_PROT_OVERLAP 4 /* overlap */
+#define WLC_PROT_N_USER 10 /* nmode specified by user */
+#define WLC_PROT_N_CFG 11 /* n protection */
+#define WLC_PROT_N_CFG_OVR 12 /* n protection override */
+#define WLC_PROT_N_NONGF 13 /* non-GF protection */
+#define WLC_PROT_N_NONGF_OVR 14 /* non-GF protection override */
+#define WLC_PROT_N_PAM_OVR 15 /* n preamble override */
+#define WLC_PROT_N_OBSS 16 /* non-HT OBSS present */
+
+#define WLC_BITSCNT(x) bcm_bitcount((u8 *)&(x), sizeof(u8))
+
+/* Maximum wait time for a MAC suspend */
+#define WLC_MAX_MAC_SUSPEND 83000 /* uS: 83mS is max packet time (64KB ampdu @ 6Mbps) */
+
+/* Probe Response timeout - responses for probe requests older that this are tossed, zero to disable
+ */
+#define WLC_PRB_RESP_TIMEOUT 0 /* Disable probe response timeout */
+
+/* transmit buffer max headroom for protocol headers */
+#define TXOFF (D11_TXH_LEN + D11_PHY_HDR_LEN)
+
+/* For managing scan result lists */
+typedef struct wlc_bss_list {
+ uint count;
+ bool beacon; /* set for beacon, cleared for probe response */
+ wlc_bss_info_t *ptrs[MAXBSS];
+} wlc_bss_list_t;
+
+#define SW_TIMER_MAC_STAT_UPD 30 /* periodic MAC stats update */
+
+/* Double check that unsupported cores are not enabled */
+#if CONF_MSK(D11CONF, 0x4f) || CONF_GE(D11CONF, MAXCOREREV)
+#error "Configuration for D11CONF includes unsupported versions."
+#endif /* Bad versions */
+
+#define VALID_COREREV(corerev) CONF_HAS(D11CONF, corerev)
+
+/* values for shortslot_override */
+#define WLC_SHORTSLOT_AUTO -1 /* Driver will manage Shortslot setting */
+#define WLC_SHORTSLOT_OFF 0 /* Turn off short slot */
+#define WLC_SHORTSLOT_ON 1 /* Turn on short slot */
+
+/* value for short/long and mixmode/greenfield preamble */
+
+#define WLC_LONG_PREAMBLE (0)
+#define WLC_SHORT_PREAMBLE (1 << 0)
+#define WLC_GF_PREAMBLE (1 << 1)
+#define WLC_MM_PREAMBLE (1 << 2)
+#define WLC_IS_MIMO_PREAMBLE(_pre) (((_pre) == WLC_GF_PREAMBLE) || ((_pre) == WLC_MM_PREAMBLE))
+
+/* values for barker_preamble */
+#define WLC_BARKER_SHORT_ALLOWED 0 /* Short pre-amble allowed */
+
+/* A fifo is full. Clear precedences related to that FIFO */
+#define WLC_TX_FIFO_CLEAR(wlc, fifo) ((wlc)->tx_prec_map &= ~(wlc)->fifo2prec_map[fifo])
+
+/* Fifo is NOT full. Enable precedences for that FIFO */
+#define WLC_TX_FIFO_ENAB(wlc, fifo) ((wlc)->tx_prec_map |= (wlc)->fifo2prec_map[fifo])
+
+/* TxFrameID */
+/* seq and frag bits: SEQNUM_SHIFT, FRAGNUM_MASK (802.11.h) */
+/* rate epoch bits: TXFID_RATE_SHIFT, TXFID_RATE_MASK ((wlc_rate.c) */
+#define TXFID_QUEUE_MASK 0x0007 /* Bits 0-2 */
+#define TXFID_SEQ_MASK 0x7FE0 /* Bits 5-15 */
+#define TXFID_SEQ_SHIFT 5 /* Number of bit shifts */
+#define TXFID_RATE_PROBE_MASK 0x8000 /* Bit 15 for rate probe */
+#define TXFID_RATE_MASK 0x0018 /* Mask for bits 3 and 4 */
+#define TXFID_RATE_SHIFT 3 /* Shift 3 bits for rate mask */
+
+/* promote boardrev */
+#define BOARDREV_PROMOTABLE 0xFF /* from */
+#define BOARDREV_PROMOTED 1 /* to */
+
+/* if wpa is in use then portopen is true when the group key is plumbed otherwise it is always true
+ */
+#define WSEC_ENABLED(wsec) ((wsec) & (WEP_ENABLED | TKIP_ENABLED | AES_ENABLED))
+#define WLC_SW_KEYS(wlc, bsscfg) ((((wlc)->wsec_swkeys) || \
+ ((bsscfg)->wsec & WSEC_SWFLAG)))
+
+#define WLC_PORTOPEN(cfg) \
+ (((cfg)->WPA_auth != WPA_AUTH_DISABLED && WSEC_ENABLED((cfg)->wsec)) ? \
+ (cfg)->wsec_portopen : true)
+
+#define PS_ALLOWED(wlc) wlc_ps_allowed(wlc)
+#define STAY_AWAKE(wlc) wlc_stay_awake(wlc)
+
+#define DATA_BLOCK_TX_SUPR (1 << 4)
+
+/* 802.1D Priority to TX FIFO number for wme */
+extern const u8 prio2fifo[];
+
+/* Ucode MCTL_WAKE override bits */
+#define WLC_WAKE_OVERRIDE_CLKCTL 0x01
+#define WLC_WAKE_OVERRIDE_PHYREG 0x02
+#define WLC_WAKE_OVERRIDE_MACSUSPEND 0x04
+#define WLC_WAKE_OVERRIDE_TXFIFO 0x08
+#define WLC_WAKE_OVERRIDE_FORCEFAST 0x10
+
+/* stuff pulled in from wlc.c */
+
+/* Interrupt bit error summary. Don't include I_RU: we refill DMA at other
+ * times; and if we run out, constant I_RU interrupts may cause lockup. We
+ * will still get error counts from rx0ovfl.
+ */
+#define I_ERRORS (I_PC | I_PD | I_DE | I_RO | I_XU)
+/* default software intmasks */
+#define DEF_RXINTMASK (I_RI) /* enable rx int on rxfifo only */
+#define DEF_MACINTMASK (MI_TXSTOP | MI_TBTT | MI_ATIMWINEND | MI_PMQ | \
+ MI_PHYTXERR | MI_DMAINT | MI_TFS | MI_BG_NOISE | \
+ MI_CCA | MI_TO | MI_GP0 | MI_RFDISABLE | MI_PWRUP)
+
+#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */
+#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */
+#define RETRY_LONG_DEF 4 /* Default Long retry count */
+#define RETRY_SHORT_FB 3 /* Short retry count for fallback rate */
+#define RETRY_LONG_FB 2 /* Long retry count for fallback rate */
+
+#define MAXTXPKTS 6 /* max # pkts pending */
+
+/* frameburst */
+#define MAXTXFRAMEBURST 8 /* vanilla xpress mode: max frames/burst */
+#define MAXFRAMEBURST_TXOP 10000 /* Frameburst TXOP in usec */
+
+/* Per-AC retry limit register definitions; uses bcmdefs.h bitfield macros */
+#define EDCF_SHORT_S 0
+#define EDCF_SFB_S 4
+#define EDCF_LONG_S 8
+#define EDCF_LFB_S 12
+#define EDCF_SHORT_M BITFIELD_MASK(4)
+#define EDCF_SFB_M BITFIELD_MASK(4)
+#define EDCF_LONG_M BITFIELD_MASK(4)
+#define EDCF_LFB_M BITFIELD_MASK(4)
+
+#define WLC_WME_RETRY_SHORT_GET(wlc, ac) GFIELD(wlc->wme_retries[ac], EDCF_SHORT)
+#define WLC_WME_RETRY_SFB_GET(wlc, ac) GFIELD(wlc->wme_retries[ac], EDCF_SFB)
+#define WLC_WME_RETRY_LONG_GET(wlc, ac) GFIELD(wlc->wme_retries[ac], EDCF_LONG)
+#define WLC_WME_RETRY_LFB_GET(wlc, ac) GFIELD(wlc->wme_retries[ac], EDCF_LFB)
+
+#define WLC_WME_RETRY_SHORT_SET(wlc, ac, val) \
+ (wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], EDCF_SHORT, val))
+#define WLC_WME_RETRY_SFB_SET(wlc, ac, val) \
+ (wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], EDCF_SFB, val))
+#define WLC_WME_RETRY_LONG_SET(wlc, ac, val) \
+ (wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], EDCF_LONG, val))
+#define WLC_WME_RETRY_LFB_SET(wlc, ac, val) \
+ (wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], EDCF_LFB, val))
+
+/* PLL requests */
+#define WLC_PLLREQ_SHARED 0x1 /* pll is shared on old chips */
+#define WLC_PLLREQ_RADIO_MON 0x2 /* hold pll for radio monitor register checking */
+#define WLC_PLLREQ_FLIP 0x4 /* hold/release pll for some short operation */
+
+/* Do we support this rate? */
+#define VALID_RATE_DBG(wlc, rspec) wlc_valid_rate(wlc, rspec, WLC_BAND_AUTO, true)
+
+/*
+ * Macros to check if AP or STA is active.
+ * AP Active means more than just configured: driver and BSS are "up";
+ * that is, we are beaconing/responding as an AP (aps_associated).
+ * STA Active similarly means the driver is up and a configured STA BSS
+ * is up: either associated (stas_associated) or trying.
+ *
+ * Macro definitions vary as per AP/STA ifdefs, allowing references to
+ * ifdef'd structure fields and constant values (0) for optimization.
+ * Make sure to enclose blocks of code such that any routines they
+ * reference can also be unused and optimized out by the linker.
+ */
+/* NOTE: References structure fields defined in wlc.h */
+#define AP_ACTIVE(wlc) (0)
+
+/*
+ * Detect Card removed.
+ * Even checking an sbconfig register read will not false trigger when the core is in reset.
+ * it breaks CF address mechanism. Accessing gphy phyversion will cause SB error if aphy
+ * is in reset on 4306B0-DB. Need a simple accessible reg with fixed 0/1 pattern
+ * (some platforms return all 0).
+ * If clocks are present, call the sb routine which will figure out if the device is removed.
+ */
+#ifdef WLC_HIGH_ONLY
+#define DEVICEREMOVED(wlc) (!wlc->device_present)
+#else
+#define DEVICEREMOVED(wlc) \
+ ((wlc->hw->clk) ? \
+ ((R_REG(wlc->hw->osh, &wlc->hw->regs->maccontrol) & \
+ (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN) : \
+ (si_deviceremoved(wlc->hw->sih)))
+#endif /* WLC_HIGH_ONLY */
+
+#define WLCWLUNIT(wlc) ((wlc)->pub->unit)
+
+typedef struct wlc_protection {
+ bool _g; /* use g spec protection, driver internal */
+ s8 g_override; /* override for use of g spec protection */
+ u8 gmode_user; /* user config gmode, operating band->gmode is different */
+ s8 overlap; /* Overlap BSS/IBSS protection for both 11g and 11n */
+ s8 nmode_user; /* user config nmode, operating pub->nmode is different */
+ s8 n_cfg; /* use OFDM protection on MIMO frames */
+ s8 n_cfg_override; /* override for use of N protection */
+ bool nongf; /* non-GF present protection */
+ s8 nongf_override; /* override for use of GF protection */
+ s8 n_pam_override; /* override for preamble: MM or GF */
+ bool n_obss; /* indicated OBSS Non-HT STA present */
+
+ uint longpre_detect_timeout; /* #sec until long preamble bcns gone */
+ uint barker_detect_timeout; /* #sec until bcns signaling Barker long preamble */
+ /* only is gone */
+ uint ofdm_ibss_timeout; /* #sec until ofdm IBSS beacons gone */
+ uint ofdm_ovlp_timeout; /* #sec until ofdm overlapping BSS bcns gone */
+ uint nonerp_ibss_timeout; /* #sec until nonerp IBSS beacons gone */
+ uint nonerp_ovlp_timeout; /* #sec until nonerp overlapping BSS bcns gone */
+ uint g_ibss_timeout; /* #sec until bcns signaling Use_Protection gone */
+ uint n_ibss_timeout; /* #sec until bcns signaling Use_OFDM_Protection gone */
+ uint ht20in40_ovlp_timeout; /* #sec until 20MHz overlapping OPMODE gone */
+ uint ht20in40_ibss_timeout; /* #sec until 20MHz-only HT station bcns gone */
+ uint non_gf_ibss_timeout; /* #sec until non-GF bcns gone */
+} wlc_protection_t;
+
+/* anything affects the single/dual streams/antenna operation */
+typedef struct wlc_stf {
+ u8 hw_txchain; /* HW txchain bitmap cfg */
+ u8 txchain; /* txchain bitmap being used */
+ u8 txstreams; /* number of txchains being used */
+
+ u8 hw_rxchain; /* HW rxchain bitmap cfg */
+ u8 rxchain; /* rxchain bitmap being used */
+ u8 rxstreams; /* number of rxchains being used */
+
+ u8 ant_rx_ovr; /* rx antenna override */
+ s8 txant; /* userTx antenna setting */
+ u16 phytxant; /* phyTx antenna setting in txheader */
+
+ u8 ss_opmode; /* singlestream Operational mode, 0:siso; 1:cdd */
+ bool ss_algosel_auto; /* if true, use wlc->stf->ss_algo_channel; */
+ /* else use wlc->band->stf->ss_mode_band; */
+ u16 ss_algo_channel; /* ss based on per-channel algo: 0: SISO, 1: CDD 2: STBC */
+ u8 no_cddstbc; /* stf override, 1: no CDD (or STBC) allowed */
+
+ u8 rxchain_restore_delay; /* delay time to restore default rxchain */
+
+ s8 ldpc; /* AUTO/ON/OFF ldpc cap supported */
+ u8 txcore[MAX_STREAMS_SUPPORTED + 1]; /* bitmap of selected core for each Nsts */
+ s8 spatial_policy;
+} wlc_stf_t;
+
+#define WLC_STF_SS_STBC_TX(wlc, scb) \
+ (((wlc)->stf->txstreams > 1) && (((wlc)->band->band_stf_stbc_tx == ON) || \
+ (SCB_STBC_CAP((scb)) && \
+ (wlc)->band->band_stf_stbc_tx == AUTO && \
+ isset(&((wlc)->stf->ss_algo_channel), PHY_TXC1_MODE_STBC))))
+
+#define WLC_STBC_CAP_PHY(wlc) (WLCISNPHY(wlc->band) && NREV_GE(wlc->band->phyrev, 3))
+
+#define WLC_SGI_CAP_PHY(wlc) ((WLCISNPHY(wlc->band) && NREV_GE(wlc->band->phyrev, 3)) || \
+ WLCISLCNPHY(wlc->band))
+
+#define WLC_CHAN_PHYTYPE(x) (((x) & RXS_CHAN_PHYTYPE_MASK) >> RXS_CHAN_PHYTYPE_SHIFT)
+#define WLC_CHAN_CHANNEL(x) (((x) & RXS_CHAN_ID_MASK) >> RXS_CHAN_ID_SHIFT)
+#define WLC_RX_CHANNEL(rxh) (WLC_CHAN_CHANNEL((rxh)->RxChan))
+
+/* wlc_bss_info flag bit values */
+#define WLC_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */
+
+/* Flags used in wlc_txq_info.stopped */
+#define TXQ_STOP_FOR_PRIOFC_MASK 0x000000FF /* per prio flow control bits */
+#define TXQ_STOP_FOR_PKT_DRAIN 0x00000100 /* stop txq enqueue for packet drain */
+#define TXQ_STOP_FOR_AMPDU_FLOW_CNTRL 0x00000200 /* stop txq enqueue for ampdu flow control */
+
+#define WLC_HT_WEP_RESTRICT 0x01 /* restrict HT with WEP */
+#define WLC_HT_TKIP_RESTRICT 0x02 /* restrict HT with TKIP */
+
+/*
+ * core state (mac)
+ */
+typedef struct wlccore {
+#ifdef WLC_LOW
+ uint coreidx; /* # sb enumerated core */
+
+ /* fifo */
+ uint *txavail[NFIFO]; /* # tx descriptors available */
+ s16 txpktpend[NFIFO]; /* tx admission control */
+#endif /* WLC_LOW */
+
+ macstat_t *macstat_snapshot; /* mac hw prev read values */
+} wlccore_t;
+
+/*
+ * band state (phy+ana+radio)
+ */
+typedef struct wlcband {
+ int bandtype; /* WLC_BAND_2G, WLC_BAND_5G */
+ uint bandunit; /* bandstate[] index */
+
+ u16 phytype; /* phytype */
+ u16 phyrev;
+ u16 radioid;
+ u16 radiorev;
+ wlc_phy_t *pi; /* pointer to phy specific information */
+ bool abgphy_encore;
+
+ u8 gmode; /* currently active gmode (see wlioctl.h) */
+
+ struct scb *hwrs_scb; /* permanent scb for hw rateset */
+
+ wlc_rateset_t defrateset; /* band-specific copy of default_bss.rateset */
+
+ ratespec_t rspec_override; /* 802.11 rate override */
+ ratespec_t mrspec_override; /* multicast rate override */
+ u8 band_stf_ss_mode; /* Configured STF type, 0:siso; 1:cdd */
+ s8 band_stf_stbc_tx; /* STBC TX 0:off; 1:force on; -1:auto */
+ wlc_rateset_t hw_rateset; /* rates supported by chip (phy-specific) */
+ u8 basic_rate[WLC_MAXRATE + 1]; /* basic rates indexed by rate */
+ bool mimo_cap_40; /* 40 MHz cap enabled on this band */
+ s8 antgain; /* antenna gain from srom */
+
+ u16 CWmin; /* The minimum size of contention window, in unit of aSlotTime */
+ u16 CWmax; /* The maximum size of contention window, in unit of aSlotTime */
+ u16 bcntsfoff; /* beacon tsf offset */
+} wlcband_t;
+
+/* generic function callback takes just one arg */
+typedef void (*cb_fn_t) (void *);
+
+/* tx completion callback takes 3 args */
+typedef void (*pkcb_fn_t) (wlc_info_t *wlc, uint txstatus, void *arg);
+
+typedef struct pkt_cb {
+ pkcb_fn_t fn; /* function to call when tx frame completes */
+ void *arg; /* void arg for fn */
+ u8 nextidx; /* index of next call back if threading */
+ bool entered; /* recursion check */
+} pkt_cb_t;
+
+ /* module control blocks */
+typedef struct modulecb {
+ char name[32]; /* module name : NULL indicates empty array member */
+ const bcm_iovar_t *iovars; /* iovar table */
+ void *hdl; /* handle passed when handler 'doiovar' is called */
+ watchdog_fn_t watchdog_fn; /* watchdog handler */
+ iovar_fn_t iovar_fn; /* iovar handler */
+ down_fn_t down_fn; /* down handler. Note: the int returned
+ * by the down function is a count of the
+ * number of timers that could not be
+ * freed.
+ */
+} modulecb_t;
+
+ /* dump control blocks */
+typedef struct dumpcb_s {
+ const char *name; /* dump name */
+ dump_fn_t dump_fn; /* 'wl dump' handler */
+ void *dump_fn_arg;
+ struct dumpcb_s *next;
+} dumpcb_t;
+
+/* virtual interface */
+struct wlc_if {
+ wlc_if_t *next;
+ u8 type; /* WLC_IFTYPE_BSS or WLC_IFTYPE_WDS */
+ u8 index; /* assigned in wl_add_if(), index of the wlif if any,
+ * not necessarily corresponding to bsscfg._idx or
+ * AID2PVBMAP(scb).
+ */
+ u8 flags; /* flags for the interface */
+ wl_if_t *wlif; /* pointer to wlif */
+ struct wlc_txq_info *qi; /* pointer to associated tx queue */
+ union {
+ struct scb *scb; /* pointer to scb if WLC_IFTYPE_WDS */
+ struct wlc_bsscfg *bsscfg; /* pointer to bsscfg if WLC_IFTYPE_BSS */
+ } u;
+};
+
+/* flags for the interface */
+#define WLC_IF_LINKED 0x02 /* this interface is linked to a wl_if */
+
+#ifdef WLC_LOW
+typedef struct wlc_hwband {
+ int bandtype; /* WLC_BAND_2G, WLC_BAND_5G */
+ uint bandunit; /* bandstate[] index */
+ u16 mhfs[MHFMAX]; /* MHF array shadow */
+ u8 bandhw_stf_ss_mode; /* HW configured STF type, 0:siso; 1:cdd */
+ u16 CWmin;
+ u16 CWmax;
+ u32 core_flags;
+
+ u16 phytype; /* phytype */
+ u16 phyrev;
+ u16 radioid;
+ u16 radiorev;
+ wlc_phy_t *pi; /* pointer to phy specific information */
+ bool abgphy_encore;
+} wlc_hwband_t;
+#endif /* WLC_LOW */
+
+struct wlc_hw_info {
+#ifdef WLC_SPLIT
+ rpc_info_t *rpc; /* Handle to RPC module */
+#endif
+ osl_t *osh; /* pointer to os handle */
+ bool _piomode; /* true if pio mode */
+ wlc_info_t *wlc;
+
+ /* fifo */
+ hnddma_t *di[NFIFO]; /* hnddma handles, per fifo */
+
+#ifdef WLC_LOW
+ uint unit; /* device instance number */
+
+ /* version info */
+ u16 vendorid; /* PCI vendor id */
+ u16 deviceid; /* PCI device id */
+ uint corerev; /* core revision */
+ u8 sromrev; /* version # of the srom */
+ u16 boardrev; /* version # of particular board */
+ u32 boardflags; /* Board specific flags from srom */
+ u32 boardflags2; /* More board flags if sromrev >= 4 */
+ u32 machwcap; /* MAC capabilities (corerev >= 13) */
+ u32 machwcap_backup; /* backup of machwcap (corerev >= 13) */
+ u16 ucode_dbgsel; /* dbgsel for ucode debug(config gpio) */
+
+ si_t *sih; /* SB handle (cookie for siutils calls) */
+ char *vars; /* "environment" name=value */
+ uint vars_size; /* size of vars, free vars on detach */
+ d11regs_t *regs; /* pointer to device registers */
+ void *physhim; /* phy shim layer handler */
+ void *phy_sh; /* pointer to shared phy state */
+ wlc_hwband_t *band; /* pointer to active per-band state */
+ wlc_hwband_t *bandstate[MAXBANDS]; /* per-band state (one per phy/radio) */
+ u16 bmac_phytxant; /* cache of high phytxant state */
+ bool shortslot; /* currently using 11g ShortSlot timing */
+ u16 SRL; /* 802.11 dot11ShortRetryLimit */
+ u16 LRL; /* 802.11 dot11LongRetryLimit */
+ u16 SFBL; /* Short Frame Rate Fallback Limit */
+ u16 LFBL; /* Long Frame Rate Fallback Limit */
+
+ bool up; /* d11 hardware up and running */
+ uint now; /* # elapsed seconds */
+ uint _nbands; /* # bands supported */
+ chanspec_t chanspec; /* bmac chanspec shadow */
+
+ uint *txavail[NFIFO]; /* # tx descriptors available */
+ u16 *xmtfifo_sz; /* fifo size in 256B for each xmt fifo */
+
+ mbool pllreq; /* pll requests to keep PLL on */
+
+ u8 suspended_fifos; /* Which TX fifo to remain awake for */
+ u32 maccontrol; /* Cached value of maccontrol */
+ uint mac_suspend_depth; /* current depth of mac_suspend levels */
+ u32 wake_override; /* Various conditions to force MAC to WAKE mode */
+ u32 mute_override; /* Prevent ucode from sending beacons */
+ struct ether_addr etheraddr; /* currently configured ethernet address */
+ u32 led_gpio_mask; /* LED GPIO Mask */
+ bool noreset; /* true= do not reset hw, used by WLC_OUT */
+ bool forcefastclk; /* true if the h/w is forcing the use of fast clk */
+ bool clk; /* core is out of reset and has clock */
+ bool sbclk; /* sb has clock */
+ bmac_pmq_t *bmac_pmq; /* bmac PM states derived from ucode PMQ */
+ bool phyclk; /* phy is out of reset and has clock */
+ bool dma_lpbk; /* core is in DMA loopback */
+
+#ifdef BCMSDIO
+ void *sdh;
+#endif
+ bool ucode_loaded; /* true after ucode downloaded */
+
+#ifdef WLC_LOW_ONLY
+ struct wl_timer *wdtimer; /* timer for watchdog routine */
+ struct ether_addr orig_etheraddr; /* original hw ethernet address */
+ u16 rpc_dngl_agg; /* rpc agg control for dongle */
+ u32 mem_required_def; /* memory required to replenish RX DMA ring */
+ u32 mem_required_lower; /* memory required with lower RX bound */
+ u32 mem_required_least; /* minimum memory requirement to handle RX */
+
+#endif /* WLC_LOW_ONLY */
+
+ u8 hw_stf_ss_opmode; /* STF single stream operation mode */
+ u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic
+ * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board
+ */
+ u32 antsel_avail; /* put antsel_info_t here if more info is needed */
+#endif /* WLC_LOW */
+};
+
+/* TX Queue information
+ *
+ * Each flow of traffic out of the device has a TX Queue with independent
+ * flow control. Several interfaces may be associated with a single TX Queue
+ * if they belong to the same flow of traffic from the device. For multi-channel
+ * operation there are independent TX Queues for each channel.
+ */
+typedef struct wlc_txq_info {
+ struct wlc_txq_info *next;
+ struct pktq q;
+ uint stopped; /* tx flow control bits */
+} wlc_txq_info_t;
+
+/*
+ * Principal common (os-independent) software data structure.
+ */
+struct wlc_info {
+ wlc_pub_t *pub; /* pointer to wlc public state */
+ osl_t *osh; /* pointer to os handle */
+ struct wl_info *wl; /* pointer to os-specific private state */
+ d11regs_t *regs; /* pointer to device registers */
+
+ wlc_hw_info_t *hw; /* HW related state used primarily by BMAC */
+#ifdef WLC_SPLIT
+ rpc_info_t *rpc; /* Handle to RPC module */
+#endif
+
+ /* clock */
+ int clkreq_override; /* setting for clkreq for PCIE : Auto, 0, 1 */
+ u16 fastpwrup_dly; /* time in us needed to bring up d11 fast clock */
+
+ /* interrupt */
+ u32 macintstatus; /* bit channel between isr and dpc */
+ u32 macintmask; /* sw runtime master macintmask value */
+ u32 defmacintmask; /* default "on" macintmask value */
+
+ /* up and down */
+ bool device_present; /* (removable) device is present */
+
+ bool clk; /* core is out of reset and has clock */
+
+ /* multiband */
+ wlccore_t *core; /* pointer to active io core */
+ wlcband_t *band; /* pointer to active per-band state */
+ wlccore_t *corestate; /* per-core state (one per hw core) */
+ wlcband_t *bandstate[MAXBANDS]; /* per-band state (one per phy/radio) */
+
+ bool war16165; /* PCI slow clock 16165 war flag */
+
+ bool tx_suspended; /* data fifos need to remain suspended */
+
+ uint txpend16165war;
+
+ /* packet queue */
+ uint qvalid; /* DirFrmQValid and BcMcFrmQValid */
+
+ /* Regulatory power limits */
+ s8 txpwr_local_max; /* regulatory local txpwr max */
+ u8 txpwr_local_constraint; /* local power contraint in dB */
+
+#ifdef WLC_HIGH_ONLY
+ rpctx_info_t *rpctx; /* RPC TX module */
+ bool reset_bmac_pending; /* bmac reset is in progressing */
+ u32 rpc_agg; /* host agg: bit 16-31, bmac agg: bit 0-15 */
+ u32 rpc_msglevel; /* host rpc: bit 16-31, bmac rpc: bit 0-15 */
+#endif
+
+ ampdu_info_t *ampdu; /* ampdu module handler */
+ antsel_info_t *asi; /* antsel module handler */
+ wlc_cm_info_t *cmi; /* channel manager module handler */
+
+ void *btparam; /* bus type specific cookie */
+
+ uint vars_size; /* size of vars, free vars on detach */
+
+ u16 vendorid; /* PCI vendor id */
+ u16 deviceid; /* PCI device id */
+ uint ucode_rev; /* microcode revision */
+
+ u32 machwcap; /* MAC capabilities, BMAC shadow */
+
+ struct ether_addr perm_etheraddr; /* original sprom local ethernet address */
+
+ bool bandlocked; /* disable auto multi-band switching */
+ bool bandinit_pending; /* track band init in auto band */
+
+ bool radio_monitor; /* radio timer is running */
+ bool down_override; /* true=down */
+ bool going_down; /* down path intermediate variable */
+
+ bool mpc; /* enable minimum power consumption */
+ u8 mpc_dlycnt; /* # of watchdog cnt before turn disable radio */
+ u8 mpc_offcnt; /* # of watchdog cnt that radio is disabled */
+ u8 mpc_delay_off; /* delay radio disable by # of watchdog cnt */
+ u8 prev_non_delay_mpc; /* prev state wlc_is_non_delay_mpc */
+
+ /* timer */
+ struct wl_timer *wdtimer; /* timer for watchdog routine */
+ uint fast_timer; /* Periodic timeout for 'fast' timer */
+ uint slow_timer; /* Periodic timeout for 'slow' timer */
+ uint glacial_timer; /* Periodic timeout for 'glacial' timer */
+ uint phycal_mlo; /* last time measurelow calibration was done */
+ uint phycal_txpower; /* last time txpower calibration was done */
+
+ struct wl_timer *radio_timer; /* timer for hw radio button monitor routine */
+ struct wl_timer *pspoll_timer; /* periodic pspoll timer */
+
+ /* promiscuous */
+ bool monitor; /* monitor (MPDU sniffing) mode */
+ bool bcnmisc_ibss; /* bcns promisc mode override for IBSS */
+ bool bcnmisc_scan; /* bcns promisc mode override for scan */
+ bool bcnmisc_monitor; /* bcns promisc mode override for monitor */
+
+ u8 bcn_wait_prd; /* max waiting period (for beacon) in 1024TU */
+
+ /* driver feature */
+ bool _rifs; /* enable per-packet rifs */
+ s32 rifs_advert; /* RIFS mode advertisement */
+ s8 sgi_tx; /* sgi tx */
+ bool wet; /* true if wireless ethernet bridging mode */
+
+ /* AP-STA synchronization, power save */
+ bool check_for_unaligned_tbtt; /* check unaligned tbtt flag */
+ bool PM_override; /* no power-save flag, override PM(user input) */
+ bool PMenabled; /* current power-management state (CAM or PS) */
+ bool PMpending; /* waiting for tx status with PM indicated set */
+ bool PMblocked; /* block any PSPolling in PS mode, used to buffer
+ * AP traffic, also used to indicate in progress
+ * of scan, rm, etc. off home channel activity.
+ */
+ bool PSpoll; /* whether there is an outstanding PS-Poll frame */
+ u8 PM; /* power-management mode (CAM, PS or FASTPS) */
+ bool PMawakebcn; /* bcn recvd during current waking state */
+
+ bool WME_PM_blocked; /* Can STA go to PM when in WME Auto mode */
+ bool wake; /* host-specified PS-mode sleep state */
+ u8 pspoll_prd; /* pspoll interval in milliseconds */
+ u8 bcn_li_bcn; /* beacon listen interval in # beacons */
+ u8 bcn_li_dtim; /* beacon listen interval in # dtims */
+
+ bool WDarmed; /* watchdog timer is armed */
+ u32 WDlast; /* last time wlc_watchdog() was called */
+
+ /* WME */
+ ac_bitmap_t wme_dp; /* Discard (oldest first) policy per AC */
+ bool wme_apsd; /* enable Advanced Power Save Delivery */
+ ac_bitmap_t wme_admctl; /* bit i set if AC i under admission control */
+ u16 edcf_txop[AC_COUNT]; /* current txop for each ac */
+ wme_param_ie_t wme_param_ie; /* WME parameter info element, which on STA
+ * contains parameters in use locally, and on
+ * AP contains parameters advertised to STA
+ * in beacons and assoc responses.
+ */
+ bool wme_prec_queuing; /* enable/disable non-wme STA prec queuing */
+ u16 wme_retries[AC_COUNT]; /* per-AC retry limits */
+
+ int vlan_mode; /* OK to use 802.1Q Tags (ON, OFF, AUTO) */
+ u16 tx_prec_map; /* Precedence map based on HW FIFO space */
+ u16 fifo2prec_map[NFIFO]; /* pointer to fifo2_prec map based on WME */
+
+ /* BSS Configurations */
+ wlc_bsscfg_t *bsscfg[WLC_MAXBSSCFG]; /* set of BSS configurations, idx 0 is default and
+ * always valid
+ */
+ wlc_bsscfg_t *cfg; /* the primary bsscfg (can be AP or STA) */
+ u8 stas_associated; /* count of ASSOCIATED STA bsscfgs */
+ u8 aps_associated; /* count of UP AP bsscfgs */
+ u8 block_datafifo; /* prohibit posting frames to data fifos */
+ bool bcmcfifo_drain; /* TX_BCMC_FIFO is set to drain */
+
+ /* tx queue */
+ wlc_txq_info_t *tx_queues; /* common TX Queue list */
+
+ /* event */
+ wlc_eventq_t *eventq; /* event queue for deferred processing */
+
+ /* security */
+ wsec_key_t *wsec_keys[WSEC_MAX_KEYS]; /* dynamic key storage */
+ wsec_key_t *wsec_def_keys[WLC_DEFAULT_KEYS]; /* default key storage */
+ bool wsec_swkeys; /* indicates that all keys should be
+ * treated as sw keys (used for debugging)
+ */
+ modulecb_t *modulecb;
+ dumpcb_t *dumpcb_head;
+
+ u8 mimoft; /* SIGN or 11N */
+ u8 mimo_band_bwcap; /* bw cap per band type */
+ s8 txburst_limit_override; /* tx burst limit override */
+ u16 txburst_limit; /* tx burst limit value */
+ s8 cck_40txbw; /* 11N, cck tx b/w override when in 40MHZ mode */
+ s8 ofdm_40txbw; /* 11N, ofdm tx b/w override when in 40MHZ mode */
+ s8 mimo_40txbw; /* 11N, mimo tx b/w override when in 40MHZ mode */
+ ht_cap_ie_t ht_cap; /* HT CAP IE being advertised by this node */
+
+ uint seckeys; /* 54 key table shm address */
+ uint tkmickeys; /* 12 TKIP MIC key table shm address */
+
+ wlc_bss_info_t *default_bss; /* configured BSS parameters */
+
+ u16 AID; /* association ID */
+ u16 counter; /* per-sdu monotonically increasing counter */
+ u16 mc_fid_counter; /* BC/MC FIFO frame ID counter */
+
+ bool ibss_allowed; /* false, all IBSS will be ignored during a scan
+ * and the driver will not allow the creation of
+ * an IBSS network
+ */
+ bool ibss_coalesce_allowed;
+
+ char country_default[WLC_CNTRY_BUF_SZ]; /* saved country for leaving 802.11d
+ * auto-country mode
+ */
+ char autocountry_default[WLC_CNTRY_BUF_SZ]; /* initial country for 802.11d
+ * auto-country mode
+ */
+#ifdef BCMDBG
+ bcm_tlv_t *country_ie_override; /* debug override of announced Country IE */
+#endif
+
+ u16 prb_resp_timeout; /* do not send prb resp if request older than this,
+ * 0 = disable
+ */
+
+ wlc_rateset_t sup_rates_override; /* use only these rates in 11g supported rates if
+ * specifed
+ */
+
+ chanspec_t home_chanspec; /* shared home chanspec */
+
+ /* PHY parameters */
+ chanspec_t chanspec; /* target operational channel */
+ u16 usr_fragthresh; /* user configured fragmentation threshold */
+ u16 fragthresh[NFIFO]; /* per-fifo fragmentation thresholds */
+ u16 RTSThresh; /* 802.11 dot11RTSThreshold */
+ u16 SRL; /* 802.11 dot11ShortRetryLimit */
+ u16 LRL; /* 802.11 dot11LongRetryLimit */
+ u16 SFBL; /* Short Frame Rate Fallback Limit */
+ u16 LFBL; /* Long Frame Rate Fallback Limit */
+
+ /* network config */
+ bool shortpreamble; /* currently operating with CCK ShortPreambles */
+ bool shortslot; /* currently using 11g ShortSlot timing */
+ s8 barker_preamble; /* current Barker Preamble Mode */
+ s8 shortslot_override; /* 11g ShortSlot override */
+ bool include_legacy_erp; /* include Legacy ERP info elt ID 47 as well as g ID 42 */
+ bool barker_overlap_control; /* true: be aware of overlapping BSSs for barker */
+ bool ignore_bcns; /* override: ignore non shortslot bcns in a 11g network */
+ bool legacy_probe; /* restricts probe requests to CCK rates */
+
+ wlc_protection_t *protection;
+ s8 PLCPHdr_override; /* 802.11b Preamble Type override */
+
+ wlc_stf_t *stf;
+
+ pkt_cb_t *pkt_callback; /* tx completion callback handlers */
+
+ u32 txretried; /* tx retried number in one msdu */
+
+ ratespec_t bcn_rspec; /* save bcn ratespec purpose */
+
+ bool apsd_sta_usp; /* Unscheduled Service Period in progress on STA */
+ struct wl_timer *apsd_trigger_timer; /* timer for wme apsd trigger frames */
+ u32 apsd_trigger_timeout; /* timeout value for apsd_trigger_timer (in ms)
+ * 0 == disable
+ */
+ ac_bitmap_t apsd_trigger_ac; /* Permissible Acess Category in which APSD Null
+ * Trigger frames can be send
+ */
+ wlc_ap_info_t *ap;
+
+ u8 htphy_membership; /* HT PHY membership */
+
+ bool _regulatory_domain; /* 802.11d enabled? */
+
+ u8 mimops_PM;
+
+ u8 txpwr_percent; /* power output percentage */
+
+ u8 ht_wsec_restriction; /* the restriction of HT with TKIP or WEP */
+
+ uint tempsense_lasttime;
+
+ u16 tx_duty_cycle_ofdm; /* maximum allowed duty cycle for OFDM */
+ u16 tx_duty_cycle_cck; /* maximum allowed duty cycle for CCK */
+
+ u16 next_bsscfg_ID;
+
+ wlc_if_t *wlcif_list; /* linked list of wlc_if structs */
+ wlc_txq_info_t *active_queue; /* txq for the currently active transmit context */
+ u32 mpc_dur; /* total time (ms) in mpc mode except for the
+ * portion since radio is turned off last time
+ */
+ u32 mpc_laston_ts; /* timestamp (ms) when radio is turned off last
+ * time
+ */
+ bool pr80838_war;
+ uint hwrxoff;
+};
+
+/* antsel module specific state */
+struct antsel_info {
+ wlc_info_t *wlc; /* pointer to main wlc structure */
+ wlc_pub_t *pub; /* pointer to public fn */
+ u8 antsel_type; /* Type of boardlevel mimo antenna switch-logic
+ * 0 = N/A, 1 = 2x4 board, 2 = 2x3 CB2 board
+ */
+ u8 antsel_antswitch; /* board level antenna switch type */
+ bool antsel_avail; /* Ant selection availability (SROM based) */
+ wlc_antselcfg_t antcfg_11n; /* antenna configuration */
+ wlc_antselcfg_t antcfg_cur; /* current antenna config (auto) */
+};
+
+#define CHANNEL_BANDUNIT(wlc, ch) (((ch) <= CH_MAX_2G_CHANNEL) ? BAND_2G_INDEX : BAND_5G_INDEX)
+#define OTHERBANDUNIT(wlc) ((uint)((wlc)->band->bandunit ? BAND_2G_INDEX : BAND_5G_INDEX))
+
+#define IS_MBAND_UNLOCKED(wlc) \
+ ((NBANDS(wlc) > 1) && !(wlc)->bandlocked)
+
+#ifdef WLC_LOW
+#define WLC_BAND_PI_RADIO_CHANSPEC wlc_phy_chanspec_get(wlc->band->pi)
+#else
+#define WLC_BAND_PI_RADIO_CHANSPEC (wlc->chanspec)
+#endif
+
+/* sum the individual fifo tx pending packet counts */
+#if defined(WLC_HIGH_ONLY)
+#define TXPKTPENDTOT(wlc) (wlc_rpctx_txpktpend((wlc)->rpctx, 0, true))
+#define TXPKTPENDGET(wlc, fifo) (wlc_rpctx_txpktpend((wlc)->rpctx, (fifo), false))
+#define TXPKTPENDINC(wlc, fifo, val) (wlc_rpctx_txpktpendinc((wlc)->rpctx, (fifo), (val)))
+#define TXPKTPENDDEC(wlc, fifo, val) (wlc_rpctx_txpktpenddec((wlc)->rpctx, (fifo), (val)))
+#define TXPKTPENDCLR(wlc, fifo) (wlc_rpctx_txpktpendclr((wlc)->rpctx, (fifo)))
+#define TXAVAIL(wlc, fifo) (wlc_rpctx_txavail((wlc)->rpctx, (fifo)))
+#define GETNEXTTXP(wlc, _queue) (wlc_rpctx_getnexttxp((wlc)->rpctx, (_queue)))
+
+#else
+#define TXPKTPENDTOT(wlc) ((wlc)->core->txpktpend[0] + (wlc)->core->txpktpend[1] + \
+ (wlc)->core->txpktpend[2] + (wlc)->core->txpktpend[3])
+#define TXPKTPENDGET(wlc, fifo) ((wlc)->core->txpktpend[(fifo)])
+#define TXPKTPENDINC(wlc, fifo, val) ((wlc)->core->txpktpend[(fifo)] += (val))
+#define TXPKTPENDDEC(wlc, fifo, val) ((wlc)->core->txpktpend[(fifo)] -= (val))
+#define TXPKTPENDCLR(wlc, fifo) ((wlc)->core->txpktpend[(fifo)] = 0)
+#define TXAVAIL(wlc, fifo) (*(wlc)->core->txavail[(fifo)])
+#define GETNEXTTXP(wlc, _queue) \
+ dma_getnexttxp((wlc)->hw->di[(_queue)], HNDDMA_RANGE_TRANSMITTED)
+#endif /* WLC_HIGH_ONLY */
+
+#define WLC_IS_MATCH_SSID(wlc, ssid1, ssid2, len1, len2) \
+ ((len1 == len2) && !bcmp(ssid1, ssid2, len1))
+
+/* API shared by both WLC_HIGH and WLC_LOW driver */
+extern void wlc_high_dpc(wlc_info_t *wlc, u32 macintstatus);
+extern void wlc_fatal_error(wlc_info_t *wlc);
+extern void wlc_bmac_rpc_watchdog(wlc_info_t *wlc);
+extern void wlc_recv(wlc_info_t *wlc, void *p);
+extern bool wlc_dotxstatus(wlc_info_t *wlc, tx_status_t *txs, u32 frm_tx2);
+extern void wlc_txfifo(wlc_info_t *wlc, uint fifo, void *p, bool commit,
+ s8 txpktpend);
+extern void wlc_txfifo_complete(wlc_info_t *wlc, uint fifo, s8 txpktpend);
+extern void wlc_info_init(wlc_info_t *wlc, int unit);
+extern void wlc_print_txstatus(tx_status_t *txs);
+extern int wlc_xmtfifo_sz_get(wlc_info_t *wlc, uint fifo, uint *blocks);
+extern void wlc_write_template_ram(wlc_info_t *wlc, int offset, int len,
+ void *buf);
+extern void wlc_write_hw_bcntemplates(wlc_info_t *wlc, void *bcn, int len,
+ bool both);
+#if defined(BCMDBG)
+extern void wlc_get_rcmta(wlc_info_t *wlc, int idx, struct ether_addr *addr);
+#endif
+extern void wlc_set_rcmta(wlc_info_t *wlc, int idx,
+ const struct ether_addr *addr);
+extern void wlc_set_addrmatch(wlc_info_t *wlc, int match_reg_offset,
+ const struct ether_addr *addr);
+extern void wlc_read_tsf(wlc_info_t *wlc, u32 *tsf_l_ptr,
+ u32 *tsf_h_ptr);
+extern void wlc_set_cwmin(wlc_info_t *wlc, u16 newmin);
+extern void wlc_set_cwmax(wlc_info_t *wlc, u16 newmax);
+extern void wlc_fifoerrors(wlc_info_t *wlc);
+extern void wlc_pllreq(wlc_info_t *wlc, bool set, mbool req_bit);
+extern void wlc_reset_bmac_done(wlc_info_t *wlc);
+extern void wlc_protection_upd(wlc_info_t *wlc, uint idx, int val);
+extern void wlc_hwtimer_gptimer_set(wlc_info_t *wlc, uint us);
+extern void wlc_hwtimer_gptimer_abort(wlc_info_t *wlc);
+
+#if defined(BCMDBG)
+extern void wlc_print_rxh(d11rxhdr_t *rxh);
+extern void wlc_print_hdrs(wlc_info_t *wlc, const char *prefix, u8 *frame,
+ d11txh_t *txh, d11rxhdr_t *rxh, uint len);
+extern void wlc_print_txdesc(d11txh_t *txh);
+#endif
+#if defined(BCMDBG)
+extern void wlc_print_dot11_mac_hdr(u8 *buf, int len);
+#endif
+
+#ifdef WLC_LOW
+extern void wlc_setxband(wlc_hw_info_t *wlc_hw, uint bandunit);
+extern void wlc_coredisable(wlc_hw_info_t *wlc_hw);
+#endif
+
+extern bool wlc_valid_rate(wlc_info_t *wlc, ratespec_t rate, int band,
+ bool verbose);
+extern void wlc_ap_upd(wlc_info_t *wlc);
+
+/* helper functions */
+extern void wlc_shm_ssid_upd(wlc_info_t *wlc, wlc_bsscfg_t *cfg);
+extern int wlc_set_gmode(wlc_info_t *wlc, u8 gmode, bool config);
+
+extern void wlc_mac_bcn_promisc_change(wlc_info_t *wlc, bool promisc);
+extern void wlc_mac_bcn_promisc(wlc_info_t *wlc);
+extern void wlc_mac_promisc(wlc_info_t *wlc);
+extern void wlc_txflowcontrol(wlc_info_t *wlc, wlc_txq_info_t *qi, bool on,
+ int prio);
+extern void wlc_txflowcontrol_override(wlc_info_t *wlc, wlc_txq_info_t *qi,
+ bool on, uint override);
+extern bool wlc_txflowcontrol_prio_isset(wlc_info_t *wlc, wlc_txq_info_t *qi,
+ int prio);
+extern void wlc_send_q(wlc_info_t *wlc, wlc_txq_info_t *qi);
+extern int wlc_prep_pdu(wlc_info_t *wlc, void *pdu, uint *fifo);
+
+extern u16 wlc_calc_lsig_len(wlc_info_t *wlc, ratespec_t ratespec,
+ uint mac_len);
+extern ratespec_t wlc_rspec_to_rts_rspec(wlc_info_t *wlc, ratespec_t rspec,
+ bool use_rspec, u16 mimo_ctlchbw);
+extern u16 wlc_compute_rtscts_dur(wlc_info_t *wlc, bool cts_only,
+ ratespec_t rts_rate, ratespec_t frame_rate,
+ u8 rts_preamble_type,
+ u8 frame_preamble_type, uint frame_len,
+ bool ba);
+
+extern void wlc_tbtt(wlc_info_t *wlc, d11regs_t *regs);
+
+#if defined(BCMDBG)
+extern void wlc_dump_ie(wlc_info_t *wlc, bcm_tlv_t *ie, struct bcmstrbuf *b);
+#endif
+
+extern bool wlc_ps_check(wlc_info_t *wlc);
+extern void wlc_reprate_init(wlc_info_t *wlc);
+extern void wlc_bsscfg_reprate_init(wlc_bsscfg_t *bsscfg);
+extern void wlc_uint64_sub(u32 *a_high, u32 *a_low, u32 b_high,
+ u32 b_low);
+extern u32 wlc_calc_tbtt_offset(u32 bi, u32 tsf_h, u32 tsf_l);
+
+/* Shared memory access */
+extern void wlc_write_shm(wlc_info_t *wlc, uint offset, u16 v);
+extern u16 wlc_read_shm(wlc_info_t *wlc, uint offset);
+extern void wlc_set_shm(wlc_info_t *wlc, uint offset, u16 v, int len);
+extern void wlc_copyto_shm(wlc_info_t *wlc, uint offset, const void *buf,
+ int len);
+extern void wlc_copyfrom_shm(wlc_info_t *wlc, uint offset, void *buf, int len);
+
+extern void wlc_update_beacon(wlc_info_t *wlc);
+extern void wlc_bss_update_beacon(wlc_info_t *wlc, struct wlc_bsscfg *bsscfg);
+
+extern void wlc_update_probe_resp(wlc_info_t *wlc, bool suspend);
+extern void wlc_bss_update_probe_resp(wlc_info_t *wlc, wlc_bsscfg_t *cfg,
+ bool suspend);
+
+extern bool wlc_ismpc(wlc_info_t *wlc);
+extern bool wlc_is_non_delay_mpc(wlc_info_t *wlc);
+extern void wlc_radio_mpc_upd(wlc_info_t *wlc);
+extern bool wlc_prec_enq(wlc_info_t *wlc, struct pktq *q, void *pkt, int prec);
+extern bool wlc_prec_enq_head(wlc_info_t *wlc, struct pktq *q, void *pkt,
+ int prec, bool head);
+extern u16 wlc_phytxctl1_calc(wlc_info_t *wlc, ratespec_t rspec);
+extern void wlc_compute_plcp(wlc_info_t *wlc, ratespec_t rate, uint length,
+ u8 *plcp);
+extern uint wlc_calc_frame_time(wlc_info_t *wlc, ratespec_t ratespec,
+ u8 preamble_type, uint mac_len);
+
+extern void wlc_set_chanspec(wlc_info_t *wlc, chanspec_t chanspec);
+
+extern bool wlc_timers_init(wlc_info_t *wlc, int unit);
+
+extern const bcm_iovar_t wlc_iovars[];
+
+extern int wlc_doiovar(void *hdl, const bcm_iovar_t *vi, u32 actionid,
+ const char *name, void *params, uint p_len, void *arg,
+ int len, int val_size, wlc_if_t *wlcif);
+
+#if defined(BCMDBG)
+extern void wlc_print_ies(wlc_info_t *wlc, u8 *ies, uint ies_len);
+#endif
+
+extern int wlc_set_nmode(wlc_info_t *wlc, s32 nmode);
+extern void wlc_ht_mimops_cap_update(wlc_info_t *wlc, u8 mimops_mode);
+extern void wlc_mimops_action_ht_send(wlc_info_t *wlc, wlc_bsscfg_t *bsscfg,
+ u8 mimops_mode);
+
+extern void wlc_switch_shortslot(wlc_info_t *wlc, bool shortslot);
+extern void wlc_set_bssid(wlc_bsscfg_t *cfg);
+extern void wlc_edcf_setparams(wlc_bsscfg_t *cfg, bool suspend);
+extern void wlc_wme_setparams(wlc_info_t *wlc, u16 aci, void *arg,
+ bool suspend);
+
+extern void wlc_set_ratetable(wlc_info_t *wlc);
+extern int wlc_set_mac(wlc_bsscfg_t *cfg);
+extern void wlc_beacon_phytxctl_txant_upd(wlc_info_t *wlc,
+ ratespec_t bcn_rate);
+extern void wlc_mod_prb_rsp_rate_table(wlc_info_t *wlc, uint frame_len);
+extern ratespec_t wlc_lowest_basic_rspec(wlc_info_t *wlc, wlc_rateset_t *rs);
+extern u16 wlc_compute_bcntsfoff(wlc_info_t *wlc, ratespec_t rspec,
+ bool short_preamble, bool phydelay);
+extern void wlc_radio_disable(wlc_info_t *wlc);
+extern void wlc_bcn_li_upd(wlc_info_t *wlc);
+
+extern int wlc_get_revision_info(wlc_info_t *wlc, void *buf, uint len);
+extern void wlc_out(wlc_info_t *wlc);
+extern void wlc_set_home_chanspec(wlc_info_t *wlc, chanspec_t chanspec);
+extern void wlc_watchdog_upd(wlc_info_t *wlc, bool tbtt);
+extern bool wlc_ps_allowed(wlc_info_t *wlc);
+extern bool wlc_stay_awake(wlc_info_t *wlc);
+extern void wlc_wme_initparams_sta(wlc_info_t *wlc, wme_param_ie_t *pe);
+
+extern void wlc_bss_list_free(wlc_info_t *wlc, wlc_bss_list_t *bss_list);
+#endif /* _wlc_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_phy_shim.c b/drivers/staging/brcm80211/sys/wlc_phy_shim.c
new file mode 100644
index 000000000000..bf8e2e1a15f6
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_phy_shim.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * This is "two-way" interface, acting as the SHIM layer between WL and PHY layer.
+ * WL driver can optinally call this translation layer to do some preprocessing, then reach PHY.
+ * On the PHY->WL driver direction, all calls go through this layer since PHY doesn't have the
+ * access to wlc_hw pointer.
+ */
+
+#include <linux/kernel.h>
+#include <bcmdefs.h>
+#include <wlc_cfg.h>
+#include <linuxver.h>
+#include <bcmutils.h>
+#include <osl.h>
+
+#include <proto/802.11.h>
+#include <bcmwifi.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+#include <sbconfig.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <hnddma.h>
+#include <hndpmu.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_pub.h>
+#include <wlc_channel.h>
+#include <bcmsrom.h>
+#include <wlc_key.h>
+
+#include <wlc_mac80211.h>
+
+#include <wlc_bmac.h>
+#include <wlc_phy_shim.h>
+#include <wlc_phy_hal.h>
+#include <wl_export.h>
+
+/* PHY SHIM module specific state */
+struct wlc_phy_shim_info {
+ wlc_hw_info_t *wlc_hw; /* pointer to main wlc_hw structure */
+ void *wlc; /* pointer to main wlc structure */
+ void *wl; /* pointer to os-specific private state */
+};
+
+wlc_phy_shim_info_t *wlc_phy_shim_attach(wlc_hw_info_t *wlc_hw,
+ void *wl, void *wlc) {
+ wlc_phy_shim_info_t *physhim = NULL;
+
+ physhim = kzalloc(sizeof(wlc_phy_shim_info_t), GFP_ATOMIC);
+ if (!physhim) {
+ WL_ERROR(("wl%d: wlc_phy_shim_attach: out of mem\n", wlc_hw->unit));
+ return NULL;
+ }
+ physhim->wlc_hw = wlc_hw;
+ physhim->wlc = wlc;
+ physhim->wl = wl;
+
+ return physhim;
+}
+
+void wlc_phy_shim_detach(wlc_phy_shim_info_t *physhim)
+{
+ if (!physhim)
+ return;
+
+ kfree(physhim);
+}
+
+struct wlapi_timer *wlapi_init_timer(wlc_phy_shim_info_t *physhim,
+ void (*fn) (void *arg), void *arg,
+ const char *name)
+{
+ return (struct wlapi_timer *)wl_init_timer(physhim->wl, fn, arg, name);
+}
+
+void wlapi_free_timer(wlc_phy_shim_info_t *physhim, struct wlapi_timer *t)
+{
+ wl_free_timer(physhim->wl, (struct wl_timer *)t);
+}
+
+void
+wlapi_add_timer(wlc_phy_shim_info_t *physhim, struct wlapi_timer *t, uint ms,
+ int periodic)
+{
+ wl_add_timer(physhim->wl, (struct wl_timer *)t, ms, periodic);
+}
+
+bool wlapi_del_timer(wlc_phy_shim_info_t *physhim, struct wlapi_timer *t)
+{
+ return wl_del_timer(physhim->wl, (struct wl_timer *)t);
+}
+
+void wlapi_intrson(wlc_phy_shim_info_t *physhim)
+{
+ wl_intrson(physhim->wl);
+}
+
+u32 wlapi_intrsoff(wlc_phy_shim_info_t *physhim)
+{
+ return wl_intrsoff(physhim->wl);
+}
+
+void wlapi_intrsrestore(wlc_phy_shim_info_t *physhim, u32 macintmask)
+{
+ wl_intrsrestore(physhim->wl, macintmask);
+}
+
+void wlapi_bmac_write_shm(wlc_phy_shim_info_t *physhim, uint offset, u16 v)
+{
+ wlc_bmac_write_shm(physhim->wlc_hw, offset, v);
+}
+
+u16 wlapi_bmac_read_shm(wlc_phy_shim_info_t *physhim, uint offset)
+{
+ return wlc_bmac_read_shm(physhim->wlc_hw, offset);
+}
+
+void
+wlapi_bmac_mhf(wlc_phy_shim_info_t *physhim, u8 idx, u16 mask,
+ u16 val, int bands)
+{
+ wlc_bmac_mhf(physhim->wlc_hw, idx, mask, val, bands);
+}
+
+void wlapi_bmac_corereset(wlc_phy_shim_info_t *physhim, u32 flags)
+{
+ wlc_bmac_corereset(physhim->wlc_hw, flags);
+}
+
+void wlapi_suspend_mac_and_wait(wlc_phy_shim_info_t *physhim)
+{
+ wlc_suspend_mac_and_wait(physhim->wlc);
+}
+
+void wlapi_switch_macfreq(wlc_phy_shim_info_t *physhim, u8 spurmode)
+{
+ wlc_bmac_switch_macfreq(physhim->wlc_hw, spurmode);
+}
+
+void wlapi_enable_mac(wlc_phy_shim_info_t *physhim)
+{
+ wlc_enable_mac(physhim->wlc);
+}
+
+void wlapi_bmac_mctrl(wlc_phy_shim_info_t *physhim, u32 mask, u32 val)
+{
+ wlc_bmac_mctrl(physhim->wlc_hw, mask, val);
+}
+
+void wlapi_bmac_phy_reset(wlc_phy_shim_info_t *physhim)
+{
+ wlc_bmac_phy_reset(physhim->wlc_hw);
+}
+
+void wlapi_bmac_bw_set(wlc_phy_shim_info_t *physhim, u16 bw)
+{
+ wlc_bmac_bw_set(physhim->wlc_hw, bw);
+}
+
+u16 wlapi_bmac_get_txant(wlc_phy_shim_info_t *physhim)
+{
+ return wlc_bmac_get_txant(physhim->wlc_hw);
+}
+
+void wlapi_bmac_phyclk_fgc(wlc_phy_shim_info_t *physhim, bool clk)
+{
+ wlc_bmac_phyclk_fgc(physhim->wlc_hw, clk);
+}
+
+void wlapi_bmac_macphyclk_set(wlc_phy_shim_info_t *physhim, bool clk)
+{
+ wlc_bmac_macphyclk_set(physhim->wlc_hw, clk);
+}
+
+void wlapi_bmac_core_phypll_ctl(wlc_phy_shim_info_t *physhim, bool on)
+{
+ wlc_bmac_core_phypll_ctl(physhim->wlc_hw, on);
+}
+
+void wlapi_bmac_core_phypll_reset(wlc_phy_shim_info_t *physhim)
+{
+ wlc_bmac_core_phypll_reset(physhim->wlc_hw);
+}
+
+void wlapi_bmac_ucode_wake_override_phyreg_set(wlc_phy_shim_info_t *physhim)
+{
+ wlc_ucode_wake_override_set(physhim->wlc_hw, WLC_WAKE_OVERRIDE_PHYREG);
+}
+
+void wlapi_bmac_ucode_wake_override_phyreg_clear(wlc_phy_shim_info_t *physhim)
+{
+ wlc_ucode_wake_override_clear(physhim->wlc_hw,
+ WLC_WAKE_OVERRIDE_PHYREG);
+}
+
+void
+wlapi_bmac_write_template_ram(wlc_phy_shim_info_t *physhim, int offset,
+ int len, void *buf)
+{
+ wlc_bmac_write_template_ram(physhim->wlc_hw, offset, len, buf);
+}
+
+u16 wlapi_bmac_rate_shm_offset(wlc_phy_shim_info_t *physhim, u8 rate)
+{
+ return wlc_bmac_rate_shm_offset(physhim->wlc_hw, rate);
+}
+
+void wlapi_ucode_sample_init(wlc_phy_shim_info_t *physhim)
+{
+}
+
+void
+wlapi_copyfrom_objmem(wlc_phy_shim_info_t *physhim, uint offset, void *buf,
+ int len, u32 sel)
+{
+ wlc_bmac_copyfrom_objmem(physhim->wlc_hw, offset, buf, len, sel);
+}
+
+void
+wlapi_copyto_objmem(wlc_phy_shim_info_t *physhim, uint offset, const void *buf,
+ int l, u32 sel)
+{
+ wlc_bmac_copyto_objmem(physhim->wlc_hw, offset, buf, l, sel);
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_phy_shim.h b/drivers/staging/brcm80211/sys/wlc_phy_shim.h
new file mode 100644
index 000000000000..c151a5d8c693
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_phy_shim.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_phy_shim_h_
+#define _wlc_phy_shim_h_
+
+#define RADAR_TYPE_NONE 0 /* Radar type None */
+#define RADAR_TYPE_ETSI_1 1 /* ETSI 1 Radar type */
+#define RADAR_TYPE_ETSI_2 2 /* ETSI 2 Radar type */
+#define RADAR_TYPE_ETSI_3 3 /* ETSI 3 Radar type */
+#define RADAR_TYPE_ITU_E 4 /* ITU E Radar type */
+#define RADAR_TYPE_ITU_K 5 /* ITU K Radar type */
+#define RADAR_TYPE_UNCLASSIFIED 6 /* Unclassified Radar type */
+#define RADAR_TYPE_BIN5 7 /* long pulse radar type */
+#define RADAR_TYPE_STG2 8 /* staggered-2 radar */
+#define RADAR_TYPE_STG3 9 /* staggered-3 radar */
+#define RADAR_TYPE_FRA 10 /* French radar */
+
+/* French radar pulse widths */
+#define FRA_T1_20MHZ 52770
+#define FRA_T2_20MHZ 61538
+#define FRA_T3_20MHZ 66002
+#define FRA_T1_40MHZ 105541
+#define FRA_T2_40MHZ 123077
+#define FRA_T3_40MHZ 132004
+#define FRA_ERR_20MHZ 60
+#define FRA_ERR_40MHZ 120
+
+#define ANTSEL_NA 0 /* No boardlevel selection available */
+#define ANTSEL_2x4 1 /* 2x4 boardlevel selection available */
+#define ANTSEL_2x3 2 /* 2x3 CB2 boardlevel selection available */
+
+/* Rx Antenna diversity control values */
+#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */
+#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */
+#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */
+#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */
+#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */
+#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* default antdiv setting */
+
+/* Forward declarations */
+struct wlc_hw_info;
+typedef struct wlc_phy_shim_info wlc_phy_shim_info_t;
+
+extern wlc_phy_shim_info_t *wlc_phy_shim_attach(struct wlc_hw_info *wlc_hw,
+ void *wl, void *wlc);
+extern void wlc_phy_shim_detach(wlc_phy_shim_info_t *physhim);
+
+/* PHY to WL utility functions */
+struct wlapi_timer;
+extern struct wlapi_timer *wlapi_init_timer(wlc_phy_shim_info_t *physhim,
+ void (*fn) (void *arg), void *arg,
+ const char *name);
+extern void wlapi_free_timer(wlc_phy_shim_info_t *physhim,
+ struct wlapi_timer *t);
+extern void wlapi_add_timer(wlc_phy_shim_info_t *physhim,
+ struct wlapi_timer *t, uint ms, int periodic);
+extern bool wlapi_del_timer(wlc_phy_shim_info_t *physhim,
+ struct wlapi_timer *t);
+extern void wlapi_intrson(wlc_phy_shim_info_t *physhim);
+extern u32 wlapi_intrsoff(wlc_phy_shim_info_t *physhim);
+extern void wlapi_intrsrestore(wlc_phy_shim_info_t *physhim,
+ u32 macintmask);
+
+extern void wlapi_bmac_write_shm(wlc_phy_shim_info_t *physhim, uint offset,
+ u16 v);
+extern u16 wlapi_bmac_read_shm(wlc_phy_shim_info_t *physhim, uint offset);
+extern void wlapi_bmac_mhf(wlc_phy_shim_info_t *physhim, u8 idx,
+ u16 mask, u16 val, int bands);
+extern void wlapi_bmac_corereset(wlc_phy_shim_info_t *physhim, u32 flags);
+extern void wlapi_suspend_mac_and_wait(wlc_phy_shim_info_t *physhim);
+extern void wlapi_switch_macfreq(wlc_phy_shim_info_t *physhim, u8 spurmode);
+extern void wlapi_enable_mac(wlc_phy_shim_info_t *physhim);
+extern void wlapi_bmac_mctrl(wlc_phy_shim_info_t *physhim, u32 mask,
+ u32 val);
+extern void wlapi_bmac_phy_reset(wlc_phy_shim_info_t *physhim);
+extern void wlapi_bmac_bw_set(wlc_phy_shim_info_t *physhim, u16 bw);
+extern void wlapi_bmac_phyclk_fgc(wlc_phy_shim_info_t *physhim, bool clk);
+extern void wlapi_bmac_macphyclk_set(wlc_phy_shim_info_t *physhim, bool clk);
+extern void wlapi_bmac_core_phypll_ctl(wlc_phy_shim_info_t *physhim, bool on);
+extern void wlapi_bmac_core_phypll_reset(wlc_phy_shim_info_t *physhim);
+extern void wlapi_bmac_ucode_wake_override_phyreg_set(wlc_phy_shim_info_t *
+ physhim);
+extern void wlapi_bmac_ucode_wake_override_phyreg_clear(wlc_phy_shim_info_t *
+ physhim);
+extern void wlapi_bmac_write_template_ram(wlc_phy_shim_info_t *physhim, int o,
+ int len, void *buf);
+extern u16 wlapi_bmac_rate_shm_offset(wlc_phy_shim_info_t *physhim,
+ u8 rate);
+extern void wlapi_ucode_sample_init(wlc_phy_shim_info_t *physhim);
+extern void wlapi_copyfrom_objmem(wlc_phy_shim_info_t *physhim, uint,
+ void *buf, int, u32 sel);
+extern void wlapi_copyto_objmem(wlc_phy_shim_info_t *physhim, uint,
+ const void *buf, int, u32);
+
+extern void wlapi_high_update_phy_mode(wlc_phy_shim_info_t *physhim,
+ u32 phy_mode);
+extern u16 wlapi_bmac_get_txant(wlc_phy_shim_info_t *physhim);
+#endif /* _wlc_phy_shim_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_pub.h b/drivers/staging/brcm80211/sys/wlc_pub.h
new file mode 100644
index 000000000000..a6a8c33483c9
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_pub.h
@@ -0,0 +1,627 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_pub_h_
+#define _wlc_pub_h_
+
+#include <wlc_types.h>
+#include <wlc_scb.h>
+
+#define WLC_NUMRATES 16 /* max # of rates in a rateset */
+#define MAXMULTILIST 32 /* max # multicast addresses */
+#define D11_PHY_HDR_LEN 6 /* Phy header length - 6 bytes */
+
+/* phy types */
+#define PHY_TYPE_A 0 /* Phy type A */
+#define PHY_TYPE_G 2 /* Phy type G */
+#define PHY_TYPE_N 4 /* Phy type N */
+#define PHY_TYPE_LP 5 /* Phy type Low Power A/B/G */
+#define PHY_TYPE_SSN 6 /* Phy type Single Stream N */
+#define PHY_TYPE_LCN 8 /* Phy type Single Stream N */
+#define PHY_TYPE_LCNXN 9 /* Phy type 2-stream N */
+#define PHY_TYPE_HT 7 /* Phy type 3-Stream N */
+
+/* bw */
+#define WLC_10_MHZ 10 /* 10Mhz nphy channel bandwidth */
+#define WLC_20_MHZ 20 /* 20Mhz nphy channel bandwidth */
+#define WLC_40_MHZ 40 /* 40Mhz nphy channel bandwidth */
+
+#define CHSPEC_WLC_BW(chanspec) (CHSPEC_IS40(chanspec) ? WLC_40_MHZ : \
+ CHSPEC_IS20(chanspec) ? WLC_20_MHZ : \
+ WLC_10_MHZ)
+
+#define WLC_RSSI_MINVAL -200 /* Low value, e.g. for forcing roam */
+#define WLC_RSSI_NO_SIGNAL -91 /* NDIS RSSI link quality cutoffs */
+#define WLC_RSSI_VERY_LOW -80 /* Very low quality cutoffs */
+#define WLC_RSSI_LOW -70 /* Low quality cutoffs */
+#define WLC_RSSI_GOOD -68 /* Good quality cutoffs */
+#define WLC_RSSI_VERY_GOOD -58 /* Very good quality cutoffs */
+#define WLC_RSSI_EXCELLENT -57 /* Excellent quality cutoffs */
+
+#define WLC_PHYTYPE(_x) (_x) /* macro to perform WLC PHY -> D11 PHY TYPE, currently 1:1 */
+
+#define MA_WINDOW_SZ 8 /* moving average window size */
+
+#define WLC_SNR_INVALID 0 /* invalid SNR value */
+
+/* a large TX Power as an init value to factor out of min() calculations,
+ * keep low enough to fit in an s8, units are .25 dBm
+ */
+#define WLC_TXPWR_MAX (127) /* ~32 dBm = 1,500 mW */
+
+/* legacy rx Antenna diversity for SISO rates */
+#define ANT_RX_DIV_FORCE_0 0 /* Use antenna 0 */
+#define ANT_RX_DIV_FORCE_1 1 /* Use antenna 1 */
+#define ANT_RX_DIV_START_1 2 /* Choose starting with 1 */
+#define ANT_RX_DIV_START_0 3 /* Choose starting with 0 */
+#define ANT_RX_DIV_ENABLE 3 /* APHY bbConfig Enable RX Diversity */
+#define ANT_RX_DIV_DEF ANT_RX_DIV_START_0 /* default antdiv setting */
+
+/* legacy rx Antenna diversity for SISO rates */
+#define ANT_TX_FORCE_0 0 /* Tx on antenna 0, "legacy term Main" */
+#define ANT_TX_FORCE_1 1 /* Tx on antenna 1, "legacy term Aux" */
+#define ANT_TX_LAST_RX 3 /* Tx on phy's last good Rx antenna */
+#define ANT_TX_DEF 3 /* driver's default tx antenna setting */
+
+#define TXCORE_POLICY_ALL 0x1 /* use all available core for transmit */
+
+/* Tx Chain values */
+#define TXCHAIN_DEF 0x1 /* def bitmap of txchain */
+#define TXCHAIN_DEF_NPHY 0x3 /* default bitmap of tx chains for nphy */
+#define TXCHAIN_DEF_HTPHY 0x7 /* default bitmap of tx chains for nphy */
+#define RXCHAIN_DEF 0x1 /* def bitmap of rxchain */
+#define RXCHAIN_DEF_NPHY 0x3 /* default bitmap of rx chains for nphy */
+#define RXCHAIN_DEF_HTPHY 0x7 /* default bitmap of rx chains for nphy */
+#define ANTSWITCH_NONE 0 /* no antenna switch */
+#define ANTSWITCH_TYPE_1 1 /* antenna switch on 4321CB2, 2of3 */
+#define ANTSWITCH_TYPE_2 2 /* antenna switch on 4321MPCI, 2of3 */
+#define ANTSWITCH_TYPE_3 3 /* antenna switch on 4322, 2of3 */
+
+#define RXBUFSZ PKTBUFSZ
+#ifndef AIDMAPSZ
+#define AIDMAPSZ (roundup(MAXSCB, NBBY)/NBBY) /* aid bitmap size in bytes */
+#endif /* AIDMAPSZ */
+
+typedef struct wlc_tunables {
+ int ntxd; /* size of tx descriptor table */
+ int nrxd; /* size of rx descriptor table */
+ int rxbufsz; /* size of rx buffers to post */
+ int nrxbufpost; /* # of rx buffers to post */
+ int maxscb; /* # of SCBs supported */
+ int ampdunummpdu; /* max number of mpdu in an ampdu */
+ int maxpktcb; /* max # of packet callbacks */
+ int maxucodebss; /* max # of BSS handled in ucode bcn/prb */
+ int maxucodebss4; /* max # of BSS handled in sw bcn/prb */
+ int maxbss; /* max # of bss info elements in scan list */
+ int datahiwat; /* data msg txq hiwat mark */
+ int ampdudatahiwat; /* AMPDU msg txq hiwat mark */
+ int rxbnd; /* max # of rx bufs to process before deferring to dpc */
+ int txsbnd; /* max # tx status to process in wlc_txstatus() */
+ int memreserved; /* memory reserved for BMAC's USB dma rx */
+} wlc_tunables_t;
+
+typedef struct wlc_rateset {
+ uint count; /* number of rates in rates[] */
+ u8 rates[WLC_NUMRATES]; /* rates in 500kbps units w/hi bit set if basic */
+ u8 htphy_membership; /* HT PHY Membership */
+ u8 mcs[MCSSET_LEN]; /* supported mcs index bit map */
+} wlc_rateset_t;
+
+struct rsn_parms {
+ u8 flags; /* misc booleans (e.g., supported) */
+ u8 multicast; /* multicast cipher */
+ u8 ucount; /* count of unicast ciphers */
+ u8 unicast[4]; /* unicast ciphers */
+ u8 acount; /* count of auth modes */
+ u8 auth[4]; /* Authentication modes */
+ u8 PAD[4]; /* padding for future growth */
+};
+
+/*
+ * buffer length needed for wlc_format_ssid
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+#define SSID_FMT_BUF_LEN ((4 * DOT11_MAX_SSID_LEN) + 1)
+
+#define RSN_FLAGS_SUPPORTED 0x1 /* Flag for rsn_params */
+#define RSN_FLAGS_PREAUTH 0x2 /* Flag for WPA2 rsn_params */
+
+/* All the HT-specific default advertised capabilities (including AMPDU)
+ * should be grouped here at one place
+ */
+#define AMPDU_DEF_MPDU_DENSITY 6 /* default mpdu density (110 ==> 4us) */
+
+/* defaults for the HT (MIMO) bss */
+#define HT_CAP ((HT_CAP_MIMO_PS_OFF << HT_CAP_MIMO_PS_SHIFT) | HT_CAP_40MHZ | \
+ HT_CAP_GF | HT_CAP_MAX_AMSDU | HT_CAP_DSSS_CCK)
+
+/* WLC packet type is a void * */
+typedef void *wlc_pkt_t;
+
+/* Event data type */
+typedef struct wlc_event {
+ wl_event_msg_t event; /* encapsulated event */
+ struct ether_addr *addr; /* used to keep a trace of the potential present of
+ * an address in wlc_event_msg_t
+ */
+ int bsscfgidx; /* BSS config when needed */
+ struct wl_if *wlif; /* pointer to wlif */
+ void *data; /* used to hang additional data on an event */
+ struct wlc_event *next; /* enables ordered list of pending events */
+} wlc_event_t;
+
+/* wlc internal bss_info, wl external one is in wlioctl.h */
+typedef struct wlc_bss_info {
+ struct ether_addr BSSID; /* network BSSID */
+ u16 flags; /* flags for internal attributes */
+ u8 SSID_len; /* the length of SSID */
+ u8 SSID[32]; /* SSID string */
+ s16 RSSI; /* receive signal strength (in dBm) */
+ s16 SNR; /* receive signal SNR in dB */
+ u16 beacon_period; /* units are Kusec */
+ u16 atim_window; /* units are Kusec */
+ chanspec_t chanspec; /* Channel num, bw, ctrl_sb and band */
+ s8 infra; /* 0=IBSS, 1=infrastructure, 2=unknown */
+ wlc_rateset_t rateset; /* supported rates */
+ u8 dtim_period; /* DTIM period */
+ s8 phy_noise; /* noise right after tx (in dBm) */
+ u16 capability; /* Capability information */
+ struct dot11_bcn_prb *bcn_prb; /* beacon/probe response frame (ioctl na) */
+ u16 bcn_prb_len; /* beacon/probe response frame length (ioctl na) */
+ u8 wme_qosinfo; /* QoS Info from WME IE; valid if WLC_BSS_WME flag set */
+ struct rsn_parms wpa;
+ struct rsn_parms wpa2;
+ u16 qbss_load_aac; /* qbss load available admission capacity */
+ /* qbss_load_chan_free <- (0xff - channel_utilization of qbss_load_ie_t) */
+ u8 qbss_load_chan_free; /* indicates how free the channel is */
+ u8 mcipher; /* multicast cipher */
+ u8 wpacfg; /* wpa config index */
+} wlc_bss_info_t;
+
+/* forward declarations */
+struct wlc_if;
+
+/* wlc_ioctl error codes */
+#define WLC_ENOIOCTL 1 /* No such Ioctl */
+#define WLC_EINVAL 2 /* Invalid value */
+#define WLC_ETOOSMALL 3 /* Value too small */
+#define WLC_ETOOBIG 4 /* Value too big */
+#define WLC_ERANGE 5 /* Out of range */
+#define WLC_EDOWN 6 /* Down */
+#define WLC_EUP 7 /* Up */
+#define WLC_ENOMEM 8 /* No Memory */
+#define WLC_EBUSY 9 /* Busy */
+
+/* IOVar flags for common error checks */
+#define IOVF_MFG (1<<3) /* flag for mfgtest iovars */
+#define IOVF_WHL (1<<4) /* value must be whole (0-max) */
+#define IOVF_NTRL (1<<5) /* value must be natural (1-max) */
+
+#define IOVF_SET_UP (1<<6) /* set requires driver be up */
+#define IOVF_SET_DOWN (1<<7) /* set requires driver be down */
+#define IOVF_SET_CLK (1<<8) /* set requires core clock */
+#define IOVF_SET_BAND (1<<9) /* set requires fixed band */
+
+#define IOVF_GET_UP (1<<10) /* get requires driver be up */
+#define IOVF_GET_DOWN (1<<11) /* get requires driver be down */
+#define IOVF_GET_CLK (1<<12) /* get requires core clock */
+#define IOVF_GET_BAND (1<<13) /* get requires fixed band */
+#define IOVF_OPEN_ALLOW (1<<14) /* set allowed iovar for opensrc */
+
+/* watchdog down and dump callback function proto's */
+typedef int (*watchdog_fn_t) (void *handle);
+typedef int (*down_fn_t) (void *handle);
+typedef int (*dump_fn_t) (void *handle, struct bcmstrbuf *b);
+
+/* IOVar handler
+ *
+ * handle - a pointer value registered with the function
+ * vi - iovar_info that was looked up
+ * actionid - action ID, calculated by IOV_GVAL() and IOV_SVAL() based on varid.
+ * name - the actual iovar name
+ * params/plen - parameters and length for a get, input only.
+ * arg/len - buffer and length for value to be set or retrieved, input or output.
+ * vsize - value size, valid for integer type only.
+ * wlcif - interface context (wlc_if pointer)
+ *
+ * All pointers may point into the same buffer.
+ */
+typedef int (*iovar_fn_t) (void *handle, const bcm_iovar_t *vi,
+ u32 actionid, const char *name, void *params,
+ uint plen, void *arg, int alen, int vsize,
+ struct wlc_if *wlcif);
+
+#define MAC80211_PROMISC_BCNS (1 << 0)
+#define MAC80211_SCAN (1 << 1)
+
+/*
+ * Public portion of "common" os-independent state structure.
+ * The wlc handle points at this.
+ */
+typedef struct wlc_pub {
+ void *wlc;
+
+ struct ieee80211_hw *ieee_hw;
+ struct scb *global_scb;
+ scb_ampdu_t *global_ampdu;
+ uint mac80211_state;
+ uint unit; /* device instance number */
+ uint corerev; /* core revision */
+ osl_t *osh; /* pointer to os handle */
+ si_t *sih; /* SB handle (cookie for siutils calls) */
+ char *vars; /* "environment" name=value */
+ bool up; /* interface up and running */
+ bool hw_off; /* HW is off */
+ wlc_tunables_t *tunables; /* tunables: ntxd, nrxd, maxscb, etc. */
+ bool hw_up; /* one time hw up/down(from boot or hibernation) */
+ bool _piomode; /* true if pio mode *//* BMAC_NOTE: NEED In both */
+ uint _nbands; /* # bands supported */
+ uint now; /* # elapsed seconds */
+
+ bool promisc; /* promiscuous destination address */
+ bool delayed_down; /* down delayed */
+ bool _ap; /* AP mode enabled */
+ bool _apsta; /* simultaneous AP/STA mode enabled */
+ bool _assoc_recreate; /* association recreation on up transitions */
+ int _wme; /* WME QoS mode */
+ u8 _mbss; /* MBSS mode on */
+ bool allmulti; /* enable all multicasts */
+ bool associated; /* true:part of [I]BSS, false: not */
+ /* (union of stas_associated, aps_associated) */
+ bool phytest_on; /* whether a PHY test is running */
+ bool bf_preempt_4306; /* True to enable 'darwin' mode */
+ bool _ampdu; /* ampdu enabled or not */
+ bool _cac; /* 802.11e CAC enabled */
+ u8 _n_enab; /* bitmap of 11N + HT support */
+ bool _n_reqd; /* N support required for clients */
+
+ s8 _coex; /* 20/40 MHz BSS Management AUTO, ENAB, DISABLE */
+ bool _priofc; /* Priority-based flowcontrol */
+
+ struct ether_addr cur_etheraddr; /* our local ethernet address */
+
+ struct ether_addr *multicast; /* ptr to list of multicast addresses */
+ uint nmulticast; /* # enabled multicast addresses */
+
+ u32 wlfeatureflag; /* Flags to control sw features from registry */
+ int psq_pkts_total; /* total num of ps pkts */
+
+ u16 txmaxpkts; /* max number of large pkts allowed to be pending */
+
+ /* s/w decryption counters */
+ u32 swdecrypt; /* s/w decrypt attempts */
+
+ int bcmerror; /* last bcm error */
+
+ mbool radio_disabled; /* bit vector for radio disabled reasons */
+ bool radio_active; /* radio on/off state */
+ u16 roam_time_thresh; /* Max. # secs. of not hearing beacons
+ * before roaming.
+ */
+ bool align_wd_tbtt; /* Align watchdog with tbtt indication
+ * handling. This flag is cleared by default
+ * and is set by per port code explicitly and
+ * you need to make sure the OSL_SYSUPTIME()
+ * is implemented properly in osl of that port
+ * when it enables this Power Save feature.
+ */
+#ifdef BCMSDIO
+ uint sdiod_drive_strength; /* SDIO drive strength */
+#endif /* BCMSDIO */
+
+ u16 boardrev; /* version # of particular board */
+ u8 sromrev; /* version # of the srom */
+ char srom_ccode[WLC_CNTRY_BUF_SZ]; /* Country Code in SROM */
+ u32 boardflags; /* Board specific flags from srom */
+ u32 boardflags2; /* More board flags if sromrev >= 4 */
+ bool tempsense_disable; /* disable periodic tempsense check */
+
+ bool _lmac; /* lmac module included and enabled */
+ bool _lmacproto; /* lmac protocol module included and enabled */
+ bool phy_11ncapable; /* the PHY/HW is capable of 802.11N */
+ bool _ampdumac; /* mac assist ampdu enabled or not */
+} wlc_pub_t;
+
+/* wl_monitor rx status per packet */
+typedef struct wl_rxsts {
+ uint pkterror; /* error flags per pkt */
+ uint phytype; /* 802.11 A/B/G ... */
+ uint channel; /* channel */
+ uint datarate; /* rate in 500kbps */
+ uint antenna; /* antenna pkts received on */
+ uint pktlength; /* pkt length minus bcm phy hdr */
+ u32 mactime; /* time stamp from mac, count per 1us */
+ uint sq; /* signal quality */
+ s32 signal; /* in dbm */
+ s32 noise; /* in dbm */
+ uint preamble; /* Unknown, short, long */
+ uint encoding; /* Unknown, CCK, PBCC, OFDM */
+ uint nfrmtype; /* special 802.11n frames(AMPDU, AMSDU) */
+ struct wl_if *wlif; /* wl interface */
+} wl_rxsts_t;
+
+/* status per error RX pkt */
+#define WL_RXS_CRC_ERROR 0x00000001 /* CRC Error in packet */
+#define WL_RXS_RUNT_ERROR 0x00000002 /* Runt packet */
+#define WL_RXS_ALIGN_ERROR 0x00000004 /* Misaligned packet */
+#define WL_RXS_OVERSIZE_ERROR 0x00000008 /* packet bigger than RX_LENGTH (usually 1518) */
+#define WL_RXS_WEP_ICV_ERROR 0x00000010 /* Integrity Check Value error */
+#define WL_RXS_WEP_ENCRYPTED 0x00000020 /* Encrypted with WEP */
+#define WL_RXS_PLCP_SHORT 0x00000040 /* Short PLCP error */
+#define WL_RXS_DECRYPT_ERR 0x00000080 /* Decryption error */
+#define WL_RXS_OTHER_ERR 0x80000000 /* Other errors */
+
+/* phy type */
+#define WL_RXS_PHY_A 0x00000000 /* A phy type */
+#define WL_RXS_PHY_B 0x00000001 /* B phy type */
+#define WL_RXS_PHY_G 0x00000002 /* G phy type */
+#define WL_RXS_PHY_N 0x00000004 /* N phy type */
+
+/* encoding */
+#define WL_RXS_ENCODING_CCK 0x00000000 /* CCK encoding */
+#define WL_RXS_ENCODING_OFDM 0x00000001 /* OFDM encoding */
+
+/* preamble */
+#define WL_RXS_UNUSED_STUB 0x0 /* stub to match with wlc_ethereal.h */
+#define WL_RXS_PREAMBLE_SHORT 0x00000001 /* Short preamble */
+#define WL_RXS_PREAMBLE_LONG 0x00000002 /* Long preamble */
+#define WL_RXS_PREAMBLE_MIMO_MM 0x00000003 /* MIMO mixed mode preamble */
+#define WL_RXS_PREAMBLE_MIMO_GF 0x00000004 /* MIMO green field preamble */
+
+#define WL_RXS_NFRM_AMPDU_FIRST 0x00000001 /* first MPDU in A-MPDU */
+#define WL_RXS_NFRM_AMPDU_SUB 0x00000002 /* subsequent MPDU(s) in A-MPDU */
+#define WL_RXS_NFRM_AMSDU_FIRST 0x00000004 /* first MSDU in A-MSDU */
+#define WL_RXS_NFRM_AMSDU_SUB 0x00000008 /* subsequent MSDU(s) in A-MSDU */
+
+/* forward declare and use the struct notation so we don't have to
+ * have it defined if not necessary.
+ */
+struct wlc_info;
+struct wlc_hw_info;
+struct wlc_bsscfg;
+struct wlc_if;
+
+/***********************************************
+ * Feature-related macros to optimize out code *
+ * *********************************************
+ */
+
+/* AP Support (versus STA) */
+#define AP_ENAB(pub) (0)
+
+/* Macro to check if APSTA mode enabled */
+#define APSTA_ENAB(pub) (0)
+
+/* Some useful combinations */
+#define STA_ONLY(pub) (!AP_ENAB(pub))
+#define AP_ONLY(pub) (AP_ENAB(pub) && !APSTA_ENAB(pub))
+
+#define ENAB_1x1 0x01
+#define ENAB_2x2 0x02
+#define ENAB_3x3 0x04
+#define ENAB_4x4 0x08
+#define SUPPORT_11N (ENAB_1x1|ENAB_2x2)
+#define SUPPORT_HT (ENAB_1x1|ENAB_2x2|ENAB_3x3)
+/* WL11N Support */
+#if ((defined(NCONF) && (NCONF != 0)) || (defined(LCNCONF) && (LCNCONF != 0)) || \
+ (defined(HTCONF) && (HTCONF != 0)) || (defined(SSLPNCONF) && (SSLPNCONF != 0)))
+#define N_ENAB(pub) ((pub)->_n_enab & SUPPORT_11N)
+#define N_REQD(pub) ((pub)->_n_reqd)
+#else
+#define N_ENAB(pub) 0
+#define N_REQD(pub) 0
+#endif
+
+#if (defined(HTCONF) && (HTCONF != 0))
+#define HT_ENAB(pub) (((pub)->_n_enab & SUPPORT_HT) == SUPPORT_HT)
+#else
+#define HT_ENAB(pub) 0
+#endif
+
+#define AMPDU_AGG_HOST 1
+#define AMPDU_ENAB(pub) ((pub)->_ampdu)
+
+#define EDCF_ENAB(pub) (WME_ENAB(pub))
+#define QOS_ENAB(pub) (WME_ENAB(pub) || N_ENAB(pub))
+
+#define MONITOR_ENAB(wlc) (bcmspace && (wlc)->monitor)
+
+#define PROMISC_ENAB(wlc) (bcmspace && (wlc)->promisc)
+
+extern void wlc_pkttag_info_move(wlc_pub_t *pub, void *pkt_from, void *pkt_to);
+
+#define WLPKTTAGSCB(p) (WLPKTTAG(p)->_scb)
+
+#define WLC_PREC_COUNT 16 /* Max precedence level implemented */
+
+/* pri is PKTPRIO encoded in the packet. This maps the Packet priority to
+ * enqueue precedence as defined in wlc_prec_map
+ */
+extern const u8 wlc_prio2prec_map[];
+#define WLC_PRIO_TO_PREC(pri) wlc_prio2prec_map[(pri) & 7]
+
+/* This maps priority to one precedence higher - Used by PS-Poll response packets to
+ * simulate enqueue-at-head operation, but still maintain the order on the queue
+ */
+#define WLC_PRIO_TO_HI_PREC(pri) min(WLC_PRIO_TO_PREC(pri) + 1, WLC_PREC_COUNT - 1)
+
+extern const u8 wme_fifo2ac[];
+#define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]]
+
+/* Mask to describe all precedence levels */
+#define WLC_PREC_BMP_ALL MAXBITVAL(WLC_PREC_COUNT)
+
+/* Define a bitmap of precedences comprised by each AC */
+#define WLC_PREC_BMP_AC_BE (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_BE)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_BE)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_EE)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_EE)))
+#define WLC_PREC_BMP_AC_BK (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_BK)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_BK)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_NONE)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_NONE)))
+#define WLC_PREC_BMP_AC_VI (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_CL)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_CL)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_VI)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_VI)))
+#define WLC_PREC_BMP_AC_VO (NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_VO)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_VO)) | \
+ NBITVAL(WLC_PRIO_TO_PREC(PRIO_8021D_NC)) | \
+ NBITVAL(WLC_PRIO_TO_HI_PREC(PRIO_8021D_NC)))
+
+/* WME Support */
+#define WME_ENAB(pub) ((pub)->_wme != OFF)
+#define WME_AUTO(wlc) ((wlc)->pub->_wme == AUTO)
+
+#define WLC_USE_COREFLAGS 0xffffffff /* invalid core flags, use the saved coreflags */
+
+#define WLC_UPDATE_STATS(wlc) 0 /* No stats support */
+#define WLCNTINCR(a) /* No stats support */
+#define WLCNTDECR(a) /* No stats support */
+#define WLCNTADD(a, delta) /* No stats support */
+#define WLCNTSET(a, value) /* No stats support */
+#define WLCNTVAL(a) 0 /* No stats support */
+
+/* common functions for every port */
+extern void *wlc_attach(void *wl, u16 vendor, u16 device, uint unit,
+ bool piomode, osl_t *osh, void *regsva, uint bustype,
+ void *btparam, uint *perr);
+extern uint wlc_detach(struct wlc_info *wlc);
+extern int wlc_up(struct wlc_info *wlc);
+extern uint wlc_down(struct wlc_info *wlc);
+
+extern int wlc_set(struct wlc_info *wlc, int cmd, int arg);
+extern int wlc_get(struct wlc_info *wlc, int cmd, int *arg);
+extern int wlc_iovar_getint(struct wlc_info *wlc, const char *name, int *arg);
+extern int wlc_iovar_setint(struct wlc_info *wlc, const char *name, int arg);
+extern bool wlc_chipmatch(u16 vendor, u16 device);
+extern void wlc_init(struct wlc_info *wlc);
+extern void wlc_reset(struct wlc_info *wlc);
+
+extern void wlc_intrson(struct wlc_info *wlc);
+extern u32 wlc_intrsoff(struct wlc_info *wlc);
+extern void wlc_intrsrestore(struct wlc_info *wlc, u32 macintmask);
+extern bool wlc_intrsupd(struct wlc_info *wlc);
+extern bool wlc_isr(struct wlc_info *wlc, bool *wantdpc);
+extern bool wlc_dpc(struct wlc_info *wlc, bool bounded);
+extern bool wlc_send80211_raw(struct wlc_info *wlc, wlc_if_t *wlcif, void *p,
+ uint ac);
+extern int wlc_iovar_op(struct wlc_info *wlc, const char *name, void *params,
+ int p_len, void *arg, int len, bool set,
+ struct wlc_if *wlcif);
+extern int wlc_ioctl(struct wlc_info *wlc, int cmd, void *arg, int len,
+ struct wlc_if *wlcif);
+/* helper functions */
+extern void wlc_statsupd(struct wlc_info *wlc);
+extern int wlc_get_header_len(void);
+
+extern wlc_pub_t *wlc_pub(void *wlc);
+
+/* common functions for every port */
+extern int wlc_bmac_up_prep(struct wlc_hw_info *wlc_hw);
+extern int wlc_bmac_up_finish(struct wlc_hw_info *wlc_hw);
+extern int wlc_bmac_down_prep(struct wlc_hw_info *wlc_hw);
+extern int wlc_bmac_down_finish(struct wlc_hw_info *wlc_hw);
+
+extern u32 wlc_reg_read(struct wlc_info *wlc, void *r, uint size);
+extern void wlc_reg_write(struct wlc_info *wlc, void *r, u32 v, uint size);
+extern void wlc_corereset(struct wlc_info *wlc, u32 flags);
+extern void wlc_mhf(struct wlc_info *wlc, u8 idx, u16 mask, u16 val,
+ int bands);
+extern u16 wlc_mhf_get(struct wlc_info *wlc, u8 idx, int bands);
+extern u32 wlc_delta_txfunfl(struct wlc_info *wlc, int fifo);
+extern void wlc_rate_lookup_init(struct wlc_info *wlc, wlc_rateset_t *rateset);
+extern void wlc_default_rateset(struct wlc_info *wlc, wlc_rateset_t *rs);
+
+/* wlc_phy.c helper functions */
+extern void wlc_set_ps_ctrl(struct wlc_info *wlc);
+extern void wlc_mctrl(struct wlc_info *wlc, u32 mask, u32 val);
+extern void wlc_scb_ratesel_init_all(struct wlc_info *wlc);
+
+/* ioctl */
+extern int wlc_iovar_gets8(struct wlc_info *wlc, const char *name,
+ s8 *arg);
+extern int wlc_iovar_check(wlc_pub_t *pub, const bcm_iovar_t *vi, void *arg,
+ int len, bool set);
+
+extern int wlc_module_register(wlc_pub_t *pub, const bcm_iovar_t *iovars,
+ const char *name, void *hdl, iovar_fn_t iovar_fn,
+ watchdog_fn_t watchdog_fn, down_fn_t down_fn);
+extern int wlc_module_unregister(wlc_pub_t *pub, const char *name, void *hdl);
+extern void wlc_event_if(struct wlc_info *wlc, struct wlc_bsscfg *cfg,
+ wlc_event_t *e, const struct ether_addr *addr);
+extern void wlc_suspend_mac_and_wait(struct wlc_info *wlc);
+extern void wlc_enable_mac(struct wlc_info *wlc);
+extern u16 wlc_rate_shm_offset(struct wlc_info *wlc, u8 rate);
+extern u32 wlc_get_rspec_history(struct wlc_bsscfg *cfg);
+extern u32 wlc_get_current_highest_rate(struct wlc_bsscfg *cfg);
+
+static inline int wlc_iovar_getuint(struct wlc_info *wlc, const char *name,
+ uint *arg)
+{
+ return wlc_iovar_getint(wlc, name, (int *)arg);
+}
+
+static inline int wlc_iovar_getu8(struct wlc_info *wlc, const char *name,
+ u8 *arg)
+{
+ return wlc_iovar_gets8(wlc, name, (s8 *) arg);
+}
+
+static inline int wlc_iovar_setuint(struct wlc_info *wlc, const char *name,
+ uint arg)
+{
+ return wlc_iovar_setint(wlc, name, (int)arg);
+}
+
+#if defined(BCMDBG)
+extern int wlc_iocregchk(struct wlc_info *wlc, uint band);
+#endif
+#if defined(BCMDBG)
+extern int wlc_iocpichk(struct wlc_info *wlc, uint phytype);
+#endif
+
+/* helper functions */
+extern void wlc_getrand(struct wlc_info *wlc, u8 *buf, int len);
+
+struct scb;
+extern void wlc_ps_on(struct wlc_info *wlc, struct scb *scb);
+extern void wlc_ps_off(struct wlc_info *wlc, struct scb *scb, bool discard);
+extern bool wlc_radio_monitor_stop(struct wlc_info *wlc);
+
+#if defined(BCMDBG)
+extern int wlc_format_ssid(char *buf, const unsigned char ssid[], uint ssid_len);
+#endif
+
+extern void wlc_pmkid_build_cand_list(struct wlc_bsscfg *cfg, bool check_SSID);
+extern void wlc_pmkid_event(struct wlc_bsscfg *cfg);
+
+#define MAXBANDS 2 /* Maximum #of bands */
+/* bandstate array indices */
+#define BAND_2G_INDEX 0 /* wlc->bandstate[x] index */
+#define BAND_5G_INDEX 1 /* wlc->bandstate[x] index */
+
+#define BAND_2G_NAME "2.4G"
+#define BAND_5G_NAME "5G"
+
+#if defined(BCMSDIO) || defined(WLC_HIGH_ONLY)
+void wlc_device_removed(void *arg);
+#endif
+
+/* BMAC RPC: 7 u32 params: pkttotlen, fifo, commit, fid, txpktpend, pktflag, rpc_id */
+#define WLC_RPCTX_PARAMS 32
+
+#endif /* _wlc_pub_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_rate.c b/drivers/staging/brcm80211/sys/wlc_rate.c
new file mode 100644
index 000000000000..d2d72568756d
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_rate.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <bcmdefs.h>
+#include <wlc_cfg.h>
+#include <osl.h>
+#include <linuxver.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+
+#include <proto/802.11.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wl_dbg.h>
+#include <wlc_pub.h>
+
+/* Rate info per rate: It tells whether a rate is ofdm or not and its phy_rate value */
+const u8 rate_info[WLC_MAXRATE + 1] = {
+ /* 0 1 2 3 4 5 6 7 8 9 */
+/* 0 */ 0x00, 0x00, 0x0a, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 10 */ 0x00, 0x37, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x00,
+/* 20 */ 0x00, 0x00, 0x6e, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00,
+/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x00,
+/* 50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 60 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 70 */ 0x00, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00,
+/* 100 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c
+};
+
+/* rates are in units of Kbps */
+const mcs_info_t mcs_table[MCS_TABLE_SIZE] = {
+ /* MCS 0: SS 1, MOD: BPSK, CR 1/2 */
+ {6500, 13500, CEIL(6500 * 10, 9), CEIL(13500 * 10, 9), 0x00,
+ WLC_RATE_6M},
+ /* MCS 1: SS 1, MOD: QPSK, CR 1/2 */
+ {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x08,
+ WLC_RATE_12M},
+ /* MCS 2: SS 1, MOD: QPSK, CR 3/4 */
+ {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x0A,
+ WLC_RATE_18M},
+ /* MCS 3: SS 1, MOD: 16QAM, CR 1/2 */
+ {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x10,
+ WLC_RATE_24M},
+ /* MCS 4: SS 1, MOD: 16QAM, CR 3/4 */
+ {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x12,
+ WLC_RATE_36M},
+ /* MCS 5: SS 1, MOD: 64QAM, CR 2/3 */
+ {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x19,
+ WLC_RATE_48M},
+ /* MCS 6: SS 1, MOD: 64QAM, CR 3/4 */
+ {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x1A,
+ WLC_RATE_54M},
+ /* MCS 7: SS 1, MOD: 64QAM, CR 5/6 */
+ {65000, 135000, CEIL(65000 * 10, 9), CEIL(135000 * 10, 9), 0x1C,
+ WLC_RATE_54M},
+ /* MCS 8: SS 2, MOD: BPSK, CR 1/2 */
+ {13000, 27000, CEIL(13000 * 10, 9), CEIL(27000 * 10, 9), 0x40,
+ WLC_RATE_6M},
+ /* MCS 9: SS 2, MOD: QPSK, CR 1/2 */
+ {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0x48,
+ WLC_RATE_12M},
+ /* MCS 10: SS 2, MOD: QPSK, CR 3/4 */
+ {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x4A,
+ WLC_RATE_18M},
+ /* MCS 11: SS 2, MOD: 16QAM, CR 1/2 */
+ {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0x50,
+ WLC_RATE_24M},
+ /* MCS 12: SS 2, MOD: 16QAM, CR 3/4 */
+ {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x52,
+ WLC_RATE_36M},
+ /* MCS 13: SS 2, MOD: 64QAM, CR 2/3 */
+ {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0x59,
+ WLC_RATE_48M},
+ /* MCS 14: SS 2, MOD: 64QAM, CR 3/4 */
+ {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x5A,
+ WLC_RATE_54M},
+ /* MCS 15: SS 2, MOD: 64QAM, CR 5/6 */
+ {130000, 270000, CEIL(130000 * 10, 9), CEIL(270000 * 10, 9), 0x5C,
+ WLC_RATE_54M},
+ /* MCS 16: SS 3, MOD: BPSK, CR 1/2 */
+ {19500, 40500, CEIL(19500 * 10, 9), CEIL(40500 * 10, 9), 0x80,
+ WLC_RATE_6M},
+ /* MCS 17: SS 3, MOD: QPSK, CR 1/2 */
+ {39000, 81000, CEIL(39000 * 10, 9), CEIL(81000 * 10, 9), 0x88,
+ WLC_RATE_12M},
+ /* MCS 18: SS 3, MOD: QPSK, CR 3/4 */
+ {58500, 121500, CEIL(58500 * 10, 9), CEIL(121500 * 10, 9), 0x8A,
+ WLC_RATE_18M},
+ /* MCS 19: SS 3, MOD: 16QAM, CR 1/2 */
+ {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0x90,
+ WLC_RATE_24M},
+ /* MCS 20: SS 3, MOD: 16QAM, CR 3/4 */
+ {117000, 243000, CEIL(117000 * 10, 9), CEIL(243000 * 10, 9), 0x92,
+ WLC_RATE_36M},
+ /* MCS 21: SS 3, MOD: 64QAM, CR 2/3 */
+ {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0x99,
+ WLC_RATE_48M},
+ /* MCS 22: SS 3, MOD: 64QAM, CR 3/4 */
+ {175500, 364500, CEIL(175500 * 10, 9), CEIL(364500 * 10, 9), 0x9A,
+ WLC_RATE_54M},
+ /* MCS 23: SS 3, MOD: 64QAM, CR 5/6 */
+ {195000, 405000, CEIL(195000 * 10, 9), CEIL(405000 * 10, 9), 0x9B,
+ WLC_RATE_54M},
+ /* MCS 24: SS 4, MOD: BPSK, CR 1/2 */
+ {26000, 54000, CEIL(26000 * 10, 9), CEIL(54000 * 10, 9), 0xC0,
+ WLC_RATE_6M},
+ /* MCS 25: SS 4, MOD: QPSK, CR 1/2 */
+ {52000, 108000, CEIL(52000 * 10, 9), CEIL(108000 * 10, 9), 0xC8,
+ WLC_RATE_12M},
+ /* MCS 26: SS 4, MOD: QPSK, CR 3/4 */
+ {78000, 162000, CEIL(78000 * 10, 9), CEIL(162000 * 10, 9), 0xCA,
+ WLC_RATE_18M},
+ /* MCS 27: SS 4, MOD: 16QAM, CR 1/2 */
+ {104000, 216000, CEIL(104000 * 10, 9), CEIL(216000 * 10, 9), 0xD0,
+ WLC_RATE_24M},
+ /* MCS 28: SS 4, MOD: 16QAM, CR 3/4 */
+ {156000, 324000, CEIL(156000 * 10, 9), CEIL(324000 * 10, 9), 0xD2,
+ WLC_RATE_36M},
+ /* MCS 29: SS 4, MOD: 64QAM, CR 2/3 */
+ {208000, 432000, CEIL(208000 * 10, 9), CEIL(432000 * 10, 9), 0xD9,
+ WLC_RATE_48M},
+ /* MCS 30: SS 4, MOD: 64QAM, CR 3/4 */
+ {234000, 486000, CEIL(234000 * 10, 9), CEIL(486000 * 10, 9), 0xDA,
+ WLC_RATE_54M},
+ /* MCS 31: SS 4, MOD: 64QAM, CR 5/6 */
+ {260000, 540000, CEIL(260000 * 10, 9), CEIL(540000 * 10, 9), 0xDB,
+ WLC_RATE_54M},
+ /* MCS 32: SS 1, MOD: BPSK, CR 1/2 */
+ {0, 6000, 0, CEIL(6000 * 10, 9), 0x00, WLC_RATE_6M},
+};
+
+/* phycfg for legacy OFDM frames: code rate, modulation scheme, spatial streams
+ * Number of spatial streams: always 1
+ * other fields: refer to table 78 of section 17.3.2.2 of the original .11a standard
+ */
+typedef struct legacy_phycfg {
+ u32 rate_ofdm; /* ofdm mac rate */
+ u8 tx_phy_ctl3; /* phy ctl byte 3, code rate, modulation type, # of streams */
+} legacy_phycfg_t;
+
+#define LEGACY_PHYCFG_TABLE_SIZE 12 /* Number of legacy_rate_cfg entries in the table */
+
+/* In CCK mode LPPHY overloads OFDM Modulation bits with CCK Data Rate */
+/* Eventually MIMOPHY would also be converted to this format */
+/* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */
+static const legacy_phycfg_t legacy_phycfg_table[LEGACY_PHYCFG_TABLE_SIZE] = {
+ {WLC_RATE_1M, 0x00}, /* CCK 1Mbps, data rate 0 */
+ {WLC_RATE_2M, 0x08}, /* CCK 2Mbps, data rate 1 */
+ {WLC_RATE_5M5, 0x10}, /* CCK 5.5Mbps, data rate 2 */
+ {WLC_RATE_11M, 0x18}, /* CCK 11Mbps, data rate 3 */
+ {WLC_RATE_6M, 0x00}, /* OFDM 6Mbps, code rate 1/2, BPSK, 1 spatial stream */
+ {WLC_RATE_9M, 0x02}, /* OFDM 9Mbps, code rate 3/4, BPSK, 1 spatial stream */
+ {WLC_RATE_12M, 0x08}, /* OFDM 12Mbps, code rate 1/2, QPSK, 1 spatial stream */
+ {WLC_RATE_18M, 0x0A}, /* OFDM 18Mbps, code rate 3/4, QPSK, 1 spatial stream */
+ {WLC_RATE_24M, 0x10}, /* OFDM 24Mbps, code rate 1/2, 16-QAM, 1 spatial stream */
+ {WLC_RATE_36M, 0x12}, /* OFDM 36Mbps, code rate 3/4, 16-QAM, 1 spatial stream */
+ {WLC_RATE_48M, 0x19}, /* OFDM 48Mbps, code rate 2/3, 64-QAM, 1 spatial stream */
+ {WLC_RATE_54M, 0x1A}, /* OFDM 54Mbps, code rate 3/4, 64-QAM, 1 spatial stream */
+};
+
+/* Hardware rates (also encodes default basic rates) */
+
+const wlc_rateset_t cck_ofdm_mimo_rates = {
+ 12,
+ { /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, 54 Mbps */
+ 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
+ 0x6c},
+ 0x00,
+ {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+const wlc_rateset_t ofdm_mimo_rates = {
+ 8,
+ { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
+ 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
+ 0x00,
+ {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Default ratesets that include MCS32 for 40BW channels */
+const wlc_rateset_t cck_ofdm_40bw_mimo_rates = {
+ 12,
+ { /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, 54 Mbps */
+ 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
+ 0x6c},
+ 0x00,
+ {0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+const wlc_rateset_t ofdm_40bw_mimo_rates = {
+ 8,
+ { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
+ 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
+ 0x00,
+ {0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+const wlc_rateset_t cck_ofdm_rates = {
+ 12,
+ { /* 1b, 2b, 5.5b, 6, 9, 11b, 12, 18, 24, 36, 48, 54 Mbps */
+ 0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0x60,
+ 0x6c},
+ 0x00,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+const wlc_rateset_t gphy_legacy_rates = {
+ 4,
+ { /* 1b, 2b, 5.5b, 11b Mbps */
+ 0x82, 0x84, 0x8b, 0x96},
+ 0x00,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+const wlc_rateset_t ofdm_rates = {
+ 8,
+ { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
+ 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c},
+ 0x00,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+const wlc_rateset_t cck_rates = {
+ 4,
+ { /* 1b, 2b, 5.5, 11 Mbps */
+ 0x82, 0x84, 0x0b, 0x16},
+ 0x00,
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00}
+};
+
+static bool wlc_rateset_valid(wlc_rateset_t *rs, bool check_brate);
+
+/* check if rateset is valid.
+ * if check_brate is true, rateset without a basic rate is considered NOT valid.
+ */
+static bool wlc_rateset_valid(wlc_rateset_t *rs, bool check_brate)
+{
+ uint idx;
+
+ if (!rs->count)
+ return false;
+
+ if (!check_brate)
+ return true;
+
+ /* error if no basic rates */
+ for (idx = 0; idx < rs->count; idx++) {
+ if (rs->rates[idx] & WLC_RATE_FLAG)
+ return true;
+ }
+ return false;
+}
+
+void wlc_rateset_mcs_upd(wlc_rateset_t *rs, u8 txstreams)
+{
+ int i;
+ for (i = txstreams; i < MAX_STREAMS_SUPPORTED; i++)
+ rs->mcs[i] = 0;
+}
+
+/* filter based on hardware rateset, and sort filtered rateset with basic bit(s) preserved,
+ * and check if resulting rateset is valid.
+*/
+bool
+wlc_rate_hwrs_filter_sort_validate(wlc_rateset_t *rs,
+ const wlc_rateset_t *hw_rs,
+ bool check_brate, u8 txstreams)
+{
+ u8 rateset[WLC_MAXRATE + 1];
+ u8 r;
+ uint count;
+ uint i;
+
+ bzero(rateset, sizeof(rateset));
+ count = rs->count;
+
+ for (i = 0; i < count; i++) {
+ /* mask off "basic rate" bit, WLC_RATE_FLAG */
+ r = (int)rs->rates[i] & RATE_MASK;
+ if ((r > WLC_MAXRATE) || (rate_info[r] == 0)) {
+ continue;
+ }
+ rateset[r] = rs->rates[i]; /* preserve basic bit! */
+ }
+
+ /* fill out the rates in order, looking at only supported rates */
+ count = 0;
+ for (i = 0; i < hw_rs->count; i++) {
+ r = hw_rs->rates[i] & RATE_MASK;
+ ASSERT(r <= WLC_MAXRATE);
+ if (rateset[r])
+ rs->rates[count++] = rateset[r];
+ }
+
+ rs->count = count;
+
+ /* only set the mcs rate bit if the equivalent hw mcs bit is set */
+ for (i = 0; i < MCSSET_LEN; i++)
+ rs->mcs[i] = (rs->mcs[i] & hw_rs->mcs[i]);
+
+ if (wlc_rateset_valid(rs, check_brate))
+ return true;
+ else
+ return false;
+}
+
+/* caluclate the rate of a rx'd frame and return it as a ratespec */
+ratespec_t BCMFASTPATH wlc_compute_rspec(d11rxhdr_t *rxh, u8 *plcp)
+{
+ int phy_type;
+ ratespec_t rspec = PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT;
+
+ phy_type =
+ ((rxh->RxChan & RXS_CHAN_PHYTYPE_MASK) >> RXS_CHAN_PHYTYPE_SHIFT);
+
+ if ((phy_type == PHY_TYPE_N) || (phy_type == PHY_TYPE_SSN) ||
+ (phy_type == PHY_TYPE_LCN) || (phy_type == PHY_TYPE_HT)) {
+ switch (rxh->PhyRxStatus_0 & PRXS0_FT_MASK) {
+ case PRXS0_CCK:
+ rspec =
+ CCK_PHY2MAC_RATE(((cck_phy_hdr_t *) plcp)->signal);
+ break;
+ case PRXS0_OFDM:
+ rspec =
+ OFDM_PHY2MAC_RATE(((ofdm_phy_hdr_t *) plcp)->
+ rlpt[0]);
+ break;
+ case PRXS0_PREN:
+ rspec = (plcp[0] & MIMO_PLCP_MCS_MASK) | RSPEC_MIMORATE;
+ if (plcp[0] & MIMO_PLCP_40MHZ) {
+ /* indicate rspec is for 40 MHz mode */
+ rspec &= ~RSPEC_BW_MASK;
+ rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
+ }
+ break;
+ case PRXS0_STDN:
+ /* fallthru */
+ default:
+ /* not supported */
+ ASSERT(0);
+ break;
+ }
+ if (PLCP3_ISSGI(plcp[3]))
+ rspec |= RSPEC_SHORT_GI;
+ } else
+ if ((phy_type == PHY_TYPE_A) || (rxh->PhyRxStatus_0 & PRXS0_OFDM))
+ rspec = OFDM_PHY2MAC_RATE(((ofdm_phy_hdr_t *) plcp)->rlpt[0]);
+ else
+ rspec = CCK_PHY2MAC_RATE(((cck_phy_hdr_t *) plcp)->signal);
+
+ return rspec;
+}
+
+/* copy rateset src to dst as-is (no masking or sorting) */
+void wlc_rateset_copy(const wlc_rateset_t *src, wlc_rateset_t *dst)
+{
+ bcopy(src, dst, sizeof(wlc_rateset_t));
+}
+
+/*
+ * Copy and selectively filter one rateset to another.
+ * 'basic_only' means only copy basic rates.
+ * 'rates' indicates cck (11b) and ofdm rates combinations.
+ * - 0: cck and ofdm
+ * - 1: cck only
+ * - 2: ofdm only
+ * 'xmask' is the copy mask (typically 0x7f or 0xff).
+ */
+void
+wlc_rateset_filter(wlc_rateset_t *src, wlc_rateset_t *dst, bool basic_only,
+ u8 rates, uint xmask, bool mcsallow)
+{
+ uint i;
+ uint r;
+ uint count;
+
+ count = 0;
+ for (i = 0; i < src->count; i++) {
+ r = src->rates[i];
+ if (basic_only && !(r & WLC_RATE_FLAG))
+ continue;
+ if ((rates == WLC_RATES_CCK) && IS_OFDM((r & RATE_MASK)))
+ continue;
+ if ((rates == WLC_RATES_OFDM) && IS_CCK((r & RATE_MASK)))
+ continue;
+ dst->rates[count++] = r & xmask;
+ }
+ dst->count = count;
+ dst->htphy_membership = src->htphy_membership;
+
+ if (mcsallow && rates != WLC_RATES_CCK)
+ bcopy(&src->mcs[0], &dst->mcs[0], MCSSET_LEN);
+ else
+ wlc_rateset_mcs_clear(dst);
+}
+
+/* select rateset for a given phy_type and bandtype and filter it, sort it
+ * and fill rs_tgt with result
+ */
+void
+wlc_rateset_default(wlc_rateset_t *rs_tgt, const wlc_rateset_t *rs_hw,
+ uint phy_type, int bandtype, bool cck_only, uint rate_mask,
+ bool mcsallow, u8 bw, u8 txstreams)
+{
+ const wlc_rateset_t *rs_dflt;
+ wlc_rateset_t rs_sel;
+ if ((PHYTYPE_IS(phy_type, PHY_TYPE_HT)) ||
+ (PHYTYPE_IS(phy_type, PHY_TYPE_N)) ||
+ (PHYTYPE_IS(phy_type, PHY_TYPE_LCN)) ||
+ (PHYTYPE_IS(phy_type, PHY_TYPE_SSN))) {
+ if (BAND_5G(bandtype)) {
+ rs_dflt = (bw == WLC_20_MHZ ?
+ &ofdm_mimo_rates : &ofdm_40bw_mimo_rates);
+ } else {
+ rs_dflt = (bw == WLC_20_MHZ ?
+ &cck_ofdm_mimo_rates :
+ &cck_ofdm_40bw_mimo_rates);
+ }
+ } else if (PHYTYPE_IS(phy_type, PHY_TYPE_LP)) {
+ rs_dflt = (BAND_5G(bandtype)) ? &ofdm_rates : &cck_ofdm_rates;
+ } else if (PHYTYPE_IS(phy_type, PHY_TYPE_A)) {
+ rs_dflt = &ofdm_rates;
+ } else if (PHYTYPE_IS(phy_type, PHY_TYPE_G)) {
+ rs_dflt = &cck_ofdm_rates;
+ } else {
+ ASSERT(0); /* should not happen */
+ rs_dflt = &cck_rates; /* force cck */
+ }
+
+ /* if hw rateset is not supplied, assign selected rateset to it */
+ if (!rs_hw)
+ rs_hw = rs_dflt;
+
+ wlc_rateset_copy(rs_dflt, &rs_sel);
+ wlc_rateset_mcs_upd(&rs_sel, txstreams);
+ wlc_rateset_filter(&rs_sel, rs_tgt, false,
+ cck_only ? WLC_RATES_CCK : WLC_RATES_CCK_OFDM,
+ rate_mask, mcsallow);
+ wlc_rate_hwrs_filter_sort_validate(rs_tgt, rs_hw, false,
+ mcsallow ? txstreams : 1);
+}
+
+s16 BCMFASTPATH wlc_rate_legacy_phyctl(uint rate)
+{
+ uint i;
+ for (i = 0; i < LEGACY_PHYCFG_TABLE_SIZE; i++)
+ if (rate == legacy_phycfg_table[i].rate_ofdm)
+ return legacy_phycfg_table[i].tx_phy_ctl3;
+
+ return -1;
+}
+
+void wlc_rateset_mcs_clear(wlc_rateset_t *rateset)
+{
+ uint i;
+ for (i = 0; i < MCSSET_LEN; i++)
+ rateset->mcs[i] = 0;
+}
+
+void wlc_rateset_mcs_build(wlc_rateset_t *rateset, u8 txstreams)
+{
+ bcopy(&cck_ofdm_mimo_rates.mcs[0], &rateset->mcs[0], MCSSET_LEN);
+ wlc_rateset_mcs_upd(rateset, txstreams);
+}
+
+/* Based on bandwidth passed, allow/disallow MCS 32 in the rateset */
+void wlc_rateset_bw_mcs_filter(wlc_rateset_t *rateset, u8 bw)
+{
+ if (bw == WLC_40_MHZ)
+ setbit(rateset->mcs, 32);
+ else
+ clrbit(rateset->mcs, 32);
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_rate.h b/drivers/staging/brcm80211/sys/wlc_rate.h
new file mode 100644
index 000000000000..25ba2a423639
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_rate.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WLC_RATE_H_
+#define _WLC_RATE_H_
+
+extern const u8 rate_info[];
+extern const struct wlc_rateset cck_ofdm_mimo_rates;
+extern const struct wlc_rateset ofdm_mimo_rates;
+extern const struct wlc_rateset cck_ofdm_rates;
+extern const struct wlc_rateset ofdm_rates;
+extern const struct wlc_rateset cck_rates;
+extern const struct wlc_rateset gphy_legacy_rates;
+extern const struct wlc_rateset wlc_lrs_rates;
+extern const struct wlc_rateset rate_limit_1_2;
+
+typedef struct mcs_info {
+ u32 phy_rate_20; /* phy rate in kbps [20Mhz] */
+ u32 phy_rate_40; /* phy rate in kbps [40Mhz] */
+ u32 phy_rate_20_sgi; /* phy rate in kbps [20Mhz] with SGI */
+ u32 phy_rate_40_sgi; /* phy rate in kbps [40Mhz] with SGI */
+ u8 tx_phy_ctl3; /* phy ctl byte 3, code rate, modulation type, # of streams */
+ u8 leg_ofdm; /* matching legacy ofdm rate in 500bkps */
+} mcs_info_t;
+
+#define WLC_MAXMCS 32 /* max valid mcs index */
+#define MCS_TABLE_SIZE 33 /* Number of mcs entries in the table */
+extern const mcs_info_t mcs_table[];
+
+#define MCS_INVALID 0xFF
+#define MCS_CR_MASK 0x07 /* Code Rate bit mask */
+#define MCS_MOD_MASK 0x38 /* Modulation bit shift */
+#define MCS_MOD_SHIFT 3 /* MOdulation bit shift */
+#define MCS_TXS_MASK 0xc0 /* num tx streams - 1 bit mask */
+#define MCS_TXS_SHIFT 6 /* num tx streams - 1 bit shift */
+#define MCS_CR(_mcs) (mcs_table[_mcs].tx_phy_ctl3 & MCS_CR_MASK)
+#define MCS_MOD(_mcs) ((mcs_table[_mcs].tx_phy_ctl3 & MCS_MOD_MASK) >> MCS_MOD_SHIFT)
+#define MCS_TXS(_mcs) ((mcs_table[_mcs].tx_phy_ctl3 & MCS_TXS_MASK) >> MCS_TXS_SHIFT)
+#define MCS_RATE(_mcs, _is40, _sgi) (_sgi ? \
+ (_is40 ? mcs_table[_mcs].phy_rate_40_sgi : mcs_table[_mcs].phy_rate_20_sgi) : \
+ (_is40 ? mcs_table[_mcs].phy_rate_40 : mcs_table[_mcs].phy_rate_20))
+#define VALID_MCS(_mcs) ((_mcs < MCS_TABLE_SIZE))
+
+#define WLC_RATE_FLAG 0x80 /* Rate flag: basic or ofdm */
+
+/* Macros to use the rate_info table */
+#define RATE_MASK 0x7f /* Rate value mask w/o basic rate flag */
+#define RATE_MASK_FULL 0xff /* Rate value mask with basic rate flag */
+
+#define WLC_RATE_500K_TO_BPS(rate) ((rate) * 500000) /* convert 500kbps to bps */
+
+/* rate spec : holds rate and mode specific information required to generate a tx frame. */
+/* Legacy CCK and OFDM information is held in the same manner as was done in the past */
+/* (in the lower byte) the upper 3 bytes primarily hold MIMO specific information */
+typedef u32 ratespec_t;
+
+/* rate spec bit fields */
+#define RSPEC_RATE_MASK 0x0000007F /* Either 500Kbps units or MIMO MCS idx */
+#define RSPEC_MIMORATE 0x08000000 /* mimo MCS is stored in RSPEC_RATE_MASK */
+#define RSPEC_BW_MASK 0x00000700 /* mimo bw mask */
+#define RSPEC_BW_SHIFT 8 /* mimo bw shift */
+#define RSPEC_STF_MASK 0x00003800 /* mimo Space/Time/Frequency mode mask */
+#define RSPEC_STF_SHIFT 11 /* mimo Space/Time/Frequency mode shift */
+#define RSPEC_CT_MASK 0x0000C000 /* mimo coding type mask */
+#define RSPEC_CT_SHIFT 14 /* mimo coding type shift */
+#define RSPEC_STC_MASK 0x00300000 /* mimo num STC streams per PLCP defn. */
+#define RSPEC_STC_SHIFT 20 /* mimo num STC streams per PLCP defn. */
+#define RSPEC_LDPC_CODING 0x00400000 /* mimo bit indicates adv coding in use */
+#define RSPEC_SHORT_GI 0x00800000 /* mimo bit indicates short GI in use */
+#define RSPEC_OVERRIDE 0x80000000 /* bit indicates override both rate & mode */
+#define RSPEC_OVERRIDE_MCS_ONLY 0x40000000 /* bit indicates override rate only */
+
+#define WLC_HTPHY 127 /* HT PHY Membership */
+
+#define RSPEC_ACTIVE(rspec) (rspec & (RSPEC_RATE_MASK | RSPEC_MIMORATE))
+#define RSPEC2RATE(rspec) ((rspec & RSPEC_MIMORATE) ? \
+ MCS_RATE((rspec & RSPEC_RATE_MASK), RSPEC_IS40MHZ(rspec), RSPEC_ISSGI(rspec)) : \
+ (rspec & RSPEC_RATE_MASK))
+/* return rate in unit of 500Kbps -- for internal use in wlc_rate_sel.c */
+#define RSPEC2RATE500K(rspec) ((rspec & RSPEC_MIMORATE) ? \
+ MCS_RATE((rspec & RSPEC_RATE_MASK), state->is40bw, RSPEC_ISSGI(rspec))/500 : \
+ (rspec & RSPEC_RATE_MASK))
+#define CRSPEC2RATE500K(rspec) ((rspec & RSPEC_MIMORATE) ? \
+ MCS_RATE((rspec & RSPEC_RATE_MASK), RSPEC_IS40MHZ(rspec), RSPEC_ISSGI(rspec))/500 :\
+ (rspec & RSPEC_RATE_MASK))
+
+#define RSPEC2KBPS(rspec) (IS_MCS(rspec) ? RSPEC2RATE(rspec) : RSPEC2RATE(rspec)*500)
+#define RSPEC_PHYTXBYTE2(rspec) ((rspec & 0xff00) >> 8)
+#define RSPEC_GET_BW(rspec) ((rspec & RSPEC_BW_MASK) >> RSPEC_BW_SHIFT)
+#define RSPEC_IS40MHZ(rspec) ((((rspec & RSPEC_BW_MASK) >> RSPEC_BW_SHIFT) == \
+ PHY_TXC1_BW_40MHZ) || (((rspec & RSPEC_BW_MASK) >> \
+ RSPEC_BW_SHIFT) == PHY_TXC1_BW_40MHZ_DUP))
+#define RSPEC_ISSGI(rspec) ((rspec & RSPEC_SHORT_GI) == RSPEC_SHORT_GI)
+#define RSPEC_MIMOPLCP3(rspec) ((rspec & 0xf00000) >> 16)
+#define PLCP3_ISSGI(plcp) (plcp & (RSPEC_SHORT_GI >> 16))
+#define RSPEC_STC(rspec) ((rspec & RSPEC_STC_MASK) >> RSPEC_STC_SHIFT)
+#define RSPEC_STF(rspec) ((rspec & RSPEC_STF_MASK) >> RSPEC_STF_SHIFT)
+#define PLCP3_ISSTBC(plcp) ((plcp & (RSPEC_STC_MASK) >> 16) == 0x10)
+#define PLCP3_STC_MASK 0x30
+#define PLCP3_STC_SHIFT 4
+
+/* Rate info table; takes a legacy rate or ratespec_t */
+#define IS_MCS(r) (r & RSPEC_MIMORATE)
+#define IS_OFDM(r) (!IS_MCS(r) && (rate_info[(r) & RSPEC_RATE_MASK] & WLC_RATE_FLAG))
+#define IS_CCK(r) (!IS_MCS(r) && (((r) & RATE_MASK) == WLC_RATE_1M || \
+ ((r) & RATE_MASK) == WLC_RATE_2M || \
+ ((r) & RATE_MASK) == WLC_RATE_5M5 || ((r) & RATE_MASK) == WLC_RATE_11M))
+#define IS_SINGLE_STREAM(mcs) (((mcs) <= HIGHEST_SINGLE_STREAM_MCS) || ((mcs) == 32))
+#define CCK_RSPEC(cck) ((cck) & RSPEC_RATE_MASK)
+#define OFDM_RSPEC(ofdm) (((ofdm) & RSPEC_RATE_MASK) |\
+ (PHY_TXC1_MODE_CDD << RSPEC_STF_SHIFT))
+#define LEGACY_RSPEC(rate) (IS_CCK(rate) ? CCK_RSPEC(rate) : OFDM_RSPEC(rate))
+
+#define MCS_RSPEC(mcs) (((mcs) & RSPEC_RATE_MASK) | RSPEC_MIMORATE | \
+ (IS_SINGLE_STREAM(mcs) ? (PHY_TXC1_MODE_CDD << RSPEC_STF_SHIFT) : \
+ (PHY_TXC1_MODE_SDM << RSPEC_STF_SHIFT)))
+
+/* Convert encoded rate value in plcp header to numerical rates in 500 KHz increments */
+extern const u8 ofdm_rate_lookup[];
+#define OFDM_PHY2MAC_RATE(rlpt) (ofdm_rate_lookup[rlpt & 0x7])
+#define CCK_PHY2MAC_RATE(signal) (signal/5)
+
+/* Rates specified in wlc_rateset_filter() */
+#define WLC_RATES_CCK_OFDM 0
+#define WLC_RATES_CCK 1
+#define WLC_RATES_OFDM 2
+
+/* use the stuct form instead of typedef to fix dependency problems */
+struct wlc_rateset;
+
+/* sanitize, and sort a rateset with the basic bit(s) preserved, validate rateset */
+extern bool wlc_rate_hwrs_filter_sort_validate(struct wlc_rateset *rs,
+ const struct wlc_rateset *hw_rs,
+ bool check_brate,
+ u8 txstreams);
+/* copy rateset src to dst as-is (no masking or sorting) */
+extern void wlc_rateset_copy(const struct wlc_rateset *src,
+ struct wlc_rateset *dst);
+
+/* would be nice to have these documented ... */
+extern ratespec_t wlc_compute_rspec(d11rxhdr_t *rxh, u8 *plcp);
+
+extern void wlc_rateset_filter(struct wlc_rateset *src, struct wlc_rateset *dst,
+ bool basic_only, u8 rates, uint xmask,
+ bool mcsallow);
+extern void wlc_rateset_default(struct wlc_rateset *rs_tgt,
+ const struct wlc_rateset *rs_hw, uint phy_type,
+ int bandtype, bool cck_only, uint rate_mask,
+ bool mcsallow, u8 bw, u8 txstreams);
+extern s16 wlc_rate_legacy_phyctl(uint rate);
+
+extern void wlc_rateset_mcs_upd(struct wlc_rateset *rs, u8 txstreams);
+extern void wlc_rateset_mcs_clear(struct wlc_rateset *rateset);
+extern void wlc_rateset_mcs_build(struct wlc_rateset *rateset, u8 txstreams);
+extern void wlc_rateset_bw_mcs_filter(struct wlc_rateset *rateset, u8 bw);
+
+#endif /* _WLC_RATE_H_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_rpc.h b/drivers/staging/brcm80211/sys/wlc_rpc.h
new file mode 100644
index 000000000000..db39645ccbdc
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_rpc.h
@@ -0,0 +1,527 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _WLC_RPC_H_
+#define _WLC_RPC_H_
+
+#include <wlc_types.h>
+
+/* RPC IDs, reordering is OK. This needs to be in sync with RPC_ID_TABLE below */
+typedef enum {
+ WLRPC_NULL_ID = 0,
+ WLRPC_WLC_REG_READ_ID,
+ WLRPC_WLC_REG_WRITE_ID,
+ WLRPC_WLC_MHF_SET_ID,
+ WLRPC_WLC_MHF_GET_ID,
+ WLRPC_WLC_BMAC_UP_PREP_ID,
+ WLRPC_WLC_BMAC_UP_FINISH_ID,
+ WLRPC_WLC_BMAC_DOWN_PREP_ID,
+ WLRPC_WLC_BMAC_DOWN_FINISH_ID,
+ WLRPC_WLC_BMAC_WRITE_HW_BCNTEMPLATES_ID,
+ WLRPC_WLC_BMAC_RESET_ID,
+ WLRPC_WLC_DNGL_REBOOT_ID,
+ WLRPC_WLC_BMAC_RPC_TXQ_WM_SET_ID,
+ WLRPC_WLC_BMAC_RPC_TXQ_WM_GET_ID,
+ WLRPC_WLC_BMAC_RPC_AGG_SET_ID,
+ WLRPC_WLC_BMAC_RPC_MSGLEVEL_SET_ID,
+ WLRPC_WLC_BMAC_RPC_AGG_LIMIT_SET_ID,
+ WLRPC_WLC_BMAC_RPC_AGG_LIMIT_GET_ID,
+ WLRPC_WLC_BMAC_INIT_ID,
+ WLRPC_WLC_BMAC_SET_CWMIN_ID,
+ WLRPC_WLC_BMAC_MUTE_ID,
+ WLRPC_WLC_PHY_DOIOVAR_ID,
+ WLRPC_WLC_PHY_HOLD_UPD_ID,
+ WLRPC_WLC_PHY_MUTE_UPD_ID,
+ WLRPC_WLC_PHY_CLEAR_TSSI_ID,
+ WLRPC_WLC_PHY_ANT_RXDIV_GET_ID,
+ WLRPC_WLC_PHY_ANT_RXDIV_SET_ID,
+ WLRPC_WLC_PHY_PREAMBLE_SET_ID,
+ WLRPC_WLC_PHY_FREQTRACK_END_ID,
+ WLRPC_WLC_PHY_FREQTRACK_START_ID,
+ WLRPC_WLC_PHY_IOCTL_ID,
+ WLRPC_WLC_PHY_NOISE_SAMPLE_REQUEST_ID,
+ WLRPC_WLC_PHY_CAL_PERICAL_ID,
+ WLRPC_WLC_PHY_TXPOWER_GET_ID,
+ WLRPC_WLC_PHY_TXPOWER_SET_ID,
+ WLRPC_WLC_PHY_TXPOWER_SROMLIMIT_ID,
+ WLRPC_WLC_PHY_RADAR_DETECT_ENABLE_ID,
+ WLRPC_WLC_PHY_RADAR_DETECT_RUN_ID,
+ WLRPC_WLC_PHY_TEST_ISON_ID,
+ WLRPC_WLC_BMAC_COPYFROM_OBJMEM_ID,
+ WLRPC_WLC_BMAC_COPYTO_OBJMEM_ID,
+ WLRPC_WLC_ENABLE_MAC_ID,
+ WLRPC_WLC_MCTRL_ID,
+ WLRPC_WLC_CORERESET_ID,
+ WLRPC_WLC_BMAC_READ_SHM_ID,
+ WLRPC_WLC_BMAC_READ_TSF_ID,
+ WLRPC_WLC_BMAC_SET_ADDRMATCH_ID,
+ WLRPC_WLC_BMAC_SET_CWMAX_ID,
+ WLRPC_WLC_BMAC_SET_RCMTA_ID,
+ WLRPC_WLC_BMAC_SET_SHM_ID,
+ WLRPC_WLC_SUSPEND_MAC_AND_WAIT_ID,
+ WLRPC_WLC_BMAC_WRITE_SHM_ID,
+ WLRPC_WLC_BMAC_WRITE_TEMPLATE_RAM_ID,
+ WLRPC_WLC_TX_FIFO_SUSPEND_ID,
+ WLRPC_WLC_TX_FIFO_RESUME_ID,
+ WLRPC_WLC_TX_FIFO_SUSPENDED_ID,
+ WLRPC_WLC_HW_ETHERADDR_ID,
+ WLRPC_WLC_SET_HW_ETHERADDR_ID,
+ WLRPC_WLC_BMAC_CHANSPEC_SET_ID,
+ WLRPC_WLC_BMAC_TXANT_SET_ID,
+ WLRPC_WLC_BMAC_ANTSEL_TYPE_SET_ID,
+ WLRPC_WLC_BMAC_TXFIFO_ID,
+ WLRPC_WLC_RADIO_READ_HWDISABLED_ID,
+ WLRPC_WLC_RM_CCA_MEASURE_ID,
+ WLRPC_WLC_SET_SHORTSLOT_ID,
+ WLRPC_WLC_WAIT_FOR_WAKE_ID,
+ WLRPC_WLC_PHY_TXPOWER_GET_CURRENT_ID,
+ WLRPC_WLC_PHY_TXPOWER_HW_CTRL_GET_ID,
+ WLRPC_WLC_PHY_TXPOWER_HW_CTRL_SET_ID,
+ WLRPC_WLC_PHY_BSSINIT_ID,
+ WLRPC_WLC_BAND_STF_SS_SET_ID,
+ WLRPC_WLC_PHY_BAND_FIRST_CHANSPEC_ID,
+ WLRPC_WLC_PHY_TXPOWER_LIMIT_SET_ID,
+ WLRPC_WLC_PHY_BAND_CHANNELS_ID,
+ WLRPC_WLC_BMAC_REVINFO_GET_ID,
+ WLRPC_WLC_BMAC_STATE_GET_ID,
+ WLRPC_WLC_BMAC_XMTFIFO_SZ_GET_ID,
+ WLRPC_WLC_BMAC_XMTFIFO_SZ_SET_ID,
+ WLRPC_WLC_BMAC_VALIDATE_CHIP_ACCESS_ID,
+ WLRPC_WLC_RM_CCA_COMPLETE_ID,
+ WLRPC_WLC_RECV_ID,
+ WLRPC_WLC_DOTXSTATUS_ID,
+ WLRPC_WLC_HIGH_DPC_ID,
+ WLRPC_WLC_FATAL_ERROR_ID,
+ WLRPC_WLC_PHY_SET_CHANNEL_14_WIDE_FILTER_ID,
+ WLRPC_WLC_PHY_NOISE_AVG_ID,
+ WLRPC_WLC_PHYCHAIN_INIT_ID,
+ WLRPC_WLC_PHYCHAIN_SET_ID,
+ WLRPC_WLC_PHYCHAIN_GET_ID,
+ WLRPC_WLC_PHY_TKIP_RIFS_WAR_ID,
+ WLRPC_WLC_BMAC_COPYFROM_VARS_ID,
+ WLRPC_WLC_BMAC_RETRYLIMIT_UPD_ID,
+ WLRPC_WLC_BMAC_BTC_MODE_SET_ID,
+ WLRPC_WLC_BMAC_BTC_MODE_GET_ID,
+ WLRPC_WLC_BMAC_BTC_WIRE_SET_ID,
+ WLRPC_WLC_BMAC_BTC_WIRE_GET_ID,
+ WLRPC_WLC_BMAC_SET_NORESET_ID,
+ WLRPC_WLC_AMPDU_TXSTATUS_COMPLETE_ID,
+ WLRPC_WLC_BMAC_FIFOERRORS_ID,
+ WLRPC_WLC_PHY_TXPOWER_GET_TARGET_MIN_ID,
+ WLRPC_WLC_PHY_TXPOWER_GET_TARGET_MAX_ID,
+ WLRPC_WLC_NOISE_CB_ID,
+ WLRPC_WLC_BMAC_LED_HW_DEINIT_ID,
+ WLRPC_WLC_BMAC_LED_HW_MASK_INIT_ID,
+ WLRPC_WLC_PLLREQ_ID,
+ WLRPC_WLC_BMAC_TACLEAR_ID,
+ WLRPC_WLC_BMAC_SET_CLK_ID,
+ WLRPC_WLC_PHY_OFDM_RATESET_WAR_ID,
+ WLRPC_WLC_PHY_BF_PREEMPT_ENABLE_ID,
+ WLRPC_WLC_BMAC_DOIOVARS_ID,
+ WLRPC_WLC_BMAC_DUMP_ID,
+ WLRPC_WLC_CISWRITE_ID,
+ WLRPC_WLC_CISDUMP_ID,
+ WLRPC_WLC_UPDATE_PHY_MODE_ID,
+ WLRPC_WLC_RESET_BMAC_DONE_ID,
+ WLRPC_WLC_BMAC_LED_BLINK_EVENT_ID,
+ WLRPC_WLC_BMAC_LED_SET_ID,
+ WLRPC_WLC_BMAC_LED_BLINK_ID,
+ WLRPC_WLC_BMAC_LED_ID,
+ WLRPC_WLC_BMAC_RATE_SHM_OFFSET_ID,
+ WLRPC_SI_ISCORE_UP_ID,
+ WLRPC_WLC_BMAC_PS_SWITCH_ID,
+ WLRPC_WLC_PHY_STF_SSMODE_GET_ID,
+ WLRPC_WLC_BMAC_DEBUG_ID,
+ WLRPC_WLC_EXTLOG_MSG_ID,
+ WLRPC_WLC_EXTLOG_CFG_ID,
+ WLRPC_BCM_ASSERT_LOG_ID,
+ WLRPC_BCM_ASSERT_TYPE_ID,
+ WLRPC_WLC_BMAC_SET_PHYCAL_CACHE_FLAG_ID,
+ WLRPC_WLC_BMAC_GET_PHYCAL_CACHE_FLAG_ID,
+ WLRPC_WLC_PHY_CAL_CACHE_INIT_ID,
+ WLRPC_WLC_PHY_CAL_CACHE_DEINIT_ID,
+ WLRPC_WLC_BMAC_HW_UP_ID,
+ WLRPC_WLC_BMAC_SET_TXPWR_PERCENT_ID,
+ WLRPC_WLC_PHYCHAIN_ACTIVE_GET_ID,
+ WLRPC_WLC_BMAC_BLINK_SYNC_ID,
+ WLRPC_WLC_BMAC_UCODE_DBGSEL_SET_ID,
+ WLRPC_WLC_BMAC_UCODE_DBGSEL_GET_ID,
+ WLRPC_WLC_PHY_RADAR_DETECT_MODE_SET_ID,
+ WLRPC_WLC_PHY_ACIM_NOISEM_RESET_NPHY_ID,
+ WLRPC_WLC_PHY_INTERFER_SET_NPHY_ID,
+ WLRPC_WLC_BMAC_IFSCTL_EDCRS_SET_ID,
+ WLRPC_WLC_PKTENGTX,
+ WLRPC_WLC_BMAC_SET_DEAF,
+ WLRPC_WLC_BMAC_CLEAR_DEAF,
+ WLRPC_WLC_BMAC_BTC_FLAGS_SET_ID,
+ WLRPC_WLC_BMAC_BTC_FLAGS_GET_ID,
+ WLRPC_WLC_BMAC_SET_RCMTA_TYPE_ID,
+ WLRPC_WLC_BMAC_BTC_FLAGS_UPD_ID,
+ WLRPC_WLC_BMAC_BTC_STUCKWAR_ID,
+ WLRPC_WLC_BMAC_CCA_STATS_READ_ID,
+ WLRPC_WLC_BMAC_ANTSEL_SET_ID,
+ WLRPC_WLC_BMAC_SET_UCODE_LOADED,
+ WLRPC_WLC_PHY_LDPC_SET_ID,
+
+ WLRPC_LAST
+} wlc_rpc_id_t;
+
+#if defined(BCMDBG) | 0
+struct name_entry {
+ int id;
+ char *name;
+};
+
+#define NAME_ENTRY(x) {x, #x}
+
+#define RPC_ID_TABLE { \
+ NAME_ENTRY(WLRPC_WLC_REG_READ_ID), \
+ NAME_ENTRY(WLRPC_WLC_REG_WRITE_ID), \
+ NAME_ENTRY(WLRPC_WLC_MHF_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_MHF_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_UP_PREP_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_UP_FINISH_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_DOWN_PREP_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_DOWN_FINISH_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_WRITE_HW_BCNTEMPLATES_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RESET_ID), \
+ NAME_ENTRY(WLRPC_WLC_DNGL_REBOOT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RPC_TXQ_WM_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RPC_TXQ_WM_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RPC_AGG_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RPC_MSGLEVEL_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RPC_AGG_LIMIT_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RPC_AGG_LIMIT_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_INIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_CWMIN_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_MUTE_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_DOIOVAR_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_HOLD_UPD_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_MUTE_UPD_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_CLEAR_TSSI_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_ANT_RXDIV_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_ANT_RXDIV_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_PREAMBLE_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_FREQTRACK_END_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_FREQTRACK_START_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_IOCTL_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_NOISE_SAMPLE_REQUEST_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_CAL_PERICAL_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_SROMLIMIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_RADAR_DETECT_ENABLE_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_RADAR_DETECT_RUN_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TEST_ISON_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_COPYFROM_OBJMEM_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_COPYTO_OBJMEM_ID), \
+ NAME_ENTRY(WLRPC_WLC_ENABLE_MAC_ID), \
+ NAME_ENTRY(WLRPC_WLC_MCTRL_ID), \
+ NAME_ENTRY(WLRPC_WLC_CORERESET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_READ_SHM_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_READ_TSF_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_ADDRMATCH_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_CWMAX_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_RCMTA_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_SHM_ID), \
+ NAME_ENTRY(WLRPC_WLC_SUSPEND_MAC_AND_WAIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_WRITE_SHM_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_WRITE_TEMPLATE_RAM_ID), \
+ NAME_ENTRY(WLRPC_WLC_TX_FIFO_SUSPEND_ID), \
+ NAME_ENTRY(WLRPC_WLC_TX_FIFO_RESUME_ID), \
+ NAME_ENTRY(WLRPC_WLC_TX_FIFO_SUSPENDED_ID), \
+ NAME_ENTRY(WLRPC_WLC_HW_ETHERADDR_ID), \
+ NAME_ENTRY(WLRPC_WLC_SET_HW_ETHERADDR_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_CHANSPEC_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_TXANT_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_ANTSEL_TYPE_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_TXFIFO_ID), \
+ NAME_ENTRY(WLRPC_WLC_RADIO_READ_HWDISABLED_ID), \
+ NAME_ENTRY(WLRPC_WLC_RM_CCA_MEASURE_ID), \
+ NAME_ENTRY(WLRPC_WLC_SET_SHORTSLOT_ID), \
+ NAME_ENTRY(WLRPC_WLC_WAIT_FOR_WAKE_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_GET_CURRENT_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_HW_CTRL_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_HW_CTRL_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_BSSINIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BAND_STF_SS_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_BAND_FIRST_CHANSPEC_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_LIMIT_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_BAND_CHANNELS_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_REVINFO_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_STATE_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_XMTFIFO_SZ_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_XMTFIFO_SZ_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_VALIDATE_CHIP_ACCESS_ID), \
+ NAME_ENTRY(WLRPC_WLC_RM_CCA_COMPLETE_ID), \
+ NAME_ENTRY(WLRPC_WLC_RECV_ID), \
+ NAME_ENTRY(WLRPC_WLC_DOTXSTATUS_ID), \
+ NAME_ENTRY(WLRPC_WLC_HIGH_DPC_ID), \
+ NAME_ENTRY(WLRPC_WLC_FATAL_ERROR_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_SET_CHANNEL_14_WIDE_FILTER_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_NOISE_AVG_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHYCHAIN_INIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHYCHAIN_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHYCHAIN_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TKIP_RIFS_WAR_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_COPYFROM_VARS_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RETRYLIMIT_UPD_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BTC_MODE_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BTC_MODE_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BTC_WIRE_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BTC_WIRE_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_NORESET_ID), \
+ NAME_ENTRY(WLRPC_WLC_AMPDU_TXSTATUS_COMPLETE_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_FIFOERRORS_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_GET_TARGET_MIN_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_TXPOWER_GET_TARGET_MAX_ID), \
+ NAME_ENTRY(WLRPC_WLC_NOISE_CB_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_LED_HW_DEINIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_LED_HW_MASK_INIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_PLLREQ_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_TACLEAR_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_CLK_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_OFDM_RATESET_WAR_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_BF_PREEMPT_ENABLE_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_DOIOVARS_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_DUMP_ID), \
+ NAME_ENTRY(WLRPC_WLC_CISWRITE_ID), \
+ NAME_ENTRY(WLRPC_WLC_CISDUMP_ID), \
+ NAME_ENTRY(WLRPC_WLC_UPDATE_PHY_MODE_ID), \
+ NAME_ENTRY(WLRPC_WLC_RESET_BMAC_DONE_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_LED_BLINK_EVENT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_LED_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_LED_BLINK_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_LED_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_RATE_SHM_OFFSET_ID), \
+ NAME_ENTRY(WLRPC_SI_ISCORE_UP_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_PS_SWITCH_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_STF_SSMODE_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_DEBUG_ID), \
+ NAME_ENTRY(WLRPC_WLC_EXTLOG_MSG_ID), \
+ NAME_ENTRY(WLRPC_WLC_EXTLOG_CFG_ID), \
+ NAME_ENTRY(WLRPC_BCM_ASSERT_LOG_ID), \
+ NAME_ENTRY(WLRPC_BCM_ASSERT_TYPE_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_PHYCAL_CACHE_FLAG_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_GET_PHYCAL_CACHE_FLAG_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_CAL_CACHE_INIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_CAL_CACHE_DEINIT_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_HW_UP_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_TXPWR_PERCENT_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHYCHAIN_ACTIVE_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BLINK_SYNC_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_UCODE_DBGSEL_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_UCODE_DBGSEL_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_RADAR_DETECT_MODE_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_ACIM_NOISEM_RESET_NPHY_ID), \
+ NAME_ENTRY(WLRPC_WLC_PHY_INTERFER_SET_NPHY_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_IFSCTL_EDCRS_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_PKTENGTX), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_DEAF), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_CLEAR_DEAF), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BTC_FLAGS_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_BTC_FLAGS_GET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_RCMTA_TYPE_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_CCA_STATS_READ_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_ANTSEL_SET_ID), \
+ NAME_ENTRY(WLRPC_WLC_BMAC_SET_UCODE_LOADED), \
+ NAME_ENTRY(WLRPC_WLC_PHY_LDPC_SET_ID), \
+ {0, NULL} \
+ }
+
+static __inline char *_wlc_rpc_id_lookup(const struct name_entry *tbl, int _id)
+{
+ const struct name_entry *elt = tbl;
+ static char __unknown[64];
+ for (; elt->name != NULL; elt++) {
+ if (_id == elt->id)
+ break;
+ }
+ if (_id == elt->id)
+ strncpy(__unknown, elt->name, sizeof(__unknown));
+ else
+ snprintf(__unknown, sizeof(__unknown), "ID:%d", _id);
+ return __unknown;
+}
+
+#define WLC_RPC_ID_LOOKUP(tbl, _id) (_wlc_rpc_id_lookup(tbl, _id))
+
+#endif /* BCMDBG */
+
+/* refer to txpwr_limits_t for each elements, mcs32 is the at the end for 1 byte */
+#define TXPOWER_XDR_SZ (roundup(WLC_NUM_RATES_CCK, 4) + roundup(WLC_NUM_RATES_OFDM, 4) * 4 + \
+ roundup(WLC_NUM_RATES_MCS_1_STREAM, 4) * 6 + roundup(WLC_NUM_RATES_MCS_2_STREAM, 4) * 2 + \
+ roundup(1, 4))
+
+#define wlc_rpc_txpwr_limits(b, txpwr, op, err) \
+ do { \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->cck, WLC_NUM_RATES_CCK); \
+ ASSERT(!(err)); \
+ \
+ /* 20 MHz Legacy OFDM rates with SISO transmission */ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->ofdm, WLC_NUM_RATES_OFDM); \
+ ASSERT(!(err)); \
+ \
+ /* 20 MHz Legacy OFDM rates with CDD transmission */ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->ofdm_cdd, WLC_NUM_RATES_OFDM); \
+ ASSERT(!(err)); \
+ \
+ /* 40 MHz Legacy OFDM rates with SISO transmission */ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->ofdm_40_siso, WLC_NUM_RATES_OFDM); \
+ ASSERT(!(err)); \
+ \
+ /* 40 MHz Legacy OFDM rates with CDD transmission */ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->ofdm_40_cdd, WLC_NUM_RATES_OFDM); \
+ ASSERT(!(err)); \
+ \
+ /* 20MHz MCS rates SISO/CDD/STBC/SDM */ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_20_siso, WLC_NUM_RATES_MCS_1_STREAM); \
+ ASSERT(!(err)); \
+ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_20_cdd, WLC_NUM_RATES_MCS_1_STREAM); \
+ ASSERT(!(err)); \
+ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_20_stbc, WLC_NUM_RATES_MCS_1_STREAM); \
+ ASSERT(!(err)); \
+ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_20_mimo, WLC_NUM_RATES_MCS_2_STREAM); \
+ ASSERT(!(err)); \
+ \
+ /* 40MHz MCS rates SISO/CDD/STBC/SDM */ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_40_siso, WLC_NUM_RATES_MCS_1_STREAM); \
+ ASSERT(!(err)); \
+ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_40_cdd, WLC_NUM_RATES_MCS_1_STREAM); \
+ ASSERT(!(err)); \
+ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_40_stbc, WLC_NUM_RATES_MCS_1_STREAM); \
+ ASSERT(!(err)); \
+ \
+ (err) = bcm_xdr_##op##_u8_vec((b), (txpwr)->mcs_40_mimo, WLC_NUM_RATES_MCS_2_STREAM); \
+ ASSERT(!(err)); \
+ } while (0)
+
+typedef struct wlc_rpc_ctx {
+ rpc_info_t *rpc;
+ wlc_info_t *wlc;
+ wlc_hw_info_t *wlc_hw;
+} wlc_rpc_ctx_t;
+
+static inline rpc_buf_t *wlc_rpc_buf_alloc(rpc_info_t *rpc, bcm_xdr_buf_t *b,
+ uint len, wlc_rpc_id_t rpc_id)
+{
+ rpc_buf_t *rpc_buf;
+
+ rpc_buf = bcm_rpc_buf_alloc(rpc, len + sizeof(u32));
+
+ if (!rpc_buf)
+ return NULL;
+
+ bcm_xdr_buf_init(b, bcm_rpc_buf_data(bcm_rpc_tp_get(rpc), rpc_buf),
+ len + sizeof(u32));
+
+ bcm_xdr_pack_u32(b, rpc_id);
+
+ return rpc_buf;
+}
+
+#if defined(BCMDBG)
+static __inline wlc_rpc_id_t
+wlc_rpc_id_get(struct rpc_info *rpc, rpc_buf_t *buf)
+{
+ wlc_rpc_id_t rpc_id;
+ bcm_xdr_buf_t b;
+
+ bcm_xdr_buf_init(&b, bcm_rpc_buf_data(bcm_rpc_tp_get(rpc), buf),
+ sizeof(u32));
+
+ bcm_xdr_unpack_u32(&b, (u32 *)((unsigned long) & rpc_id));
+ return rpc_id;
+}
+#endif
+
+static __inline int _wlc_rpc_call(struct rpc_info *rpc, rpc_buf_t *send)
+{
+ int _err = 0;
+#if defined(BCMDBG)
+ wlc_rpc_id_t rpc_id = wlc_rpc_id_get(rpc, send);
+ /* const struct name_entry rpc_name_tbl[] = RPC_ID_TABLE; */
+ static struct name_entry rpc_name_tbl[] = RPC_ID_TABLE;
+ WL_TRACE(("%s: Called id %s\n", __func__,
+ WLC_RPC_ID_LOOKUP(rpc_name_tbl, rpc_id)));
+#endif
+ _err = bcm_rpc_call(rpc, send);
+ if (_err) {
+#if defined(BCMDBG)
+ WL_ERROR(("%s: Call id %s FAILED\n", __func__,
+ WLC_RPC_ID_LOOKUP(rpc_name_tbl, rpc_id)));
+#endif
+ _err = 0;
+ }
+ return _err;
+}
+
+#define wlc_rpc_call(rpc, send) (_wlc_rpc_call(rpc, send))
+
+#include <sbhnddma.h>
+#include <sbhndpio.h>
+#include <d11.h>
+
+#ifdef WLC_LOW
+extern void wlc_rpc_bmac_dispatch(wlc_rpc_ctx_t *rpc_ctx, struct rpc_buf *buf);
+extern void wlc_rpc_bmac_dump_txfifohist(wlc_hw_info_t *wlc_hw,
+ bool dump_clear);
+#else
+extern void wlc_rpc_high_dispatch(wlc_rpc_ctx_t *ctx, struct rpc_buf *buf);
+#endif
+
+/* Packed structure for ease of transport across RPC bus along u32 boundary */
+typedef struct wlc_rpc_txstatus {
+ u32 PAD_framelen;
+ u32 status_frameid;
+ u32 sequence_lasttxtime;
+ u32 ackphyrxsh_phyerr;
+} wlc_rpc_txstatus_t;
+
+static inline
+ void txstatus2rpc_txstatus(tx_status_t *txstatus,
+ wlc_rpc_txstatus_t *rpc_txstatus)
+{
+ rpc_txstatus->PAD_framelen = txstatus->framelen;
+ rpc_txstatus->status_frameid =
+ (txstatus->status << 16) | txstatus->frameid;
+ rpc_txstatus->sequence_lasttxtime =
+ (txstatus->sequence << 16) | txstatus->lasttxtime;
+ rpc_txstatus->ackphyrxsh_phyerr =
+ (txstatus->ackphyrxsh << 16) | txstatus->phyerr;
+}
+
+static inline
+ void rpc_txstatus2txstatus(wlc_rpc_txstatus_t *rpc_txstatus,
+ tx_status_t *txstatus)
+{
+ txstatus->framelen = rpc_txstatus->PAD_framelen & 0xffff;
+ txstatus->status = (rpc_txstatus->status_frameid >> 16) & 0xffff;
+ txstatus->frameid = rpc_txstatus->status_frameid & 0xffff;
+ txstatus->sequence = (rpc_txstatus->sequence_lasttxtime >> 16) & 0xffff;
+ txstatus->lasttxtime = rpc_txstatus->sequence_lasttxtime & 0xffff;
+ txstatus->ackphyrxsh = (rpc_txstatus->ackphyrxsh_phyerr >> 16) & 0xffff;
+ txstatus->phyerr = rpc_txstatus->ackphyrxsh_phyerr & 0xffff;
+}
+
+extern void wlc_bmac_dngl_reboot(rpc_info_t *rpc);
+
+#endif /* WLC_RPC_H */
diff --git a/drivers/staging/brcm80211/sys/wlc_rpctx.h b/drivers/staging/brcm80211/sys/wlc_rpctx.h
new file mode 100644
index 000000000000..7427154a4bd4
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_rpctx.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_rpctx_h_
+#define _wlc_rpctx_h_
+
+/* forward declaration */
+struct wlc_info;
+
+/* This controls how many packets are given to the dongle. This is required as
+ * NTXD needs to be power of 2 but we may not have enough memory to absorb that
+ * large number of frames
+ */
+#ifndef NRPCTXBUFPOST
+#define NRPCTXBUFPOST NTXD
+#endif
+
+#if defined(WLC_HIGH_ONLY)
+
+struct wlc_rpc_phy {
+ struct rpc_info *rpc;
+};
+
+#define RPCTX_ENAB(pub) (true)
+extern rpctx_info_t *wlc_rpctx_attach(wlc_pub_t *pub, struct wlc_info *wlc);
+extern int wlc_rpctx_fifoinit(rpctx_info_t *rpctx, uint fifo, uint ntxd);
+extern void wlc_rpctx_detach(rpctx_info_t *rpctx);
+extern int wlc_rpctx_dump(rpctx_info_t *rpctx, struct bcmstrbuf *b);
+extern void *wlc_rpctx_getnexttxp(rpctx_info_t *rpctx, uint fifo);
+extern void wlc_rpctx_txreclaim(rpctx_info_t *rpctx);
+extern uint wlc_rpctx_txavail(rpctx_info_t *rpctx, uint fifo);
+extern int wlc_rpctx_pkteng(rpctx_info_t *rpctx, uint fifo, void *p);
+extern int wlc_rpctx_tx(rpctx_info_t *rpctx, uint fifo, void *p, bool commit,
+ u16 frameid, u8 txpktpend);
+extern void wlc_rpctx_txpktpendinc(rpctx_info_t *rpctx, uint fifo, u8 val);
+extern void wlc_rpctx_txpktpenddec(rpctx_info_t *rpctx, uint fifo, u8 val);
+extern void wlc_rpctx_txpktpendclr(rpctx_info_t *rpctx, uint fifo);
+extern int wlc_rpctx_txpktpend(rpctx_info_t *rpctx, uint fifo, bool all);
+
+#else
+#define RPCTX_ENAB(pub) (false)
+#define wlc_rpctx_attach(pub, wlc) (NULL)
+#define wlc_rpctx_fifoinit(rpctx, fifo, ntxd) (0)
+#define wlc_rpctx_detach(rpctx) ASSERT(0)
+#define wlc_rpctx_txavail(rpctx, f) (false)
+#define wlc_rpctx_dump(rpctx, b) (0)
+#define wlc_rpctx_getnexttxp(rpctx, f) (NULL)
+#define wlc_rpctx_txreclaim(rpctx) ASSERT(0)
+#define wlc_rpctx_pkteng(rpctx, fifo, p) do { } while (0)
+#define wlc_rpctx_tx(rpctx, f, p, c, fid, t) (0)
+#define wlc_rpctx_txpktpendinc(rpctx, f, val) do { } while (0)
+#define wlc_rpctx_txpktpenddec(rpctx, f, val) do { } while (0)
+#define wlc_rpctx_txpktpendclr(rpctx, f) do { } while (0)
+#define wlc_rpctx_txpktpend(rpctx, f, all) (0)
+
+#endif /* WLC_HIGH */
+
+#endif /* _wlc_rpctx_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_scb.h b/drivers/staging/brcm80211/sys/wlc_scb.h
new file mode 100644
index 000000000000..ce26c740e6c1
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_scb.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_scb_h_
+#define _wlc_scb_h_
+
+#include <proto/802.1d.h>
+
+extern bool wlc_aggregatable(wlc_info_t *wlc, u8 tid);
+
+#define AMPDU_TX_BA_MAX_WSIZE 64 /* max Tx ba window size (in pdu) */
+/* structure to store per-tid state for the ampdu initiator */
+typedef struct scb_ampdu_tid_ini {
+ u32 magic;
+ u8 tx_in_transit; /* number of pending mpdus in transit in driver */
+ u8 tid; /* initiator tid for easy lookup */
+ u8 txretry[AMPDU_TX_BA_MAX_WSIZE]; /* tx retry count; indexed by seq modulo */
+ struct scb *scb; /* backptr for easy lookup */
+} scb_ampdu_tid_ini_t;
+
+#define AMPDU_MAX_SCB_TID NUMPRIO
+
+typedef struct scb_ampdu {
+ struct scb *scb; /* back pointer for easy reference */
+ u8 mpdu_density; /* mpdu density */
+ u8 max_pdu; /* max pdus allowed in ampdu */
+ u8 release; /* # of mpdus released at a time */
+ u16 min_len; /* min mpdu len to support the density */
+ u32 max_rxlen; /* max ampdu rcv length; 8k, 16k, 32k, 64k */
+ struct pktq txq; /* sdu transmit queue pending aggregation */
+
+ /* This could easily be a ini[] pointer and we keep this info in wl itself instead
+ * of having mac80211 hold it for us. Also could be made dynamic per tid instead of
+ * static.
+ */
+ scb_ampdu_tid_ini_t ini[AMPDU_MAX_SCB_TID]; /* initiator info - per tid (NUMPRIO) */
+} scb_ampdu_t;
+
+#define SCB_MAGIC 0xbeefcafe
+#define INI_MAGIC 0xabcd1234
+
+/* station control block - one per remote MAC address */
+struct scb {
+ u32 magic;
+ u32 flags; /* various bit flags as defined below */
+ u32 flags2; /* various bit flags2 as defined below */
+ u8 state; /* current state bitfield of auth/assoc process */
+ struct ether_addr ea; /* station address */
+ void *fragbuf[NUMPRIO]; /* defragmentation buffer per prio */
+ uint fragresid[NUMPRIO]; /* #bytes unused in frag buffer per prio */
+
+ u16 seqctl[NUMPRIO]; /* seqctl of last received frame (for dups) */
+ u16 seqctl_nonqos; /* seqctl of last received frame (for dups) for
+ * non-QoS data and management
+ */
+ u16 seqnum[NUMPRIO]; /* WME: driver maintained sw seqnum per priority */
+
+ scb_ampdu_t scb_ampdu; /* AMPDU state including per tid info */
+};
+
+/* scb flags */
+#define SCB_WMECAP 0x0040 /* may ONLY be set if WME_ENAB(wlc) */
+#define SCB_HTCAP 0x10000 /* HT (MIMO) capable device */
+#define SCB_IS40 0x80000 /* 40MHz capable */
+#define SCB_STBCCAP 0x40000000 /* STBC Capable */
+#define SCB_WME(a) ((a)->flags & SCB_WMECAP)/* implies WME_ENAB */
+#define SCB_SEQNUM(scb, prio) ((scb)->seqnum[(prio)])
+#define SCB_PS(a) NULL
+#define SCB_STBC_CAP(a) ((a)->flags & SCB_STBCCAP)
+#define SCB_AMPDU(a) true
+#endif /* _wlc_scb_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_stf.c b/drivers/staging/brcm80211/sys/wlc_stf.c
new file mode 100644
index 000000000000..4728ad90e295
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_stf.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <wlc_cfg.h>
+#include <linuxver.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <proto/802.11.h>
+#include <wlioctl.h>
+#include <bcmwifi.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_channel.h>
+#include <wlc_bsscfg.h>
+#include <wlc_mac80211.h>
+#include <wlc_scb.h>
+#include <wl_export.h>
+#include <wlc_bmac.h>
+#include <wlc_stf.h>
+
+#define WLC_STF_SS_STBC_RX(wlc) (WLCISNPHY(wlc->band) && \
+ NREV_GT(wlc->band->phyrev, 3) && NREV_LE(wlc->band->phyrev, 6))
+
+static s8 wlc_stf_stbc_rx_get(wlc_info_t *wlc);
+static bool wlc_stf_stbc_tx_set(wlc_info_t *wlc, s32 int_val);
+static int wlc_stf_txcore_set(wlc_info_t *wlc, u8 Nsts, u8 val);
+static int wlc_stf_spatial_policy_set(wlc_info_t *wlc, int val);
+static void wlc_stf_stbc_rx_ht_update(wlc_info_t *wlc, int val);
+
+static void _wlc_stf_phy_txant_upd(wlc_info_t *wlc);
+static u16 _wlc_stf_phytxchain_sel(wlc_info_t *wlc, ratespec_t rspec);
+
+#define NSTS_1 1
+#define NSTS_2 2
+#define NSTS_3 3
+#define NSTS_4 4
+const u8 txcore_default[5] = {
+ (0), /* bitmap of the core enabled */
+ (0x01), /* For Nsts = 1, enable core 1 */
+ (0x03), /* For Nsts = 2, enable core 1 & 2 */
+ (0x07), /* For Nsts = 3, enable core 1, 2 & 3 */
+ (0x0f) /* For Nsts = 4, enable all cores */
+};
+
+static void wlc_stf_stbc_rx_ht_update(wlc_info_t *wlc, int val)
+{
+ ASSERT((val == HT_CAP_RX_STBC_NO)
+ || (val == HT_CAP_RX_STBC_ONE_STREAM));
+
+ /* MIMOPHYs rev3-6 cannot receive STBC with only one rx core active */
+ if (WLC_STF_SS_STBC_RX(wlc)) {
+ if ((wlc->stf->rxstreams == 1) && (val != HT_CAP_RX_STBC_NO))
+ return;
+ }
+
+ wlc->ht_cap.cap &= ~HT_CAP_RX_STBC_MASK;
+ wlc->ht_cap.cap |= (val << HT_CAP_RX_STBC_SHIFT);
+
+ if (wlc->pub->up) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ }
+}
+
+/* every WLC_TEMPSENSE_PERIOD seconds temperature check to decide whether to turn on/off txchain */
+void wlc_tempsense_upd(wlc_info_t *wlc)
+{
+ wlc_phy_t *pi = wlc->band->pi;
+ uint active_chains, txchain;
+
+ /* Check if the chip is too hot. Disable one Tx chain, if it is */
+ /* high 4 bits are for Rx chain, low 4 bits are for Tx chain */
+ active_chains = wlc_phy_stf_chain_active_get(pi);
+ txchain = active_chains & 0xf;
+
+ if (wlc->stf->txchain == wlc->stf->hw_txchain) {
+ if (txchain && (txchain < wlc->stf->hw_txchain)) {
+ /* turn off 1 tx chain */
+ wlc_stf_txchain_set(wlc, txchain, true);
+ }
+ } else if (wlc->stf->txchain < wlc->stf->hw_txchain) {
+ if (txchain == wlc->stf->hw_txchain) {
+ /* turn back on txchain */
+ wlc_stf_txchain_set(wlc, txchain, true);
+ }
+ }
+}
+
+void
+wlc_stf_ss_algo_channel_get(wlc_info_t *wlc, u16 *ss_algo_channel,
+ chanspec_t chanspec)
+{
+ tx_power_t power;
+ u8 siso_mcs_id, cdd_mcs_id, stbc_mcs_id;
+
+ /* Clear previous settings */
+ *ss_algo_channel = 0;
+
+ if (!wlc->pub->up) {
+ *ss_algo_channel = (u16) -1;
+ return;
+ }
+
+ wlc_phy_txpower_get_current(wlc->band->pi, &power,
+ CHSPEC_CHANNEL(chanspec));
+
+ siso_mcs_id = (CHSPEC_IS40(chanspec)) ?
+ WL_TX_POWER_MCS40_SISO_FIRST : WL_TX_POWER_MCS20_SISO_FIRST;
+ cdd_mcs_id = (CHSPEC_IS40(chanspec)) ?
+ WL_TX_POWER_MCS40_CDD_FIRST : WL_TX_POWER_MCS20_CDD_FIRST;
+ stbc_mcs_id = (CHSPEC_IS40(chanspec)) ?
+ WL_TX_POWER_MCS40_STBC_FIRST : WL_TX_POWER_MCS20_STBC_FIRST;
+
+ /* criteria to choose stf mode */
+
+ /* the "+3dbm (12 0.25db units)" is to account for the fact that with CDD, tx occurs
+ * on both chains
+ */
+ if (power.target[siso_mcs_id] > (power.target[cdd_mcs_id] + 12))
+ setbit(ss_algo_channel, PHY_TXC1_MODE_SISO);
+ else
+ setbit(ss_algo_channel, PHY_TXC1_MODE_CDD);
+
+ /* STBC is ORed into to algo channel as STBC requires per-packet SCB capability check
+ * so cannot be default mode of operation. One of SISO, CDD have to be set
+ */
+ if (power.target[siso_mcs_id] <= (power.target[stbc_mcs_id] + 12))
+ setbit(ss_algo_channel, PHY_TXC1_MODE_STBC);
+}
+
+static s8 wlc_stf_stbc_rx_get(wlc_info_t *wlc)
+{
+ return (wlc->ht_cap.cap & HT_CAP_RX_STBC_MASK) >> HT_CAP_RX_STBC_SHIFT;
+}
+
+static bool wlc_stf_stbc_tx_set(wlc_info_t *wlc, s32 int_val)
+{
+ if ((int_val != AUTO) && (int_val != OFF) && (int_val != ON)) {
+ return false;
+ }
+
+ if ((int_val == ON) && (wlc->stf->txstreams == 1))
+ return false;
+
+ if ((int_val == OFF) || (wlc->stf->txstreams == 1)
+ || !WLC_STBC_CAP_PHY(wlc))
+ wlc->ht_cap.cap &= ~HT_CAP_TX_STBC;
+ else
+ wlc->ht_cap.cap |= HT_CAP_TX_STBC;
+
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = (s8) int_val;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = (s8) int_val;
+
+ return true;
+}
+
+bool wlc_stf_stbc_rx_set(wlc_info_t *wlc, s32 int_val)
+{
+ if ((int_val != HT_CAP_RX_STBC_NO)
+ && (int_val != HT_CAP_RX_STBC_ONE_STREAM)) {
+ return false;
+ }
+
+ if (WLC_STF_SS_STBC_RX(wlc)) {
+ if ((int_val != HT_CAP_RX_STBC_NO)
+ && (wlc->stf->rxstreams == 1))
+ return false;
+ }
+
+ wlc_stf_stbc_rx_ht_update(wlc, int_val);
+ return true;
+}
+
+static int wlc_stf_txcore_set(wlc_info_t *wlc, u8 Nsts, u8 core_mask)
+{
+ WL_TRACE(("wl%d: %s: Nsts %d core_mask %x\n",
+ wlc->pub->unit, __func__, Nsts, core_mask));
+
+ ASSERT((Nsts > 0) && (Nsts <= MAX_STREAMS_SUPPORTED));
+
+ if (WLC_BITSCNT(core_mask) > wlc->stf->txstreams) {
+ core_mask = 0;
+ }
+
+ if ((WLC_BITSCNT(core_mask) == wlc->stf->txstreams) &&
+ ((core_mask & ~wlc->stf->txchain)
+ || !(core_mask & wlc->stf->txchain))) {
+ core_mask = wlc->stf->txchain;
+ }
+
+ ASSERT(!core_mask || Nsts <= WLC_BITSCNT(core_mask));
+
+ wlc->stf->txcore[Nsts] = core_mask;
+ /* Nsts = 1..4, txcore index = 1..4 */
+ if (Nsts == 1) {
+ /* Needs to update beacon and ucode generated response
+ * frames when 1 stream core map changed
+ */
+ wlc->stf->phytxant = core_mask << PHY_TXC_ANT_SHIFT;
+ wlc_bmac_txant_set(wlc->hw, wlc->stf->phytxant);
+ if (wlc->clk) {
+ wlc_suspend_mac_and_wait(wlc);
+ wlc_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec);
+ wlc_enable_mac(wlc);
+ }
+ }
+
+ return BCME_OK;
+}
+
+static int wlc_stf_spatial_policy_set(wlc_info_t *wlc, int val)
+{
+ int i;
+ u8 core_mask = 0;
+
+ WL_TRACE(("wl%d: %s: val %x\n", wlc->pub->unit, __func__, val));
+
+ wlc->stf->spatial_policy = (s8) val;
+ for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) {
+ core_mask = (val == MAX_SPATIAL_EXPANSION) ?
+ wlc->stf->txchain : txcore_default[i];
+ wlc_stf_txcore_set(wlc, (u8) i, core_mask);
+ }
+ return BCME_OK;
+}
+
+int wlc_stf_txchain_set(wlc_info_t *wlc, s32 int_val, bool force)
+{
+ u8 txchain = (u8) int_val;
+ u8 txstreams;
+ uint i;
+
+ if (wlc->stf->txchain == txchain)
+ return BCME_OK;
+
+ if ((txchain & ~wlc->stf->hw_txchain)
+ || !(txchain & wlc->stf->hw_txchain))
+ return BCME_RANGE;
+
+ /* if nrate override is configured to be non-SISO STF mode, reject reducing txchain to 1 */
+ txstreams = (u8) WLC_BITSCNT(txchain);
+ if (txstreams > MAX_STREAMS_SUPPORTED)
+ return BCME_RANGE;
+
+ if (txstreams == 1) {
+ for (i = 0; i < NBANDS(wlc); i++)
+ if ((RSPEC_STF(wlc->bandstate[i]->rspec_override) !=
+ PHY_TXC1_MODE_SISO)
+ || (RSPEC_STF(wlc->bandstate[i]->mrspec_override) !=
+ PHY_TXC1_MODE_SISO)) {
+ if (!force)
+ return BCME_ERROR;
+
+ /* over-write the override rspec */
+ if (RSPEC_STF(wlc->bandstate[i]->rspec_override)
+ != PHY_TXC1_MODE_SISO) {
+ wlc->bandstate[i]->rspec_override = 0;
+ WL_ERROR(("%s(): temp sense override non-SISO" " rspec_override.\n", __func__));
+ }
+ if (RSPEC_STF
+ (wlc->bandstate[i]->mrspec_override) !=
+ PHY_TXC1_MODE_SISO) {
+ wlc->bandstate[i]->mrspec_override = 0;
+ WL_ERROR(("%s(): temp sense override non-SISO" " mrspec_override.\n", __func__));
+ }
+ }
+ }
+
+ wlc->stf->txchain = txchain;
+ wlc->stf->txstreams = txstreams;
+ wlc_stf_stbc_tx_set(wlc, wlc->band->band_stf_stbc_tx);
+ wlc_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ wlc_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+ wlc->stf->txant =
+ (wlc->stf->txstreams == 1) ? ANT_TX_FORCE_0 : ANT_TX_DEF;
+ _wlc_stf_phy_txant_upd(wlc);
+
+ wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain,
+ wlc->stf->rxchain);
+
+ for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++)
+ wlc_stf_txcore_set(wlc, (u8) i, txcore_default[i]);
+
+ return BCME_OK;
+}
+
+int wlc_stf_rxchain_set(wlc_info_t *wlc, s32 int_val)
+{
+ u8 rxchain_cnt;
+ u8 rxchain = (u8) int_val;
+ u8 mimops_mode;
+ u8 old_rxchain, old_rxchain_cnt;
+
+ if (wlc->stf->rxchain == rxchain)
+ return BCME_OK;
+
+ if ((rxchain & ~wlc->stf->hw_rxchain)
+ || !(rxchain & wlc->stf->hw_rxchain))
+ return BCME_RANGE;
+
+ rxchain_cnt = (u8) WLC_BITSCNT(rxchain);
+ if (WLC_STF_SS_STBC_RX(wlc)) {
+ if ((rxchain_cnt == 1)
+ && (wlc_stf_stbc_rx_get(wlc) != HT_CAP_RX_STBC_NO))
+ return BCME_RANGE;
+ }
+
+ if (APSTA_ENAB(wlc->pub) && (wlc->pub->associated))
+ return BCME_ASSOCIATED;
+
+ old_rxchain = wlc->stf->rxchain;
+ old_rxchain_cnt = wlc->stf->rxstreams;
+
+ wlc->stf->rxchain = rxchain;
+ wlc->stf->rxstreams = rxchain_cnt;
+
+ if (rxchain_cnt != old_rxchain_cnt) {
+ mimops_mode =
+ (rxchain_cnt == 1) ? HT_CAP_MIMO_PS_ON : HT_CAP_MIMO_PS_OFF;
+ wlc->mimops_PM = mimops_mode;
+ if (AP_ENAB(wlc->pub)) {
+ wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain,
+ wlc->stf->rxchain);
+ wlc_ht_mimops_cap_update(wlc, mimops_mode);
+ if (wlc->pub->associated)
+ wlc_mimops_action_ht_send(wlc, wlc->cfg,
+ mimops_mode);
+ return BCME_OK;
+ }
+ if (wlc->pub->associated) {
+ if (mimops_mode == HT_CAP_MIMO_PS_OFF) {
+ /* if mimops is off, turn on the Rx chain first */
+ wlc_phy_stf_chain_set(wlc->band->pi,
+ wlc->stf->txchain,
+ wlc->stf->rxchain);
+ wlc_ht_mimops_cap_update(wlc, mimops_mode);
+ }
+ } else {
+ wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain,
+ wlc->stf->rxchain);
+ wlc_ht_mimops_cap_update(wlc, mimops_mode);
+ }
+ } else if (old_rxchain != rxchain)
+ wlc_phy_stf_chain_set(wlc->band->pi, wlc->stf->txchain,
+ wlc->stf->rxchain);
+
+ return BCME_OK;
+}
+
+/* update wlc->stf->ss_opmode which represents the operational stf_ss mode we're using */
+int wlc_stf_ss_update(wlc_info_t *wlc, wlcband_t *band)
+{
+ int ret_code = 0;
+ u8 prev_stf_ss;
+ u8 upd_stf_ss;
+
+ prev_stf_ss = wlc->stf->ss_opmode;
+
+ /* NOTE: opmode can only be SISO or CDD as STBC is decided on a per-packet basis */
+ if (WLC_STBC_CAP_PHY(wlc) &&
+ wlc->stf->ss_algosel_auto
+ && (wlc->stf->ss_algo_channel != (u16) -1)) {
+ ASSERT(isset(&wlc->stf->ss_algo_channel, PHY_TXC1_MODE_CDD)
+ || isset(&wlc->stf->ss_algo_channel,
+ PHY_TXC1_MODE_SISO));
+ upd_stf_ss = (wlc->stf->no_cddstbc || (wlc->stf->txstreams == 1)
+ || isset(&wlc->stf->ss_algo_channel,
+ PHY_TXC1_MODE_SISO)) ? PHY_TXC1_MODE_SISO
+ : PHY_TXC1_MODE_CDD;
+ } else {
+ if (wlc->band != band)
+ return ret_code;
+ upd_stf_ss = (wlc->stf->no_cddstbc
+ || (wlc->stf->txstreams ==
+ 1)) ? PHY_TXC1_MODE_SISO : band->
+ band_stf_ss_mode;
+ }
+ if (prev_stf_ss != upd_stf_ss) {
+ wlc->stf->ss_opmode = upd_stf_ss;
+ wlc_bmac_band_stf_ss_set(wlc->hw, upd_stf_ss);
+ }
+
+ return ret_code;
+}
+
+int wlc_stf_attach(wlc_info_t *wlc)
+{
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_SISO;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_ss_mode = PHY_TXC1_MODE_CDD;
+
+ if (WLCISNPHY(wlc->band) &&
+ (wlc_phy_txpower_hw_ctrl_get(wlc->band->pi) != PHY_TPC_HW_ON))
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_ss_mode =
+ PHY_TXC1_MODE_CDD;
+ wlc_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+ wlc_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+
+ wlc_stf_stbc_rx_ht_update(wlc, HT_CAP_RX_STBC_NO);
+ wlc->bandstate[BAND_2G_INDEX]->band_stf_stbc_tx = OFF;
+ wlc->bandstate[BAND_5G_INDEX]->band_stf_stbc_tx = OFF;
+
+ if (WLC_STBC_CAP_PHY(wlc)) {
+ wlc->stf->ss_algosel_auto = true;
+ wlc->stf->ss_algo_channel = (u16) -1; /* Init the default value */
+ }
+ return 0;
+}
+
+void wlc_stf_detach(wlc_info_t *wlc)
+{
+}
+
+int wlc_stf_ant_txant_validate(wlc_info_t *wlc, s8 val)
+{
+ int bcmerror = BCME_OK;
+
+ /* when there is only 1 tx_streams, don't allow to change the txant */
+ if (WLCISNPHY(wlc->band) && (wlc->stf->txstreams == 1))
+ return ((val == wlc->stf->txant) ? bcmerror : BCME_RANGE);
+
+ switch (val) {
+ case -1:
+ val = ANT_TX_DEF;
+ break;
+ case 0:
+ val = ANT_TX_FORCE_0;
+ break;
+ case 1:
+ val = ANT_TX_FORCE_1;
+ break;
+ case 3:
+ val = ANT_TX_LAST_RX;
+ break;
+ default:
+ bcmerror = BCME_RANGE;
+ break;
+ }
+
+ if (bcmerror == BCME_OK)
+ wlc->stf->txant = (s8) val;
+
+ return bcmerror;
+
+}
+
+/*
+ * Centralized txant update function. call it whenever wlc->stf->txant and/or wlc->stf->txchain
+ * change
+ *
+ * Antennas are controlled by ucode indirectly, which drives PHY or GPIO to
+ * achieve various tx/rx antenna selection schemes
+ *
+ * legacy phy, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 means auto(last rx)
+ * for NREV<3, bit 6 and bit 7 means antenna 0 and 1 respectively, bit6+bit7 means last rx and
+ * do tx-antenna selection for SISO transmissions
+ * for NREV=3, bit 6 and bit _8_ means antenna 0 and 1 respectively, bit6+bit7 means last rx and
+ * do tx-antenna selection for SISO transmissions
+ * for NREV>=7, bit 6 and bit 7 mean antenna 0 and 1 respectively, nit6+bit7 means both cores active
+*/
+static void _wlc_stf_phy_txant_upd(wlc_info_t *wlc)
+{
+ s8 txant;
+
+ txant = (s8) wlc->stf->txant;
+ ASSERT(txant == ANT_TX_FORCE_0 || txant == ANT_TX_FORCE_1
+ || txant == ANT_TX_LAST_RX);
+
+ if (WLC_PHY_11N_CAP(wlc->band)) {
+ if (txant == ANT_TX_FORCE_0) {
+ wlc->stf->phytxant = PHY_TXC_ANT_0;
+ } else if (txant == ANT_TX_FORCE_1) {
+ wlc->stf->phytxant = PHY_TXC_ANT_1;
+
+ if (WLCISNPHY(wlc->band) &&
+ NREV_GE(wlc->band->phyrev, 3)
+ && NREV_LT(wlc->band->phyrev, 7)) {
+ wlc->stf->phytxant = PHY_TXC_ANT_2;
+ }
+ } else {
+ if (WLCISLCNPHY(wlc->band) || WLCISSSLPNPHY(wlc->band))
+ wlc->stf->phytxant = PHY_TXC_LCNPHY_ANT_LAST;
+ else {
+ /* keep this assert to catch out of sync wlc->stf->txcore */
+ ASSERT(wlc->stf->txchain > 0);
+ wlc->stf->phytxant =
+ wlc->stf->txchain << PHY_TXC_ANT_SHIFT;
+ }
+ }
+ } else {
+ if (txant == ANT_TX_FORCE_0)
+ wlc->stf->phytxant = PHY_TXC_OLD_ANT_0;
+ else if (txant == ANT_TX_FORCE_1)
+ wlc->stf->phytxant = PHY_TXC_OLD_ANT_1;
+ else
+ wlc->stf->phytxant = PHY_TXC_OLD_ANT_LAST;
+ }
+
+ wlc_bmac_txant_set(wlc->hw, wlc->stf->phytxant);
+}
+
+void wlc_stf_phy_txant_upd(wlc_info_t *wlc)
+{
+ _wlc_stf_phy_txant_upd(wlc);
+}
+
+void wlc_stf_phy_chain_calc(wlc_info_t *wlc)
+{
+ /* get available rx/tx chains */
+ wlc->stf->hw_txchain = (u8) getintvar(wlc->pub->vars, "txchain");
+ wlc->stf->hw_rxchain = (u8) getintvar(wlc->pub->vars, "rxchain");
+
+ /* these parameter are intended to be used for all PHY types */
+ if (wlc->stf->hw_txchain == 0 || wlc->stf->hw_txchain == 0xf) {
+ if (WLCISNPHY(wlc->band)) {
+ wlc->stf->hw_txchain = TXCHAIN_DEF_NPHY;
+ } else {
+ wlc->stf->hw_txchain = TXCHAIN_DEF;
+ }
+ }
+
+ wlc->stf->txchain = wlc->stf->hw_txchain;
+ wlc->stf->txstreams = (u8) WLC_BITSCNT(wlc->stf->hw_txchain);
+
+ if (wlc->stf->hw_rxchain == 0 || wlc->stf->hw_rxchain == 0xf) {
+ if (WLCISNPHY(wlc->band)) {
+ wlc->stf->hw_rxchain = RXCHAIN_DEF_NPHY;
+ } else {
+ wlc->stf->hw_rxchain = RXCHAIN_DEF;
+ }
+ }
+
+ wlc->stf->rxchain = wlc->stf->hw_rxchain;
+ wlc->stf->rxstreams = (u8) WLC_BITSCNT(wlc->stf->hw_rxchain);
+
+ /* initialize the txcore table */
+ bcopy(txcore_default, wlc->stf->txcore, sizeof(wlc->stf->txcore));
+
+ /* default spatial_policy */
+ wlc->stf->spatial_policy = MIN_SPATIAL_EXPANSION;
+ wlc_stf_spatial_policy_set(wlc, MIN_SPATIAL_EXPANSION);
+}
+
+static u16 _wlc_stf_phytxchain_sel(wlc_info_t *wlc, ratespec_t rspec)
+{
+ u16 phytxant = wlc->stf->phytxant;
+
+ if (RSPEC_STF(rspec) != PHY_TXC1_MODE_SISO) {
+ ASSERT(wlc->stf->txstreams > 1);
+ phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT;
+ } else if (wlc->stf->txant == ANT_TX_DEF)
+ phytxant = wlc->stf->txchain << PHY_TXC_ANT_SHIFT;
+ phytxant &= PHY_TXC_ANT_MASK;
+ return phytxant;
+}
+
+u16 wlc_stf_phytxchain_sel(wlc_info_t *wlc, ratespec_t rspec)
+{
+ return _wlc_stf_phytxchain_sel(wlc, rspec);
+}
+
+u16 wlc_stf_d11hdrs_phyctl_txant(wlc_info_t *wlc, ratespec_t rspec)
+{
+ u16 phytxant = wlc->stf->phytxant;
+ u16 mask = PHY_TXC_ANT_MASK;
+
+ /* for non-siso rates or default setting, use the available chains */
+ if (WLCISNPHY(wlc->band)) {
+ ASSERT(wlc->stf->txchain != 0);
+ phytxant = _wlc_stf_phytxchain_sel(wlc, rspec);
+ mask = PHY_TXC_HTANT_MASK;
+ }
+ phytxant |= phytxant & mask;
+ return phytxant;
+}
diff --git a/drivers/staging/brcm80211/sys/wlc_stf.h b/drivers/staging/brcm80211/sys/wlc_stf.h
new file mode 100644
index 000000000000..ee9b02a119bb
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_stf.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_stf_h_
+#define _wlc_stf_h_
+
+#define MIN_SPATIAL_EXPANSION 0
+#define MAX_SPATIAL_EXPANSION 1
+
+extern int wlc_stf_attach(wlc_info_t *wlc);
+extern void wlc_stf_detach(wlc_info_t *wlc);
+
+extern void wlc_tempsense_upd(wlc_info_t *wlc);
+extern void wlc_stf_ss_algo_channel_get(wlc_info_t *wlc,
+ u16 *ss_algo_channel,
+ chanspec_t chanspec);
+extern int wlc_stf_ss_update(wlc_info_t *wlc, struct wlcband *band);
+extern void wlc_stf_phy_txant_upd(wlc_info_t *wlc);
+extern int wlc_stf_txchain_set(wlc_info_t *wlc, s32 int_val, bool force);
+extern int wlc_stf_rxchain_set(wlc_info_t *wlc, s32 int_val);
+extern bool wlc_stf_stbc_rx_set(wlc_info_t *wlc, s32 int_val);
+
+extern int wlc_stf_ant_txant_validate(wlc_info_t *wlc, s8 val);
+extern void wlc_stf_phy_txant_upd(wlc_info_t *wlc);
+extern void wlc_stf_phy_chain_calc(wlc_info_t *wlc);
+extern u16 wlc_stf_phytxchain_sel(wlc_info_t *wlc, ratespec_t rspec);
+extern u16 wlc_stf_d11hdrs_phyctl_txant(wlc_info_t *wlc, ratespec_t rspec);
+extern u16 wlc_stf_spatial_expansion_get(wlc_info_t *wlc, ratespec_t rspec);
+#endif /* _wlc_stf_h_ */
diff --git a/drivers/staging/brcm80211/sys/wlc_types.h b/drivers/staging/brcm80211/sys/wlc_types.h
new file mode 100644
index 000000000000..33047ebab979
--- /dev/null
+++ b/drivers/staging/brcm80211/sys/wlc_types.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wlc_types_h_
+#define _wlc_types_h_
+
+/* forward declarations */
+
+typedef struct wlc_info wlc_info_t;
+typedef struct wlc_hw_info wlc_hw_info_t;
+typedef struct wlc_if wlc_if_t;
+typedef struct wl_if wl_if_t;
+typedef struct led_info led_info_t;
+typedef struct bmac_led bmac_led_t;
+typedef struct bmac_led_info bmac_led_info_t;
+typedef struct scb_module scb_module_t;
+typedef struct ba_info ba_info_t;
+typedef struct ampdu_info ampdu_info_t;
+typedef struct ratesel_info ratesel_info_t;
+typedef struct wlc_ap_info wlc_ap_info_t;
+typedef struct wlc_auth_info wlc_auth_info_t;
+typedef struct supplicant supplicant_t;
+typedef struct authenticator authenticator_t;
+typedef struct antsel_info antsel_info_t;
+#if !defined(WLC_LOW)
+typedef struct rpctx_info rpctx_info_t;
+#endif
+#ifdef WLC_LOW
+typedef struct bmac_pmq bmac_pmq_t;
+#endif
+
+struct d11init;
+
+#ifndef _hnddma_pub_
+#define _hnddma_pub_
+typedef const struct hnddma_pub hnddma_t;
+#endif /* _hnddma_pub_ */
+
+#endif /* _wlc_types_h_ */