# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+# one and only common flag
+subdir-ccflags-y := -DBCMDBG
+
obj-$(CONFIG_BRCMFMAC) += brcmfmac/
obj-$(CONFIG_BRCMSMAC) += brcmsmac/
ccflags-y := \
-DARP_OFFLOAD_SUPPORT \
- -DBCMDBG \
-DBCMLXSDMMC \
-DBCMPLATFORM_BUS \
-DBCMSDIO \
obj-m += brcmfmac.o
brcmfmac-objs += $(DHDOFILES)
-
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ccflags-y := \
- -DBCMDBG \
-DWLC_HIGH \
-DWLC_LOW \
-DSTA \
-DDBAND \
-DBCMDMA32 \
-DBCMNVRAMR \
- -Idrivers/staging/brcm80211/brcmsmac \
- -Idrivers/staging/brcm80211/brcmsmac/sys \
+ -Idrivers/staging/brcm80211/brcmsmac \
-Idrivers/staging/brcm80211/brcmsmac/phy \
-Idrivers/staging/brcm80211/util \
-Idrivers/staging/brcm80211/include
BRCMSMAC_OFILES := \
- sys/wl_mac80211.o \
- sys/wl_ucode_loader.o \
- sys/wlc_alloc.o \
- sys/wlc_ampdu.o \
- sys/wlc_antsel.o \
- sys/wlc_bmac.o \
- sys/wlc_channel.o \
- sys/wlc_event.o \
- sys/wlc_mac80211.o \
- sys/wlc_phy_shim.o \
- sys/wlc_rate.o \
- sys/wlc_stf.o \
+ wl_mac80211.o \
+ wl_ucode_loader.o \
+ wlc_alloc.o \
+ wlc_ampdu.o \
+ wlc_antsel.o \
+ wlc_bmac.o \
+ wlc_channel.o \
+ wlc_event.o \
+ wlc_mac80211.o \
+ wlc_phy_shim.o \
+ wlc_rate.o \
+ wlc_stf.o \
phy/wlc_phy_cmn.o \
phy/wlc_phy_lcn.o \
phy/wlc_phy_n.o \
--- /dev/null
+/*
+ * 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
+++ /dev/null
-/*
- * 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
+++ /dev/null
-/*
- * 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_NONE(fmt, args...) no_printk(fmt, ##args)
-
-#define WL_PRINT(level, fmt, args...) \
-do { \
- if (wl_msg_level & level) \
- printk(fmt, ##args); \
-} while (0)
-
-#ifdef BCMDBG
-
-#define WL_ERROR(fmt, args...) WL_PRINT(WL_ERROR_VAL, fmt, ##args)
-#define WL_TRACE(fmt, args...) WL_PRINT(WL_TRACE_VAL, fmt, ##args)
-#define WL_AMPDU(fmt, args...) WL_PRINT(WL_AMPDU_VAL, fmt, ##args)
-#define WL_FFPLD(fmt, args...) WL_PRINT(WL_FFPLD_VAL, fmt, ##args)
-
-#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_PRINT(level, fmt, args...) \
-do { \
- if (wl_ampdu_dbg & level) { \
- WL_AMPDU(fmt, ##args); \
- } \
-} while (0)
-
-#define WL_AMPDU_UPDN(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_UPDN_VAL, fmt, ##args)
-#define WL_AMPDU_RX(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_RX_VAL, fmt, ##args)
-#define WL_AMPDU_ERR(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_ERR_VAL, fmt, ##args)
-#define WL_AMPDU_TX(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_TX_VAL, fmt, ##args)
-#define WL_AMPDU_CTL(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_CTL_VAL, fmt, ##args)
-#define WL_AMPDU_HW(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_HW_VAL, fmt, ##args)
-#define WL_AMPDU_HWTXS(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_HWTXS_VAL, fmt, ##args)
-#define WL_AMPDU_HWDBG(fmt, args...) \
- WL_AMPDU_PRINT(WL_AMPDU_HWDBG_VAL, fmt, ##args)
-#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(fmt, args...) no_printk(fmt, ##args)
-#define WL_TRACE(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU(fmt, args...) no_printk(fmt, ##args)
-#define WL_FFPLD(fmt, args...) no_printk(fmt, ##args)
-
-#define WL_ERROR_ON() 0
-
-#define WL_AMPDU_UPDN(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_RX(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_ERR(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_TX(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_CTL(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_HW(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_HWTXS(fmt, args...) no_printk(fmt, ##args)
-#define WL_AMPDU_HWDBG(fmt, args...) no_printk(fmt, ##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_ */
+++ /dev/null
-/*
- * 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(struct osl_info *osh, void *p, unsigned char *buf,
- int len, uint offset);
-extern void *wl_get_pktbuffer(struct osl_info *osh, int len);
-extern int wl_set_pktlen(struct osl_info *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_ */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <linux/sched.h>
-#include <osl.h>
-#define WLC_MAXBSSCFG 1 /* single BSS configs */
-
-#include <wlc_cfg.h>
-#include <net/mac80211.h>
-#include <phy_version.h>
-#include <bcmutils.h>
-#include <pcicfg.h>
-#include <wlioctl.h>
-#include <wlc_key.h>
-#include <sbhndpio.h>
-#include <sbhnddma.h>
-#include <wlc_channel.h>
-#include <wlc_pub.h>
-#include <wlc_scb.h>
-#include <wl_dbg.h>
-#include <wl_export.h>
-
-#include <wl_mac80211.h>
-#include <linux/firmware.h>
-#include <wl_ucode.h>
-#include <d11ucode_ext.h>
-
-
-static void wl_timer(unsigned long data);
-static void _wl_timer(wl_timer_t *t);
-
-
-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];
-};
-
-#define WL_DEV_IF(dev) ((struct wl_if *)netdev_priv(dev))
-#define WL_INFO(dev) ((struct wl_info *)(WL_DEV_IF(dev)->wl))
-static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev);
-static void wl_release_fw(struct wl_info *wl);
-
-/* local prototypes */
-static int wl_start(struct sk_buff *skb, struct wl_info *wl);
-static int wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw,
- struct sk_buff *skb);
-static void wl_dpc(unsigned long data);
-
-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");
-
-/* 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);
-
-
-#ifdef BCMDBG
-static int msglevel = 0xdeadbeef;
-module_param(msglevel, int, 0);
-static int phymsglevel = 0xdeadbeef;
-module_param(phymsglevel, int, 0);
-#endif /* BCMDBG */
-
-#define HW_TO_WL(hw) (hw->priv)
-#define WL_TO_HW(wl) (wl->pub->ieee_hw)
-static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
-static int wl_ops_start(struct ieee80211_hw *hw);
-static void wl_ops_stop(struct ieee80211_hw *hw);
-static int wl_ops_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);
-
-static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
-{
- int status;
- struct wl_info *wl = hw->priv;
- WL_LOCK(wl);
- if (!wl->pub->up) {
- WL_ERROR("ops->tx called while down\n");
- status = -ENETDOWN;
- goto done;
- }
- status = wl_start(skb, wl);
- done:
- WL_UNLOCK(wl);
- return status;
-}
-
-static int wl_ops_start(struct ieee80211_hw *hw)
-{
- struct wl_info *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)
-{
- struct wl_info *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)
-{
- struct wl_info *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)
-{
- struct wl_info *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;
- struct wl_info *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)
-{
- struct wl_info *wl = HW_TO_WL(hw);
- int val;
-
-
- if (changed & BSS_CHANGED_ASSOC) {
- WL_ERROR("Associated:\t%s\n", info->assoc ? "True" : "False");
- /* association status changed (associated/disassociated)
- * also implies a change in the AID.
- */
- }
- 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,
- 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) */
- }
- return;
-}
-
-static void
-wl_ops_configure_filter(struct ieee80211_hw *hw,
- unsigned int changed_flags,
- unsigned int *total_flags, u64 multicast)
-{
- struct wl_info *wl = hw->priv;
-
- 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");
- 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);
- }
- 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: Unknown 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)
-{
- struct wl_info *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;
- struct wl_info *wl = hw->priv;
-
- /* Init the scb */
- scb = (struct scb *)sta->drv_priv;
- memset(scb, 0, 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;
- wl->pub->global_ampdu->max_pdu = 16;
- pktq_init(&scb->scb_ampdu.txq, AMPDU_MAX_SCB_TID,
- AMPDU_MAX_SCB_TID * PKTQ_LEN_DEFAULT);
-
- sta->ht_cap.ht_supported = true;
- sta->ht_cap.ampdu_factor = AMPDU_RX_FACTOR_64K;
- 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
- struct wl_info *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 = {
- .tx = wl_ops_tx,
- .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(struct wl_info *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 struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
- uint bustype, void *btparam, uint irq)
-{
- struct wl_info *wl;
- struct osl_info *osh;
- int unit, err;
-
- unsigned long base_addr;
- struct ieee80211_hw *hw;
- u8 perm[ETH_ALEN];
-
- unit = wl_found;
- err = 0;
-
- if (unit < 0) {
- WL_ERROR("wl%d: unit number overflow, exiting\n", unit);
- return NULL;
- }
-
- osh = osl_attach(btparam, bustype);
- ASSERT(osh);
-
- /* allocate private info */
- hw = pci_get_drvdata(btparam); /* btparam == pdev */
- wl = hw->priv;
- ASSERT(wl);
-
- wl->osh = osh;
- atomic_set(&wl->callbacks, 0);
-
- /* setup the bottom half handler */
- tasklet_init(&wl->tasklet, wl_dpc, (unsigned long) wl);
-
-
-
- base_addr = regs;
-
- if (bustype == PCI_BUS) {
- wl->piomode = false;
- } else if (bustype == RPC_BUS) {
- /* Do nothing */
- } else {
- bustype = PCI_BUS;
- WL_TRACE("force to PCI\n");
- }
- wl->bcm_bustype = bustype;
-
- wl->regsva = ioremap_nocache(base_addr, PCI_BAR0_WINSZ);
- if (wl->regsva == NULL) {
- WL_ERROR("wl%d: ioremap() failed\n", unit);
- goto fail;
- }
- spin_lock_init(&wl->lock);
- spin_lock_init(&wl->isr_lock);
-
- /* prepare ucode */
- if (wl_request_fw(wl, (struct pci_dev *)btparam)) {
- printf("%s: Failed to find firmware usually in %s\n",
- KBUILD_MODNAME, "/lib/firmware/brcm");
- wl_release_fw(wl);
- wl_remove((struct pci_dev *)btparam);
- goto fail1;
- }
-
- /* common load-time initialization */
- wl->wlc = wlc_attach((void *)wl, vendor, device, unit, wl->piomode, osh,
- wl->regsva, wl->bcm_bustype, btparam, &err);
- wl_release_fw(wl);
- if (!wl->wlc) {
- printf("%s: wlc_attach() failed with code %d\n",
- KBUILD_MODNAME, err);
- 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);
-
-
- if (wlc_iovar_setint(wl->wlc, "mpc", 0)) {
- WL_ERROR("wl%d: Error setting MPC variable to 0\n", unit);
- }
-
- /* 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;
-
- /* 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, ETH_ALEN);
- 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);
- }
- WL_ERROR("wl%d: Broadcom BCM43xx 802.11 MAC80211 Driver (" PHY_VERSION_STR ")",
- unit);
-
-#ifdef BCMDBG
- printf(" (Compiled at " __TIME__ " on " __DATE__ ")");
-#endif /* BCMDBG */
- printf("\n");
-
- wl_found++;
- return wl;
-
- fail:
- wl_free(wl);
-fail1:
- return NULL;
-}
-
-
-
-#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,
- .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 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)
-{
- struct wl_info *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]);
-
- 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;
- }
- 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++;
- if (phy_list[0] == 'n' || phy_list[0] == 'c') {
- 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);
-}
-
-/**
- * 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;
- struct wl_info *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(struct wl_info), &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);
-
- memset(hw->priv, 0, 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)
-{
- struct wl_info *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)
-{
- struct wl_info *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)
-{
- struct wl_info *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,
-};
-
-/**
- * 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);
- }
- {
- 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 /* BCMDBG */
-
- error = pci_register_driver(&wl_pci_driver);
- if (!error)
- return 0;
-
-
-
- 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)
-{
- pci_unregister_driver(&wl_pci_driver);
-
-}
-
-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(struct wl_info *wl)
-{
- wl_timer_t *t, *next;
- struct osl_info *osh;
-
- ASSERT(wl);
- /* free ucode data */
- if (wl->fw.fw_cnt)
- wl_ucode_data_free();
- if (wl->irq)
- free_irq(wl->irq, wl);
-
- /* 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 && wl->bcm_bustype != SDIO_BUS &&
- wl->bcm_bustype != JTAG_BUS) {
- iounmap((void *)wl->regsva);
- }
- wl->regsva = NULL;
-
-
- osl_detach(osh);
-}
-
-/* transmit a packet */
-static int BCMFASTPATH wl_start(struct sk_buff *skb, struct wl_info *wl)
-{
- if (!wl)
- return -ENETDOWN;
-
- return wl_start_int(wl, WL_TO_HW(wl), skb);
-}
-
-static int BCMFASTPATH
-wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, struct sk_buff *skb)
-{
- wlc_sendpkt_mac80211(wl->wlc, skb, hw);
- return NETDEV_TX_OK;
-}
-
-void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
- int prio)
-{
- WL_ERROR("Shouldn't be here %s\n", __func__);
-}
-
-void wl_init(struct wl_info *wl)
-{
- WL_TRACE("wl%d: wl_init\n", wl->pub->unit);
-
- wl_reset(wl);
-
- wlc_init(wl->wlc);
-}
-
-uint wl_reset(struct wl_info *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(struct wl_info *wl)
-{
- unsigned long flags;
-
- INT_LOCK(wl, flags);
- wlc_intrson(wl->wlc);
- INT_UNLOCK(wl, flags);
-}
-
-bool wl_alloc_dma_resources(struct wl_info *wl, uint addrwidth)
-{
- return true;
-}
-
-u32 BCMFASTPATH wl_intrsoff(struct wl_info *wl)
-{
- unsigned long flags;
- u32 status;
-
- INT_LOCK(wl, flags);
- status = wlc_intrsoff(wl->wlc);
- INT_UNLOCK(wl, flags);
- return status;
-}
-
-void wl_intrsrestore(struct wl_info *wl, u32 macintmask)
-{
- unsigned long flags;
-
- INT_LOCK(wl, flags);
- wlc_intrsrestore(wl->wlc, macintmask);
- INT_UNLOCK(wl, flags);
-}
-
-int wl_up(struct wl_info *wl)
-{
- int error = 0;
-
- if (wl->pub->up)
- return 0;
-
- error = wlc_up(wl->wlc);
-
- return error;
-}
-
-void wl_down(struct wl_info *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);
-
- /* 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);
-
- WL_LOCK(wl);
-}
-
-irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id)
-{
- struct wl_info *wl;
- bool ours, wantdpc;
- unsigned long flags;
-
- wl = (struct wl_info *) 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);
-}
-
-static void BCMFASTPATH wl_dpc(unsigned long data)
-{
- struct wl_info *wl;
-
- wl = (struct wl_info *) 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);
-}
-
-static void wl_link_up(struct wl_info *wl, char *ifname)
-{
- WL_ERROR("wl%d: link up (%s)\n", wl->pub->unit, ifname);
-}
-
-static void wl_link_down(struct wl_info *wl, char *ifname)
-{
- WL_ERROR("wl%d: link down (%s)\n", wl->pub->unit, ifname);
-}
-
-void wl_event(struct wl_info *wl, char *ifname, wlc_event_t *e)
-{
-
- switch (e->event.event_type) {
- case WLC_E_LINK:
- case WLC_E_NDIS_LINK:
- if (e->event.flags & WLC_EVENT_MSG_LINK)
- wl_link_up(wl, ifname);
- else
- wl_link_down(wl, ifname);
- break;
- case WLC_E_RADIO:
- break;
- }
-}
-
-static void wl_timer(unsigned long data)
-{
- _wl_timer((wl_timer_t *) data);
-}
-
-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(struct wl_info *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;
- }
-
- memset(t, 0, 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(struct wl_info *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(struct wl_info *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(struct wl_info *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)
-{
- struct wl_info *wl = (struct wl_info *) 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;
-};
-
-char *wl_firmwares[WL_MAX_FW] = {
- "brcm/bcm43xx",
- NULL
-};
-
-int wl_ucode_init_buf(struct wl_info *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(struct wl_info *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;
-}
-
-static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
-{
- int status;
- struct device *device = &pdev->dev;
- char fw_name[100];
- int i;
-
- memset((void *)&wl->fw, 0, 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;
- return wl_ucode_data_init(wl);
-}
-
-void wl_ucode_free_buf(void *p)
-{
- kfree(p);
-}
-
-static void wl_release_fw(struct wl_info *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]);
- }
-}
-
-
-/*
- * checks validity of all firmware images loaded from user space
- */
-int wl_check_firmwares(struct wl_info *wl)
-{
- int i;
- int entry;
- int rc = 0;
- const struct firmware *fw;
- const struct firmware *fw_hdr;
- struct wl_fw_hdr *ucode_hdr;
- for (i = 0; i < WL_MAX_FW && rc == 0; i++) {
- fw = wl->fw.fw_bin[i];
- fw_hdr = wl->fw.fw_hdr[i];
- if (fw == NULL && fw_hdr == NULL) {
- break;
- } else if (fw == NULL || fw_hdr == NULL) {
- WL_ERROR("%s: invalid bin/hdr fw\n", __func__);
- rc = -EBADF;
- } else if (fw_hdr->size % sizeof(struct wl_fw_hdr)) {
- WL_ERROR("%s: non integral fw hdr file size %d/%zu\n",
- __func__, fw_hdr->size,
- sizeof(struct wl_fw_hdr));
- rc = -EBADF;
- } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
- WL_ERROR("%s: out of bounds fw file size %d\n",
- __func__, fw->size);
- rc = -EBADF;
- } else {
- /* check if ucode section overruns firmware image */
- ucode_hdr = (struct wl_fw_hdr *)fw_hdr->data;
- for (entry = 0; entry < wl->fw.hdr_num_entries[i] && rc;
- entry++, ucode_hdr++) {
- if (ucode_hdr->offset + ucode_hdr->len >
- fw->size) {
- WL_ERROR("%s: conflicting bin/hdr\n",
- __func__);
- rc = -EBADF;
- }
- }
- }
- }
- if (rc == 0 && wl->fw.fw_cnt != i) {
- WL_ERROR("%s: invalid fw_cnt=%d\n", __func__, wl->fw.fw_cnt);
- rc = -EBADF;
- }
- return rc;
-}
-
+++ /dev/null
-/*
- * 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 {
- struct wlc_pub *pub; /* pointer to public wlc state */
- void *wlc; /* pointer to private common os-independent data */
- struct osl_info *osh; /* pointer to os handler */
- u32 magic;
-
- int irq;
-
- spinlock_t lock; /* per-device perimeter lock */
- spinlock_t isr_lock; /* per-device ISR synchronization lock */
- 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 */
- 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 */
- 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;
-};
-
-#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)
-
-#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(struct wl_info *wl);
-extern int wl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-
-#endif /* _wl_mac80211_h_ */
+++ /dev/null
-/*
- * 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 MIN_FW_SIZE 40000 /* minimum firmware file size in bytes */
-#define MAX_FW_SIZE 150000
-
-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;
-
-extern int wl_ucode_data_init(struct wl_info *wl);
-extern void wl_ucode_data_free(void);
-
-extern int wl_ucode_init_buf(struct wl_info *wl, void **pbuf, unsigned int idx);
-extern int wl_ucode_init_uint(struct wl_info *wl, unsigned *data,
- unsigned int idx);
-extern void wl_ucode_free_buf(void *);
-extern int wl_check_firmwares(struct wl_info *wl);
+++ /dev/null
-/*
- * 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/types.h>
-#include <bcmdefs.h>
-#include <d11ucode_ext.h>
-#include <wl_ucode.h>
-
-
-
-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(struct wl_info *wl)
-{
- int rc;
- rc = wl_check_firmwares(wl);
- if (rc < 0)
- return rc;
- 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;
-}
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <osl.h>
-#include <bcmutils.h>
-#include <siutils.h>
-#include <wlioctl.h>
-#include <wlc_pub.h>
-#include <wlc_key.h>
-#include <sbhndpio.h>
-#include <sbhnddma.h>
-#include <wlc_event.h>
-#include <wlc_mac80211.h>
-#include <wlc_alloc.h>
-#include <wl_dbg.h>
-
-static struct wlc_pub *wlc_pub_malloc(struct osl_info *osh, uint unit,
- uint *err, uint devid);
-static void wlc_pub_mfree(struct osl_info *osh, struct wlc_pub *pub);
-static void wlc_tunables_init(wlc_tunables_t *tunables, uint devid);
-
-void *wlc_calloc(struct osl_info *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;
-}
-
-static struct wlc_pub *wlc_pub_malloc(struct osl_info *osh, uint unit,
- uint *err, uint devid)
-{
- struct wlc_pub *pub;
-
- pub = (struct wlc_pub *) wlc_calloc(osh, unit, sizeof(struct wlc_pub));
- 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 = (u8 *)wlc_calloc(osh, unit,
- (ETH_ALEN * MAXMULTILIST));
- if (pub->multicast == NULL) {
- *err = 1003;
- goto fail;
- }
-
- return pub;
-
- fail:
- wlc_pub_mfree(osh, pub);
- return NULL;
-}
-
-static void wlc_pub_mfree(struct osl_info *osh, struct wlc_pub *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(struct osl_info *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(struct osl_info *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;
- kfree(current_bss);
- cfg->current_bss = NULL;
- }
-
- kfree(cfg);
-}
-
-void wlc_bsscfg_ID_assign(struct wlc_info *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
- */
-struct wlc_info *wlc_attach_malloc(struct osl_info *osh, uint unit, uint *err,
- uint devid)
-{
- struct wlc_info *wlc;
-
- wlc = (struct wlc_info *) wlc_calloc(osh, unit,
- sizeof(struct wlc_info));
- if (wlc == NULL) {
- *err = 1002;
- goto fail;
- }
-
- wlc->hwrxoff = WL_HWRXOFF;
-
- /* allocate struct wlc_pub state structure */
- wlc->pub = wlc_pub_malloc(osh, unit, err, devid);
- if (wlc->pub == NULL) {
- *err = 1003;
- goto fail;
- }
- wlc->pub->wlc = wlc;
-
- /* allocate struct wlc_hw_info state structure */
-
- wlc->hw = (struct wlc_hw_info *)wlc_calloc(osh, unit,
- sizeof(struct wlc_hw_info));
- if (wlc->hw == NULL) {
- *err = 1005;
- goto fail;
- }
- wlc->hw->wlc = wlc;
-
- 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));
- }
- }
-
- 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] = (struct wlcband *)wlc_calloc(osh, unit,
- (sizeof(struct wlcband)*MAXBANDS));
- if (wlc->bandstate[0] == NULL) {
- *err = 1025;
- goto fail;
- } else {
- int i;
-
- for (i = 1; i < MAXBANDS; i++) {
- wlc->bandstate[i] =
- (struct wlcband *) ((unsigned long)wlc->bandstate[0]
- + (sizeof(struct wlcband)*i));
- }
- }
-
- wlc->corestate = (struct wlccore *)wlc_calloc(osh, unit,
- sizeof(struct wlccore));
- 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(struct wlc_info *wlc, struct osl_info *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) {
- if (wlc->hw->bandstate[0]) {
- kfree(wlc->hw->bandstate[0]);
- wlc->hw->bandstate[0] = NULL;
- }
-
- /* free hw struct */
- kfree(wlc->hw);
- wlc->hw = NULL;
- }
-
- /* free the wlc */
- kfree(wlc);
- wlc = NULL;
-}
+++ /dev/null
-/*
- * 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(struct osl_info *osh, uint unit, uint size);
-
-extern struct wlc_info *wlc_attach_malloc(struct osl_info *osh, uint unit,
- uint *err, uint devid);
-extern void wlc_detach_mfree(struct wlc_info *wlc, struct osl_info *osh);
-
-struct wlc_bsscfg;
-extern struct wlc_bsscfg *wlc_bsscfg_malloc(struct osl_info *osh, uint unit);
-extern void wlc_bsscfg_mfree(struct osl_info *osh, struct wlc_bsscfg *cfg);
+++ /dev/null
-/*
- * 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 <osl.h>
-#include <bcmutils.h>
-#include <siutils.h>
-#include <bcmendian.h>
-#include <wlioctl.h>
-#include <sbhndpio.h>
-#include <sbhnddma.h>
-#include <hnddma.h>
-#include <d11.h>
-#include <wlc_rate.h>
-#include <wlc_pub.h>
-#include <wlc_key.h>
-#include <wlc_event.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>
-#include <wl_dbg.h>
-
-
-#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 (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 {
- struct wlc_info *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 */
-
-};
-
-#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(struct ampdu_info *ampdu);
-static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
-static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
-
-static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
- scb_ampdu_t *scb_ampdu,
- u8 tid, bool override);
-static void ampdu_cleanup_tid_ini(struct ampdu_info *ampdu,
- scb_ampdu_t *scb_ampdu,
- u8 tid, bool force);
-static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
-static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
-static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
-
-#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
-
-static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
- struct scb *scb,
- struct sk_buff *p, tx_status_t *txs,
- u32 frmtxstatus, u32 frmtxstatus2);
-
-static inline u16 pkt_txh_seqnum(struct wlc_info *wlc, struct sk_buff *p)
-{
- d11txh_t *txh;
- struct ieee80211_hdr *h;
- txh = (d11txh_t *) p->data;
- h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
- return ltoh16(h->seq_ctrl) >> SEQNUM_SHIFT;
-}
-
-struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
-{
- struct ampdu_info *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(struct ampdu_info), 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;
- 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(struct ampdu_info *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(struct ampdu_info *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(struct ampdu_info *ampdu)
-{
- WL_NONE("%s: Entering\n", __func__);
-}
-
-static void scb_ampdu_update_config(struct ampdu_info *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(struct ampdu_info *ampdu)
-{
- scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
-}
-
-static void wlc_ffpld_init(struct ampdu_info *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(struct wlc_info *wlc, int fid)
-{
- struct ampdu_info *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(struct ampdu_info *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(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
- uint prec)
-{
- scb_ampdu_t *scb_ampdu;
- scb_ampdu_tid_ini_t *ini;
- u8 tid = (u8) (p->priority);
-
- 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(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
- struct sk_buff **pdu, int prec)
-{
- struct wlc_info *wlc;
- struct osl_info *osh;
- struct sk_buff *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 ieee80211_hdr *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 ieee80211_rts *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) (p->priority);
- 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 *) p->data;
- plcp = (u8 *) (txh + 1);
- h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
- seq = ltoh16(h->seq_ctrl) >> 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 ieee80211_rts *)&txh->rts_frame;
- fc = ltoh16(rts->frame_control);
- 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\n",
- 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) (p->priority) == 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 *) pkt[count - 1]->data;
- 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 *) pkt[0]->data;
- 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 ieee80211_rts *)&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->duration = 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->duration;
- /* 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__);
- }
- for (i = 0; i < count; i++)
- wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
- ampdu->txpkt_weight);
-
- }
- /* endif (count) */
- return err;
-}
-
-void BCMFASTPATH
-wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
- struct sk_buff *p, tx_status_t *txs)
-{
- scb_ampdu_t *scb_ampdu;
- struct wlc_info *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, p->priority);
- 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) {
- 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);
- }
-
- wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
- wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
-}
-
-void
-rate_status(struct wlc_info *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;
- }
-}
-
-#define SHORTNAME "AMPDU status"
-
-static void BCMFASTPATH
-wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
- struct sk_buff *p, tx_status_t *txs,
- u32 s1, u32 s2)
-{
- scb_ampdu_t *scb_ampdu;
- struct wlc_info *wlc = ampdu->wlc;
- scb_ampdu_tid_ini_t *ini;
- u8 bitmap[8], queue, tid;
- d11txh_t *txh;
- u8 *plcp;
- struct ieee80211_hdr *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];
- memset(hole, 0, 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) (p->priority);
-
- 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);
-
- memset(bitmap, 0, 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;
- }
- }
- } 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 *) p->data);
- 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 *) p->data;
- mcl = ltoh16(txh->MacTxControlLow);
- plcp = (u8 *) (txh + 1);
- h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
- seq = ltoh16(h->seq_ctrl) >> 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;
-
- skb_pull(p, D11_PHY_HDR_LEN);
- skb_pull(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;
- skb_pull(p, D11_PHY_HDR_LEN);
- skb_pull(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(struct ampdu_info *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(struct ampdu_info *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(struct ampdu_info *ampdu, bool on)
-{
- struct wlc_info *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(struct ampdu_info *ampdu)
-{
- if (WLC_PHY_11N_CAP(ampdu->wlc->band))
- return true;
- else
- return false;
-}
-
-static void ampdu_update_max_txlen(struct ampdu_info *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(struct ampdu_info *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(struct wlc_info *wlc)
-{
- char template[T_RAM_ACCESS_SZ * 2];
-
- /* driver needs to write the ta in the template; ta is at offset 16 */
- memset(template, 0, sizeof(template));
- bcopy((char *)wlc->pub->cur_etheraddr, template, ETH_ALEN);
- wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
- template);
-}
-
-bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
-{
- return wlc->ampdu->ini_enable[tid];
-}
-
-void wlc_ampdu_shm_upd(struct ampdu_info *ampdu)
-{
- struct wlc_info *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);
- }
-}
+++ /dev/null
-/*
- * 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 struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc);
-extern void wlc_ampdu_detach(struct ampdu_info *ampdu);
-extern bool wlc_ampdu_cap(struct ampdu_info *ampdu);
-extern int wlc_ampdu_set(struct ampdu_info *ampdu, bool on);
-extern int wlc_sendampdu(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
- struct sk_buff **aggp, int prec);
-extern void wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
- struct sk_buff *p, tx_status_t *txs);
-extern void wlc_ampdu_reset(struct ampdu_info *ampdu);
-extern void wlc_ampdu_macaddr_upd(struct wlc_info *wlc);
-extern void wlc_ampdu_shm_upd(struct ampdu_info *ampdu);
-
-extern u8 wlc_ampdu_null_delim_cnt(struct ampdu_info *ampdu, struct scb *scb,
- ratespec_t rspec, int phylen);
-extern void scb_ampdu_cleanup(struct ampdu_info *ampdu, struct scb *scb);
-
-#endif /* _wlc_ampdu_h_ */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <bcmdefs.h>
-#include <osl.h>
-#include <bcmutils.h>
-#include <siutils.h>
-#include <wlioctl.h>
-
-#include <bcmdevs.h>
-#include <sbhndpio.h>
-#include <sbhnddma.h>
-#include <d11.h>
-#include <wlc_rate.h>
-#include <wlc_key.h>
-#include <wlc_pub.h>
-#include <wl_dbg.h>
-#include <wlc_event.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(struct antsel_info *asi, wlc_antselcfg_t *antsel);
-static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id);
-static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
-static void wlc_antsel_init_cfg(struct antsel_info *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 */
-};
-
-struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc,
- struct osl_info *osh,
- struct wlc_pub *pub,
- struct wlc_hw_info *wlc_hw) {
- struct antsel_info *asi;
-
- asi = kzalloc(sizeof(struct antsel_info), 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(struct antsel_info *asi)
-{
- if (!asi)
- return;
-
- kfree(asi);
-}
-
-void wlc_antsel_init(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *asi, wlc_antselcfg_t *antsel)
-{
- struct wlc_info *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 */
+++ /dev/null
-/*
- * 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 struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc,
- struct osl_info *osh,
- struct wlc_pub *pub,
- struct wlc_hw_info *wlc_hw);
-extern void wlc_antsel_detach(struct antsel_info *asi);
-extern void wlc_antsel_init(struct antsel_info *asi);
-extern void wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef,
- bool sel,
- u8 id, u8 fbid, u8 *antcfg,
- u8 *fbantcfg);
-extern u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
-#endif /* _wlc_antsel_h_ */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.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>
-#include <bcmdevs.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_event.h>
-#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"
-#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>
-#include <wl_dbg.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(struct wlc_hw_info *wlc, uint mode);
-static void wlc_coreinit(struct wlc_info *wlc);
-
-/* used by wlc_wakeucode_init() */
-static void wlc_write_inits(struct wlc_hw_info *wlc_hw, const d11init_t *inits);
-static void wlc_ucode_write(struct wlc_hw_info *wlc_hw, const u32 ucode[],
- const uint nbytes);
-static void wlc_ucode_download(struct wlc_hw_info *wlc);
-static void wlc_ucode_txant_set(struct wlc_hw_info *wlc_hw);
-
-/* used by wlc_dpc() */
-static bool wlc_bmac_dotxstatus(struct wlc_hw_info *wlc, tx_status_t *txs,
- u32 s2);
-static bool wlc_bmac_txstatus_corerev4(struct wlc_hw_info *wlc);
-static bool wlc_bmac_txstatus(struct wlc_hw_info *wlc, bool bound, bool *fatal);
-static bool wlc_bmac_recv(struct wlc_hw_info *wlc_hw, uint fifo, bool bound);
-
-/* used by wlc_down() */
-static void wlc_flushqueues(struct wlc_info *wlc);
-
-static void wlc_write_mhf(struct wlc_hw_info *wlc_hw, u16 *mhfs);
-static void wlc_mctrl_reset(struct wlc_hw_info *wlc_hw);
-static void wlc_corerev_fifofixup(struct wlc_hw_info *wlc_hw);
-
-/* Low Level Prototypes */
-static u16 wlc_bmac_read_objmem(struct wlc_hw_info *wlc_hw, uint offset,
- u32 sel);
-static void wlc_bmac_write_objmem(struct wlc_hw_info *wlc_hw, uint offset,
- u16 v, u32 sel);
-static bool wlc_bmac_attach_dmapio(struct wlc_info *wlc, uint j, bool wme);
-static void wlc_bmac_detach_dmapio(struct wlc_hw_info *wlc_hw);
-static void wlc_ucode_bsinit(struct wlc_hw_info *wlc_hw);
-static bool wlc_validboardtype(struct wlc_hw_info *wlc);
-static bool wlc_isgoodchip(struct wlc_hw_info *wlc_hw);
-static char *wlc_get_macaddr(struct wlc_hw_info *wlc_hw);
-static void wlc_mhfdef(struct wlc_info *wlc, u16 *mhfs, u16 mhf2_init);
-static void wlc_mctrl_write(struct wlc_hw_info *wlc_hw);
-static void wlc_ucode_mute_override_set(struct wlc_hw_info *wlc_hw);
-static void wlc_ucode_mute_override_clear(struct wlc_hw_info *wlc_hw);
-static u32 wlc_wlintrsoff(struct wlc_info *wlc);
-static void wlc_wlintrsrestore(struct wlc_info *wlc, u32 macintmask);
-static void wlc_gpio_init(struct wlc_info *wlc);
-static void wlc_write_hw_bcntemplate0(struct wlc_hw_info *wlc_hw, void *bcn,
- int len);
-static void wlc_write_hw_bcntemplate1(struct wlc_hw_info *wlc_hw, void *bcn,
- int len);
-static void wlc_bmac_bsinit(struct wlc_info *wlc, chanspec_t chanspec);
-static u32 wlc_setband_inact(struct wlc_info *wlc, uint bandunit);
-static void wlc_bmac_setband(struct wlc_hw_info *wlc_hw, uint bandunit,
- chanspec_t chanspec);
-static void wlc_bmac_update_slot_timing(struct wlc_hw_info *wlc_hw,
- bool shortslot);
-static void wlc_upd_ofdm_pctl1_table(struct wlc_hw_info *wlc_hw);
-static u16 wlc_bmac_ofdm_ratetable_offset(struct wlc_hw_info *wlc_hw,
- u8 rate);
-
-/* === Low Level functions === */
-
-void wlc_bmac_set_shortslot(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw,
- bool shortslot)
-{
- struct osl_info *osh;
- d11regs_t *regs;
-
- osh = wlc_hw->osh;
- regs = wlc_hw->regs;
-
- if (shortslot) {
- /* 11g short slot: 11a timing */
- W_REG(osh, ®s->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, ®s->ifs_slot, 0x0212); /* BPHY_SLOT_TIME */
- wlc_bmac_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME);
- }
-}
-
-static void WLBANDINITFN(wlc_ucode_bsinit) (struct wlc_hw_info *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) (struct wlc_info *wlc, uint bandunit)
-{
- struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, uint fifo, bool bound)
-{
- struct sk_buff *p;
- struct sk_buff *head = NULL;
- struct sk_buff *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 {
- tail->prev = 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 = head->prev;
- p->prev = NULL;
-
- /* record the tsf_l in wlc_rxd11hdr */
- wlc_rxhdr = (wlc_d11rxhdr_t *) p->data;
- 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(struct wlc_info *wlc, bool bounded)
-{
- u32 macintstatus;
- struct wlc_hw_info *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, ®s->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__, wlc_hw->sih->chip,
- 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, ®s->gptimer, 0);
- }
-
- if (macintstatus & MI_RFDISABLE) {
-#if defined(BCMDBG)
- u32 rfd = R_REG(wlc_hw->osh, ®s->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)
-{
- struct wlc_info *wlc = (struct wlc_info *) arg;
- struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, wlc_bmac_state_t *state)
-{
- state->machwcap = wlc_hw->machwcap;
-
- return 0;
-}
-
-static bool wlc_bmac_attach_dmapio(struct wlc_info *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;
- struct wlc_hw_info *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;
- struct osl_info *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));
-
- 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(struct wlc_hw_info *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(struct wlc_info *wlc, u16 vendor, u16 device, uint unit,
- bool piomode, struct osl_info *osh, void *regsva,
- uint bustype, void *btparam)
-{
- struct wlc_hw_info *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 struct wlc_hw_info 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 ((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 ((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, ®s->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 (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 (is_broadcast_ether_addr(wlc_hw->etheraddr) ||
- is_zero_ether_addr(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(struct wlc_hw_info *wlc_hw)
-{
- struct wlc_info *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(struct wlc_info *wlc)
-{
- uint i;
- wlc_hwband_t *band;
- struct wlc_hw_info *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 (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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, chanspec_t chanspec,
- bool mute) {
- u32 macintmask;
- bool fastclk;
- struct wlc_info *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(struct wlc_hw_info *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 (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 (wlc_hw->sih->bustype == PCI_BUS)
- si_pci_down(wlc_hw->sih);
- wlc_bmac_xtal(wlc_hw, OFF);
- return BCME_RADIOOFF;
- }
-
- if (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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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 (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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, u8 *ea)
-{
- bcopy(wlc_hw->etheraddr, ea, ETH_ALEN);
-}
-
-void wlc_bmac_set_hw_etheraddr(struct wlc_hw_info *wlc_hw,
- u8 *ea)
-{
- bcopy(ea, wlc_hw->etheraddr, ETH_ALEN);
-}
-
-int wlc_bmac_bandtype(struct wlc_hw_info *wlc_hw)
-{
- return wlc_hw->band->bandtype;
-}
-
-void *wlc_cur_phy(struct wlc_info *wlc)
-{
- struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_info *wlc, u16 *mhfs, u16 mhf2_init)
-{
- struct wlc_hw_info *wlc_hw = wlc->hw;
-
- memset(mhfs, 0, MHFMAX * sizeof(u16));
-
- 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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, int idx,
- const u8 *addr)
-{
- d11regs_t *regs = wlc_hw->regs;
- volatile u16 *objdata16 = (volatile u16 *)®s->objdata;
- u32 mac_hm;
- u16 mac_l;
- struct osl_info *osh;
-
- WL_TRACE("wl%d: %s\n", wlc_hw->unit, __func__);
-
- ASSERT(wlc_hw->corerev > 4);
-
- mac_hm =
- (addr[3] << 24) | (addr[2] << 16) |
- (addr[1] << 8) | addr[0];
- mac_l = (addr[5] << 8) | addr[4];
-
- osh = wlc_hw->osh;
-
- W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
- (void)R_REG(osh, ®s->objaddr);
- W_REG(osh, ®s->objdata, mac_hm);
- W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
- (void)R_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw, int match_reg_offset,
- const u8 *addr)
-{
- d11regs_t *regs;
- u16 mac_l;
- u16 mac_m;
- u16 mac_h;
- struct osl_info *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[0] | (addr[1] << 8);
- mac_m = addr[2] | (addr[3] << 8);
- mac_h = addr[4] | (addr[5] << 8);
-
- osh = wlc_hw->osh;
-
- /* enter the MAC addr into the RXE match registers */
- W_REG(osh, ®s->rcm_ctl, RCM_INC_DATA | match_reg_offset);
- W_REG(osh, ®s->rcm_mat_data, mac_l);
- W_REG(osh, ®s->rcm_mat_data, mac_m);
- W_REG(osh, ®s->rcm_mat_data, mac_h);
-
-}
-
-void
-wlc_bmac_write_template_ram(struct wlc_hw_info *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 */
- struct osl_info *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, ®s->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, ®s->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, ®s->tplatewrdata, word);
-
- buf = (u8 *) buf + sizeof(u32);
- len -= sizeof(u32);
- }
-}
-
-void wlc_bmac_set_cwmin(struct wlc_hw_info *wlc_hw, u16 newmin)
-{
- struct osl_info *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(struct wlc_hw_info *wlc_hw, u16 newmax)
-{
- struct osl_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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, ®s->maccommand, MCMD_BCN0VLD);
-}
-
-static void
-wlc_write_hw_bcntemplate1(struct wlc_hw_info *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, ®s->maccommand, MCMD_BCN1VLD);
-}
-
-/* mac is assumed to be suspended at this point */
-void
-wlc_bmac_write_hw_bcntemplates(struct wlc_hw_info *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, ®s->maccommand) & MCMD_BCN0VLD))
- wlc_write_hw_bcntemplate0(wlc_hw, bcn, len);
- /* bcn 1 */
- else if (!
- (R_REG(wlc_hw->osh, ®s->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) (struct wlc_hw_info *wlc_hw)
-{
- u16 v;
- struct wlc_info *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) (struct wlc_info *wlc, chanspec_t chanspec)
-{
- struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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) (struct wlc_hw_info *wlc_hw, uint bandunit,
- chanspec_t chanspec) {
- struct wlc_info *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) (struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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 ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
- (wlc_hw->sih->chip == BCM43225_CHIP_ID) ||
- (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(struct wlc_hw_info *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 (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 ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
- (wlc_hw->sih->chip == BCM43225_CHIP_ID) ||
- (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)
- && (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(struct wlc_hw_info *wlc_hw, uint fifo)
-{
- struct hnddma_pub *di = wlc_hw->di[fifo];
- struct osl_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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;
- struct osl_info *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, ®s->xmtfifocmd, txfifo_cmd);
- W_REG(osh, ®s->xmtfifodef, txfifo_def);
- if (D11REV_GE(wlc_hw->corerev, 16))
- W_REG(osh, ®s->xmtfifodef1, txfifo_def1);
-
- W_REG(osh, ®s->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(struct wlc_info *wlc)
-{
- struct wlc_hw_info *wlc_hw = wlc->hw;
- d11regs_t *regs;
- u32 sflags;
- uint bcnint_us;
- uint i = 0;
- bool fifosz_fixup = false;
- struct osl_info *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, ®s->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, ®s->macintstatus) & MI_MACSSPNDD) == 0),
- 1000 * 1000);
- if ((R_REG(osh, ®s->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, ®s->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, ®s->intrcvlazy[0], (1 << IRL_FC_SHIFT));
- if (D11REV_IS(wlc_hw->corerev, 4))
- W_REG(osh, ®s->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, ®s->tsf_cfprep, (bcnint_us << CFPREP_CBI_SHIFT));
- W_REG(osh, ®s->tsf_cfpstart, bcnint_us);
- W_REG(osh, ®s->macintstatus, MI_GP1);
-
- /* write interrupt mask */
- W_REG(osh, ®s->intctrlregs[RX_FIFO].intmask, DEF_RXINTMASK);
- if (D11REV_IS(wlc_hw->corerev, 4))
- W_REG(osh, ®s->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, ®s->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, ®s->objaddr, OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
- (void)R_REG(osh, ®s->objaddr);
- W_REG(osh, ®s->objdata, wlc_hw->SRL);
- W_REG(osh, ®s->objaddr, OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
- (void)R_REG(osh, ®s->objaddr);
- W_REG(osh, ®s->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, ®s->ifs_ctl, 0x0FFF);
- W_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw, u8 spurmode)
-{
- d11regs_t *regs;
- struct osl_info *osh;
- regs = wlc_hw->regs;
- osh = wlc_hw->osh;
-
- if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
- (wlc_hw->sih->chip == BCM43225_CHIP_ID)) {
- if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */
- W_REG(osh, ®s->tsf_clk_frac_l, 0x2082);
- W_REG(osh, ®s->tsf_clk_frac_h, 0x8);
- } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */
- W_REG(osh, ®s->tsf_clk_frac_l, 0x5341);
- W_REG(osh, ®s->tsf_clk_frac_h, 0x8);
- } else { /* 120Mhz */
- W_REG(osh, ®s->tsf_clk_frac_l, 0x8889);
- W_REG(osh, ®s->tsf_clk_frac_h, 0x8);
- }
- } else if (WLCISLCNPHY(wlc_hw->band)) {
- if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */
- W_REG(osh, ®s->tsf_clk_frac_l, 0x7CE0);
- W_REG(osh, ®s->tsf_clk_frac_h, 0xC);
- } else { /* 80Mhz */
- W_REG(osh, ®s->tsf_clk_frac_l, 0xCCCD);
- W_REG(osh, ®s->tsf_clk_frac_h, 0xC);
- }
- }
-}
-
-/* Initialize GPIOs that are controlled by D11 core */
-static void wlc_gpio_init(struct wlc_info *wlc)
-{
- struct wlc_hw_info *wlc_hw = wlc->hw;
- d11regs_t *regs;
- u32 gc, gm;
- struct osl_info *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, ®s->psm_gpio_oe,
- (BOARD_GPIO_12 | BOARD_GPIO_13));
- OR_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw)
-{
- struct wlc_info *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(struct wlc_hw_info *wlc_hw, const u32 ucode[],
- const uint nbytes) {
- struct osl_info *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, ®s->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
- (void)R_REG(osh, ®s->objaddr);
- for (i = 0; i < count; i++)
- W_REG(osh, ®s->objdata, ucode[i]);
-}
-
-static void wlc_write_inits(struct wlc_hw_info *wlc_hw, const d11init_t *inits)
-{
- int i;
- struct osl_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw)
-{
- return (u16) wlc_hw->wlc->stf->txant;
-}
-
-void wlc_bmac_antsel_type_set(struct wlc_hw_info *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(struct wlc_hw_info *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,
- ®s->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",
- idx, unit);
- 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, ®s->intctrlregs[idx].intstatus,
- intstatus);
- }
-}
-
-void wlc_intrson(struct wlc_info *wlc)
-{
- struct wlc_hw_info *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(struct wlc_info *wlc)
-{
- if (!wlc->hw->up)
- return 0;
-
- return wl_intrsoff(wlc->wl);
-}
-
-static void wlc_wlintrsrestore(struct wlc_info *wlc, u32 macintmask)
-{
- if (!wlc->hw->up)
- return;
-
- wl_intrsrestore(wlc->wl, macintmask);
-}
-
-u32 wlc_intrsoff(struct wlc_info *wlc)
-{
- struct wlc_hw_info *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(struct wlc_info *wlc, u32 macintmask)
-{
- struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool on, mbool flags)
-{
- u8 null_ether_addr[ETH_ALEN] = {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(struct wlc_hw_info *wlc_hw, bool user_flag)
-{
- wlc_phy_set_deaf(wlc_hw->band->pi, user_flag);
-}
-
-int wlc_bmac_xmtfifo_sz_get(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_info *wlc, bool in_isr)
-{
- struct wlc_hw_info *wlc_hw = wlc->hw;
- d11regs_t *regs = wlc_hw->regs;
- u32 macintstatus;
- u32 intstatus_rxfifo, intstatus_txsfifo;
- struct osl_info *osh;
-
- osh = wlc_hw->osh;
-
- /* macintstatus includes a DMA interrupt summary bit */
- macintstatus = R_REG(osh, ®s->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, ®s->macintmask, 0);
- (void)R_REG(osh, ®s->macintmask); /* sync readback */
- wlc->macintmask = 0;
-
- /* clear device interrupts */
- W_REG(osh, ®s->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, ®s->intctrlregs[RX_FIFO].intstatus);
- intstatus_txsfifo =
- R_REG(osh,
- ®s->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,
- ®s->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,
- ®s->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, ®s->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(struct wlc_info *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(struct wlc_info *wlc, bool *wantdpc)
-{
- struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw)
-{
- struct sk_buff *status_p;
- tx_status_t *txs;
- struct osl_info *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 *) status_p->data;
- /* 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);
-
- pkt_buf_free_skb(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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool bound, bool *fatal)
-{
- bool morepending = false;
- struct wlc_info *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;
- struct osl_info *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, ®s->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, ®s->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(struct wlc_info *wlc)
-{
- struct wlc_hw_info *wlc_hw = wlc->hw;
- d11regs_t *regs = wlc_hw->regs;
- u32 mc, mi;
- struct osl_info *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, ®s->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, ®s->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, ®s->macintstatus) & MI_MACSSPNDD),
- WLC_MAX_MAC_SUSPEND);
-
- if (!(R_REG(osh, ®s->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, ®s->psmdebug),
- R_REG(osh, ®s->phydebug),
- R_REG(osh, ®s->psm_brc));
- }
-
- mc = R_REG(osh, ®s->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(struct wlc_info *wlc)
-{
- struct wlc_hw_info *wlc_hw = wlc->hw;
- d11regs_t *regs = wlc_hw->regs;
- u32 mc, mi;
- struct osl_info *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, ®s->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, ®s->macintstatus, MI_MACSSPNDD);
-
- mc = R_REG(osh, ®s->maccontrol);
- ASSERT(!(mc & MCTL_PSM_JMP_0));
- ASSERT(mc & MCTL_EN_MAC);
- ASSERT(mc & MCTL_PSM_RUN);
-
- mi = R_REG(osh, ®s->macintstatus);
- ASSERT(!(mi & MI_MACSSPNDD));
-
- wlc_ucode_wake_override_clear(wlc_hw, WLC_WAKE_OVERRIDE_MACSUSPEND);
-}
-
-void wlc_bmac_ifsctl_edcrs_set(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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, ®s->tsf_timerlow);
- *tsf_h_ptr = R_REG(wlc_hw->osh, ®s->tsf_timerhigh);
-
- return;
-}
-
-bool wlc_bmac_validate_chip_access(struct wlc_hw_info *wlc_hw)
-{
- d11regs_t *regs;
- u32 w, val;
- volatile u16 *reg16;
- struct osl_info *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, ®s->objaddr, OBJADDR_SHM_SEL | 0);
- (void)R_REG(osh, ®s->objaddr);
- w = R_REG(osh, ®s->objdata);
-
- /* Can we write and read back a 32bit register? */
- W_REG(osh, ®s->objaddr, OBJADDR_SHM_SEL | 0);
- (void)R_REG(osh, ®s->objaddr);
- W_REG(osh, ®s->objdata, (u32) 0xaa5555aa);
-
- W_REG(osh, ®s->objaddr, OBJADDR_SHM_SEL | 0);
- (void)R_REG(osh, ®s->objaddr);
- val = R_REG(osh, ®s->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, ®s->objaddr, OBJADDR_SHM_SEL | 0);
- (void)R_REG(osh, ®s->objaddr);
- W_REG(osh, ®s->objdata, (u32) 0x55aaaa55);
-
- W_REG(osh, ®s->objaddr, OBJADDR_SHM_SEL | 0);
- (void)R_REG(osh, ®s->objaddr);
- val = R_REG(osh, ®s->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, ®s->objaddr, OBJADDR_SHM_SEL | 0);
- (void)R_REG(osh, ®s->objaddr);
- W_REG(osh, ®s->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 *)®s->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, ®s->tsf_cfpstart, 0xCCCCBBBB);
-
- /* verify with the 16 bit registers that have no side effects */
- val = R_REG(osh, ®s->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, ®s->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, ®s->tsf_cfpstart, 0);
-
- w = R_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw, bool on)
-{
- d11regs_t *regs;
- struct osl_info *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, ®s->clk_ctl_st,
- (CCS_ERSRC_REQ_HT | CCS_ERSRC_REQ_D11PLL |
- CCS_ERSRC_REQ_PHYPLL));
- SPINWAIT((R_REG(osh, ®s->clk_ctl_st) &
- (CCS_ERSRC_AVAIL_HT)) != (CCS_ERSRC_AVAIL_HT),
- PHYPLL_WAIT_US);
-
- tmp = R_REG(osh, ®s->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, ®s->clk_ctl_st,
- (CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL));
- SPINWAIT((R_REG(osh, ®s->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, ®s->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, ®s->clk_ctl_st, ~CCS_ERSRC_REQ_PHYPLL);
- tmp = R_REG(osh, ®s->clk_ctl_st);
- }
-}
-
-void wlc_coredisable(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_info *wlc)
-{
- struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, uint offset)
-{
- return wlc_bmac_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
-}
-
-void wlc_bmac_write_shm(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, uint offset, u32 sel)
-{
- d11regs_t *regs = wlc_hw->regs;
- volatile u16 *objdata_lo = (volatile u16 *)®s->objdata;
- volatile u16 *objdata_hi = objdata_lo + 1;
- u16 v;
-
- ASSERT((offset & 1) == 0);
-
- W_REG(wlc_hw->osh, ®s->objaddr, sel | (offset >> 2));
- (void)R_REG(wlc_hw->osh, ®s->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(struct wlc_hw_info *wlc_hw, uint offset, u16 v, u32 sel)
-{
- d11regs_t *regs = wlc_hw->regs;
- volatile u16 *objdata_lo = (volatile u16 *)®s->objdata;
- volatile u16 *objdata_hi = objdata_lo + 1;
-
- ASSERT((offset & 1) == 0);
-
- W_REG(wlc_hw->osh, ®s->objaddr, sel | (offset >> 2));
- (void)R_REG(wlc_hw->osh, ®s->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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool noreset_flag)
-{
- wlc_hw->noreset = noreset_flag;
-}
-
-void wlc_bmac_set_ucode_loaded(struct wlc_hw_info *wlc_hw, bool ucode_loaded)
-{
- wlc_hw->ucode_loaded = ucode_loaded;
-}
-
-void wlc_bmac_pllreq(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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 (!((wlc_hw->sih->bustype == SI_BUS) ||
- ((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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, u8 val)
-{
- wlc_phy_txpwr_percent_set(wlc_hw->band->pi, val);
-}
-
-void wlc_bmac_antsel_set(struct wlc_hw_info *wlc_hw, u32 antsel_avail)
-{
- wlc_hw->antsel_avail = antsel_avail;
-}
+++ /dev/null
-/*
- * 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(struct wlc_hw_info) and HIGH(struct wlc_info)
- 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(struct wlc_info *wlc, u16 vendor, u16 device,
- uint unit, bool piomode, struct osl_info *osh,
- void *regsva, uint bustype, void *btparam);
-extern int wlc_bmac_detach(struct wlc_info *wlc);
-extern void wlc_bmac_watchdog(void *arg);
-extern void wlc_bmac_info_init(struct wlc_hw_info *wlc_hw);
-
-/* up/down, reset, clk */
-extern void wlc_bmac_xtal(struct wlc_hw_info *wlc_hw, bool want);
-
-extern void wlc_bmac_copyto_objmem(struct wlc_hw_info *wlc_hw,
- uint offset, const void *buf, int len,
- u32 sel);
-extern void wlc_bmac_copyfrom_objmem(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool clk);
-extern void wlc_bmac_core_phypll_reset(struct wlc_hw_info *wlc_hw);
-extern void wlc_bmac_core_phypll_ctl(struct wlc_hw_info *wlc_hw, bool on);
-extern void wlc_bmac_phyclk_fgc(struct wlc_hw_info *wlc_hw, bool clk);
-extern void wlc_bmac_macphyclk_set(struct wlc_hw_info *wlc_hw, bool clk);
-extern void wlc_bmac_phy_reset(struct wlc_hw_info *wlc_hw);
-extern void wlc_bmac_corereset(struct wlc_hw_info *wlc_hw, u32 flags);
-extern void wlc_bmac_reset(struct wlc_hw_info *wlc_hw);
-extern void wlc_bmac_init(struct wlc_hw_info *wlc_hw, chanspec_t chanspec,
- bool mute);
-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 void wlc_bmac_corereset(struct wlc_hw_info *wlc_hw, u32 flags);
-extern void wlc_bmac_switch_macfreq(struct wlc_hw_info *wlc_hw, u8 spurmode);
-
-/* chanspec, ucode interface */
-extern int wlc_bmac_bandtype(struct wlc_hw_info *wlc_hw);
-extern void wlc_bmac_set_chanspec(struct wlc_hw_info *wlc_hw,
- chanspec_t chanspec,
- bool mute, struct txpwr_limits *txpwr);
-
-extern void wlc_bmac_txfifo(struct wlc_hw_info *wlc_hw, uint fifo, void *p,
- bool commit, u16 frameid, u8 txpktpend);
-extern int wlc_bmac_xmtfifo_sz_get(struct wlc_hw_info *wlc_hw, uint fifo,
- uint *blocks);
-extern void wlc_bmac_mhf(struct wlc_hw_info *wlc_hw, u8 idx, u16 mask,
- u16 val, int bands);
-extern void wlc_bmac_mctrl(struct wlc_hw_info *wlc_hw, u32 mask, u32 val);
-extern u16 wlc_bmac_mhf_get(struct wlc_hw_info *wlc_hw, u8 idx, int bands);
-extern int wlc_bmac_xmtfifo_sz_set(struct wlc_hw_info *wlc_hw, uint fifo,
- uint blocks);
-extern void wlc_bmac_txant_set(struct wlc_hw_info *wlc_hw, u16 phytxant);
-extern u16 wlc_bmac_get_txant(struct wlc_hw_info *wlc_hw);
-extern void wlc_bmac_antsel_type_set(struct wlc_hw_info *wlc_hw,
- u8 antsel_type);
-extern int wlc_bmac_revinfo_get(struct wlc_hw_info *wlc_hw,
- wlc_bmac_revinfo_t *revinfo);
-extern int wlc_bmac_state_get(struct wlc_hw_info *wlc_hw,
- wlc_bmac_state_t *state);
-extern void wlc_bmac_write_shm(struct wlc_hw_info *wlc_hw, uint offset, u16 v);
-extern u16 wlc_bmac_read_shm(struct wlc_hw_info *wlc_hw, uint offset);
-extern void wlc_bmac_set_shm(struct wlc_hw_info *wlc_hw, uint offset, u16 v,
- int len);
-extern void wlc_bmac_write_template_ram(struct wlc_hw_info *wlc_hw, int offset,
- int len, void *buf);
-extern void wlc_bmac_copyfrom_vars(struct wlc_hw_info *wlc_hw, char **buf,
- uint *len);
-
-extern void wlc_bmac_process_ps_switch(struct wlc_hw_info *wlc,
- struct ether_addr *ea, s8 ps_on);
-extern void wlc_bmac_hw_etheraddr(struct wlc_hw_info *wlc_hw,
- u8 *ea);
-extern void wlc_bmac_set_hw_etheraddr(struct wlc_hw_info *wlc_hw,
- u8 *ea);
-extern bool wlc_bmac_validate_chip_access(struct wlc_hw_info *wlc_hw);
-
-extern bool wlc_bmac_radio_read_hwdisabled(struct wlc_hw_info *wlc_hw);
-extern void wlc_bmac_set_shortslot(struct wlc_hw_info *wlc_hw, bool shortslot);
-extern void wlc_bmac_mute(struct wlc_hw_info *wlc_hw, bool want, mbool flags);
-extern void wlc_bmac_set_deaf(struct wlc_hw_info *wlc_hw, bool user_flag);
-extern void wlc_bmac_band_stf_ss_set(struct wlc_hw_info *wlc_hw, u8 stf_mode);
-
-extern void wlc_bmac_wait_for_wake(struct wlc_hw_info *wlc_hw);
-extern bool wlc_bmac_tx_fifo_suspended(struct wlc_hw_info *wlc_hw,
- uint tx_fifo);
-extern void wlc_bmac_tx_fifo_suspend(struct wlc_hw_info *wlc_hw, uint tx_fifo);
-extern void wlc_bmac_tx_fifo_resume(struct wlc_hw_info *wlc_hw, uint tx_fifo);
-
-extern void wlc_ucode_wake_override_set(struct wlc_hw_info *wlc_hw,
- u32 override_bit);
-extern void wlc_ucode_wake_override_clear(struct wlc_hw_info *wlc_hw,
- u32 override_bit);
-
-extern void wlc_bmac_set_rcmta(struct wlc_hw_info *wlc_hw, int idx,
- const u8 *addr);
-extern void wlc_bmac_set_addrmatch(struct wlc_hw_info *wlc_hw,
- int match_reg_offset,
- const u8 *addr);
-extern void wlc_bmac_write_hw_bcntemplates(struct wlc_hw_info *wlc_hw,
- void *bcn, int len, bool both);
-
-extern void wlc_bmac_read_tsf(struct wlc_hw_info *wlc_hw, u32 *tsf_l_ptr,
- u32 *tsf_h_ptr);
-extern void wlc_bmac_set_cwmin(struct wlc_hw_info *wlc_hw, u16 newmin);
-extern void wlc_bmac_set_cwmax(struct wlc_hw_info *wlc_hw, u16 newmax);
-extern void wlc_bmac_set_noreset(struct wlc_hw_info *wlc, bool noreset_flag);
-extern void wlc_bmac_set_ucode_loaded(struct wlc_hw_info *wlc,
- bool ucode_loaded);
-
-extern void wlc_bmac_retrylimit_upd(struct wlc_hw_info *wlc_hw, u16 SRL,
- u16 LRL);
-
-extern void wlc_bmac_fifoerrors(struct wlc_hw_info *wlc_hw);
-
-
-/* API for BMAC driver (e.g. wlc_phy.c etc) */
-
-extern void wlc_bmac_bw_set(struct wlc_hw_info *wlc_hw, u16 bw);
-extern void wlc_bmac_pllreq(struct wlc_hw_info *wlc_hw, bool set,
- mbool req_bit);
-extern void wlc_bmac_set_clk(struct wlc_hw_info *wlc_hw, bool on);
-extern bool wlc_bmac_taclear(struct wlc_hw_info *wlc_hw, bool ta_ok);
-extern void wlc_bmac_hw_up(struct wlc_hw_info *wlc_hw);
-
-extern void wlc_bmac_dump(struct wlc_hw_info *wlc_hw, struct bcmstrbuf *b,
- wlc_bmac_dump_id_t dump_id);
-extern void wlc_gpio_fast_deinit(struct wlc_hw_info *wlc_hw);
-
-extern bool wlc_bmac_radio_hw(struct wlc_hw_info *wlc_hw, bool enable);
-extern u16 wlc_bmac_rate_shm_offset(struct wlc_hw_info *wlc_hw, u8 rate);
-
-extern void wlc_bmac_assert_type_set(struct wlc_hw_info *wlc_hw, u32 type);
-extern void wlc_bmac_set_txpwr_percent(struct wlc_hw_info *wlc_hw, u8 val);
-extern void wlc_bmac_blink_sync(struct wlc_hw_info *wlc_hw, u32 led_pins);
-extern void wlc_bmac_ifsctl_edcrs_set(struct wlc_hw_info *wlc_hw, bool abie,
- bool isht);
-
-extern void wlc_bmac_antsel_set(struct wlc_hw_info *wlc_hw, u32 antsel_avail);
+++ /dev/null
-/*
- * 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:
- (struct wlc_info *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[IEEE80211_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 */
-
- u8 BSSID[ETH_ALEN]; /* BSSID (associated) */
- u8 cur_etheraddr[ETH_ALEN]; /* 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_ */
+++ /dev/null
-/*
- * 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
-
-/* **** 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
- */
-
-
-/* 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_ */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <bcmutils.h>
-#include <siutils.h>
-#include <sbhndpio.h>
-#include <sbhnddma.h>
-#include <wlioctl.h>
-#include <wlc_pub.h>
-#include <wlc_key.h>
-#include <wlc_event.h>
-#include <wlc_mac80211.h>
-#include <wlc_bmac.h>
-#include <wlc_stf.h>
-#include <wlc_channel.h>
-#include <wl_dbg.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 {
- struct wlc_pub *pub;
- struct wlc_info *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;
-
- memset(channels, 0, 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(struct wlc_info *wlc)
-{
- wlc_cm_info_t *wlc_cm;
- char country_abbrev[WLC_CNTRY_BUF_SZ];
- const country_info_t *country;
- struct wlc_pub *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 */
- memset(country_abbrev, 0, 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)
-{
- struct wlc_info *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;
- struct wlc_info *wlc = wlc_cm->wlc;
- char prev_country_abbrev[WLC_CNTRY_BUF_SZ];
-
- ASSERT(country != NULL);
-
- /* save current country state */
- wlc_cm->country = country;
-
- memset(&prev_country_abbrev, 0, 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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *wlc = wlc_cm->wlc;
- uint i, j;
- struct wlcband *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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *wlc = wlc_cm->wlc;
- uint i, j;
- struct wlcband *band;
- const chanvec_t *chanvec;
-
- memset(&wlc_cm->quiet_channels, 0, 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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *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)
-{
- struct wlc_info *wlc = wlc_cm->wlc;
- uint i;
- uint chan;
- int maxpwr;
- int delta;
- const country_info_t *country;
- struct wlcband *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;
-
- memset(txpwr, 0, 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)
-{
- struct wlc_info *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);
-}
+++ /dev/null
-/*
- * 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 */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <osl.h>
-#include <bcmutils.h>
-#include <siutils.h>
-#include <sbhndpio.h>
-#include <sbhnddma.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>
-#include <wl_dbg.h>
-
-/* 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;
- struct wlc_pub *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(struct wlc_pub *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;
-}
+++ /dev/null
-/*
- * 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(struct wlc_pub *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(struct wlc_info *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_ */
+++ /dev/null
-/*
- * 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 {
- u8 ea[ETH_ALEN]; /* 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_ */
+++ /dev/null
-/*
- * 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 <linux/etherdevice.h>
-#include <bcmdefs.h>
-#include <bcmdevs.h>
-#include <wlc_cfg.h>
-#include <osl.h>
-#include <bcmutils.h>
-#include <bcmwifi.h>
-#include <siutils.h>
-#include <bcmendian.h>
-#include <pcicfg.h>
-#include <bcmsrom.h>
-#include <wlioctl.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_key.h>
-#include <wlc_bsscfg.h>
-#include <wlc_channel.h>
-#include <wlc_event.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>
-#include "d11ucode_ext.h"
-#include <wlc_alloc.h>
-#include <net/mac80211.h>
-#include <wl_dbg.h>
-
-
-/*
- * WPA(2) definitions
- */
-#define RSN_CAP_4_REPLAY_CNTRS 2
-#define RSN_CAP_16_REPLAY_CNTRS 3
-
-#define WPA_CAP_4_REPLAY_CNTRS RSN_CAP_4_REPLAY_CNTRS
-#define WPA_CAP_16_REPLAY_CNTRS RSN_CAP_16_REPLAY_CNTRS
-
-/*
- * 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 * IEEE80211_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))
-
-#define WLC_WAR16165(wlc) (wlc->pub->sih->bustype == PCI_BUS && \
- (!AP_ENAB(wlc->pub)) && (wlc->war16165))
-
-/* 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
-
-#define EPI_VERSION_NUM 0x054b0b00
-
-#ifdef BCMDBG
-/* pointer to most recently allocated wl/wlc */
-static struct wlc_info *wlc_info_dbg = (struct wlc_info *) (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 */
-static u16 BCMFASTPATH wlc_d11hdrs_mac80211(struct wlc_info *wlc,
- struct ieee80211_hw *hw,
- struct sk_buff *p,
- struct scb *scb, uint frag,
- uint nfrags, uint queue,
- uint next_frag_len,
- wsec_key_t *key,
- ratespec_t rspec_override);
-
-static void wlc_bss_default_init(struct wlc_info *wlc);
-static void wlc_ucode_mac_upd(struct wlc_info *wlc);
-static ratespec_t mac80211_wlc_set_nrate(struct wlc_info *wlc,
- struct wlcband *cur_band, u32 int_val);
-static void wlc_tx_prec_map_init(struct wlc_info *wlc);
-static void wlc_watchdog(void *arg);
-static void wlc_watchdog_by_timer(void *arg);
-static int wlc_set_rateset(struct wlc_info *wlc, wlc_rateset_t *rs_arg);
-static int wlc_iovar_rangecheck(struct wlc_info *wlc, u32 val,
- const bcm_iovar_t *vi);
-static u8 wlc_local_constraint_qdbm(struct wlc_info *wlc);
-
-/* send and receive */
-static wlc_txq_info_t *wlc_txq_alloc(struct wlc_info *wlc,
- struct osl_info *osh);
-static void wlc_txq_free(struct wlc_info *wlc, struct osl_info *osh,
- wlc_txq_info_t *qi);
-static void wlc_txflowcontrol_signal(struct wlc_info *wlc, wlc_txq_info_t *qi,
- bool on, int prio);
-static void wlc_txflowcontrol_reset(struct wlc_info *wlc);
-static u16 wlc_compute_airtime(struct wlc_info *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(struct wlc_info *wlc, ratespec_t rate,
- u8 preamble_type, uint next_frag_len);
-static void wlc_recvctl(struct wlc_info *wlc, struct osl_info *osh,
- d11rxhdr_t *rxh, struct sk_buff *p);
-static uint wlc_calc_frame_len(struct wlc_info *wlc, ratespec_t rate,
- u8 preamble_type, uint dur);
-static uint wlc_calc_ack_time(struct wlc_info *wlc, ratespec_t rate,
- u8 preamble_type);
-static uint wlc_calc_cts_time(struct wlc_info *wlc, ratespec_t rate,
- u8 preamble_type);
-/* interrupt, up/down, band */
-static void wlc_setband(struct wlc_info *wlc, uint bandunit);
-static chanspec_t wlc_init_chanspec(struct wlc_info *wlc);
-static void wlc_bandinit_ordered(struct wlc_info *wlc, chanspec_t chanspec);
-static void wlc_bsinit(struct wlc_info *wlc);
-static int wlc_duty_cycle_set(struct wlc_info *wlc, int duty_cycle, bool isOFDM,
- bool writeToShm);
-static void wlc_radio_hwdisable_upd(struct wlc_info *wlc);
-static bool wlc_radio_monitor_start(struct wlc_info *wlc);
-static void wlc_radio_timer(void *arg);
-static void wlc_radio_enable(struct wlc_info *wlc);
-static void wlc_radio_upd(struct wlc_info *wlc);
-
-/* scan, association, BSS */
-static uint wlc_calc_ba_time(struct wlc_info *wlc, ratespec_t rate,
- u8 preamble_type);
-static void wlc_update_mimo_band_bwcap(struct wlc_info *wlc, u8 bwcap);
-static void wlc_ht_update_sgi_rx(struct wlc_info *wlc, int val);
-static void wlc_ht_update_ldpc(struct wlc_info *wlc, s8 val);
-static void wlc_war16165(struct wlc_info *wlc, bool tx);
-
-static void wlc_process_eventq(void *arg);
-static void wlc_wme_retries_write(struct wlc_info *wlc);
-static bool wlc_attach_stf_ant_init(struct wlc_info *wlc);
-static uint wlc_attach_module(struct wlc_info *wlc);
-static void wlc_detach_module(struct wlc_info *wlc);
-static void wlc_timers_deinit(struct wlc_info *wlc);
-static void wlc_down_led_upd(struct wlc_info *wlc);
-static uint wlc_down_del_timer(struct wlc_info *wlc);
-static void wlc_ofdm_rateset_war(struct wlc_info *wlc);
-static int _wlc_ioctl(struct wlc_info *wlc, int cmd, void *arg, int len,
- struct wlc_if *wlcif);
-
-#if defined(BCMDBG)
-void wlc_get_rcmta(struct wlc_info *wlc, int idx, u8 *addr)
-{
- d11regs_t *regs = wlc->regs;
- u32 v32;
- struct osl_info *osh;
-
- WL_TRACE("wl%d: %s\n", WLCWLUNIT(wlc), __func__);
-
- ASSERT(wlc->pub->corerev > 4);
-
- osh = wlc->osh;
-
- W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
- (void)R_REG(osh, ®s->objaddr);
- v32 = R_REG(osh, ®s->objdata);
- addr[0] = (u8) v32;
- addr[1] = (u8) (v32 >> 8);
- addr[2] = (u8) (v32 >> 16);
- addr[3] = (u8) (v32 >> 24);
- W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
- (void)R_REG(osh, ®s->objaddr);
- v32 = R_REG(osh, (volatile u16 *)®s->objdata);
- addr[4] = (u8) v32;
- addr[5] = (u8) (v32 >> 8);
-}
-#endif /* defined(BCMDBG) */
-
-/* keep the chip awake if needed */
-bool wlc_stay_awake(struct wlc_info *wlc)
-{
- return true;
-}
-
-/* conditions under which the PM bit should be set in outgoing frames and STAY_AWAKE is meaningful
- */
-bool wlc_ps_allowed(struct wlc_info *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(struct wlc_info *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 */
- memset((char *)wlc->core->macstat_snapshot, 0,
- sizeof(macstat_t));
- }
-
- wlc_bmac_reset(wlc->hw);
- wlc_ampdu_reset(wlc->ampdu);
- wlc->txretried = 0;
-
-}
-
-void wlc_fatal_error(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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, ®s->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, ®s->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(struct wlc_info *wlc, bool promisc)
-{
- wlc->bcnmisc_monitor = promisc;
- wlc_mac_bcn_promisc(wlc);
-}
-
-void wlc_mac_bcn_promisc(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
- }
- /* 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));
- 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(struct wlc_info *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;
- struct wlc_info *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)
-{
- struct wlc_info *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(struct wlc_info *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 &=
- ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
- if (wlc->shortslot)
- cfg->current_bss->capability |=
- WLAN_CAPABILITY_SHORT_SLOT_TIME;
- }
-
- wlc_bmac_set_shortslot(wlc->hw, shortslot);
-}
-
-static u8 wlc_local_constraint_qdbm(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
-
- memset(&power, 0, 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;
-
- memset(old_power, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, int val)
-{
- wlc->ht_cap.cap_info &= ~(IEEE80211_HT_CAP_SGI_20 |
- IEEE80211_HT_CAP_SGI_40);
- wlc->ht_cap.cap_info |= (val & WLC_N_SGI_20) ?
- IEEE80211_HT_CAP_SGI_20 : 0;
- wlc->ht_cap.cap_info |= (val & WLC_N_SGI_40) ?
- IEEE80211_HT_CAP_SGI_40 : 0;
-
- if (wlc->pub->up) {
- wlc_update_beacon(wlc);
- wlc_update_probe_resp(wlc, true);
- }
-}
-
-static void wlc_ht_update_ldpc(struct wlc_info *wlc, s8 val)
-{
- wlc->stf->ldpc = val;
-
- wlc->ht_cap.cap_info &= ~IEEE80211_HT_CAP_LDPC_CODING;
- if (wlc->stf->ldpc != OFF)
- wlc->ht_cap.cap_info |= IEEE80211_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(struct wlc_info *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(struct wlc_info *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) (struct wlc_info *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) (struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 {
- memset((char *)&acp_shm, 0, 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)
-{
- struct wlc_info *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++) {
- memset((char *)&acp_shm, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
-}
-
-struct wlc_pub *wlc_pub(void *wlc)
-{
- return ((struct wlc_info *) 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,
- struct osl_info *osh, void *regsva, uint bustype,
- void *btparam, uint *perr)
-{
- struct wlc_info *wlc;
- uint err = 0;
- uint j;
- struct wlc_pub *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 ethhdr) == ETH_HLEN);
- 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 ieee80211_hdr) == DOT11_A4_HDR_LEN);
- ASSERT(sizeof(struct ieee80211_rts) == DOT11_RTS_LEN);
- ASSERT(sizeof(tx_status_t) == TXSTATUS_LEN);
- ASSERT(sizeof(struct ieee80211_ht_cap) == HT_CAP_IE_LEN);
-#ifdef BRCM_FULLMAC
- ASSERT(offsetof(wl_scan_params_t, channel_list) ==
- WL_SCAN_PARAMS_FIXED_SIZE);
-#endif
- 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 struct wlc_info state and its substructures */
- wlc = (struct wlc_info *) 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 struct wlc_info 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);
-
-
- /* 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);
-
- /* 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];
- }
-
- wlc_bmac_hw_etheraddr(wlc->hw, wlc->perm_etheraddr);
-
- bcopy((char *)&wlc->perm_etheraddr, (char *)&pub->cur_etheraddr,
- ETH_ALEN);
-
- 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_info = 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_info &= ~IEEE80211_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_info &= ~IEEE80211_HT_CAP_GRN_FLD;
-
- /* initialize radio_mpc_disable according to wlc->mpc */
- wlc_radio_mpc_upd(wlc);
-
- if (WLANTSEL_ENAB(wlc)) {
- if ((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(struct wlc_info *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(struct wlc_info *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;
-}
-
-
-static void wlc_timers_deinit(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 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);
-
- 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
-{
- return false;
-}
-
-bool wlc_ismpc(struct wlc_info *wlc)
-{
- return (wlc->mpc_delay_off == 0) && (wlc_is_non_delay_mpc(wlc));
-}
-
-void wlc_radio_mpc_upd(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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;
-
- wlc_bmac_watchdog(wlc);
-
- /* 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);
- }
- /* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
- ASSERT(wlc_bmac_taclear(wlc->hw, true));
-
- /* 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(struct wlc_info *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)
- && (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(struct wlc_info *wlc)
-{
- wlc->tx_prec_map = WLC_PREC_BMP_ALL;
- memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16));
-
- /* 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(struct wlc_info *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(struct wlc_info *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;
-
-
- /* Verify all packets are flushed from the driver */
- if (wlc->osh->pktalloced != 0) {
- WL_ERROR("%d packets not freed at wlc_down!!!!!!\n",
- wlc->osh->pktalloced);
- }
-#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(struct wlc_info *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
- */
- struct wlcband *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 */
- memset(&wlc->sup_rates_override, 0, sizeof(wlc_rateset_t));
-
- /* Clear rateset override */
- memset(&rs, 0, 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 |= WLAN_CAPABILITY_SHORT_PREAMBLE;
- else
- wlc->default_bss->capability &= ~WLAN_CAPABILITY_SHORT_PREAMBLE;
-
- /* 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 |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
- else
- wlc->default_bss->capability &=
- ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
-
- /* 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, int cmd, int *arg)
-{
- return wlc_ioctl(wlc, cmd, arg, sizeof(int), NULL);
-}
-
-static void wlc_ofdm_rateset_war(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
- struct osl_info *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);
- }
- 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;
- }
-
- memset((char *)&key, 0, 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, key.ea,
- ETH_ALEN);
- }
-
- 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) &&
- is_zero_ether_addr(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 = ¤t_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;
-
- memset(&rs, 0, 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;
- }
-
- memset(&rs, 0, 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(¤t_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) {
- memset(&wlc->sup_rates_override, 0,
- 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;
- }
-
- }
- /* 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);
-
- return bcmerror;
-}
-
-#if defined(BCMDBG)
-/* consolidated register access ioctl error checking */
-int wlc_iocregchk(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_pub *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)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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(struct wlc_pub *pub, const char *name, void *hdl)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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)) {
- memset(&wlc->modulecb[i], 0, 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(struct wlc_info *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(struct wlc_info *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;
- 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(struct wlc_pub *pub, const bcm_iovar_t *vi, void *arg, int len,
- bool set)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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)
-{
- struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 ieee80211_rts 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 > IEEE80211_MAX_SSID_LEN)
- ssid_len = IEEE80211_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(struct wlc_info *wlc, u8 rate)
-{
- return wlc_bmac_rate_shm_offset(wlc->hw, rate);
-}
-
-/* Callback for device removed */
-
-/*
- * 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(struct wlc_info *wlc, struct pktq *q, void *pkt, int prec)
-{
- return wlc_prec_enq_head(wlc, q, pkt, prec, false);
-}
-
-bool BCMFASTPATH
-wlc_prec_enq_head(struct wlc_info *wlc, struct pktq *q, struct sk_buff *pkt,
- int prec, bool head)
-{
- struct sk_buff *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(p->priority)].packets);
- WLCNTADD(wlc->pub->_wme_cnt->
- tx_failed[WME_PRIO2AC(p->priority)].bytes,
- pkttotlen(wlc->osh, p));
- }
-
- ASSERT(0);
- pkt_buf_free_skb(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, struct sk_buff *sdu,
- uint prec)
-{
- struct wlc_info *wlc = (struct wlc_info *) ctx;
- wlc_txq_info_t *qi = wlc->active_queue; /* Check me */
- struct pktq *q = &qi->q;
- int prio;
-
- prio = sdu->priority;
-
- 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 */
- pkt_buf_free_skb(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(struct wlc_info *wlc, struct sk_buff *sdu,
- struct ieee80211_hw *hw)
-{
- u8 prio;
- uint fifo;
- void *pkt;
- struct scb *scb = &global_scb;
- struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data);
- u16 type, fc;
-
- ASSERT(sdu);
-
- fc = ltoh16(d11_header->frame_control);
- type = (fc & IEEE80211_FCTL_FTYPE);
-
- /* 802.11 standard requires management traffic to go at highest priority */
- prio = (type == IEEE80211_FTYPE_DATA ? sdu->priority : MAXPRIO);
- fifo = prio2fifo[prio];
-
- ASSERT((uint) skb_headroom(sdu) >= TXOFF);
- ASSERT(!(sdu->cloned));
- ASSERT(!(sdu->next));
- ASSERT(!(sdu->prev));
- 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(struct wlc_info *wlc, wlc_txq_info_t *qi)
-{
- struct sk_buff *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(struct wlc_info *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(struct wlc_info *wlc, uint fifo, struct sk_buff *p, bool commit,
- s8 txpktpend)
-{
- u16 frameid = INVALIDFID;
- d11txh_t *txh;
-
- ASSERT(fifo < NFIFO);
- txh = (d11txh_t *) (p->data);
-
- /* 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);
-
-
- /* 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");
- }
-}
-
-static u16
-wlc_compute_airtime(struct wlc_info *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(struct wlc_info *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);
-
- memset(plcp, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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);
- ASSERT(wlc->chanspec == wlc_phy_chanspec_get(wlc->band->pi));
- }
-#endif /* BCMDBG */
- return phyctl1;
-}
-
-ratespec_t BCMFASTPATH
-wlc_rspec_to_rts_rspec(struct wlc_info *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(struct wlc_info *wlc, struct ieee80211_hw *hw,
- struct sk_buff *p, struct scb *scb, uint frag,
- uint nfrags, uint queue, uint next_frag_len,
- wsec_key_t *key, ratespec_t rspec_override)
-{
- struct ieee80211_hdr *h;
- d11txh_t *txh;
- u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN];
- struct osl_info *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 ieee80211_rts *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 ieee80211_hdr *)(p->data);
- fc = ltoh16(h->frame_control);
- type = (fc & IEEE80211_FCTL_FTYPE);
-
- qos = (type == IEEE80211_FTYPE_DATA &&
- FC_SUBTYPE_ANY_QOS(fc));
-
- /* compute length of frame in bytes for use in PLCP computations */
- len = pkttotlen(osh, p);
- phylen = len + 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 = skb_push(p, D11_PHY_HDR_LEN);
-
- /* add Broadcom tx descriptor header */
- txh = (d11txh_t *) skb_push(p, D11_TXH_LEN);
- memset((char *)txh, 0, 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, p->priority)++;
- }
-
- /* extract fragment number from frame first */
- seq = ltoh16(seq) & FRAGNUM_MASK;
- seq |= (SCB_SEQNUM(scb, p->priority) << SEQNUM_SHIFT);
- h->seq_ctrl = 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];
- }
-
- 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) &&
- !is_multicast_ether_addr(h->addr1)) {
- /* 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) &&
- !is_multicast_ether_addr(h->addr1) && !use_rifs) {
- durid =
- wlc_compute_frame_dur(wlc, rspec[0], preamble_type[0],
- next_frag_len);
- h->duration_id = 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->duration_id = htol16(durid);
- }
-
- /* DUR field for fallback rate */
- if (fc == FC_PS_POLL)
- txh->FragDurFallback = h->duration_id;
- else if (is_multicast_ether_addr(h->addr1) || 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 (!is_multicast_ether_addr(h->addr1))
- 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->frame_control, (char *)&txh->MacFrameControl,
- sizeof(u16));
- txh->TxFesTimeNormal = htol16(0);
-
- txh->TxFesTimeFallback = htol16(0);
-
- /* TxFrameRA */
- bcopy((char *)&h->addr1, (char *)&txh->TxFrameRA, ETH_ALEN);
-
- /* 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 + FCS_LEN;
- else
- rts_phylen = DOT11_RTS_LEN + 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 ieee80211_rts *)&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->duration = 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->frame_control = htol16(FC_CTS);
- bcopy((char *)&h->addr2, (char *)&rts->ra, ETH_ALEN);
- } else {
- rts->frame_control = htol16((u16) FC_RTS);
- bcopy((char *)&h->addr1, (char *)&rts->ra,
- 2 * ETH_ALEN);
- }
-
- /* 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 {
- memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
- memset((char *)&txh->rts_frame, 0,
- sizeof(struct ieee80211_rts));
- memset((char *)txh->RTSPLCPFallback, 0,
- 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(!is_multicast_ether_addr(h->addr1));
-
- /* 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->duration);
- 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
-{
- ASSERT(wlc->pub->corerev >= 3);
- W_REG(wlc->osh, &wlc->regs->gptimer, 0);
-}
-
-static void wlc_hwtimer_gptimer_cb(struct wlc_info *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(struct wlc_info *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__, wlc->pub->sih->chip,
- 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, ®s->phydebug) & PDBG_RFD);
- /* delay the cleanup to wl_down in IBSS case */
- if ((R_REG(wlc->osh, ®s->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);
-
- ASSERT(wlc_ps_check(wlc));
-}
-
-static void *wlc_15420war(struct wlc_info *wlc, uint queue)
-{
- struct hnddma_pub *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(struct wlc_info *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(struct wlc_info *wlc, tx_status_t *txs, u32 frm_tx2)
-{
- struct sk_buff *p;
- uint queue;
- d11txh_t *txh;
- struct scb *scb = NULL;
- bool free_pdu;
- struct osl_info *osh;
- int tx_rts, tx_frame_count, tx_rts_count;
- uint totlen, supr_status;
- bool lastframe;
- struct ieee80211_hdr *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 *) (p->data);
- 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 ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
- fc = ltoh16(h->frame_control);
-
- 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 & IEEE80211_FCTL_MOREFRAGS) == 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) {
- p->next = NULL;
- p->prev = NULL;
- wlc->txretried = 0;
- /* remove PLCP & Broadcom tx descriptor header */
- skb_pull(p, D11_PHY_HDR_LEN);
- skb_pull(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)
- pkt_buf_free_skb(osh, p, true);
-
- return true;
-
-}
-
-void BCMFASTPATH
-wlc_txfifo_complete(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, d11rxhdr_t *rxh, struct sk_buff *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 = ieee80211_ofdm_chan_to_freq(
- WF_CHAN_FACTOR_5_G/2, channel);
-
- } else {
- rx_status->band = IEEE80211_BAND_2GHZ;
- rx_status->freq = ieee80211_dsss_chan_to_freq(channel);
- }
-
- rx_status->signal = wlc_rxh->rssi; /* signal */
-
- /* noise */
- /* qual */
- rx_status->antenna = (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; /* ant */
-
- plcp = p->data;
-
- 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(struct wlc_info *wlc, struct osl_info *osh, d11rxhdr_t *rxh,
- struct sk_buff *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 = p->len - D11_PHY_HDR_LEN - FCS_LEN;
- skb_pull(p, D11_PHY_HDR_LEN);
- __skb_trim(p, len_mpdu);
-
- ASSERT(!(p->next));
- ASSERT(!(p->prev));
-
- 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);
- osh->pktalloced--;
- return;
-}
-
-void wlc_bss_list_free(struct wlc_info *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) {
- 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(struct wlc_info *wlc, struct sk_buff *p)
-{
- d11rxhdr_t *rxh;
- struct ieee80211_hdr *h;
- struct osl_info *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 *) (p->data);
-
- /* strip off rxhdr */
- skb_pull(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 (p->len < 2) {
- WLCNTINCR(wlc->pub->_cnt->rxrunt);
- WL_ERROR("wl%d: wlc_recv: rcvd runt of len %d\n",
- wlc->pub->unit, p->len);
- goto toss;
- }
- skb_pull(p, 2);
- }
-
- h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN);
- len = p->len;
-
- 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->frame_control)) {
- fc = ltoh16(h->frame_control);
- } 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 & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
- (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
- if ((is_zero_ether_addr(h->addr2) ||
- is_multicast_ether_addr(h->addr2))) {
- WL_ERROR("wl%d: %s: dropping a frame with "
- "invalid src mac address, a2: %pM\n",
- wlc->pub->unit, __func__, h->addr2);
- WLCNTINCR(wlc->pub->_cnt->rxbadsrcmac);
- goto toss;
- }
- WLCNTINCR(wlc->pub->_cnt->rxfrag);
- }
- }
-
- /* due to sheer numbers, toss out probe reqs for now */
- if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
- 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:
- pkt_buf_free_skb(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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 +
- FCS_LEN));
-}
-
-static uint BCMFASTPATH
-wlc_calc_ack_time(struct wlc_info *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 + FCS_LEN));
- return dur;
-}
-
-static uint
-wlc_calc_cts_time(struct wlc_info *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(struct wlc_info *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 */
- memset(br, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, u8 bwcap)
-{
- uint i;
- struct wlcband *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, uint type, ratespec_t bcn_rspec,
- wlc_bsscfg_t *cfg, u16 *buf, int *len)
-{
- static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
- cck_phy_hdr_t *plcp;
- struct ieee80211_mgmt *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 */
- memset((char *)buf, 0, 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 + 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 ieee80211_mgmt *)&plcp[0];
- else
- h = (struct ieee80211_mgmt *)&plcp[1];
-
- /* fill in 802.11 header */
- h->frame_control = 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 *)ðer_bcast, (char *)&h->da,
- ETH_ALEN);
- bcopy((char *)&cfg->cur_etheraddr, (char *)&h->sa, ETH_ALEN);
- bcopy((char *)&cfg->BSSID, (char *)&h->bssid, ETH_ALEN);
-
- /* 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(struct wlc_info *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;
- struct osl_info *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, ®s->maccommand) & both_valid) == both_valid) {
- /* clear any previous status */
- W_REG(osh, ®s->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, ®s->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(struct wlc_info *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(struct wlc_info *wlc, wlc_bsscfg_t *cfg)
-{
- u8 *ssidptr = cfg->SSID;
- u16 base = M_SSID;
- u8 ssidbuf[IEEE80211_MAX_SSID_LEN];
-
- /* padding the ssid with zero and copy it into shm */
- memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN);
- bcopy(ssidptr, ssidbuf, cfg->SSID_len);
-
- wlc_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN);
-
- if (!MBSS_BCN_ENAB(cfg))
- wlc_write_shm(wlc, M_SSIDLEN, (u16) cfg->SSID_len);
-}
-
-void wlc_update_probe_resp(struct wlc_info *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(struct wlc_info *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 + 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(struct wlc_info *wlc, struct sk_buff *pdu, uint *fifop)
-{
- struct osl_info *osh;
- uint fifo;
- d11txh_t *txh;
- struct ieee80211_hdr *h;
- struct scb *scb;
- u16 fc;
-
- osh = wlc->osh;
-
- ASSERT(pdu);
- txh = (d11txh_t *) (pdu->data);
- ASSERT(txh);
- h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
- ASSERT(h);
- fc = ltoh16(h->frame_control);
-
- /* 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 ((ltoh16(txh->MacFrameControl) & IEEE80211_FCTL_FTYPE) !=
- IEEE80211_FTYPE_DATA)
- WLCNTINCR(wlc->pub->_cnt->txctl);
-
- return 0;
-}
-
-/* init tx reported rate mechanism */
-void wlc_reprate_init(struct wlc_info *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;
- memset((char *)bsscfg->txrspec, 0, sizeof(bsscfg->txrspec));
-}
-
-/* Retrieve a consolidated set of revision information,
- * typically for the WLC_GET_REVINFO ioctl
- */
-int wlc_get_revision_info(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
-{
- chanspec_t chanspec;
- struct wlcband *band;
- wlc_bss_info_t *bi = wlc->default_bss;
-
- /* init default and target BSS with some sane initial values */
- memset((char *)(bi), 0, 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)
-{
- struct wlc_info *wlc = (struct wlc_info *) 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(struct wlc_info *wlc, struct wlcband *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, u32 mask, u32 val)
-{
- wlc_bmac_mctrl(wlc->hw, mask, val);
-}
-
-void wlc_corereset(struct wlc_info *wlc, u32 flags)
-{
- wlc_bmac_corereset(wlc->hw, flags);
-}
-
-void wlc_mhf(struct wlc_info *wlc, u8 idx, u16 mask, u16 val, int bands)
-{
- wlc_bmac_mhf(wlc->hw, idx, mask, val, bands);
-}
-
-u16 wlc_mhf_get(struct wlc_info *wlc, u8 idx, int bands)
-{
- return wlc_bmac_mhf_get(wlc->hw, idx, bands);
-}
-
-int wlc_xmtfifo_sz_get(struct wlc_info *wlc, uint fifo, uint *blocks)
-{
- return wlc_bmac_xmtfifo_sz_get(wlc->hw, fifo, blocks);
-}
-
-void wlc_write_template_ram(struct wlc_info *wlc, int offset, int len,
- void *buf)
-{
- wlc_bmac_write_template_ram(wlc->hw, offset, len, buf);
-}
-
-void wlc_write_hw_bcntemplates(struct wlc_info *wlc, void *bcn, int len,
- bool both)
-{
- wlc_bmac_write_hw_bcntemplates(wlc->hw, bcn, len, both);
-}
-
-void
-wlc_set_addrmatch(struct wlc_info *wlc, int match_reg_offset,
- const u8 *addr)
-{
- wlc_bmac_set_addrmatch(wlc->hw, match_reg_offset, addr);
-}
-
-void wlc_set_rcmta(struct wlc_info *wlc, int idx, const u8 *addr)
-{
- wlc_bmac_set_rcmta(wlc->hw, idx, addr);
-}
-
-void wlc_read_tsf(struct wlc_info *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(struct wlc_info *wlc, u16 newmin)
-{
- wlc->band->CWmin = newmin;
- wlc_bmac_set_cwmin(wlc->hw, newmin);
-}
-
-void wlc_set_cwmax(struct wlc_info *wlc, u16 newmax)
-{
- wlc->band->CWmax = newmax;
- wlc_bmac_set_cwmax(wlc->hw, newmax);
-}
-
-void wlc_fifoerrors(struct wlc_info *wlc)
-{
-
- wlc_bmac_fifoerrors(wlc->hw);
-}
-
-/* Search mem rw utilities */
-
-void wlc_pllreq(struct wlc_info *wlc, bool set, mbool req_bit)
-{
- wlc_bmac_pllreq(wlc->hw, set, req_bit);
-}
-
-void wlc_reset_bmac_done(struct wlc_info *wlc)
-{
-}
-
-void wlc_ht_mimops_cap_update(struct wlc_info *wlc, u8 mimops_mode)
-{
- wlc->ht_cap.cap_info &= ~HT_CAP_MIMO_PS_MASK;
- wlc->ht_cap.cap_info |= (mimops_mode << IEEE80211_HT_CAP_SM_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(struct wlc_info *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(struct wlc_info *wlc, wlc_txq_info_t *qi,
- bool on, int prio)
-{
- uint prio_bits;
- uint cur_bits;
-
- WL_ERROR("%s: flow control 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, wlc_txq_info_t *qi, bool on,
- int prio)
-{
- struct wlc_if *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(struct wlc_info *wlc, struct osl_info *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(struct wlc_info *wlc, struct osl_info *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);
-}
+++ /dev/null
-/*
- * 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 <wlioctl.h>
-#include <wlc_phy_hal.h>
-#include <wlc_channel.h>
-#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.
- */
-#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)))
-
-#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)
- */
-struct wlccore {
- uint coreidx; /* # sb enumerated core */
-
- /* fifo */
- uint *txavail[NFIFO]; /* # tx descriptors available */
- s16 txpktpend[NFIFO]; /* tx admission control */
-
- macstat_t *macstat_snapshot; /* mac hw prev read values */
-};
-
-/*
- * band state (phy+ana+radio)
- */
-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 */
-};
-
-/* generic function callback takes just one arg */
-typedef void (*cb_fn_t) (void *);
-
-/* tx completion callback takes 3 args */
-typedef void (*pkcb_fn_t) (struct wlc_info *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 {
- struct wlc_if *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 */
- struct wl_if *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 */
-
-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;
-
-struct wlc_hw_info {
- struct osl_info *osh; /* pointer to os handle */
- bool _piomode; /* true if pio mode */
- struct wlc_info *wlc;
-
- /* fifo */
- struct hnddma_pub *di[NFIFO]; /* hnddma handles, per fifo */
-
- 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 */
- u8 etheraddr[ETH_ALEN]; /* 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 */
- struct bmac_pmq *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 */
-
- bool ucode_loaded; /* true after ucode downloaded */
-
-
- 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 struct antsel_info here if more info is
- * needed
- */
-};
-
-/* 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 {
- struct wlc_pub *pub; /* pointer to wlc public state */
- struct osl_info *osh; /* pointer to os handle */
- struct wl_info *wl; /* pointer to os-specific private state */
- d11regs_t *regs; /* pointer to device registers */
-
- struct wlc_hw_info *hw; /* HW related state used primarily by BMAC */
-
- /* 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 */
- struct wlccore *core; /* pointer to active io core */
- struct wlcband *band; /* pointer to active per-band state */
- struct wlccore *corestate; /* per-core state (one per hw core) */
- /* per-band state (one per phy/radio): */
- struct wlcband *bandstate[MAXBANDS];
-
- 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 */
-
-
- struct ampdu_info *ampdu; /* ampdu module handler */
- struct antsel_info *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 */
-
- u8 perm_etheraddr[ETH_ALEN]; /* 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 being advertised by this node: */
- struct ieee80211_ht_cap ht_cap;
-
- 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
- */
- 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;
-
- struct wlc_if *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 {
- struct wlc_info *wlc; /* pointer to main wlc structure */
- struct wlc_pub *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)
-
-#define WLC_BAND_PI_RADIO_CHANSPEC wlc_phy_chanspec_get(wlc->band->pi)
-
-/* sum the individual fifo tx pending packet counts */
-#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)
-
-#define WLC_IS_MATCH_SSID(wlc, ssid1, ssid2, len1, len2) \
- ((len1 == len2) && !memcmp(ssid1, ssid2, len1))
-
-extern void wlc_high_dpc(struct wlc_info *wlc, u32 macintstatus);
-extern void wlc_fatal_error(struct wlc_info *wlc);
-extern void wlc_bmac_rpc_watchdog(struct wlc_info *wlc);
-extern void wlc_recv(struct wlc_info *wlc, struct sk_buff *p);
-extern bool wlc_dotxstatus(struct wlc_info *wlc, tx_status_t *txs, u32 frm_tx2);
-extern void wlc_txfifo(struct wlc_info *wlc, uint fifo, struct sk_buff *p,
- bool commit, s8 txpktpend);
-extern void wlc_txfifo_complete(struct wlc_info *wlc, uint fifo, s8 txpktpend);
-extern void wlc_txq_enq(void *ctx, struct scb *scb, struct sk_buff *sdu,
- uint prec);
-extern void wlc_info_init(struct wlc_info *wlc, int unit);
-extern void wlc_print_txstatus(tx_status_t *txs);
-extern int wlc_xmtfifo_sz_get(struct wlc_info *wlc, uint fifo, uint *blocks);
-extern void wlc_write_template_ram(struct wlc_info *wlc, int offset, int len,
- void *buf);
-extern void wlc_write_hw_bcntemplates(struct wlc_info *wlc, void *bcn, int len,
- bool both);
-#if defined(BCMDBG)
-extern void wlc_get_rcmta(struct wlc_info *wlc, int idx,
- u8 *addr);
-#endif
-extern void wlc_set_rcmta(struct wlc_info *wlc, int idx,
- const u8 *addr);
-extern void wlc_set_addrmatch(struct wlc_info *wlc, int match_reg_offset,
- const u8 *addr);
-extern void wlc_read_tsf(struct wlc_info *wlc, u32 *tsf_l_ptr,
- u32 *tsf_h_ptr);
-extern void wlc_set_cwmin(struct wlc_info *wlc, u16 newmin);
-extern void wlc_set_cwmax(struct wlc_info *wlc, u16 newmax);
-extern void wlc_fifoerrors(struct wlc_info *wlc);
-extern void wlc_pllreq(struct wlc_info *wlc, bool set, mbool req_bit);
-extern void wlc_reset_bmac_done(struct wlc_info *wlc);
-extern void wlc_protection_upd(struct wlc_info *wlc, uint idx, int val);
-extern void wlc_hwtimer_gptimer_set(struct wlc_info *wlc, uint us);
-extern void wlc_hwtimer_gptimer_abort(struct wlc_info *wlc);
-
-#if defined(BCMDBG)
-extern void wlc_print_rxh(d11rxhdr_t *rxh);
-extern void wlc_print_hdrs(struct wlc_info *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
-
-extern void wlc_setxband(struct wlc_hw_info *wlc_hw, uint bandunit);
-extern void wlc_coredisable(struct wlc_hw_info *wlc_hw);
-
-extern bool wlc_valid_rate(struct wlc_info *wlc, ratespec_t rate, int band,
- bool verbose);
-extern void wlc_ap_upd(struct wlc_info *wlc);
-
-/* helper functions */
-extern void wlc_shm_ssid_upd(struct wlc_info *wlc, wlc_bsscfg_t *cfg);
-extern int wlc_set_gmode(struct wlc_info *wlc, u8 gmode, bool config);
-
-extern void wlc_mac_bcn_promisc_change(struct wlc_info *wlc, bool promisc);
-extern void wlc_mac_bcn_promisc(struct wlc_info *wlc);
-extern void wlc_mac_promisc(struct wlc_info *wlc);
-extern void wlc_txflowcontrol(struct wlc_info *wlc, wlc_txq_info_t *qi, bool on,
- int prio);
-extern void wlc_txflowcontrol_override(struct wlc_info *wlc, wlc_txq_info_t *qi,
- bool on, uint override);
-extern bool wlc_txflowcontrol_prio_isset(struct wlc_info *wlc,
- wlc_txq_info_t *qi, int prio);
-extern void wlc_send_q(struct wlc_info *wlc, wlc_txq_info_t *qi);
-extern int wlc_prep_pdu(struct wlc_info *wlc, struct sk_buff *pdu, uint *fifo);
-
-extern u16 wlc_calc_lsig_len(struct wlc_info *wlc, ratespec_t ratespec,
- uint mac_len);
-extern ratespec_t wlc_rspec_to_rts_rspec(struct wlc_info *wlc, ratespec_t rspec,
- bool use_rspec, u16 mimo_ctlchbw);
-extern u16 wlc_compute_rtscts_dur(struct wlc_info *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(struct wlc_info *wlc, d11regs_t *regs);
-
-#if defined(BCMDBG)
-extern void wlc_dump_ie(struct wlc_info *wlc, bcm_tlv_t *ie,
- struct bcmstrbuf *b);
-#endif
-
-extern bool wlc_ps_check(struct wlc_info *wlc);
-extern void wlc_reprate_init(struct wlc_info *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(struct wlc_info *wlc, uint offset, u16 v);
-extern u16 wlc_read_shm(struct wlc_info *wlc, uint offset);
-extern void wlc_set_shm(struct wlc_info *wlc, uint offset, u16 v, int len);
-extern void wlc_copyto_shm(struct wlc_info *wlc, uint offset, const void *buf,
- int len);
-extern void wlc_copyfrom_shm(struct wlc_info *wlc, uint offset, void *buf,
- int len);
-
-extern void wlc_update_beacon(struct wlc_info *wlc);
-extern void wlc_bss_update_beacon(struct wlc_info *wlc,
- struct wlc_bsscfg *bsscfg);
-
-extern void wlc_update_probe_resp(struct wlc_info *wlc, bool suspend);
-extern void wlc_bss_update_probe_resp(struct wlc_info *wlc, wlc_bsscfg_t *cfg,
- bool suspend);
-
-extern bool wlc_ismpc(struct wlc_info *wlc);
-extern bool wlc_is_non_delay_mpc(struct wlc_info *wlc);
-extern void wlc_radio_mpc_upd(struct wlc_info *wlc);
-extern bool wlc_prec_enq(struct wlc_info *wlc, struct pktq *q, void *pkt,
- int prec);
-extern bool wlc_prec_enq_head(struct wlc_info *wlc, struct pktq *q,
- struct sk_buff *pkt, int prec, bool head);
-extern u16 wlc_phytxctl1_calc(struct wlc_info *wlc, ratespec_t rspec);
-extern void wlc_compute_plcp(struct wlc_info *wlc, ratespec_t rate, uint length,
- u8 *plcp);
-extern uint wlc_calc_frame_time(struct wlc_info *wlc, ratespec_t ratespec,
- u8 preamble_type, uint mac_len);
-
-extern void wlc_set_chanspec(struct wlc_info *wlc, chanspec_t chanspec);
-
-extern bool wlc_timers_init(struct wlc_info *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, struct wlc_if *wlcif);
-
-#if defined(BCMDBG)
-extern void wlc_print_ies(struct wlc_info *wlc, u8 *ies, uint ies_len);
-#endif
-
-extern int wlc_set_nmode(struct wlc_info *wlc, s32 nmode);
-extern void wlc_ht_mimops_cap_update(struct wlc_info *wlc, u8 mimops_mode);
-extern void wlc_mimops_action_ht_send(struct wlc_info *wlc,
- wlc_bsscfg_t *bsscfg, u8 mimops_mode);
-
-extern void wlc_switch_shortslot(struct wlc_info *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_set_ratetable(struct wlc_info *wlc);
-extern int wlc_set_mac(wlc_bsscfg_t *cfg);
-extern void wlc_beacon_phytxctl_txant_upd(struct wlc_info *wlc,
- ratespec_t bcn_rate);
-extern void wlc_mod_prb_rsp_rate_table(struct wlc_info *wlc, uint frame_len);
-extern ratespec_t wlc_lowest_basic_rspec(struct wlc_info *wlc,
- wlc_rateset_t *rs);
-extern u16 wlc_compute_bcntsfoff(struct wlc_info *wlc, ratespec_t rspec,
- bool short_preamble, bool phydelay);
-extern void wlc_radio_disable(struct wlc_info *wlc);
-extern void wlc_bcn_li_upd(struct wlc_info *wlc);
-
-extern int wlc_get_revision_info(struct wlc_info *wlc, void *buf, uint len);
-extern void wlc_out(struct wlc_info *wlc);
-extern void wlc_set_home_chanspec(struct wlc_info *wlc, chanspec_t chanspec);
-extern void wlc_watchdog_upd(struct wlc_info *wlc, bool tbtt);
-extern bool wlc_ps_allowed(struct wlc_info *wlc);
-extern bool wlc_stay_awake(struct wlc_info *wlc);
-extern void wlc_wme_initparams_sta(struct wlc_info *wlc, wme_param_ie_t *pe);
-
-extern void wlc_bss_list_free(struct wlc_info *wlc, wlc_bss_list_t *bss_list);
-extern void wlc_ht_mimops_cap_update(struct wlc_info *wlc, u8 mimops_mode);
-#endif /* _wlc_h_ */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <linux/pci.h>
-#include <osl.h>
-#include <bcmutils.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_event.h>
-
-#include <wlc_mac80211.h>
-
-#include <wlc_bmac.h>
-#include <wlc_phy_shim.h>
-#include <wlc_phy_hal.h>
-#include <wl_export.h>
-#include <wl_dbg.h>
-
-/* PHY SHIM module specific state */
-struct wlc_phy_shim_info {
- struct wlc_hw_info *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(struct wlc_hw_info *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);
-}
+++ /dev/null
-/*
- * 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_ */
+++ /dev/null
-/*
- * 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 * IEEE80211_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 << IEEE80211_HT_CAP_SM_PS_SHIFT) |\
- IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD |\
- HT_CAP_MAX_AMSDU | IEEE80211_HT_CAP_DSSSCCK40)
-
-/* 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 {
- u8 BSSID[ETH_ALEN]; /* 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 */
- 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.
- */
-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 */
- struct osl_info *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 */
-
- u8 cur_etheraddr[ETH_ALEN]; /* our local ethernet address */
-
- u8 *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.
- */
-
- 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 */
-};
-
-/* 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) ((wlc)->monitor)
-
-#define PROMISC_ENAB(wlc) ((wlc)->promisc)
-
-#define WLC_PREC_COUNT 16 /* Max precedence level implemented */
-
-/* pri is priority 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, struct osl_info *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, struct wlc_if *wlcif,
- void *p, uint ac);
-extern bool wlc_sendpkt_mac80211(struct wlc_info *wlc, struct sk_buff *sdu,
- struct ieee80211_hw *hw);
-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 void wlc_mac_bcn_promisc_change(struct wlc_info *wlc, bool promisc);
-extern void wlc_set_addrmatch(struct wlc_info *wlc, int match_reg_offset,
- const u8 *addr);
-extern void wlc_wme_setparams(struct wlc_info *wlc, u16 aci, void *arg,
- bool suspend);
-
-extern struct wlc_pub *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(struct wlc_pub *pub, const bcm_iovar_t *vi,
- void *arg,
- int len, bool set);
-
-extern int wlc_module_register(struct wlc_pub *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(struct wlc_pub *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"
-
-/* BMAC RPC: 7 u32 params: pkttotlen, fifo, commit, fid, txpktpend, pktflag, rpc_id */
-#define WLC_RPCTX_PARAMS 32
-
-#endif /* _wlc_pub_h_ */
+++ /dev/null
-/*
- * 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 <linux/module.h>
-#include <bcmutils.h>
-#include <siutils.h>
-#include <bcmendian.h>
-#include <wlioctl.h>
-
-#include <sbhndpio.h>
-#include <sbhnddma.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;
-
- memset(rateset, 0, 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);
-}
+++ /dev/null
-/*
- * 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_ */
+++ /dev/null
-/*
- * 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(struct wlc_info *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 */
- u8 ea[ETH_ALEN]; /* 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_ */
+++ /dev/null
-/*
- * 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/module.h>
-#include <wlc_cfg.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 <sbhndpio.h>
-#include <sbhnddma.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_event.h>
-#include <wlc_mac80211.h>
-#include <wlc_scb.h>
-#include <wl_export.h>
-#include <wlc_bmac.h>
-#include <wlc_stf.h>
-#include <wl_dbg.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(struct wlc_info *wlc);
-static bool wlc_stf_stbc_tx_set(struct wlc_info *wlc, s32 int_val);
-static int wlc_stf_txcore_set(struct wlc_info *wlc, u8 Nsts, u8 val);
-static int wlc_stf_spatial_policy_set(struct wlc_info *wlc, int val);
-static void wlc_stf_stbc_rx_ht_update(struct wlc_info *wlc, int val);
-
-static void _wlc_stf_phy_txant_upd(struct wlc_info *wlc);
-static u16 _wlc_stf_phytxchain_sel(struct wlc_info *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(struct wlc_info *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_info &= ~HT_CAP_RX_STBC_MASK;
- wlc->ht_cap.cap_info |= (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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
-{
- return (wlc->ht_cap.cap_info & HT_CAP_RX_STBC_MASK)
- >> HT_CAP_RX_STBC_SHIFT;
-}
-
-static bool wlc_stf_stbc_tx_set(struct wlc_info *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_info &= ~IEEE80211_HT_CAP_TX_STBC;
- else
- wlc->ht_cap.cap_info |= IEEE80211_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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, struct wlcband *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(struct wlc_info *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(struct wlc_info *wlc)
-{
-}
-
-int wlc_stf_ant_txant_validate(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
-{
- _wlc_stf_phy_txant_upd(wlc);
-}
-
-void wlc_stf_phy_chain_calc(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, ratespec_t rspec)
-{
- return _wlc_stf_phytxchain_sel(wlc, rspec);
-}
-
-u16 wlc_stf_d11hdrs_phyctl_txant(struct wlc_info *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;
-}
+++ /dev/null
-/*
- * 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(struct wlc_info *wlc);
-extern void wlc_stf_detach(struct wlc_info *wlc);
-
-extern void wlc_tempsense_upd(struct wlc_info *wlc);
-extern void wlc_stf_ss_algo_channel_get(struct wlc_info *wlc,
- u16 *ss_algo_channel,
- chanspec_t chanspec);
-extern int wlc_stf_ss_update(struct wlc_info *wlc, struct wlcband *band);
-extern void wlc_stf_phy_txant_upd(struct wlc_info *wlc);
-extern int wlc_stf_txchain_set(struct wlc_info *wlc, s32 int_val, bool force);
-extern int wlc_stf_rxchain_set(struct wlc_info *wlc, s32 int_val);
-extern bool wlc_stf_stbc_rx_set(struct wlc_info *wlc, s32 int_val);
-
-extern int wlc_stf_ant_txant_validate(struct wlc_info *wlc, s8 val);
-extern void wlc_stf_phy_txant_upd(struct wlc_info *wlc);
-extern void wlc_stf_phy_chain_calc(struct wlc_info *wlc);
-extern u16 wlc_stf_phytxchain_sel(struct wlc_info *wlc, ratespec_t rspec);
-extern u16 wlc_stf_d11hdrs_phyctl_txant(struct wlc_info *wlc, ratespec_t rspec);
-extern u16 wlc_stf_spatial_expansion_get(struct wlc_info *wlc,
- ratespec_t rspec);
-#endif /* _wlc_stf_h_ */
+++ /dev/null
-/*
- * 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 */
-
-struct wlc_info;
-struct wlc_hw_info;
-struct wlc_if;
-struct wl_if;
-struct ampdu_info;
-struct antsel_info;
-struct bmac_pmq;
-
-struct d11init;
-
-#ifndef _hnddma_pub_
-#define _hnddma_pub_
-struct hnddma_pub;
-#endif /* _hnddma_pub_ */
-
-#endif /* _wlc_types_h_ */
--- /dev/null
+/*
+ * 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_NONE(fmt, args...) no_printk(fmt, ##args)
+
+#define WL_PRINT(level, fmt, args...) \
+do { \
+ if (wl_msg_level & level) \
+ printk(fmt, ##args); \
+} while (0)
+
+#ifdef BCMDBG
+
+#define WL_ERROR(fmt, args...) WL_PRINT(WL_ERROR_VAL, fmt, ##args)
+#define WL_TRACE(fmt, args...) WL_PRINT(WL_TRACE_VAL, fmt, ##args)
+#define WL_AMPDU(fmt, args...) WL_PRINT(WL_AMPDU_VAL, fmt, ##args)
+#define WL_FFPLD(fmt, args...) WL_PRINT(WL_FFPLD_VAL, fmt, ##args)
+
+#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_PRINT(level, fmt, args...) \
+do { \
+ if (wl_ampdu_dbg & level) { \
+ WL_AMPDU(fmt, ##args); \
+ } \
+} while (0)
+
+#define WL_AMPDU_UPDN(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_UPDN_VAL, fmt, ##args)
+#define WL_AMPDU_RX(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_RX_VAL, fmt, ##args)
+#define WL_AMPDU_ERR(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_ERR_VAL, fmt, ##args)
+#define WL_AMPDU_TX(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_TX_VAL, fmt, ##args)
+#define WL_AMPDU_CTL(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_CTL_VAL, fmt, ##args)
+#define WL_AMPDU_HW(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_HW_VAL, fmt, ##args)
+#define WL_AMPDU_HWTXS(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_HWTXS_VAL, fmt, ##args)
+#define WL_AMPDU_HWDBG(fmt, args...) \
+ WL_AMPDU_PRINT(WL_AMPDU_HWDBG_VAL, fmt, ##args)
+#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(fmt, args...) no_printk(fmt, ##args)
+#define WL_TRACE(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU(fmt, args...) no_printk(fmt, ##args)
+#define WL_FFPLD(fmt, args...) no_printk(fmt, ##args)
+
+#define WL_ERROR_ON() 0
+
+#define WL_AMPDU_UPDN(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_RX(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_ERR(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_TX(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_CTL(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_HW(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_HWTXS(fmt, args...) no_printk(fmt, ##args)
+#define WL_AMPDU_HWDBG(fmt, args...) no_printk(fmt, ##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_ */
--- /dev/null
+/*
+ * 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(struct osl_info *osh, void *p, unsigned char *buf,
+ int len, uint offset);
+extern void *wl_get_pktbuffer(struct osl_info *osh, int len);
+extern int wl_set_pktlen(struct osl_info *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_ */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <osl.h>
+#define WLC_MAXBSSCFG 1 /* single BSS configs */
+
+#include <wlc_cfg.h>
+#include <net/mac80211.h>
+#include <phy_version.h>
+#include <bcmutils.h>
+#include <pcicfg.h>
+#include <wlioctl.h>
+#include <wlc_key.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <wlc_channel.h>
+#include <wlc_pub.h>
+#include <wlc_scb.h>
+#include <wl_dbg.h>
+#include <wl_export.h>
+
+#include <wl_mac80211.h>
+#include <linux/firmware.h>
+#include <wl_ucode.h>
+#include <d11ucode_ext.h>
+
+
+static void wl_timer(unsigned long data);
+static void _wl_timer(wl_timer_t *t);
+
+
+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];
+};
+
+#define WL_DEV_IF(dev) ((struct wl_if *)netdev_priv(dev))
+#define WL_INFO(dev) ((struct wl_info *)(WL_DEV_IF(dev)->wl))
+static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev);
+static void wl_release_fw(struct wl_info *wl);
+
+/* local prototypes */
+static int wl_start(struct sk_buff *skb, struct wl_info *wl);
+static int wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+static void wl_dpc(unsigned long data);
+
+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");
+
+/* 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);
+
+
+#ifdef BCMDBG
+static int msglevel = 0xdeadbeef;
+module_param(msglevel, int, 0);
+static int phymsglevel = 0xdeadbeef;
+module_param(phymsglevel, int, 0);
+#endif /* BCMDBG */
+
+#define HW_TO_WL(hw) (hw->priv)
+#define WL_TO_HW(wl) (wl->pub->ieee_hw)
+static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb);
+static int wl_ops_start(struct ieee80211_hw *hw);
+static void wl_ops_stop(struct ieee80211_hw *hw);
+static int wl_ops_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);
+
+static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ int status;
+ struct wl_info *wl = hw->priv;
+ WL_LOCK(wl);
+ if (!wl->pub->up) {
+ WL_ERROR("ops->tx called while down\n");
+ status = -ENETDOWN;
+ goto done;
+ }
+ status = wl_start(skb, wl);
+ done:
+ WL_UNLOCK(wl);
+ return status;
+}
+
+static int wl_ops_start(struct ieee80211_hw *hw)
+{
+ struct wl_info *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)
+{
+ struct wl_info *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)
+{
+ struct wl_info *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)
+{
+ struct wl_info *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;
+ struct wl_info *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)
+{
+ struct wl_info *wl = HW_TO_WL(hw);
+ int val;
+
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ WL_ERROR("Associated:\t%s\n", info->assoc ? "True" : "False");
+ /* association status changed (associated/disassociated)
+ * also implies a change in the AID.
+ */
+ }
+ 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,
+ 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) */
+ }
+ return;
+}
+
+static void
+wl_ops_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags, u64 multicast)
+{
+ struct wl_info *wl = hw->priv;
+
+ 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");
+ 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);
+ }
+ 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: Unknown 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)
+{
+ struct wl_info *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;
+ struct wl_info *wl = hw->priv;
+
+ /* Init the scb */
+ scb = (struct scb *)sta->drv_priv;
+ memset(scb, 0, 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;
+ wl->pub->global_ampdu->max_pdu = 16;
+ pktq_init(&scb->scb_ampdu.txq, AMPDU_MAX_SCB_TID,
+ AMPDU_MAX_SCB_TID * PKTQ_LEN_DEFAULT);
+
+ sta->ht_cap.ht_supported = true;
+ sta->ht_cap.ampdu_factor = AMPDU_RX_FACTOR_64K;
+ 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
+ struct wl_info *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 = {
+ .tx = wl_ops_tx,
+ .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(struct wl_info *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 struct wl_info *wl_attach(u16 vendor, u16 device, unsigned long regs,
+ uint bustype, void *btparam, uint irq)
+{
+ struct wl_info *wl;
+ struct osl_info *osh;
+ int unit, err;
+
+ unsigned long base_addr;
+ struct ieee80211_hw *hw;
+ u8 perm[ETH_ALEN];
+
+ unit = wl_found;
+ err = 0;
+
+ if (unit < 0) {
+ WL_ERROR("wl%d: unit number overflow, exiting\n", unit);
+ return NULL;
+ }
+
+ osh = osl_attach(btparam, bustype);
+ ASSERT(osh);
+
+ /* allocate private info */
+ hw = pci_get_drvdata(btparam); /* btparam == pdev */
+ wl = hw->priv;
+ ASSERT(wl);
+
+ wl->osh = osh;
+ atomic_set(&wl->callbacks, 0);
+
+ /* setup the bottom half handler */
+ tasklet_init(&wl->tasklet, wl_dpc, (unsigned long) wl);
+
+
+
+ base_addr = regs;
+
+ if (bustype == PCI_BUS) {
+ wl->piomode = false;
+ } else if (bustype == RPC_BUS) {
+ /* Do nothing */
+ } else {
+ bustype = PCI_BUS;
+ WL_TRACE("force to PCI\n");
+ }
+ wl->bcm_bustype = bustype;
+
+ wl->regsva = ioremap_nocache(base_addr, PCI_BAR0_WINSZ);
+ if (wl->regsva == NULL) {
+ WL_ERROR("wl%d: ioremap() failed\n", unit);
+ goto fail;
+ }
+ spin_lock_init(&wl->lock);
+ spin_lock_init(&wl->isr_lock);
+
+ /* prepare ucode */
+ if (wl_request_fw(wl, (struct pci_dev *)btparam)) {
+ printf("%s: Failed to find firmware usually in %s\n",
+ KBUILD_MODNAME, "/lib/firmware/brcm");
+ wl_release_fw(wl);
+ wl_remove((struct pci_dev *)btparam);
+ goto fail1;
+ }
+
+ /* common load-time initialization */
+ wl->wlc = wlc_attach((void *)wl, vendor, device, unit, wl->piomode, osh,
+ wl->regsva, wl->bcm_bustype, btparam, &err);
+ wl_release_fw(wl);
+ if (!wl->wlc) {
+ printf("%s: wlc_attach() failed with code %d\n",
+ KBUILD_MODNAME, err);
+ 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);
+
+
+ if (wlc_iovar_setint(wl->wlc, "mpc", 0)) {
+ WL_ERROR("wl%d: Error setting MPC variable to 0\n", unit);
+ }
+
+ /* 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;
+
+ /* 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, ETH_ALEN);
+ 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);
+ }
+ WL_ERROR("wl%d: Broadcom BCM43xx 802.11 MAC80211 Driver (" PHY_VERSION_STR ")",
+ unit);
+
+#ifdef BCMDBG
+ printf(" (Compiled at " __TIME__ " on " __DATE__ ")");
+#endif /* BCMDBG */
+ printf("\n");
+
+ wl_found++;
+ return wl;
+
+ fail:
+ wl_free(wl);
+fail1:
+ return NULL;
+}
+
+
+
+#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,
+ .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 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)
+{
+ struct wl_info *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]);
+
+ 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;
+ }
+ 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++;
+ if (phy_list[0] == 'n' || phy_list[0] == 'c') {
+ 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);
+}
+
+/**
+ * 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;
+ struct wl_info *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(struct wl_info), &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);
+
+ memset(hw->priv, 0, 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)
+{
+ struct wl_info *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)
+{
+ struct wl_info *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)
+{
+ struct wl_info *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,
+};
+
+/**
+ * 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);
+ }
+ {
+ 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 /* BCMDBG */
+
+ error = pci_register_driver(&wl_pci_driver);
+ if (!error)
+ return 0;
+
+
+
+ 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)
+{
+ pci_unregister_driver(&wl_pci_driver);
+
+}
+
+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(struct wl_info *wl)
+{
+ wl_timer_t *t, *next;
+ struct osl_info *osh;
+
+ ASSERT(wl);
+ /* free ucode data */
+ if (wl->fw.fw_cnt)
+ wl_ucode_data_free();
+ if (wl->irq)
+ free_irq(wl->irq, wl);
+
+ /* 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 && wl->bcm_bustype != SDIO_BUS &&
+ wl->bcm_bustype != JTAG_BUS) {
+ iounmap((void *)wl->regsva);
+ }
+ wl->regsva = NULL;
+
+
+ osl_detach(osh);
+}
+
+/* transmit a packet */
+static int BCMFASTPATH wl_start(struct sk_buff *skb, struct wl_info *wl)
+{
+ if (!wl)
+ return -ENETDOWN;
+
+ return wl_start_int(wl, WL_TO_HW(wl), skb);
+}
+
+static int BCMFASTPATH
+wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ wlc_sendpkt_mac80211(wl->wlc, skb, hw);
+ return NETDEV_TX_OK;
+}
+
+void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state,
+ int prio)
+{
+ WL_ERROR("Shouldn't be here %s\n", __func__);
+}
+
+void wl_init(struct wl_info *wl)
+{
+ WL_TRACE("wl%d: wl_init\n", wl->pub->unit);
+
+ wl_reset(wl);
+
+ wlc_init(wl->wlc);
+}
+
+uint wl_reset(struct wl_info *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(struct wl_info *wl)
+{
+ unsigned long flags;
+
+ INT_LOCK(wl, flags);
+ wlc_intrson(wl->wlc);
+ INT_UNLOCK(wl, flags);
+}
+
+bool wl_alloc_dma_resources(struct wl_info *wl, uint addrwidth)
+{
+ return true;
+}
+
+u32 BCMFASTPATH wl_intrsoff(struct wl_info *wl)
+{
+ unsigned long flags;
+ u32 status;
+
+ INT_LOCK(wl, flags);
+ status = wlc_intrsoff(wl->wlc);
+ INT_UNLOCK(wl, flags);
+ return status;
+}
+
+void wl_intrsrestore(struct wl_info *wl, u32 macintmask)
+{
+ unsigned long flags;
+
+ INT_LOCK(wl, flags);
+ wlc_intrsrestore(wl->wlc, macintmask);
+ INT_UNLOCK(wl, flags);
+}
+
+int wl_up(struct wl_info *wl)
+{
+ int error = 0;
+
+ if (wl->pub->up)
+ return 0;
+
+ error = wlc_up(wl->wlc);
+
+ return error;
+}
+
+void wl_down(struct wl_info *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);
+
+ /* 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);
+
+ WL_LOCK(wl);
+}
+
+irqreturn_t BCMFASTPATH wl_isr(int irq, void *dev_id)
+{
+ struct wl_info *wl;
+ bool ours, wantdpc;
+ unsigned long flags;
+
+ wl = (struct wl_info *) 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);
+}
+
+static void BCMFASTPATH wl_dpc(unsigned long data)
+{
+ struct wl_info *wl;
+
+ wl = (struct wl_info *) 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);
+}
+
+static void wl_link_up(struct wl_info *wl, char *ifname)
+{
+ WL_ERROR("wl%d: link up (%s)\n", wl->pub->unit, ifname);
+}
+
+static void wl_link_down(struct wl_info *wl, char *ifname)
+{
+ WL_ERROR("wl%d: link down (%s)\n", wl->pub->unit, ifname);
+}
+
+void wl_event(struct wl_info *wl, char *ifname, wlc_event_t *e)
+{
+
+ switch (e->event.event_type) {
+ case WLC_E_LINK:
+ case WLC_E_NDIS_LINK:
+ if (e->event.flags & WLC_EVENT_MSG_LINK)
+ wl_link_up(wl, ifname);
+ else
+ wl_link_down(wl, ifname);
+ break;
+ case WLC_E_RADIO:
+ break;
+ }
+}
+
+static void wl_timer(unsigned long data)
+{
+ _wl_timer((wl_timer_t *) data);
+}
+
+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(struct wl_info *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;
+ }
+
+ memset(t, 0, 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(struct wl_info *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(struct wl_info *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(struct wl_info *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)
+{
+ struct wl_info *wl = (struct wl_info *) 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;
+};
+
+char *wl_firmwares[WL_MAX_FW] = {
+ "brcm/bcm43xx",
+ NULL
+};
+
+int wl_ucode_init_buf(struct wl_info *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(struct wl_info *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;
+}
+
+static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev)
+{
+ int status;
+ struct device *device = &pdev->dev;
+ char fw_name[100];
+ int i;
+
+ memset((void *)&wl->fw, 0, 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;
+ return wl_ucode_data_init(wl);
+}
+
+void wl_ucode_free_buf(void *p)
+{
+ kfree(p);
+}
+
+static void wl_release_fw(struct wl_info *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]);
+ }
+}
+
+
+/*
+ * checks validity of all firmware images loaded from user space
+ */
+int wl_check_firmwares(struct wl_info *wl)
+{
+ int i;
+ int entry;
+ int rc = 0;
+ const struct firmware *fw;
+ const struct firmware *fw_hdr;
+ struct wl_fw_hdr *ucode_hdr;
+ for (i = 0; i < WL_MAX_FW && rc == 0; i++) {
+ fw = wl->fw.fw_bin[i];
+ fw_hdr = wl->fw.fw_hdr[i];
+ if (fw == NULL && fw_hdr == NULL) {
+ break;
+ } else if (fw == NULL || fw_hdr == NULL) {
+ WL_ERROR("%s: invalid bin/hdr fw\n", __func__);
+ rc = -EBADF;
+ } else if (fw_hdr->size % sizeof(struct wl_fw_hdr)) {
+ WL_ERROR("%s: non integral fw hdr file size %d/%zu\n",
+ __func__, fw_hdr->size,
+ sizeof(struct wl_fw_hdr));
+ rc = -EBADF;
+ } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
+ WL_ERROR("%s: out of bounds fw file size %d\n",
+ __func__, fw->size);
+ rc = -EBADF;
+ } else {
+ /* check if ucode section overruns firmware image */
+ ucode_hdr = (struct wl_fw_hdr *)fw_hdr->data;
+ for (entry = 0; entry < wl->fw.hdr_num_entries[i] && rc;
+ entry++, ucode_hdr++) {
+ if (ucode_hdr->offset + ucode_hdr->len >
+ fw->size) {
+ WL_ERROR("%s: conflicting bin/hdr\n",
+ __func__);
+ rc = -EBADF;
+ }
+ }
+ }
+ }
+ if (rc == 0 && wl->fw.fw_cnt != i) {
+ WL_ERROR("%s: invalid fw_cnt=%d\n", __func__, wl->fw.fw_cnt);
+ rc = -EBADF;
+ }
+ return rc;
+}
+
--- /dev/null
+/*
+ * 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 {
+ struct wlc_pub *pub; /* pointer to public wlc state */
+ void *wlc; /* pointer to private common os-independent data */
+ struct osl_info *osh; /* pointer to os handler */
+ u32 magic;
+
+ int irq;
+
+ spinlock_t lock; /* per-device perimeter lock */
+ spinlock_t isr_lock; /* per-device ISR synchronization lock */
+ 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 */
+ 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 */
+ 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;
+};
+
+#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)
+
+#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(struct wl_info *wl);
+extern int wl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+#endif /* _wl_mac80211_h_ */
--- /dev/null
+/*
+ * 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 MIN_FW_SIZE 40000 /* minimum firmware file size in bytes */
+#define MAX_FW_SIZE 150000
+
+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;
+
+extern int wl_ucode_data_init(struct wl_info *wl);
+extern void wl_ucode_data_free(void);
+
+extern int wl_ucode_init_buf(struct wl_info *wl, void **pbuf, unsigned int idx);
+extern int wl_ucode_init_uint(struct wl_info *wl, unsigned *data,
+ unsigned int idx);
+extern void wl_ucode_free_buf(void *);
+extern int wl_check_firmwares(struct wl_info *wl);
--- /dev/null
+/*
+ * 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/types.h>
+#include <bcmdefs.h>
+#include <d11ucode_ext.h>
+#include <wl_ucode.h>
+
+
+
+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(struct wl_info *wl)
+{
+ int rc;
+ rc = wl_check_firmwares(wl);
+ if (rc < 0)
+ return rc;
+ 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;
+}
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <wlioctl.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <wlc_event.h>
+#include <wlc_mac80211.h>
+#include <wlc_alloc.h>
+#include <wl_dbg.h>
+
+static struct wlc_pub *wlc_pub_malloc(struct osl_info *osh, uint unit,
+ uint *err, uint devid);
+static void wlc_pub_mfree(struct osl_info *osh, struct wlc_pub *pub);
+static void wlc_tunables_init(wlc_tunables_t *tunables, uint devid);
+
+void *wlc_calloc(struct osl_info *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;
+}
+
+static struct wlc_pub *wlc_pub_malloc(struct osl_info *osh, uint unit,
+ uint *err, uint devid)
+{
+ struct wlc_pub *pub;
+
+ pub = (struct wlc_pub *) wlc_calloc(osh, unit, sizeof(struct wlc_pub));
+ 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 = (u8 *)wlc_calloc(osh, unit,
+ (ETH_ALEN * MAXMULTILIST));
+ if (pub->multicast == NULL) {
+ *err = 1003;
+ goto fail;
+ }
+
+ return pub;
+
+ fail:
+ wlc_pub_mfree(osh, pub);
+ return NULL;
+}
+
+static void wlc_pub_mfree(struct osl_info *osh, struct wlc_pub *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(struct osl_info *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(struct osl_info *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;
+ kfree(current_bss);
+ cfg->current_bss = NULL;
+ }
+
+ kfree(cfg);
+}
+
+void wlc_bsscfg_ID_assign(struct wlc_info *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
+ */
+struct wlc_info *wlc_attach_malloc(struct osl_info *osh, uint unit, uint *err,
+ uint devid)
+{
+ struct wlc_info *wlc;
+
+ wlc = (struct wlc_info *) wlc_calloc(osh, unit,
+ sizeof(struct wlc_info));
+ if (wlc == NULL) {
+ *err = 1002;
+ goto fail;
+ }
+
+ wlc->hwrxoff = WL_HWRXOFF;
+
+ /* allocate struct wlc_pub state structure */
+ wlc->pub = wlc_pub_malloc(osh, unit, err, devid);
+ if (wlc->pub == NULL) {
+ *err = 1003;
+ goto fail;
+ }
+ wlc->pub->wlc = wlc;
+
+ /* allocate struct wlc_hw_info state structure */
+
+ wlc->hw = (struct wlc_hw_info *)wlc_calloc(osh, unit,
+ sizeof(struct wlc_hw_info));
+ if (wlc->hw == NULL) {
+ *err = 1005;
+ goto fail;
+ }
+ wlc->hw->wlc = wlc;
+
+ 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));
+ }
+ }
+
+ 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] = (struct wlcband *)wlc_calloc(osh, unit,
+ (sizeof(struct wlcband)*MAXBANDS));
+ if (wlc->bandstate[0] == NULL) {
+ *err = 1025;
+ goto fail;
+ } else {
+ int i;
+
+ for (i = 1; i < MAXBANDS; i++) {
+ wlc->bandstate[i] =
+ (struct wlcband *) ((unsigned long)wlc->bandstate[0]
+ + (sizeof(struct wlcband)*i));
+ }
+ }
+
+ wlc->corestate = (struct wlccore *)wlc_calloc(osh, unit,
+ sizeof(struct wlccore));
+ 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(struct wlc_info *wlc, struct osl_info *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) {
+ if (wlc->hw->bandstate[0]) {
+ kfree(wlc->hw->bandstate[0]);
+ wlc->hw->bandstate[0] = NULL;
+ }
+
+ /* free hw struct */
+ kfree(wlc->hw);
+ wlc->hw = NULL;
+ }
+
+ /* free the wlc */
+ kfree(wlc);
+ wlc = NULL;
+}
--- /dev/null
+/*
+ * 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(struct osl_info *osh, uint unit, uint size);
+
+extern struct wlc_info *wlc_attach_malloc(struct osl_info *osh, uint unit,
+ uint *err, uint devid);
+extern void wlc_detach_mfree(struct wlc_info *wlc, struct osl_info *osh);
+
+struct wlc_bsscfg;
+extern struct wlc_bsscfg *wlc_bsscfg_malloc(struct osl_info *osh, uint unit);
+extern void wlc_bsscfg_mfree(struct osl_info *osh, struct wlc_bsscfg *cfg);
--- /dev/null
+/*
+ * 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 <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <hnddma.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_event.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>
+#include <wl_dbg.h>
+
+
+#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 (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 {
+ struct wlc_info *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 */
+
+};
+
+#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(struct ampdu_info *ampdu);
+static int wlc_ffpld_check_txfunfl(struct wlc_info *wlc, int f);
+static void wlc_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
+
+static scb_ampdu_tid_ini_t *wlc_ampdu_init_tid_ini(struct ampdu_info *ampdu,
+ scb_ampdu_t *scb_ampdu,
+ u8 tid, bool override);
+static void ampdu_cleanup_tid_ini(struct ampdu_info *ampdu,
+ scb_ampdu_t *scb_ampdu,
+ u8 tid, bool force);
+static void ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur);
+static void scb_ampdu_update_config(struct ampdu_info *ampdu, struct scb *scb);
+static void scb_ampdu_update_config_all(struct ampdu_info *ampdu);
+
+#define wlc_ampdu_txflowcontrol(a, b, c) do {} while (0)
+
+static void wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
+ struct scb *scb,
+ struct sk_buff *p, tx_status_t *txs,
+ u32 frmtxstatus, u32 frmtxstatus2);
+
+static inline u16 pkt_txh_seqnum(struct wlc_info *wlc, struct sk_buff *p)
+{
+ d11txh_t *txh;
+ struct ieee80211_hdr *h;
+ txh = (d11txh_t *) p->data;
+ h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+ return ltoh16(h->seq_ctrl) >> SEQNUM_SHIFT;
+}
+
+struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc)
+{
+ struct ampdu_info *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(struct ampdu_info), 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;
+ 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(struct ampdu_info *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(struct ampdu_info *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(struct ampdu_info *ampdu)
+{
+ WL_NONE("%s: Entering\n", __func__);
+}
+
+static void scb_ampdu_update_config(struct ampdu_info *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(struct ampdu_info *ampdu)
+{
+ scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
+}
+
+static void wlc_ffpld_init(struct ampdu_info *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(struct wlc_info *wlc, int fid)
+{
+ struct ampdu_info *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(struct ampdu_info *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(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p,
+ uint prec)
+{
+ scb_ampdu_t *scb_ampdu;
+ scb_ampdu_tid_ini_t *ini;
+ u8 tid = (u8) (p->priority);
+
+ 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(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
+ struct sk_buff **pdu, int prec)
+{
+ struct wlc_info *wlc;
+ struct osl_info *osh;
+ struct sk_buff *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 ieee80211_hdr *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 ieee80211_rts *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) (p->priority);
+ 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 *) p->data;
+ plcp = (u8 *) (txh + 1);
+ h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
+ seq = ltoh16(h->seq_ctrl) >> 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 ieee80211_rts *)&txh->rts_frame;
+ fc = ltoh16(rts->frame_control);
+ 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\n",
+ 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) (p->priority) == 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 *) pkt[count - 1]->data;
+ 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 *) pkt[0]->data;
+ 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 ieee80211_rts *)&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->duration = 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->duration;
+ /* 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__);
+ }
+ for (i = 0; i < count; i++)
+ wlc_txfifo(wlc, fifo, pkt[i], i == (count - 1),
+ ampdu->txpkt_weight);
+
+ }
+ /* endif (count) */
+ return err;
+}
+
+void BCMFASTPATH
+wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+ struct sk_buff *p, tx_status_t *txs)
+{
+ scb_ampdu_t *scb_ampdu;
+ struct wlc_info *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, p->priority);
+ 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) {
+ 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);
+ }
+
+ wlc_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
+ wlc_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
+}
+
+void
+rate_status(struct wlc_info *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;
+ }
+}
+
+#define SHORTNAME "AMPDU status"
+
+static void BCMFASTPATH
+wlc_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
+ struct sk_buff *p, tx_status_t *txs,
+ u32 s1, u32 s2)
+{
+ scb_ampdu_t *scb_ampdu;
+ struct wlc_info *wlc = ampdu->wlc;
+ scb_ampdu_tid_ini_t *ini;
+ u8 bitmap[8], queue, tid;
+ d11txh_t *txh;
+ u8 *plcp;
+ struct ieee80211_hdr *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];
+ memset(hole, 0, 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) (p->priority);
+
+ 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);
+
+ memset(bitmap, 0, 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;
+ }
+ }
+ } 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 *) p->data);
+ 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 *) p->data;
+ mcl = ltoh16(txh->MacTxControlLow);
+ plcp = (u8 *) (txh + 1);
+ h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
+ seq = ltoh16(h->seq_ctrl) >> 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;
+
+ skb_pull(p, D11_PHY_HDR_LEN);
+ skb_pull(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;
+ skb_pull(p, D11_PHY_HDR_LEN);
+ skb_pull(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(struct ampdu_info *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(struct ampdu_info *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(struct ampdu_info *ampdu, bool on)
+{
+ struct wlc_info *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(struct ampdu_info *ampdu)
+{
+ if (WLC_PHY_11N_CAP(ampdu->wlc->band))
+ return true;
+ else
+ return false;
+}
+
+static void ampdu_update_max_txlen(struct ampdu_info *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(struct ampdu_info *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(struct wlc_info *wlc)
+{
+ char template[T_RAM_ACCESS_SZ * 2];
+
+ /* driver needs to write the ta in the template; ta is at offset 16 */
+ memset(template, 0, sizeof(template));
+ bcopy((char *)wlc->pub->cur_etheraddr, template, ETH_ALEN);
+ wlc_write_template_ram(wlc, (T_BA_TPL_BASE + 16), (T_RAM_ACCESS_SZ * 2),
+ template);
+}
+
+bool wlc_aggregatable(struct wlc_info *wlc, u8 tid)
+{
+ return wlc->ampdu->ini_enable[tid];
+}
+
+void wlc_ampdu_shm_upd(struct ampdu_info *ampdu)
+{
+ struct wlc_info *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);
+ }
+}
--- /dev/null
+/*
+ * 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 struct ampdu_info *wlc_ampdu_attach(struct wlc_info *wlc);
+extern void wlc_ampdu_detach(struct ampdu_info *ampdu);
+extern bool wlc_ampdu_cap(struct ampdu_info *ampdu);
+extern int wlc_ampdu_set(struct ampdu_info *ampdu, bool on);
+extern int wlc_sendampdu(struct ampdu_info *ampdu, wlc_txq_info_t *qi,
+ struct sk_buff **aggp, int prec);
+extern void wlc_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+ struct sk_buff *p, tx_status_t *txs);
+extern void wlc_ampdu_reset(struct ampdu_info *ampdu);
+extern void wlc_ampdu_macaddr_upd(struct wlc_info *wlc);
+extern void wlc_ampdu_shm_upd(struct ampdu_info *ampdu);
+
+extern u8 wlc_ampdu_null_delim_cnt(struct ampdu_info *ampdu, struct scb *scb,
+ ratespec_t rspec, int phylen);
+extern void scb_ampdu_cleanup(struct ampdu_info *ampdu, struct scb *scb);
+
+#endif /* _wlc_ampdu_h_ */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <wlioctl.h>
+
+#include <bcmdevs.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <d11.h>
+#include <wlc_rate.h>
+#include <wlc_key.h>
+#include <wlc_pub.h>
+#include <wl_dbg.h>
+#include <wlc_event.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(struct antsel_info *asi, wlc_antselcfg_t *antsel);
+static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id);
+static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
+static void wlc_antsel_init_cfg(struct antsel_info *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 */
+};
+
+struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc,
+ struct osl_info *osh,
+ struct wlc_pub *pub,
+ struct wlc_hw_info *wlc_hw) {
+ struct antsel_info *asi;
+
+ asi = kzalloc(sizeof(struct antsel_info), 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(struct antsel_info *asi)
+{
+ if (!asi)
+ return;
+
+ kfree(asi);
+}
+
+void wlc_antsel_init(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *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(struct antsel_info *asi, wlc_antselcfg_t *antsel)
+{
+ struct wlc_info *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 */
--- /dev/null
+/*
+ * 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 struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc,
+ struct osl_info *osh,
+ struct wlc_pub *pub,
+ struct wlc_hw_info *wlc_hw);
+extern void wlc_antsel_detach(struct antsel_info *asi);
+extern void wlc_antsel_init(struct antsel_info *asi);
+extern void wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef,
+ bool sel,
+ u8 id, u8 fbid, u8 *antcfg,
+ u8 *fbantcfg);
+extern u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
+#endif /* _wlc_antsel_h_ */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.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>
+#include <bcmdevs.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_event.h>
+#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"
+#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>
+#include <wl_dbg.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(struct wlc_hw_info *wlc, uint mode);
+static void wlc_coreinit(struct wlc_info *wlc);
+
+/* used by wlc_wakeucode_init() */
+static void wlc_write_inits(struct wlc_hw_info *wlc_hw, const d11init_t *inits);
+static void wlc_ucode_write(struct wlc_hw_info *wlc_hw, const u32 ucode[],
+ const uint nbytes);
+static void wlc_ucode_download(struct wlc_hw_info *wlc);
+static void wlc_ucode_txant_set(struct wlc_hw_info *wlc_hw);
+
+/* used by wlc_dpc() */
+static bool wlc_bmac_dotxstatus(struct wlc_hw_info *wlc, tx_status_t *txs,
+ u32 s2);
+static bool wlc_bmac_txstatus_corerev4(struct wlc_hw_info *wlc);
+static bool wlc_bmac_txstatus(struct wlc_hw_info *wlc, bool bound, bool *fatal);
+static bool wlc_bmac_recv(struct wlc_hw_info *wlc_hw, uint fifo, bool bound);
+
+/* used by wlc_down() */
+static void wlc_flushqueues(struct wlc_info *wlc);
+
+static void wlc_write_mhf(struct wlc_hw_info *wlc_hw, u16 *mhfs);
+static void wlc_mctrl_reset(struct wlc_hw_info *wlc_hw);
+static void wlc_corerev_fifofixup(struct wlc_hw_info *wlc_hw);
+
+/* Low Level Prototypes */
+static u16 wlc_bmac_read_objmem(struct wlc_hw_info *wlc_hw, uint offset,
+ u32 sel);
+static void wlc_bmac_write_objmem(struct wlc_hw_info *wlc_hw, uint offset,
+ u16 v, u32 sel);
+static bool wlc_bmac_attach_dmapio(struct wlc_info *wlc, uint j, bool wme);
+static void wlc_bmac_detach_dmapio(struct wlc_hw_info *wlc_hw);
+static void wlc_ucode_bsinit(struct wlc_hw_info *wlc_hw);
+static bool wlc_validboardtype(struct wlc_hw_info *wlc);
+static bool wlc_isgoodchip(struct wlc_hw_info *wlc_hw);
+static char *wlc_get_macaddr(struct wlc_hw_info *wlc_hw);
+static void wlc_mhfdef(struct wlc_info *wlc, u16 *mhfs, u16 mhf2_init);
+static void wlc_mctrl_write(struct wlc_hw_info *wlc_hw);
+static void wlc_ucode_mute_override_set(struct wlc_hw_info *wlc_hw);
+static void wlc_ucode_mute_override_clear(struct wlc_hw_info *wlc_hw);
+static u32 wlc_wlintrsoff(struct wlc_info *wlc);
+static void wlc_wlintrsrestore(struct wlc_info *wlc, u32 macintmask);
+static void wlc_gpio_init(struct wlc_info *wlc);
+static void wlc_write_hw_bcntemplate0(struct wlc_hw_info *wlc_hw, void *bcn,
+ int len);
+static void wlc_write_hw_bcntemplate1(struct wlc_hw_info *wlc_hw, void *bcn,
+ int len);
+static void wlc_bmac_bsinit(struct wlc_info *wlc, chanspec_t chanspec);
+static u32 wlc_setband_inact(struct wlc_info *wlc, uint bandunit);
+static void wlc_bmac_setband(struct wlc_hw_info *wlc_hw, uint bandunit,
+ chanspec_t chanspec);
+static void wlc_bmac_update_slot_timing(struct wlc_hw_info *wlc_hw,
+ bool shortslot);
+static void wlc_upd_ofdm_pctl1_table(struct wlc_hw_info *wlc_hw);
+static u16 wlc_bmac_ofdm_ratetable_offset(struct wlc_hw_info *wlc_hw,
+ u8 rate);
+
+/* === Low Level functions === */
+
+void wlc_bmac_set_shortslot(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw,
+ bool shortslot)
+{
+ struct osl_info *osh;
+ d11regs_t *regs;
+
+ osh = wlc_hw->osh;
+ regs = wlc_hw->regs;
+
+ if (shortslot) {
+ /* 11g short slot: 11a timing */
+ W_REG(osh, ®s->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, ®s->ifs_slot, 0x0212); /* BPHY_SLOT_TIME */
+ wlc_bmac_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME);
+ }
+}
+
+static void WLBANDINITFN(wlc_ucode_bsinit) (struct wlc_hw_info *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) (struct wlc_info *wlc, uint bandunit)
+{
+ struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, uint fifo, bool bound)
+{
+ struct sk_buff *p;
+ struct sk_buff *head = NULL;
+ struct sk_buff *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 {
+ tail->prev = 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 = head->prev;
+ p->prev = NULL;
+
+ /* record the tsf_l in wlc_rxd11hdr */
+ wlc_rxhdr = (wlc_d11rxhdr_t *) p->data;
+ 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(struct wlc_info *wlc, bool bounded)
+{
+ u32 macintstatus;
+ struct wlc_hw_info *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, ®s->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__, wlc_hw->sih->chip,
+ 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, ®s->gptimer, 0);
+ }
+
+ if (macintstatus & MI_RFDISABLE) {
+#if defined(BCMDBG)
+ u32 rfd = R_REG(wlc_hw->osh, ®s->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)
+{
+ struct wlc_info *wlc = (struct wlc_info *) arg;
+ struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, wlc_bmac_state_t *state)
+{
+ state->machwcap = wlc_hw->machwcap;
+
+ return 0;
+}
+
+static bool wlc_bmac_attach_dmapio(struct wlc_info *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;
+ struct wlc_hw_info *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;
+ struct osl_info *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));
+
+ 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(struct wlc_hw_info *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(struct wlc_info *wlc, u16 vendor, u16 device, uint unit,
+ bool piomode, struct osl_info *osh, void *regsva,
+ uint bustype, void *btparam)
+{
+ struct wlc_hw_info *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 struct wlc_hw_info 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 ((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 ((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, ®s->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 (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 (is_broadcast_ether_addr(wlc_hw->etheraddr) ||
+ is_zero_ether_addr(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(struct wlc_hw_info *wlc_hw)
+{
+ struct wlc_info *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(struct wlc_info *wlc)
+{
+ uint i;
+ wlc_hwband_t *band;
+ struct wlc_hw_info *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 (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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, chanspec_t chanspec,
+ bool mute) {
+ u32 macintmask;
+ bool fastclk;
+ struct wlc_info *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(struct wlc_hw_info *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 (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 (wlc_hw->sih->bustype == PCI_BUS)
+ si_pci_down(wlc_hw->sih);
+ wlc_bmac_xtal(wlc_hw, OFF);
+ return BCME_RADIOOFF;
+ }
+
+ if (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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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 (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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, u8 *ea)
+{
+ bcopy(wlc_hw->etheraddr, ea, ETH_ALEN);
+}
+
+void wlc_bmac_set_hw_etheraddr(struct wlc_hw_info *wlc_hw,
+ u8 *ea)
+{
+ bcopy(ea, wlc_hw->etheraddr, ETH_ALEN);
+}
+
+int wlc_bmac_bandtype(struct wlc_hw_info *wlc_hw)
+{
+ return wlc_hw->band->bandtype;
+}
+
+void *wlc_cur_phy(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_info *wlc, u16 *mhfs, u16 mhf2_init)
+{
+ struct wlc_hw_info *wlc_hw = wlc->hw;
+
+ memset(mhfs, 0, MHFMAX * sizeof(u16));
+
+ 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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, int idx,
+ const u8 *addr)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ volatile u16 *objdata16 = (volatile u16 *)®s->objdata;
+ u32 mac_hm;
+ u16 mac_l;
+ struct osl_info *osh;
+
+ WL_TRACE("wl%d: %s\n", wlc_hw->unit, __func__);
+
+ ASSERT(wlc_hw->corerev > 4);
+
+ mac_hm =
+ (addr[3] << 24) | (addr[2] << 16) |
+ (addr[1] << 8) | addr[0];
+ mac_l = (addr[5] << 8) | addr[4];
+
+ osh = wlc_hw->osh;
+
+ W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
+ (void)R_REG(osh, ®s->objaddr);
+ W_REG(osh, ®s->objdata, mac_hm);
+ W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
+ (void)R_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw, int match_reg_offset,
+ const u8 *addr)
+{
+ d11regs_t *regs;
+ u16 mac_l;
+ u16 mac_m;
+ u16 mac_h;
+ struct osl_info *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[0] | (addr[1] << 8);
+ mac_m = addr[2] | (addr[3] << 8);
+ mac_h = addr[4] | (addr[5] << 8);
+
+ osh = wlc_hw->osh;
+
+ /* enter the MAC addr into the RXE match registers */
+ W_REG(osh, ®s->rcm_ctl, RCM_INC_DATA | match_reg_offset);
+ W_REG(osh, ®s->rcm_mat_data, mac_l);
+ W_REG(osh, ®s->rcm_mat_data, mac_m);
+ W_REG(osh, ®s->rcm_mat_data, mac_h);
+
+}
+
+void
+wlc_bmac_write_template_ram(struct wlc_hw_info *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 */
+ struct osl_info *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, ®s->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, ®s->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, ®s->tplatewrdata, word);
+
+ buf = (u8 *) buf + sizeof(u32);
+ len -= sizeof(u32);
+ }
+}
+
+void wlc_bmac_set_cwmin(struct wlc_hw_info *wlc_hw, u16 newmin)
+{
+ struct osl_info *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(struct wlc_hw_info *wlc_hw, u16 newmax)
+{
+ struct osl_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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, ®s->maccommand, MCMD_BCN0VLD);
+}
+
+static void
+wlc_write_hw_bcntemplate1(struct wlc_hw_info *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, ®s->maccommand, MCMD_BCN1VLD);
+}
+
+/* mac is assumed to be suspended at this point */
+void
+wlc_bmac_write_hw_bcntemplates(struct wlc_hw_info *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, ®s->maccommand) & MCMD_BCN0VLD))
+ wlc_write_hw_bcntemplate0(wlc_hw, bcn, len);
+ /* bcn 1 */
+ else if (!
+ (R_REG(wlc_hw->osh, ®s->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) (struct wlc_hw_info *wlc_hw)
+{
+ u16 v;
+ struct wlc_info *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) (struct wlc_info *wlc, chanspec_t chanspec)
+{
+ struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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) (struct wlc_hw_info *wlc_hw, uint bandunit,
+ chanspec_t chanspec) {
+ struct wlc_info *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) (struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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 ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+ (wlc_hw->sih->chip == BCM43225_CHIP_ID) ||
+ (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(struct wlc_hw_info *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 (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 ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+ (wlc_hw->sih->chip == BCM43225_CHIP_ID) ||
+ (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)
+ && (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(struct wlc_hw_info *wlc_hw, uint fifo)
+{
+ struct hnddma_pub *di = wlc_hw->di[fifo];
+ struct osl_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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;
+ struct osl_info *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, ®s->xmtfifocmd, txfifo_cmd);
+ W_REG(osh, ®s->xmtfifodef, txfifo_def);
+ if (D11REV_GE(wlc_hw->corerev, 16))
+ W_REG(osh, ®s->xmtfifodef1, txfifo_def1);
+
+ W_REG(osh, ®s->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(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *wlc_hw = wlc->hw;
+ d11regs_t *regs;
+ u32 sflags;
+ uint bcnint_us;
+ uint i = 0;
+ bool fifosz_fixup = false;
+ struct osl_info *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, ®s->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, ®s->macintstatus) & MI_MACSSPNDD) == 0),
+ 1000 * 1000);
+ if ((R_REG(osh, ®s->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, ®s->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, ®s->intrcvlazy[0], (1 << IRL_FC_SHIFT));
+ if (D11REV_IS(wlc_hw->corerev, 4))
+ W_REG(osh, ®s->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, ®s->tsf_cfprep, (bcnint_us << CFPREP_CBI_SHIFT));
+ W_REG(osh, ®s->tsf_cfpstart, bcnint_us);
+ W_REG(osh, ®s->macintstatus, MI_GP1);
+
+ /* write interrupt mask */
+ W_REG(osh, ®s->intctrlregs[RX_FIFO].intmask, DEF_RXINTMASK);
+ if (D11REV_IS(wlc_hw->corerev, 4))
+ W_REG(osh, ®s->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, ®s->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, ®s->objaddr, OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+ (void)R_REG(osh, ®s->objaddr);
+ W_REG(osh, ®s->objdata, wlc_hw->SRL);
+ W_REG(osh, ®s->objaddr, OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+ (void)R_REG(osh, ®s->objaddr);
+ W_REG(osh, ®s->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, ®s->ifs_ctl, 0x0FFF);
+ W_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw, u8 spurmode)
+{
+ d11regs_t *regs;
+ struct osl_info *osh;
+ regs = wlc_hw->regs;
+ osh = wlc_hw->osh;
+
+ if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+ (wlc_hw->sih->chip == BCM43225_CHIP_ID)) {
+ if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */
+ W_REG(osh, ®s->tsf_clk_frac_l, 0x2082);
+ W_REG(osh, ®s->tsf_clk_frac_h, 0x8);
+ } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */
+ W_REG(osh, ®s->tsf_clk_frac_l, 0x5341);
+ W_REG(osh, ®s->tsf_clk_frac_h, 0x8);
+ } else { /* 120Mhz */
+ W_REG(osh, ®s->tsf_clk_frac_l, 0x8889);
+ W_REG(osh, ®s->tsf_clk_frac_h, 0x8);
+ }
+ } else if (WLCISLCNPHY(wlc_hw->band)) {
+ if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */
+ W_REG(osh, ®s->tsf_clk_frac_l, 0x7CE0);
+ W_REG(osh, ®s->tsf_clk_frac_h, 0xC);
+ } else { /* 80Mhz */
+ W_REG(osh, ®s->tsf_clk_frac_l, 0xCCCD);
+ W_REG(osh, ®s->tsf_clk_frac_h, 0xC);
+ }
+ }
+}
+
+/* Initialize GPIOs that are controlled by D11 core */
+static void wlc_gpio_init(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *wlc_hw = wlc->hw;
+ d11regs_t *regs;
+ u32 gc, gm;
+ struct osl_info *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, ®s->psm_gpio_oe,
+ (BOARD_GPIO_12 | BOARD_GPIO_13));
+ OR_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw)
+{
+ struct wlc_info *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(struct wlc_hw_info *wlc_hw, const u32 ucode[],
+ const uint nbytes) {
+ struct osl_info *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, ®s->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
+ (void)R_REG(osh, ®s->objaddr);
+ for (i = 0; i < count; i++)
+ W_REG(osh, ®s->objdata, ucode[i]);
+}
+
+static void wlc_write_inits(struct wlc_hw_info *wlc_hw, const d11init_t *inits)
+{
+ int i;
+ struct osl_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw)
+{
+ return (u16) wlc_hw->wlc->stf->txant;
+}
+
+void wlc_bmac_antsel_type_set(struct wlc_hw_info *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(struct wlc_hw_info *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,
+ ®s->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",
+ idx, unit);
+ 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, ®s->intctrlregs[idx].intstatus,
+ intstatus);
+ }
+}
+
+void wlc_intrson(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *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(struct wlc_info *wlc)
+{
+ if (!wlc->hw->up)
+ return 0;
+
+ return wl_intrsoff(wlc->wl);
+}
+
+static void wlc_wlintrsrestore(struct wlc_info *wlc, u32 macintmask)
+{
+ if (!wlc->hw->up)
+ return;
+
+ wl_intrsrestore(wlc->wl, macintmask);
+}
+
+u32 wlc_intrsoff(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *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(struct wlc_info *wlc, u32 macintmask)
+{
+ struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool on, mbool flags)
+{
+ u8 null_ether_addr[ETH_ALEN] = {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(struct wlc_hw_info *wlc_hw, bool user_flag)
+{
+ wlc_phy_set_deaf(wlc_hw->band->pi, user_flag);
+}
+
+int wlc_bmac_xmtfifo_sz_get(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_info *wlc, bool in_isr)
+{
+ struct wlc_hw_info *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ u32 macintstatus;
+ u32 intstatus_rxfifo, intstatus_txsfifo;
+ struct osl_info *osh;
+
+ osh = wlc_hw->osh;
+
+ /* macintstatus includes a DMA interrupt summary bit */
+ macintstatus = R_REG(osh, ®s->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, ®s->macintmask, 0);
+ (void)R_REG(osh, ®s->macintmask); /* sync readback */
+ wlc->macintmask = 0;
+
+ /* clear device interrupts */
+ W_REG(osh, ®s->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, ®s->intctrlregs[RX_FIFO].intstatus);
+ intstatus_txsfifo =
+ R_REG(osh,
+ ®s->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,
+ ®s->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,
+ ®s->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, ®s->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(struct wlc_info *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(struct wlc_info *wlc, bool *wantdpc)
+{
+ struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw)
+{
+ struct sk_buff *status_p;
+ tx_status_t *txs;
+ struct osl_info *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 *) status_p->data;
+ /* 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);
+
+ pkt_buf_free_skb(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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool bound, bool *fatal)
+{
+ bool morepending = false;
+ struct wlc_info *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;
+ struct osl_info *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, ®s->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, ®s->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(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ u32 mc, mi;
+ struct osl_info *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, ®s->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, ®s->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, ®s->macintstatus) & MI_MACSSPNDD),
+ WLC_MAX_MAC_SUSPEND);
+
+ if (!(R_REG(osh, ®s->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, ®s->psmdebug),
+ R_REG(osh, ®s->phydebug),
+ R_REG(osh, ®s->psm_brc));
+ }
+
+ mc = R_REG(osh, ®s->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(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *wlc_hw = wlc->hw;
+ d11regs_t *regs = wlc_hw->regs;
+ u32 mc, mi;
+ struct osl_info *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, ®s->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, ®s->macintstatus, MI_MACSSPNDD);
+
+ mc = R_REG(osh, ®s->maccontrol);
+ ASSERT(!(mc & MCTL_PSM_JMP_0));
+ ASSERT(mc & MCTL_EN_MAC);
+ ASSERT(mc & MCTL_PSM_RUN);
+
+ mi = R_REG(osh, ®s->macintstatus);
+ ASSERT(!(mi & MI_MACSSPNDD));
+
+ wlc_ucode_wake_override_clear(wlc_hw, WLC_WAKE_OVERRIDE_MACSUSPEND);
+}
+
+void wlc_bmac_ifsctl_edcrs_set(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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, ®s->tsf_timerlow);
+ *tsf_h_ptr = R_REG(wlc_hw->osh, ®s->tsf_timerhigh);
+
+ return;
+}
+
+bool wlc_bmac_validate_chip_access(struct wlc_hw_info *wlc_hw)
+{
+ d11regs_t *regs;
+ u32 w, val;
+ volatile u16 *reg16;
+ struct osl_info *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, ®s->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, ®s->objaddr);
+ w = R_REG(osh, ®s->objdata);
+
+ /* Can we write and read back a 32bit register? */
+ W_REG(osh, ®s->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, ®s->objaddr);
+ W_REG(osh, ®s->objdata, (u32) 0xaa5555aa);
+
+ W_REG(osh, ®s->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, ®s->objaddr);
+ val = R_REG(osh, ®s->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, ®s->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, ®s->objaddr);
+ W_REG(osh, ®s->objdata, (u32) 0x55aaaa55);
+
+ W_REG(osh, ®s->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, ®s->objaddr);
+ val = R_REG(osh, ®s->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, ®s->objaddr, OBJADDR_SHM_SEL | 0);
+ (void)R_REG(osh, ®s->objaddr);
+ W_REG(osh, ®s->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 *)®s->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, ®s->tsf_cfpstart, 0xCCCCBBBB);
+
+ /* verify with the 16 bit registers that have no side effects */
+ val = R_REG(osh, ®s->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, ®s->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, ®s->tsf_cfpstart, 0);
+
+ w = R_REG(osh, ®s->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(struct wlc_hw_info *wlc_hw, bool on)
+{
+ d11regs_t *regs;
+ struct osl_info *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, ®s->clk_ctl_st,
+ (CCS_ERSRC_REQ_HT | CCS_ERSRC_REQ_D11PLL |
+ CCS_ERSRC_REQ_PHYPLL));
+ SPINWAIT((R_REG(osh, ®s->clk_ctl_st) &
+ (CCS_ERSRC_AVAIL_HT)) != (CCS_ERSRC_AVAIL_HT),
+ PHYPLL_WAIT_US);
+
+ tmp = R_REG(osh, ®s->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, ®s->clk_ctl_st,
+ (CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL));
+ SPINWAIT((R_REG(osh, ®s->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, ®s->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, ®s->clk_ctl_st, ~CCS_ERSRC_REQ_PHYPLL);
+ tmp = R_REG(osh, ®s->clk_ctl_st);
+ }
+}
+
+void wlc_coredisable(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_info *wlc)
+{
+ struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, uint offset)
+{
+ return wlc_bmac_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
+}
+
+void wlc_bmac_write_shm(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, uint offset, u32 sel)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ volatile u16 *objdata_lo = (volatile u16 *)®s->objdata;
+ volatile u16 *objdata_hi = objdata_lo + 1;
+ u16 v;
+
+ ASSERT((offset & 1) == 0);
+
+ W_REG(wlc_hw->osh, ®s->objaddr, sel | (offset >> 2));
+ (void)R_REG(wlc_hw->osh, ®s->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(struct wlc_hw_info *wlc_hw, uint offset, u16 v, u32 sel)
+{
+ d11regs_t *regs = wlc_hw->regs;
+ volatile u16 *objdata_lo = (volatile u16 *)®s->objdata;
+ volatile u16 *objdata_hi = objdata_lo + 1;
+
+ ASSERT((offset & 1) == 0);
+
+ W_REG(wlc_hw->osh, ®s->objaddr, sel | (offset >> 2));
+ (void)R_REG(wlc_hw->osh, ®s->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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool noreset_flag)
+{
+ wlc_hw->noreset = noreset_flag;
+}
+
+void wlc_bmac_set_ucode_loaded(struct wlc_hw_info *wlc_hw, bool ucode_loaded)
+{
+ wlc_hw->ucode_loaded = ucode_loaded;
+}
+
+void wlc_bmac_pllreq(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *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 (!((wlc_hw->sih->bustype == SI_BUS) ||
+ ((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(struct wlc_hw_info *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(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, u8 val)
+{
+ wlc_phy_txpwr_percent_set(wlc_hw->band->pi, val);
+}
+
+void wlc_bmac_antsel_set(struct wlc_hw_info *wlc_hw, u32 antsel_avail)
+{
+ wlc_hw->antsel_avail = antsel_avail;
+}
--- /dev/null
+/*
+ * 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(struct wlc_hw_info) and HIGH(struct wlc_info)
+ 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(struct wlc_info *wlc, u16 vendor, u16 device,
+ uint unit, bool piomode, struct osl_info *osh,
+ void *regsva, uint bustype, void *btparam);
+extern int wlc_bmac_detach(struct wlc_info *wlc);
+extern void wlc_bmac_watchdog(void *arg);
+extern void wlc_bmac_info_init(struct wlc_hw_info *wlc_hw);
+
+/* up/down, reset, clk */
+extern void wlc_bmac_xtal(struct wlc_hw_info *wlc_hw, bool want);
+
+extern void wlc_bmac_copyto_objmem(struct wlc_hw_info *wlc_hw,
+ uint offset, const void *buf, int len,
+ u32 sel);
+extern void wlc_bmac_copyfrom_objmem(struct wlc_hw_info *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(struct wlc_hw_info *wlc_hw, bool clk);
+extern void wlc_bmac_core_phypll_reset(struct wlc_hw_info *wlc_hw);
+extern void wlc_bmac_core_phypll_ctl(struct wlc_hw_info *wlc_hw, bool on);
+extern void wlc_bmac_phyclk_fgc(struct wlc_hw_info *wlc_hw, bool clk);
+extern void wlc_bmac_macphyclk_set(struct wlc_hw_info *wlc_hw, bool clk);
+extern void wlc_bmac_phy_reset(struct wlc_hw_info *wlc_hw);
+extern void wlc_bmac_corereset(struct wlc_hw_info *wlc_hw, u32 flags);
+extern void wlc_bmac_reset(struct wlc_hw_info *wlc_hw);
+extern void wlc_bmac_init(struct wlc_hw_info *wlc_hw, chanspec_t chanspec,
+ bool mute);
+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 void wlc_bmac_corereset(struct wlc_hw_info *wlc_hw, u32 flags);
+extern void wlc_bmac_switch_macfreq(struct wlc_hw_info *wlc_hw, u8 spurmode);
+
+/* chanspec, ucode interface */
+extern int wlc_bmac_bandtype(struct wlc_hw_info *wlc_hw);
+extern void wlc_bmac_set_chanspec(struct wlc_hw_info *wlc_hw,
+ chanspec_t chanspec,
+ bool mute, struct txpwr_limits *txpwr);
+
+extern void wlc_bmac_txfifo(struct wlc_hw_info *wlc_hw, uint fifo, void *p,
+ bool commit, u16 frameid, u8 txpktpend);
+extern int wlc_bmac_xmtfifo_sz_get(struct wlc_hw_info *wlc_hw, uint fifo,
+ uint *blocks);
+extern void wlc_bmac_mhf(struct wlc_hw_info *wlc_hw, u8 idx, u16 mask,
+ u16 val, int bands);
+extern void wlc_bmac_mctrl(struct wlc_hw_info *wlc_hw, u32 mask, u32 val);
+extern u16 wlc_bmac_mhf_get(struct wlc_hw_info *wlc_hw, u8 idx, int bands);
+extern int wlc_bmac_xmtfifo_sz_set(struct wlc_hw_info *wlc_hw, uint fifo,
+ uint blocks);
+extern void wlc_bmac_txant_set(struct wlc_hw_info *wlc_hw, u16 phytxant);
+extern u16 wlc_bmac_get_txant(struct wlc_hw_info *wlc_hw);
+extern void wlc_bmac_antsel_type_set(struct wlc_hw_info *wlc_hw,
+ u8 antsel_type);
+extern int wlc_bmac_revinfo_get(struct wlc_hw_info *wlc_hw,
+ wlc_bmac_revinfo_t *revinfo);
+extern int wlc_bmac_state_get(struct wlc_hw_info *wlc_hw,
+ wlc_bmac_state_t *state);
+extern void wlc_bmac_write_shm(struct wlc_hw_info *wlc_hw, uint offset, u16 v);
+extern u16 wlc_bmac_read_shm(struct wlc_hw_info *wlc_hw, uint offset);
+extern void wlc_bmac_set_shm(struct wlc_hw_info *wlc_hw, uint offset, u16 v,
+ int len);
+extern void wlc_bmac_write_template_ram(struct wlc_hw_info *wlc_hw, int offset,
+ int len, void *buf);
+extern void wlc_bmac_copyfrom_vars(struct wlc_hw_info *wlc_hw, char **buf,
+ uint *len);
+
+extern void wlc_bmac_process_ps_switch(struct wlc_hw_info *wlc,
+ struct ether_addr *ea, s8 ps_on);
+extern void wlc_bmac_hw_etheraddr(struct wlc_hw_info *wlc_hw,
+ u8 *ea);
+extern void wlc_bmac_set_hw_etheraddr(struct wlc_hw_info *wlc_hw,
+ u8 *ea);
+extern bool wlc_bmac_validate_chip_access(struct wlc_hw_info *wlc_hw);
+
+extern bool wlc_bmac_radio_read_hwdisabled(struct wlc_hw_info *wlc_hw);
+extern void wlc_bmac_set_shortslot(struct wlc_hw_info *wlc_hw, bool shortslot);
+extern void wlc_bmac_mute(struct wlc_hw_info *wlc_hw, bool want, mbool flags);
+extern void wlc_bmac_set_deaf(struct wlc_hw_info *wlc_hw, bool user_flag);
+extern void wlc_bmac_band_stf_ss_set(struct wlc_hw_info *wlc_hw, u8 stf_mode);
+
+extern void wlc_bmac_wait_for_wake(struct wlc_hw_info *wlc_hw);
+extern bool wlc_bmac_tx_fifo_suspended(struct wlc_hw_info *wlc_hw,
+ uint tx_fifo);
+extern void wlc_bmac_tx_fifo_suspend(struct wlc_hw_info *wlc_hw, uint tx_fifo);
+extern void wlc_bmac_tx_fifo_resume(struct wlc_hw_info *wlc_hw, uint tx_fifo);
+
+extern void wlc_ucode_wake_override_set(struct wlc_hw_info *wlc_hw,
+ u32 override_bit);
+extern void wlc_ucode_wake_override_clear(struct wlc_hw_info *wlc_hw,
+ u32 override_bit);
+
+extern void wlc_bmac_set_rcmta(struct wlc_hw_info *wlc_hw, int idx,
+ const u8 *addr);
+extern void wlc_bmac_set_addrmatch(struct wlc_hw_info *wlc_hw,
+ int match_reg_offset,
+ const u8 *addr);
+extern void wlc_bmac_write_hw_bcntemplates(struct wlc_hw_info *wlc_hw,
+ void *bcn, int len, bool both);
+
+extern void wlc_bmac_read_tsf(struct wlc_hw_info *wlc_hw, u32 *tsf_l_ptr,
+ u32 *tsf_h_ptr);
+extern void wlc_bmac_set_cwmin(struct wlc_hw_info *wlc_hw, u16 newmin);
+extern void wlc_bmac_set_cwmax(struct wlc_hw_info *wlc_hw, u16 newmax);
+extern void wlc_bmac_set_noreset(struct wlc_hw_info *wlc, bool noreset_flag);
+extern void wlc_bmac_set_ucode_loaded(struct wlc_hw_info *wlc,
+ bool ucode_loaded);
+
+extern void wlc_bmac_retrylimit_upd(struct wlc_hw_info *wlc_hw, u16 SRL,
+ u16 LRL);
+
+extern void wlc_bmac_fifoerrors(struct wlc_hw_info *wlc_hw);
+
+
+/* API for BMAC driver (e.g. wlc_phy.c etc) */
+
+extern void wlc_bmac_bw_set(struct wlc_hw_info *wlc_hw, u16 bw);
+extern void wlc_bmac_pllreq(struct wlc_hw_info *wlc_hw, bool set,
+ mbool req_bit);
+extern void wlc_bmac_set_clk(struct wlc_hw_info *wlc_hw, bool on);
+extern bool wlc_bmac_taclear(struct wlc_hw_info *wlc_hw, bool ta_ok);
+extern void wlc_bmac_hw_up(struct wlc_hw_info *wlc_hw);
+
+extern void wlc_bmac_dump(struct wlc_hw_info *wlc_hw, struct bcmstrbuf *b,
+ wlc_bmac_dump_id_t dump_id);
+extern void wlc_gpio_fast_deinit(struct wlc_hw_info *wlc_hw);
+
+extern bool wlc_bmac_radio_hw(struct wlc_hw_info *wlc_hw, bool enable);
+extern u16 wlc_bmac_rate_shm_offset(struct wlc_hw_info *wlc_hw, u8 rate);
+
+extern void wlc_bmac_assert_type_set(struct wlc_hw_info *wlc_hw, u32 type);
+extern void wlc_bmac_set_txpwr_percent(struct wlc_hw_info *wlc_hw, u8 val);
+extern void wlc_bmac_blink_sync(struct wlc_hw_info *wlc_hw, u32 led_pins);
+extern void wlc_bmac_ifsctl_edcrs_set(struct wlc_hw_info *wlc_hw, bool abie,
+ bool isht);
+
+extern void wlc_bmac_antsel_set(struct wlc_hw_info *wlc_hw, u32 antsel_avail);
--- /dev/null
+/*
+ * 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:
+ (struct wlc_info *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[IEEE80211_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 */
+
+ u8 BSSID[ETH_ALEN]; /* BSSID (associated) */
+ u8 cur_etheraddr[ETH_ALEN]; /* 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_ */
--- /dev/null
+/*
+ * 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
+
+/* **** 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
+ */
+
+
+/* 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_ */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <sbhndpio.h>
+#include <sbhnddma.h>
+#include <wlioctl.h>
+#include <wlc_pub.h>
+#include <wlc_key.h>
+#include <wlc_event.h>
+#include <wlc_mac80211.h>
+#include <wlc_bmac.h>
+#include <wlc_stf.h>
+#include <wlc_channel.h>
+#include <wl_dbg.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 {
+ struct wlc_pub *pub;
+ struct wlc_info *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;
+
+ memset(channels, 0, 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(struct wlc_info *wlc)
+{
+ wlc_cm_info_t *wlc_cm;
+ char country_abbrev[WLC_CNTRY_BUF_SZ];
+ const country_info_t *country;
+ struct wlc_pub *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 */
+ memset(country_abbrev, 0, 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)
+{
+ struct wlc_info *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;
+ struct wlc_info *wlc = wlc_cm->wlc;
+ char prev_country_abbrev[WLC_CNTRY_BUF_SZ];
+
+ ASSERT(country != NULL);
+
+ /* save current country state */
+ wlc_cm->country = country;
+
+ memset(&prev_country_abbrev, 0, 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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *wlc = wlc_cm->wlc;
+ uint i, j;
+ struct wlcband *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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *wlc = wlc_cm->wlc;
+ uint i, j;
+ struct wlcband *band;
+ const chanvec_t *chanvec;
+
+ memset(&wlc_cm->quiet_channels, 0, 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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *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)
+{
+ struct wlc_info *wlc = wlc_cm->wlc;
+ uint i;
+ uint chan;
+ int maxpwr;
+ int delta;
+ const country_info_t *country;
+ struct wlcband *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;
+
+ memset(txpwr, 0, 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)
+{
+ struct wlc_info *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);
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <sbhndpio.h>
+#include <sbhnddma.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>
+#include <wl_dbg.h>
+
+/* 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;
+ struct wlc_pub *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(struct wlc_pub *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;
+}
--- /dev/null
+/*
+ * 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(struct wlc_pub *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(struct wlc_info *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_ */
--- /dev/null
+/*
+ * 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 {
+ u8 ea[ETH_ALEN]; /* 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_ */
--- /dev/null
+/*
+ * 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 <linux/etherdevice.h>
+#include <bcmdefs.h>
+#include <bcmdevs.h>
+#include <wlc_cfg.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <bcmwifi.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <pcicfg.h>
+#include <bcmsrom.h>
+#include <wlioctl.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_key.h>
+#include <wlc_bsscfg.h>
+#include <wlc_channel.h>
+#include <wlc_event.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>
+#include "d11ucode_ext.h"
+#include <wlc_alloc.h>
+#include <net/mac80211.h>
+#include <wl_dbg.h>
+
+
+/*
+ * WPA(2) definitions
+ */
+#define RSN_CAP_4_REPLAY_CNTRS 2
+#define RSN_CAP_16_REPLAY_CNTRS 3
+
+#define WPA_CAP_4_REPLAY_CNTRS RSN_CAP_4_REPLAY_CNTRS
+#define WPA_CAP_16_REPLAY_CNTRS RSN_CAP_16_REPLAY_CNTRS
+
+/*
+ * 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 * IEEE80211_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))
+
+#define WLC_WAR16165(wlc) (wlc->pub->sih->bustype == PCI_BUS && \
+ (!AP_ENAB(wlc->pub)) && (wlc->war16165))
+
+/* 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
+
+#define EPI_VERSION_NUM 0x054b0b00
+
+#ifdef BCMDBG
+/* pointer to most recently allocated wl/wlc */
+static struct wlc_info *wlc_info_dbg = (struct wlc_info *) (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 */
+static u16 BCMFASTPATH wlc_d11hdrs_mac80211(struct wlc_info *wlc,
+ struct ieee80211_hw *hw,
+ struct sk_buff *p,
+ struct scb *scb, uint frag,
+ uint nfrags, uint queue,
+ uint next_frag_len,
+ wsec_key_t *key,
+ ratespec_t rspec_override);
+
+static void wlc_bss_default_init(struct wlc_info *wlc);
+static void wlc_ucode_mac_upd(struct wlc_info *wlc);
+static ratespec_t mac80211_wlc_set_nrate(struct wlc_info *wlc,
+ struct wlcband *cur_band, u32 int_val);
+static void wlc_tx_prec_map_init(struct wlc_info *wlc);
+static void wlc_watchdog(void *arg);
+static void wlc_watchdog_by_timer(void *arg);
+static int wlc_set_rateset(struct wlc_info *wlc, wlc_rateset_t *rs_arg);
+static int wlc_iovar_rangecheck(struct wlc_info *wlc, u32 val,
+ const bcm_iovar_t *vi);
+static u8 wlc_local_constraint_qdbm(struct wlc_info *wlc);
+
+/* send and receive */
+static wlc_txq_info_t *wlc_txq_alloc(struct wlc_info *wlc,
+ struct osl_info *osh);
+static void wlc_txq_free(struct wlc_info *wlc, struct osl_info *osh,
+ wlc_txq_info_t *qi);
+static void wlc_txflowcontrol_signal(struct wlc_info *wlc, wlc_txq_info_t *qi,
+ bool on, int prio);
+static void wlc_txflowcontrol_reset(struct wlc_info *wlc);
+static u16 wlc_compute_airtime(struct wlc_info *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(struct wlc_info *wlc, ratespec_t rate,
+ u8 preamble_type, uint next_frag_len);
+static void wlc_recvctl(struct wlc_info *wlc, struct osl_info *osh,
+ d11rxhdr_t *rxh, struct sk_buff *p);
+static uint wlc_calc_frame_len(struct wlc_info *wlc, ratespec_t rate,
+ u8 preamble_type, uint dur);
+static uint wlc_calc_ack_time(struct wlc_info *wlc, ratespec_t rate,
+ u8 preamble_type);
+static uint wlc_calc_cts_time(struct wlc_info *wlc, ratespec_t rate,
+ u8 preamble_type);
+/* interrupt, up/down, band */
+static void wlc_setband(struct wlc_info *wlc, uint bandunit);
+static chanspec_t wlc_init_chanspec(struct wlc_info *wlc);
+static void wlc_bandinit_ordered(struct wlc_info *wlc, chanspec_t chanspec);
+static void wlc_bsinit(struct wlc_info *wlc);
+static int wlc_duty_cycle_set(struct wlc_info *wlc, int duty_cycle, bool isOFDM,
+ bool writeToShm);
+static void wlc_radio_hwdisable_upd(struct wlc_info *wlc);
+static bool wlc_radio_monitor_start(struct wlc_info *wlc);
+static void wlc_radio_timer(void *arg);
+static void wlc_radio_enable(struct wlc_info *wlc);
+static void wlc_radio_upd(struct wlc_info *wlc);
+
+/* scan, association, BSS */
+static uint wlc_calc_ba_time(struct wlc_info *wlc, ratespec_t rate,
+ u8 preamble_type);
+static void wlc_update_mimo_band_bwcap(struct wlc_info *wlc, u8 bwcap);
+static void wlc_ht_update_sgi_rx(struct wlc_info *wlc, int val);
+static void wlc_ht_update_ldpc(struct wlc_info *wlc, s8 val);
+static void wlc_war16165(struct wlc_info *wlc, bool tx);
+
+static void wlc_process_eventq(void *arg);
+static void wlc_wme_retries_write(struct wlc_info *wlc);
+static bool wlc_attach_stf_ant_init(struct wlc_info *wlc);
+static uint wlc_attach_module(struct wlc_info *wlc);
+static void wlc_detach_module(struct wlc_info *wlc);
+static void wlc_timers_deinit(struct wlc_info *wlc);
+static void wlc_down_led_upd(struct wlc_info *wlc);
+static uint wlc_down_del_timer(struct wlc_info *wlc);
+static void wlc_ofdm_rateset_war(struct wlc_info *wlc);
+static int _wlc_ioctl(struct wlc_info *wlc, int cmd, void *arg, int len,
+ struct wlc_if *wlcif);
+
+#if defined(BCMDBG)
+void wlc_get_rcmta(struct wlc_info *wlc, int idx, u8 *addr)
+{
+ d11regs_t *regs = wlc->regs;
+ u32 v32;
+ struct osl_info *osh;
+
+ WL_TRACE("wl%d: %s\n", WLCWLUNIT(wlc), __func__);
+
+ ASSERT(wlc->pub->corerev > 4);
+
+ osh = wlc->osh;
+
+ W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | (idx * 2)));
+ (void)R_REG(osh, ®s->objaddr);
+ v32 = R_REG(osh, ®s->objdata);
+ addr[0] = (u8) v32;
+ addr[1] = (u8) (v32 >> 8);
+ addr[2] = (u8) (v32 >> 16);
+ addr[3] = (u8) (v32 >> 24);
+ W_REG(osh, ®s->objaddr, (OBJADDR_RCMTA_SEL | ((idx * 2) + 1)));
+ (void)R_REG(osh, ®s->objaddr);
+ v32 = R_REG(osh, (volatile u16 *)®s->objdata);
+ addr[4] = (u8) v32;
+ addr[5] = (u8) (v32 >> 8);
+}
+#endif /* defined(BCMDBG) */
+
+/* keep the chip awake if needed */
+bool wlc_stay_awake(struct wlc_info *wlc)
+{
+ return true;
+}
+
+/* conditions under which the PM bit should be set in outgoing frames and STAY_AWAKE is meaningful
+ */
+bool wlc_ps_allowed(struct wlc_info *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(struct wlc_info *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 */
+ memset((char *)wlc->core->macstat_snapshot, 0,
+ sizeof(macstat_t));
+ }
+
+ wlc_bmac_reset(wlc->hw);
+ wlc_ampdu_reset(wlc->ampdu);
+ wlc->txretried = 0;
+
+}
+
+void wlc_fatal_error(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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, ®s->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, ®s->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(struct wlc_info *wlc, bool promisc)
+{
+ wlc->bcnmisc_monitor = promisc;
+ wlc_mac_bcn_promisc(wlc);
+}
+
+void wlc_mac_bcn_promisc(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
+ }
+ /* 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));
+ 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(struct wlc_info *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;
+ struct wlc_info *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)
+{
+ struct wlc_info *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(struct wlc_info *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 &=
+ ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ if (wlc->shortslot)
+ cfg->current_bss->capability |=
+ WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ }
+
+ wlc_bmac_set_shortslot(wlc->hw, shortslot);
+}
+
+static u8 wlc_local_constraint_qdbm(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
+
+ memset(&power, 0, 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;
+
+ memset(old_power, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, int val)
+{
+ wlc->ht_cap.cap_info &= ~(IEEE80211_HT_CAP_SGI_20 |
+ IEEE80211_HT_CAP_SGI_40);
+ wlc->ht_cap.cap_info |= (val & WLC_N_SGI_20) ?
+ IEEE80211_HT_CAP_SGI_20 : 0;
+ wlc->ht_cap.cap_info |= (val & WLC_N_SGI_40) ?
+ IEEE80211_HT_CAP_SGI_40 : 0;
+
+ if (wlc->pub->up) {
+ wlc_update_beacon(wlc);
+ wlc_update_probe_resp(wlc, true);
+ }
+}
+
+static void wlc_ht_update_ldpc(struct wlc_info *wlc, s8 val)
+{
+ wlc->stf->ldpc = val;
+
+ wlc->ht_cap.cap_info &= ~IEEE80211_HT_CAP_LDPC_CODING;
+ if (wlc->stf->ldpc != OFF)
+ wlc->ht_cap.cap_info |= IEEE80211_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(struct wlc_info *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(struct wlc_info *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) (struct wlc_info *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) (struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 {
+ memset((char *)&acp_shm, 0, 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)
+{
+ struct wlc_info *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++) {
+ memset((char *)&acp_shm, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
+}
+
+struct wlc_pub *wlc_pub(void *wlc)
+{
+ return ((struct wlc_info *) 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,
+ struct osl_info *osh, void *regsva, uint bustype,
+ void *btparam, uint *perr)
+{
+ struct wlc_info *wlc;
+ uint err = 0;
+ uint j;
+ struct wlc_pub *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 ethhdr) == ETH_HLEN);
+ 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 ieee80211_hdr) == DOT11_A4_HDR_LEN);
+ ASSERT(sizeof(struct ieee80211_rts) == DOT11_RTS_LEN);
+ ASSERT(sizeof(tx_status_t) == TXSTATUS_LEN);
+ ASSERT(sizeof(struct ieee80211_ht_cap) == HT_CAP_IE_LEN);
+#ifdef BRCM_FULLMAC
+ ASSERT(offsetof(wl_scan_params_t, channel_list) ==
+ WL_SCAN_PARAMS_FIXED_SIZE);
+#endif
+ 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 struct wlc_info state and its substructures */
+ wlc = (struct wlc_info *) 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 struct wlc_info 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);
+
+
+ /* 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);
+
+ /* 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];
+ }
+
+ wlc_bmac_hw_etheraddr(wlc->hw, wlc->perm_etheraddr);
+
+ bcopy((char *)&wlc->perm_etheraddr, (char *)&pub->cur_etheraddr,
+ ETH_ALEN);
+
+ 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_info = 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_info &= ~IEEE80211_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_info &= ~IEEE80211_HT_CAP_GRN_FLD;
+
+ /* initialize radio_mpc_disable according to wlc->mpc */
+ wlc_radio_mpc_upd(wlc);
+
+ if (WLANTSEL_ENAB(wlc)) {
+ if ((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(struct wlc_info *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(struct wlc_info *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;
+}
+
+
+static void wlc_timers_deinit(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 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);
+
+ 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
+{
+ return false;
+}
+
+bool wlc_ismpc(struct wlc_info *wlc)
+{
+ return (wlc->mpc_delay_off == 0) && (wlc_is_non_delay_mpc(wlc));
+}
+
+void wlc_radio_mpc_upd(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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;
+
+ wlc_bmac_watchdog(wlc);
+
+ /* 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);
+ }
+ /* BMAC_NOTE: for HIGH_ONLY driver, this seems being called after RPC bus failed */
+ ASSERT(wlc_bmac_taclear(wlc->hw, true));
+
+ /* 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(struct wlc_info *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)
+ && (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(struct wlc_info *wlc)
+{
+ wlc->tx_prec_map = WLC_PREC_BMP_ALL;
+ memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16));
+
+ /* 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(struct wlc_info *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(struct wlc_info *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;
+
+
+ /* Verify all packets are flushed from the driver */
+ if (wlc->osh->pktalloced != 0) {
+ WL_ERROR("%d packets not freed at wlc_down!!!!!!\n",
+ wlc->osh->pktalloced);
+ }
+#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(struct wlc_info *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
+ */
+ struct wlcband *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 */
+ memset(&wlc->sup_rates_override, 0, sizeof(wlc_rateset_t));
+
+ /* Clear rateset override */
+ memset(&rs, 0, 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 |= WLAN_CAPABILITY_SHORT_PREAMBLE;
+ else
+ wlc->default_bss->capability &= ~WLAN_CAPABILITY_SHORT_PREAMBLE;
+
+ /* 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 |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
+ else
+ wlc->default_bss->capability &=
+ ~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+
+ /* 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, int cmd, int *arg)
+{
+ return wlc_ioctl(wlc, cmd, arg, sizeof(int), NULL);
+}
+
+static void wlc_ofdm_rateset_war(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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;
+ struct osl_info *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);
+ }
+ 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;
+ }
+
+ memset((char *)&key, 0, 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, key.ea,
+ ETH_ALEN);
+ }
+
+ 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) &&
+ is_zero_ether_addr(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 = ¤t_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;
+
+ memset(&rs, 0, 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;
+ }
+
+ memset(&rs, 0, 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(¤t_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) {
+ memset(&wlc->sup_rates_override, 0,
+ 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;
+ }
+
+ }
+ /* 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);
+
+ return bcmerror;
+}
+
+#if defined(BCMDBG)
+/* consolidated register access ioctl error checking */
+int wlc_iocregchk(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_pub *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)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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(struct wlc_pub *pub, const char *name, void *hdl)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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)) {
+ memset(&wlc->modulecb[i], 0, 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(struct wlc_info *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(struct wlc_info *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;
+ 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(struct wlc_pub *pub, const bcm_iovar_t *vi, void *arg, int len,
+ bool set)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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)
+{
+ struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 ieee80211_rts 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 > IEEE80211_MAX_SSID_LEN)
+ ssid_len = IEEE80211_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(struct wlc_info *wlc, u8 rate)
+{
+ return wlc_bmac_rate_shm_offset(wlc->hw, rate);
+}
+
+/* Callback for device removed */
+
+/*
+ * 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(struct wlc_info *wlc, struct pktq *q, void *pkt, int prec)
+{
+ return wlc_prec_enq_head(wlc, q, pkt, prec, false);
+}
+
+bool BCMFASTPATH
+wlc_prec_enq_head(struct wlc_info *wlc, struct pktq *q, struct sk_buff *pkt,
+ int prec, bool head)
+{
+ struct sk_buff *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(p->priority)].packets);
+ WLCNTADD(wlc->pub->_wme_cnt->
+ tx_failed[WME_PRIO2AC(p->priority)].bytes,
+ pkttotlen(wlc->osh, p));
+ }
+
+ ASSERT(0);
+ pkt_buf_free_skb(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, struct sk_buff *sdu,
+ uint prec)
+{
+ struct wlc_info *wlc = (struct wlc_info *) ctx;
+ wlc_txq_info_t *qi = wlc->active_queue; /* Check me */
+ struct pktq *q = &qi->q;
+ int prio;
+
+ prio = sdu->priority;
+
+ 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 */
+ pkt_buf_free_skb(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(struct wlc_info *wlc, struct sk_buff *sdu,
+ struct ieee80211_hw *hw)
+{
+ u8 prio;
+ uint fifo;
+ void *pkt;
+ struct scb *scb = &global_scb;
+ struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data);
+ u16 type, fc;
+
+ ASSERT(sdu);
+
+ fc = ltoh16(d11_header->frame_control);
+ type = (fc & IEEE80211_FCTL_FTYPE);
+
+ /* 802.11 standard requires management traffic to go at highest priority */
+ prio = (type == IEEE80211_FTYPE_DATA ? sdu->priority : MAXPRIO);
+ fifo = prio2fifo[prio];
+
+ ASSERT((uint) skb_headroom(sdu) >= TXOFF);
+ ASSERT(!(sdu->cloned));
+ ASSERT(!(sdu->next));
+ ASSERT(!(sdu->prev));
+ 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(struct wlc_info *wlc, wlc_txq_info_t *qi)
+{
+ struct sk_buff *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(struct wlc_info *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(struct wlc_info *wlc, uint fifo, struct sk_buff *p, bool commit,
+ s8 txpktpend)
+{
+ u16 frameid = INVALIDFID;
+ d11txh_t *txh;
+
+ ASSERT(fifo < NFIFO);
+ txh = (d11txh_t *) (p->data);
+
+ /* 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);
+
+
+ /* 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");
+ }
+}
+
+static u16
+wlc_compute_airtime(struct wlc_info *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(struct wlc_info *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);
+
+ memset(plcp, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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);
+ ASSERT(wlc->chanspec == wlc_phy_chanspec_get(wlc->band->pi));
+ }
+#endif /* BCMDBG */
+ return phyctl1;
+}
+
+ratespec_t BCMFASTPATH
+wlc_rspec_to_rts_rspec(struct wlc_info *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(struct wlc_info *wlc, struct ieee80211_hw *hw,
+ struct sk_buff *p, struct scb *scb, uint frag,
+ uint nfrags, uint queue, uint next_frag_len,
+ wsec_key_t *key, ratespec_t rspec_override)
+{
+ struct ieee80211_hdr *h;
+ d11txh_t *txh;
+ u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN];
+ struct osl_info *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 ieee80211_rts *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 ieee80211_hdr *)(p->data);
+ fc = ltoh16(h->frame_control);
+ type = (fc & IEEE80211_FCTL_FTYPE);
+
+ qos = (type == IEEE80211_FTYPE_DATA &&
+ FC_SUBTYPE_ANY_QOS(fc));
+
+ /* compute length of frame in bytes for use in PLCP computations */
+ len = pkttotlen(osh, p);
+ phylen = len + 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 = skb_push(p, D11_PHY_HDR_LEN);
+
+ /* add Broadcom tx descriptor header */
+ txh = (d11txh_t *) skb_push(p, D11_TXH_LEN);
+ memset((char *)txh, 0, 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, p->priority)++;
+ }
+
+ /* extract fragment number from frame first */
+ seq = ltoh16(seq) & FRAGNUM_MASK;
+ seq |= (SCB_SEQNUM(scb, p->priority) << SEQNUM_SHIFT);
+ h->seq_ctrl = 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];
+ }
+
+ 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) &&
+ !is_multicast_ether_addr(h->addr1)) {
+ /* 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) &&
+ !is_multicast_ether_addr(h->addr1) && !use_rifs) {
+ durid =
+ wlc_compute_frame_dur(wlc, rspec[0], preamble_type[0],
+ next_frag_len);
+ h->duration_id = 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->duration_id = htol16(durid);
+ }
+
+ /* DUR field for fallback rate */
+ if (fc == FC_PS_POLL)
+ txh->FragDurFallback = h->duration_id;
+ else if (is_multicast_ether_addr(h->addr1) || 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 (!is_multicast_ether_addr(h->addr1))
+ 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->frame_control, (char *)&txh->MacFrameControl,
+ sizeof(u16));
+ txh->TxFesTimeNormal = htol16(0);
+
+ txh->TxFesTimeFallback = htol16(0);
+
+ /* TxFrameRA */
+ bcopy((char *)&h->addr1, (char *)&txh->TxFrameRA, ETH_ALEN);
+
+ /* 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 + FCS_LEN;
+ else
+ rts_phylen = DOT11_RTS_LEN + 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 ieee80211_rts *)&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->duration = 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->frame_control = htol16(FC_CTS);
+ bcopy((char *)&h->addr2, (char *)&rts->ra, ETH_ALEN);
+ } else {
+ rts->frame_control = htol16((u16) FC_RTS);
+ bcopy((char *)&h->addr1, (char *)&rts->ra,
+ 2 * ETH_ALEN);
+ }
+
+ /* 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 {
+ memset((char *)txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN);
+ memset((char *)&txh->rts_frame, 0,
+ sizeof(struct ieee80211_rts));
+ memset((char *)txh->RTSPLCPFallback, 0,
+ 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(!is_multicast_ether_addr(h->addr1));
+
+ /* 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->duration);
+ 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
+{
+ ASSERT(wlc->pub->corerev >= 3);
+ W_REG(wlc->osh, &wlc->regs->gptimer, 0);
+}
+
+static void wlc_hwtimer_gptimer_cb(struct wlc_info *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(struct wlc_info *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__, wlc->pub->sih->chip,
+ 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, ®s->phydebug) & PDBG_RFD);
+ /* delay the cleanup to wl_down in IBSS case */
+ if ((R_REG(wlc->osh, ®s->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);
+
+ ASSERT(wlc_ps_check(wlc));
+}
+
+static void *wlc_15420war(struct wlc_info *wlc, uint queue)
+{
+ struct hnddma_pub *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(struct wlc_info *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(struct wlc_info *wlc, tx_status_t *txs, u32 frm_tx2)
+{
+ struct sk_buff *p;
+ uint queue;
+ d11txh_t *txh;
+ struct scb *scb = NULL;
+ bool free_pdu;
+ struct osl_info *osh;
+ int tx_rts, tx_frame_count, tx_rts_count;
+ uint totlen, supr_status;
+ bool lastframe;
+ struct ieee80211_hdr *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 *) (p->data);
+ 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 ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+ fc = ltoh16(h->frame_control);
+
+ 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 & IEEE80211_FCTL_MOREFRAGS) == 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) {
+ p->next = NULL;
+ p->prev = NULL;
+ wlc->txretried = 0;
+ /* remove PLCP & Broadcom tx descriptor header */
+ skb_pull(p, D11_PHY_HDR_LEN);
+ skb_pull(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)
+ pkt_buf_free_skb(osh, p, true);
+
+ return true;
+
+}
+
+void BCMFASTPATH
+wlc_txfifo_complete(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, d11rxhdr_t *rxh, struct sk_buff *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 = ieee80211_ofdm_chan_to_freq(
+ WF_CHAN_FACTOR_5_G/2, channel);
+
+ } else {
+ rx_status->band = IEEE80211_BAND_2GHZ;
+ rx_status->freq = ieee80211_dsss_chan_to_freq(channel);
+ }
+
+ rx_status->signal = wlc_rxh->rssi; /* signal */
+
+ /* noise */
+ /* qual */
+ rx_status->antenna = (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; /* ant */
+
+ plcp = p->data;
+
+ 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(struct wlc_info *wlc, struct osl_info *osh, d11rxhdr_t *rxh,
+ struct sk_buff *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 = p->len - D11_PHY_HDR_LEN - FCS_LEN;
+ skb_pull(p, D11_PHY_HDR_LEN);
+ __skb_trim(p, len_mpdu);
+
+ ASSERT(!(p->next));
+ ASSERT(!(p->prev));
+
+ 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);
+ osh->pktalloced--;
+ return;
+}
+
+void wlc_bss_list_free(struct wlc_info *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) {
+ 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(struct wlc_info *wlc, struct sk_buff *p)
+{
+ d11rxhdr_t *rxh;
+ struct ieee80211_hdr *h;
+ struct osl_info *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 *) (p->data);
+
+ /* strip off rxhdr */
+ skb_pull(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 (p->len < 2) {
+ WLCNTINCR(wlc->pub->_cnt->rxrunt);
+ WL_ERROR("wl%d: wlc_recv: rcvd runt of len %d\n",
+ wlc->pub->unit, p->len);
+ goto toss;
+ }
+ skb_pull(p, 2);
+ }
+
+ h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN);
+ len = p->len;
+
+ 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->frame_control)) {
+ fc = ltoh16(h->frame_control);
+ } 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 & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
+ (fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
+ if ((is_zero_ether_addr(h->addr2) ||
+ is_multicast_ether_addr(h->addr2))) {
+ WL_ERROR("wl%d: %s: dropping a frame with "
+ "invalid src mac address, a2: %pM\n",
+ wlc->pub->unit, __func__, h->addr2);
+ WLCNTINCR(wlc->pub->_cnt->rxbadsrcmac);
+ goto toss;
+ }
+ WLCNTINCR(wlc->pub->_cnt->rxfrag);
+ }
+ }
+
+ /* due to sheer numbers, toss out probe reqs for now */
+ if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) {
+ 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:
+ pkt_buf_free_skb(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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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 +
+ FCS_LEN));
+}
+
+static uint BCMFASTPATH
+wlc_calc_ack_time(struct wlc_info *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 + FCS_LEN));
+ return dur;
+}
+
+static uint
+wlc_calc_cts_time(struct wlc_info *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(struct wlc_info *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 */
+ memset(br, 0, 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, u8 bwcap)
+{
+ uint i;
+ struct wlcband *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, uint type, ratespec_t bcn_rspec,
+ wlc_bsscfg_t *cfg, u16 *buf, int *len)
+{
+ static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
+ cck_phy_hdr_t *plcp;
+ struct ieee80211_mgmt *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 */
+ memset((char *)buf, 0, 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 + 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 ieee80211_mgmt *)&plcp[0];
+ else
+ h = (struct ieee80211_mgmt *)&plcp[1];
+
+ /* fill in 802.11 header */
+ h->frame_control = 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 *)ðer_bcast, (char *)&h->da,
+ ETH_ALEN);
+ bcopy((char *)&cfg->cur_etheraddr, (char *)&h->sa, ETH_ALEN);
+ bcopy((char *)&cfg->BSSID, (char *)&h->bssid, ETH_ALEN);
+
+ /* 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(struct wlc_info *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;
+ struct osl_info *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, ®s->maccommand) & both_valid) == both_valid) {
+ /* clear any previous status */
+ W_REG(osh, ®s->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, ®s->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(struct wlc_info *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(struct wlc_info *wlc, wlc_bsscfg_t *cfg)
+{
+ u8 *ssidptr = cfg->SSID;
+ u16 base = M_SSID;
+ u8 ssidbuf[IEEE80211_MAX_SSID_LEN];
+
+ /* padding the ssid with zero and copy it into shm */
+ memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN);
+ bcopy(ssidptr, ssidbuf, cfg->SSID_len);
+
+ wlc_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN);
+
+ if (!MBSS_BCN_ENAB(cfg))
+ wlc_write_shm(wlc, M_SSIDLEN, (u16) cfg->SSID_len);
+}
+
+void wlc_update_probe_resp(struct wlc_info *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(struct wlc_info *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 + 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(struct wlc_info *wlc, struct sk_buff *pdu, uint *fifop)
+{
+ struct osl_info *osh;
+ uint fifo;
+ d11txh_t *txh;
+ struct ieee80211_hdr *h;
+ struct scb *scb;
+ u16 fc;
+
+ osh = wlc->osh;
+
+ ASSERT(pdu);
+ txh = (d11txh_t *) (pdu->data);
+ ASSERT(txh);
+ h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);
+ ASSERT(h);
+ fc = ltoh16(h->frame_control);
+
+ /* 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 ((ltoh16(txh->MacFrameControl) & IEEE80211_FCTL_FTYPE) !=
+ IEEE80211_FTYPE_DATA)
+ WLCNTINCR(wlc->pub->_cnt->txctl);
+
+ return 0;
+}
+
+/* init tx reported rate mechanism */
+void wlc_reprate_init(struct wlc_info *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;
+ memset((char *)bsscfg->txrspec, 0, sizeof(bsscfg->txrspec));
+}
+
+/* Retrieve a consolidated set of revision information,
+ * typically for the WLC_GET_REVINFO ioctl
+ */
+int wlc_get_revision_info(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
+{
+ chanspec_t chanspec;
+ struct wlcband *band;
+ wlc_bss_info_t *bi = wlc->default_bss;
+
+ /* init default and target BSS with some sane initial values */
+ memset((char *)(bi), 0, 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)
+{
+ struct wlc_info *wlc = (struct wlc_info *) 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(struct wlc_info *wlc, struct wlcband *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, u32 mask, u32 val)
+{
+ wlc_bmac_mctrl(wlc->hw, mask, val);
+}
+
+void wlc_corereset(struct wlc_info *wlc, u32 flags)
+{
+ wlc_bmac_corereset(wlc->hw, flags);
+}
+
+void wlc_mhf(struct wlc_info *wlc, u8 idx, u16 mask, u16 val, int bands)
+{
+ wlc_bmac_mhf(wlc->hw, idx, mask, val, bands);
+}
+
+u16 wlc_mhf_get(struct wlc_info *wlc, u8 idx, int bands)
+{
+ return wlc_bmac_mhf_get(wlc->hw, idx, bands);
+}
+
+int wlc_xmtfifo_sz_get(struct wlc_info *wlc, uint fifo, uint *blocks)
+{
+ return wlc_bmac_xmtfifo_sz_get(wlc->hw, fifo, blocks);
+}
+
+void wlc_write_template_ram(struct wlc_info *wlc, int offset, int len,
+ void *buf)
+{
+ wlc_bmac_write_template_ram(wlc->hw, offset, len, buf);
+}
+
+void wlc_write_hw_bcntemplates(struct wlc_info *wlc, void *bcn, int len,
+ bool both)
+{
+ wlc_bmac_write_hw_bcntemplates(wlc->hw, bcn, len, both);
+}
+
+void
+wlc_set_addrmatch(struct wlc_info *wlc, int match_reg_offset,
+ const u8 *addr)
+{
+ wlc_bmac_set_addrmatch(wlc->hw, match_reg_offset, addr);
+}
+
+void wlc_set_rcmta(struct wlc_info *wlc, int idx, const u8 *addr)
+{
+ wlc_bmac_set_rcmta(wlc->hw, idx, addr);
+}
+
+void wlc_read_tsf(struct wlc_info *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(struct wlc_info *wlc, u16 newmin)
+{
+ wlc->band->CWmin = newmin;
+ wlc_bmac_set_cwmin(wlc->hw, newmin);
+}
+
+void wlc_set_cwmax(struct wlc_info *wlc, u16 newmax)
+{
+ wlc->band->CWmax = newmax;
+ wlc_bmac_set_cwmax(wlc->hw, newmax);
+}
+
+void wlc_fifoerrors(struct wlc_info *wlc)
+{
+
+ wlc_bmac_fifoerrors(wlc->hw);
+}
+
+/* Search mem rw utilities */
+
+void wlc_pllreq(struct wlc_info *wlc, bool set, mbool req_bit)
+{
+ wlc_bmac_pllreq(wlc->hw, set, req_bit);
+}
+
+void wlc_reset_bmac_done(struct wlc_info *wlc)
+{
+}
+
+void wlc_ht_mimops_cap_update(struct wlc_info *wlc, u8 mimops_mode)
+{
+ wlc->ht_cap.cap_info &= ~HT_CAP_MIMO_PS_MASK;
+ wlc->ht_cap.cap_info |= (mimops_mode << IEEE80211_HT_CAP_SM_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(struct wlc_info *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(struct wlc_info *wlc, wlc_txq_info_t *qi,
+ bool on, int prio)
+{
+ uint prio_bits;
+ uint cur_bits;
+
+ WL_ERROR("%s: flow control 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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, wlc_txq_info_t *qi, bool on,
+ int prio)
+{
+ struct wlc_if *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(struct wlc_info *wlc, struct osl_info *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(struct wlc_info *wlc, struct osl_info *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);
+}
--- /dev/null
+/*
+ * 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 <wlioctl.h>
+#include <wlc_phy_hal.h>
+#include <wlc_channel.h>
+#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.
+ */
+#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)))
+
+#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)
+ */
+struct wlccore {
+ uint coreidx; /* # sb enumerated core */
+
+ /* fifo */
+ uint *txavail[NFIFO]; /* # tx descriptors available */
+ s16 txpktpend[NFIFO]; /* tx admission control */
+
+ macstat_t *macstat_snapshot; /* mac hw prev read values */
+};
+
+/*
+ * band state (phy+ana+radio)
+ */
+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 */
+};
+
+/* generic function callback takes just one arg */
+typedef void (*cb_fn_t) (void *);
+
+/* tx completion callback takes 3 args */
+typedef void (*pkcb_fn_t) (struct wlc_info *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 {
+ struct wlc_if *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 */
+ struct wl_if *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 */
+
+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;
+
+struct wlc_hw_info {
+ struct osl_info *osh; /* pointer to os handle */
+ bool _piomode; /* true if pio mode */
+ struct wlc_info *wlc;
+
+ /* fifo */
+ struct hnddma_pub *di[NFIFO]; /* hnddma handles, per fifo */
+
+ 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 */
+ u8 etheraddr[ETH_ALEN]; /* 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 */
+ struct bmac_pmq *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 */
+
+ bool ucode_loaded; /* true after ucode downloaded */
+
+
+ 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 struct antsel_info here if more info is
+ * needed
+ */
+};
+
+/* 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 {
+ struct wlc_pub *pub; /* pointer to wlc public state */
+ struct osl_info *osh; /* pointer to os handle */
+ struct wl_info *wl; /* pointer to os-specific private state */
+ d11regs_t *regs; /* pointer to device registers */
+
+ struct wlc_hw_info *hw; /* HW related state used primarily by BMAC */
+
+ /* 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 */
+ struct wlccore *core; /* pointer to active io core */
+ struct wlcband *band; /* pointer to active per-band state */
+ struct wlccore *corestate; /* per-core state (one per hw core) */
+ /* per-band state (one per phy/radio): */
+ struct wlcband *bandstate[MAXBANDS];
+
+ 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 */
+
+
+ struct ampdu_info *ampdu; /* ampdu module handler */
+ struct antsel_info *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 */
+
+ u8 perm_etheraddr[ETH_ALEN]; /* 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 being advertised by this node: */
+ struct ieee80211_ht_cap ht_cap;
+
+ 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
+ */
+ 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;
+
+ struct wlc_if *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 {
+ struct wlc_info *wlc; /* pointer to main wlc structure */
+ struct wlc_pub *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)
+
+#define WLC_BAND_PI_RADIO_CHANSPEC wlc_phy_chanspec_get(wlc->band->pi)
+
+/* sum the individual fifo tx pending packet counts */
+#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)
+
+#define WLC_IS_MATCH_SSID(wlc, ssid1, ssid2, len1, len2) \
+ ((len1 == len2) && !memcmp(ssid1, ssid2, len1))
+
+extern void wlc_high_dpc(struct wlc_info *wlc, u32 macintstatus);
+extern void wlc_fatal_error(struct wlc_info *wlc);
+extern void wlc_bmac_rpc_watchdog(struct wlc_info *wlc);
+extern void wlc_recv(struct wlc_info *wlc, struct sk_buff *p);
+extern bool wlc_dotxstatus(struct wlc_info *wlc, tx_status_t *txs, u32 frm_tx2);
+extern void wlc_txfifo(struct wlc_info *wlc, uint fifo, struct sk_buff *p,
+ bool commit, s8 txpktpend);
+extern void wlc_txfifo_complete(struct wlc_info *wlc, uint fifo, s8 txpktpend);
+extern void wlc_txq_enq(void *ctx, struct scb *scb, struct sk_buff *sdu,
+ uint prec);
+extern void wlc_info_init(struct wlc_info *wlc, int unit);
+extern void wlc_print_txstatus(tx_status_t *txs);
+extern int wlc_xmtfifo_sz_get(struct wlc_info *wlc, uint fifo, uint *blocks);
+extern void wlc_write_template_ram(struct wlc_info *wlc, int offset, int len,
+ void *buf);
+extern void wlc_write_hw_bcntemplates(struct wlc_info *wlc, void *bcn, int len,
+ bool both);
+#if defined(BCMDBG)
+extern void wlc_get_rcmta(struct wlc_info *wlc, int idx,
+ u8 *addr);
+#endif
+extern void wlc_set_rcmta(struct wlc_info *wlc, int idx,
+ const u8 *addr);
+extern void wlc_set_addrmatch(struct wlc_info *wlc, int match_reg_offset,
+ const u8 *addr);
+extern void wlc_read_tsf(struct wlc_info *wlc, u32 *tsf_l_ptr,
+ u32 *tsf_h_ptr);
+extern void wlc_set_cwmin(struct wlc_info *wlc, u16 newmin);
+extern void wlc_set_cwmax(struct wlc_info *wlc, u16 newmax);
+extern void wlc_fifoerrors(struct wlc_info *wlc);
+extern void wlc_pllreq(struct wlc_info *wlc, bool set, mbool req_bit);
+extern void wlc_reset_bmac_done(struct wlc_info *wlc);
+extern void wlc_protection_upd(struct wlc_info *wlc, uint idx, int val);
+extern void wlc_hwtimer_gptimer_set(struct wlc_info *wlc, uint us);
+extern void wlc_hwtimer_gptimer_abort(struct wlc_info *wlc);
+
+#if defined(BCMDBG)
+extern void wlc_print_rxh(d11rxhdr_t *rxh);
+extern void wlc_print_hdrs(struct wlc_info *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
+
+extern void wlc_setxband(struct wlc_hw_info *wlc_hw, uint bandunit);
+extern void wlc_coredisable(struct wlc_hw_info *wlc_hw);
+
+extern bool wlc_valid_rate(struct wlc_info *wlc, ratespec_t rate, int band,
+ bool verbose);
+extern void wlc_ap_upd(struct wlc_info *wlc);
+
+/* helper functions */
+extern void wlc_shm_ssid_upd(struct wlc_info *wlc, wlc_bsscfg_t *cfg);
+extern int wlc_set_gmode(struct wlc_info *wlc, u8 gmode, bool config);
+
+extern void wlc_mac_bcn_promisc_change(struct wlc_info *wlc, bool promisc);
+extern void wlc_mac_bcn_promisc(struct wlc_info *wlc);
+extern void wlc_mac_promisc(struct wlc_info *wlc);
+extern void wlc_txflowcontrol(struct wlc_info *wlc, wlc_txq_info_t *qi, bool on,
+ int prio);
+extern void wlc_txflowcontrol_override(struct wlc_info *wlc, wlc_txq_info_t *qi,
+ bool on, uint override);
+extern bool wlc_txflowcontrol_prio_isset(struct wlc_info *wlc,
+ wlc_txq_info_t *qi, int prio);
+extern void wlc_send_q(struct wlc_info *wlc, wlc_txq_info_t *qi);
+extern int wlc_prep_pdu(struct wlc_info *wlc, struct sk_buff *pdu, uint *fifo);
+
+extern u16 wlc_calc_lsig_len(struct wlc_info *wlc, ratespec_t ratespec,
+ uint mac_len);
+extern ratespec_t wlc_rspec_to_rts_rspec(struct wlc_info *wlc, ratespec_t rspec,
+ bool use_rspec, u16 mimo_ctlchbw);
+extern u16 wlc_compute_rtscts_dur(struct wlc_info *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(struct wlc_info *wlc, d11regs_t *regs);
+
+#if defined(BCMDBG)
+extern void wlc_dump_ie(struct wlc_info *wlc, bcm_tlv_t *ie,
+ struct bcmstrbuf *b);
+#endif
+
+extern bool wlc_ps_check(struct wlc_info *wlc);
+extern void wlc_reprate_init(struct wlc_info *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(struct wlc_info *wlc, uint offset, u16 v);
+extern u16 wlc_read_shm(struct wlc_info *wlc, uint offset);
+extern void wlc_set_shm(struct wlc_info *wlc, uint offset, u16 v, int len);
+extern void wlc_copyto_shm(struct wlc_info *wlc, uint offset, const void *buf,
+ int len);
+extern void wlc_copyfrom_shm(struct wlc_info *wlc, uint offset, void *buf,
+ int len);
+
+extern void wlc_update_beacon(struct wlc_info *wlc);
+extern void wlc_bss_update_beacon(struct wlc_info *wlc,
+ struct wlc_bsscfg *bsscfg);
+
+extern void wlc_update_probe_resp(struct wlc_info *wlc, bool suspend);
+extern void wlc_bss_update_probe_resp(struct wlc_info *wlc, wlc_bsscfg_t *cfg,
+ bool suspend);
+
+extern bool wlc_ismpc(struct wlc_info *wlc);
+extern bool wlc_is_non_delay_mpc(struct wlc_info *wlc);
+extern void wlc_radio_mpc_upd(struct wlc_info *wlc);
+extern bool wlc_prec_enq(struct wlc_info *wlc, struct pktq *q, void *pkt,
+ int prec);
+extern bool wlc_prec_enq_head(struct wlc_info *wlc, struct pktq *q,
+ struct sk_buff *pkt, int prec, bool head);
+extern u16 wlc_phytxctl1_calc(struct wlc_info *wlc, ratespec_t rspec);
+extern void wlc_compute_plcp(struct wlc_info *wlc, ratespec_t rate, uint length,
+ u8 *plcp);
+extern uint wlc_calc_frame_time(struct wlc_info *wlc, ratespec_t ratespec,
+ u8 preamble_type, uint mac_len);
+
+extern void wlc_set_chanspec(struct wlc_info *wlc, chanspec_t chanspec);
+
+extern bool wlc_timers_init(struct wlc_info *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, struct wlc_if *wlcif);
+
+#if defined(BCMDBG)
+extern void wlc_print_ies(struct wlc_info *wlc, u8 *ies, uint ies_len);
+#endif
+
+extern int wlc_set_nmode(struct wlc_info *wlc, s32 nmode);
+extern void wlc_ht_mimops_cap_update(struct wlc_info *wlc, u8 mimops_mode);
+extern void wlc_mimops_action_ht_send(struct wlc_info *wlc,
+ wlc_bsscfg_t *bsscfg, u8 mimops_mode);
+
+extern void wlc_switch_shortslot(struct wlc_info *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_set_ratetable(struct wlc_info *wlc);
+extern int wlc_set_mac(wlc_bsscfg_t *cfg);
+extern void wlc_beacon_phytxctl_txant_upd(struct wlc_info *wlc,
+ ratespec_t bcn_rate);
+extern void wlc_mod_prb_rsp_rate_table(struct wlc_info *wlc, uint frame_len);
+extern ratespec_t wlc_lowest_basic_rspec(struct wlc_info *wlc,
+ wlc_rateset_t *rs);
+extern u16 wlc_compute_bcntsfoff(struct wlc_info *wlc, ratespec_t rspec,
+ bool short_preamble, bool phydelay);
+extern void wlc_radio_disable(struct wlc_info *wlc);
+extern void wlc_bcn_li_upd(struct wlc_info *wlc);
+
+extern int wlc_get_revision_info(struct wlc_info *wlc, void *buf, uint len);
+extern void wlc_out(struct wlc_info *wlc);
+extern void wlc_set_home_chanspec(struct wlc_info *wlc, chanspec_t chanspec);
+extern void wlc_watchdog_upd(struct wlc_info *wlc, bool tbtt);
+extern bool wlc_ps_allowed(struct wlc_info *wlc);
+extern bool wlc_stay_awake(struct wlc_info *wlc);
+extern void wlc_wme_initparams_sta(struct wlc_info *wlc, wme_param_ie_t *pe);
+
+extern void wlc_bss_list_free(struct wlc_info *wlc, wlc_bss_list_t *bss_list);
+extern void wlc_ht_mimops_cap_update(struct wlc_info *wlc, u8 mimops_mode);
+#endif /* _wlc_h_ */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <osl.h>
+#include <bcmutils.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_event.h>
+
+#include <wlc_mac80211.h>
+
+#include <wlc_bmac.h>
+#include <wlc_phy_shim.h>
+#include <wlc_phy_hal.h>
+#include <wl_export.h>
+#include <wl_dbg.h>
+
+/* PHY SHIM module specific state */
+struct wlc_phy_shim_info {
+ struct wlc_hw_info *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(struct wlc_hw_info *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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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 * IEEE80211_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 << IEEE80211_HT_CAP_SM_PS_SHIFT) |\
+ IEEE80211_HT_CAP_SUP_WIDTH_20_40 | IEEE80211_HT_CAP_GRN_FLD |\
+ HT_CAP_MAX_AMSDU | IEEE80211_HT_CAP_DSSSCCK40)
+
+/* 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 {
+ u8 BSSID[ETH_ALEN]; /* 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 */
+ 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.
+ */
+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 */
+ struct osl_info *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 */
+
+ u8 cur_etheraddr[ETH_ALEN]; /* our local ethernet address */
+
+ u8 *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.
+ */
+
+ 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 */
+};
+
+/* 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) ((wlc)->monitor)
+
+#define PROMISC_ENAB(wlc) ((wlc)->promisc)
+
+#define WLC_PREC_COUNT 16 /* Max precedence level implemented */
+
+/* pri is priority 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, struct osl_info *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, struct wlc_if *wlcif,
+ void *p, uint ac);
+extern bool wlc_sendpkt_mac80211(struct wlc_info *wlc, struct sk_buff *sdu,
+ struct ieee80211_hw *hw);
+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 void wlc_mac_bcn_promisc_change(struct wlc_info *wlc, bool promisc);
+extern void wlc_set_addrmatch(struct wlc_info *wlc, int match_reg_offset,
+ const u8 *addr);
+extern void wlc_wme_setparams(struct wlc_info *wlc, u16 aci, void *arg,
+ bool suspend);
+
+extern struct wlc_pub *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(struct wlc_pub *pub, const bcm_iovar_t *vi,
+ void *arg,
+ int len, bool set);
+
+extern int wlc_module_register(struct wlc_pub *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(struct wlc_pub *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"
+
+/* BMAC RPC: 7 u32 params: pkttotlen, fifo, commit, fid, txpktpend, pktflag, rpc_id */
+#define WLC_RPCTX_PARAMS 32
+
+#endif /* _wlc_pub_h_ */
--- /dev/null
+/*
+ * 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 <linux/module.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmendian.h>
+#include <wlioctl.h>
+
+#include <sbhndpio.h>
+#include <sbhnddma.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;
+
+ memset(rateset, 0, 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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(struct wlc_info *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 */
+ u8 ea[ETH_ALEN]; /* 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_ */
--- /dev/null
+/*
+ * 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/module.h>
+#include <wlc_cfg.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 <sbhndpio.h>
+#include <sbhnddma.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_event.h>
+#include <wlc_mac80211.h>
+#include <wlc_scb.h>
+#include <wl_export.h>
+#include <wlc_bmac.h>
+#include <wlc_stf.h>
+#include <wl_dbg.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(struct wlc_info *wlc);
+static bool wlc_stf_stbc_tx_set(struct wlc_info *wlc, s32 int_val);
+static int wlc_stf_txcore_set(struct wlc_info *wlc, u8 Nsts, u8 val);
+static int wlc_stf_spatial_policy_set(struct wlc_info *wlc, int val);
+static void wlc_stf_stbc_rx_ht_update(struct wlc_info *wlc, int val);
+
+static void _wlc_stf_phy_txant_upd(struct wlc_info *wlc);
+static u16 _wlc_stf_phytxchain_sel(struct wlc_info *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(struct wlc_info *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_info &= ~HT_CAP_RX_STBC_MASK;
+ wlc->ht_cap.cap_info |= (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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
+{
+ return (wlc->ht_cap.cap_info & HT_CAP_RX_STBC_MASK)
+ >> HT_CAP_RX_STBC_SHIFT;
+}
+
+static bool wlc_stf_stbc_tx_set(struct wlc_info *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_info &= ~IEEE80211_HT_CAP_TX_STBC;
+ else
+ wlc->ht_cap.cap_info |= IEEE80211_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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, struct wlcband *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(struct wlc_info *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(struct wlc_info *wlc)
+{
+}
+
+int wlc_stf_ant_txant_validate(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc)
+{
+ _wlc_stf_phy_txant_upd(wlc);
+}
+
+void wlc_stf_phy_chain_calc(struct wlc_info *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(struct wlc_info *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(struct wlc_info *wlc, ratespec_t rspec)
+{
+ return _wlc_stf_phytxchain_sel(wlc, rspec);
+}
+
+u16 wlc_stf_d11hdrs_phyctl_txant(struct wlc_info *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;
+}
--- /dev/null
+/*
+ * 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(struct wlc_info *wlc);
+extern void wlc_stf_detach(struct wlc_info *wlc);
+
+extern void wlc_tempsense_upd(struct wlc_info *wlc);
+extern void wlc_stf_ss_algo_channel_get(struct wlc_info *wlc,
+ u16 *ss_algo_channel,
+ chanspec_t chanspec);
+extern int wlc_stf_ss_update(struct wlc_info *wlc, struct wlcband *band);
+extern void wlc_stf_phy_txant_upd(struct wlc_info *wlc);
+extern int wlc_stf_txchain_set(struct wlc_info *wlc, s32 int_val, bool force);
+extern int wlc_stf_rxchain_set(struct wlc_info *wlc, s32 int_val);
+extern bool wlc_stf_stbc_rx_set(struct wlc_info *wlc, s32 int_val);
+
+extern int wlc_stf_ant_txant_validate(struct wlc_info *wlc, s8 val);
+extern void wlc_stf_phy_txant_upd(struct wlc_info *wlc);
+extern void wlc_stf_phy_chain_calc(struct wlc_info *wlc);
+extern u16 wlc_stf_phytxchain_sel(struct wlc_info *wlc, ratespec_t rspec);
+extern u16 wlc_stf_d11hdrs_phyctl_txant(struct wlc_info *wlc, ratespec_t rspec);
+extern u16 wlc_stf_spatial_expansion_get(struct wlc_info *wlc,
+ ratespec_t rspec);
+#endif /* _wlc_stf_h_ */
--- /dev/null
+/*
+ * 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 */
+
+struct wlc_info;
+struct wlc_hw_info;
+struct wlc_if;
+struct wl_if;
+struct ampdu_info;
+struct antsel_info;
+struct bmac_pmq;
+
+struct d11init;
+
+#ifndef _hnddma_pub_
+#define _hnddma_pub_
+struct hnddma_pub;
+#endif /* _hnddma_pub_ */
+
+#endif /* _wlc_types_h_ */