build/patch/kernel/sun8i-dev/add-xradio-wireless-driver.patch
2016-12-29 13:10:47 +03:00

24127 lines
700 KiB
Diff

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 8c8edaf1..122b980b 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -32,6 +32,7 @@ source "drivers/net/wireless/rsi/Kconfig"
source "drivers/net/wireless/st/Kconfig"
source "drivers/net/wireless/ti/Kconfig"
source "drivers/net/wireless/zydas/Kconfig"
+source "drivers/net/wireless/xradio/Kconfig"
config PCMCIA_RAYCS
tristate "Aviator/Raytheon 2.4GHz wireless support"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index f00d4295..50db6f2b 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/
obj-$(CONFIG_WLAN_VENDOR_ST) += st/
obj-$(CONFIG_WLAN_VENDOR_TI) += ti/
obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/
+obj-$(CONFIG_WLAN_VENDOR_XRADIO) += xradio/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
diff --git a/drivers/net/wireless/xradio/Kconfig b/drivers/net/wireless/xradio/Kconfig
new file mode 100644
index 00000000..85ff4604
--- /dev/null
+++ b/drivers/net/wireless/xradio/Kconfig
@@ -0,0 +1,63 @@
+config WLAN_VENDOR_XRADIO
+ tristate "XRADIO WLAN support"
+ depends on MAC80211
+ default n
+ help
+
+ This is an experimental driver for the XRADIO chip-set.
+ Enabling this option enables the generic driver without
+ any platform support.
+ Please select the appropriate platform below.
+
+config XRADIO_SDIO
+ bool
+
+config XRADIO_USE_EXTENSIONS
+ bool
+
+config XRADIO_5GHZ_SUPPORT
+ bool
+
+if WLAN_VENDOR_XRADIO
+
+config XRADIO_XR819
+ bool "XRADIO XR819 support"
+ depends on WLAN_VENDOR_XRADIO
+ select XRADIO_SDIO
+ select XRADIO_USE_EXTENSIONS
+ select XRADIO_5GHZ_SUPPORT
+ default y
+
+config XRADIO_NON_POWER_OF_TWO_BLOCKSIZES
+ bool "Platform supports non-power-of-two SDIO transfer"
+ depends on WLAN_VENDOR_XRADIO
+ default y
+ help
+ Say N here only if you are running the driver on a platform
+ which does not have support for non-power-of-two SDIO transfer.
+ If unsure, say Y.
+
+config XRADIO_5GHZ_SUPPORT
+ bool "5GHz band support"
+ depends on WLAN_VENDOR_XRADIO
+ default n
+ help
+ Say Y if your device supports 5GHz band. If unsure, say N.
+
+config XRADIO_WAPI_SUPPORT
+ bool "WAPI support"
+ depends on WLAN_VENDOR_XRADIO
+ default n
+ help
+ Say Y if your compat-wireless support WAPI.
+ If unsure, say N.
+
+config XRADIO_USE_EXTENSIONS
+ bool "Extensions for WFD and PS mode"
+ depends on WLAN_VENDOR_XRADIO
+ default y
+ help
+ Say Y if you want to include XR extensions
+ If unsure, say Y.
+
+endif
diff --git a/drivers/net/wireless/xradio/Makefile b/drivers/net/wireless/xradio/Makefile
new file mode 100644
index 00000000..b2b0badc
--- /dev/null
+++ b/drivers/net/wireless/xradio/Makefile
@@ -0,0 +1,52 @@
+
+obj-$(CONFIG_WLAN_VENDOR_XRADIO) += xradio_wlan.o
+
+xradio_wlan-objs := \
+ fwio.o \
+ txrx.o \
+ main.o \
+ queue.o \
+ hwio.o \
+ bh.o \
+ wsm.o \
+ sta.o \
+ ap.o \
+ keys.o \
+ scan.o \
+ debug.o \
+ module.o \
+ sdio.o \
+ pm.o
+
+#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DP2P_MULTIVIF
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DMCAST_FWDING
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_SUSPEND_RESUME_FILTER_ENABLE
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DAP_AGGREGATE_FW_FIX
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DAP_HT_CAP_UPDATE
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DHW_ERROR_WIFI_RESET
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DAP_HT_COMPAT_FIX
+#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_DEBUG
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_DUMP_ON_ERROR
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_DEBUGFS
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES
+
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_SUSPEND_POWER_OFF
+
+# Extra IE for probe response from upper layer is needed in P2P GO
+# For offloading probe response to FW, the extra IE must be included
+# in the probe response template
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DPROBE_RESP_EXTRA_IE
+
+# Modified by wzw
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DTES_P2P_0002_ROC_RESTART
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DTES_P2P_000B_EXTEND_INACTIVITY_CNT
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DTES_P2P_000B_DISABLE_EAPOL_FILTER
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_USE_LONG_DTIM_PERIOD
+ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_USE_LONG_KEEP_ALIVE_PERIOD
+
+#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DDEBUG
+
+#~dgp
+#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_DISABLE_HW_CRYPTO
+
+ldflags-$(CONFIG_WLAN_VENDOR_XRADIO) += --strip-debug
diff --git a/drivers/net/wireless/xradio/ap.c b/drivers/net/wireless/xradio/ap.c
new file mode 100644
index 00000000..a51ed77e
--- /dev/null
+++ b/drivers/net/wireless/xradio/ap.c
@@ -0,0 +1,1670 @@
+/*
+ * STA and AP APIs for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include "xradio.h"
+#include "sta.h"
+#include "ap.h"
+#include "bh.h"
+#include "net/mac80211.h"
+
+#define XRADIO_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
+#define XRADIO_ENABLE_ARP_FILTER_OFFLOAD 3
+
+#ifndef ERP_INFO_BYTE_OFFSET
+#define ERP_INFO_BYTE_OFFSET 2
+#endif
+
+static int xradio_upload_beacon(struct xradio_vif *priv);
+#ifdef PROBE_RESP_EXTRA_IE
+static int xradio_upload_proberesp(struct xradio_vif *priv);
+#endif
+static int xradio_upload_pspoll(struct xradio_vif *priv);
+static int xradio_upload_null(struct xradio_vif *priv);
+static int xradio_upload_qosnull(struct xradio_vif *priv);
+static int xradio_start_ap(struct xradio_vif *priv);
+static int xradio_update_beaconing(struct xradio_vif *priv);
+/*
+static int xradio_enable_beaconing(struct xradio_vif *priv,
+ bool enable);
+*/
+static void __xradio_sta_notify(struct xradio_vif *priv,
+ enum sta_notify_cmd notify_cmd,
+ int link_id);
+
+/* ******************************************************************** */
+/* AP API */
+int xradio_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct xradio_link_entry *entry;
+ struct sk_buff *skb;
+#ifdef AP_AGGREGATE_FW_FIX
+ struct xradio_common *hw_priv = hw->priv;
+#endif
+
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+
+ if (priv->mode != NL80211_IFTYPE_AP) {
+ return 0;
+ }
+
+ sta_priv->priv = priv;
+ sta_priv->link_id = xradio_find_link_id(priv, sta->addr);
+ if (SYS_WARN(!sta_priv->link_id)) {
+ /* Impossible error */
+ wiphy_debug(hw->wiphy, "No more link IDs available.\n");
+ return -ENOENT;
+ }
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) ==
+ IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) {
+ priv->sta_asleep_mask |= BIT(sta_priv->link_id);
+ }
+ entry->status = XRADIO_LINK_HARD;
+ while ((skb = skb_dequeue(&entry->rx_queue)))
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+
+#ifdef AP_AGGREGATE_FW_FIX
+ hw_priv->connected_sta_cnt++;
+ if(hw_priv->connected_sta_cnt>1) {
+ wsm_lock_tx(hw_priv);
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+ XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+ priv->if_id));
+ wsm_unlock_tx(hw_priv);
+ }
+#endif
+
+ return 0;
+}
+
+int xradio_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_sta_priv *sta_priv =
+ (struct xradio_sta_priv *)&sta->drv_priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct xradio_link_entry *entry;
+
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+
+ if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) {
+ wiphy_warn(hw->wiphy, "no station to remove\n");
+ return 0;
+ }
+
+ entry = &priv->link_id_db[sta_priv->link_id - 1];
+ spin_lock_bh(&priv->ps_state_lock);
+ entry->status = XRADIO_LINK_RESERVE;
+ entry->timestamp = jiffies;
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ spin_unlock_bh(&priv->ps_state_lock);
+ flush_workqueue(hw_priv->workqueue);
+
+#ifdef AP_AGGREGATE_FW_FIX
+ hw_priv->connected_sta_cnt--;
+ if(hw_priv->connected_sta_cnt <= 1) {
+ if ((priv->if_id != 1) ||
+ ((priv->if_id == 1) && hw_priv->is_go_thru_go_neg)) {
+ wsm_lock_tx(hw_priv);
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+ XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+ priv->if_id));
+ wsm_unlock_tx(hw_priv);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+static void __xradio_sta_notify(struct xradio_vif *priv,
+ enum sta_notify_cmd notify_cmd,
+ int link_id)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ u32 bit, prev;
+
+ /* Zero link id means "for all link IDs" */
+ if (link_id)
+ bit = BIT(link_id);
+ else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
+ bit = 0;
+ else
+ bit = priv->link_id_map;
+ prev = priv->sta_asleep_mask & bit;
+
+ switch (notify_cmd) {
+ case STA_NOTIFY_SLEEP:
+ if (!prev) {
+ if (priv->buffered_multicasts &&
+ !priv->sta_asleep_mask)
+ queue_work(hw_priv->workqueue,
+ &priv->multicast_start_work);
+ priv->sta_asleep_mask |= bit;
+ }
+ break;
+ case STA_NOTIFY_AWAKE:
+ if (prev) {
+ priv->sta_asleep_mask &= ~bit;
+ priv->pspoll_mask &= ~bit;
+ if (priv->tx_multicast && link_id &&
+ !priv->sta_asleep_mask)
+ queue_work(hw_priv->workqueue,
+ &priv->multicast_stop_work);
+ xradio_bh_wakeup(hw_priv);
+ }
+ break;
+ }
+}
+
+void xradio_sta_notify(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta)
+{
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv;
+
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+ spin_lock_bh(&priv->ps_state_lock);
+ __xradio_sta_notify(priv, notify_cmd, sta_priv->link_id);
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+static void xradio_ps_notify(struct xradio_vif *priv,
+ int link_id, bool ps)
+{
+ if (link_id > MAX_STA_IN_AP_MODE) {
+ ap_printk(XRADIO_DBG_WARN,"link_id is invalid=%d\n", link_id);
+ return;
+ }
+
+ ap_printk(XRADIO_DBG_NIY, "%s for LinkId: %d. STAs asleep: %.8X\n",
+ ps ? "Stop" : "Start", link_id, priv->sta_asleep_mask);
+
+ /* TODO:COMBO: __xradio_sta_notify changed. */
+ __xradio_sta_notify(priv, ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
+}
+
+static int xradio_set_tim_impl(struct xradio_vif *priv, bool aid0_bit_set)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct sk_buff *skb;
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ };
+ u16 tim_offset, tim_length;
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ ap_printk(XRADIO_DBG_MSG, "%s mcast: %s.\n", __func__,
+ aid0_bit_set ? "ena" : "dis");
+
+ skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length);
+ if (!skb) {
+ __xradio_flush(hw_priv, true, priv->if_id);
+ return -ENOENT;
+ }
+
+ if (tim_offset && tim_length >= 6) {
+ /* Ignore DTIM count from mac80211:
+ * firmware handles DTIM internally. */
+ skb->data[tim_offset + 2] = 0;
+
+ /* Set/reset aid0 bit */
+ if (aid0_bit_set)
+ skb->data[tim_offset + 4] |= 1;
+ else
+ skb->data[tim_offset + 4] &= ~1;
+ }
+
+ update_ie.ies = &skb->data[tim_offset];
+ update_ie.length = tim_length;
+ //filter same tim info, yangfh
+ if(memcmp(priv->last_tim, update_ie.ies, tim_length)) {
+ SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
+ memcpy(priv->last_tim, update_ie.ies, tim_length);
+ ap_printk(XRADIO_DBG_MSG,"%02x %02x %02x %02x %02x %02x\n",
+ update_ie.ies[0], update_ie.ies[1], update_ie.ies[2],
+ update_ie.ies[3], update_ie.ies[4], update_ie.ies[5]);
+ }
+
+ dev_kfree_skb(skb);
+
+ return 0;
+}
+
+void xradio_set_tim_work(struct work_struct *work)
+{
+ struct xradio_vif *priv = container_of(work, struct xradio_vif, set_tim_work);
+ xradio_set_tim_impl(priv, priv->aid0_bit_set);
+}
+
+int xradio_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set)
+{
+ struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv;
+ struct xradio_vif *priv = sta_priv->priv;
+
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+ SYS_WARN(priv->mode != NL80211_IFTYPE_AP);
+ queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
+ return 0;
+}
+
+void xradio_set_cts_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, set_cts_work.work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0};
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ .ies = erp_ie,
+ .length = 3,
+ };
+ u32 erp_info;
+ __le32 use_cts_prot;
+
+ mutex_lock(&hw_priv->conf_mutex);
+ erp_info = priv->erp_info;
+ mutex_unlock(&hw_priv->conf_mutex);
+ use_cts_prot = (erp_info & WLAN_ERP_USE_PROTECTION)? __cpu_to_le32(1) : 0;
+
+ erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
+
+ ap_printk(XRADIO_DBG_MSG, "ERP information 0x%x\n", erp_info);
+
+ /* TODO:COMBO: If 2 interfaces are on the same channel they share
+ the same ERP values */
+ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION,
+ &use_cts_prot, sizeof(use_cts_prot), priv->if_id));
+ /* If STA Mode update_ie is not required */
+ if (priv->mode != NL80211_IFTYPE_STATION) {
+ SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
+ }
+
+ return;
+}
+
+static int xradio_set_btcoexinfo(struct xradio_vif *priv)
+{
+ struct wsm_override_internal_txrate arg;
+ int ret = 0;
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ /* Plumb PSPOLL and NULL template */
+ SYS_WARN(xradio_upload_pspoll(priv));
+ SYS_WARN(xradio_upload_null(priv));
+ } else {
+ return 0;
+ }
+
+ memset(&arg, 0, sizeof(struct wsm_override_internal_txrate));
+
+ if (!priv->vif->p2p) {
+ /* STATION mode */
+ if (priv->bss_params.operationalRateSet & ~0xF) {
+ ap_printk(XRADIO_DBG_NIY, "STA has ERP rates\n");
+ /* G or BG mode */
+ arg.internalTxRate = (__ffs(
+ priv->bss_params.operationalRateSet & ~0xF));
+ } else {
+ ap_printk(XRADIO_DBG_NIY, "STA has non ERP rates\n");
+ /* B only mode */
+ arg.internalTxRate = (__ffs(
+ priv->association_mode.basicRateSet));
+ }
+ arg.nonErpInternalTxRate = (__ffs(
+ priv->association_mode.basicRateSet));
+ } else {
+ /* P2P mode */
+ arg.internalTxRate = (__ffs(
+ priv->bss_params.operationalRateSet & ~0xF));
+ arg.nonErpInternalTxRate = (__ffs(
+ priv->bss_params.operationalRateSet & ~0xF));
+ }
+
+ ap_printk(XRADIO_DBG_NIY, "BTCOEX_INFO" "MODE %d, internalTxRate : %x,"
+ "nonErpInternalTxRate: %x\n", priv->mode, arg.internalTxRate,
+ arg.nonErpInternalTxRate);
+
+ ret = SYS_WARN(wsm_write_mib(xrwl_vifpriv_to_hwpriv(priv),
+ WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ &arg, sizeof(arg), priv->if_id));
+
+ return ret;
+}
+
+void xradio_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed)
+{
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+
+#ifdef P2P_MULTIVIF
+ if (priv->if_id == XRWL_GENERIC_IF_ID)
+ return;
+#endif
+ mutex_lock(&hw_priv->conf_mutex);
+ if (changed & BSS_CHANGED_BSSID) {
+#ifdef CONFIG_XRADIO_TESTMODE
+ spin_lock_bh(&hw_priv->tsm_lock);
+ if (hw_priv->tsm_info.sta_associated) {
+ unsigned now = jiffies;
+ hw_priv->tsm_info.sta_roamed = 1;
+ if ((now - hw_priv->tsm_info.txconf_timestamp_vo) >
+ (now - hw_priv->tsm_info.rx_timestamp_vo))
+ hw_priv->tsm_info.use_rx_roaming = 1;
+ } else {
+ hw_priv->tsm_info.sta_associated = 1;
+ }
+ spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ memcpy(priv->bssid, info->bssid, ETH_ALEN);
+ xradio_setup_mac_pvif(priv);
+ }
+
+ /* TODO: BSS_CHANGED_IBSS */
+ if (changed & BSS_CHANGED_ARP_FILTER) {
+ struct wsm_arp_ipv4_filter filter = {0};
+ int i;
+ ap_printk(XRADIO_DBG_MSG, "[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n",
+ info->arp_addr_cnt);
+
+ if (info->arp_addr_cnt){
+ if (vif->type == NL80211_IFTYPE_STATION)
+ filter.enable = (u32)XRADIO_ENABLE_ARP_FILTER_OFFLOAD;
+ else if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ filter.enable = (u32)(1<<1);
+ else
+ filter.enable = 0;
+ }
+
+ /* Currently only one IP address is supported by firmware.
+ * In case of more IPs arp filtering will be disabled. */
+ if (info->arp_addr_cnt > 0 &&
+ info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
+ for (i = 0; i < info->arp_addr_cnt; i++) {
+ filter.ipv4Address[i] = info->arp_addr_list[i];
+ ap_printk(XRADIO_DBG_NIY, "[STA]addr[%d]: 0x%X\n", i, filter.ipv4Address[i]);
+ }
+ } else
+ filter.enable = 0;
+
+ if (filter.enable)
+ xradio_set_arpreply(dev, vif);
+
+ priv->filter4.enable = filter.enable;
+ ap_printk(XRADIO_DBG_NIY, "[STA]arp ip filter enable: %d\n", __le32_to_cpu(filter.enable));
+
+ if (wsm_set_arp_ipv4_filter(hw_priv, &filter, priv->if_id))
+ SYS_WARN(1);
+
+ if (filter.enable &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA)) {
+ /* Firmware requires that value for this 1-byte field must
+ * be specified in units of 500us. Values above the 128ms
+ * threshold are not supported. */
+ //if (info->dynamic_ps_timeout >= 0x80)
+ // priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
+ //else
+ // priv->powersave_mode.fastPsmIdlePeriod = info->dynamic_ps_timeout << 1;
+
+ priv->powersave_mode.fastPsmIdlePeriod = 200;//when connected,the dev->conf.dynamic_ps_timeout value is 0
+ priv->powersave_mode.apPsmChangePeriod = 200; //100ms, add by yangfh
+ ap_printk(XRADIO_DBG_NIY, "[STA]fastPsmIdle=%d, apPsmChange=%d\n",
+ priv->powersave_mode.fastPsmIdlePeriod,
+ priv->powersave_mode.apPsmChangePeriod);
+
+ if (priv->setbssparams_done) {
+ int ret = 0;
+ struct wsm_set_pm pm = priv->powersave_mode;
+ if (priv->user_power_set_true)
+ priv->powersave_mode.pmMode = priv->user_pm_mode;
+ else if ((priv->power_set_true &&
+ ((priv->powersave_mode.pmMode == WSM_PSM_ACTIVE) ||
+ (priv->powersave_mode.pmMode == WSM_PSM_PS))) ||
+ !priv->power_set_true)
+ priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+
+ ret = xradio_set_pm (priv, &priv->powersave_mode);
+ if(ret)
+ priv->powersave_mode = pm;
+ } else {
+ priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+ }
+ priv->power_set_true = 0;
+ priv->user_power_set_true = 0;
+ }
+ }
+
+ if (changed & BSS_CHANGED_BEACON) {
+ ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_BEACON\n");
+#ifdef HIDDEN_SSID
+ if(priv->join_status != XRADIO_JOIN_STATUS_AP) {
+ priv->hidden_ssid = info->hidden_ssid;
+ priv->ssid_length = info->ssid_len;
+ memcpy(priv->ssid, info->ssid, info->ssid_len);
+ } else
+ ap_printk(XRADIO_DBG_NIY, "priv->join_status=%d\n", priv->join_status);
+#endif
+ SYS_WARN(xradio_upload_beacon(priv));
+ SYS_WARN(xradio_update_beaconing(priv));
+ }
+
+ if (changed & BSS_CHANGED_BEACON_ENABLED) {
+ ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_BEACON_ENABLED dummy\n");
+ priv->enable_beacon = info->enable_beacon;
+ }
+
+ if (changed & BSS_CHANGED_BEACON_INT) {
+ ap_printk(XRADIO_DBG_NIY, "CHANGED_BEACON_INT\n");
+ /* Restart AP only when connected */
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ SYS_WARN(xradio_update_beaconing(priv));
+ }
+
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ wsm_lock_tx(hw_priv);
+ priv->wep_default_key_id = -1;
+ wsm_unlock_tx(hw_priv);
+
+ if (!info->assoc /* && !info->ibss_joined */) {
+ priv->cqm_link_loss_count = XRADIO_LINK_LOSS_THOLD_DEF;
+ priv->cqm_beacon_loss_count = XRADIO_BSS_LOSS_THOLD_DEF;
+ priv->cqm_tx_failure_thold = 0;
+ }
+ priv->cqm_tx_failure_count = 0;
+ }
+
+ if (changed &
+ (BSS_CHANGED_ASSOC |
+ BSS_CHANGED_BASIC_RATES |
+ BSS_CHANGED_ERP_PREAMBLE |
+ BSS_CHANGED_HT |
+ BSS_CHANGED_ERP_SLOT)) {
+ int is_combo = 0;
+ int i;
+ struct xradio_vif *tmp_priv;
+ ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_ASSOC.\n");
+ if (info->assoc) { /* TODO: ibss_joined */
+ struct ieee80211_sta *sta = NULL;
+ if (info->dtim_period)
+ priv->join_dtim_period = info->dtim_period;
+ priv->beacon_int = info->beacon_int;
+
+ /* Associated: kill join timeout */
+ cancel_delayed_work_sync(&priv->join_timeout);
+
+ rcu_read_lock();
+ if (info->bssid)
+ sta = ieee80211_find_sta(vif, info->bssid);
+ if (sta) {
+ /* TODO:COMBO:Change this once
+ * mac80211 changes are available */
+ SYS_BUG(!hw_priv->channel);
+ hw_priv->ht_oper.ht_cap = sta->ht_cap;
+ priv->bss_params.operationalRateSet =__cpu_to_le32(
+ xradio_rate_mask_to_wsm(hw_priv, sta->supp_rates[hw_priv->channel->band]));
+ /* TODO by Icenowy: I think this may lead to some problems. */
+// hw_priv->ht_oper.channel_type = info->channel_type;
+ hw_priv->ht_oper.operation_mode = info->ht_operation_mode;
+ } else {
+ memset(&hw_priv->ht_oper, 0, sizeof(hw_priv->ht_oper));
+ priv->bss_params.operationalRateSet = -1;
+ }
+ rcu_read_unlock();
+ priv->htcap = (sta && xradio_is_ht(&hw_priv->ht_oper));
+ xradio_for_each_vif(hw_priv, tmp_priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv)
+#else
+ if (!tmp_priv)
+#endif
+ continue;
+ if (tmp_priv->join_status >= XRADIO_JOIN_STATUS_STA)
+ is_combo++;
+ }
+
+ if (is_combo > 1) {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE;
+ ap_printk(XRADIO_DBG_WARN, "%sASSOC is_combo %d\n",
+ (priv->join_status == XRADIO_JOIN_STATUS_STA)?"[STA] ":"",
+ hw_priv->vif0_throttle);
+ } else if ((priv->join_status == XRADIO_JOIN_STATUS_STA) && priv->htcap) {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE;
+ ap_printk(XRADIO_DBG_WARN, "[STA] ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle);
+ } else {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE;
+ ap_printk(XRADIO_DBG_WARN, "ASSOC not_combo 11BG %d\n",hw_priv->vif0_throttle);
+ }
+
+ if (sta) {
+ __le32 val = 0;
+ if (hw_priv->ht_oper.operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) {
+ ap_printk(XRADIO_DBG_NIY,"[STA] Non-GF STA present\n");
+ /* Non Green field capable STA */
+ val = __cpu_to_le32(BIT(1));
+ }
+ SYS_WARN(wsm_write_mib(hw_priv, WSM_MID_ID_SET_HT_PROTECTION,
+ &val, sizeof(val), priv->if_id));
+ }
+
+ priv->association_mode.greenfieldMode = xradio_ht_greenfield(&hw_priv->ht_oper);
+ priv->association_mode.flags =
+ WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES |
+ WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE |
+ WSM_ASSOCIATION_MODE_USE_HT_MODE |
+ WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET |
+ WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING;
+
+ priv->association_mode.preambleType =
+ (info->use_short_preamble ? WSM_JOIN_PREAMBLE_SHORT : WSM_JOIN_PREAMBLE_LONG);
+ priv->association_mode.basicRateSet = __cpu_to_le32(
+ xradio_rate_mask_to_wsm(hw_priv,info->basic_rates));
+ priv->association_mode.mpduStartSpacing =
+ xradio_ht_ampdu_density(&hw_priv->ht_oper);
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold;
+ //priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold;
+ //priv->cqm_tx_failure_count = 0;
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+
+ priv->bss_params.beaconLostCount = (priv->cqm_beacon_loss_count ?
+ priv->cqm_beacon_loss_count : priv->cqm_link_loss_count);
+
+ priv->bss_params.aid = info->aid;
+
+ if (priv->join_dtim_period < 1)
+ priv->join_dtim_period = 1;
+
+ ap_printk(XRADIO_DBG_MSG, "[STA] DTIM %d, interval: %d\n",
+ priv->join_dtim_period, priv->beacon_int);
+ ap_printk(XRADIO_DBG_MSG, "[STA] Preamble: %d, " \
+ "Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n",
+ priv->association_mode.preambleType,
+ priv->association_mode.greenfieldMode,
+ priv->bss_params.aid,
+ priv->bss_params.operationalRateSet,
+ priv->association_mode.basicRateSet);
+ SYS_WARN(wsm_set_association_mode(hw_priv, &priv->association_mode, priv->if_id));
+ SYS_WARN(wsm_keep_alive_period(hw_priv, XRADIO_KEEP_ALIVE_PERIOD /* sec */,
+ priv->if_id));
+ SYS_WARN(wsm_set_bss_params(hw_priv, &priv->bss_params, priv->if_id));
+ priv->setbssparams_done = true;
+#ifdef XRADIO_USE_LONG_DTIM_PERIOD
+{
+ int join_dtim_period_extend;
+ if (priv->join_dtim_period <= 3) {
+ join_dtim_period_extend = priv->join_dtim_period * 3;
+ } else if (priv->join_dtim_period <= 5) {
+ join_dtim_period_extend = priv->join_dtim_period * 2;
+ } else {
+ join_dtim_period_extend = priv->join_dtim_period;
+ }
+ SYS_WARN(wsm_set_beacon_wakeup_period(hw_priv,
+ ((priv->beacon_int * join_dtim_period_extend) > MAX_BEACON_SKIP_TIME_MS
+ ? 1 : join_dtim_period_extend) , 0, priv->if_id));
+}
+#else
+ SYS_WARN(wsm_set_beacon_wakeup_period(hw_priv,
+ ((priv->beacon_int * priv->join_dtim_period) > MAX_BEACON_SKIP_TIME_MS
+ ? 1 : priv->join_dtim_period) , 0, priv->if_id));
+#endif
+ if (priv->htcap) {
+ wsm_lock_tx(hw_priv);
+ /* Statically enabling block ack for TX/RX */
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv, hw_priv->ba_tid_mask,
+ hw_priv->ba_tid_mask, priv->if_id));
+ wsm_unlock_tx(hw_priv);
+ }
+ /*set ps active,avoid that when connecting process,the device sleeps,then can't receive pkts.*/
+ if (changed & BSS_CHANGED_ASSOC)
+ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+ xradio_set_pm(priv, &priv->powersave_mode);
+ if (priv->vif->p2p) {
+ ap_printk(XRADIO_DBG_NIY, "[STA] Setting p2p powersave configuration.\n");
+ SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, &priv->p2p_ps_modeinfo, priv->if_id));
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //xradio_notify_noa(priv, XRADIO_NOA_NOTIFICATION_DELAY);
+#endif
+ }
+
+ if (priv->mode == NL80211_IFTYPE_STATION)
+ SYS_WARN(xradio_upload_qosnull(priv));
+
+ if (hw_priv->is_BT_Present)
+ SYS_WARN(xradio_set_btcoexinfo(priv));
+#if 0
+ /* It's better to override internal TX rete; otherwise
+ * device sends RTS at too high rate. However device
+ * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a
+ * good choice for RTS/CTS, but that means PS poll
+ * will be sent at the same rate - impact on link
+ * budget. Not sure what is better.. */
+
+ /* Update: internal rate selection algorythm is not
+ * bad: if device is not receiving CTS at high rate,
+ * it drops RTS rate.
+ * So, conclusion: if-0 the code. Keep code just for
+ * information:
+ * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */
+
+ /* ~3 is a bug in device: RTS/CTS is not working at
+ * low rates */
+ __le32 internal_tx_rate = __cpu_to_le32(
+ __ffs(priv->association_mode.basicRateSet & ~3));
+ SYS_WARN(wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ &internal_tx_rate,sizeof(internal_tx_rate)));
+#endif
+ } else {
+ memset(&priv->association_mode, 0, sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ }
+ }
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) {
+ u32 prev_erp_info = priv->erp_info;
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ if (info->use_cts_prot)
+ priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+ else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT))
+ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+
+ if (prev_erp_info != priv->erp_info)
+ queue_delayed_work(hw_priv->workqueue, &priv->set_cts_work, 0*HZ);
+ }
+ }
+
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) {
+ __le32 slot_time = info->use_short_slot ? __cpu_to_le32(9) : __cpu_to_le32(20);
+ ap_printk(XRADIO_DBG_MSG, "[STA] Slot time :%d us.\n", __le32_to_cpu(slot_time));
+ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_SLOT_TIME, &slot_time,
+ sizeof(slot_time), priv->if_id));
+ }
+ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rollingAverageCount = 8,
+ };
+
+#if 0
+ /* For verification purposes */
+ info->cqm_rssi_thold = -50;
+ info->cqm_rssi_hyst = 4;
+#endif /* 0 */
+
+ ap_printk(XRADIO_DBG_NIY, "[CQM] RSSI threshold subscribe: %d(+-%d)\n",
+ info->cqm_rssi_thold, info->cqm_rssi_hyst);
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ priv->cqm_rssi_thold = info->cqm_rssi_thold;
+ priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+
+ if (info->cqm_rssi_thold || info->cqm_rssi_hyst) {
+ /* RSSI subscription enabled */
+ /* TODO: It's not a correct way of setting threshold.
+ * Upper and lower must be set equal here and adjusted
+ * in callback. However current implementation is much
+ * more relaible and stable. */
+ if (priv->cqm_use_rssi) {
+ threshold.upperThreshold = info->cqm_rssi_thold + info->cqm_rssi_hyst;
+ threshold.lowerThreshold = info->cqm_rssi_thold;
+ } else {
+ /* convert RSSI to RCPI, RCPI = (RSSI + 110) * 2 */
+ threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110)<<1;
+ threshold.lowerThreshold = (info->cqm_rssi_thold + 110)<<1;
+ }
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE;
+ } else {
+ /* There is a bug in FW, see sta.c. We have to enable
+ * dummy subscription to get correct RSSI values. */
+ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER;
+ }
+ SYS_WARN(wsm_set_rcpi_rssi_threshold(hw_priv, &threshold, priv->if_id));
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold;
+ //priv->cqm_tx_failure_count = 0;
+
+ //if (priv->cqm_beacon_loss_count != info->cqm_beacon_miss_thold) {
+ // priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold;
+ // priv->bss_params.beaconLostCount = (priv->cqm_beacon_loss_count?
+ // priv->cqm_beacon_loss_count : priv->cqm_link_loss_count);
+ /* Make sure we are associated before sending
+ * set_bss_params to firmware */
+ if (priv->bss_params.aid) {
+ SYS_WARN(wsm_set_bss_params(hw_priv, &priv->bss_params, priv->if_id));
+ priv->setbssparams_done = true;
+ }
+ //}
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+ }
+ /*
+ * in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have
+ * BSS_CHANGED_PS and BSS_CHANGED_RETRY_LIMITS enum value.
+ */
+#if 0
+ if (changed & BSS_CHANGED_PS) {
+ if (info->ps_enabled == false)
+ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+ else if (info->dynamic_ps_timeout <= 0)
+ priv->powersave_mode.pmMode = WSM_PSM_PS;
+ else
+ priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
+
+ ap_printk(XRADIO_DBG_MSG, "[STA] Aid: %d, Joined: %s, Powersave: %s\n",
+ priv->bss_params.aid,
+ priv->join_status == XRADIO_JOIN_STATUS_STA ? "yes" : "no",
+ (priv->powersave_mode.pmMode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" :
+ priv->powersave_mode.pmMode == WSM_PSM_PS ? "WSM_PSM_PS" :
+ priv->powersave_mode.pmMode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" :
+ "UNKNOWN"));
+
+ /* Firmware requires that value for this 1-byte field must
+ * be specified in units of 500us. Values above the 128ms
+ * threshold are not supported. */
+ if (info->dynamic_ps_timeout >= 0x80)
+ priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
+ else
+ priv->powersave_mode.fastPsmIdlePeriod = info->dynamic_ps_timeout << 1;
+ ap_printk(XRADIO_DBG_NIY, "[STA]CHANGED_PS fastPsmIdle=%d, apPsmChange=%d\n",
+ priv->powersave_mode.fastPsmIdlePeriod,
+ priv->powersave_mode.apPsmChangePeriod);
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA && priv->bss_params.aid &&
+ priv->setbssparams_done && priv->filter4.enable)
+ xradio_set_pm(priv, &priv->powersave_mode);
+ else
+ priv->power_set_true = 1;
+ }
+
+ if (changed & BSS_CHANGED_RETRY_LIMITS) {
+ ap_printk(XRADIO_DBG_NIY, "Retry limits: %d (long), %d (short).\n",
+ info->retry_long, info->retry_short);
+ spin_lock_bh(&hw_priv->tx_policy_cache.lock);
+ /*TODO:COMBO: for now it's still handled per hw and kept
+ * in xradio_common */
+ hw_priv->long_frame_max_tx_count = info->retry_long;
+ hw_priv->short_frame_max_tx_count =
+ (info->retry_short < 0x0F ? info->retry_short : 0x0F);
+ hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count;
+ spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
+ /* TBD: I think we don't need tx_policy_force_upload().
+ * Outdated policies will leave cache in a normal way. */
+ /* SYS_WARN(tx_policy_force_upload(priv)); */
+ }
+#endif
+ /*in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have BSS_CHANGED_P2P_PS enum value*/
+#if 0
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ if (changed & BSS_CHANGED_P2P_PS) {
+ struct wsm_p2p_ps_modeinfo *modeinfo;
+ modeinfo = &priv->p2p_ps_modeinfo;
+ ap_printk(XRADIO_DBG_NIY, "[AP] BSS_CHANGED_P2P_PS\n");
+ ap_printk(XRADIO_DBG_NIY, "[AP] Legacy PS: %d for AID %d in %d mode.\n",
+ info->p2p_ps.legacy_ps, priv->bss_params.aid, priv->join_status);
+
+ if (info->p2p_ps.legacy_ps >= 0) {
+ if (info->p2p_ps.legacy_ps > 0)
+ priv->powersave_mode.pmMode = WSM_PSM_PS;
+ else
+ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+
+ if(info->p2p_ps.ctwindow && info->p2p_ps.opp_ps)
+ priv->powersave_mode.pmMode = WSM_PSM_PS;
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA)
+ xradio_set_pm(priv, &priv->powersave_mode);
+ }
+
+ ap_printk(XRADIO_DBG_MSG, "[AP] CTWindow: %d\n", info->p2p_ps.ctwindow);
+ if (info->p2p_ps.ctwindow >= 128)
+ modeinfo->oppPsCTWindow = 127;
+ else if (info->p2p_ps.ctwindow >= 0)
+ modeinfo->oppPsCTWindow = info->p2p_ps.ctwindow;
+
+ ap_printk(XRADIO_DBG_MSG, "[AP] Opportunistic: %d\n", info->p2p_ps.opp_ps);
+ switch (info->p2p_ps.opp_ps) {
+ case 0:
+ modeinfo->oppPsCTWindow &= ~(BIT(7));
+ break;
+ case 1:
+ modeinfo->oppPsCTWindow |= BIT(7);
+ break;
+ default:
+ break;
+ }
+
+ ap_printk(XRADIO_DBG_MSG, "[AP] NOA: %d, %d, %d, %d\n",
+ info->p2p_ps.count, info->p2p_ps.start,
+ info->p2p_ps.duration, info->p2p_ps.interval);
+ /* Notice of Absence */
+ modeinfo->count = info->p2p_ps.count;
+
+ if (info->p2p_ps.count) {
+ /* In case P2P_GO we need some extra time to be sure
+ * we will update beacon/probe_resp IEs correctly */
+#define NOA_DELAY_START_MS 300
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ modeinfo->startTime = __cpu_to_le32(info->p2p_ps.start + NOA_DELAY_START_MS);
+ else
+ modeinfo->startTime = __cpu_to_le32(info->p2p_ps.start);
+ modeinfo->duration = __cpu_to_le32(info->p2p_ps.duration);
+ modeinfo->interval = __cpu_to_le32(info->p2p_ps.interval);
+ modeinfo->dtimCount = 1;
+ modeinfo->reserved = 0;
+ } else {
+ modeinfo->dtimCount = 0;
+ modeinfo->startTime = 0;
+ modeinfo->reserved = 0;
+ modeinfo->duration = 0;
+ modeinfo->interval = 0;
+ }
+
+#if defined(CONFIG_XRADIO_DEBUG)
+ print_hex_dump_bytes("p2p_set_ps_modeinfo: ", DUMP_PREFIX_NONE,
+ (u8 *)modeinfo, sizeof(*modeinfo));
+#endif /* CONFIG_XRADIO_DEBUG */
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA ||
+ priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, modeinfo, priv->if_id));
+ }
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ /* Temporary solution while firmware don't support NOA change
+ * notification yet */
+ xradio_notify_noa(priv, 10);
+#endif
+ }
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+#endif
+ mutex_unlock(&hw_priv->conf_mutex);
+}
+
+void xradio_multicast_start_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, multicast_start_work);
+ long tmo = priv->join_dtim_period * (priv->beacon_int + 20) * HZ / 1024;
+
+ cancel_work_sync(&priv->multicast_stop_work);
+ if (!priv->aid0_bit_set) {
+ wsm_lock_tx(priv->hw_priv);
+ xradio_set_tim_impl(priv, true);
+ priv->aid0_bit_set = true;
+ mod_timer(&priv->mcast_timeout, jiffies + tmo);
+ wsm_unlock_tx(priv->hw_priv);
+ }
+}
+
+void xradio_multicast_stop_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, multicast_stop_work);
+
+ if (priv->aid0_bit_set) {
+ del_timer_sync(&priv->mcast_timeout);
+ wsm_lock_tx(priv->hw_priv);
+ priv->aid0_bit_set = false;
+ xradio_set_tim_impl(priv, false);
+ wsm_unlock_tx(priv->hw_priv);
+ }
+}
+
+void xradio_mcast_timeout(unsigned long arg)
+{
+ struct xradio_vif *priv = (struct xradio_vif *)arg;
+
+ ap_printk(XRADIO_DBG_WARN, "Multicast delivery timeout.\n");
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts;
+ if (priv->tx_multicast)
+ xradio_bh_wakeup(xrwl_vifpriv_to_hwpriv(priv));
+ spin_unlock_bh(&priv->ps_state_lock);
+}
+
+int xradio_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params)
+{
+ /* Aggregation is implemented fully in firmware,
+ * including block ack negotiation.
+ * In case of AMPDU aggregation in RX direction
+ * re-ordering of packets takes place on host. mac80211
+ * needs the ADDBA Request to setup reodering.mac80211 also
+ * sends ADDBA Response which is discarded in the driver as
+ * FW generates the ADDBA Response on its own.*/
+ int ret;
+
+ switch (params->action) {
+ case IEEE80211_AMPDU_RX_START:
+ case IEEE80211_AMPDU_RX_STOP:
+ /* Just return OK to mac80211 */
+ ret = 0;
+ break;
+ default:
+ ret = -ENOTSUPP;
+ }
+ return ret;
+}
+
+/* ******************************************************************** */
+/* WSM callback */
+void xradio_suspend_resume(struct xradio_vif *priv, struct wsm_suspend_resume *arg)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+#if 0
+ ap_printk(XRADIO_DBG_MSG, "[AP] %s: %s\n",
+ arg->stop ? "stop" : "start",
+ arg->multicast ? "broadcast" : "unicast");
+#endif
+ if (arg->multicast) {
+ bool cancel_tmo = false;
+ spin_lock_bh(&priv->ps_state_lock);
+ if (arg->stop) {
+ priv->tx_multicast = false;
+ } else {
+ /* Firmware sends this indication every DTIM if there
+ * is a STA in powersave connected. There is no reason
+ * to suspend, following wakeup will consume much more
+ * power than it could be saved. */
+ xradio_pm_stay_awake(&hw_priv->pm_state, (priv->join_dtim_period *
+ (priv->beacon_int + 20) * HZ / 1024));
+ priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts;
+ if (priv->tx_multicast) {
+ cancel_tmo = true;
+ xradio_bh_wakeup(hw_priv);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (cancel_tmo)
+ del_timer_sync(&priv->mcast_timeout);
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ xradio_ps_notify(priv, arg->link_id, arg->stop);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (!arg->stop)
+ xradio_bh_wakeup(hw_priv);
+ }
+ return;
+}
+
+/* ******************************************************************** */
+/* AP privates */
+
+static int xradio_upload_beacon(struct xradio_vif *priv)
+{
+ int ret = 0;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_BEACON,
+ };
+ struct ieee80211_mgmt *mgmt;
+ u8 *erp_inf, *ies, *ht_oper;
+ u32 ies_len;
+
+ if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ)
+ frame.rate = WSM_TRANSMIT_RATE_6;
+
+ frame.skb = ieee80211_beacon_get(priv->hw, priv->vif);
+ if (SYS_WARN(!frame.skb))
+ return -ENOMEM;
+
+ mgmt = (void *)frame.skb->data;
+ ies = mgmt->u.beacon.variable;
+ ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt);
+
+ ht_oper = (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len);
+ if (ht_oper) {
+ /* Enable RIFS*/
+ ht_oper[3] |= 8;
+ }
+
+ erp_inf = (u8 *)cfg80211_find_ie(WLAN_EID_ERP_INFO, ies, ies_len);
+ if (erp_inf) {
+ if (erp_inf[ERP_INFO_BYTE_OFFSET]
+ & WLAN_ERP_BARKER_PREAMBLE)
+ priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE;
+ else
+ priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE;
+
+ if (erp_inf[ERP_INFO_BYTE_OFFSET]
+ & WLAN_ERP_NON_ERP_PRESENT) {
+ priv->erp_info |= WLAN_ERP_USE_PROTECTION;
+ priv->erp_info |= WLAN_ERP_NON_ERP_PRESENT;
+ } else {
+ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION;
+ priv->erp_info &= ~WLAN_ERP_NON_ERP_PRESENT;
+ }
+ }
+
+#ifdef HIDDEN_SSID
+ if (priv->hidden_ssid) {
+ u8 *ssid_ie;
+ u8 ssid_len;
+
+ ap_printk(XRADIO_DBG_NIY, "%s: hidden_ssid set\n", __func__);
+ ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+ SYS_WARN(!ssid_ie);
+ ssid_len = ssid_ie[1];
+ if (ssid_len) {
+ ap_printk(XRADIO_DBG_MSG, "hidden_ssid with zero content ssid\n");
+ ssid_ie[1] = 0;
+ memmove(ssid_ie + 2, ssid_ie + 2 + ssid_len,
+ (ies + ies_len -
+ (ssid_ie + 2 + ssid_len)));
+ frame.skb->len -= ssid_len;
+ } else {
+ ap_printk(XRADIO_DBG_WARN, "hidden ssid with ssid len 0 not supported");
+ dev_kfree_skb(frame.skb);
+ return -1;
+ }
+ }
+#endif
+
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+ if (!ret) {
+#ifdef PROBE_RESP_EXTRA_IE
+ ret = xradio_upload_proberesp(priv);
+#else
+ /* TODO: Distille probe resp; remove TIM
+ * and other beacon-specific IEs */
+ *(__le16 *)frame.skb->data = __cpu_to_le16(
+ IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP);
+ frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE;
+ /* TODO: Ideally probe response template should separately
+ configured by supplicant through openmac. This is a
+ temporary work-around known to fail p2p group info
+ attribute related tests
+ */
+ if (0 /* priv->vif->p2p */)
+ ret = wsm_set_probe_responder(priv, true);
+ else {
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+ SYS_WARN(wsm_set_probe_responder(priv, false));
+ }
+#endif
+ }
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+
+#ifdef PROBE_RESP_EXTRA_IE
+static int xradio_upload_proberesp(struct xradio_vif *priv)
+{
+ int ret = 0;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE,
+ };
+#ifdef HIDDEN_SSID
+ u8 *ssid_ie;
+#endif
+
+ if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ)
+ frame.rate = WSM_TRANSMIT_RATE_6;
+
+ frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif);
+ if (SYS_WARN(!frame.skb))
+ return -ENOMEM;
+
+#ifdef HIDDEN_SSID
+ if (priv->hidden_ssid) {
+ int offset;
+ u8 ssid_len;
+ /* we are assuming beacon from upper layer will always contain
+ zero filled ssid for hidden ap. The beacon shall never have
+ ssid len = 0.
+ */
+
+ offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+ ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, frame.skb->data + offset,
+ frame.skb->len - offset);
+ ssid_len = ssid_ie[1];
+ if (ssid_len && (ssid_len == priv->ssid_length)) {
+ memcpy(ssid_ie + 2, priv->ssid, ssid_len);
+ } else {
+ ap_printk(XRADIO_DBG_ERROR, "%s: hidden ssid with mismatched ssid_len %d\n",
+ __func__, ssid_len);
+ dev_kfree_skb(frame.skb);
+ return -1;
+ }
+ }
+#endif
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+ SYS_WARN(wsm_set_probe_responder(priv, false));
+
+ dev_kfree_skb(frame.skb);
+
+ return ret;
+}
+#endif
+
+static int xradio_upload_pspoll(struct xradio_vif *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PS_POLL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
+ if (SYS_WARN(!frame.skb))
+ return -ENOMEM;
+ ret = wsm_set_template_frame(xrwl_vifpriv_to_hwpriv(priv), &frame, priv->if_id);
+ dev_kfree_skb(frame.skb);
+ return ret;
+}
+
+static int xradio_upload_null(struct xradio_vif *priv)
+{
+ int ret = 0;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_NULL,
+ .rate = 0xFF,
+ };
+
+ frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
+ if (SYS_WARN(!frame.skb))
+ return -ENOMEM;
+
+ ret = wsm_set_template_frame(xrwl_vifpriv_to_hwpriv(priv), &frame, priv->if_id);
+ dev_kfree_skb(frame.skb);
+ return ret;
+}
+
+static int xradio_upload_qosnull(struct xradio_vif *priv)
+{
+ struct ieee80211_qos_hdr* qos_null_template;
+ struct sk_buff* skb;
+ int ret = 0;
+ struct xradio_common *hw_priv =xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_QOS_NULL,
+ .rate = 0xFF,
+ };
+ if (!hw_priv)
+ ap_printk(XRADIO_DBG_ERROR,"%s: Cannot find xradio_common pointer!\n",__FUNCTION__);
+ /*set qos template*/
+ skb = dev_alloc_skb(hw_priv->hw->extra_tx_headroom + sizeof(struct ieee80211_qos_hdr));
+ if (!skb) {
+ ap_printk(XRADIO_DBG_ERROR,"%s: failed to allocate buffer for qos nullfunc template!\n",__FUNCTION__);
+ return -1;
+ }
+ skb_reserve(skb, hw_priv->hw->extra_tx_headroom);
+ qos_null_template = (struct ieee80211_qos_hdr *)skb_put(skb,sizeof(struct ieee80211_qos_hdr));
+ memset(qos_null_template, 0, sizeof(struct ieee80211_qos_hdr));
+ memcpy(qos_null_template->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
+ memcpy(qos_null_template->addr2, priv->vif->addr, ETH_ALEN);
+ memcpy(qos_null_template->addr3, priv->vif->bss_conf.bssid, ETH_ALEN);
+ qos_null_template->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_STYPE_QOS_NULLFUNC |
+ IEEE80211_FCTL_TODS);
+ frame.skb = skb;
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+ dev_kfree_skb(frame.skb);
+ return ret;
+}
+
+/* This API is nolonegr present in WSC */
+#if 0
+static int xradio_enable_beaconing(struct xradio_vif *priv,
+ bool enable)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_beacon_transmit transmit = {
+ .enableBeaconing = enable,
+ };
+
+ return wsm_beacon_transmit(hw_priv, &transmit, priv->if_id);
+}
+#endif
+
+static int xradio_start_ap(struct xradio_vif *priv)
+{
+ int ret;
+#ifndef HIDDEN_SSID
+ const u8 *ssidie;
+ struct sk_buff *skb;
+ int offset;
+#endif
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_start start = {
+ .mode = priv->vif->p2p ? WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
+ /* TODO:COMBO:Change once mac80211 support is available */
+ .band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+ .channelNumber = hw_priv->channel->hw_value,
+ .beaconInterval = conf->beacon_int,
+ .DTIMPeriod = conf->dtim_period,
+ .preambleType = conf->use_short_preamble ?
+ WSM_JOIN_PREAMBLE_SHORT :WSM_JOIN_PREAMBLE_LONG,
+ .probeDelay = 100,
+ .basicRateSet = xradio_rate_mask_to_wsm(hw_priv, conf->basic_rates),
+#ifdef P2P_MULTIVIF
+ .CTWindow = priv->vif->p2p ? 0xFFFFFFFF : 0,
+#endif
+ };
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+
+#ifdef TES_P2P_000B_EXTEND_INACTIVITY_CNT
+ ///w, TES_P2P_000B WorkAround:
+ ///w, when inactivity count of a peer device is zero,
+ ///w, which will reset while receiving a peer device frame,
+ ///w, firmware will disconnect with it.
+ ///w, due to some reason, such as scan/phy error, we miss these frame.
+ ///w, then we can't keep connection with peer device.
+ ///w, we set the min_inactivity value to large as WorkAround.
+ //min_inactivity be modified to 20, yangfh.
+ struct wsm_inactivity inactivity = {
+ .min_inactivity = 20,
+ .max_inactivity = 10,
+ };
+#else
+ struct wsm_inactivity inactivity = {
+ .min_inactivity = 9,
+ .max_inactivity = 1,
+ };
+#endif
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if (priv->if_id)
+ start.mode |= WSM_FLAG_MAC_INSTANCE_1;
+ else
+ start.mode &= ~WSM_FLAG_MAC_INSTANCE_1;
+
+ hw_priv->connected_sta_cnt = 0;
+
+#ifndef HIDDEN_SSID
+ /* Get SSID */
+ skb = ieee80211_beacon_get(priv->hw, priv->vif);
+ if (SYS_WARN(!skb)) {
+ ap_printk(XRADIO_DBG_ERROR,"%s, ieee80211_beacon_get failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+ ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, skb->len - offset);
+
+ memset(priv->ssid, 0, sizeof(priv->ssid));
+ if (ssidie) {
+ priv->ssid_length = ssidie[1];
+ if (SYS_WARN(priv->ssid_length > sizeof(priv->ssid)))
+ priv->ssid_length = sizeof(priv->ssid);
+ memcpy(priv->ssid, &ssidie[2], priv->ssid_length);
+ } else {
+ priv->ssid_length = 0;
+ }
+ dev_kfree_skb(skb);
+#endif
+
+ priv->beacon_int = conf->beacon_int;
+ priv->join_dtim_period = conf->dtim_period;
+ memset(&priv->last_tim[0], 0, sizeof(priv->last_tim)); //yangfh
+
+ start.ssidLength = priv->ssid_length;
+ memcpy(&start.ssid[0], priv->ssid, start.ssidLength);
+
+ memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
+
+ ap_printk(XRADIO_DBG_NIY, "[AP] ch: %d(%d), bcn: %d(%d),"
+ "bss_rate: 0x%.8X, ssid: %.*s.\n",
+ start.channelNumber, start.band,
+ start.beaconInterval, start.DTIMPeriod,
+ start.basicRateSet, start.ssidLength, start.ssid);
+ ret = SYS_WARN(wsm_start(hw_priv, &start, priv->if_id));
+
+ if (!ret && priv->vif->p2p) {
+ ap_printk(XRADIO_DBG_NIY,"[AP] Setting p2p powersave configuration.\n");
+ SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv,
+ &priv->p2p_ps_modeinfo, priv->if_id));
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //xradio_notify_noa(priv, XRADIO_NOA_NOTIFICATION_DELAY);
+#endif
+ }
+
+ /*Set Inactivity time*/
+ if(!(strstr(&start.ssid[0], "6.1.12"))) {
+ wsm_set_inactivity(hw_priv, &inactivity, priv->if_id);
+ }
+ if (!ret) {
+#ifndef AP_AGGREGATE_FW_FIX
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
+ XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, priv->if_id));
+#else
+ if ((priv->if_id ==1) && !hw_priv->is_go_thru_go_neg)
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, //modified for WFD by yangfh
+ XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, priv->if_id));
+ else
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
+ XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, priv->if_id));
+#endif
+ priv->join_status = XRADIO_JOIN_STATUS_AP;
+ /* xradio_update_filtering(priv); */
+ }
+ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE;
+ ap_printk(XRADIO_DBG_WARN, "vif%d, AP/GO mode THROTTLE=%d\n", priv->if_id,
+ priv->if_id==0?hw_priv->vif0_throttle:hw_priv->vif1_throttle);
+ return ret;
+}
+
+static int xradio_update_beaconing(struct xradio_vif *priv)
+{
+ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_reset reset = {
+ .link_id = 0,
+ .reset_statistics = true,
+ };
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if (priv->mode == NL80211_IFTYPE_AP) {
+ /* TODO: check if changed channel, band */
+ if (priv->join_status != XRADIO_JOIN_STATUS_AP ||
+ priv->beacon_int != conf->beacon_int) {
+ ap_printk(XRADIO_DBG_WARN, "ap restarting!\n");
+ wsm_lock_tx(hw_priv);
+ if (priv->join_status != XRADIO_JOIN_STATUS_PASSIVE)
+ SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id));
+ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE;
+ SYS_WARN(xradio_start_ap(priv));
+ wsm_unlock_tx(hw_priv);
+ } else
+ ap_printk(XRADIO_DBG_NIY, "ap started join_status: %d\n", priv->join_status);
+ }
+ return 0;
+}
+
+int xradio_find_link_id(struct xradio_vif *priv, const u8 *mac)
+{
+ int i, ret = 0;
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) {
+ if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) &&
+ priv->link_id_db[i].status) {
+ priv->link_id_db[i].timestamp = jiffies;
+ ret = i + 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ return ret;
+}
+
+int xradio_alloc_link_id(struct xradio_vif *priv, const u8 *mac)
+{
+ int i, ret = 0;
+ unsigned long max_inactivity = 0;
+ unsigned long now = jiffies;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) {
+ if (!priv->link_id_db[i].status) {
+ ret = i + 1;
+ break;
+ } else if (priv->link_id_db[i].status != XRADIO_LINK_HARD &&
+ !hw_priv->tx_queue_stats.link_map_cache[i + 1]) {
+ unsigned long inactivity = now - priv->link_id_db[i].timestamp;
+ if (inactivity < max_inactivity)
+ continue;
+ max_inactivity = inactivity;
+ ret = i + 1;
+ }
+ }
+ if (ret) {
+ struct xradio_link_entry *entry = &priv->link_id_db[ret - 1];
+ ap_printk(XRADIO_DBG_NIY, "STA added, link_id: %d\n", ret);
+ entry->status = XRADIO_LINK_RESERVE;
+ memcpy(&entry->mac, mac, ETH_ALEN);
+ memset(&entry->buffered, 0, XRADIO_MAX_TID);
+ skb_queue_head_init(&entry->rx_queue);
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ } else {
+ ap_printk(XRADIO_DBG_WARN, "Early: no more link IDs available.\n");
+ }
+
+ spin_unlock_bh(&priv->ps_state_lock);
+ return ret;
+}
+
+void xradio_link_id_work(struct work_struct *work)
+{
+ struct xradio_vif *priv = container_of(work, struct xradio_vif, link_id_work);
+ struct xradio_common *hw_priv = priv->hw_priv;
+
+ wsm_flush_tx(hw_priv);
+ xradio_link_id_gc_work(&priv->link_id_gc_work.work);
+ wsm_unlock_tx(hw_priv);
+}
+
+void xradio_link_id_gc_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, link_id_gc_work.work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_map_link map_link = {
+ .link_id = 0,
+ };
+ unsigned long now = jiffies;
+ unsigned long next_gc = -1;
+ long ttl;
+ bool need_reset;
+ u32 mask;
+ int i;
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_AP)
+ return;
+
+ wsm_lock_tx(hw_priv);
+ spin_lock_bh(&priv->ps_state_lock);
+ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) {
+ need_reset = false;
+ mask = BIT(i + 1);
+ if (priv->link_id_db[i].status == XRADIO_LINK_RESERVE ||
+ (priv->link_id_db[i].status == XRADIO_LINK_HARD &&
+ !(priv->link_id_map & mask))) {
+ if (priv->link_id_map & mask) {
+ priv->sta_asleep_mask &= ~mask;
+ priv->pspoll_mask &= ~mask;
+ need_reset = true;
+ }
+ priv->link_id_map |= mask;
+ if (priv->link_id_db[i].status != XRADIO_LINK_HARD)
+ priv->link_id_db[i].status = XRADIO_LINK_SOFT;
+ memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN);
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (need_reset) {
+ SYS_WARN(xrwl_unmap_link(priv, i + 1));
+ }
+ map_link.link_id = i + 1;
+ SYS_WARN(wsm_map_link(hw_priv, &map_link, priv->if_id));
+ next_gc = min(next_gc, XRADIO_LINK_ID_GC_TIMEOUT);
+ spin_lock_bh(&priv->ps_state_lock);
+ } else if (priv->link_id_db[i].status == XRADIO_LINK_SOFT) {
+ ttl = priv->link_id_db[i].timestamp - now + XRADIO_LINK_ID_GC_TIMEOUT;
+ if (ttl <= 0) {
+ need_reset = true;
+ priv->link_id_db[i].status = XRADIO_LINK_OFF;
+ priv->link_id_map &= ~mask;
+ priv->sta_asleep_mask &= ~mask;
+ priv->pspoll_mask &= ~mask;
+ memset(map_link.mac_addr, 0, ETH_ALEN);
+ spin_unlock_bh(&priv->ps_state_lock);
+ SYS_WARN(xrwl_unmap_link(priv, i + 1));
+ spin_lock_bh(&priv->ps_state_lock);
+ } else {
+ next_gc = min_t(unsigned long, next_gc, ttl);
+ }
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ } else if (priv->link_id_db[i].status == XRADIO_LINK_RESET ||
+ priv->link_id_db[i].status == XRADIO_LINK_RESET_REMAP) {
+ int status = priv->link_id_db[i].status;
+ priv->link_id_db[i].status = XRADIO_LINK_OFF;
+ priv->link_id_db[i].timestamp = now;
+ spin_unlock_bh(&priv->ps_state_lock);
+ SYS_WARN(xrwl_unmap_link(priv, i + 1));
+ if (status == XRADIO_LINK_RESET_REMAP) {
+ memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN);
+ map_link.link_id = i + 1;
+ SYS_WARN(wsm_map_link(hw_priv, &map_link, priv->if_id));
+ next_gc = min(next_gc, XRADIO_LINK_ID_GC_TIMEOUT);
+ priv->link_id_db[i].status = priv->link_id_db[i].prev_status;
+ }
+ spin_lock_bh(&priv->ps_state_lock);
+#endif
+ }
+ if (need_reset) {
+ skb_queue_purge(&priv->link_id_db[i].rx_queue);
+ ap_printk(XRADIO_DBG_NIY, "STA removed, link_id: %d\n", i + 1);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ if (next_gc != -1)
+ queue_delayed_work(hw_priv->workqueue, &priv->link_id_gc_work, next_gc);
+ wsm_unlock_tx(hw_priv);
+}
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+#if 0
+void xradio_notify_noa(struct xradio_vif *priv, int delay)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct cfg80211_p2p_ps p2p_ps = {0};
+ struct wsm_p2p_ps_modeinfo *modeinfo;
+ modeinfo = &priv->p2p_ps_modeinfo;
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_AP)
+ return;
+
+ if (delay)
+ msleep(delay);
+
+ if (!SYS_WARN(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo))) {
+#if defined(CONFIG_XRADIO_DEBUG)
+ print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ", DUMP_PREFIX_NONE,
+ (u8 *)modeinfo, sizeof(*modeinfo));
+#endif /* CONFIG_XRADIO_DEBUG */
+ p2p_ps.opp_ps = !!(modeinfo->oppPsCTWindow & BIT(7));
+ p2p_ps.ctwindow = modeinfo->oppPsCTWindow & (~BIT(7));
+ p2p_ps.count = modeinfo->count;
+ p2p_ps.start = __le32_to_cpu(modeinfo->startTime);
+ p2p_ps.duration = __le32_to_cpu(modeinfo->duration);
+ p2p_ps.interval = __le32_to_cpu(modeinfo->interval);
+ p2p_ps.index = modeinfo->reserved;
+
+ ieee80211_p2p_noa_notify(priv->vif, &p2p_ps, GFP_KERNEL);
+ }
+}
+#endif
+#endif
+int xrwl_unmap_link(struct xradio_vif *priv, int link_id)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ int ret = 0;
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+
+ if (is_hardware_xradio(hw_priv)) {
+ struct wsm_map_link maplink = {
+ .link_id = link_id,
+ .unmap = true,
+ };
+ if (link_id)
+ memcpy(&maplink.mac_addr[0], priv->link_id_db[link_id - 1].mac, ETH_ALEN);
+ return wsm_map_link(hw_priv, &maplink, priv->if_id);
+ } else {
+ struct wsm_reset reset = {
+ .link_id = link_id,
+ .reset_statistics = true,
+ };
+ ret = wsm_reset(hw_priv, &reset, priv->if_id);
+ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
+ return ret;
+ }
+}
+#ifdef AP_HT_CAP_UPDATE
+void xradio_ht_oper_update_work(struct work_struct *work)
+{
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u8 *ht_oper, *ies;
+ u32 ies_len;
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, ht_oper_update_work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_update_ie update_ie = {
+ .what = WSM_UPDATE_IE_BEACON,
+ .count = 1,
+ };
+
+ skb = ieee80211_beacon_get(priv->hw, priv->vif);
+ if (SYS_WARN(!skb))
+ return;
+
+ mgmt = (void *)skb->data;
+ ies = mgmt->u.beacon.variable;
+ ies_len = skb->len - (u32)(ies - (u8 *)mgmt);
+ ht_oper= (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len);
+ if(ht_oper && priv->ht_oper == HT_INFO_MASK) {
+ ht_oper[HT_INFO_OFFSET] |= 0x11;
+ update_ie.ies = ht_oper;
+ update_ie.length = HT_INFO_IE_LEN;
+ SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
+ }
+ dev_kfree_skb(skb);
+}
+#endif
diff --git a/drivers/net/wireless/xradio/ap.h b/drivers/net/wireless/xradio/ap.h
new file mode 100644
index 00000000..ca05bffe
--- /dev/null
+++ b/drivers/net/wireless/xradio/ap.h
@@ -0,0 +1,63 @@
+/*
+ * STA and AP APIs for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef AP_H_INCLUDED
+#define AP_H_INCLUDED
+
+#define XRADIO_NOA_NOTIFICATION_DELAY 10
+
+#ifdef AP_HT_CAP_UPDATE
+#define HT_INFO_OFFSET 4
+#define HT_INFO_MASK 0x0011
+#define HT_INFO_IE_LEN 22
+#endif
+
+int xradio_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
+ bool set);
+int xradio_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+int xradio_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_sta *sta);
+void xradio_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ enum sta_notify_cmd notify_cmd,
+ struct ieee80211_sta *sta);
+void xradio_bss_info_changed(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *info,
+ u32 changed);
+int xradio_ampdu_action(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_ampdu_params *params);
+/* enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+ u8 buf_size);*/
+
+void xradio_suspend_resume(struct xradio_vif *priv,
+ struct wsm_suspend_resume *arg);
+void xradio_set_tim_work(struct work_struct *work);
+void xradio_set_cts_work(struct work_struct *work);
+void xradio_multicast_start_work(struct work_struct *work);
+void xradio_multicast_stop_work(struct work_struct *work);
+void xradio_mcast_timeout(unsigned long arg);
+int xradio_find_link_id(struct xradio_vif *priv, const u8 *mac);
+int xradio_alloc_link_id(struct xradio_vif *priv, const u8 *mac);
+void xradio_link_id_work(struct work_struct *work);
+void xradio_link_id_gc_work(struct work_struct *work);
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+/*in linux3.4 mac,it does't have the noa pass*/
+//void xradio_notify_noa(struct xradio_vif *priv, int delay);
+#endif
+int xrwl_unmap_link(struct xradio_vif *priv, int link_id);
+#ifdef AP_HT_CAP_UPDATE
+void xradio_ht_oper_update_work(struct work_struct *work);
+#endif
+
+#endif
diff --git a/drivers/net/wireless/xradio/bh.c b/drivers/net/wireless/xradio/bh.c
new file mode 100644
index 00000000..220f85a2
--- /dev/null
+++ b/drivers/net/wireless/xradio/bh.c
@@ -0,0 +1,880 @@
+/*
+ * Data Transmission thread implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/kthread.h>
+
+#include "xradio.h"
+#include "bh.h"
+#include "hwio.h"
+#include "wsm.h"
+#include "sdio.h"
+
+/* TODO: Verify these numbers with WSM specification. */
+#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4)
+/* an SPI message cannot be bigger than (2"12-1)*2 bytes
+ * "*2" to cvt to bytes */
+#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2)
+#define PIGGYBACK_CTRL_REG (2)
+#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG)
+
+/* Suspend state privates */
+enum xradio_bh_pm_state {
+ XRADIO_BH_RESUMED = 0,
+ XRADIO_BH_SUSPEND,
+ XRADIO_BH_SUSPENDED,
+ XRADIO_BH_RESUME,
+};
+typedef int (*xradio_wsm_handler)(struct xradio_common *hw_priv, u8 *data, size_t size);
+
+#ifdef MCAST_FWDING
+int wsm_release_buffer_to_fw(struct xradio_vif *priv, int count);
+#endif
+static int xradio_bh(void *arg);
+
+int xradio_register_bh(struct xradio_common *hw_priv)
+{
+ int err = 0;
+ struct sched_param param = { .sched_priority = 1 };
+
+ SYS_BUG(hw_priv->bh_thread);
+ atomic_set(&hw_priv->bh_tx, 0);
+ atomic_set(&hw_priv->bh_term, 0);
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED);
+ hw_priv->buf_id_tx = 0;
+ hw_priv->buf_id_rx = 0;
+ init_waitqueue_head(&hw_priv->bh_wq);
+ init_waitqueue_head(&hw_priv->bh_evt_wq);
+
+ hw_priv->bh_thread = kthread_create(&xradio_bh, hw_priv, XRADIO_BH_THREAD);
+ if (IS_ERR(hw_priv->bh_thread)) {
+ err = PTR_ERR(hw_priv->bh_thread);
+ hw_priv->bh_thread = NULL;
+ } else {
+ SYS_WARN(sched_setscheduler(hw_priv->bh_thread, SCHED_FIFO, &param));
+#ifdef HAS_PUT_TASK_STRUCT
+ get_task_struct(hw_priv->bh_thread);
+#endif
+ wake_up_process(hw_priv->bh_thread);
+ }
+ return err;
+}
+
+void xradio_unregister_bh(struct xradio_common *hw_priv)
+{
+ struct task_struct *thread = hw_priv->bh_thread;
+
+ if (SYS_WARN(!thread))
+ return;
+
+ hw_priv->bh_thread = NULL;
+ kthread_stop(thread);
+#ifdef HAS_PUT_TASK_STRUCT
+ put_task_struct(thread);
+#endif
+ dev_dbg(hw_priv->pdev, "Unregister success.\n");
+}
+
+void xradio_irq_handler(struct xradio_common *hw_priv)
+{
+ xradio_bh_wakeup(hw_priv);
+}
+
+void xradio_bh_wakeup(struct xradio_common *hw_priv)
+{
+ atomic_set(&hw_priv->bh_tx, 1);
+ wake_up(&hw_priv->bh_wq);
+}
+
+int xradio_bh_suspend(struct xradio_common *hw_priv)
+{
+#ifdef MCAST_FWDING
+ int i =0;
+ struct xradio_vif *priv = NULL;
+#endif
+
+ if (hw_priv->bh_error) {
+ return -EINVAL;
+ }
+
+#ifdef MCAST_FWDING
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (!priv)
+ continue;
+ if ( (priv->multicast_filter.enable)
+ && (priv->join_status == XRADIO_JOIN_STATUS_AP) ) {
+ wsm_release_buffer_to_fw(priv,
+ (hw_priv->wsm_caps.numInpChBufs - 1));
+ break;
+ }
+ }
+#endif
+
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_SUSPEND);
+ wake_up(&hw_priv->bh_wq);
+ return wait_event_timeout(hw_priv->bh_evt_wq, (hw_priv->bh_error ||
+ XRADIO_BH_SUSPENDED == atomic_read(&hw_priv->bh_suspend)),
+ 1 * HZ)? 0 : -ETIMEDOUT;
+}
+
+int xradio_bh_resume(struct xradio_common *hw_priv)
+{
+#ifdef MCAST_FWDING
+ int ret;
+ int i =0;
+ struct xradio_vif *priv =NULL;
+#endif
+
+
+ if (hw_priv->bh_error) {
+ return -EINVAL;
+ }
+
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUME);
+ wake_up(&hw_priv->bh_wq);
+
+#ifdef MCAST_FWDING
+ ret = wait_event_timeout(hw_priv->bh_evt_wq, (hw_priv->bh_error ||
+ XRADIO_BH_RESUMED == atomic_read(&hw_priv->bh_suspend))
+ ,1 * HZ)? 0 : -ETIMEDOUT;
+
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (!priv)
+ continue;
+ if ((priv->join_status == XRADIO_JOIN_STATUS_AP) &&
+ (priv->multicast_filter.enable)) {
+ u8 count = 0;
+ SYS_WARN(wsm_request_buffer_request(priv, &count));
+ dev_dbg(hw_priv->pdev, "Reclaim Buff %d \n",count);
+ break;
+ }
+ }
+
+ return ret;
+#else
+ return wait_event_timeout(hw_priv->bh_evt_wq,hw_priv->bh_error ||
+ (XRADIO_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)),
+ 1 * HZ) ? 0 : -ETIMEDOUT;
+#endif
+
+}
+
+static inline void wsm_alloc_tx_buffer(struct xradio_common *hw_priv)
+{
+ ++hw_priv->hw_bufs_used;
+}
+
+int wsm_release_tx_buffer(struct xradio_common *hw_priv, int count)
+{
+ int ret = 0;
+ int hw_bufs_used = hw_priv->hw_bufs_used;
+
+ hw_priv->hw_bufs_used -= count;
+ if (SYS_WARN(hw_priv->hw_bufs_used < 0)) {
+ /* Tx data patch stops when all but one hw buffers are used.
+ So, re-start tx path in case we find hw_bufs_used equals
+ numInputChBufs - 1.
+ */
+ dev_err(hw_priv->pdev, "hw_bufs_used=%d, count=%d.\n",
+ hw_priv->hw_bufs_used, count);
+ ret = -1;
+ } else if (hw_bufs_used >= (hw_priv->wsm_caps.numInpChBufs - 1))
+ ret = 1;
+ if (!hw_priv->hw_bufs_used)
+ wake_up(&hw_priv->bh_evt_wq);
+ return ret;
+}
+
+int wsm_release_vif_tx_buffer(struct xradio_common *hw_priv, int if_id, int count)
+{
+ int ret = 0;
+
+ hw_priv->hw_bufs_used_vif[if_id] -= count;
+ if (!hw_priv->hw_bufs_used_vif[if_id])
+ wake_up(&hw_priv->bh_evt_wq);
+
+ if (SYS_WARN(hw_priv->hw_bufs_used_vif[if_id] < 0))
+ ret = -1;
+ return ret;
+}
+#ifdef MCAST_FWDING
+int wsm_release_buffer_to_fw(struct xradio_vif *priv, int count)
+{
+ int i;
+ u8 flags;
+ struct wsm_buf *buf;
+ size_t buf_len;
+ struct wsm_hdr *wsm;
+ struct xradio_common *hw_priv = priv->hw_priv;
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_AP) {
+ return 0;
+ }
+ dev_dbg(hw_priv->pdev, "Rel buffer to FW %d, %d\n", count, hw_priv->hw_bufs_used);
+
+ for (i = 0; i < count; i++) {
+ if ((hw_priv->hw_bufs_used + 1) < hw_priv->wsm_caps.numInpChBufs) {
+ flags = i ? 0: 0x1;
+
+ wsm_alloc_tx_buffer(hw_priv);
+ buf = &hw_priv->wsm_release_buf[i];
+ buf_len = buf->data - buf->begin;
+
+ /* Add sequence number */
+ wsm = (struct wsm_hdr *)buf->begin;
+ SYS_BUG(buf_len < sizeof(*wsm));
+
+ wsm->id &= __cpu_to_le32(~WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+ wsm->id |= cpu_to_le32(WSM_TX_SEQ(hw_priv->wsm_tx_seq));
+
+ dev_dbg(hw_priv->pdev, "REL %d\n", hw_priv->wsm_tx_seq);
+ if (SYS_WARN(xradio_data_write(hw_priv, buf->begin, buf_len))) {
+ break;
+ }
+ hw_priv->buf_released = 1;
+ hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
+ } else
+ break;
+ }
+
+ if (i == count) {
+ return 0;
+ }
+
+ /* Should not be here */
+ dev_dbg(hw_priv->pdev, "Error, Less HW buf %d,%d.\n",
+ hw_priv->hw_bufs_used, hw_priv->wsm_caps.numInpChBufs);
+ SYS_WARN(1);
+ return -1;
+}
+#endif
+
+/* reserve a packet for the case dev_alloc_skb failed in bh.*/
+int xradio_init_resv_skb(struct xradio_common *hw_priv)
+{
+ int len = (SDIO_BLOCK_SIZE<<2) + WSM_TX_EXTRA_HEADROOM + \
+ 8 + 12; /* TKIP IV + ICV and MIC */
+
+ hw_priv->skb_reserved = dev_alloc_skb(len);
+ if (hw_priv->skb_reserved) {
+ hw_priv->skb_resv_len = len;
+ } else {
+ dev_warn(hw_priv->pdev, "xr_alloc_skb failed(%d)\n", len);
+ }
+ return 0;
+}
+
+void xradio_deinit_resv_skb(struct xradio_common *hw_priv)
+{
+ if (hw_priv->skb_reserved) {
+ dev_kfree_skb(hw_priv->skb_reserved);
+ hw_priv->skb_reserved = NULL;
+ hw_priv->skb_resv_len = 0;
+ }
+}
+
+int xradio_realloc_resv_skb(struct xradio_common *hw_priv,
+ struct sk_buff *skb)
+{
+ if (!hw_priv->skb_reserved && hw_priv->skb_resv_len) {
+ hw_priv->skb_reserved = dev_alloc_skb(hw_priv->skb_resv_len);
+ if (!hw_priv->skb_reserved) {
+ hw_priv->skb_reserved = skb;
+ dev_warn(hw_priv->pdev, "xr_alloc_skb failed(%d)\n",
+ hw_priv->skb_resv_len);
+ return -1;
+ }
+ }
+ return 0; /* realloc sbk success, deliver to upper.*/
+}
+
+static inline struct sk_buff *xradio_get_resv_skb(struct xradio_common *hw_priv,
+ size_t len)
+{ struct sk_buff *skb = NULL;
+ if (hw_priv->skb_reserved && len <= hw_priv->skb_resv_len) {
+ skb = hw_priv->skb_reserved;
+ hw_priv->skb_reserved = NULL;
+ }
+ return skb;
+}
+
+static inline int xradio_put_resv_skb(struct xradio_common *hw_priv,
+ struct sk_buff *skb)
+{
+ if (!hw_priv->skb_reserved && hw_priv->skb_resv_len) {
+ hw_priv->skb_reserved = skb;
+ return 0;
+ }
+ return 1; /* sbk not put to reserve*/
+}
+
+static struct sk_buff *xradio_get_skb(struct xradio_common *hw_priv, size_t len)
+{
+ struct sk_buff *skb = NULL;
+ size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE;
+
+ /* TKIP IV + TKIP ICV and MIC - Piggyback.*/
+ alloc_len += WSM_TX_EXTRA_HEADROOM + 8 + 12- 2;
+ if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) {
+ skb = dev_alloc_skb(alloc_len);
+ /* In AP mode RXed SKB can be looped back as a broadcast.
+ * Here we reserve enough space for headers. */
+ if (skb) {
+ skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */
+ - WSM_RX_EXTRA_HEADROOM);
+ } else {
+ skb = xradio_get_resv_skb(hw_priv, alloc_len);
+ if (skb) {
+ dev_dbg(hw_priv->pdev, "get skb_reserved(%d)!\n", alloc_len);
+ skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */
+ - WSM_RX_EXTRA_HEADROOM);
+ } else {
+ dev_dbg(hw_priv->pdev, "xr_alloc_skb failed(%d)!\n", alloc_len);
+ }
+ }
+ } else {
+ skb = hw_priv->skb_cache;
+ hw_priv->skb_cache = NULL;
+ }
+ return skb;
+}
+
+static void xradio_put_skb(struct xradio_common *hw_priv, struct sk_buff *skb)
+{
+ if (hw_priv->skb_cache)
+ dev_kfree_skb(skb);
+ else
+ hw_priv->skb_cache = skb;
+}
+
+static int xradio_bh_read_ctrl_reg(struct xradio_common *hw_priv,
+ u16 *ctrl_reg)
+{
+ int ret;
+ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, ctrl_reg);
+ if (ret) {
+ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, ctrl_reg);
+ if (ret) {
+ hw_priv->bh_error = 1;
+ dev_err(hw_priv->pdev, "Failed to read control register.\n");
+ }
+ }
+
+ return ret;
+}
+
+static int xradio_device_wakeup(struct xradio_common *hw_priv)
+{
+ u16 ctrl_reg;
+ int ret, i=0;
+
+ /* To force the device to be always-on, the host sets WLAN_UP to 1 */
+ ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT);
+ if (SYS_WARN(ret))
+ return ret;
+
+ ret = xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+ if (SYS_WARN(ret))
+ return ret;
+
+ /* If the device returns WLAN_RDY as 1, the device is active and will
+ * remain active. */
+ while (!(ctrl_reg & HIF_CTRL_RDY_BIT) && i < 500) {
+ ret = xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+ msleep(1);
+ i++;
+ }
+ if (unlikely(i >= 500)) {
+ dev_err(hw_priv->pdev, "Device cannot wakeup.\n");
+ return -1;
+ } else if (unlikely(i >= 50))
+ dev_warn(hw_priv->pdev, "Device wakeup time=%dms.\n", i);
+ dev_dbg(hw_priv->pdev, "Device awake, t=%dms.\n", i);
+ return 1;
+}
+
+/* Must be called from BH thraed. */
+void xradio_enable_powersave(struct xradio_vif *priv,
+ bool enable)
+{
+ priv->powersave_enabled = enable;
+}
+
+static void xradio_bh_rx_dump(struct device *dev, u8 *data, size_t len){
+#ifdef DEBUG
+ static const char *msgnames[0xffff] = {
+ // 0x4?? is a sync response to a command
+ [0x0404] = "tx confirm",
+ [0x0406] = "mib confirm",
+ [0x0407] = "scan started",
+ [0x0409] = "configuration confirm",
+ [0x040a] = "reset confirm",
+ [0x040b] = "join confirm",
+ [0x040c] = "key added",
+ [0x040d] = "key removed",
+ [0x0410] = "pm confirm",
+ [0x0411] = "set bss params",
+ [0x0412] = "tx queue params",
+ [0x0413] = "edca confirm",
+ [0x0417] = "start confirm",
+ [0x041b] = "update ie confirm",
+ [0x041c] = "map link confirm",
+ // 0x8?? seem to be async responses or events
+ [0x0801] = "firmware startup complete",
+ [0x0804] = "rx",
+ [0x0805] = "event",
+ [0x0806] = "scan complete",
+ [0x0810] = "set pm indication"
+ };
+
+ u16 msgid, ifid;
+ u16 *p = (u16 *)data;
+ msgid = (*(p + 1)) & 0xC3F;
+ ifid = (*(p + 1)) >> 6;
+ ifid &= 0xF;
+ const char *msgname = msgnames[msgid];
+ if(msgid == 0x804 && ifid == 2){
+ msgname = "scan result";
+ }
+
+ dev_dbg(dev, "vif %d: sdio rx, msgid %s(0x%.4X) len %d\n",
+ ifid, msgname, msgid, *p);
+// print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE,
+// data, min(len, (size_t) 64));
+#endif
+}
+
+#define READLEN(ctrl) ((ctrl & HIF_CTRL_NEXT_LEN_MASK) << 1) //read_len=ctrl_reg*2.
+
+static int xradio_bh_rx_availlen(struct xradio_common *hw_priv){
+ u16 ctrl_reg = 0;
+ if (xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg)) {
+ return -EIO;
+ }
+ return READLEN(ctrl_reg);
+}
+
+static int xradio_bh_rx(struct xradio_common *hw_priv, u16* nextlen) {
+ size_t read_len = 0;
+ struct sk_buff *skb_rx = NULL;
+ struct wsm_hdr *wsm;
+ size_t wsm_len;
+ int wsm_id;
+ u8 wsm_seq;
+ size_t alloc_len;
+ u8 *data;
+ int ret;
+
+ read_len = *nextlen > 0 ? *nextlen : xradio_bh_rx_availlen(hw_priv);
+ if(read_len <= 0)
+ return read_len;
+
+ if (read_len < sizeof(struct wsm_hdr) || (read_len > EFFECTIVE_BUF_SIZE)) {
+ dev_err(hw_priv->pdev, "Invalid read len: %d", read_len);
+ return -1;
+ }
+
+ /* Add SIZE of PIGGYBACK reg (CONTROL Reg)
+ * to the NEXT Message length + 2 Bytes for SKB */
+ read_len = read_len + 2;
+
+#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES)
+ alloc_len = sdio_align_len(hw_priv, read_len);
+#else
+ /* Platform's SDIO workaround */
+ alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1);
+ if (read_len & (SDIO_BLOCK_SIZE - 1))
+ alloc_len += SDIO_BLOCK_SIZE;
+#endif /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */
+ /* Check if not exceeding XRADIO capabilities */
+ if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) {
+ dev_err(hw_priv->pdev, "Read aligned len: %d\n", alloc_len);
+ }
+
+ /* Get skb buffer. */
+ skb_rx = xradio_get_skb(hw_priv, alloc_len);
+ if (!skb_rx) {
+ dev_err(hw_priv->pdev, "xradio_get_skb failed.\n");
+ return -ENOMEM;
+ }
+ skb_trim(skb_rx, 0);
+ skb_put(skb_rx, read_len);
+ data = skb_rx->data;
+ if (!data) {
+ dev_err(hw_priv->pdev, "skb data is NULL.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Read data from device. */
+ if (xradio_data_read(hw_priv, data, alloc_len)) {
+ ret = -EIO;
+ goto out;
+ }
+
+ /* the ctrl register is appened to the end of the wsm frame
+ * so we can use this to avoid reading the control register
+ * again for the next read .. but if this is invalid we'll
+ * do an invalid read and the firmware will crash so this
+ * probably needs some sort of validation */
+ *nextlen = READLEN(__le16_to_cpu(((__le16 *) data)[(alloc_len >> 1) - 1]));
+
+ /* check wsm length. */
+ wsm = (struct wsm_hdr *) data;
+ wsm_len = __le32_to_cpu(wsm->len);
+
+ if (SYS_WARN(wsm_len > read_len)) {
+ dev_err(hw_priv->pdev, "wsm_len=%d.\n", wsm_len);
+ ret = -1;
+ goto out;
+ }
+
+ /* dump rx data. */
+ xradio_bh_rx_dump(hw_priv->pdev, data, wsm_len);
+
+ /* extract wsm id and seq. */
+ wsm_id = __le32_to_cpu(wsm->id) & 0xFFF;
+ wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7;
+ skb_trim(skb_rx, wsm_len);
+
+ /* process exceptions. */
+ if (wsm_id == 0) {
+ printk("wtf?\n");
+ ret = 0;
+ goto out;
+ } else if (unlikely(wsm_id == 0x0800)) {
+ dev_err(hw_priv->pdev, "firmware exception!\n");
+ wsm_handle_exception(hw_priv, &data[sizeof(*wsm)],
+ wsm_len - sizeof(*wsm));
+ ret = -1;
+ goto out;
+ }
+
+ hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+
+ /* Process tx frames confirm. */
+ if (wsm_id & 0x0400) {
+ if (wsm_release_tx_buffer(hw_priv, 1) < 0) {
+ dev_err(hw_priv->pdev, "tx buffer < 0.\n");
+ ret = -1;
+ goto out;
+ }
+ }
+
+ /* WSM processing frames. */
+ if (wsm_handle_rx(hw_priv, wsm_id, wsm, &skb_rx)) {
+ dev_err(hw_priv->pdev, "wsm_handle_rx failed.\n");
+ ret = -1;
+ goto out;
+ }
+
+ ret = 1;
+
+out:
+ /* Reclaim the SKB buffer */
+ if (skb_rx) {
+ if (xradio_put_resv_skb(hw_priv, skb_rx))
+ xradio_put_skb(hw_priv, skb_rx);
+ }
+
+ return ret;
+}
+
+static void xradio_bh_tx_dump(struct device *dev, u8 *data, size_t len){
+#ifdef DEBUG
+ static const char *msgnames[0xffff] = {
+ [0x0004] = "tx",
+ [0x0006] = "MIB",
+ [0x0007] = "start scan",
+ [0x0009] = "configure",
+ [0x000A] = "reset",
+ [0x000B] = "join",
+ [0x000C] = "add key",
+ [0x000D] = "remove key",
+ [0x0010] = "set pm",
+ [0x0011] = "set bss params",
+ [0x0012] = "set tx queue params",
+ [0x0013] = "set edca",
+ [0x0017] = "start",
+ [0x001b] = "update ie",
+ [0x001c] = "map link",
+ };
+ static const char *mibnames[0xffff] = {
+ [0x0003] = "DOT11_SLOT_TIME",
+ [0x1002] = "TEMPLATE_FRAME",
+ [0x1003] = "RX_FILTER",
+ [0x1004] = "BEACON_FILTER_TABLE",
+ [0x1005] = "BEACON_FILTER_ENABLE",
+ [0x1006] = "OPERATIONAL POWER MODE",
+ [0x1007] = "BEACON_WAKEUP_PERIOD",
+ [0x1009] = "RCPI_RSSI_THRESHOLD",
+ [0x1010] = "SET_ASSOCIATION_MODE",
+ [0x100e] = "BLOCK_ACK_POLICY",
+ [0x100f] = "OVERRIDE_INTERNAL_TX_RATE",
+ [0x1013] = "SET_UAPSD_INFORMATION",
+ [0x1016] = "SET_TX_RATE_RETRY_POLICY",
+ [0x1020] = "PROTECTED_MGMT_POLICY",
+ [0x1021] = "SET_HT_PROTECTION",
+ [0x1024] = "USE_MULTI_TX_CONF",
+ [0x1025] = "KEEP_ALIVE_PERIOD",
+ [0x1026] = "DISABLE_BSSID_FILTER",
+ [0x1035] = "SET_INACTIVITY",
+ };
+
+ u16 msgid, ifid, mib;
+ u16 *p = (u16 *)data;
+ msgid = (*(p + 1)) & 0x3F;
+ ifid = (*(p + 1)) >> 6;
+ ifid &= 0xF;
+ mib = *(p + 2);
+
+ WARN_ON(msgnames[msgid] == NULL);
+
+ if (msgid == 0x0006) {
+ dev_dbg(dev, "vif %d: sdio tx, msgid %s(0x%.4X) len %d MIB %s(0x%.4X)\n",
+ ifid, msgnames[msgid], msgid,*p, mibnames[mib], mib);
+ } else {
+ dev_dbg(dev, "vif %d: sdio tx, msgid %s(0x%.4X) len %d\n", ifid, msgnames[msgid], msgid, *p);
+ }
+
+// print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, data,
+// min(len, (size_t) 64));
+#endif
+}
+
+static int xradio_bh_tx(struct xradio_common *hw_priv){
+ int txavailable;
+ int txburst;
+ int vif_selected;
+ struct wsm_hdr *wsm;
+ size_t tx_len;
+ int ret;
+ u8 *data;
+
+ SYS_BUG(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
+ txavailable = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used;
+ if (txavailable) {
+ /* Wake up the devices */
+ if (hw_priv->device_can_sleep) {
+ ret = xradio_device_wakeup(hw_priv);
+ if (SYS_WARN(ret < 0)) {
+ return -1;
+ } else if (ret) {
+ hw_priv->device_can_sleep = false;
+ } else { /* Wait for "awake" interrupt */
+ dev_dbg(hw_priv->pdev,
+ "need to wait for device to wake before doing tx\n");
+ return 0;
+ }
+ }
+ /* Increase Tx buffer*/
+ wsm_alloc_tx_buffer(hw_priv);
+
+ /* Get data to send and send it. */
+ ret = wsm_get_tx(hw_priv, &data, &tx_len, &txburst, &vif_selected);
+ if (ret <= 0) {
+ wsm_release_tx_buffer(hw_priv, 1);
+ if (SYS_WARN(ret < 0)) {
+ dev_err(hw_priv->pdev, "wsm_get_tx=%d.\n", ret);
+ return -ENOMEM;
+ } else {
+ return 0;
+ }
+ } else {
+ wsm = (struct wsm_hdr *) data;
+ SYS_BUG(tx_len < sizeof(*wsm));
+ SYS_BUG(__le32_to_cpu(wsm->len) != tx_len);
+
+ /* Align tx length and check it. */
+#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES)
+ if (tx_len <= 8)
+ tx_len = 16;
+ tx_len = sdio_align_len(hw_priv, tx_len);
+#else /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */
+ /* HACK!!! Platform limitation.
+ * It is also supported by upper layer:
+ * there is always enough space at the end of the buffer. */
+ if (tx_len & (SDIO_BLOCK_SIZE - 1)) {
+ tx_len &= ~(SDIO_BLOCK_SIZE - 1);
+ tx_len += SDIO_BLOCK_SIZE;
+ }
+#endif /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */
+ /* Check if not exceeding XRADIO capabilities */
+ if (tx_len > EFFECTIVE_BUF_SIZE) {
+ dev_warn(hw_priv->pdev, "Write aligned len: %d\n", tx_len);
+ }
+
+ /* Make sequence number. */
+ wsm->id &= __cpu_to_le32(~WSM_TX_SEQ(WSM_TX_SEQ_MAX));
+ wsm->id |= cpu_to_le32(WSM_TX_SEQ(hw_priv->wsm_tx_seq));
+
+ /* Send the data to devices. */
+ if (SYS_WARN(xradio_data_write(hw_priv, data, tx_len))) {
+ wsm_release_tx_buffer(hw_priv, 1);
+ dev_err(hw_priv->pdev, "xradio_data_write failed\n");
+ return -EIO;
+ }
+
+ xradio_bh_tx_dump(hw_priv->pdev, data, tx_len);
+
+ /* Process after data have sent. */
+ if (vif_selected != -1) {
+ hw_priv->hw_bufs_used_vif[vif_selected]++;
+ }
+ wsm_txed(hw_priv, data);
+ hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX;
+
+ return 1;
+ }
+ } else
+ return 0;
+}
+
+static int xradio_bh_exchange(struct xradio_common *hw_priv) {
+ int rxdone;
+ int txdone;
+ u16 nextlen = 0;
+
+ /* query stuck frames in firmware. */
+ if (atomic_xchg(&hw_priv->query_cnt, 0)) {
+ if (schedule_work(&hw_priv->query_work) <= 0)
+ atomic_add(1, &hw_priv->query_cnt);
+ }
+
+ /* keep doing tx and rx until they both stop */
+ do {
+ txdone = xradio_bh_tx(hw_priv);
+ if (txdone < 0) {
+ break;
+ }
+ rxdone = xradio_bh_rx(hw_priv, &nextlen);
+ if (rxdone < 0) {
+ break;
+ }
+ } while (txdone > 0 || rxdone > 0);
+ return 0;
+}
+
+static int xradio_bh(void *arg)
+{
+ struct xradio_common *hw_priv = arg;
+ int term, suspend;
+ int wake = 0;
+ long timeout;
+ long status;
+
+ for (;;) {
+ timeout = HZ / 30;
+
+ // wait for something to happen or a timeout
+ status = wait_event_interruptible_timeout(hw_priv->bh_wq, ( {
+ wake = atomic_xchg(&hw_priv->bh_tx, 0);
+ term = kthread_should_stop();
+ suspend = atomic_read(&hw_priv->bh_suspend);
+ (wake || term || suspend);}), timeout);
+
+ if (wake) {
+ if(xradio_bh_exchange(hw_priv) < 0){
+ break;
+ }
+ } else if (term) {
+ dev_dbg(hw_priv->pdev, "xradio_bh exit!\n");
+ break;
+ } else if (status < 0) {
+ dev_err(hw_priv->pdev, "bh_error=%d, status=%ld\n",
+ hw_priv->bh_error, status);
+ break;
+ } else if (!status) {
+ /* check if there is data waiting but we missed the interrupt*/
+ if (xradio_bh_rx_availlen(hw_priv) > 0) {
+ dev_warn(hw_priv->pdev, "missed interrupt\n");
+ if(xradio_bh_exchange(hw_priv) < 0){
+ break;
+ }
+ }
+ /* There are some frames to be confirmed. */
+ else if (hw_priv->hw_bufs_used) {
+ long timeout = 0;
+ bool pending = 0;
+ dev_dbg(hw_priv->pdev, "Need confirm:%d!\n",
+ hw_priv->hw_bufs_used);
+ /* Check if frame transmission is timed out. */
+ pending = xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS,
+ hw_priv->pending_frame_id, &timeout);
+ /* There are some frames confirm time out. */
+ if (pending && timeout < 0) {
+ dev_err(hw_priv->pdev, "query_txpkt_timeout:%ld!\n",
+ timeout);
+ break;
+ }
+ } //else if (!txpending){
+ //if (hw_priv->powersave_enabled && !hw_priv->device_can_sleep && !atomic_read(&hw_priv->recent_scan)) {
+ // /* Device is idle, we can go to sleep. */
+ // dev_dbg(hw_priv->pdev, "Device idle(timeout), can sleep.\n");
+ // SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0));
+ // hw_priv->device_can_sleep = true;
+ //}
+ //continue;
+ //}
+ } else if (suspend) {
+ dev_dbg(hw_priv->pdev, "Host suspend request.\n");
+ /* Check powersave setting again. */
+ if (hw_priv->powersave_enabled) {
+ dev_dbg(hw_priv->pdev,
+ "Device idle(host suspend), can sleep.\n");
+ SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0));
+ hw_priv->device_can_sleep = true;
+ }
+
+ /* bh thread go to suspend. */
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_SUSPENDED);
+ wake_up(&hw_priv->bh_evt_wq);
+ status = wait_event_interruptible(hw_priv->bh_wq,
+ XRADIO_BH_RESUME == atomic_read(&hw_priv->bh_suspend));
+
+ if (status < 0) {
+ dev_err(hw_priv->pdev, "Failed to wait for resume: %ld.\n",
+ status);
+ break;
+ }
+ dev_dbg(hw_priv->pdev, "Host resume.\n");
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED);
+ wake_up(&hw_priv->bh_evt_wq);
+ }
+ } /* for (;;)*/
+
+ dev_err(hw_priv->pdev, "bh thread exiting\n");
+
+ /* If BH Error, handle it. */
+ if (!term) {
+ dev_err(hw_priv->pdev, "Fatal error, exitting code=%d.\n",
+ hw_priv->bh_error);
+
+#ifdef HW_ERROR_WIFI_RESET
+ /* notify upper layer to restart wifi.
+ * don't do it in debug version. */
+ wsm_upper_restart(hw_priv);
+#endif
+ /* TODO: schedule_work(recovery) */
+#ifndef HAS_PUT_TASK_STRUCT
+ /* The only reason of having this stupid code here is
+ * that __put_task_struct is not exported by kernel. */
+ for (;;) {
+ int status = wait_event_interruptible(hw_priv->bh_wq, ({
+ term = kthread_should_stop();
+ (term);}));
+ if (status || term)
+ break;
+ }
+#endif
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/xradio/bh.h b/drivers/net/wireless/xradio/bh.h
new file mode 100644
index 00000000..ca9187e2
--- /dev/null
+++ b/drivers/net/wireless/xradio/bh.h
@@ -0,0 +1,36 @@
+/*
+ * Data Transmission thread for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef XRADIO_BH_H
+#define XRADIO_BH_H
+
+#define XRADIO_BH_THREAD "xradio_bh"
+
+/* extern */ struct xradio_common;
+
+#define SDIO_BLOCK_SIZE (528)
+
+int xradio_register_bh(struct xradio_common *hw_priv);
+void xradio_unregister_bh(struct xradio_common *hw_priv);
+void xradio_irq_handler(struct xradio_common *hw_priv);
+void xradio_bh_wakeup(struct xradio_common *hw_priv);
+int xradio_bh_suspend(struct xradio_common *hw_priv);
+int xradio_bh_resume(struct xradio_common *hw_priv);
+/* Must be called from BH thread. */
+void xradio_enable_powersave(struct xradio_vif *priv, bool enable);
+int wsm_release_tx_buffer(struct xradio_common *hw_priv, int count);
+int wsm_release_vif_tx_buffer(struct xradio_common *hw_priv, int if_id,
+ int count);
+int xradio_init_resv_skb(struct xradio_common *hw_priv);
+void xradio_deinit_resv_skb(struct xradio_common *hw_priv);
+int xradio_realloc_resv_skb(struct xradio_common *hw_priv,
+ struct sk_buff *skb);
+#endif /* XRADIO_BH_H */
diff --git a/drivers/net/wireless/xradio/common.h b/drivers/net/wireless/xradio/common.h
new file mode 100644
index 00000000..1ce23dee
--- /dev/null
+++ b/drivers/net/wireless/xradio/common.h
@@ -0,0 +1,101 @@
+/*
+ * Common interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef XRADIO_COMMON_H
+#define XRADIO_COMMON_H
+
+/*******************************************************
+ interfaces for parse frame protocol info.
+********************************************************/
+#define LLC_LEN 8
+#define LLC_TYPE_OFF 6 //Ether type offset
+#define IP_PROTO_OFF 9 //protocol offset
+#define IP_S_ADD_OFF 12
+#define IP_D_ADD_OFF 16
+#define UDP_LEN 8
+//DHCP
+#define DHCP_BOOTP_C 68
+#define DHCP_BOOTP_S 67
+#define UDP_BOOTP_LEN 236 //exclude "Options:64"
+#define BOOTP_OPS_LEN 64
+#define DHCP_MAGIC 0x63825363
+#define DHCP_DISCOVER 0x01
+#define DHCP_OFFER 0x02
+#define DHCP_REQUEST 0x03
+#define DHCP_DECLINE 0x04
+#define DHCP_ACK 0x05
+#define DHCP_NACK 0x06
+#define DHCP_RELEASE 0x07
+
+//LLC layer.
+static inline bool is_SNAP(u8* llc_data)
+{
+ return (bool)(*(u16*)(llc_data) == 0xAAAA && llc_data[2] == 0x03); //0xAA, 0xAA, 0x03.
+}
+
+static inline bool is_STP(u8* llc_data)
+{
+ return (bool)(*(u16*)(llc_data) == 0xAAAA && llc_data[2] == 0x03); //0x42, 0x42, 0x03.
+}
+
+//IP/IPV6/ARP layer...
+static inline bool is_ip(u8* llc_data)
+{
+ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IP)); //0x0800
+}
+static inline bool is_ipv6(u8* llc_data)
+{
+ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IPV6)); //0x08dd
+}
+static inline bool is_arp(u8* llc_data)
+{
+ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_ARP)); //0x0806
+}
+static inline bool is_8021x(u8* llc_data)
+{
+ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_PAE)); //0x888E
+}
+
+//TCP/UDP layer...
+static inline bool is_tcp(u8* llc_data)
+{
+ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_TCP); //
+}
+
+static inline bool is_udp(u8* llc_data)
+{
+ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_UDP); //
+}
+
+static inline bool is_icmp(u8* llc_data)
+{
+ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_ICMP); //
+}
+
+static inline bool is_igmp(u8* llc_data)
+{
+ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_IGMP); //
+}
+
+static inline bool is_dhcp(u8* llc_data)
+{
+ u8* ip_hdr = llc_data+LLC_LEN;
+ if(!is_ip(llc_data))
+ return (bool)0;
+ if(ip_hdr[IP_PROTO_OFF] == IPPROTO_UDP) {
+ u8* udp_hdr = ip_hdr+((ip_hdr[0]&0xf)<<2); //ihl:words
+ return (bool)((((udp_hdr[0]<<8)|udp_hdr[1]) == DHCP_BOOTP_C) || //DHCP client
+ (((udp_hdr[0]<<8)|udp_hdr[1]) == DHCP_BOOTP_S)); //DHCP server
+ }
+ return (bool)0;
+}
+
+#endif //XRADIO_COMMON_H
diff --git a/drivers/net/wireless/xradio/debug.c b/drivers/net/wireless/xradio/debug.c
new file mode 100644
index 00000000..dc57641d
--- /dev/null
+++ b/drivers/net/wireless/xradio/debug.c
@@ -0,0 +1,3265 @@
+/*
+ * Debug code for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*Linux version 3.4.0 compilation*/
+//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
+#include<linux/module.h>
+//#endif
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/rtc.h>
+#include <linux/time.h>
+
+#include "xradio.h"
+#include "hwio.h"
+#include "debug.h"
+
+/*added by yangfh, for host debuglevel*/
+u8 dbg_common = 0xff;
+u8 dbg_sbus = 0xff;
+u8 dbg_bh = 0xff;
+u8 dbg_txrx = 0xff;
+u8 dbg_wsm = 0xff;
+u8 dbg_sta = 0xff;
+u8 dbg_scan = 0xff;
+u8 dbg_ap = 0xff;
+u8 dbg_pm = 0xff;
+u8 dbg_itp = 0xff;
+u8 dbg_logfile = XRADIO_DBG_ERROR;
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+/* join_status */
+static const char * const xradio_debug_join_status[] = {
+ "passive",
+ "monitor",
+ "station",
+ "access point",
+};
+
+/* WSM_JOIN_PREAMBLE_... */
+static const char * const xradio_debug_preamble[] = {
+ "long",
+ "short",
+ "long on 1 and 2 Mbps",
+};
+
+static const char * const xradio_debug_fw_types[] = {
+ "ETF",
+ "WFM",
+ "WSM",
+ "HI test",
+ "Platform test",
+};
+
+static const char * const xradio_debug_link_id[] = {
+ "OFF",
+ "REQ",
+ "SOFT",
+ "HARD",
+};
+
+static const char *xradio_debug_mode(int mode)
+{
+ switch (mode) {
+ case NL80211_IFTYPE_UNSPECIFIED:
+ return "unspecified";
+ case NL80211_IFTYPE_MONITOR:
+ return "monitor";
+ case NL80211_IFTYPE_STATION:
+ return "station";
+ case NL80211_IFTYPE_ADHOC:
+ return "ad-hok";
+ case NL80211_IFTYPE_MESH_POINT:
+ return "mesh point";
+ case NL80211_IFTYPE_AP:
+ return "access point";
+ case NL80211_IFTYPE_P2P_CLIENT:
+ return "p2p client";
+ case NL80211_IFTYPE_P2P_GO:
+ return "p2p go";
+ default:
+ return "unsupported";
+ }
+}
+
+static void xradio_queue_status_show(struct seq_file *seq,
+ struct xradio_queue *q)
+{
+ int i, if_id;
+ seq_printf(seq, "Queue %d:\n", q->queue_id);
+ seq_printf(seq, " capacity: %d\n", q->capacity);
+ seq_printf(seq, " queued: %d\n", q->num_queued);
+ seq_printf(seq, " pending: %d\n", q->num_pending);
+ seq_printf(seq, " sent: %d\n", q->num_sent);
+ seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no");
+ seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no");
+ seq_puts(seq, " link map: 0-> ");
+ for (if_id = 0; if_id < XRWL_MAX_VIFS; if_id++) {
+ for (i = 0; i < q->stats->map_capacity; ++i)
+ seq_printf(seq, "%.2d ", q->link_map_cache[if_id][i]);
+ seq_printf(seq, "<-%d\n", q->stats->map_capacity);
+ }
+}
+
+static void xradio_debug_print_map(struct seq_file *seq,
+ struct xradio_vif *priv,
+ const char *label,
+ u32 map)
+{
+ int i;
+ seq_printf(seq, "%s0-> ", label);
+ for (i = 0; i < priv->hw_priv->tx_queue_stats.map_capacity; ++i)
+ seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : "..");
+ seq_printf(seq, "<-%d\n",
+ priv->hw_priv->tx_queue_stats.map_capacity - 1);
+}
+
+static int xradio_version_show(struct seq_file *seq, void *v)
+{
+ struct xradio_common *hw_priv = seq->private;
+ seq_printf(seq, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]);
+ return 0;
+}
+
+static int xradio_version_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_version_show, inode->i_private);
+}
+
+static const struct file_operations fops_version = {
+ .open = xradio_version_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+#if defined(DGB_XRADIO_QC)
+static int xradio_hwinfo_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct xradio_common *hw_priv = seq->private;
+ u32 hw_arry[8] = { 0 };
+
+ //
+ wsm_read_mib(hw_priv, WSM_MIB_ID_HW_INFO, (void*)&hw_arry, sizeof(hw_arry), 4);
+
+ //get_random_bytes((u8 *)&hw_arry[0], 8*sizeof(u32));
+ //hw_arry[0] = 0x0B140D4;
+ //hw_arry[1] &= ~0xF803FFFF;
+ //hw_arry[2] &= ~0xC0001FFF;
+ //hw_arry[5] &= ~0xFFFFF000;
+ //hw_arry[7] &= ~0xFFFC07C0;
+
+ seq_printf(seq, "0x%08x,0x%08x,0x%08x,0x%08x,"
+ "0x%08x,0x%08x,0x%08x,0x%08x\n",
+ hw_arry[0], hw_arry[1], hw_arry[2], hw_arry[3],
+ hw_arry[4], hw_arry[5], hw_arry[6], hw_arry[7]);
+ return 0;
+}
+
+static int xradio_hwinfo_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_hwinfo_show, inode->i_private);
+}
+
+static const struct file_operations fops_hwinfo = {
+ .open = xradio_hwinfo_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+#endif
+
+static int xradio_status_show_common(struct seq_file *seq, void *v)
+{
+ int i;
+ struct list_head *item;
+ struct xradio_common *hw_priv = seq->private;
+ struct xradio_debug_common *d = hw_priv->debug;
+ int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0;
+ bool ba_ena;
+
+ spin_lock_bh(&hw_priv->ba_lock);
+ ba_cnt = hw_priv->debug->ba_cnt;
+ ba_acc = hw_priv->debug->ba_acc;
+ ba_cnt_rx = hw_priv->debug->ba_cnt_rx;
+ ba_acc_rx = hw_priv->debug->ba_acc_rx;
+ ba_ena = hw_priv->ba_ena;
+ if (ba_cnt)
+ ba_avg = ba_acc / ba_cnt;
+ if (ba_cnt_rx)
+ ba_avg_rx = ba_acc_rx / ba_cnt_rx;
+ spin_unlock_bh(&hw_priv->ba_lock);
+
+ seq_puts(seq, "XRADIO Wireless LAN driver status\n");
+ seq_printf(seq, "Hardware: %d.%d\n",
+ hw_priv->wsm_caps.hardwareId,
+ hw_priv->wsm_caps.hardwareSubId);
+ seq_printf(seq, "Firmware: %s %d.%d\n",
+ xradio_debug_fw_types[hw_priv->wsm_caps.firmwareType],
+ hw_priv->wsm_caps.firmwareVersion,
+ hw_priv->wsm_caps.firmwareBuildNumber);
+ seq_printf(seq, "FW API: %d\n",
+ hw_priv->wsm_caps.firmwareApiVer);
+ seq_printf(seq, "FW caps: 0x%.4X\n",
+ hw_priv->wsm_caps.firmwareCap);
+ if (hw_priv->channel)
+ seq_printf(seq, "Channel: %d%s\n",
+ hw_priv->channel->hw_value,
+ hw_priv->channel_switch_in_progress ?
+ " (switching)" : "");
+ seq_printf(seq, "HT: %s\n",
+ xradio_is_ht(&hw_priv->ht_oper) ? "on" : "off");
+ if (xradio_is_ht(&hw_priv->ht_oper)) {
+ seq_printf(seq, "Greenfield: %s\n",
+ xradio_ht_greenfield(&hw_priv->ht_oper) ? "yes" : "no");
+ seq_printf(seq, "AMPDU dens: %d\n",
+ xradio_ht_ampdu_density(&hw_priv->ht_oper));
+ }
+ spin_lock_bh(&hw_priv->tx_policy_cache.lock);
+ i = 0;
+ list_for_each(item, &hw_priv->tx_policy_cache.used)
+ ++i;
+ spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
+ seq_printf(seq, "RC in use: %d\n", i);
+ seq_printf(seq, "BA stat: %d, %d (%d)\n",
+ ba_cnt, ba_acc, ba_avg);
+ seq_printf(seq, "BA RX stat: %d, %d (%d)\n",
+ ba_cnt_rx, ba_acc_rx, ba_avg_rx);
+ seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off");
+
+ seq_puts(seq, "\n");
+ for (i = 0; i < 4; ++i) {
+ xradio_queue_status_show(seq, &hw_priv->tx_queue[i]);
+ seq_puts(seq, "\n");
+ }
+ seq_printf(seq, "TX burst: %d\n",
+ d->tx_burst);
+ seq_printf(seq, "RX burst: %d\n",
+ d->rx_burst);
+ seq_printf(seq, "TX miss: %d\n",
+ d->tx_cache_miss);
+ seq_printf(seq, "Long retr: %d\n",
+ hw_priv->long_frame_max_tx_count);
+ seq_printf(seq, "Short retr: %d\n",
+ hw_priv->short_frame_max_tx_count);
+
+ seq_printf(seq, "BH status: %s, errcode=%d\n",
+ atomic_read(&hw_priv->bh_term) ? "terminated" : "alive",
+ hw_priv->bh_error);
+ seq_printf(seq, "Pending TX: %d\n",
+ atomic_read(&hw_priv->bh_tx));
+
+ seq_printf(seq, "TX bufs: %d x %d bytes\n",
+ hw_priv->wsm_caps.numInpChBufs,
+ hw_priv->wsm_caps.sizeInpChBuf);
+ seq_printf(seq, "Used bufs: %d\n",
+ hw_priv->hw_bufs_used);
+ seq_printf(seq, "Powersavemode:%s\n",
+ hw_priv->powersave_enabled ? "enable" : "disable");
+ seq_printf(seq, "Device: %s\n",
+ hw_priv->device_can_sleep ? "alseep" : "awake");
+
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ seq_printf(seq, "WSM status: %s\n",
+ hw_priv->wsm_cmd.done ? "idle" : "active");
+ seq_printf(seq, "WSM cmd: 0x%.4X (%d bytes)\n",
+ hw_priv->wsm_cmd.cmd, hw_priv->wsm_cmd.len);
+ seq_printf(seq, "WSM retval: %d\n",
+ hw_priv->wsm_cmd.ret);
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+
+ seq_printf(seq, "Datapath: %s\n",
+ atomic_read(&hw_priv->tx_lock) ? "locked" : "unlocked");
+ if (atomic_read(&hw_priv->tx_lock))
+ seq_printf(seq, "TXlock cnt: %d\n",
+ atomic_read(&hw_priv->tx_lock));
+
+ seq_printf(seq, "Scan: %s\n",
+ atomic_read(&hw_priv->scan.in_progress) ? "active" : "idle");
+ seq_printf(seq, "Led state: 0x%.2X\n",
+ hw_priv->softled_state);
+
+ return 0;
+}
+
+static int xradio_status_open_common(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_status_show_common,
+ inode->i_private);
+}
+
+static const struct file_operations fops_status_common = {
+ .open = xradio_status_open_common,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int xradio_counters_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct xradio_common *hw_priv = seq->private;
+ struct wsm_counters_table counters;
+
+ ret = wsm_get_counters_table(hw_priv, &counters);
+ if (ret)
+ return ret;
+
+#define CAT_STR(x, y) x ## y
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, "%s:" tab "%d\n", #name, \
+ __le32_to_cpu(counters.CAT_STR(count, name)))
+
+ PUT_COUNTER("\t\t\t\t", PlcpErrors);
+ PUT_COUNTER("\t\t\t\t", FcsErrors);
+ PUT_COUNTER("\t\t\t\t", TxPackets);
+ PUT_COUNTER("\t\t\t\t", RxPackets);
+ PUT_COUNTER("\t\t\t", RxPacketErrors);
+ PUT_COUNTER("\t\t\t\t", RtsSuccess);
+ PUT_COUNTER("\t\t\t", RtsFailures);
+ PUT_COUNTER("\t\t", RxFramesSuccess);
+ PUT_COUNTER("\t", RxDecryptionFailures);
+ PUT_COUNTER("\t\t\t", RxMicFailures);
+ PUT_COUNTER("\t\t", RxNoKeyFailures);
+ PUT_COUNTER("\t\t", TxMulticastFrames);
+ PUT_COUNTER("\t\t", TxFramesSuccess);
+ PUT_COUNTER("\t\t", TxFrameFailures);
+ PUT_COUNTER("\t\t", TxFramesRetried);
+ PUT_COUNTER("\t", TxFramesMultiRetried);
+ PUT_COUNTER("\t\t", RxFrameDuplicates);
+ PUT_COUNTER("\t\t\t", AckFailures);
+ PUT_COUNTER("\t\t", RxMulticastFrames);
+ PUT_COUNTER("\t\t", RxCMACICVErrors);
+ PUT_COUNTER("\t\t\t", RxCMACReplays);
+ PUT_COUNTER("\t\t", RxMgmtCCMPReplays);
+ PUT_COUNTER("\t\t\t", RxBIPMICErrors);
+
+
+#undef PUT_COUNTER
+#undef CAT_STR
+
+ return 0;
+}
+
+static int xradio_counters_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_counters_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_counters = {
+ .open = xradio_counters_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int xradio_backoff_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct xradio_common *hw_priv = seq->private;
+ struct wsm_backoff_counter counters;
+
+ ret = wsm_get_backoff_dbg(hw_priv, &counters);
+ if (ret)
+ return ret;
+
+#define CAT_STR(x, y) x ## y
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, tab"%s:\t%d\n", #name, \
+ (__le32_to_cpu(counters.CAT_STR(count, name))&0xffff))
+
+ PUT_COUNTER("backoff_max ", 0);
+ PUT_COUNTER("[0,7] ", 1);
+ PUT_COUNTER("[~,15] ", 2);
+ PUT_COUNTER("[~,31] ", 3);
+ PUT_COUNTER("[~,63] ", 4);
+ PUT_COUNTER("[~,127] ", 5);
+ PUT_COUNTER("[~,255] ", 6);
+ PUT_COUNTER("[~,511] ", 7);
+ PUT_COUNTER("[~,1023] ", 8);
+
+
+#undef PUT_COUNTER
+#undef CAT_STR
+
+ return 0;
+}
+
+static int xradio_backoff_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_backoff_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_backoff = {
+ .open = xradio_backoff_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+extern u32 TxedRateIdx_Map[24];
+extern u32 RxedRateIdx_Map[24];
+
+static int xradio_ratemap_show(struct seq_file *seq, void *v)
+{
+ //int ret;
+ //struct xradio_common *hw_priv = seq->private;
+
+ seq_printf(seq, "\nRateMap for Tx & RX:\n");
+#define PUT_RATE_COUNT(name,idx) \
+ seq_printf(seq, "%s\t" "%d, %d\n", #name, \
+ __le32_to_cpu(TxedRateIdx_Map[idx]),\
+ __le32_to_cpu(RxedRateIdx_Map[idx]));\
+ TxedRateIdx_Map[idx]=0;\
+ RxedRateIdx_Map[idx]=0;
+
+ PUT_RATE_COUNT("65 Mbps:", 21);
+ PUT_RATE_COUNT("58.5 Mbps:", 20);
+ PUT_RATE_COUNT("52 Mbps:", 19);
+ PUT_RATE_COUNT("39 Mbps:", 18);
+ PUT_RATE_COUNT("26 Mbps:", 17);
+ PUT_RATE_COUNT("19.5 Mbps:", 16);
+ PUT_RATE_COUNT("13 Mbps:",15);
+ PUT_RATE_COUNT("6.5 Mbps:", 14);
+
+ PUT_RATE_COUNT("54 Mbps:", 13);
+ PUT_RATE_COUNT("48 Mbps:", 12);
+ PUT_RATE_COUNT("36 Mbps:", 11);
+ PUT_RATE_COUNT("24 Mbps:", 10);
+ PUT_RATE_COUNT("18 Mbps:", 9);
+ PUT_RATE_COUNT("12 Mbps:", 8);
+ PUT_RATE_COUNT("9 Mbps:", 7);
+ PUT_RATE_COUNT("6 Mbps:", 6);
+
+ PUT_RATE_COUNT("11 Mbps:", 3);
+ PUT_RATE_COUNT("5.5 Mbps:", 2);
+ PUT_RATE_COUNT("2 Mbps:", 1);
+ PUT_RATE_COUNT("1 Mbps:", 0);
+
+#undef PUT_RATE_COUNT
+
+ return 0;
+}
+
+static int xradio_ratemap_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_ratemap_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_ratemap= {
+ .open = xradio_ratemap_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int xradio_ampducounters_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct xradio_common *hw_priv = seq->private;
+ struct wsm_ampducounters_table counters;
+
+ ret = wsm_get_ampducounters_table(hw_priv, &counters);
+ if (ret)
+ return ret;
+
+#define CAT_STR(x, y) x ## y
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, "%s:" tab "%d\n", #name, \
+ __le32_to_cpu(counters.CAT_STR(count, name)))
+
+ PUT_COUNTER("\t\t\t\t\t", TxAMPDUs);
+ PUT_COUNTER("\t\t\t", TxMPDUsInAMPDUs);
+ PUT_COUNTER("\t\t", TxOctetsInAMPDUs_l32);
+ PUT_COUNTER("\t\t", TxOctetsInAMPDUs_h32);
+ PUT_COUNTER("\t\t\t\t\t", RxAMPDUs);
+ PUT_COUNTER("\t\t\t", RxMPDUsInAMPDUs);
+ PUT_COUNTER("\t\t", RxOctetsInAMPDUs_l32);
+ PUT_COUNTER("\t\t", RxOctetsInAMPDUs_h32);
+ PUT_COUNTER("\t", RxDelimeterCRCErrorCount);
+ PUT_COUNTER("\t\t\t", ImplictBARFailures);
+ PUT_COUNTER("\t\t\t", ExplictBARFailures);
+
+
+#undef PUT_COUNTER
+#undef CAT_STR
+
+ return 0;
+}
+
+static int xradio_ampducounters_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_ampducounters_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_ampducounters = {
+ .open = xradio_ampducounters_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int xradio_txpipe_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ struct xradio_common *hw_priv = seq->private;
+ struct wsm_txpipe_counter counters;
+
+ ret = wsm_get_txpipe_table(hw_priv, &counters);
+ if (ret)
+ return ret;
+
+#define CAT_STR(x, y) x ## y
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, tab":\t%d\n", \
+ __le32_to_cpu(counters.CAT_STR(count, name)))
+
+ PUT_COUNTER("tx-aggr ", 1);
+ PUT_COUNTER("retx-aggr ", 2);
+ PUT_COUNTER("retry_type1 ", 3);
+ PUT_COUNTER("retry_type2 ", 4);
+ PUT_COUNTER("retry_type3 ", 5);
+ PUT_COUNTER("rx-aggr-event ", 6);
+ PUT_COUNTER("rx-aggr-end ", 7);
+ PUT_COUNTER("rx-ba ", 8);
+ PUT_COUNTER("tx_ampdu_len ", 9);
+ PUT_COUNTER("fail_by_rts ", a);
+
+
+#undef PUT_COUNTER
+#undef CAT_STR
+
+ return 0;
+}
+
+static int xradio_txpipe_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_txpipe_show,
+ inode->i_private);
+}
+
+static const struct file_operations fops_txpipe = {
+ .open = xradio_txpipe_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+static int count_idx=0;
+static int xradio_dbgstats_show(struct seq_file *seq, void *v)
+{
+ int ret;
+ int avg_ampdu_len=0;
+ int FrameFail_ratio=0;
+ int FailByRts_ratio=0;
+ int FrameRetry_ratio=0;
+ int AMPDURetry_ratio=0;
+ int Retry1_ratio=0;
+ int Retry2_ratio=0;
+ int Retry3_ratio=0;
+ struct xradio_common *hw_priv = seq->private;
+ struct wsm_counters_table counters;
+ struct wsm_ampducounters_table ampdu_counters;
+ struct wsm_txpipe_counter txpipe;
+
+ ret = wsm_get_counters_table(hw_priv, &counters);
+ if (ret)
+ return ret;
+
+ ret = wsm_get_txpipe_table(hw_priv, &txpipe);
+ if (ret)
+ return ret;
+
+ ret = wsm_get_ampducounters_table(hw_priv, &ampdu_counters);
+ if (ret)
+ return ret;
+
+#define CAT_STR(x, y) x ## y
+
+#define PUT_COUNTER(tab, name) \
+ seq_printf(seq, tab "%d\n", \
+ __le32_to_cpu(counters.CAT_STR(count, name)))
+
+#define PUT_AMPDU_COUNTER(tab, name) \
+ seq_printf(seq, tab "%d\n", \
+ __le32_to_cpu(ampdu_counters.CAT_STR(count, name)))
+
+#define PUT_TXPIPE(tab, name) \
+ seq_printf(seq, tab "%d\n", \
+ __le32_to_cpu(txpipe.CAT_STR(count, name)))
+
+#define PUT_RATE_COUNT(name,idx) \
+ seq_printf(seq, "%s\t%d\n", #name, \
+ __le32_to_cpu(TxedRateIdx_Map[idx]));\
+ TxedRateIdx_Map[idx]=0;
+
+ if (ampdu_counters.countTxAMPDUs) {
+ avg_ampdu_len = (int)((ampdu_counters.countTxMPDUsInAMPDUs+(ampdu_counters.countTxAMPDUs>>1)) \
+ /ampdu_counters.countTxAMPDUs);
+ AMPDURetry_ratio = (int)(ampdu_counters.countImplictBARFailures*100/ampdu_counters.countTxAMPDUs);
+ }
+ if (counters.countAckFailures) {
+ Retry1_ratio= (int)(txpipe.count3*100/counters.countAckFailures);
+ Retry2_ratio= (int)(txpipe.count4*100/counters.countAckFailures);
+ Retry3_ratio= (int)(txpipe.count5*100/counters.countAckFailures);
+ }
+ if (ampdu_counters.countTxMPDUsInAMPDUs) {
+ FrameFail_ratio= (int)(counters.countTxFrameFailures*1000/ampdu_counters.countTxMPDUsInAMPDUs);
+ FrameRetry_ratio= (int)(counters.countAckFailures*100/ampdu_counters.countTxMPDUsInAMPDUs);
+ }
+
+ if (counters.countTxFrameFailures)
+ FailByRts_ratio = (int)(txpipe.counta*100/counters.countTxFrameFailures);
+
+ seq_printf(seq, "===========================================\n");
+ seq_printf(seq, " %02d\n", count_idx);
+ seq_printf(seq, "===========================================\n");
+ count_idx++;
+ count_idx = count_idx%100;
+ PUT_COUNTER ("RtsSuccess: ", RtsSuccess);
+ PUT_COUNTER ("RtsFailures: ", RtsFailures);
+ seq_printf(seq, "Avg_AMPDU_Len: %d\n",__le32_to_cpu(avg_ampdu_len));
+ PUT_AMPDU_COUNTER("TxAMPDUs: ", TxAMPDUs);
+ PUT_AMPDU_COUNTER("TxMPDUsInAMPDUs: ", TxMPDUsInAMPDUs);
+// PUT_COUNTER ("TxFrameRetries: ", AckFailures);
+// PUT_COUNTER ("TxFrameFailures: ", TxFrameFailures);
+ PUT_TXPIPE ("Failure_By_Rts: ", a);
+/* PUT_AMPDU_COUNTER("BA-RX-Fails ", ImplictBARFailures);
+// PUT_AMPDU_COUNTER("TxAMPDUs ", TxAMPDUs);
+// PUT_TXPIPE ("ReTx-AMPDUs ", 2);
+// PUT_TXPIPE ("Retry_type1 ", 3);
+// PUT_TXPIPE ("Retry_type2 ", 4);
+// PUT_TXPIPE ("Retry_type3 ", 5);
+*/
+ seq_printf(seq, "==============\n");
+ seq_printf(seq, "FrameFail_ratio: %d%%%%\n", __le32_to_cpu(FrameFail_ratio));
+ seq_printf(seq, "FailByRts_ratio: %d%%\n", __le32_to_cpu(FailByRts_ratio));
+ seq_printf(seq, "FrameRetry_ratio: %d%%\n", __le32_to_cpu(FrameRetry_ratio));
+ seq_printf(seq, "AMPDURetry_ratio: %d%%\n", __le32_to_cpu(AMPDURetry_ratio));
+ seq_printf(seq, "Retry1_ratio: %d%%\n", __le32_to_cpu(Retry1_ratio));
+ seq_printf(seq, "Retry2_ratio: %d%%\n", __le32_to_cpu(Retry2_ratio));
+ seq_printf(seq, "Retry3_ratio: %d%%\n", __le32_to_cpu(Retry3_ratio));
+
+ seq_printf(seq, "==============\n");
+ PUT_RATE_COUNT("65 Mbps:", 21);
+ PUT_RATE_COUNT("58.5 Mbps:", 20);
+ PUT_RATE_COUNT("52 Mbps:", 19);
+ PUT_RATE_COUNT("39 Mbps:", 18);
+ PUT_RATE_COUNT("26 Mbps:", 17);
+ PUT_RATE_COUNT("19.5 Mbps:", 16);
+ PUT_RATE_COUNT("13 Mbps:", 15);
+ PUT_RATE_COUNT("6.5 Mbps:", 14);
+
+#undef PUT_COUNTER
+#undef PUT_AMPDU_COUNTER
+#undef PUT_TXPIPE
+#undef PUT_RATE_COUNT
+#undef CAT_STR
+
+ return 0;
+}
+
+static int xradio_dbgstats_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_dbgstats_show, inode->i_private);
+}
+
+static const struct file_operations fops_dbgstats = {
+ .open = xradio_dbgstats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+static int xradio_generic_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t xradio_11n_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ struct ieee80211_supported_band *band =
+ hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ];
+ return simple_read_from_buffer(user_buf, count, ppos,
+ band->ht_cap.ht_supported ? "1\n" : "0\n", 2);
+}
+
+static ssize_t xradio_11n_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ struct ieee80211_supported_band *band[2] = {
+ hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ],
+ hw_priv->hw->wiphy->bands[NL80211_BAND_5GHZ],
+ };
+ char buf[1];
+ int ena = 0;
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, 1))
+ return -EFAULT;
+ if (buf[0] == 1)
+ ena = 1;
+
+ band[0]->ht_cap.ht_supported = ena;
+#ifdef CONFIG_XRADIO_5GHZ_SUPPORT
+ band[1]->ht_cap.ht_supported = ena;
+#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */
+
+ return count;
+}
+
+static const struct file_operations fops_11n = {
+ .open = xradio_generic_open,
+ .read = xradio_11n_read,
+ .write = xradio_11n_write,
+ .llseek = default_llseek,
+};
+
+
+static u32 fwdbg_ctrl;
+
+static ssize_t xradio_fwdbg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[12] = {0};
+ char* endptr = NULL;
+
+ count = (count > 11 ? 11 : count);
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ fwdbg_ctrl = simple_strtoul(buf, &endptr, 16);
+ xradio_dbg(XRADIO_DBG_ALWY,"fwdbg_ctrl = %d\n", fwdbg_ctrl);
+ SYS_WARN(wsm_set_fw_debug_control(hw_priv, fwdbg_ctrl, 0));
+
+ return count;
+}
+
+static ssize_t xradio_fwdbg_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[50];
+ size_t size = 0;
+
+ sprintf(buf, "fwdbg_ctrl = %u\n", fwdbg_ctrl);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+static const struct file_operations fops_fwdbg = {
+ .open = xradio_generic_open,
+ .write = xradio_fwdbg_write,
+ .read = xradio_fwdbg_read,
+ .llseek = default_llseek,
+};
+
+//add by yangfh for read/write fw registers
+static ssize_t xradio_fwreg_rw(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[256] = {0};
+ u16 buf_size = (count > 255 ? 255 : count);
+ char* startptr = &buf[0];
+ char* endptr = NULL;
+ u16 flag = 0;
+ int i, end = 16;
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ flag = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ if(flag & WSM_REG_RW_F) { //write
+ WSM_REG_W reg_w;
+ reg_w.flag = flag;
+ reg_w.data_size = 0;
+ if(flag&WSM_REG_BK_F) end = 2;
+
+ for(i=0; (i<end)&&((buf+buf_size-12)>endptr); i++) {
+ reg_w.arg[i].reg_addr = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ reg_w.arg[i].reg_val = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ }
+ if(i) reg_w.data_size = 4+i*8;
+ xradio_dbg(XRADIO_DBG_ALWY,"W:flag=0x%x, size=%d\n", reg_w.flag, reg_w.data_size);
+ wsm_write_mib(hw_priv, WSM_MIB_ID_RW_FW_REG, (void*)&reg_w, reg_w.data_size, 0);
+
+ } else { //read
+ WSM_REG_R reg_r;
+ reg_r.flag = flag;
+ reg_r.data_size = 0;
+ if(flag&WSM_REG_BK_F) end = 2;
+
+ for(i=0; (i<end)&&((buf+buf_size-6)>endptr); i++) {
+ reg_r.arg[i] = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ }
+ if(i) reg_r.data_size = 4+i*4;
+
+ wsm_read_mib(hw_priv, WSM_MIB_ID_RW_FW_REG, (void*)&reg_r,
+ sizeof(WSM_REG_R), reg_r.data_size);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"R:flag=0x%x, size=%d\n", reg_r.flag, reg_r.data_size);
+
+ end = (reg_r.data_size>>2)-1;
+ if(!end || !(reg_r.flag & WSM_REG_RET_F))
+ return count;
+
+ for(i=0; i<end; i++) {
+ xradio_dbg(XRADIO_DBG_ALWY,"0x%08x ", reg_r.arg[i]);
+ if((i&3) == 3) xradio_dbg(XRADIO_DBG_ALWY,"\n");
+ }
+ xradio_dbg(XRADIO_DBG_ALWY,"\n");
+ }
+ return count;
+}
+static const struct file_operations fops_rw_fwreg = {
+ .open = xradio_generic_open,
+ .write = xradio_fwreg_rw,
+ .llseek = default_llseek,
+};
+
+/* This ops only used in bh error occured already.
+ * It can be dangerous to use it in normal status. */
+static ssize_t xradio_fwreg_rw_direct(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //for H64 HIF test
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[256] = {0};
+ u16 buf_size = (count > 255 ? 255 : count);
+ char* startptr = &buf[0];
+ char* endptr = NULL;
+ u16 flag = 0;
+ int i, end = 16;
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ flag = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ if(flag & WSM_REG_RW_F) { //write
+ int ret = 0;
+ u32 val32 = 0;
+ WSM_REG_W reg_w;
+ reg_w.flag = flag;
+ reg_w.data_size = 0;
+ if(flag&WSM_REG_BK_F) end = 2;
+
+ for(i=0; (i<end)&&((buf+buf_size-12)>endptr); i++) {
+ reg_w.arg[i].reg_addr = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ reg_w.arg[i].reg_val = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ }
+
+ //change to direct mode
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W -- " \
+ "reading CONFIG err, ret is %d! \n", __func__, ret);
+ }
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 | HIF_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W -- " \
+ "setting direct mode err, ret is %d! \n", __func__, ret);
+ }
+
+ ret = xradio_ahb_write_32(hw_priv,reg_w.arg[0].reg_addr, reg_w.arg[0].reg_val);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:AHB write test, val of addr %x is %x! \n",
+ __func__, reg_w.arg[0].reg_addr, reg_w.arg[0].reg_val);
+ }
+
+ //return to queue mode
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 & ~HIF_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W -- " \
+ "setting queue mode err, ret is %d! \n", __func__,ret);
+ }
+ } else { //read
+ WSM_REG_R reg_r;
+ u32 val32 = 0;
+ u32 mem_val = 0;
+ int ret = 0;
+ reg_r.flag = flag;
+ reg_r.data_size = 0;
+ if(flag&WSM_REG_BK_F) end = 2;
+
+ for(i=0; (i<end)&&((buf+buf_size-6)>endptr); i++) {
+ reg_r.arg[i] = simple_strtoul(startptr, &endptr, 16);
+ startptr = endptr+1;
+ }
+ //if(i) reg_r.data_size = 4+i*4;
+ if (!(reg_r.arg[0] & 0xffff0000)) { // means read register
+ ret = xradio_reg_read_32(hw_priv, (u16)reg_r.arg[0], &val32);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W [register]-- " \
+ "reading CONFIG err, ret is %d! \n", __func__,ret);
+ }
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W [register]]-- " \
+ "reading register @0x%x,val is 0x%x\n",
+ __func__, reg_r.arg[0], val32);
+ } else { //means read memory
+
+ //change to direct mode
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W -- " \
+ "reading CONFIG err, ret is %d! \n", __func__,ret);
+ }
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 | HIF_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W -- " \
+ "setting direct mode err, ret is %d! \n", __func__,ret);
+ }
+
+ if (reg_r.arg[0] & 0x08000000) {
+ ret = xradio_ahb_read_32(hw_priv,reg_r.arg[0], &mem_val);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:AHB read test err, " \
+ "val of addr %08x is %08x \n", __func__,reg_r.arg[0],mem_val);
+ }
+ xradio_dbg(XRADIO_DBG_ALWY, "[%08x] = 0x%08x\n", reg_r.arg[0], mem_val);
+ } else if(reg_r.arg[0] & 0x09000000) {
+ ret = xradio_apb_read_32(hw_priv,reg_r.arg[0], &mem_val);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:APB read test err, " \
+ "val of addr %08x is %08x \n", __func__,reg_r.arg[0],mem_val);
+ }
+ xradio_dbg(XRADIO_DBG_ALWY, "[%08x] = 0x%08x\n", reg_r.arg[0], mem_val);
+ }
+
+ //return to queue mode
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 & ~HIF_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:test HIF R/W -- " \
+ "setting queue mode err, ret is %d! \n", __func__,ret);
+ }
+ }
+ }
+ return count;
+}
+static const struct file_operations fops_rw_fwreg_direct = {
+ .open = xradio_generic_open,
+ .write = xradio_fwreg_rw_direct,
+ .llseek = default_llseek,
+};
+//add by yangfh for setting ampdu_len
+u16 ampdu_len[2] = {16,16};
+static ssize_t xradio_ampdu_len_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[12] = {0};
+ char* endptr = NULL;
+ u8 if_id = 0;
+
+ count = (count > 11 ? 11 : count);
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ if_id = (simple_strtoul(buf, &endptr, 10) == 0);
+ ampdu_len[if_id] = simple_strtoul(endptr+1, NULL, 10);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"vif=%d, ampdu_len = %d\n", if_id, ampdu_len[if_id]);
+ wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AMPDU_NUM, &ampdu_len[if_id], sizeof(u16), if_id);
+
+ return count;
+}
+
+static ssize_t xradio_ampdu_len_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+
+ sprintf(buf, "ampdu_len(0)=%d, (1)=%d\n", ampdu_len[0], ampdu_len[1]);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static const struct file_operations fops_ampdu_len = {
+ .open = xradio_generic_open,
+ .write = xradio_ampdu_len_write,
+ .read = xradio_ampdu_len_read,
+ .llseek = default_llseek,
+};
+
+
+//add by yangfh for setting rts threshold.
+u32 rts_threshold[2] = {3000, 3000};
+static ssize_t xradio_rts_threshold_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+
+ sprintf(buf, "rts_threshold(0)=%d, (1)=%d\n", rts_threshold[0], rts_threshold[1]);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_rts_threshold_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[10] = {0};
+ char* endptr = NULL;
+ u8 if_id = 0;
+
+ count = (count > 11 ? 11 : count);
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ if_id = simple_strtoul(buf, &endptr, 10);
+ rts_threshold[if_id] = simple_strtoul(endptr+1, NULL, 10);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"vif=%d, rts_threshold = %d\n", if_id, rts_threshold[if_id]);
+ wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, &rts_threshold[if_id], sizeof(u32), if_id);
+
+ return count;
+}
+
+static const struct file_operations fops_rts_threshold = {
+ .open = xradio_generic_open,
+ .write = xradio_rts_threshold_set,
+ .read = xradio_rts_threshold_get,
+ .llseek = default_llseek,
+};
+
+//add by yangfh for disable low power mode.
+u8 low_pwr_disable = 0;
+static ssize_t xradio_low_pwr_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+
+ sprintf(buf, "low_pwr_disable=%d\n", low_pwr_disable);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_low_pwr_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[12] = {0};
+ char* endptr = NULL;
+ int if_id=0;
+ u32 val = wsm_power_mode_quiescent;
+
+ count = (count > 11 ? 11 : count);
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ low_pwr_disable = simple_strtoul(buf, &endptr, 16);
+ xradio_dbg(XRADIO_DBG_ALWY,"low_pwr_disable=%d\n", low_pwr_disable);
+
+ if(low_pwr_disable)
+ val = wsm_power_mode_active;
+
+ val |= BIT(4); //disableMoreFlagUsage
+
+ for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); if_id++)
+ wsm_write_mib(hw_priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+ sizeof(val), if_id);
+
+ return count;
+}
+
+static const struct file_operations fops_low_pwr = {
+ .open = xradio_generic_open,
+ .write = xradio_low_pwr_set,
+ .read = xradio_low_pwr_get,
+ .llseek = default_llseek,
+};
+
+//add by yangfh for disable ps mode(80211 protol).
+static ssize_t xradio_ps_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+
+ sprintf(buf, "ps_disable=%d, idleperiod=%d, changeperiod=%d\n",
+ ps_disable, ps_idleperiod, ps_changeperiod);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_ps_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[20] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ struct wsm_set_pm ps={
+ .pmMode = WSM_PSM_FAST_PS,
+ .fastPsmIdlePeriod = 0xC8 //defaut 100ms
+ };
+
+ count = (count > 19 ? 19 : count);
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ ps_disable = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ ps_idleperiod = simple_strtoul(start, &endptr, 10)&0xff;
+ start = endptr+1;
+ if(start < buf+count)
+ ps_changeperiod = simple_strtoul(start, &endptr, 10)&0xff;
+
+ xradio_dbg(XRADIO_DBG_ALWY,"ps_disable=%d, idleperiod=%d, changeperiod=%d\n",
+ ps_disable, ps_idleperiod, ps_changeperiod);
+
+ //set pm for debug
+ if (ps_disable)
+ ps.pmMode = WSM_PSM_ACTIVE;
+ if (ps_idleperiod)
+ ps.fastPsmIdlePeriod = ps_idleperiod << 1;
+ if (ps_changeperiod)
+ ps.apPsmChangePeriod = ps_changeperiod << 1;
+
+ wsm_set_pm(hw_priv, &ps, 0);
+ if (hw_priv->vif_list[1])
+ wsm_set_pm(hw_priv, &ps, 1);
+
+ return count;
+}
+
+static const struct file_operations fops_ps_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_ps_set,
+ .read = xradio_ps_get,
+ .llseek = default_llseek,
+};
+
+//add by yangfh for retry debug.
+u8 retry_dbg = 0;
+u8 tx_short=0; //save orgin value.
+u8 tx_long=0; //save orgin value.
+
+static ssize_t xradio_retry_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+
+ sprintf(buf, "retry_dbg=%d, short=%d, long=%d\n", retry_dbg,
+ hw_priv->short_frame_max_tx_count,
+ hw_priv->long_frame_max_tx_count);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_retry_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[20] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+
+ count = (count > 19 ? 19 : count);
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ retry_dbg = (simple_strtoul(start, &endptr, 10) & 0x1);
+ if (retry_dbg) { //change retry.
+ if (!tx_short)
+ tx_short = hw_priv->short_frame_max_tx_count;
+ if (!tx_long)
+ tx_long = hw_priv->long_frame_max_tx_count;
+ start = endptr+1;
+ if(start < buf+count) {
+ hw_priv->short_frame_max_tx_count = simple_strtoul(start, &endptr, 10)&0xf;
+ start = endptr+1;
+ if(start < buf+count)
+ hw_priv->long_frame_max_tx_count = simple_strtoul(start, &endptr, 10)&0xf;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY,"retry_dbg on, s=%d, l=%d\n",
+ hw_priv->short_frame_max_tx_count,
+ hw_priv->long_frame_max_tx_count);
+ } else { //restore retry.
+ if(tx_short) {
+ hw_priv->short_frame_max_tx_count = tx_short;
+ tx_short = 0;
+ }
+ if(tx_long) {
+ hw_priv->long_frame_max_tx_count = tx_long;
+ tx_long = 0;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY,"retry_dbg off, s=%d, l=%d\n",
+ hw_priv->short_frame_max_tx_count,
+ hw_priv->long_frame_max_tx_count);
+ }
+ retry_dbg |= 0x2;
+ return count;
+}
+
+static const struct file_operations fops_retry_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_retry_set,
+ .read = xradio_retry_get,
+ .llseek = default_llseek,
+};
+
+//add by yangfh for rates debug.
+u8 rates_dbg_en = 0;
+u32 rates_debug[3] = {0};
+u8 maxRate_dbg = 0;
+
+static ssize_t xradio_rates_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+ sprintf(buf, "rates_dbg_en=%d, [0]=0x%08x, [1]=0x%08x, [2]=0x%08x\n",
+ (rates_dbg_en & 0x1), rates_debug[2], rates_debug[1], rates_debug[0]);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_rates_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[50] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ int i = 0;
+ count = (count > 49 ? 49 : count);
+
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ rates_dbg_en &= ~0x1;
+ if(simple_strtoul(start, &endptr, 10)) {
+ start = endptr+1;
+ if(start < buf+count)
+ rates_debug[2] = simple_strtoul(start, &endptr, 16);
+
+ start = endptr+1;
+ if(start < buf+count)
+ rates_debug[1] = simple_strtoul(start, &endptr, 16);
+
+ start = endptr+1;
+ if(start < buf+count)
+ rates_debug[0] = simple_strtoul(start, &endptr, 16);
+
+ for (i = 21; i >= 0; i--) {
+ if ((rates_debug[i>>3]>>((i&0x7)<<2)) & 0xf) {
+ maxRate_dbg = i;
+ rates_dbg_en |= 0x1;
+ break;
+ }
+ }
+ if (rates_dbg_en & 0x1) {
+ xradio_dbg(XRADIO_DBG_ALWY,"rates_dbg on, maxrate=%d!\n", maxRate_dbg);
+ } else {
+ xradio_dbg(XRADIO_DBG_ALWY,"rates_dbg fail, invaid params!\n");
+ }
+ } else {
+ xradio_dbg(XRADIO_DBG_ALWY,"rates_dbg off\n");
+ }
+ return count;
+}
+
+static const struct file_operations fops_rates_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_rates_set,
+ .read = xradio_rates_get,
+ .llseek = default_llseek,
+};
+
+
+//add by huanglu for backoff setting.
+struct wsm_backoff_ctrl backoff_ctrl;
+
+static ssize_t xradio_backoff_ctrl_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+ sprintf(buf, "backoff_ctrl_en=%d, min=%d, max=%d\n",
+ backoff_ctrl.enable,
+ backoff_ctrl.min,
+ backoff_ctrl.max);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_backoff_ctrl_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[20] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ count = (count > 19 ? 19 : count);
+
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ backoff_ctrl.enable = simple_strtoul(start, &endptr, 10);
+ if (backoff_ctrl.enable) {
+ start = endptr+1;
+ if(start < buf+count)
+ backoff_ctrl.min = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ backoff_ctrl.max = simple_strtoul(start, &endptr, 10);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"backoff_ctrl on\n");
+ } else {
+ xradio_dbg(XRADIO_DBG_ALWY,"backoff_ctrl off\n");
+ }
+ wsm_set_backoff_ctrl(hw_priv, &backoff_ctrl);
+ return count;
+}
+
+static const struct file_operations fops_backoff_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_backoff_ctrl_set,
+ .read = xradio_backoff_ctrl_get,
+ .llseek = default_llseek,
+};
+
+//========
+//add by huanglu for TALA(Tx-Ampdu-Len-Adaption) setting.
+struct wsm_tala_para tala_para;
+static ssize_t xradio_tala_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+ sprintf(buf, "tala_para=0x%08x, tala_thresh=0x%08x\n",
+ tala_para.para, tala_para.thresh);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_tala_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[30] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ count = (count > 29 ? 29 : count);
+
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if(start < buf+count)
+ tala_para.para = simple_strtoul(start, &endptr, 16);
+ start = endptr+1;
+ if(start < buf+count)
+ tala_para.thresh = simple_strtoul(start, &endptr, 16);
+
+ wsm_set_tala(hw_priv, &tala_para);
+ return count;
+}
+
+static const struct file_operations fops_tala_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_tala_set,
+ .read = xradio_tala_get,
+ .llseek = default_llseek,
+};
+
+//Tx power debug, add by yangfh.
+char buf_show[1024] = {0};
+typedef struct _PWR_INFO_TBL{
+ u8 Index;
+ u8 u8Complete;
+ s16 s16TargetPwr;
+ s16 s16AdjustedPower;
+ s16 s16SmthErrTerm;
+ u32 u32Count;
+ u16 u16PpaVal;
+ u16 u16DigVal;
+} PWR_CTRL_TBL;
+struct _TX_PWR_SHOW {
+ u8 InfoID;
+ u8 Status;
+ u16 reserved;
+ PWR_CTRL_TBL table[16];
+} pwr_ctrl;
+static ssize_t xradio_tx_pwr_show(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+
+ int pos = 0, i = 0;
+ pwr_ctrl.InfoID = 0x1;
+ wsm_read_mib(hw_priv, WSM_MIB_ID_TX_POWER_INFO, (void*)&pwr_ctrl, sizeof(pwr_ctrl), 4);
+
+ if (pwr_ctrl.Status) {
+ pos += sprintf(&buf_show[pos], "read TX_POWER_INFO error=%x\n", pwr_ctrl.Status);
+ } else {
+ for (i=0; i<16; i++) {
+ pos += sprintf(&buf_show[pos], "M%d:%d, ALG=%d, DIG=%d\n", i,
+ pwr_ctrl.table[i].s16AdjustedPower,
+ pwr_ctrl.table[i].u16PpaVal, pwr_ctrl.table[i].u16DigVal);
+ }
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buf_show, pos);
+}
+
+static ssize_t xradio_tx_pwr_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ return count;
+}
+
+static const struct file_operations fops_tx_pwr_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_tx_pwr_set,
+ .read = xradio_tx_pwr_show,
+ .llseek = default_llseek,
+};
+
+//TPA debug, add by yangfh.2015-4-24 21:24:28
+#define CASE_NUM 9
+#define MAX_POINTS 4
+#define PWR_LEVEL_NUM 40
+#define MODULN_NUM 11
+typedef struct tag_pwr_modulation {
+ u8 def_pwr_idx; //default power index of modulation.
+ u8 max_pwr_idx; //max power index of modulation.
+ u8 mid_pwr_idx; //power index of middle point.
+ u8 cur_point; //current sample point.
+
+ u8 max_point; //the point has max q value.
+ u8 max_stable; //counter of stable max of the same point.
+ u8 exception; //the counter of exception case.
+ u8 listen_def; //whether to listen to default point.
+
+ u16 mod_smp_cnt; //total sample of the modulation.
+ u16 update_cnt; //counter of power update.
+ u32 update_time; //last time of power update.
+ u16 smp_points[MAX_POINTS*2];
+
+ u8 reserved;
+ u8 last_rate;
+ u16 last_max_Q;
+} PWR_MODULN;
+
+typedef struct tag_tpa_debug {
+ u32 update_total[MODULN_NUM];
+ u32 power_sum[MODULN_NUM];
+
+ u16 smp_case[CASE_NUM]; //counter of every case.
+ u16 reserved0;
+ u16 smp_move_cnt[MAX_POINTS]; //counter of movement of update power.
+ u16 max_point_cnt[MAX_POINTS]; //counter of max point.
+
+ u16 smp_thresh_q_cnt;
+ u16 smp_timeout;
+ u16 smp_listdef_cnt;
+ u16 smp_excep_cnt;
+ u16 smp_stable_cnt;
+
+ u8 reserved2;
+ u8 smp_last_moduln;
+ u16 point_last_smp[MAX_POINTS*2]; //Q value of point last update.
+} TPA_DEBUG_INFO;
+
+typedef struct tag_tpa_control {
+ u8 tpa_enable;
+ u8 tpa_initialized;
+ u8 point_interval;
+ u8 point_step;
+
+ u16 thresh_q;
+ u16 thresh_time;
+ u16 thresh_update;
+ u8 thresh_def_lstn;
+ u8 thresh_stable;
+ u8 pwr_level[PWR_LEVEL_NUM];
+} TPA_CONTROL;
+
+struct _TPA_INFO {
+ u8 InfoID;
+ u8 Status;
+ u8 node;
+ u8 reserved;
+ union {
+ TPA_DEBUG_INFO debug;
+ TPA_CONTROL ctrl;
+ PWR_MODULN moduln[MODULN_NUM];
+ } u;
+} tpa_info;
+static ssize_t xradio_tpa_ctrl_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ int pos = 0, i = 0;
+ memset(&tpa_info, 0, sizeof(tpa_info));
+ tpa_info.InfoID = 0x03;
+ tpa_info.node = 0;
+ wsm_read_mib(hw_priv, WSM_MIB_ID_TPA_DEBUG_INFO,
+ (void*)&tpa_info, sizeof(tpa_info), 4);
+
+ if (tpa_info.Status && tpa_info.InfoID != 0x43) {
+ pos += sprintf(&buf_show[pos], "read TPA_DEBUG_INFO error=%x\n", tpa_info.Status);
+ } else {
+ u8 *pwr = &tpa_info.u.ctrl.pwr_level[0];
+
+ pos += sprintf(&buf_show[pos], "en=%d,init=%d,intvl=%d,step=%d\n",
+ tpa_info.u.ctrl.tpa_enable, tpa_info.u.ctrl.tpa_initialized,
+ tpa_info.u.ctrl.point_interval, tpa_info.u.ctrl.point_step);
+ pos += sprintf(&buf_show[pos], "th_q=%d,th_tm=%d,th_updt=%d," \
+ "th_def_lstn=%d, th_stbl=%d\n", tpa_info.u.ctrl.thresh_q,
+ tpa_info.u.ctrl.thresh_time, tpa_info.u.ctrl.thresh_update,
+ tpa_info.u.ctrl.thresh_def_lstn, tpa_info.u.ctrl.thresh_stable);
+ for (i=0; i<4; i++) {
+ pos += sprintf(&buf_show[pos], "pwr lvl=%d.%d, %d.%d, %d.%d, %d.%d\n",
+ pwr[0]>>3, ((pwr[0]%8)*100)>>3,
+ pwr[1]>>3, ((pwr[1]%8)*100)>>3,
+ pwr[2]>>3, ((pwr[2]%8)*100)>>3,
+ pwr[3]>>3, ((pwr[3]%8)*100)>>3);
+ pwr += 4;
+ }
+ }
+ return simple_read_from_buffer(user_buf, count, ppos, buf_show, pos);
+}
+
+struct TPA_CONTROL_SET {
+ u8 tpa_enable;
+ u8 reserved;
+ u8 point_interval;
+ u8 point_step;
+
+ u16 thresh_q;
+ u16 thresh_time;
+ u16 thresh_update;
+ u8 thresh_def_lstn;
+ u8 thresh_stable;
+} tpa_ctrl_set;
+static ssize_t xradio_tpa_ctrl_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buffer[256] = {0};
+ char *buf = &buffer[0];
+ u16 buf_size = (count > 255 ? 255 : count);
+ char* startptr = &buffer[0];
+ char* endptr = NULL;
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ //////////////
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.tpa_enable = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.point_interval = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.point_step = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ //////////////
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.thresh_q = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.thresh_time = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.thresh_update = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.thresh_def_lstn = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ if ((buf+buf_size)>endptr) {
+ tpa_ctrl_set.thresh_stable = simple_strtoul(startptr, &endptr, 10);
+ startptr = endptr+1;
+ }
+ wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TPA_PARAM,
+ (void*)&tpa_ctrl_set, sizeof(tpa_ctrl_set), 0);
+
+ return count;
+}
+static const struct file_operations fops_tpa_ctrl = {
+ .open = xradio_generic_open,
+ .write = xradio_tpa_ctrl_set,
+ .read = xradio_tpa_ctrl_get,
+ .llseek = default_llseek,
+};
+
+u8 tpa_node_dbg = 0;
+static int xradio_tpa_debug(struct seq_file *seq, void *v)
+{
+ int ret, i;
+ struct xradio_common *hw_priv = seq->private;
+
+#define PUT_TPA_MODULN(tab, name) \
+ seq_printf(seq, tab":\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", \
+ __le32_to_cpu(tpa_info.u.moduln[0].name), \
+ __le32_to_cpu(tpa_info.u.moduln[1].name), \
+ __le32_to_cpu(tpa_info.u.moduln[2].name), \
+ __le32_to_cpu(tpa_info.u.moduln[3].name), \
+ __le32_to_cpu(tpa_info.u.moduln[4].name), \
+ __le32_to_cpu(tpa_info.u.moduln[5].name), \
+ __le32_to_cpu(tpa_info.u.moduln[6].name), \
+ __le32_to_cpu(tpa_info.u.moduln[7].name), \
+ __le32_to_cpu(tpa_info.u.moduln[8].name), \
+ __le32_to_cpu(tpa_info.u.moduln[9].name), \
+ __le32_to_cpu(tpa_info.u.moduln[10].name))
+
+
+
+ tpa_info.InfoID = 0x01;
+ tpa_info.node = tpa_node_dbg;
+ ret = wsm_read_mib(hw_priv, WSM_MIB_ID_TPA_DEBUG_INFO,
+ (void*)&tpa_info, sizeof(tpa_info), 4);
+
+ if (tpa_info.Status && tpa_info.InfoID != 0x41) {
+ seq_printf(seq, "read TPA_DEBUG_INFO error=%x\n", tpa_info.Status);
+ } else {
+ seq_printf(seq, "\t\tm0\tm1\tm2\tm3\tm4\tm5\tm6\tm7\tm8\tm9\tm10\t\n");
+
+ PUT_TPA_MODULN("max_idx", max_pwr_idx );
+ PUT_TPA_MODULN("def_idx", def_pwr_idx );
+ PUT_TPA_MODULN("mid_idx", mid_pwr_idx );
+ PUT_TPA_MODULN("cur_pt ", cur_point );
+ PUT_TPA_MODULN("max_pt ", max_point );
+ PUT_TPA_MODULN("stable ", max_stable );
+ PUT_TPA_MODULN("exceptn", exception );
+ PUT_TPA_MODULN("listen ", listen_def );
+ PUT_TPA_MODULN("smp_cnt", mod_smp_cnt );
+ PUT_TPA_MODULN("update ", update_cnt );
+
+
+ PUT_TPA_MODULN("pt[0] ", smp_points[0]);
+ PUT_TPA_MODULN("pt[0] ", smp_points[1]);
+ PUT_TPA_MODULN("pt[1] ", smp_points[2]);
+ PUT_TPA_MODULN("pt[1] ", smp_points[3]);
+ PUT_TPA_MODULN("pt[2] ", smp_points[4]);
+ PUT_TPA_MODULN("pt[2] ", smp_points[5]);
+ PUT_TPA_MODULN("pt[3] ", smp_points[6]);
+ PUT_TPA_MODULN("pt[3] ", smp_points[7]);
+
+ PUT_TPA_MODULN("rate ", last_rate);
+ PUT_TPA_MODULN("Max Q ", last_max_Q);
+ }
+#undef PUT_TPA_MODULN
+
+
+#define SMP_CASE(i) __le32_to_cpu(tpa_info.u.debug.smp_case[i])
+#define PWR_LVL_S(n) (tpa_info.u.debug.power_sum[n]>>3)
+
+
+ tpa_info.InfoID = 0x02;
+ tpa_info.node = tpa_node_dbg;
+ ret = wsm_read_mib(hw_priv, WSM_MIB_ID_TPA_DEBUG_INFO,
+ (void*)&tpa_info, sizeof(tpa_info), 4);
+
+ if (tpa_info.Status && tpa_info.InfoID != 0x42) {
+ seq_printf(seq, "read TPA_DEBUG_INFO error=%x\n", tpa_info.Status);
+ } else {
+ for (i=0; i<MODULN_NUM;i++) {
+ if (tpa_info.u.debug.update_total[i])
+ tpa_info.u.debug.power_sum[i] /= tpa_info.u.debug.update_total[i];
+ else
+ tpa_info.u.debug.power_sum[i] = 0;
+ }
+ seq_printf(seq, "\nupdate_total:\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", \
+ __le32_to_cpu(tpa_info.u.debug.update_total[0]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[1]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[2]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[3]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[4]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[5]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[6]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[7]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[8]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[9]), \
+ __le32_to_cpu(tpa_info.u.debug.update_total[10]));
+
+ seq_printf(seq, "pwr_avrg:\t%d\t%d\t%d\t%d\t%d\t%d\t"
+ "%d\t%d\t%d\t%d\t%d\n", \
+ PWR_LVL_S(0), \
+ PWR_LVL_S(1), \
+ PWR_LVL_S(2), \
+ PWR_LVL_S(3), \
+ PWR_LVL_S(4), \
+ PWR_LVL_S(5), \
+ PWR_LVL_S(6), \
+ PWR_LVL_S(7), \
+ PWR_LVL_S(8), \
+ PWR_LVL_S(9), \
+ PWR_LVL_S(10) );
+
+ seq_printf(seq, "SMP_CASE: %d, %d, %d, %d(E), %d, %d, %d(E), %d(E), %d\n",
+ SMP_CASE(0), SMP_CASE(1), SMP_CASE(2), SMP_CASE(3),
+ SMP_CASE(4), SMP_CASE(5), SMP_CASE(6), SMP_CASE(7),SMP_CASE(8));
+ seq_printf(seq, "MAX: M=%d, L=%d, R=%d, D=%d\n",
+ tpa_info.u.debug.max_point_cnt[0], tpa_info.u.debug.max_point_cnt[1],
+ tpa_info.u.debug.max_point_cnt[2], tpa_info.u.debug.max_point_cnt[3]);
+ seq_printf(seq, "MOVE: M=%d, L=%d, R=%d, D=%d\n",
+ tpa_info.u.debug.smp_move_cnt[0], tpa_info.u.debug.smp_move_cnt[1],
+ tpa_info.u.debug.smp_move_cnt[2], tpa_info.u.debug.smp_move_cnt[3]);
+ seq_printf(seq, "listen=%d, timeout=%d, thresh_q=%d,excep=%d, stable=%d\n",
+ tpa_info.u.debug.smp_listdef_cnt, tpa_info.u.debug.smp_timeout,
+ tpa_info.u.debug.smp_thresh_q_cnt, tpa_info.u.debug.smp_excep_cnt,
+ tpa_info.u.debug.smp_stable_cnt);
+
+ seq_printf(seq, "lsat Moduln=%d, M=%d,%d; L=%d,%d; R=%d,%d; D=%d,%d\n",
+ tpa_info.u.debug.smp_last_moduln,
+ tpa_info.u.debug.point_last_smp[0], tpa_info.u.debug.point_last_smp[1],
+ tpa_info.u.debug.point_last_smp[2], tpa_info.u.debug.point_last_smp[3],
+ tpa_info.u.debug.point_last_smp[4], tpa_info.u.debug.point_last_smp[5],
+ tpa_info.u.debug.point_last_smp[6], tpa_info.u.debug.point_last_smp[7]);
+ }
+
+#undef PWR_LVL_S
+#undef SMP_CASE
+
+ return 0;
+}
+
+static int xradio_tpa_debug_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_tpa_debug,
+ inode->i_private);
+}
+
+static const struct file_operations fops_tpa_debug = {
+ .open = xradio_tpa_debug_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+//policy_info
+u32 tx_retrylimit = 0;
+u32 tx_lower_limit = 0;
+u32 tx_over_limit = 0;
+int retry_mis = 0;
+u32 policy_upload = 0;
+u32 policy_num = 0;
+
+static ssize_t xradio_policy_info(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[256];
+ size_t size = 0;
+ sprintf(buf, "tx_retrylimit=%d, tx_lower_limit=%d, tx_over_limit=%d, retry_mis=%d\n" \
+ "policy_upload=%d, policy_num=%d\n",
+ tx_retrylimit, tx_lower_limit, tx_over_limit, retry_mis,
+ policy_upload, policy_num);
+ size = strlen(buf);
+
+ //clear counters
+ tx_retrylimit = 0;
+ tx_lower_limit = 0;
+ tx_over_limit = 0;
+ retry_mis = 0;
+ policy_upload = 0;
+ policy_num = 0;
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static const struct file_operations fops_policy_info = {
+ .open = xradio_generic_open,
+ .read = xradio_policy_info,
+ .llseek = default_llseek,
+};
+
+//info of interruption
+u32 irq_count = 0;
+u32 int_miss_cnt = 0;
+u32 fix_miss_cnt = 0;
+u32 next_rx_cnt = 0;
+u32 rx_total_cnt = 0;
+u32 tx_total_cnt = 0;
+
+static ssize_t xradio_bh_statistic(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[256];
+ size_t size = 0;
+ sprintf(buf, "irq_count=%d, rx_total=%d, miss=%d, fix=%d, next=%d, "
+ "tx_total=%d\n",
+ irq_count, rx_total_cnt, int_miss_cnt, fix_miss_cnt, next_rx_cnt,
+ tx_total_cnt);
+ size = strlen(buf);
+
+ //clear counters
+ irq_count = 0;
+ int_miss_cnt = 0;
+ fix_miss_cnt = 0;
+ next_rx_cnt = 0;
+ rx_total_cnt = 0;
+ tx_total_cnt = 0;
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static const struct file_operations fops_bh_stat = {
+ .open = xradio_generic_open,
+ .read = xradio_bh_statistic,
+ .llseek = default_llseek,
+};
+
+//add by yangfh for disable low power mode.
+extern u16 txparse_flags;
+extern u16 rxparse_flags;
+static ssize_t xradio_parse_flags_get(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+
+ sprintf(buf, "txparse=0x%04x, rxparse=0x%04x\n", txparse_flags, rxparse_flags);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_parse_flags_set(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[30] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+
+ count = (count > 29 ? 29 : count);
+
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ txparse_flags = simple_strtoul(buf, &endptr, 16);
+ start = endptr+1;
+ if(start < buf+count)
+ rxparse_flags = simple_strtoul(start, &endptr, 16);
+
+ txparse_flags &= 0x7fff;
+ rxparse_flags &= 0x7fff;
+
+ xradio_dbg(XRADIO_DBG_ALWY, "txparse=0x%04x, rxparse=0x%04x\n",
+ txparse_flags, rxparse_flags);
+ return count;
+}
+
+static const struct file_operations fops_parse_flags = {
+ .open = xradio_generic_open,
+ .write = xradio_parse_flags_set,
+ .read = xradio_parse_flags_get,
+ .llseek = default_llseek,
+};
+
+#if defined(DGB_XRADIO_HWT)
+extern u8 hwt_testing = 0;
+//HIF TX test
+u8 hwt_tx_en = 0;
+u8 hwt_tx_cfm = 0; //confirm interval
+u16 hwt_tx_len = 0; //
+u16 hwt_tx_num = 0; //
+struct timeval hwt_start_time = {0};
+struct timeval hwt_end_time = {0};
+int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg,
+ size_t arg_size);
+
+static ssize_t xradio_hwt_hif_tx(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[100] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ u8 test_en = 0;
+
+ if (hwt_testing) {
+ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n");
+ return count;
+ }
+
+ count = (count > 99 ? 99 : count);
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if(simple_strtoul(start, &endptr, 10)) {
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_tx_len = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_tx_num = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_tx_cfm = simple_strtoul(start, &endptr, 10);
+ hwt_tx_en = 1;
+ hwt_testing = 1;
+ } else {
+ hwt_tx_en = 0;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY,"hwt_tx_en=%d, hwt_tx_len=%d, hwt_tx_num=%d, hwt_tx_cfm=%d\n",
+ hwt_tx_en, hwt_tx_len, hwt_tx_num, hwt_tx_cfm);
+
+ if (!hw_priv->bh_error &&
+ atomic_add_return(1, &hw_priv->bh_tx) == 1)
+ wake_up(&hw_priv->bh_wq);
+ return count;
+}
+static const struct file_operations fops_hwt_hif_tx = {
+ .open = xradio_generic_open,
+ .write = xradio_hwt_hif_tx,
+ .llseek = default_llseek,
+};
+
+//HIF RX test
+u8 hwt_rx_en = 0;
+u16 hwt_rx_len = 0; //
+u16 hwt_rx_num = 0; //
+static ssize_t xradio_hwt_hif_rx(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[100] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ u8 test_en = 0;
+ struct wsm_buf *wsm_buf = &hw_priv->wsm_cmd_buf;
+
+ if (hwt_testing) {
+ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n");
+ return count;
+ }
+
+ count = (count > 99 ? 99 : count);
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if(simple_strtoul(start, &endptr, 10)) {
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_rx_len = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_rx_num = simple_strtoul(start, &endptr, 10);
+
+ hwt_rx_en = 1;
+ } else {
+ hwt_rx_en = 0;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY,"hwt_rx_en=%d, hwt_rx_len=%d, hwt_rx_num=%d\n",
+ hwt_rx_en, hwt_rx_len, hwt_rx_num);
+
+ //check the parameters.
+ if (hwt_rx_len < 100 || hwt_rx_len > 1500)
+ hwt_rx_len = 1500;
+ if (hwt_rx_en && hwt_rx_num) {
+ HWT_PARAMETERS hwt_hdr = {
+ .TestID = 0x0002,
+ .Params = hwt_rx_num,
+ .Data = hwt_rx_len
+ };
+ hwt_testing = 1;
+ wsm_hwt_cmd(hw_priv, (void *)&hwt_hdr.TestID, sizeof(hwt_hdr)-4);
+ do_gettimeofday(&hwt_start_time);
+ }
+
+ return count;
+}
+static const struct file_operations fops_hwt_hif_rx = {
+ .open = xradio_generic_open,
+ .write = xradio_hwt_hif_rx,
+ .llseek = default_llseek,
+};
+
+//ENC test
+u8 hwt_enc_type = 0;
+u8 hwt_key_len = 0; //
+u16 hwt_enc_len = 0; //
+u16 hwt_enc_cnt = 0; //
+static ssize_t xradio_hwt_enc(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[100] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ u8 test_en = 0;
+ struct wsm_buf *wsm_buf = &hw_priv->wsm_cmd_buf;
+
+ if (hwt_testing) {
+ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n");
+ return count;
+ }
+
+ count = (count > 99 ? 99 : count);
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ hwt_enc_type = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_key_len = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_enc_len = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_enc_cnt = simple_strtoul(start, &endptr, 10);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"enc_type=%d, key_len=%d, enc_len=%d, enc_cnt=%d\n",
+ hwt_enc_type, hwt_key_len, hwt_enc_len, hwt_enc_cnt);
+
+ //check the parameters.
+ if (hwt_enc_type < 10 && hwt_key_len <= 16 ||
+ hwt_enc_len <= 1500 || hwt_enc_cnt > 0) {
+ HWT_PARAMETERS hwt_hdr = {
+ .TestID = 0x0003,
+ .Params = (hwt_key_len<<8) | hwt_enc_type,
+ .Datalen = hwt_enc_len,
+ .Data = hwt_enc_cnt
+ };
+ hwt_testing = 1;
+ wsm_hwt_cmd(hw_priv, (void *)&hwt_hdr.TestID, sizeof(hwt_hdr)-4);
+ }
+
+ return count;
+}
+static const struct file_operations fops_hwt_enc = {
+ .open = xradio_generic_open,
+ .write = xradio_hwt_enc,
+ .llseek = default_llseek,
+};
+
+//MIC test
+u16 hwt_mic_len = 0; //
+u16 hwt_mic_cnt = 0; //
+static ssize_t xradio_hwt_mic(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[100] = {0};
+ char* start = &buf[0];
+ char* endptr = NULL;
+ u8 test_en = 0;
+ struct wsm_buf *wsm_buf = &hw_priv->wsm_cmd_buf;
+
+ if (hwt_testing) {
+ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n");
+ return count;
+ }
+
+ count = (count > 99 ? 99 : count);
+ if(!count)
+ return -EINVAL;
+ if(copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ hwt_mic_len = simple_strtoul(start, &endptr, 10);
+ start = endptr+1;
+ if(start < buf+count)
+ hwt_mic_cnt = simple_strtoul(start, &endptr, 10);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"mic_len=%d, mic_cnt=%d\n", hwt_mic_len, hwt_mic_cnt);
+
+ //check the parameters.
+ if (hwt_mic_len <= 1500 || hwt_mic_cnt > 0) {
+ HWT_PARAMETERS hwt_hdr = {
+ .TestID = 0x0004,
+ .Params = 0,
+ .Datalen = hwt_mic_len,
+ .Data = hwt_mic_cnt
+ };
+ hwt_testing = 1;
+ wsm_hwt_cmd(hw_priv, (void *)&hwt_hdr.TestID, sizeof(hwt_hdr)-4);
+ }
+
+ return count;
+}
+static const struct file_operations fops_hwt_mic = {
+ .open = xradio_generic_open,
+ .write = xradio_hwt_mic,
+ .llseek = default_llseek,
+};
+#endif //DGB_XRADIO_HWT
+
+static u32 measure_type;
+
+static ssize_t xradio_measure_type_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[12] = {0};
+ char* endptr = NULL;
+ count = (count > 11 ? 11 : count);
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+ measure_type = simple_strtoul(buf, &endptr, 16);
+
+ xradio_dbg(XRADIO_DBG_ALWY,"measure_type = %08x\n", measure_type);
+ SYS_WARN(wsm_11k_measure_requset(hw_priv, (measure_type&0xff),
+ ((measure_type&0xff00)>>8),
+ ((measure_type&0xffff0000)>>16)));
+ return count;
+}
+
+static ssize_t xradio_measure_type_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ //struct xradio_common *hw_priv = file->private_data;
+ char buf[20];
+ size_t size = 0;
+
+ sprintf(buf, "measure_type = %u\n", measure_type);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+static const struct file_operations fops_11k = {
+ .open = xradio_generic_open,
+ .write = xradio_measure_type_write,
+ .read = xradio_measure_type_read,
+ .llseek = default_llseek,
+};
+
+
+static ssize_t xradio_wsm_dumps(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[1];
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, 1))
+ return -EFAULT;
+
+ if (buf[0] == '1')
+ hw_priv->wsm_enable_wsm_dumps = 1;
+ else
+ hw_priv->wsm_enable_wsm_dumps = 0;
+
+ return count;
+}
+
+static const struct file_operations fops_wsm_dumps = {
+ .open = xradio_generic_open,
+ .write = xradio_wsm_dumps,
+ .llseek = default_llseek,
+};
+
+static ssize_t xradio_short_dump_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *hw_priv = file->private_data;
+ char buf[20];
+ size_t size = 0;
+
+ sprintf(buf, "Size: %u\n", hw_priv->wsm_dump_max_size);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_short_dump_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *priv = file->private_data;
+ char buf[20];
+ unsigned long dump_size = 0;
+
+ if (!count || count > 20)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ if (kstrtoul(buf, 10, &dump_size))
+ return -EINVAL;
+ xradio_dbg(XRADIO_DBG_ALWY,"%s get %lu\n", __func__, dump_size);
+
+ priv->wsm_dump_max_size = dump_size;
+
+ return count;
+}
+
+static const struct file_operations fops_short_dump = {
+ .open = xradio_generic_open,
+ .write = xradio_short_dump_write,
+ .read = xradio_short_dump_read,
+ .llseek = default_llseek,
+};
+
+
+static int xradio_status_show_priv(struct seq_file *seq, void *v)
+{
+ int i;
+ struct xradio_vif *priv = seq->private;
+ struct xradio_debug_priv *d = priv->debug;
+
+ seq_printf(seq, "Mode: %s%s\n",
+ xradio_debug_mode(priv->mode),
+ priv->listening ? " (listening)" : "");
+ seq_printf(seq, "Assoc: %s\n",
+ xradio_debug_join_status[priv->join_status]);
+ if (priv->rx_filter.promiscuous)
+ seq_puts(seq, "Filter: promisc\n");
+ else if (priv->rx_filter.fcs)
+ seq_puts(seq, "Filter: fcs\n");
+ if (priv->rx_filter.bssid)
+ seq_puts(seq, "Filter: bssid\n");
+ if (priv->bf_control.bcn_count)
+ seq_puts(seq, "Filter: beacons\n");
+
+ if (priv->enable_beacon ||
+ priv->mode == NL80211_IFTYPE_AP ||
+ priv->mode == NL80211_IFTYPE_ADHOC ||
+ priv->mode == NL80211_IFTYPE_MESH_POINT ||
+ priv->mode == NL80211_IFTYPE_P2P_GO)
+ seq_printf(seq, "Beaconing: %s\n",
+ priv->enable_beacon ?
+ "enabled" : "disabled");
+ if (priv->ssid_length ||
+ priv->mode == NL80211_IFTYPE_AP ||
+ priv->mode == NL80211_IFTYPE_ADHOC ||
+ priv->mode == NL80211_IFTYPE_MESH_POINT ||
+ priv->mode == NL80211_IFTYPE_P2P_GO)
+ seq_printf(seq, "SSID: %.*s\n",
+ priv->ssid_length, priv->ssid);
+
+ for (i = 0; i < 4; ++i) {
+ seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i,
+ priv->edca.params[i].cwMin,
+ priv->edca.params[i].cwMax,
+ priv->edca.params[i].aifns,
+ priv->edca.params[i].txOpLimit,
+ priv->edca.params[i].maxReceiveLifetime);
+ }
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ static const char *pmMode = "unknown";
+ switch (priv->powersave_mode.pmMode) {
+ case WSM_PSM_ACTIVE:
+ pmMode = "off";
+ break;
+ case WSM_PSM_PS:
+ pmMode = "on";
+ break;
+ case WSM_PSM_FAST_PS:
+ pmMode = "dynamic";
+ break;
+ }
+ seq_printf(seq, "Preamble: %s\n",
+ xradio_debug_preamble[
+ priv->association_mode.preambleType]);
+ seq_printf(seq, "AMPDU spcn: %d\n",
+ priv->association_mode.mpduStartSpacing);
+ seq_printf(seq, "Basic rate: 0x%.8X\n",
+ le32_to_cpu(priv->association_mode.basicRateSet));
+ seq_printf(seq, "Bss lost: %d beacons\n",
+ priv->bss_params.beaconLostCount);
+ seq_printf(seq, "AID: %d\n",
+ priv->bss_params.aid);
+ seq_printf(seq, "Rates: 0x%.8X\n",
+ priv->bss_params.operationalRateSet);
+ seq_printf(seq, "Powersave: %s\n", pmMode);
+ }
+ seq_printf(seq, "RSSI thold: %d\n",
+ priv->cqm_rssi_thold);
+ seq_printf(seq, "RSSI hyst: %d\n",
+ priv->cqm_rssi_hyst);
+ seq_printf(seq, "TXFL thold: %d\n",
+ priv->cqm_tx_failure_thold);
+ seq_printf(seq, "Linkloss: %d\n",
+ priv->cqm_link_loss_count);
+ seq_printf(seq, "Bcnloss: %d\n",
+ priv->cqm_beacon_loss_count);
+
+ xradio_debug_print_map(seq, priv, "Link map: ",
+ priv->link_id_map);
+ xradio_debug_print_map(seq, priv, "Asleep map: ",
+ priv->sta_asleep_mask);
+ xradio_debug_print_map(seq, priv, "PSPOLL map: ",
+ priv->pspoll_mask);
+
+ seq_puts(seq, "\n");
+
+ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) {
+ if (priv->link_id_db[i].status) {
+ seq_printf(seq, "Link %d: %s, %pM\n",
+ i + 1, xradio_debug_link_id[
+ priv->link_id_db[i].status],
+ priv->link_id_db[i].mac);
+ }
+ }
+
+ seq_puts(seq, "\n");
+
+ seq_printf(seq, "Powermgmt: %s\n",
+ priv->powersave_enabled ? "on" : "off");
+
+ seq_printf(seq, "TXed: %d\n",
+ d->tx);
+ seq_printf(seq, "AGG TXed: %d\n",
+ d->tx_agg);
+ seq_printf(seq, "MULTI TXed: %d (%d)\n",
+ d->tx_multi, d->tx_multi_frames);
+ seq_printf(seq, "RXed: %d\n",
+ d->rx);
+ seq_printf(seq, "AGG RXed: %d\n",
+ d->rx_agg);
+ seq_printf(seq, "TX align: %d\n",
+ d->tx_align);
+ seq_printf(seq, "TX TTL: %d\n",
+ d->tx_ttl);
+ return 0;
+}
+
+static int xradio_status_open_priv(struct inode *inode, struct file *file)
+{
+ return single_open(file, &xradio_status_show_priv,
+ inode->i_private);
+}
+
+static const struct file_operations fops_status_priv = {
+ .open = xradio_status_open_priv,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .owner = THIS_MODULE,
+};
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+
+static ssize_t xradio_hang_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_vif *priv = file->private_data;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ char buf[1];
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, 1))
+ return -EFAULT;
+
+ if (priv->vif) {
+ xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
+ //ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
+ } else
+ return -ENODEV;
+
+ return count;
+}
+
+static const struct file_operations fops_hang = {
+ .open = xradio_generic_open,
+ .write = xradio_hang_write,
+ .llseek = default_llseek,
+};
+#endif
+
+#ifdef AP_HT_COMPAT_FIX
+extern u8 ap_compat_bssid[ETH_ALEN];
+static ssize_t xradio_ht_compat_show(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_vif *priv = file->private_data;
+ char buf[100];
+ size_t size = 0;
+ sprintf(buf, "ht_compat_det=0x%x, BSSID=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ priv->ht_compat_det,
+ ap_compat_bssid[0], ap_compat_bssid[1],
+ ap_compat_bssid[2], ap_compat_bssid[3],
+ ap_compat_bssid[4], ap_compat_bssid[5]);
+ size = strlen(buf);
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, size);
+}
+
+static ssize_t xradio_ht_compat_disalbe(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_vif *priv = file->private_data;
+ char buf[2];
+ char *endptr = NULL;
+
+ if (!count)
+ return -EINVAL;
+ if (copy_from_user(buf, user_buf, 2))
+ return -EFAULT;
+
+ if (simple_strtoul(buf, &endptr, 10))
+ priv->ht_compat_det |= 0x10;
+ else
+ priv->ht_compat_det &= ~0x10;
+ return count;
+}
+
+static const struct file_operations fops_ht_compat_dis = {
+ .open = xradio_generic_open,
+ .read = xradio_ht_compat_show,
+ .write = xradio_ht_compat_disalbe,
+ .llseek = default_llseek,
+};
+#endif
+
+#define VIF_DEBUGFS_NAME_S 10
+int xradio_debug_init_priv(struct xradio_common *hw_priv,
+ struct xradio_vif *priv)
+{
+ int ret = -ENOMEM;
+ struct xradio_debug_priv *d;
+ char name[VIF_DEBUGFS_NAME_S];
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if (SYS_WARN(!hw_priv))
+ return ret;
+
+ if (SYS_WARN(!hw_priv->debug))
+ return ret;
+
+ d = kzalloc(sizeof(struct xradio_debug_priv), GFP_KERNEL);
+ priv->debug = d;
+ if (SYS_WARN(!d))
+ return ret;
+
+ memset(name, 0, VIF_DEBUGFS_NAME_S);
+ ret = snprintf(name, VIF_DEBUGFS_NAME_S, "vif_%d", priv->if_id);
+ if (SYS_WARN(ret < 0))
+ goto err;
+
+ d->debugfs_phy = debugfs_create_dir(name,
+ hw_priv->debug->debugfs_phy);
+ if (SYS_WARN(!d->debugfs_phy))
+ goto err;
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ if (SYS_WARN(!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy,
+ priv, &fops_hang)))
+ goto err;
+#endif
+
+#if defined(AP_HT_COMPAT_FIX)
+ if (SYS_WARN(!debugfs_create_file("htcompat_disable", S_IWUSR, d->debugfs_phy,
+ priv, &fops_ht_compat_dis)))
+ goto err;
+#endif
+
+ if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+ priv, &fops_status_priv))
+ goto err;
+
+ return 0;
+err:
+ priv->debug = NULL;
+ debugfs_remove_recursive(d->debugfs_phy);
+ kfree(d);
+ return ret;
+
+}
+
+void xradio_debug_release_priv(struct xradio_vif *priv)
+{
+ struct xradio_debug_priv *d = priv->debug;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ if (d) {
+ priv->debug = NULL;
+ debugfs_remove_recursive(d->debugfs_phy);
+ kfree(d);
+ }
+}
+
+int xradio_print_fw_version(struct xradio_common *hw_priv, u8* buf, size_t len)
+{
+ return snprintf(buf, len, "%s %d.%d",
+ xradio_debug_fw_types[hw_priv->wsm_caps.firmwareType],
+ hw_priv->wsm_caps.firmwareVersion,
+ hw_priv->wsm_caps.firmwareBuildNumber);
+}
+
+/*added by yangfh, for host debuglevel*/
+struct dentry *debugfs_host = NULL;
+#if defined(DGB_XRADIO_QC)
+struct dentry *debugfs_hwinfo = NULL;
+#endif
+int xradio_host_dbg_init(void)
+{
+ int line = 0;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if(!debugfs_initialized()) {
+ xradio_dbg(XRADIO_DBG_ERROR, "debugfs isnot initialized\n");
+ return 0;
+ }
+
+#define ERR_LINE do { line = __LINE__; goto err;} while(0)
+
+ debugfs_host = debugfs_create_dir("xradio_host_dbg", NULL);
+ if (!debugfs_host)
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_common", S_IRUSR | S_IWUSR, debugfs_host, &dbg_common))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_sbus", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sbus))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_ap", S_IRUSR | S_IWUSR, debugfs_host, &dbg_ap))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_sta", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sta))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_scan", S_IRUSR | S_IWUSR, debugfs_host, &dbg_scan))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_bh", S_IRUSR | S_IWUSR, debugfs_host, &dbg_bh))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_txrx", S_IRUSR | S_IWUSR, debugfs_host, &dbg_txrx))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_wsm", S_IRUSR | S_IWUSR, debugfs_host, &dbg_wsm))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_pm", S_IRUSR | S_IWUSR, debugfs_host, &dbg_pm))
+ ERR_LINE;
+
+#ifdef CONFIG_XRADIO_ITP
+ if (!debugfs_create_x8("dbg_itp", S_IRUSR | S_IWUSR, debugfs_host, &dbg_itp))
+ ERR_LINE;
+#endif
+
+ if (!debugfs_create_x8("dbg_logfile", S_IRUSR | S_IWUSR, debugfs_host, &dbg_logfile))
+ ERR_LINE;
+
+ if (!debugfs_create_x8("dbg_tpa_node", S_IRUSR | S_IWUSR, debugfs_host, &tpa_node_dbg))
+ ERR_LINE;
+
+ if (!debugfs_create_x32("set_sdio_clk", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sdio_clk))
+ ERR_LINE;
+
+ return 0;
+
+#undef ERR_LINE
+err:
+ xradio_dbg(XRADIO_DBG_ERROR, "xradio_host_dbg_init failed=%d\n", line);
+ if (debugfs_host)
+ debugfs_remove_recursive(debugfs_host);
+#if defined(DGB_XRADIO_QC)
+ debugfs_hwinfo = NULL;
+#endif
+ debugfs_host = NULL;
+ return 0;
+}
+
+void xradio_host_dbg_deinit(void)
+{
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ if(debugfs_host)
+ debugfs_remove_recursive(debugfs_host);
+#if defined(DGB_XRADIO_QC)
+ debugfs_hwinfo = NULL;
+#endif
+ debugfs_host = NULL;
+}
+
+int xradio_debug_init_common(struct xradio_common *hw_priv)
+{
+ int ret = -ENOMEM;
+ int line = 0;
+ struct xradio_debug_common *d = NULL;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ //init some debug variables here.
+ retry_dbg = 0;
+ tpa_node_dbg = 0;
+
+#define ERR_LINE do { line = __LINE__; goto err;} while(0)
+
+ d = kzalloc(sizeof(struct xradio_debug_common), GFP_KERNEL);
+ hw_priv->debug = d;
+ if (!d) {
+ xradio_dbg(XRADIO_DBG_ERROR,"%s, xr_kzalloc failed!\n", __FUNCTION__);
+ return ret;
+ }
+
+ d->debugfs_phy = debugfs_create_dir("xradio", hw_priv->hw->wiphy->debugfsdir);
+ if (!d->debugfs_phy)
+ ERR_LINE;
+
+ if (!debugfs_create_file("version", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_version))
+ ERR_LINE;
+
+ if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_status_common))
+ ERR_LINE;
+
+ if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_counters))
+ ERR_LINE;
+
+ if (!debugfs_create_file("backoff", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_backoff))
+ ERR_LINE;
+
+ if (!debugfs_create_file("txpipe", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_txpipe))
+ ERR_LINE;
+
+ if (!debugfs_create_file("ampdu", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_ampducounters))
+ ERR_LINE;
+
+ if (!debugfs_create_file("ratemap", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_ratemap))
+ ERR_LINE;
+
+ if (!debugfs_create_file("dbgstats", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_dbgstats))
+ ERR_LINE;
+
+ if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR,
+ d->debugfs_phy, hw_priv, &fops_11n))
+ ERR_LINE;
+
+ if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_wsm_dumps))
+ ERR_LINE;
+
+ if (!debugfs_create_file("set_fwdbg", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_fwdbg))
+ ERR_LINE;
+
+ if (!debugfs_create_file("rw_fwreg", S_IWUSR, d->debugfs_phy, hw_priv,
+ &fops_rw_fwreg))
+ ERR_LINE;
+
+ if (!debugfs_create_file("rw_fwreg_direct", S_IWUSR, d->debugfs_phy, hw_priv,
+ &fops_rw_fwreg_direct))
+ ERR_LINE;
+
+ if (!debugfs_create_file("set_ampdu_len", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_ampdu_len))
+ ERR_LINE;
+
+ if (!debugfs_create_file("set_rts_threshold", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_rts_threshold))
+ ERR_LINE;
+
+ if (!debugfs_create_file("low_pwr_disable", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_low_pwr))
+ ERR_LINE;
+
+ if (!debugfs_create_file("ps_disable", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_ps_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("retry_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_retry_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("rates_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_rates_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("backoff_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_backoff_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("tala_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_tala_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("tx_pwr_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_tx_pwr_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("tpa_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_tpa_ctrl))
+ ERR_LINE;
+
+ if (!debugfs_create_file("tpa_debug", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_tpa_debug))
+ ERR_LINE;
+
+ if (!debugfs_create_file("policy_info", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_policy_info))
+ ERR_LINE;
+
+ if (!debugfs_create_file("bh_stat", S_IRUSR, d->debugfs_phy,
+ hw_priv, &fops_bh_stat))
+ ERR_LINE;
+
+ if (!debugfs_create_file("parse_flags", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_parse_flags))
+ ERR_LINE;
+
+ if (!debugfs_create_file("set_measure_type", S_IRUSR | S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_11k))
+ ERR_LINE;
+
+ if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR,d->debugfs_phy,
+ hw_priv, &fops_short_dump))
+ ERR_LINE;
+
+#if defined(DGB_XRADIO_HWT)
+ //hardware test
+ if (!debugfs_create_file("hwt_hif_tx", S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_hwt_hif_tx))
+ ERR_LINE;
+
+ if (!debugfs_create_file("hwt_hif_rx", S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_hwt_hif_rx))
+ ERR_LINE;
+
+ if (!debugfs_create_file("hwt_enc", S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_hwt_enc))
+ ERR_LINE;
+
+ if (!debugfs_create_file("hwt_mic", S_IWUSR, d->debugfs_phy,
+ hw_priv, &fops_hwt_mic))
+ ERR_LINE;
+#endif //DGB_XRADIO_HWT
+
+#if defined(DGB_XRADIO_QC)
+ //for QC apk read.
+ if (debugfs_host && !debugfs_hwinfo) {
+ debugfs_hwinfo = debugfs_create_file("hwinfo", 0666, debugfs_host,
+ hw_priv, &fops_hwinfo);
+ if (!debugfs_hwinfo)
+ ERR_LINE;
+ }
+#endif
+
+ return 0;
+
+#undef ERR_LINE
+
+err:
+ xradio_dbg(XRADIO_DBG_ERROR, "xradio_debug_init_common failed=%d\n", line);
+ hw_priv->debug = NULL;
+ debugfs_remove_recursive(d->debugfs_phy);
+ kfree(d);
+ return ret;
+}
+
+void xradio_debug_release_common(struct xradio_common *hw_priv)
+{
+ struct xradio_debug_common *d = hw_priv->debug;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#if defined(DGB_XRADIO_QC)
+ if (debugfs_hwinfo) {
+ debugfs_remove(debugfs_hwinfo);
+ debugfs_hwinfo = NULL;
+ }
+#endif
+ if (d) {
+ hw_priv->debug = NULL;
+ //removed by mac80211, don't remove it again, fixed wifi on/off by yangfh
+ //debugfs_remove_recursive(d->debugfs_phy);
+ kfree(d);
+ }
+}
+#endif //CONFIG_XRADIO_DEBUGFS
+
+#define FRAME_TYPE(xx) ieee80211_is_ ## xx(fctl)
+#define FT_MSG_PUT(f, ...) do{ \
+ if(flags&f) frame_msg += sprintf(frame_msg, __VA_ARGS__); \
+ }while(0)
+
+#define PT_MSG_PUT(f, ...) do{ \
+ if(flags&f) proto_msg += sprintf(proto_msg, __VA_ARGS__); \
+ }while(0)
+
+#define FRAME_PARSE(f, name) do{ \
+ if(FRAME_TYPE(name)) { FT_MSG_PUT(f, "%s", #name); goto outprint;} \
+ } while(0)
+
+#define IS_FRAME_PRINT (frame_msg != (char *)&framebuf[0])
+#define IS_PROTO_PRINT (proto_msg != (char *)&protobuf[0])
+
+
+char framebuf[512] = {0};
+char protobuf[512] = {0};
+
+char * p2p_frame_type[] = {
+ "GO Negotiation Request",
+ "GO Negotiation Response",
+ "GO Negotiation Confirmation",
+ "P2P Invitation Request",
+ "P2P Invitation Response",
+ "Device Discoverability Request",
+ "Device Discoverability Response",
+ "Provision Discovery Request",
+ "Provision Discovery Response",
+ "Reserved"
+};
+#if (defined(CONFIG_XRADIO_DEBUG))
+void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id)
+{
+ char * frame_msg = &framebuf[0];
+ char * proto_msg = &protobuf[0];
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)mac_data;
+ u16 fctl = frame->frame_control;
+
+ memset(frame_msg, 0, sizeof(frame_msg));
+ memset(proto_msg, 0, sizeof(proto_msg));
+
+ if(ieee80211_is_data(fctl)) {
+ u8 machdrlen = ieee80211_hdrlen(fctl);
+ u8* llc_data = mac_data+machdrlen+iv_len;
+
+ if(ieee80211_is_qos_nullfunc(fctl)){
+ FT_MSG_PUT(PF_DATA, "QoS-NULL");
+ goto outprint;
+ }
+ if(ieee80211_is_data_qos(fctl))
+ FT_MSG_PUT(PF_DATA, "QoS");
+ if(ieee80211_is_nullfunc(fctl)) {
+ FT_MSG_PUT(PF_DATA, "NULL(ps=%d)", !!(fctl&IEEE80211_FCTL_PM));
+ goto outprint;
+ }
+ FT_MSG_PUT(PF_DATA, "data(TDFD=%d%d,R=%d,P=%d)",
+ !!(fctl&IEEE80211_FCTL_TODS), !!(fctl&IEEE80211_FCTL_FROMDS),
+ !!(fctl&IEEE80211_FCTL_RETRY),!!(fctl&IEEE80211_FCTL_PROTECTED));
+
+ if (is_SNAP(llc_data)) {
+ if(is_ip(llc_data)) {
+ u8 * ip_hdr = llc_data+LLC_LEN;
+ u8 * ipaddr_s = ip_hdr+IP_S_ADD_OFF;
+ u8 * ipaddr_d = ip_hdr+IP_D_ADD_OFF;
+ u8 * proto_hdr = ip_hdr+((ip_hdr[0]&0xf)<<2); //ihl:words
+
+ if(is_tcp(llc_data)) {
+ PT_MSG_PUT(PF_TCP, "TCP%s%s, src=%d, dest=%d, seq=0x%08x, ack=0x%08x",
+ (proto_hdr[13]&0x01)?"(S)":"", (proto_hdr[13]&0x02)?"(F)":"",
+ (proto_hdr[0]<<8)|proto_hdr[1], (proto_hdr[2]<<8)|proto_hdr[3],
+ (proto_hdr[4]<<24)|(proto_hdr[5]<<16)|(proto_hdr[6]<<8)|proto_hdr[7],
+ (proto_hdr[8]<<24)|(proto_hdr[9]<<16)|(proto_hdr[10]<<8)|proto_hdr[11]);
+
+ } else if(is_udp(llc_data)) {
+ if(is_dhcp(llc_data)) {
+ u8 Options_len = BOOTP_OPS_LEN;
+ u32 dhcp_magic = cpu_to_be32(DHCP_MAGIC);
+ u8 * dhcphdr = proto_hdr+UDP_LEN+UDP_BOOTP_LEN;
+ while(Options_len) {
+ if(*(u32*)dhcphdr == dhcp_magic)
+ break;
+ dhcphdr++;
+ Options_len--;
+ }
+ PT_MSG_PUT(PF_DHCP, "DHCP, Opt=%d, MsgType=%d", *(dhcphdr+4), *(dhcphdr+6));
+ } else {
+ PT_MSG_PUT(PF_UDP, "UDP, source=%d, dest=%d",
+ (proto_hdr[0]<<8)|proto_hdr[1], (proto_hdr[2]<<8)|proto_hdr[3]);
+ }
+ } else if(is_icmp(llc_data)) {
+ PT_MSG_PUT(PF_ICMP,"ICMP%s%s, Seq=%d", (8 == proto_hdr[0])?"(ping)":"",
+ (0 == proto_hdr[0])?"(reply)":"", (proto_hdr[6]<<8)|proto_hdr[7]);
+ } else if(is_igmp(llc_data)) {
+ PT_MSG_PUT(PF_UNKNWN, "IGMP, type=0x%x", proto_hdr[0]);
+ } else {
+ PT_MSG_PUT(PF_UNKNWN, "unknown IP type=%d", *(ip_hdr+IP_PROTO_OFF));
+ }
+ if (IS_PROTO_PRINT) {
+ PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(s)", \
+ ipaddr_s[0], ipaddr_s[1], ipaddr_s[2], ipaddr_s[3]);
+ PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(d)", \
+ ipaddr_d[0], ipaddr_d[1], ipaddr_d[2], ipaddr_d[3]);
+ }
+
+ } else if (is_8021x(llc_data)) {
+ PT_MSG_PUT(PF_8021X, "8021X");
+ } else { //other protol, no detail.
+ switch(cpu_to_be16(*(u16*)(llc_data+LLC_TYPE_OFF))) {
+ case ETH_P_IPV6: //0x08dd
+ PT_MSG_PUT(PF_UNKNWN, "IPv6");
+ break;
+ case ETH_P_ARP: //0x0806
+ PT_MSG_PUT(PF_UNKNWN, "ARP");
+ break;
+ case ETH_P_RARP: //0x8035
+ PT_MSG_PUT(PF_UNKNWN, "RARP");
+ break;
+ case ETH_P_DNA_RC: //0x6002
+ PT_MSG_PUT(PF_UNKNWN, "DNA Remote Console");
+ break;
+ case ETH_P_DNA_RT: //0x6003
+ PT_MSG_PUT(PF_UNKNWN, "DNA Routing");
+ break;
+ case ETH_P_8021Q: //0x8100
+ PT_MSG_PUT(PF_UNKNWN, "802.1Q VLAN");
+ break;
+ case ETH_P_LINK_CTL: //0x886c
+ PT_MSG_PUT(PF_UNKNWN, "wlan link local tunnel(HPNA)");
+ break;
+ case ETH_P_PPP_DISC: //0x8863
+ PT_MSG_PUT(PF_UNKNWN, "PPPoE discovery");
+ break;
+ case ETH_P_PPP_SES: //0x8864
+ PT_MSG_PUT(PF_UNKNWN, "PPPoE session");
+ break;
+ case ETH_P_MPLS_UC: //0x8847
+ PT_MSG_PUT(PF_UNKNWN, "MPLS Unicast");
+ break;
+ case ETH_P_MPLS_MC: //0x8848
+ PT_MSG_PUT(PF_UNKNWN, "MPLS Multicast");
+ break;
+ default:
+ PT_MSG_PUT(PF_UNKNWN, "unknown Ethernet type=0x%04x", cpu_to_be16(*(u16*)(llc_data+LLC_TYPE_OFF)));
+ break;
+ }
+ }
+ } else if (is_STP(llc_data)){
+ //spanning tree proto.
+ PT_MSG_PUT(PF_UNKNWN, "spanning tree");
+ } else {
+ PT_MSG_PUT(PF_UNKNWN, "unknown LLC type=0x%08x,0x%08x", *(u32*)(llc_data), *((u32*)(llc_data)+1));
+ }
+
+ } else if(ieee80211_is_mgmt(fctl) && (PF_MGMT&flags)) {
+
+ FRAME_PARSE(PF_MGMT, auth );
+ FRAME_PARSE(PF_MGMT, deauth );
+ FRAME_PARSE(PF_MGMT, assoc_req );
+ FRAME_PARSE(PF_MGMT, assoc_resp);
+ FRAME_PARSE(PF_MGMT, disassoc );
+ FRAME_PARSE(PF_MGMT, atim );
+
+ //for more information about action frames.
+ if (FRAME_TYPE(action)) {
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame;
+ FT_MSG_PUT(PF_MGMT, "%s", "action");
+
+ if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
+ u8 *action = (u8*)&mgmt->u.action.category;
+ u32 oui = *(u32 *)&action[2];
+ u8 oui_subtype = action[6] > 8? 9 : action[6];
+ if (action[1] == 0x09 && oui == 0x099A6F50)
+ FT_MSG_PUT(PF_MGMT, "(%s)", p2p_frame_type[oui_subtype]);
+ } else if (mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+ mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) {
+ FT_MSG_PUT(PF_MGMT, "(ADDBA_REQ-%d)", mgmt->u.action.u.addba_req.start_seq_num);
+ } else if (mgmt->u.action.category == WLAN_CATEGORY_BACK &&
+ mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_RESP) {
+ FT_MSG_PUT(PF_MGMT, "(ADDBA_RESP-%d)", mgmt->u.action.u.addba_resp.status);
+ } else {
+ FT_MSG_PUT(PF_MGMT, "(%d)", mgmt->u.action.category);
+ }
+ goto outprint;
+ }
+
+ //too much scan results, don't print if no need.
+ FRAME_PARSE(PF_SCAN, probe_req );
+ FRAME_PARSE(PF_SCAN, probe_resp );
+ FRAME_PARSE(PF_SCAN, beacon );
+ //must be last.
+ FT_MSG_PUT(PF_UNKNWN, "unknown mgmt");
+
+ } else if (ieee80211_is_ctl(fctl) && (PF_CTRL&flags)){
+
+ flags &= (~PF_MAC_SN); //no seq ctrl in ctrl frames.
+ FRAME_PARSE(PF_CTRL, back );
+ FRAME_PARSE(PF_CTRL, back_req);
+ FRAME_PARSE(PF_CTRL, ack );
+ FRAME_PARSE(PF_CTRL, rts );
+ FRAME_PARSE(PF_CTRL, cts );
+ FRAME_PARSE(PF_CTRL, pspoll );
+ //must be last.
+ FT_MSG_PUT(PF_UNKNWN, "unknown ctrl");
+ } else {
+ FT_MSG_PUT(PF_UNKNWN, "unknown mac frame, fctl=0x%04x\n", fctl);
+ }
+
+outprint:
+
+ FT_MSG_PUT(PF_MAC_SN, "-SN=%d(%d)", (frame->seq_ctrl>>4), (frame->seq_ctrl&0xf));
+
+ //output all msg.
+ if(IS_FRAME_PRINT || IS_PROTO_PRINT) {
+ u8 *related = NULL;
+ u8 *own = NULL;
+ char *r_type = NULL;
+ char *o_type = NULL;
+ u8 machdrlen = ieee80211_hdrlen(fctl);
+ u8 *sa = ieee80211_get_SA(frame);
+ u8 *da = ieee80211_get_DA(frame);
+
+ if (flags&PF_RX) {
+ related = frame->addr2;
+ own = frame->addr1;
+ r_type = "TA";
+ o_type = "RA";
+ } else {
+ related = frame->addr1;
+ own = frame->addr2;
+ r_type = "RA";
+ o_type = "TA";
+ }
+
+ if (machdrlen >= 16) { //if ACK or BA, don't print.
+ FT_MSG_PUT(PF_MACADDR, "-%02x:%02x:%02x:%02x:%02x:%02x(%s)",
+ related[0], related[1], related[2],
+ related[3], related[4], related[5],
+ r_type);
+ FT_MSG_PUT(PF_OWNMAC, "-%02x:%02x:%02x:%02x:%02x:%02x(%s)",
+ own[0],own[1],own[2],own[3],own[4],own[5],
+ o_type);
+ FT_MSG_PUT(PF_SA_DA, "-%02x:%02x:%02x:%02x:%02x:%02x(DA)",
+ da[0], da[1], da[2], da[3], da[4], da[5]);
+ FT_MSG_PUT(PF_SA_DA, "-%02x:%02x:%02x:%02x:%02x:%02x(SA)",
+ sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]);
+ }
+
+ xradio_dbg(XRADIO_DBG_ALWY, "if%d-%s%s--%s\n", if_id,
+ (PF_RX&flags)?"RX-":"TX-",framebuf, protobuf);
+ }
+}
+#endif
+#undef FT_MSG_PUT
+#undef PT_MSG_PUT
+#undef FRAME_PARSE
+#undef FRAME_TYPE
+
+#ifdef DGB_LOG_FILE
+u8 log_buffer[DGB_LOG_BUF_LEN];
+u16 log_pos = 0;
+struct file *fp_log = NULL;
+atomic_t file_ref = {0};
+#define T_LABEL_LEN 32
+char last_time_label[T_LABEL_LEN] = {0};
+
+int xradio_logfile(char *buffer, int buf_len, u8 b_time)
+{
+ int ret=-1;
+ int size = buf_len;
+ mm_segment_t old_fs = get_fs();
+
+ if (!buffer)
+ return ret;
+
+ if (buf_len < 0)
+ size = strlen(buffer);
+ if (!size)
+ return ret;
+
+ if (atomic_add_return(1, &file_ref) == 1) {
+ fp_log = filp_open(DGB_LOG_PATH0, O_CREAT|O_WRONLY, 0666);
+ if (IS_ERR(fp_log)) {
+ printk(KERN_ERR "[XRADIO] ERR, can't open %s(%d).\n",
+ DGB_LOG_PATH0, (int)fp_log);
+ goto exit;
+ }
+ }
+ //printk(KERN_ERR "[XRADIO] file_ref=%d\n", atomic_read(&file_ref));
+
+ if(fp_log->f_op->write == NULL) {
+ printk(KERN_ERR "[XRADIO] ERR, %s:File is not allow to write!\n",
+ __FUNCTION__);
+ goto exit;
+ } else {
+ set_fs(KERNEL_DS);
+ if (fp_log->f_op->llseek != NULL) {
+ vfs_llseek(fp_log, 0, SEEK_END);
+ } else {
+ fp_log->f_pos = 0;
+ }
+ if (b_time) {
+ struct timeval time_now = {0};
+ struct rtc_time tm;
+ int hour = 0;
+ char time_label[T_LABEL_LEN] = {0};
+ do_gettimeofday(&time_now);
+ rtc_time_to_tm(time_now.tv_sec, &tm);
+ hour = tm.tm_hour-sys_tz.tz_minuteswest/60;
+ sprintf(time_label,"\n%d-%02d-%02d_%02d-%02d-%02d\n",
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday+hour/24,
+ hour%24, tm.tm_min-sys_tz.tz_minuteswest%60, tm.tm_sec);
+ if (memcmp(last_time_label, time_label, T_LABEL_LEN)) {
+ memcpy(last_time_label, time_label, T_LABEL_LEN);
+ ret = vfs_write(fp_log, time_label, strlen(time_label), &fp_log->f_pos);
+ }
+ }
+ ret = vfs_write(fp_log, buffer, size, &fp_log->f_pos);
+ set_fs(old_fs);
+ }
+
+exit:
+ if (atomic_read(&file_ref) == 1) {
+ if (!IS_ERR(fp_log)) {
+ filp_close(fp_log, NULL);
+ fp_log = (struct file *)-ENOENT;
+ }
+ }
+ atomic_sub(1, &file_ref);
+ return ret;
+}
+#endif
+
+#if defined(DGB_XRADIO_HWT)
+/***************************for HWT, yangfh********************************/
+struct sk_buff *hwt_skb = NULL;
+int sent_num = 0;
+int get_hwt_hif_tx(struct xradio_common *hw_priv, u8 **data,
+ size_t *tx_len, int *burst, int *vif_selected)
+{
+
+ HWT_PARAMETERS *hwt_tx_hdr = NULL;
+ if (!hwt_tx_en || !hwt_tx_len || !hwt_tx_num ||
+ sent_num >= hwt_tx_num) {
+ if (hwt_skb) {
+ dev_kfree_skb(hwt_skb);
+ hwt_skb = NULL;
+ }
+ return 0;
+ }
+
+ if (!hwt_skb) {
+ hwt_skb = xr_alloc_skb(1504);
+ if (!hwt_skb) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:skb is NULL!\n", __func__);
+ return 0;
+ }
+ if ((u32)hwt_skb->data & 3) {
+ u8 align = 4-((u32)hwt_skb->data & 3);
+ skb_reserve(hwt_skb, align);
+ }
+ skb_put(hwt_skb, 1500);
+ }
+
+ //fill the header info
+ if (hwt_tx_len < sizeof(HWT_PARAMETERS))
+ hwt_tx_len = sizeof(HWT_PARAMETERS);
+ if (hwt_tx_len > 1500)
+ hwt_tx_len = 1500;
+ hwt_tx_hdr = hwt_skb->data;
+ hwt_tx_hdr->MsgID = 0x0024;
+ hwt_tx_hdr->Msglen = hwt_tx_len;
+ hwt_tx_hdr->TestID = 0x0001;
+ hwt_tx_hdr->Data = 0x1234;
+
+ //send the packet
+ *data = hwt_skb->data;
+ *tx_len = hwt_tx_hdr->Msglen;
+ *vif_selected = 0;
+ *burst = 2; //burst > 1 for continuous tx.
+ sent_num++;
+
+ //first packet.
+ if (sent_num == 1) {
+ do_gettimeofday(&hwt_start_time);
+ }
+
+ //set confirm
+ hwt_tx_hdr->Params = 0;
+ if (sent_num >= hwt_tx_num) {
+ hwt_tx_hdr->Params = 0x101; //last packet
+ hwt_tx_en = 0; //disable hwt_tx_en
+ xradio_dbg(XRADIO_DBG_ALWY, "%s:sent last packet!\n", __func__);
+ } else if (hwt_tx_cfm) {
+ hwt_tx_hdr->Params = !(sent_num%hwt_tx_cfm);
+ }
+
+ return 1;
+}
+#endif
diff --git a/drivers/net/wireless/xradio/debug.h b/drivers/net/wireless/xradio/debug.h
new file mode 100644
index 00000000..4e29bb88
--- /dev/null
+++ b/drivers/net/wireless/xradio/debug.h
@@ -0,0 +1,493 @@
+/*
+ * DebugFS code for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef XRADIO_DEBUG_H_INCLUDED
+#define XRADIO_DEBUG_H_INCLUDED
+
+#define XRADIO_DBG_ALWY 0x01 /* Message always need to be present even in release version. */
+#define XRADIO_DBG_ERROR 0x02 /* Error message to report an error, it can hardly works. */
+#define XRADIO_DBG_WARN 0x04 /* Warning message to inform us of something unnormal or
+ * something very important, but it still work. */
+#define XRADIO_DBG_NIY 0x08 /* Important message we need to know in unstable version. */
+#define XRADIO_DBG_MSG 0x10 /* Normal message just for debug in developing stage. */
+#define XRADIO_DBG_TRC 0x20 /* Trace of functions, for sequence of functions called. Normally,
+ * don't set this level because there are too more print. */
+#define XRADIO_DBG_LEVEL 0xFF
+
+/*added by yangfh, for host debuglevel*/
+extern u8 dbg_common ;
+extern u8 dbg_bh ;
+extern u8 dbg_txrx ;
+extern u8 dbg_wsm ;
+extern u8 dbg_sta ;
+extern u8 dbg_scan ;
+extern u8 dbg_ap ;
+extern u8 dbg_pm ;
+extern u8 dbg_itp ;
+extern u8 dbg_logfile;
+
+/*for sdio clk debug*/
+extern u32 dbg_sdio_clk;
+
+/* for ps debug */
+extern u8 ps_disable;
+extern u8 ps_idleperiod;
+extern u8 ps_changeperiod;
+//info of bh thread
+extern u32 irq_count;
+extern u32 int_miss_cnt;
+extern u32 fix_miss_cnt;
+extern u32 next_rx_cnt;
+extern u32 rx_total_cnt;
+extern u32 tx_total_cnt;
+
+#define WSM_DUMP_MAX_SIZE 20
+
+#if defined(CONFIG_XRADIO_DEBUG)
+//#define DGB_LOG_FILE //Don't log more than 500byte once.
+//#define DGB_XRADIO_QC //Enable this only in QC test.
+//#define DGB_XRADIO_HWT //Enable this only in hardware testmode using test fw.
+#endif
+
+#ifdef DGB_LOG_FILE
+#define DGB_LOG_BUF_LEN 1500
+#define DGB_LOG_PATH0 "/data/xradio_err.log"
+
+extern u8 log_buffer[DGB_LOG_BUF_LEN];
+extern u16 log_pos;
+int xradio_logfile(char *buffer, int buf_len, u8 b_time);
+
+#define LOG_FILE(b_time, msg) xradio_logfile(msg, -1, b_time)
+#define LOG_FILE_VARS(b_time, ...) do { \
+ if (!log_pos) \
+ memset(log_buffer, 0, DGB_LOG_BUF_LEN); \
+ if (log_pos <= 1000) \
+ log_pos += sprintf((char *)(log_buffer+log_pos), __VA_ARGS__); \
+ if (xradio_logfile(log_buffer, log_pos, b_time) >= 0) \
+ log_pos = 0; \
+} while (0)
+
+#else //DGB_LOG_FILE disable
+
+#define LOG_FILE(b_time, msg)
+#define LOG_FILE_VARS(b_time, ...)
+#endif
+
+
+#if (defined(CONFIG_XRADIO_DEBUG))
+/****************************** debug version *******************************/
+#if (defined(CONFIG_XRADIO_DUMP_ON_ERROR))
+#define SYS_BUG(c) BUG_ON(c)
+#define SYS_WARN(c) WARN_ON(c)
+#else
+#define SYS_BUG(c) WARN_ON(c)
+#define SYS_WARN(c) WARN_ON(c)
+#endif
+
+#define xradio_dbg(level, ...) \
+ do { \
+ if ((level) & dbg_common & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[XRADIO_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_common & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[XRADIO_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_common) \
+ printk(KERN_ERR "[XRADIO] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[XRADIO_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define txrx_printk(level, ...) \
+ do { \
+ if ((level) & dbg_txrx & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[TXRX_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_txrx & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[TXRX_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_txrx) \
+ printk(KERN_ERR "[TXRX] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[TXRX_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define wsm_printk(level, ...) \
+ do { \
+ if ((level) & dbg_wsm & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[WSM_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_wsm & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[WSM_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_wsm) \
+ printk(KERN_ERR "[WSM] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[WSM_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define sta_printk(level, ...) \
+ do { \
+ if ((level) & dbg_sta & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[STA_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_sta & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[STA_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_sta) \
+ printk(KERN_ERR "[STA] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[STA_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define scan_printk(level, ...) \
+ do { \
+ if ((level) & dbg_scan & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[SCAN_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_scan & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[SCAN_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_scan) \
+ printk(KERN_ERR "[SCAN] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[SCAN_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define ap_printk(level, ...) \
+ do { \
+ if ((level) & dbg_ap & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[AP_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_ap & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[AP_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_ap) \
+ printk(KERN_ERR "[AP] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[AP_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define pm_printk(level, ...) \
+ do { \
+ if ((level) & dbg_pm & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[PM_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_pm & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[PM_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_pm) \
+ printk(KERN_ERR "[PM] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[PM_ERR] " __VA_ARGS__); \
+ } while (0)
+
+#define itp_printk(level, ...) \
+ do { \
+ if ((level) & dbg_itp & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[ITP_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_itp & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[ITP_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_itp) \
+ printk(KERN_ERR "[ITP] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[ITP_ERR] " __VA_ARGS__); \
+ } while (0)
+
+
+#define DBG_FUN_LINE printk(KERN_ERR "%s,line=%d", __FUNCTION__, __LINE__)
+#define PARAM_CHECK_FALSE(p) \
+ do { \
+ if (!p) DBG_FUN_LINE; \
+ } while (0)
+
+#define PARAM_CHECK_TRUE(p) \
+ do { \
+ if (p) DBG_FUN_LINE; \
+ } while (0)
+
+
+//interfaces to debug packet's information.
+//for802.11
+#define PF_CTRL 0x0001
+#define PF_MGMT 0x0002
+#define PF_DATA 0x0004
+#define PF_SCAN 0x0008
+
+//for ip data
+#define PF_TCP 0x0010
+#define PF_UDP 0x0020
+#define PF_DHCP 0x0040
+#define PF_ICMP 0x0080
+
+//for special frames or info.
+#define PF_8021X 0x0100 //action frames
+#define PF_MAC_SN 0x0200 //mac seq
+#define PF_OWNMAC 0x0400 //TA in Tx, RA in Rx.
+#define PF_SA_DA 0x0800 //SA, DA for Ethernet.
+
+#define PF_MACADDR 0x1000 //RA in Tx, TA in Rx.
+#define PF_IPADDR 0x2000 //ip address of ip packets.
+#define PF_UNKNWN 0x4000 //print unknown type frames of 802.11 flag you set.
+#define PF_RX 0x8000 //0:TX, 1:RX. So, need to add PF_RX in Rx path.
+void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id);
+
+#if defined(DGB_XRADIO_HWT) //hardware test
+typedef struct HWT_PARAMETERS_S {
+ u16 Msglen;
+ u16 MsgID;
+ u16 TestID;
+ u16 Params;
+ u16 Datalen;
+ u16 Data;
+} HWT_PARAMETERS;
+int get_hwt_hif_tx(struct xradio_common *hw_priv, u8 **data,
+ size_t *tx_len, int *burst, int *vif_selected);
+#endif //DGB_XRADIO_HWT
+
+#else
+/****************************** release version *******************************/
+#define SYS_BUG(c) BUG_ON(c)
+#define SYS_WARN(c) WARN_ON(c)
+
+#define xradio_dbg(level, ...)
+#define txrx_printk(level, ...)
+#define wsm_printk(level, ...)
+#define sta_printk(level, ...)
+#define scan_printk(level, ...)
+#define ap_printk(level, ...)
+#define pm_printk(level, ...)
+#define itp_printk(level, ...)
+
+#define DBG_FUN_LINE
+#define PARAM_CHECK_FALSE
+#define PARAM_CHECK_TRUE
+
+static inline void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id)
+{
+}
+#endif //CONFIG_XRADIO_DEBUG
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+/****************************** debugfs version *******************************/
+struct xradio_debug_common {
+ struct dentry *debugfs_phy;
+ int tx_cache_miss;
+ int tx_burst;
+ int rx_burst;
+ int ba_cnt;
+ int ba_acc;
+ int ba_cnt_rx;
+ int ba_acc_rx;
+#ifdef CONFIG_XRADIO_ITP
+ struct xradio_itp itp;
+#endif /* CONFIG_XRADIO_ITP */
+};
+
+struct xradio_debug_priv {
+ struct dentry *debugfs_phy;
+ int tx;
+ int tx_agg;
+ int rx;
+ int rx_agg;
+ int tx_multi;
+ int tx_multi_frames;
+ int tx_align;
+ int tx_ttl;
+};
+
+
+#define DBG_BH_IRQ_ADD irq_count++
+#define DBG_BH_MISS_ADD int_miss_cnt++
+#define DBG_BH_FIX_RX_ADD fix_miss_cnt++
+#define DBG_BH_NEXT_RX_ADD next_rx_cnt++
+#define DBG_BH_RX_TOTAL_ADD rx_total_cnt++
+#define DBG_BH_TX_TOTAL_ADD tx_total_cnt++
+
+int xradio_debug_init_common(struct xradio_common *hw_priv);
+int xradio_debug_init_priv(struct xradio_common *hw_priv,
+ struct xradio_vif *priv);
+void xradio_debug_release_common(struct xradio_common *hw_priv);
+void xradio_debug_release_priv(struct xradio_vif *priv);
+
+static inline void xradio_debug_txed(struct xradio_vif *priv)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->tx;
+}
+
+static inline void xradio_debug_txed_agg(struct xradio_vif *priv)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->tx_agg;
+}
+
+static inline void xradio_debug_txed_multi(struct xradio_vif *priv,
+ int count)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->tx_multi;
+ priv->debug->tx_multi_frames += count;
+}
+
+static inline void xradio_debug_rxed(struct xradio_vif *priv)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->rx;
+}
+
+static inline void xradio_debug_rxed_agg(struct xradio_vif *priv)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->rx_agg;
+}
+
+static inline void xradio_debug_tx_cache_miss(struct xradio_common *hw_priv)
+{
+ if (!hw_priv->debug)
+ return;
+ ++hw_priv->debug->tx_cache_miss;
+}
+
+static inline void xradio_debug_tx_align(struct xradio_vif *priv)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->tx_align;
+}
+
+static inline void xradio_debug_tx_ttl(struct xradio_vif *priv)
+{
+ if (!priv->debug)
+ return;
+ ++priv->debug->tx_ttl;
+}
+
+static inline void xradio_debug_tx_burst(struct xradio_common *hw_priv)
+{
+ if (!hw_priv->debug)
+ return;
+ ++hw_priv->debug->tx_burst;
+}
+
+static inline void xradio_debug_rx_burst(struct xradio_common *hw_priv)
+{
+ if (!hw_priv->debug)
+ return;
+ ++hw_priv->debug->rx_burst;
+}
+
+static inline void xradio_debug_ba(struct xradio_common *hw_priv,
+ int ba_cnt, int ba_acc, int ba_cnt_rx,
+ int ba_acc_rx)
+{
+ if (!hw_priv->debug)
+ return;
+ hw_priv->debug->ba_cnt = ba_cnt;
+ hw_priv->debug->ba_acc = ba_acc;
+ hw_priv->debug->ba_cnt_rx = ba_cnt_rx;
+ hw_priv->debug->ba_acc_rx = ba_acc_rx;
+}
+
+int xradio_print_fw_version(struct xradio_common *hw_priv, u8* buf, size_t len);
+
+int xradio_host_dbg_init(void);
+void xradio_host_dbg_deinit(void);
+
+#else /* CONFIG_XRADIO_DEBUGFS */
+/****************************** no debugfs version *******************************/
+#define DBG_BH_MISS_ADD
+#define DBG_BH_FIX_RX_ADD
+#define DBG_BH_NEXT_RX_ADD
+#define DBG_BH_RX_TOTAL_ADD
+#define DBG_BH_TX_TOTAL_ADD
+
+static inline int xradio_debug_init_common(struct xradio_common *hw_priv)
+{
+ return 0;
+}
+
+static inline int xradio_debug_init_priv(struct xradio_common *hw_priv,
+ struct xradio_vif *priv)
+{
+ return 0;
+}
+
+static inline void xradio_debug_release_common(struct xradio_common *hw_priv)
+{
+}
+
+static inline void xradio_debug_release_priv(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_txed(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_txed_agg(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_txed_multi(struct xradio_vif *priv,
+ int count)
+{
+}
+
+static inline void xradio_debug_rxed(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_rxed_agg(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_tx_cache_miss(struct xradio_common *common)
+{
+}
+
+static inline void xradio_debug_tx_align(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_tx_ttl(struct xradio_vif *priv)
+{
+}
+
+static inline void xradio_debug_tx_burst(struct xradio_common *hw_priv)
+{
+}
+
+static inline void xradio_debug_rx_burst(struct xradio_common *hw_priv)
+{
+}
+
+static inline void xradio_debug_ba(struct xradio_common *hw_priv,
+ int ba_cnt, int ba_acc, int ba_cnt_rx,
+ int ba_acc_rx)
+{
+}
+
+static inline int xradio_print_fw_version(struct xradio_vif *priv,
+ u8* buf, size_t len)
+{
+ return 0;
+}
+
+static inline int xradio_host_dbg_init(void)
+{
+ return 0;
+}
+
+static inline void xradio_host_dbg_deinit(void)
+{
+}
+#endif /* CONFIG_XRADIO_DEBUGFS */
+
+#endif /* XRADIO_DEBUG_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/fwio.c b/drivers/net/wireless/xradio/fwio.c
new file mode 100644
index 00000000..47660d1f
--- /dev/null
+++ b/drivers/net/wireless/xradio/fwio.c
@@ -0,0 +1,560 @@
+/*
+ * Firmware I/O implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+
+#include "xradio.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sdio.h"
+
+/* Macroses are local. */
+#define APB_WRITE(reg, val) \
+ do { \
+ ret = xradio_apb_write_32(hw_priv, APB_ADDR(reg), (val)); \
+ if (ret < 0) { \
+ xradio_dbg(XRADIO_DBG_ERROR, \
+ "%s: can't write %s at line %d.\n", \
+ __func__, #reg, __LINE__); \
+ goto error; \
+ } \
+ } while (0)
+#define APB_READ(reg, val) \
+ do { \
+ ret = xradio_apb_read_32(hw_priv, APB_ADDR(reg), &(val)); \
+ if (ret < 0) { \
+ xradio_dbg(XRADIO_DBG_ERROR, \
+ "%s: can't read %s at line %d.\n", \
+ __func__, #reg, __LINE__); \
+ goto error; \
+ } \
+ } while (0)
+#define REG_WRITE(reg, val) \
+ do { \
+ ret = xradio_reg_write_32(hw_priv, (reg), (val)); \
+ if (ret < 0) { \
+ xradio_dbg(XRADIO_DBG_ERROR, \
+ "%s: can't write %s at line %d.\n", \
+ __func__, #reg, __LINE__); \
+ goto error; \
+ } \
+ } while (0)
+#define REG_READ(reg, val) \
+ do { \
+ ret = xradio_reg_read_32(hw_priv, (reg), &(val)); \
+ if (ret < 0) { \
+ xradio_dbg(XRADIO_DBG_ERROR, \
+ "%s: can't read %s at line %d.\n", \
+ __func__, #reg, __LINE__); \
+ goto error; \
+ } \
+ } while (0)
+
+
+static int xradio_get_hw_type(u32 config_reg_val, int *major_revision)
+{
+ int hw_type = -1;
+ u32 hif_type = (config_reg_val >> 24) & 0x4;
+ //u32 hif_vers = (config_reg_val >> 31) & 0x1;
+
+ /* Check if we have XRADIO*/
+ if (hif_type == 0x4) {
+ *major_revision = 0x4;
+ hw_type = HIF_HW_TYPE_XRADIO;
+ } else {
+ //hw type unknown.
+ *major_revision = 0x0;
+ }
+ return hw_type;
+}
+
+/*
+ * This function is called to Parse the SDD file
+ * to extract some informations
+ */
+static int xradio_parse_sdd(struct xradio_common *hw_priv, u32 *dpll)
+{
+ int ret = 0;
+ const char *sdd_path = NULL;
+ struct xradio_sdd *pElement = NULL;
+ int parsedLength = 0;
+
+ SYS_BUG(hw_priv->sdd != NULL);
+
+ /* select and load sdd file depend on hardware version. */
+ switch (hw_priv->hw_revision) {
+ case XR819_HW_REV0:
+ sdd_path = XR819_SDD_FILE;
+ break;
+ default:
+ dev_dbg(hw_priv->pdev, "unknown hardware version.\n");
+ return ret;
+ }
+
+ ret = request_firmware(&hw_priv->sdd, sdd_path, hw_priv->pdev);
+ if (unlikely(ret)) {
+ dev_dbg(hw_priv->pdev, "can't load sdd file %s.\n",
+ sdd_path);
+ return ret;
+ }
+
+ //parse SDD config.
+ hw_priv->is_BT_Present = false;
+ pElement = (struct xradio_sdd *)hw_priv->sdd->data;
+ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length);
+ pElement = FIND_NEXT_ELT(pElement);
+
+ while (parsedLength < hw_priv->sdd->size) {
+ switch (pElement->id) {
+ case SDD_PTA_CFG_ELT_ID:
+ hw_priv->conf_listen_interval = (*((u16 *)pElement->data+1) >> 7) & 0x1F;
+ hw_priv->is_BT_Present = true;
+ xradio_dbg(XRADIO_DBG_NIY, "PTA element found.Listen Interval %d\n",
+ hw_priv->conf_listen_interval);
+ break;
+ case SDD_REFERENCE_FREQUENCY_ELT_ID:
+ switch(*((uint16_t*)pElement->data)) {
+ case 0x32C8:
+ *dpll = 0x1D89D241;
+ break;
+ case 0x3E80:
+ *dpll = 0x1E1;
+ break;
+ case 0x41A0:
+ *dpll = 0x124931C1;
+ break;
+ case 0x4B00:
+ *dpll = 0x191;
+ break;
+ case 0x5DC0:
+ *dpll = 0x141;
+ break;
+ case 0x6590:
+ *dpll = 0x0EC4F121;
+ break;
+ case 0x8340:
+ *dpll = 0x92490E1;
+ break;
+ case 0x9600:
+ *dpll = 0x100010C1;
+ break;
+ case 0x9C40:
+ *dpll = 0xC1;
+ break;
+ case 0xBB80:
+ *dpll = 0xA1;
+ break;
+ case 0xCB20:
+ *dpll = 0x7627091;
+ break;
+ default:
+ *dpll = DPLL_INIT_VAL_XRADIO;
+ xradio_dbg(XRADIO_DBG_WARN, "Unknown Reference clock frequency."
+ "Use default DPLL value=0x%08x.", DPLL_INIT_VAL_XRADIO);
+ break;
+ }
+ default:
+ break;
+ }
+ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length);
+ pElement = FIND_NEXT_ELT(pElement);
+ }
+
+ dev_dbg(hw_priv->pdev, "sdd size=%d parse len=%d.\n",
+ hw_priv->sdd->size, parsedLength);
+
+ //
+ if (hw_priv->is_BT_Present == false) {
+ hw_priv->conf_listen_interval = 0;
+ xradio_dbg(XRADIO_DBG_NIY, "PTA element NOT found.\n");
+ }
+ return ret;
+}
+
+static int xradio_firmware(struct xradio_common *hw_priv)
+{
+ int ret, block, num_blocks;
+ unsigned i;
+ u32 val32;
+ u32 put = 0, get = 0;
+ u8 *buf = NULL;
+ const char *fw_path;
+ const struct firmware *firmware = NULL;
+
+ switch (hw_priv->hw_revision) {
+ case XR819_HW_REV0:
+ fw_path = XR819_FIRMWARE;
+ break;
+ default:
+ dev_dbg(hw_priv->pdev, "invalid silicon revision %d.\n",
+ hw_priv->hw_revision);
+ return -EINVAL;
+ }
+ /* Initialize common registers */
+ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE);
+ APB_WRITE(DOWNLOAD_PUT_REG, 0);
+ APB_WRITE(DOWNLOAD_GET_REG, 0);
+ APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING);
+ APB_WRITE(DOWNLOAD_FLAGS_REG, 0);
+
+ /* Release CPU from RESET */
+ REG_READ(HIF_CONFIG_REG_ID, val32);
+ val32 &= ~HIF_CONFIG_CPU_RESET_BIT;
+ REG_WRITE(HIF_CONFIG_REG_ID, val32);
+
+ /* Enable Clock */
+ val32 &= ~HIF_CONFIG_CPU_CLK_DIS_BIT;
+ REG_WRITE(HIF_CONFIG_REG_ID, val32);
+
+ /* Load a firmware file */
+ ret = request_firmware(&firmware, fw_path, hw_priv->pdev);
+ if (ret) {
+ dev_dbg(hw_priv->pdev, "can't load firmware file %s.\n",
+ fw_path);
+ goto error;
+ }
+ SYS_BUG(!firmware->data);
+
+ buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL);
+ if (!buf) {
+ dev_dbg(hw_priv->pdev, "can't allocate firmware buffer.\n");
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ /* Check if the bootloader is ready */
+ for (i = 0; i < 100; i++/*= 1 + i / 2*/) {
+ APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32);
+ if (val32 == DOWNLOAD_I_AM_HERE)
+ break;
+ mdelay(10);
+ } /* End of for loop */
+ if (val32 != DOWNLOAD_I_AM_HERE) {
+ dev_dbg(hw_priv->pdev, "bootloader is not ready.\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* Calculcate number of download blocks */
+ num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1;
+
+ /* Updating the length in Download Ctrl Area */
+ val32 = firmware->size; /* Explicit cast from size_t to u32 */
+ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32);
+
+ /* Firmware downloading loop */
+ for (block = 0; block < num_blocks ; block++) {
+ size_t tx_size;
+ size_t block_size;
+
+ /* check the download status */
+ APB_READ(DOWNLOAD_STATUS_REG, val32);
+ if (val32 != DOWNLOAD_PENDING) {
+ dev_dbg(hw_priv->pdev, "bootloader reported error %d.\n",
+ val32);
+ ret = -EIO;
+ goto error;
+ }
+
+ /* loop until put - get <= 24K */
+ for (i = 0; i < 100; i++) {
+ APB_READ(DOWNLOAD_GET_REG, get);
+ if ((put - get) <= (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE))
+ break;
+ mdelay(i);
+ }
+
+ if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) {
+ dev_dbg(hw_priv->pdev, "Timeout waiting for FIFO.\n");
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* calculate the block size */
+ tx_size = block_size = min((size_t)(firmware->size - put), (size_t)DOWNLOAD_BLOCK_SIZE);
+ memcpy(buf, &firmware->data[put], block_size);
+
+ if (block_size < DOWNLOAD_BLOCK_SIZE) {
+ memset(&buf[block_size], 0, DOWNLOAD_BLOCK_SIZE - block_size);
+ tx_size = DOWNLOAD_BLOCK_SIZE;
+ }
+
+ /* send the block to sram */
+ ret = xradio_apb_write(hw_priv, APB_ADDR(DOWNLOAD_FIFO_OFFSET + (put & (DOWNLOAD_FIFO_SIZE - 1))),
+ buf, tx_size);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't write block at line %d.\n", __func__, __LINE__);
+ goto error;
+ }
+
+ /* update the put register */
+ put += block_size;
+ APB_WRITE(DOWNLOAD_PUT_REG, put);
+ } /* End of firmware download loop */
+
+ /* Wait for the download completion */
+ for (i = 0; i < 300; i += 1 + i / 2) {
+ APB_READ(DOWNLOAD_STATUS_REG, val32);
+ if (val32 != DOWNLOAD_PENDING)
+ break;
+ mdelay(i);
+ }
+ if (val32 != DOWNLOAD_SUCCESS) {
+ dev_dbg(hw_priv->pdev, "wait for download completion failed. " \
+ "Read: 0x%.8X\n", val32);
+ ret = -ETIMEDOUT;
+ goto error;
+ } else {
+ dev_dbg(hw_priv->pdev, "Firmware completed.\n");
+ ret = 0;
+ }
+
+error:
+ if(buf)
+ kfree(buf);
+ if (firmware) {
+ release_firmware(firmware);
+ }
+ return ret;
+}
+
+static int xradio_bootloader(struct xradio_common *hw_priv)
+{
+ const char *bl_path = XR819_BOOTLOADER;
+ u32 addr = AHB_MEMORY_ADDRESS;
+ int ret;
+ u32 i;
+ u32 *data;
+ const struct firmware *bootloader;
+
+ /* Load a bootloader file */
+ ret = request_firmware(&bootloader, bl_path, hw_priv->pdev);
+ if (ret) {
+ dev_dbg(hw_priv->pdev, "can't load bootloader file %s.\n",
+ bl_path);
+ goto error;
+ }
+
+ /* Down bootloader. */
+ data = (u32 *)bootloader->data;
+ for(i = 0; i < (bootloader->size)/4; i++, addr+=4) {
+ REG_WRITE(HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ REG_WRITE(HIF_AHB_DPORT_REG_ID,data[i]);
+ }
+ dev_dbg(hw_priv->pdev, "Bootloader complete\n");
+
+error:
+ if(bootloader) {
+ release_firmware(bootloader);
+ }
+ return ret;
+}
+
+bool test_retry = false;
+int xradio_load_firmware(struct xradio_common *hw_priv)
+{
+ int ret;
+ int i;
+ u32 val32;
+ u16 val16;
+ u32 dpll = 0;
+ int major_revision;
+
+ /* Read CONFIG Register Value - We will read 32 bits */
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "can't read config register, err=%d.\n",
+ ret);
+ return ret;
+ }
+
+ //check hardware type and revision.
+ hw_priv->hw_type = xradio_get_hw_type(val32, &major_revision);
+ switch (hw_priv->hw_type) {
+ case HIF_HW_TYPE_XRADIO:
+ dev_dbg(hw_priv->pdev, "HW_TYPE_XRADIO detected.\n");
+ break;
+ default:
+ dev_dbg(hw_priv->pdev, "Unknown hardware: %d.\n",
+ hw_priv->hw_type);
+ return -ENOTSUPP;
+ }
+ if (major_revision == 4) {
+ hw_priv->hw_revision = XR819_HW_REV0;
+ dev_dbg(hw_priv->pdev, "XRADIO_HW_REV 1.0 detected.\n");
+ } else {
+ dev_dbg(hw_priv->pdev, "Unsupported major revision %d.\n",
+ major_revision);
+ return -ENOTSUPP;
+ }
+
+ //load sdd file, and get config from it.
+ ret = xradio_parse_sdd(hw_priv, &dpll);
+ if (ret < 0) {
+ return ret;
+ }
+
+ //set dpll initial value and check.
+ ret = xradio_reg_write_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, dpll);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "can't write DPLL register.\n");
+ goto out;
+ }
+ msleep(5);
+ ret = xradio_reg_read_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, &val32);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "can't read DPLL register.\n");
+ goto out;
+ }
+ if (val32 != dpll) {
+ dev_dbg(hw_priv->pdev, "unable to initialise " \
+ "DPLL register. Wrote 0x%.8X, read 0x%.8X.\n",
+ dpll, val32);
+ ret = -EIO;
+ goto out;
+ }
+
+ /* Set wakeup bit in device */
+ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &val16);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "set_wakeup: can't read control register.\n");
+ goto out;
+ }
+ ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, val16 | HIF_CTRL_WUP_BIT);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "set_wakeup: can't write control register.\n");
+ goto out;
+ }
+
+ /* Wait for wakeup */
+ for (i = 0 ; i < 300 ; i += 1 + i / 2) {
+ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &val16);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "Wait_for_wakeup: "
+ "can't read control register.\n");
+ goto out;
+ }
+ if (val16 & HIF_CTRL_RDY_BIT) {
+ break;
+ }
+ msleep(i);
+ }
+ if ((val16 & HIF_CTRL_RDY_BIT) == 0) {
+ dev_dbg(hw_priv->pdev, "Wait for wakeup:"
+ "device is not responding.\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ } else {
+ dev_dbg(hw_priv->pdev, "WLAN device is ready.\n");
+ }
+
+ /* Checking for access mode and download firmware. */
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "check_access_mode: "
+ "can't read config register.\n");
+ goto out;
+ }
+ if (val32 & HIF_CONFIG_ACCESS_MODE_BIT) {
+ /* Down bootloader. */
+ ret = xradio_bootloader(hw_priv);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "can't download bootloader.\n");
+ goto out;
+ }
+ /* Down firmware. */
+ ret = xradio_firmware(hw_priv);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "can't download firmware.\n");
+ goto out;
+ }
+ } else {
+ dev_dbg(hw_priv->pdev, "check_access_mode: "
+ "device is already in QUEUE mode.\n");
+ /* TODO: verify this branch. Do we need something to do? */
+ }
+
+ if (HIF_HW_TYPE_XRADIO == hw_priv->hw_type) {
+ /* If device is XRADIO the IRQ enable/disable bits
+ * are in CONFIG register */
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "enable_irq: can't read " \
+ "config register.\n");
+ goto unsubscribe;
+ }
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 | HIF_CONF_IRQ_RDY_ENABLE);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "enable_irq: can't write " \
+ "config register.\n");
+ goto unsubscribe;
+ }
+ } else {
+ /* If device is XRADIO the IRQ enable/disable bits
+ * are in CONTROL register */
+ /* Enable device interrupts - Both DATA_RDY and WLAN_RDY */
+ ret = xradio_reg_read_16(hw_priv, HIF_CONFIG_REG_ID, &val16);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "enable_irq: can't read " \
+ "control register.\n");
+ goto unsubscribe;
+ }
+ ret = xradio_reg_write_16(hw_priv, HIF_CONFIG_REG_ID,
+ val16 | HIF_CTRL_IRQ_RDY_ENABLE);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "enable_irq: can't write " \
+ "control register.\n");
+ goto unsubscribe;
+ }
+
+ }
+
+ /* Configure device for MESSSAGE MODE */
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "set_mode: can't read config register.\n");
+ goto unsubscribe;
+ }
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 & ~HIF_CONFIG_ACCESS_MODE_BIT);
+ if (ret < 0) {
+ dev_dbg(hw_priv->pdev, "set_mode: can't write config register.\n");
+ goto unsubscribe;
+ }
+
+ /* Unless we read the CONFIG Register we are
+ * not able to get an interrupt */
+ mdelay(10);
+ xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ return 0;
+
+unsubscribe:
+out:
+ if (hw_priv->sdd) {
+ release_firmware(hw_priv->sdd);
+ hw_priv->sdd = NULL;
+ }
+ return ret;
+}
+
+int xradio_dev_deinit(struct xradio_common *hw_priv)
+{
+ if (hw_priv->sdd) {
+ release_firmware(hw_priv->sdd);
+ hw_priv->sdd = NULL;
+ }
+ return 0;
+}
diff --git a/drivers/net/wireless/xradio/fwio.h b/drivers/net/wireless/xradio/fwio.h
new file mode 100644
index 00000000..b5efeb15
--- /dev/null
+++ b/drivers/net/wireless/xradio/fwio.h
@@ -0,0 +1,33 @@
+/*
+ * Firmware APIs for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef FWIO_H_INCLUDED
+#define FWIO_H_INCLUDED
+
+#define XR819_HW_REV0 (8190)
+#define XR819_BOOTLOADER ("xr819/boot_xr819.bin")
+#define XR819_FIRMWARE ("xr819/fw_xr819.bin")
+#define XR819_SDD_FILE ("xr819/sdd_xr819.bin")
+
+#define SDD_PTA_CFG_ELT_ID 0xEB
+#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xC5
+#define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0)
+#define FIND_NEXT_ELT(e) (struct xradio_sdd *)((u8 *)&e->data + e->length)
+struct xradio_sdd {
+ u8 id;
+ u8 length;
+ u8 data[];
+};
+
+struct xradio_common;
+int xradio_load_firmware(struct xradio_common *hw_priv);
+int xradio_dev_deinit(struct xradio_common *hw_priv);
+
+#endif
diff --git a/drivers/net/wireless/xradio/ht.h b/drivers/net/wireless/xradio/ht.h
new file mode 100644
index 00000000..f2a21cce
--- /dev/null
+++ b/drivers/net/wireless/xradio/ht.h
@@ -0,0 +1,41 @@
+/*
+ * HT-related code for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef XRADIO_HT_H_INCLUDED
+#define XRADIO_HT_H_INCLUDED
+
+#include <net/mac80211.h>
+
+struct xradio_ht_oper {
+ struct ieee80211_sta_ht_cap ht_cap;
+ enum nl80211_channel_type channel_type;
+ u16 operation_mode;
+};
+
+static inline int xradio_is_ht(const struct xradio_ht_oper *ht_oper)
+{
+ return ht_oper->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int xradio_ht_greenfield(const struct xradio_ht_oper *ht_oper)
+{
+ return (xradio_is_ht(ht_oper) &&
+ (ht_oper->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+ !(ht_oper->operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT));
+}
+
+static inline int xradio_ht_ampdu_density(const struct xradio_ht_oper *ht_oper)
+{
+ if (!xradio_is_ht(ht_oper))
+ return 0;
+ return ht_oper->ht_cap.ampdu_density;
+}
+
+#endif /* XRADIO_HT_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/hwio.c b/drivers/net/wireless/xradio/hwio.c
new file mode 100644
index 00000000..a6b59175
--- /dev/null
+++ b/drivers/net/wireless/xradio/hwio.c
@@ -0,0 +1,280 @@
+/*
+ * Hardware I/O implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+
+#include "xradio.h"
+#include "hwio.h"
+#include "sdio.h"
+
+#define CHECK_ADDR_LEN 1
+
+ /* Sdio addr is 4*spi_addr */
+#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2)
+#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \
+ ((((buf_id) & 0x1F) << 7) \
+ | (((mpf) & 1) << 6) \
+ | (((rfu) & 1) << 5) \
+ | (((reg_id_ofs) & 0x1F) << 0))
+#define MAX_RETRY 3
+
+
+static int __xradio_read(struct xradio_common *hw_priv, u16 addr,
+ void *buf, size_t buf_len, int buf_id)
+{
+ u16 addr_sdio;
+ u32 sdio_reg_addr_17bit ;
+
+#if (CHECK_ADDR_LEN)
+ /* Check if buffer is aligned to 4 byte boundary */
+ if (SYS_WARN(((unsigned long)buf & 3) && (buf_len > 4))) {
+ dev_dbg(hw_priv->pdev, "buffer is not aligned.\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* Convert to SDIO Register Address */
+ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+ return sdio_data_read(hw_priv,
+ sdio_reg_addr_17bit,
+ buf, buf_len);
+}
+
+static int __xradio_write(struct xradio_common *hw_priv, u16 addr,
+ const void *buf, size_t buf_len, int buf_id)
+{
+ u16 addr_sdio;
+ u32 sdio_reg_addr_17bit ;
+
+#if (CHECK_ADDR_LEN)
+ /* Check if buffer is aligned to 4 byte boundary */
+ if (SYS_WARN(((unsigned long)buf & 3) && (buf_len > 4))) {
+ dev_err(hw_priv->pdev, "buffer is not aligned.\n");
+ return -EINVAL;
+ }
+#endif
+
+ /* Convert to SDIO Register Address */
+ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr);
+ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio);
+
+ return sdio_data_write(hw_priv,
+ sdio_reg_addr_17bit,
+ buf, buf_len);
+}
+
+static inline int __xradio_read_reg32(struct xradio_common *hw_priv,
+ u16 addr, u32 *val)
+{
+ return __xradio_read(hw_priv, addr, val, sizeof(val), 0);
+}
+
+static inline int __xradio_write_reg32(struct xradio_common *hw_priv,
+ u16 addr, u32 val)
+{
+ return __xradio_write(hw_priv, addr, &val, sizeof(val), 0);
+}
+
+int xradio_reg_read(struct xradio_common *hw_priv, u16 addr,
+ void *buf, size_t buf_len)
+{
+ int ret;
+ sdio_lock(hw_priv);
+ ret = __xradio_read(hw_priv, addr, buf, buf_len, 0);
+ sdio_unlock(hw_priv);
+ return ret;
+}
+
+int xradio_reg_write(struct xradio_common *hw_priv, u16 addr,
+ const void *buf, size_t buf_len)
+{
+ int ret;
+ sdio_lock(hw_priv);
+ ret = __xradio_write(hw_priv, addr, buf, buf_len, 0);
+ sdio_unlock(hw_priv);
+ return ret;
+}
+
+int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len)
+{
+ int ret, retry = 1;
+ sdio_lock(hw_priv);
+ {
+ int buf_id_rx = hw_priv->buf_id_rx;
+ while (retry <= MAX_RETRY) {
+ ret = __xradio_read(hw_priv, HIF_IN_OUT_QUEUE_REG_ID, buf,
+ buf_len, buf_id_rx + 1);
+ if (!ret) {
+ buf_id_rx = (buf_id_rx + 1) & 3;
+ hw_priv->buf_id_rx = buf_id_rx;
+ break;
+ } else {
+ //~dgp this retrying stuff is silly as it can crash the fw if there is nothing to read
+ dev_err(hw_priv->pdev, "data read error :%d - attempt %d of %d\n", ret, retry, MAX_RETRY);
+ retry++;
+ mdelay(1);
+ }
+ }
+ }
+ sdio_unlock(hw_priv);
+ return ret;
+}
+
+int xradio_data_write(struct xradio_common *hw_priv, const void *buf,
+ size_t buf_len)
+{
+ int ret, retry = 1;
+ sdio_lock(hw_priv);
+ {
+ int buf_id_tx = hw_priv->buf_id_tx;
+ while (retry <= MAX_RETRY) {
+ ret = __xradio_write(hw_priv, HIF_IN_OUT_QUEUE_REG_ID, buf,
+ buf_len, buf_id_tx);
+ if (!ret) {
+ buf_id_tx = (buf_id_tx + 1) & 31;
+ hw_priv->buf_id_tx = buf_id_tx;
+ break;
+ } else {
+ dev_err(hw_priv->pdev, "data write error :%d - attempt %d - %d\n", ret, retry, MAX_RETRY);
+ retry++;
+ mdelay(1);
+ }
+ }
+ }
+ sdio_unlock(hw_priv);
+ return ret;
+}
+
+int xradio_indirect_read(struct xradio_common *hw_priv, u32 addr, void *buf,
+ size_t buf_len, u32 prefetch, u16 port_addr)
+{
+ u32 val32 = 0;
+ int i, ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ dev_err(hw_priv->pdev, "Can't read more than 0xfff words.\n");
+ return -EINVAL;
+ goto out;
+ }
+
+ sdio_lock(hw_priv);
+ /* Write address */
+ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't write address register.\n");
+ goto out;
+ }
+
+ /* Read CONFIG Register Value - We will read 32 bits */
+ ret = __xradio_read_reg32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't read config register.\n");
+ goto out;
+ }
+
+ /* Set PREFETCH bit */
+ ret = __xradio_write_reg32(hw_priv, HIF_CONFIG_REG_ID, val32 | prefetch);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't write prefetch bit.\n");
+ goto out;
+ }
+
+ /* Check for PRE-FETCH bit to be cleared */
+ for (i = 0; i < 20; i++) {
+ ret = __xradio_read_reg32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't check prefetch bit.\n");
+ goto out;
+ }
+ if (!(val32 & prefetch))
+ break;
+ mdelay(i);
+ }
+
+ if (val32 & prefetch) {
+ dev_err(hw_priv->pdev, "Prefetch bit is not cleared.\n");
+ goto out;
+ }
+
+ /* Read data port */
+ ret = __xradio_read(hw_priv, port_addr, buf, buf_len, 0);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't read data port.\n");
+ goto out;
+ }
+
+out:
+ sdio_unlock(hw_priv);
+ return ret;
+}
+
+int xradio_apb_write(struct xradio_common *hw_priv, u32 addr, const void *buf,
+ size_t buf_len)
+{
+ int ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ dev_err(hw_priv->pdev, "Can't wrire more than 0xfff words.\n");
+ return -EINVAL;
+ }
+
+ sdio_lock(hw_priv);
+
+ /* Write address */
+ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't write address register.\n");
+ goto out;
+ }
+
+ /* Write data port */
+ ret = __xradio_write(hw_priv, HIF_SRAM_DPORT_REG_ID, buf, buf_len, 0);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't write data port.\n");
+ goto out;
+ }
+
+out:
+ sdio_unlock(hw_priv);
+ return ret;
+}
+
+int xradio_ahb_write(struct xradio_common *hw_priv, u32 addr, const void *buf,
+ size_t buf_len)
+{
+ int ret;
+
+ if ((buf_len / 2) >= 0x1000) {
+ dev_err(hw_priv->pdev, "Can't wrire more than 0xfff words.\n");
+ return -EINVAL;
+ }
+
+ sdio_lock(hw_priv);
+
+ /* Write address */
+ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't write address register.\n");
+ goto out;
+ }
+
+ /* Write data port */
+ ret = __xradio_write(hw_priv, HIF_AHB_DPORT_REG_ID, buf, buf_len, 0);
+ if (ret < 0) {
+ dev_err(hw_priv->pdev, "Can't write data port.\n");
+ goto out;
+ }
+
+out:
+ sdio_unlock(hw_priv);
+ return ret;
+}
diff --git a/drivers/net/wireless/xradio/hwio.h b/drivers/net/wireless/xradio/hwio.h
new file mode 100644
index 00000000..531c2b8c
--- /dev/null
+++ b/drivers/net/wireless/xradio/hwio.h
@@ -0,0 +1,229 @@
+/*
+ * hardware interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef XRADIO_HWIO_H_INCLUDED
+#define XRADIO_HWIO_H_INCLUDED
+
+/* extern */ struct xradio_common;
+
+/* DPLL initial values */
+#define DPLL_INIT_VAL_XRADIO (0x0EC4F121)
+
+/* Hardware Type Definitions */
+#define HIF_HW_TYPE_XRADIO (1)
+
+
+/* boot loader start address in SRAM */
+#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000)
+/* 32K, 0x4000 to 0xDFFF */
+#define DOWNLOAD_FIFO_OFFSET (0x00004000)
+/* 32K */
+#define DOWNLOAD_FIFO_SIZE (0x00008000)
+/* 128 bytes, 0xFF80 to 0xFFFF */
+#define DOWNLOAD_CTRL_OFFSET (0x0000FF80)
+#define DOWNLOAD_CTRL_DATA_DWORDS (32-6)
+
+/* Download control area */
+struct download_cntl_t {
+ /* size of whole firmware file (including Cheksum), host init */
+ u32 ImageSize;
+ /* downloading flags */
+ u32 Flags;
+ /* No. of bytes put into the download, init & updated by host */
+ u32 Put;
+ /* last traced program counter, last ARM reg_pc */
+ u32 TracePc;
+ /* No. of bytes read from the download, host init, device updates */
+ u32 Get;
+ /* r0, boot losader status, host init to pending, device updates */
+ u32 Status;
+ /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */
+ u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS];
+};
+
+#define DOWNLOAD_IMAGE_SIZE_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize))
+#define DOWNLOAD_FLAGS_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags))
+#define DOWNLOAD_PUT_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put))
+#define DOWNLOAD_TRACE_PC_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc))
+#define DOWNLOAD_GET_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get))
+#define DOWNLOAD_STATUS_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status))
+#define DOWNLOAD_DEBUG_DATA_REG \
+ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData))
+
+#define DOWNLOAD_DEBUG_DATA_LEN (108)
+#define DOWNLOAD_BLOCK_SIZE (1024)
+
+/* For boot loader detection */
+#define DOWNLOAD_ARE_YOU_HERE (0x87654321)
+#define DOWNLOAD_I_AM_HERE (0x12345678)
+
+/* Download error code */
+#define DOWNLOAD_PENDING (0xFFFFFFFF)
+#define DOWNLOAD_SUCCESS (0)
+#define DOWNLOAD_EXCEPTION (1)
+#define DOWNLOAD_ERR_MEM_1 (2)
+#define DOWNLOAD_ERR_MEM_2 (3)
+#define DOWNLOAD_ERR_SOFTWARE (4)
+#define DOWNLOAD_ERR_FILE_SIZE (5)
+#define DOWNLOAD_ERR_CHECKSUM (6)
+#define DOWNLOAD_ERR_OVERFLOW (7)
+#define DOWNLOAD_ERR_IMAGE (8)
+#define DOWNLOAD_ERR_HOST (9)
+#define DOWNLOAD_ERR_ABORT (10)
+
+#define SYS_BASE_ADDR_SILICON (0)
+#define AHB_MEMORY_ADDRESS (SYS_BASE_ADDR_SILICON + 0x08000000)
+#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000)
+#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON)
+#define APB_ADDR(addr) (PAC_SHARED_MEMORY_SILICON + (addr))
+
+/* ***************************************************************
+*Device register definitions
+*************************************************************** */
+/* WBF - SPI Register Addresses */
+#define HIF_ADDR_ID_BASE (0x0000)
+/* 16/32 bits */
+#define HIF_CONFIG_REG_ID (0x0000)
+/* 16/32 bits */
+#define HIF_CONTROL_REG_ID (0x0001)
+/* 16 bits, Q mode W/R */
+#define HIF_IN_OUT_QUEUE_REG_ID (0x0002)
+/* 32 bits, AHB bus R/W */
+#define HIF_AHB_DPORT_REG_ID (0x0003)
+/* 16/32 bits */
+#define HIF_SRAM_BASE_ADDR_REG_ID (0x0004)
+/* 32 bits, APB bus R/W */
+#define HIF_SRAM_DPORT_REG_ID (0x0005)
+/* 32 bits, t_settle/general */
+#define HIF_TSET_GEN_R_W_REG_ID (0x0006)
+/* 16 bits, Q mode read, no length */
+#define HIF_FRAME_OUT_REG_ID (0x0007)
+#define HIF_ADDR_ID_MAX (HIF_FRAME_OUT_REG_ID)
+
+/* WBF - Control register bit set */
+/* next o/p length, bit 11 to 0 */
+#define HIF_CTRL_NEXT_LEN_MASK (0x0FFF)
+#define HIF_CTRL_WUP_BIT (BIT(12))
+#define HIF_CTRL_RDY_BIT (BIT(13))
+#define HIF_CTRL_IRQ_ENABLE (BIT(14))
+#define HIF_CTRL_RDY_ENABLE (BIT(15))
+#define HIF_CTRL_IRQ_RDY_ENABLE (BIT(14)|BIT(15))
+
+/* SPI Config register bit set */
+#define HIF_CONFIG_FRAME_BIT (BIT(2))
+#define HIF_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4))
+#define HIF_CONFIG_WORD_MODE_1 (BIT(3))
+#define HIF_CONFIG_WORD_MODE_2 (BIT(4))
+#define HIF_CONFIG_ERROR_0_BIT (BIT(5))
+#define HIF_CONFIG_ERROR_1_BIT (BIT(6))
+#define HIF_CONFIG_ERROR_2_BIT (BIT(7))
+/* TBD: Sure??? */
+#define HIF_CONFIG_CSN_FRAME_BIT (BIT(7))
+#define HIF_CONFIG_ERROR_3_BIT (BIT(8))
+#define HIF_CONFIG_ERROR_4_BIT (BIT(9))
+/* QueueM */
+#define HIF_CONFIG_ACCESS_MODE_BIT (BIT(10))
+/* AHB bus */
+#define HIF_CONFIG_AHB_PFETCH_BIT (BIT(11))
+#define HIF_CONFIG_CPU_CLK_DIS_BIT (BIT(12))
+/* APB bus */
+#define HIF_CONFIG_PFETCH_BIT (BIT(13))
+/* cpu reset */
+#define HIF_CONFIG_CPU_RESET_BIT (BIT(14))
+#define HIF_CONFIG_CLEAR_INT_BIT (BIT(15))
+
+/* For XRADIO the IRQ Enable and Ready Bits are in CONFIG register */
+#define HIF_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17))
+
+int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len);
+int xradio_data_write(struct xradio_common *hw_priv, const void *buf, size_t buf_len);
+int xradio_reg_read(struct xradio_common *hw_priv, u16 addr, void *buf, size_t buf_len);
+int xradio_reg_write(struct xradio_common *hw_priv, u16 addr, const void *buf, size_t buf_len);
+int xradio_indirect_read(struct xradio_common *hw_priv, u32 addr, void *buf,
+ size_t buf_len, u32 prefetch, u16 port_addr);
+int xradio_apb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, size_t buf_len);
+int xradio_ahb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, size_t buf_len);
+
+
+static inline int xradio_reg_read_16(struct xradio_common *hw_priv,
+ u16 addr, u16 *val)
+{
+ int ret = 0;
+ u32 bigVal = 0;
+ ret = xradio_reg_read(hw_priv, addr, &bigVal, sizeof(bigVal));
+ *val = (u16)bigVal;
+ return ret;
+}
+
+static inline int xradio_reg_write_16(struct xradio_common *hw_priv,
+ u16 addr, u16 val)
+{
+ u32 bigVal = (u32)val;
+ return xradio_reg_write(hw_priv, addr, &bigVal, sizeof(bigVal));
+}
+
+static inline int xradio_reg_read_32(struct xradio_common *hw_priv,
+ u16 addr, u32 *val)
+{
+ return xradio_reg_read(hw_priv, addr, val, sizeof(val));
+}
+
+static inline int xradio_reg_write_32(struct xradio_common *hw_priv,
+ u16 addr, u32 val)
+{
+ return xradio_reg_write(hw_priv, addr, &val, sizeof(val));
+}
+
+static inline int xradio_apb_read(struct xradio_common *hw_priv, u32 addr,
+ void *buf, size_t buf_len)
+{
+ return xradio_indirect_read(hw_priv, addr, buf, buf_len, HIF_CONFIG_PFETCH_BIT,
+ HIF_SRAM_DPORT_REG_ID);
+}
+
+static inline int xradio_ahb_read(struct xradio_common *hw_priv, u32 addr,
+ void *buf, size_t buf_len)
+{
+ return xradio_indirect_read(hw_priv, addr, buf, buf_len, HIF_CONFIG_AHB_PFETCH_BIT,
+ HIF_AHB_DPORT_REG_ID);
+}
+
+static inline int xradio_apb_read_32(struct xradio_common *hw_priv,
+ u32 addr, u32 *val)
+{
+ return xradio_apb_read(hw_priv, addr, val, sizeof(val));
+}
+
+static inline int xradio_apb_write_32(struct xradio_common *hw_priv,
+ u32 addr, u32 val)
+{
+ return xradio_apb_write(hw_priv, addr, &val, sizeof(val));
+}
+
+static inline int xradio_ahb_read_32(struct xradio_common *hw_priv,
+ u32 addr, u32 *val)
+{
+ return xradio_ahb_read(hw_priv, addr, val, sizeof(val));
+}
+
+static inline int xradio_ahb_write_32(struct xradio_common *hw_priv,
+ u32 addr, u32 val)
+{
+ return xradio_ahb_write(hw_priv, addr, &val, sizeof(val));
+}
+
+#endif /* XRADIO_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/keys.c b/drivers/net/wireless/xradio/keys.c
new file mode 100644
index 00000000..9b3e14fd
--- /dev/null
+++ b/drivers/net/wireless/xradio/keys.c
@@ -0,0 +1,206 @@
+#include <net/mac80211.h>
+
+#include "xradio.h"
+#include "sta.h"
+#include "keys.h"
+
+int xradio_alloc_key(struct xradio_common *hw_priv)
+{
+ int idx;
+
+ idx = ffs(~hw_priv->key_map) - 1;
+ if (idx < 0 || idx > WSM_KEY_MAX_INDEX)
+ return -1;
+
+ hw_priv->key_map |= BIT(idx);
+ hw_priv->keys[idx].entryIndex = idx;
+ return idx;
+}
+
+void xradio_free_key(struct xradio_common *hw_priv, int idx)
+{
+ SYS_BUG(!(hw_priv->key_map & BIT(idx)));
+ memset(&hw_priv->keys[idx], 0, sizeof(hw_priv->keys[idx]));
+ hw_priv->key_map &= ~BIT(idx);
+}
+
+void xradio_free_keys(struct xradio_common *hw_priv)
+{
+ memset(&hw_priv->keys, 0, sizeof(hw_priv->keys));
+ hw_priv->key_map = 0;
+}
+
+int xradio_upload_keys(struct xradio_vif *priv)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ int idx, ret = 0;
+
+
+ for (idx = 0; idx <= WSM_KEY_MAX_IDX; ++idx)
+ if (hw_priv->key_map & BIT(idx)) {
+ ret = wsm_add_key(hw_priv, &hw_priv->keys[idx], priv->if_id);
+ if (ret < 0)
+ break;
+ }
+ return ret;
+}
+
+int xradio_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key)
+{
+#ifdef XRADIO_DISABLE_HW_CRYPTO
+ wiphy_info(dev->wiphy, "hw crypto is disabled, ignoring key request\n");
+ return -EOPNOTSUPP;
+#else
+ int ret = -EOPNOTSUPP;
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+
+ wiphy_info(dev->wiphy, "vif %d: set_key cmd %d\n", priv->if_id, (int) cmd);
+
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+ mutex_lock(&hw_priv->conf_mutex);
+
+ if (cmd == SET_KEY) {
+ u8 *peer_addr = NULL;
+ int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? 1 : 0;
+ int idx = xradio_alloc_key(hw_priv);
+ struct wsm_add_key *wsm_key = &hw_priv->keys[idx];
+
+ if (idx < 0) {
+ wiphy_err(dev->wiphy, "xradio_alloc_key failed!\n");
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ SYS_BUG(pairwise && !sta);
+ if (sta)
+ peer_addr = sta->addr;
+
+ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE;
+
+ priv->cipherType = key->cipher;
+ switch (key->cipher) {
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ if (key->keylen > 16) {
+ xradio_free_key(hw_priv, idx);
+ wiphy_err(dev->wiphy, "keylen too long=%d!\n", key->keylen);
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
+ memcpy(wsm_key->wepPairwiseKey.peerAddress, peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wepPairwiseKey.keyData, &key->key[0], key->keylen);
+ wsm_key->wepPairwiseKey.keyLength = key->keylen;
+ wiphy_debug(dev->wiphy, "WEP_PAIRWISE keylen=%d!\n",
+ key->keylen);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
+ memcpy(wsm_key->wepGroupKey.keyData, &key->key[0], key->keylen);
+ wsm_key->wepGroupKey.keyLength = key->keylen;
+ wsm_key->wepGroupKey.keyId = key->keyidx;
+ wiphy_debug(dev->wiphy, "WEP_GROUP keylen=%d!\n",
+ key->keylen);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_TKIP:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
+ memcpy(wsm_key->tkipPairwiseKey.peerAddress, peer_addr, ETH_ALEN);
+ memcpy(wsm_key->tkipPairwiseKey.tkipKeyData, &key->key[0], 16);
+ memcpy(wsm_key->tkipPairwiseKey.txMicKey, &key->key[16], 8);
+ memcpy(wsm_key->tkipPairwiseKey.rxMicKey, &key->key[24], 8);
+ wiphy_debug(dev->wiphy,"TKIP_PAIRWISE keylen=%d!\n",
+ key->keylen);
+ } else {
+ size_t mic_offset = (priv->mode == NL80211_IFTYPE_AP) ? 16 : 24;
+ wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
+ memcpy(wsm_key->tkipGroupKey.tkipKeyData,&key->key[0], 16);
+ memcpy(wsm_key->tkipGroupKey.rxMicKey, &key->key[mic_offset], 8);
+
+ /* TODO: Where can I find TKIP SEQ? */
+ memset(wsm_key->tkipGroupKey.rxSeqCounter, 0, 8);
+ wsm_key->tkipGroupKey.keyId = key->keyidx;
+ wiphy_debug(dev->wiphy,"TKIP_GROUP keylen=%d!\n",
+ key->keylen);
+ }
+ break;
+ case WLAN_CIPHER_SUITE_CCMP:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
+ memcpy(wsm_key->aesPairwiseKey.peerAddress, peer_addr, ETH_ALEN);
+ memcpy(wsm_key->aesPairwiseKey.aesKeyData, &key->key[0], 16);
+ wiphy_debug(dev->wiphy, "CCMP_PAIRWISE keylen=%d!\n",
+ key->keylen);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
+ memcpy(wsm_key->aesGroupKey.aesKeyData, &key->key[0], 16);
+ /* TODO: Where can I find AES SEQ? */
+ memset(wsm_key->aesGroupKey.rxSeqCounter, 0, 8);
+ wsm_key->aesGroupKey.keyId = key->keyidx;
+ wiphy_debug(dev->wiphy, "CCMP_GROUP keylen=%d!\n",
+ key->keylen);
+ }
+ break;
+#ifdef CONFIG_XRADIO_WAPI_SUPPORT
+ case WLAN_CIPHER_SUITE_SMS4:
+ if (pairwise) {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
+ memcpy(wsm_key->wapiPairwiseKey.peerAddress, peer_addr, ETH_ALEN);
+ memcpy(wsm_key->wapiPairwiseKey.wapiKeyData, &key->key[0], 16);
+ memcpy(wsm_key->wapiPairwiseKey.micKeyData, &key->key[16], 16);
+ wsm_key->wapiPairwiseKey.keyId = key->keyidx;
+ sta_printk(XRADIO_DBG_NIY,"%s: WAPI_PAIRWISE keylen=%d!\n",
+ __func__, key->keylen);
+ } else {
+ wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
+ memcpy(wsm_key->wapiGroupKey.wapiKeyData, &key->key[0], 16);
+ memcpy(wsm_key->wapiGroupKey.micKeyData, &key->key[16], 16);
+ wsm_key->wapiGroupKey.keyId = key->keyidx;
+ sta_printk(XRADIO_DBG_NIY,"%s: WAPI_GROUP keylen=%d!\n",
+ __func__, key->keylen);
+ }
+ break;
+#endif /* CONFIG_XRADIO_WAPI_SUPPORT */
+ default:
+ wiphy_err(dev->wiphy, "key->cipher unknown(%d)!\n", key->cipher);
+ xradio_free_key(hw_priv, idx);
+ ret = -EOPNOTSUPP;
+ goto finally;
+ }
+ ret = SYS_WARN(wsm_add_key(hw_priv, wsm_key, priv->if_id));
+ if (!ret)
+ key->hw_key_idx = idx;
+ else
+ xradio_free_key(hw_priv, idx);
+
+ if (!ret && (pairwise || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) &&
+ (priv->filter4.enable & 0x2))
+ xradio_set_arpreply(dev, vif);
+ } else if (cmd == DISABLE_KEY) {
+ struct wsm_remove_key wsm_key = {
+ .entryIndex = key->hw_key_idx,
+ };
+
+ if (wsm_key.entryIndex > WSM_KEY_MAX_IDX) {
+ ret = -EINVAL;
+ goto finally;
+ }
+
+ xradio_free_key(hw_priv, wsm_key.entryIndex);
+ ret = wsm_remove_key(hw_priv, &wsm_key, priv->if_id);
+ } else {
+ wiphy_err(dev->wiphy, "Unsupported command\n");
+ }
+
+finally:
+ mutex_unlock(&hw_priv->conf_mutex);
+ return ret;
+#endif // XRADIO_DISABLE_HW_CRYPTO
+}
diff --git a/drivers/net/wireless/xradio/keys.h b/drivers/net/wireless/xradio/keys.h
new file mode 100644
index 00000000..23c5880e
--- /dev/null
+++ b/drivers/net/wireless/xradio/keys.h
@@ -0,0 +1,12 @@
+#ifndef __KEYS_H_
+#define __KEYS_H_INCLUDED
+
+int xradio_alloc_key(struct xradio_common *hw_priv);
+void xradio_free_key(struct xradio_common *hw_priv, int idx);
+void xradio_free_keys(struct xradio_common *hw_priv);
+int xradio_upload_keys(struct xradio_vif *priv);
+int xradio_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+
+#endif
\ No newline at end of file
diff --git a/drivers/net/wireless/xradio/main.c b/drivers/net/wireless/xradio/main.c
new file mode 100644
index 00000000..d41a75c9
--- /dev/null
+++ b/drivers/net/wireless/xradio/main.c
@@ -0,0 +1,730 @@
+/*
+ * Main code of XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*Linux version 3.4.0 compilation*/
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/etherdevice.h>
+#include <linux/vmalloc.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <net/mac80211.h>
+#include <net/cfg80211.h>
+#include <linux/of_net.h>
+#include <linux/etherdevice.h>
+#include <linux/mmc/sdio_func.h>
+
+#include "xradio.h"
+#include "txrx.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "ap.h"
+#include "keys.h"
+#include "scan.h"
+#include "pm.h"
+#include "sdio.h"
+
+/* TODO: use rates and channels from the device */
+#define RATETAB_ENT(_rate, _rateid, _flags) \
+ { \
+ .bitrate = (_rate), \
+ .hw_value = (_rateid), \
+ .flags = (_flags), \
+ }
+
+static struct ieee80211_rate xradio_rates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 6, 0),
+ RATETAB_ENT(90, 7, 0),
+ RATETAB_ENT(120, 8, 0),
+ RATETAB_ENT(180, 9, 0),
+ RATETAB_ENT(240, 10, 0),
+ RATETAB_ENT(360, 11, 0),
+ RATETAB_ENT(480, 12, 0),
+ RATETAB_ENT(540, 13, 0),
+};
+
+static struct ieee80211_rate xradio_mcs_rates[] = {
+ RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
+ RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
+};
+
+#define xradio_g_rates (xradio_rates + 0)
+#define xradio_a_rates (xradio_rates + 4)
+#define xradio_n_rates (xradio_mcs_rates)
+
+#define xradio_g_rates_size (ARRAY_SIZE(xradio_rates))
+#define xradio_a_rates_size (ARRAY_SIZE(xradio_rates) - 4)
+#define xradio_n_rates_size (ARRAY_SIZE(xradio_mcs_rates))
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = NL80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = NL80211_BAND_5GHZ, \
+ .center_freq = 5000 + (5 * (_channel)), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel xradio_2ghz_chantable[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+#ifdef CONFIG_XRADIO_5GHZ_SUPPORT
+static struct ieee80211_channel xradio_5ghz_chantable[] = {
+ CHAN5G(34, 0), CHAN5G(36, 0),
+ CHAN5G(38, 0), CHAN5G(40, 0),
+ CHAN5G(42, 0), CHAN5G(44, 0),
+ CHAN5G(46, 0), CHAN5G(48, 0),
+ CHAN5G(52, 0), CHAN5G(56, 0),
+ CHAN5G(60, 0), CHAN5G(64, 0),
+ CHAN5G(100, 0), CHAN5G(104, 0),
+ CHAN5G(108, 0), CHAN5G(112, 0),
+ CHAN5G(116, 0), CHAN5G(120, 0),
+ CHAN5G(124, 0), CHAN5G(128, 0),
+ CHAN5G(132, 0), CHAN5G(136, 0),
+ CHAN5G(140, 0), CHAN5G(149, 0),
+ CHAN5G(153, 0), CHAN5G(157, 0),
+ CHAN5G(161, 0), CHAN5G(165, 0),
+ CHAN5G(184, 0), CHAN5G(188, 0),
+ CHAN5G(192, 0), CHAN5G(196, 0),
+ CHAN5G(200, 0), CHAN5G(204, 0),
+ CHAN5G(208, 0), CHAN5G(212, 0),
+ CHAN5G(216, 0),
+};
+#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */
+
+static struct ieee80211_supported_band xradio_band_2ghz = {
+ .channels = xradio_2ghz_chantable,
+ .n_channels = ARRAY_SIZE(xradio_2ghz_chantable),
+ .bitrates = xradio_g_rates,
+ .n_bitrates = xradio_g_rates_size,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT),
+ .ht_supported = 1,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask[0] = 0xFF,
+ .rx_highest = __cpu_to_le16(0x41),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+
+#ifdef CONFIG_XRADIO_5GHZ_SUPPORT
+static struct ieee80211_supported_band xradio_band_5ghz = {
+ .channels = xradio_5ghz_chantable,
+ .n_channels = ARRAY_SIZE(xradio_5ghz_chantable),
+ .bitrates = xradio_a_rates,
+ .n_bitrates = xradio_a_rates_size,
+ .ht_cap = {
+ .cap = IEEE80211_HT_CAP_GRN_FLD |
+ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT),
+ .ht_supported = 1,
+ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K,
+ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
+ .mcs = {
+ .rx_mask[0] = 0xFF,
+ .rx_highest = __cpu_to_le16(0x41),
+ .tx_params = IEEE80211_HT_MCS_TX_DEFINED,
+ },
+ },
+};
+#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */
+
+static const unsigned long xradio_ttl[] = {
+ 1 * HZ, /* VO */
+ 2 * HZ, /* VI */
+ 5 * HZ, /* BE */
+ 10 * HZ /* BK */
+};
+
+static const struct ieee80211_ops xradio_ops = {
+ .start = xradio_start,
+ .stop = xradio_stop,
+ .add_interface = xradio_add_interface,
+ .remove_interface = xradio_remove_interface,
+ .change_interface = xradio_change_interface,
+ .tx = xradio_tx,
+ .hw_scan = xradio_hw_scan,
+#ifdef ROAM_OFFLOAD
+ .sched_scan_start = xradio_hw_sched_scan_start,
+ .sched_scan_stop = xradio_hw_sched_scan_stop,
+#endif /*ROAM_OFFLOAD*/
+ .set_tim = xradio_set_tim,
+ .sta_notify = xradio_sta_notify,
+ .sta_add = xradio_sta_add,
+ .sta_remove = xradio_sta_remove,
+ .set_key = xradio_set_key,
+ .set_rts_threshold = xradio_set_rts_threshold,
+ .config = xradio_config,
+ .bss_info_changed = xradio_bss_info_changed,
+ .prepare_multicast = xradio_prepare_multicast,
+ .configure_filter = xradio_configure_filter,
+ .conf_tx = xradio_conf_tx,
+ .get_stats = xradio_get_stats,
+ .ampdu_action = xradio_ampdu_action,
+ .flush = xradio_flush,
+#ifdef CONFIG_PM
+ .suspend = xradio_wow_suspend,
+ .resume = xradio_wow_resume,
+#endif /* CONFIG_PM */
+ /* Intentionally not offloaded: */
+ /*.channel_switch = xradio_channel_switch, */
+ .remain_on_channel = xradio_remain_on_channel,
+ .cancel_remain_on_channel = xradio_cancel_remain_on_channel,
+#ifdef CONFIG_XRADIO_TESTMODE
+ .testmode_cmd = xradio_testmode_cmd,
+#endif /* CONFIG_XRADIO_TESTMODE */
+};
+
+
+/*************************************** functions ***************************************/
+
+static void xradio_set_ifce_comb(struct xradio_common *hw_priv,
+ struct ieee80211_hw *hw)
+{
+#ifdef P2P_MULTIVIF
+ hw_priv->if_limits1[0].max = 2;
+#else
+ hw_priv->if_limits1[0].max = 1;
+#endif
+
+ hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION);
+ hw_priv->if_limits1[1].max = 1;
+ hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP);
+
+#ifdef P2P_MULTIVIF
+ hw_priv->if_limits2[0].max = 3;
+#else
+ hw_priv->if_limits2[0].max = 2;
+#endif
+ hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION);
+
+#ifdef P2P_MULTIVIF
+ hw_priv->if_limits3[0].max = 2;
+#else
+ hw_priv->if_limits3[0].max = 1;
+#endif
+
+ hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION);
+ hw_priv->if_limits3[1].max = 1;
+ hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+
+ /* TODO:COMBO: mac80211 doesn't yet support more than 1
+ * different channel */
+ hw_priv->if_combs[0].num_different_channels = 1;
+#ifdef P2P_MULTIVIF
+ hw_priv->if_combs[0].max_interfaces = 3;
+#else
+ hw_priv->if_combs[0].max_interfaces = 2;
+#endif
+ hw_priv->if_combs[0].limits = hw_priv->if_limits1;
+ hw_priv->if_combs[0].n_limits = 2;
+
+ hw_priv->if_combs[1].num_different_channels = 1;
+
+#ifdef P2P_MULTIVIF
+ hw_priv->if_combs[1].max_interfaces = 3;
+#else
+ hw_priv->if_combs[1].max_interfaces = 2;
+#endif
+ hw_priv->if_combs[1].limits = hw_priv->if_limits2;
+ hw_priv->if_combs[1].n_limits = 1;
+
+ hw_priv->if_combs[2].num_different_channels = 1;
+#ifdef P2P_MULTIVIF
+ hw_priv->if_combs[2].max_interfaces = 3;
+#else
+ hw_priv->if_combs[2].max_interfaces = 2;
+#endif
+ hw_priv->if_combs[2].limits = hw_priv->if_limits3;
+ hw_priv->if_combs[2].n_limits = 2;
+
+ hw->wiphy->iface_combinations = &hw_priv->if_combs[0];
+ hw->wiphy->n_iface_combinations = 3;
+}
+
+struct ieee80211_hw *xradio_init_common(size_t hw_priv_data_len)
+{
+ int i;
+ struct ieee80211_hw *hw;
+ struct xradio_common *hw_priv;
+ struct ieee80211_supported_band *sband;
+ int band;
+
+ /* Alloc ieee_802.11 hw and xradio_common struct. */
+ hw = ieee80211_alloc_hw(hw_priv_data_len, &xradio_ops);
+ if (!hw)
+ return NULL;
+ hw_priv = hw->priv;
+ memset(hw_priv, 0, sizeof(*hw_priv));
+
+ /* Initialize members of hw_priv. */
+ hw_priv->hw = hw;
+ hw_priv->if_id_slot = 0;
+ hw_priv->roc_if_id = -1;
+ atomic_set(&hw_priv->num_vifs, 0);
+ /* initial rates and channels TODO: fetch from FW */
+ hw_priv->rates = xradio_rates;
+ hw_priv->mcs_rates = xradio_n_rates;
+#ifdef ROAM_OFFLOAD
+ hw_priv->auto_scanning = 0;
+ hw_priv->frame_rcvd = 0;
+ hw_priv->num_scanchannels = 0;
+ hw_priv->num_2g_channels = 0;
+ hw_priv->num_5g_channels = 0;
+#endif /*ROAM_OFFLOAD*/
+#ifdef AP_AGGREGATE_FW_FIX
+ /* Enable block ACK for 4 TID (BE,VI,VI,VO). */
+ hw_priv->ba_tid_mask = 0xB1; /*due to HW limitations*/
+#else
+ /* Enable block ACK for every TID but voice. */
+ hw_priv->ba_tid_mask = 0x3F;
+#endif
+ hw_priv->noise = -94;
+ /* hw_priv->beacon_req_id = cpu_to_le32(0); */
+
+ /* Initialize members of ieee80211_hw, it works in UMAC. */
+ hw->sta_data_size = sizeof(struct xradio_sta_priv);
+ hw->vif_data_size = sizeof(struct xradio_vif);
+
+ ieee80211_hw_set(hw, SIGNAL_DBM);
+ ieee80211_hw_set(hw, SUPPORTS_PS);
+ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
+ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
+ ieee80211_hw_set(hw, CONNECTION_MONITOR);
+
+/* hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_CONNECTION_MONITOR;*/
+ //IEEE80211_HW_SUPPORTS_CQM_RSSI |
+ /* Aggregation is fully controlled by firmware.
+ * Do not need any support from the mac80211 stack */
+ /* IEEE80211_HW_AMPDU_AGGREGATION | */
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //IEEE80211_HW_SUPPORTS_P2P_PS |
+ //IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS |
+ // IEEE80211_HW_SUPPORTS_CQM_TX_FAIL |
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+ //IEEE80211_HW_BEACON_FILTER;
+
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+ BIT(NL80211_IFTYPE_ADHOC) |
+ BIT(NL80211_IFTYPE_AP) |
+ BIT(NL80211_IFTYPE_MESH_POINT) |
+ BIT(NL80211_IFTYPE_P2P_CLIENT) |
+ BIT(NL80211_IFTYPE_P2P_GO);
+
+ /* Support only for limited wowlan functionalities */
+ /* TODO by Icenowy: RESTORE THIS */
+/* hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT;
+ hw->wiphy->wowlan.n_patterns = 0;*/
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+ /* fix the problem that driver can not set pro-resp templet frame to fw */
+ hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
+
+#if defined(CONFIG_XRADIO_DISABLE_BEACON_HINTS)
+ hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
+#endif
+ hw->wiphy->n_addresses = XRWL_MAX_VIFS;
+ hw->wiphy->addresses = hw_priv->addresses;
+ hw->wiphy->max_remain_on_channel_duration = 500;
+ hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+ 8 /* TKIP IV */ +
+ 12 /* TKIP ICV and MIC */;
+ hw->wiphy->bands[NL80211_BAND_2GHZ] = &xradio_band_2ghz;
+#ifdef CONFIG_XRADIO_5GHZ_SUPPORT
+ hw->wiphy->bands[NL80211_BAND_5GHZ] = &xradio_band_5ghz;
+#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */
+ hw->queues = AC_QUEUE_NUM;
+ hw->max_rates = MAX_RATES_STAGE;
+ hw->max_rate_tries = MAX_RATES_RETRY;
+ /* Channel params have to be cleared before registering wiphy again */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = hw->wiphy->bands[band];
+ if (!sband)
+ continue;
+ for (i = 0; i < sband->n_channels; i++) {
+ sband->channels[i].flags = 0;
+ sband->channels[i].max_antenna_gain = 0;
+ sband->channels[i].max_power = 30;
+ }
+ }
+ /* hw_priv->channel init value is the local->oper_channel init value;when transplanting,take care */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ sband = hw->wiphy->bands[band];
+ if (!sband)
+ continue;
+ if(!hw_priv->channel){
+ hw_priv->channel = &sband->channels[2];
+ }
+ }
+ hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS;
+ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
+ SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr);
+
+ /* Initialize locks. */
+ spin_lock_init(&hw_priv->vif_list_lock);
+ mutex_init(&hw_priv->wsm_cmd_mux);
+ mutex_init(&hw_priv->conf_mutex);
+ mutex_init(&hw_priv->wsm_oper_lock);
+ atomic_set(&hw_priv->tx_lock, 0);
+ sema_init(&hw_priv->tx_lock_sem, 1);
+
+ hw_priv->workqueue = create_singlethread_workqueue(XRADIO_WORKQUEUE);
+ sema_init(&hw_priv->scan.lock, 1);
+ sema_init(&hw_priv->scan.status_lock,1);
+ INIT_WORK(&hw_priv->scan.work, xradio_scan_work);
+#ifdef ROAM_OFFLOAD
+ INIT_WORK(&hw_priv->scan.swork, xradio_sched_scan_work);
+#endif /*ROAM_OFFLOAD*/
+ INIT_DELAYED_WORK(&hw_priv->scan.probe_work, xradio_probe_work);
+ INIT_DELAYED_WORK(&hw_priv->scan.timeout, xradio_scan_timeout);
+ INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, xradio_rem_chan_timeout);
+ INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work);
+ atomic_set(&hw_priv->upload_count, 0);
+ memset(&hw_priv->connet_time, 0, sizeof(hw_priv->connet_time));
+
+ spin_lock_init(&hw_priv->event_queue_lock);
+ INIT_LIST_HEAD(&hw_priv->event_queue);
+ INIT_WORK(&hw_priv->event_handler, xradio_event_handler);
+ INIT_WORK(&hw_priv->ba_work, xradio_ba_work);
+ spin_lock_init(&hw_priv->ba_lock);
+ init_timer(&hw_priv->ba_timer);
+ hw_priv->ba_timer.data = (unsigned long)hw_priv;
+ hw_priv->ba_timer.function = xradio_ba_timer;
+
+ if (unlikely(xradio_queue_stats_init(&hw_priv->tx_queue_stats,
+ WLAN_LINK_ID_MAX,xradio_skb_dtor, hw_priv))) {
+ ieee80211_free_hw(hw);
+ return NULL;
+ }
+ for (i = 0; i < AC_QUEUE_NUM; ++i) {
+ if (unlikely(xradio_queue_init(&hw_priv->tx_queue[i],
+ &hw_priv->tx_queue_stats, i, XRWL_MAX_QUEUE_SZ, xradio_ttl[i]))) {
+ for (; i > 0; i--)
+ xradio_queue_deinit(&hw_priv->tx_queue[i - 1]);
+ xradio_queue_stats_deinit(&hw_priv->tx_queue_stats);
+ ieee80211_free_hw(hw);
+ return NULL;
+ }
+ }
+
+ init_waitqueue_head(&hw_priv->channel_switch_done);
+ init_waitqueue_head(&hw_priv->wsm_cmd_wq);
+ init_waitqueue_head(&hw_priv->wsm_startup_done);
+ init_waitqueue_head(&hw_priv->offchannel_wq);
+ hw_priv->wsm_caps.firmwareReady = 0;
+ hw_priv->driver_ready = 0;
+ hw_priv->offchannel_done = 0;
+ wsm_buf_init(&hw_priv->wsm_cmd_buf);
+ spin_lock_init(&hw_priv->wsm_cmd.lock);
+ tx_policy_init(hw_priv);
+ xradio_init_resv_skb(hw_priv);
+ /* add for setting short_frame_max_tx_count(mean wdev->retry_short) to drv,init the max_rate_tries */
+ spin_lock_bh(&hw_priv->tx_policy_cache.lock);
+ hw_priv->long_frame_max_tx_count = hw->conf.long_frame_max_tx_count;
+ hw_priv->short_frame_max_tx_count =
+ (hw->conf.short_frame_max_tx_count< 0x0F) ?
+ hw->conf.short_frame_max_tx_count : 0x0F;
+ hw_priv->hw->max_rate_tries = hw->conf.short_frame_max_tx_count;
+ spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
+
+ for (i = 0; i < XRWL_MAX_VIFS; i++)
+ hw_priv->hw_bufs_used_vif[i] = 0;
+
+#ifdef MCAST_FWDING
+ for (i = 0; i < WSM_MAX_BUF; i++)
+ wsm_init_release_buffer_request(hw_priv, i);
+ hw_priv->buf_released = 0;
+#endif
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE;
+
+#if defined(CONFIG_XRADIO_DEBUG)
+ hw_priv->wsm_enable_wsm_dumps = 0;
+ hw_priv->wsm_dump_max_size = WSM_DUMP_MAX_SIZE;
+#endif /* CONFIG_XRADIO_DEBUG */
+ hw_priv->query_packetID = 0;
+ atomic_set(&hw_priv->query_cnt, 0);
+ INIT_WORK(&hw_priv->query_work, wsm_query_work);
+
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+ atomic_set(&hw_priv->suspend_state, XRADIO_RESUME);
+#endif
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ hw_priv->test_frame.data = NULL;
+ hw_priv->test_frame.len = 0;
+ spin_lock_init(&hw_priv->tsm_lock);
+ INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout,
+ xradio_advance_scan_timeout);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+ xradio_set_ifce_comb(hw_priv, hw_priv->hw);
+
+ return hw;
+}
+
+void xradio_free_common(struct ieee80211_hw *dev)
+{
+ int i;
+ struct xradio_common *hw_priv = dev->priv;
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ kfree(hw_priv->test_frame.data);
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+ cancel_work_sync(&hw_priv->query_work);
+ del_timer_sync(&hw_priv->ba_timer);
+ mutex_destroy(&hw_priv->wsm_oper_lock);
+ mutex_destroy(&hw_priv->conf_mutex);
+ mutex_destroy(&hw_priv->wsm_cmd_mux);
+ wsm_buf_deinit(&hw_priv->wsm_cmd_buf);
+ flush_workqueue(hw_priv->workqueue);
+ destroy_workqueue(hw_priv->workqueue);
+ hw_priv->workqueue = NULL;
+
+ xradio_deinit_resv_skb(hw_priv);
+ if (hw_priv->skb_cache) {
+ dev_kfree_skb(hw_priv->skb_cache);
+ hw_priv->skb_cache = NULL;
+ }
+
+ for (i = 0; i < 4; ++i)
+ xradio_queue_deinit(&hw_priv->tx_queue[i]);
+ xradio_queue_stats_deinit(&hw_priv->tx_queue_stats);
+
+ for (i = 0; i < XRWL_MAX_VIFS; i++) {
+ kfree(hw_priv->vif_list[i]);
+ hw_priv->vif_list[i] = NULL;
+ }
+
+//fixed memory leakage by yangfh
+#ifdef MCAST_FWDING
+ wsm_deinit_release_buffer(hw_priv);
+#endif
+ /* unsigned int i; */
+ ieee80211_free_hw(dev);
+}
+
+int xradio_register_common(struct ieee80211_hw *dev)
+{
+ int err = 0;
+ struct xradio_common *hw_priv = dev->priv;
+
+ SET_IEEE80211_DEV(dev, hw_priv->pdev);
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ dev_dbg(hw_priv->pdev, "Cannot register device (%d).\n", err);
+ return err;
+ }
+ dev_dbg(hw_priv->pdev, "is registered as '%s'\n",
+ wiphy_name(dev->wiphy));
+
+ xradio_debug_init_common(hw_priv);
+ hw_priv->driver_ready = 1;
+ wake_up(&hw_priv->wsm_startup_done);
+ return 0;
+}
+
+void xradio_unregister_common(struct ieee80211_hw *dev)
+{
+ struct xradio_common *hw_priv = dev->priv;
+
+ if (wiphy_dev(dev->wiphy)) {
+ ieee80211_unregister_hw(dev);
+ SET_IEEE80211_DEV(dev, NULL);
+ xradio_debug_release_common(hw_priv);
+ }
+ hw_priv->driver_ready = 0;
+}
+
+int xradio_core_init(struct sdio_func* func)
+{
+ int err = -ENOMEM;
+ u16 ctrl_reg;
+ int if_id;
+ struct ieee80211_hw *dev;
+ struct xradio_common *hw_priv;
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+ unsigned char randomaddr[ETH_ALEN];
+ const unsigned char *addr = NULL;
+
+ //init xradio_common
+ dev = xradio_init_common(sizeof(struct xradio_common));
+ if (!dev) {
+ xradio_dbg(XRADIO_DBG_ERROR,"xradio_init_common failed\n");
+ return err;
+ }
+ hw_priv = dev->priv;
+ hw_priv->pdev = &func->dev;
+ hw_priv->sdio_func = func;
+ sdio_set_drvdata(func, hw_priv);
+
+ // fill in mac addresses
+ if (hw_priv->pdev->of_node) {
+ addr = of_get_mac_address(hw_priv->pdev->of_node);
+ }
+ if (!addr) {
+ dev_warn(hw_priv->pdev, "no mac address provided, using random\n");
+ eth_random_addr(randomaddr);
+ addr = randomaddr;
+ }
+ memcpy(hw_priv->addresses[0].addr, addr, ETH_ALEN);
+ memcpy(hw_priv->addresses[1].addr, addr, ETH_ALEN);
+ hw_priv->addresses[1].addr[5] += 0x01;
+#ifdef P2P_MULTIVIF
+ memcpy(hw_priv->addresses[2].addr, addr, ETH_ALEN);
+ hw_priv->addresses[2].addr[4] ^= 0x80;
+#endif
+
+ /* WSM callbacks. */
+ hw_priv->wsm_cbc.scan_complete = xradio_scan_complete_cb;
+ hw_priv->wsm_cbc.tx_confirm = xradio_tx_confirm_cb;
+ hw_priv->wsm_cbc.suspend_resume = xradio_suspend_resume;
+ /* hw_priv->wsm_cbc.set_pm_complete = xradio_set_pm_complete_cb; */
+ hw_priv->wsm_cbc.channel_switch = xradio_channel_switch_cb;
+
+ /*init pm and wakelock. */
+#ifdef CONFIG_PM
+ err = xradio_pm_init(&hw_priv->pm_state, hw_priv);
+ if (err) {
+ xradio_dbg(XRADIO_DBG_ERROR, "xradio_pm_init failed(%d).\n", err);
+ goto err2;
+ }
+#endif
+ /* Register bh thread*/
+ err = xradio_register_bh(hw_priv);
+ if (err) {
+ xradio_dbg(XRADIO_DBG_ERROR, "xradio_register_bh failed(%d).\n",
+ err);
+ goto err3;
+ }
+
+ /* Load firmware and register Interrupt Handler */
+ err = xradio_load_firmware(hw_priv);
+ if (err) {
+ xradio_dbg(XRADIO_DBG_ERROR, "xradio_load_firmware failed(%d).\n",
+ err);
+ goto err4;
+ }
+
+ /* Set sdio blocksize. */
+ sdio_lock(hw_priv);
+ SYS_WARN(sdio_set_blk_size(hw_priv,
+ SDIO_BLOCK_SIZE));
+ sdio_unlock(hw_priv);
+
+ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+ hw_priv->wsm_caps.firmwareReady, 3*HZ) <= 0) {
+
+ /* TODO: Needs to find how to reset device */
+ /* in QUEUE mode properly. */
+ xradio_dbg(XRADIO_DBG_ERROR, "Firmware Startup Timeout!\n");
+ err = -ETIMEDOUT;
+ goto err5;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY,"Firmware Startup Done.\n");
+
+ /* Keep device wake up. */
+ SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT));
+ if (xradio_reg_read_16(hw_priv,HIF_CONTROL_REG_ID, &ctrl_reg))
+ SYS_WARN(xradio_reg_read_16(hw_priv,HIF_CONTROL_REG_ID, &ctrl_reg));
+ SYS_WARN(!(ctrl_reg & HIF_CTRL_RDY_BIT));
+
+ /* Set device mode parameter. */
+ for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); if_id++) {
+ /* Set low-power mode. */
+ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, if_id));
+ /* Enable multi-TX confirmation */
+ SYS_WARN(wsm_use_multi_tx_conf(hw_priv, true, if_id));
+ }
+
+ /* Register wireless net device. */
+ err = xradio_register_common(dev);
+ if (err) {
+ xradio_dbg(XRADIO_DBG_ERROR,"xradio_register_common failed(%d)!\n", err);
+ goto err5;
+ }
+
+ return err;
+
+err5:
+ xradio_dev_deinit(hw_priv);
+err4:
+ xradio_unregister_bh(hw_priv);
+err3:
+ xradio_pm_deinit(&hw_priv->pm_state);
+err2:
+err1:
+ xradio_free_common(dev);
+ return err;
+}
+
+void xradio_core_deinit(struct sdio_func* func)
+{
+ struct xradio_common* hw_priv = sdio_get_drvdata(func);
+ if (hw_priv) {
+ xradio_unregister_common(hw_priv->hw);
+ xradio_dev_deinit(hw_priv);
+ xradio_unregister_bh(hw_priv);
+ xradio_pm_deinit(&hw_priv->pm_state);
+ xradio_free_common(hw_priv->hw);
+ }
+ return;
+}
diff --git a/drivers/net/wireless/xradio/main.h b/drivers/net/wireless/xradio/main.h
new file mode 100644
index 00000000..2238d751
--- /dev/null
+++ b/drivers/net/wireless/xradio/main.h
@@ -0,0 +1,7 @@
+#ifndef __XRADIO_MAIN_H
+#define __XRADIO_MAIN_H
+
+int xradio_core_init(struct sdio_func* func);
+void xradio_core_deinit(struct sdio_func* func);
+
+#endif
\ No newline at end of file
diff --git a/drivers/net/wireless/xradio/module.c b/drivers/net/wireless/xradio/module.c
new file mode 100644
index 00000000..83d4d7db
--- /dev/null
+++ b/drivers/net/wireless/xradio/module.c
@@ -0,0 +1,29 @@
+#include <linux/module.h>
+
+#include "xradio.h"
+#include "debug.h"
+#include "sdio.h"
+
+MODULE_AUTHOR("XRadioTech");
+MODULE_DESCRIPTION("XRadioTech WLAN driver core");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xradio_core");
+
+/* Init Module function -> Called by insmod */
+static int __init xradio_core_entry(void)
+{
+ int ret = 0;
+ ret = xradio_host_dbg_init();
+ ret = xradio_sdio_register();
+ return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit xradio_core_exit(void)
+{
+ xradio_sdio_unregister();
+ xradio_host_dbg_deinit();
+}
+
+module_init(xradio_core_entry);
+module_exit(xradio_core_exit);
diff --git a/drivers/net/wireless/xradio/pm.c b/drivers/net/wireless/xradio/pm.c
new file mode 100644
index 00000000..49d171e2
--- /dev/null
+++ b/drivers/net/wireless/xradio/pm.c
@@ -0,0 +1,812 @@
+/*
+ * PM implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/if_ether.h>
+#include "xradio.h"
+#include "pm.h"
+#include "sta.h"
+#include "bh.h"
+#include "sdio.h"
+
+#define XRADIO_BEACON_SKIPPING_MULTIPLIER 3
+
+struct xradio_udp_port_filter {
+ struct wsm_udp_port_filter_hdr hdr;
+ struct wsm_udp_port_filter dhcp;
+ struct wsm_udp_port_filter upnp;
+} __packed;
+
+struct xradio_ether_type_filter {
+ struct wsm_ether_type_filter_hdr hdr;
+ struct wsm_ether_type_filter ip;
+ struct wsm_ether_type_filter pae;
+ struct wsm_ether_type_filter wapi;
+} __packed;
+
+static struct xradio_udp_port_filter xradio_udp_port_filter_on = {
+ .hdr.nrFilters = 2,
+ .dhcp = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_OUT,
+ .portType = WSM_FILTER_PORT_TYPE_DST,
+ .udpPort = __cpu_to_le16(67),
+ },
+ .upnp = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_OUT,
+ .portType = WSM_FILTER_PORT_TYPE_DST,
+ .udpPort = __cpu_to_le16(1900),
+ },
+ /* Please add other known ports to be filtered out here and
+ * update nrFilters field in the header.
+ * Up to 4 filters are allowed. */
+};
+
+static struct wsm_udp_port_filter_hdr xradio_udp_port_filter_off = {
+ .nrFilters = 0,
+};
+
+#ifndef ETH_P_WAPI
+#define ETH_P_WAPI 0x88B4
+#endif
+
+#ifdef TES_P2P_000B_DISABLE_EAPOL_FILTER
+/* TES_P2P_000B WorkAround:
+ * when the link keep 10min more or less(i am not sure),
+ * wpa_s session maybe expired, and want to update group key.
+ * it will use eapol frame(802.1x,0x888E).
+ * if driver suspend, and discard eapol frame, then session end.
+ * i don't know why original code discards eapol frame in suspend.
+ * but now make this filter disable as WorkAround. wzw */
+static struct xradio_ether_type_filter xradio_ether_type_filter_on = {
+ .hdr.nrFilters = 1,
+/* .ip = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_IN,
+ .etherType = __cpu_to_le16(ETH_P_IP),
+ },*/
+/* .pae = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_IN,
+ .etherType = __cpu_to_le16(ETH_P_PAE),
+ },*/
+ .wapi = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_IN,
+ .etherType = __cpu_to_le16(ETH_P_WAPI),
+ },
+ /* Please add other known ether types to be filtered out here and
+ * update nrFilters field in the header.
+ * Up to 4 filters are allowed. */
+};
+#else
+static struct xradio_ether_type_filter xradio_ether_type_filter_on = {
+ .hdr.nrFilters = 2,
+/* .ip = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_IN,
+ .etherType = __cpu_to_le16(ETH_P_IP),
+ },*/
+ .pae = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_IN,
+ .etherType = __cpu_to_le16(ETH_P_PAE),
+ },
+ .wapi = {
+ .filterAction = WSM_FILTER_ACTION_FILTER_IN,
+ .etherType = __cpu_to_le16(ETH_P_WAPI),
+ },
+ /* Please add other known ether types to be filtered out here and
+ * update nrFilters field in the header.
+ * Up to 4 filters are allowed. */
+};
+#endif
+
+static struct wsm_ether_type_filter_hdr xradio_ether_type_filter_off = {
+ .nrFilters = 0,
+};
+
+static int xradio_suspend_late(struct device *dev);
+static void xradio_pm_release(struct device *dev);
+static int xradio_pm_probe(struct platform_device *pdev);
+static int __xradio_wow_suspend(struct xradio_vif *priv,
+ struct cfg80211_wowlan *wowlan);
+static int __xradio_wow_resume(struct xradio_vif *priv);
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+static int xradio_poweroff_suspend(struct xradio_common *hw_priv);
+static int xradio_poweroff_resume(struct xradio_common *hw_priv);
+#endif
+
+
+/* private */
+struct xradio_suspend_state {
+ unsigned long bss_loss_tmo;
+ unsigned long connection_loss_tmo;
+ unsigned long join_tmo;
+ unsigned long direct_probe;
+ unsigned long link_id_gc;
+ bool beacon_skipping;
+};
+
+static const struct dev_pm_ops xradio_pm_ops = {
+ .suspend_noirq = xradio_suspend_late,
+};
+
+static struct platform_driver xradio_power_driver = {
+ .probe = xradio_pm_probe,
+ .driver = {
+ .name = XRADIO_PM_DEVICE,
+ .pm = &xradio_pm_ops,
+ },
+};
+
+static int xradio_pm_init_common(struct xradio_pm_state *pm,
+ struct xradio_common *hw_priv)
+{
+ int ret;
+ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ spin_lock_init(&pm->lock);
+ /* Register pm driver. */
+ ret = platform_driver_register(&xradio_power_driver);
+ if (ret) {
+ pm_printk(XRADIO_DBG_ERROR, "%s:platform_driver_register failed(%d)!\n",
+ __FUNCTION__, ret);
+ return ret;
+ }
+
+ /* Add pm device. */
+ pm->pm_dev = platform_device_alloc(XRADIO_PM_DEVICE, 0);
+ if (!pm->pm_dev) {
+ pm_printk(XRADIO_DBG_ERROR, "%s:platform_device_alloc failed!\n",
+ __FUNCTION__);
+ platform_driver_unregister(&xradio_power_driver);
+ return -ENOMEM;
+ }
+ pm->pm_dev->dev.platform_data = hw_priv;
+ ret = platform_device_add(pm->pm_dev);
+ if (ret) {
+ pm_printk(XRADIO_DBG_ERROR, "%s:platform_device_add failed(%d)!\n",
+ __FUNCTION__, ret);
+ platform_driver_unregister(&xradio_power_driver);
+ kfree(pm->pm_dev);
+ pm->pm_dev = NULL;
+ }
+
+ return ret;
+}
+
+static void xradio_pm_deinit_common(struct xradio_pm_state *pm)
+{
+ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ platform_driver_unregister(&xradio_power_driver);
+ if (pm->pm_dev) {
+ pm->pm_dev->dev.platform_data = NULL;
+ platform_device_unregister(pm->pm_dev); /* kfree is already do */
+ pm->pm_dev = NULL;
+ }
+}
+
+#ifdef CONFIG_WAKELOCK
+
+int xradio_pm_init(struct xradio_pm_state *pm,
+ struct xradio_common *hw_priv)
+{
+ int ret = 0;
+ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ ret = xradio_pm_init_common(pm, hw_priv);
+ if (!ret)
+ wake_lock_init(&pm->wakelock, WAKE_LOCK_SUSPEND, XRADIO_WAKE_LOCK);
+ else
+ pm_printk(XRADIO_DBG_ERROR,"xradio_pm_init_common failed!\n");
+ return ret;
+}
+
+void xradio_pm_deinit(struct xradio_pm_state *pm)
+{
+ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ if (wake_lock_active(&pm->wakelock))
+ wake_unlock(&pm->wakelock);
+ wake_lock_destroy(&pm->wakelock);
+ xradio_pm_deinit_common(pm);
+}
+
+void xradio_pm_stay_awake(struct xradio_pm_state *pm,
+ unsigned long tmo)
+{
+ long cur_tmo;
+ pm_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+
+ spin_lock_bh(&pm->lock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+ cur_tmo = pm->wakelock.ws.timer.expires - jiffies;
+#else
+ cur_tmo = pm->wakelock.expires - jiffies;
+#endif
+ if (!wake_lock_active(&pm->wakelock) || cur_tmo < (long)tmo)
+ wake_lock_timeout(&pm->wakelock, tmo);
+ spin_unlock_bh(&pm->lock);
+}
+void xradio_pm_lock_awake(struct xradio_pm_state *pm)
+{
+
+ spin_lock_bh(&pm->lock);
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0))
+ pm->expires_save = pm->wakelock.ws.timer.expires;
+#else
+ pm->expires_save = pm->wakelock.expires;
+#endif
+ wake_lock_timeout(&pm->wakelock, LONG_MAX);
+ spin_unlock_bh(&pm->lock);
+}
+void xradio_pm_unlock_awake(struct xradio_pm_state *pm)
+{
+
+ spin_lock_bh(&pm->lock);
+ pm->expires_save -= jiffies;
+ if (pm->expires_save)
+ wake_lock_timeout(&pm->wakelock, pm->expires_save);
+ else
+ wake_lock_timeout(&pm->wakelock, 1);
+ spin_unlock_bh(&pm->lock);
+}
+
+#else /* CONFIG_WAKELOCK */
+
+static void xradio_pm_stay_awake_tmo(unsigned long arg)
+{
+}
+
+int xradio_pm_init(struct xradio_pm_state *pm,
+ struct xradio_common *hw_priv)
+{
+ int ret = 0;
+ pm_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+
+ ret = xradio_pm_init_common(pm, hw_priv);
+ if (!ret) {
+ init_timer(&pm->stay_awake);
+ pm->stay_awake.data = (unsigned long)pm;
+ pm->stay_awake.function = xradio_pm_stay_awake_tmo;
+ } else
+ pm_printk(XRADIO_DBG_ERROR,"xradio_pm_init_common failed!\n");
+ return ret;
+}
+
+void xradio_pm_deinit(struct xradio_pm_state *pm)
+{
+ del_timer_sync(&pm->stay_awake);
+ xradio_pm_deinit_common(pm);
+}
+
+void xradio_pm_stay_awake(struct xradio_pm_state *pm,
+ unsigned long tmo)
+{
+ long cur_tmo;
+
+ spin_lock_bh(&pm->lock);
+ cur_tmo = pm->stay_awake.expires - jiffies;
+ if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo)
+ mod_timer(&pm->stay_awake, jiffies + tmo);
+ spin_unlock_bh(&pm->lock);
+}
+void xradio_pm_lock_awake(struct xradio_pm_state *pm)
+{
+
+ spin_lock_bh(&pm->lock);
+ pm->expires_save = pm->stay_awake.expires;
+ mod_timer(&pm->stay_awake, jiffies + LONG_MAX);
+ spin_unlock_bh(&pm->lock);
+}
+void xradio_pm_unlock_awake(struct xradio_pm_state *pm)
+{
+
+ spin_lock_bh(&pm->lock);
+ if (time_before(jiffies, pm->expires_save))
+ mod_timer(&pm->stay_awake, pm->expires_save);
+ else
+ mod_timer(&pm->stay_awake, jiffies + 1);
+ spin_unlock_bh(&pm->lock);
+}
+#endif /* CONFIG_WAKELOCK */
+
+static long xradio_suspend_work(struct delayed_work *work)
+{
+ int ret = cancel_delayed_work(work);
+ long tmo;
+ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__);
+
+ if (ret > 0) {
+ /* Timer is pending */
+ tmo = work->timer.expires - jiffies;
+ if (tmo < 0)
+ tmo = 0;
+ } else {
+ tmo = -1;
+ }
+ return tmo;
+}
+
+static int xradio_resume_work(struct xradio_common *hw_priv,
+ struct delayed_work *work,
+ unsigned long tmo)
+{
+ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__);
+ if ((long)tmo < 0)
+ return 1;
+
+ return queue_delayed_work(hw_priv->workqueue, work, tmo);
+}
+
+static int xradio_suspend_late(struct device *dev)
+{
+ struct xradio_common *hw_priv = dev->platform_data;
+
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+ if (XRADIO_POWEROFF_SUSP == atomic_read(&hw_priv->suspend_state)) {
+ return 0; /* we don't rx data when power down wifi.*/
+ }
+#endif
+
+ //if (atomic_read(&hw_priv->bh_rx)) {
+ // pm_printk(XRADIO_DBG_WARN, "%s: Suspend interrupted.\n", __func__);
+ // return -EAGAIN;
+ //}
+ return 0;
+}
+
+static void xradio_pm_release(struct device *dev)
+{
+ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__);
+}
+
+static int xradio_pm_probe(struct platform_device *pdev)
+{
+ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__);
+ pdev->dev.release = xradio_pm_release;
+ return 0;
+}
+
+int xradio_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv;
+ int i, ret = 0;
+
+
+ if(hw_priv->bh_error) return -EBUSY;
+ WARN_ON(!atomic_read(&hw_priv->num_vifs));
+
+ if (work_pending(&hw_priv->query_work))
+ return -EBUSY;
+
+#ifdef ROAM_OFFLOAD
+ xradio_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !priv)
+#else
+ if (!priv)
+#endif
+ continue;
+ if((priv->vif->type == NL80211_IFTYPE_STATION)
+ && (priv->join_status == XRADIO_JOIN_STATUS_STA)) {
+ down(&hw_priv->scan.lock);
+ hw_priv->scan.if_id = priv->if_id;
+ xradio_sched_scan_work(&hw_priv->scan.swork);
+ }
+ }
+#endif /*ROAM_OFFLOAD*/
+
+ /* Do not suspend when datapath is not idle */
+ if (hw_priv->tx_queue_stats.num_queued[0] +
+ hw_priv->tx_queue_stats.num_queued[1]) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of tx_queue is not empty.\n");
+ return -EBUSY;
+ }
+
+ /* Make sure there is no configuration requests in progress. */
+ if (!mutex_trylock(&hw_priv->conf_mutex)) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of configuration requests.\n");
+ return -EBUSY;
+ }
+
+ /* Make sure there is no wsm_oper_lock in progress. */
+ if (!mutex_trylock(&hw_priv->wsm_oper_lock)) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of wsm_oper_lock.\n");
+ mutex_unlock(&hw_priv->conf_mutex);
+ return -EBUSY;
+ }
+
+ /* Do not suspend when scanning or ROC*/
+ if (down_trylock(&hw_priv->scan.lock)) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of scan requests.\n");
+ goto revert1;
+ }
+
+ if (delayed_work_pending(&hw_priv->scan.probe_work)) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of probe frames tx in progress.\n");
+ goto revert2;
+ }
+
+ /* Lock TX. */
+ wsm_lock_tx_async(hw_priv);
+
+ /* Wait to avoid possible race with bh code.
+ * But do not wait too long... */
+ if (wait_event_timeout(hw_priv->bh_evt_wq,
+ !hw_priv->hw_bufs_used, HZ / 10) <= 0) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of there are frames not confirm.\n");
+ goto revert3;
+ }
+
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+// if (STANDBY_WITH_POWER_OFF == standby_level) {
+ if (1) {
+ return xradio_poweroff_suspend(hw_priv);
+ }
+#endif
+
+ xradio_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !priv)
+#else
+ if (!priv)
+#endif
+ continue;
+
+ ret = __xradio_wow_suspend(priv, wowlan);
+ if (ret) {
+ for (; i >= 0; i--) {
+ if (!hw_priv->vif_list[i])
+ continue;
+ priv = (struct xradio_vif *)hw_priv->vif_list[i]->drv_priv;
+ __xradio_wow_resume(priv);
+ }
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of __xradio_wow_suspend failed!\n");
+ goto revert3;
+ }
+ }
+
+ /* Stop serving thread */
+ if (xradio_bh_suspend(hw_priv)) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ "because of xradio_bh_suspend failed!\n");
+ xradio_wow_resume(hw);
+ return -EBUSY;
+ }
+
+ /* Enable IRQ wake */
+ ret = sdio_pm(hw_priv, true);
+ if (ret) {
+ pm_printk(XRADIO_DBG_WARN, "Don't suspend sbus pm failed\n");
+ xradio_wow_resume(hw);
+ return -EBUSY;
+ }
+
+ /* Force resume if event is coming from the device. */
+ //if (atomic_read(&hw_priv->bh_rx)) {
+ // pm_printk(XRADIO_DBG_WARN, "Don't suspend "
+ // "because of recieved rx event!\n");
+ // xradio_wow_resume(hw);
+ // return -EAGAIN;
+ //}
+ return 0;
+
+revert3:
+ wsm_unlock_tx(hw_priv);
+revert2:
+ up(&hw_priv->scan.lock);
+revert1:
+ mutex_unlock(&hw_priv->conf_mutex);
+ mutex_unlock(&hw_priv->wsm_oper_lock);
+ return -EBUSY;
+}
+
+static int __xradio_wow_suspend(struct xradio_vif *priv,
+ struct cfg80211_wowlan *wowlan)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct xradio_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
+ struct xradio_suspend_state *state;
+ int ret;
+#ifdef MCAST_FWDING
+ struct wsm_forwarding_offload fwdoffload = {
+ .fwenable = 0x1,
+ .flags = 0x1,
+ };
+#endif
+
+
+ /* Do not suspend when join work is scheduled */
+ if (work_pending(&priv->join_work)) {
+ pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend "
+ "when join work is scheduled\n", __func__);
+ goto revert1;
+ }
+
+ /* Set UDP filter */
+ wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_on.hdr,
+ priv->if_id);
+
+ /* Set ethernet frame type filter */
+ wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_on.hdr,
+ priv->if_id);
+
+ /* Set IP multicast filter */
+ wsm_set_host_sleep(hw_priv, 1, priv->if_id);
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ WARN_ON(wsm_set_keepalive_filter(priv, true));
+
+#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE
+ /* Set Multicast Address Filter */
+ if (priv->multicast_filter.numOfAddresses) {
+ priv->multicast_filter.enable = 1;
+ wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+ }
+
+ /* Set Enable Broadcast Address Filter */
+ priv->broadcast_filter.action_mode = 1;
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ priv->broadcast_filter.address_mode = 3;
+
+ xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter);
+#endif
+
+#ifdef MCAST_FWDING
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
+#endif
+
+ /* Allocate state */
+ state = kzalloc(sizeof(struct xradio_suspend_state), GFP_KERNEL);
+ if (!state) {
+ pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend "
+ "alloc xradio_suspend_state failed.\n", __func__);
+ goto revert2;
+ }
+ /* Store delayed work states. */
+ state->bss_loss_tmo = xradio_suspend_work(&priv->bss_loss_work);
+ state->connection_loss_tmo = xradio_suspend_work(&priv->connection_loss_work);
+ state->join_tmo = xradio_suspend_work(&priv->join_timeout);
+ state->link_id_gc = xradio_suspend_work(&priv->link_id_gc_work);
+
+ /* Enable beacon skipping */
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA &&
+ priv->join_dtim_period && !priv->has_multicast_subscription) {
+ state->beacon_skipping = true;
+ wsm_set_beacon_wakeup_period(hw_priv, priv->join_dtim_period,
+ XRADIO_BEACON_SKIPPING_MULTIPLIER * \
+ priv->join_dtim_period, priv->if_id);
+ }
+
+ ret = timer_pending(&priv->mcast_timeout);
+ if (ret) {
+ pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend "
+ "mcast timeout timer_pending failed.\n", __func__);
+ goto revert3;
+ }
+
+ /* Store suspend state */
+ pm_state_vif->suspend_state = state;
+
+ return 0;
+
+revert3:
+ xradio_resume_work(hw_priv, &priv->bss_loss_work, state->bss_loss_tmo);
+ xradio_resume_work(hw_priv, &priv->connection_loss_work,
+ state->connection_loss_tmo);
+ xradio_resume_work(hw_priv, &priv->join_timeout, state->join_tmo);
+ xradio_resume_work(hw_priv, &priv->link_id_gc_work, state->link_id_gc);
+ kfree(state);
+
+revert2:
+ wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_off, priv->if_id);
+ wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_off, priv->if_id);
+ wsm_set_host_sleep(hw_priv, 0, priv->if_id);
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ WARN_ON(wsm_set_keepalive_filter(priv, false));
+
+#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE
+ /* Set Multicast Address Filter */
+ if (priv->multicast_filter.numOfAddresses) {
+ priv->multicast_filter.enable = 0;
+ wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+ }
+
+ /* Set Enable Broadcast Address Filter */
+ priv->broadcast_filter.action_mode = 0;
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ priv->broadcast_filter.address_mode = 0;
+ xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter);
+#endif
+
+#ifdef MCAST_FWDING
+ fwdoffload.flags = 0x0;
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
+#endif
+
+revert1:
+ /* mutex_unlock(&hw_priv->conf_mutex); */
+ return -EBUSY;
+}
+
+int xradio_wow_resume(struct ieee80211_hw *hw)
+{
+
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv;
+ int i, ret = 0;
+
+
+ WARN_ON(!atomic_read(&hw_priv->num_vifs));
+ if(hw_priv->bh_error) return 0;
+
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+ if (XRADIO_POWEROFF_SUSP == atomic_read(&hw_priv->suspend_state)) {
+ return xradio_poweroff_resume(hw_priv);
+ }
+#endif
+
+ /* Disable IRQ wake */
+ sdio_pm(hw_priv, false);
+
+ up(&hw_priv->scan.lock);
+
+ /* Resume BH thread */
+ WARN_ON(xradio_bh_resume(hw_priv));
+
+ xradio_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !priv)
+#else
+ if (!priv)
+#endif
+ continue;
+ ret = __xradio_wow_resume(priv);
+ if (ret) {
+ pm_printk(XRADIO_DBG_ERROR, "%s:__xradio_wow_resume failed!\n", __func__);
+ break;
+ }
+ }
+
+ wsm_unlock_tx(hw_priv);
+
+ /* Unlock configuration mutex */
+ mutex_unlock(&hw_priv->conf_mutex);
+ mutex_unlock(&hw_priv->wsm_oper_lock);
+
+ return ret;
+}
+
+static int __xradio_wow_resume(struct xradio_vif *priv)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct xradio_pm_state_vif *pm_state_vif = &priv->pm_state_vif;
+ struct xradio_suspend_state *state;
+#ifdef MCAST_FWDING
+ struct wsm_forwarding_offload fwdoffload = {
+ .fwenable = 0x1,
+ .flags = 0x0,
+ };
+#endif
+
+
+ /* Restore suspend state */
+ state = pm_state_vif->suspend_state;
+ pm_state_vif->suspend_state = NULL;
+
+#ifdef ROAM_OFFLOAD
+ if((priv->vif->type == NL80211_IFTYPE_STATION)
+ && (priv->join_status == XRADIO_JOIN_STATUS_STA))
+ xradio_hw_sched_scan_stop(hw_priv);
+#endif /*ROAM_OFFLOAD*/
+
+ if (state->beacon_skipping) {
+#ifdef XRADIO_USE_LONG_DTIM_PERIOD
+ int join_dtim_period_extend;
+ if (priv->join_dtim_period <= 3) {
+ join_dtim_period_extend = priv->join_dtim_period * 3;
+ } else if (priv->join_dtim_period <= 5) {
+ join_dtim_period_extend = priv->join_dtim_period * 2;
+ } else {
+ join_dtim_period_extend = priv->join_dtim_period;
+ }
+ wsm_set_beacon_wakeup_period(hw_priv,
+ ((priv->beacon_int * join_dtim_period_extend) > MAX_BEACON_SKIP_TIME_MS ?
+ 1 : join_dtim_period_extend) , 0, priv->if_id);
+#else
+ wsm_set_beacon_wakeup_period(hw_priv, priv->beacon_int *
+ (priv->join_dtim_period > MAX_BEACON_SKIP_TIME_MS ? 1 : priv->join_dtim_period),
+ 0, priv->if_id);
+#endif
+ state->beacon_skipping = false;
+ }
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ WARN_ON(wsm_set_keepalive_filter(priv, false));
+
+#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE
+ /* Set Multicast Address Filter */
+ if (priv->multicast_filter.numOfAddresses) {
+ priv->multicast_filter.enable = 0;
+ wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+ }
+ /* Set Enable Broadcast Address Filter */
+ priv->broadcast_filter.action_mode = 0;
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ priv->broadcast_filter.address_mode = 0;
+ xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter);
+#endif
+
+#ifdef MCAST_FWDING
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP)
+ WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id));
+#endif
+
+ /* Resume delayed work */
+ xradio_resume_work(hw_priv, &priv->bss_loss_work, state->bss_loss_tmo);
+ xradio_resume_work(hw_priv, &priv->connection_loss_work,
+ state->connection_loss_tmo);
+ xradio_resume_work(hw_priv, &priv->join_timeout, state->join_tmo);
+ xradio_resume_work(hw_priv, &priv->link_id_gc_work, state->link_id_gc);
+
+ /* Remove UDP port filter */
+ wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_off, priv->if_id);
+
+ /* Remove ethernet frame type filter */
+ wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_off, priv->if_id);
+
+ /* Remove IP multicast filter */
+ wsm_set_host_sleep(hw_priv, 0, priv->if_id);
+ /* Free memory */
+ kfree(state);
+
+ return 0;
+}
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+static int xradio_poweroff_suspend(struct xradio_common *hw_priv)
+{
+
+ //flush all work.
+ cancel_work_sync(&hw_priv->query_work);
+ flush_workqueue(hw_priv->workqueue);
+ /* Schedule hardware restart, ensure no cmds in progress.*/
+ mutex_lock(&hw_priv->wsm_cmd_mux);
+ atomic_set(&hw_priv->suspend_state, XRADIO_POWEROFF_SUSP);
+ //hw_priv->hw_restart = true;
+ mutex_unlock(&hw_priv->wsm_cmd_mux);
+ /* Stop serving thread */
+ if (xradio_bh_suspend(hw_priv)) {
+ pm_printk(XRADIO_DBG_WARN, "%s, xradio_bh_suspend failed!\n", __func__);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int xradio_poweroff_resume(struct xradio_common *hw_priv)
+{
+
+ /* Revert locks */
+ wsm_unlock_tx(hw_priv);
+ up(&hw_priv->scan.lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+ mutex_unlock(&hw_priv->wsm_oper_lock);
+ //if (schedule_work(&hw_priv->hw_restart_work) <= 0)
+ // pm_printk(XRADIO_DBG_ERROR, "%s restart_work failed!\n", __func__);
+ return 0;
+}
+#endif
diff --git a/drivers/net/wireless/xradio/pm.h b/drivers/net/wireless/xradio/pm.h
new file mode 100644
index 00000000..6443cbc4
--- /dev/null
+++ b/drivers/net/wireless/xradio/pm.h
@@ -0,0 +1,64 @@
+/*
+ * power management interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#ifndef PM_H_INCLUDED
+#define PM_H_INCLUDED
+
+#ifdef CONFIG_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+#ifdef CONFIG_PM
+
+#define XRADIO_PM_DEVICE "xradio_pm"
+#define XRADIO_WAKE_LOCK "xradio_wlan"
+
+/* extern */ struct xradio_common;
+ /* private */ struct xradio_suspend_state;
+
+struct xradio_pm_state {
+#ifdef CONFIG_WAKELOCK
+ struct wake_lock wakelock;
+#else
+ struct timer_list stay_awake;
+#endif
+ struct platform_device *pm_dev;
+ spinlock_t lock;
+ unsigned long expires_save;
+};
+
+struct xradio_pm_state_vif {
+ struct xradio_suspend_state *suspend_state;
+};
+
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+enum suspend_state {
+ XRADIO_RESUME = 0,
+ XRADIO_CONNECT_SUSP,
+ XRADIO_DISCONNECT_SUSP,
+ XRADIO_POWEROFF_SUSP
+};
+#endif
+int xradio_pm_init(struct xradio_pm_state *pm, struct xradio_common *priv);
+void xradio_pm_deinit(struct xradio_pm_state *pm);
+void xradio_pm_stay_awake(struct xradio_pm_state *pm, unsigned long tmo);
+void xradio_pm_lock_awake(struct xradio_pm_state *pm);
+void xradio_pm_unlock_awake(struct xradio_pm_state *pm);
+int xradio_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
+int xradio_wow_resume(struct ieee80211_hw *hw);
+
+#endif /* CONFIG_PM */
+
+#endif
diff --git a/drivers/net/wireless/xradio/queue.c b/drivers/net/wireless/xradio/queue.c
new file mode 100644
index 00000000..1d8c824c
--- /dev/null
+++ b/drivers/net/wireless/xradio/queue.c
@@ -0,0 +1,898 @@
+/*
+ * Queue implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/sched.h>
+#include "xradio.h"
+#include "queue.h"
+#ifdef CONFIG_XRADIO_TESTMODE
+#include <linux/time.h>
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+/* private */ struct xradio_queue_item
+{
+ struct list_head head;
+ struct sk_buff *skb;
+ u32 packetID;
+ unsigned long queue_timestamp;
+ unsigned long xmit_timestamp;
+#ifdef CONFIG_XRADIO_TESTMODE
+ unsigned long mdelay_timestamp;
+ unsigned long qdelay_timestamp;
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ struct xradio_txpriv txpriv;
+ u8 generation;
+ u8 pack_stk_wr;
+};
+
+static inline void __xradio_queue_lock(struct xradio_queue *queue)
+{
+ struct xradio_queue_stats *stats = queue->stats;
+ if (queue->tx_locked_cnt++ == 0) {
+ txrx_printk(XRADIO_DBG_MSG, "[TX] Queue %d is locked.\n",
+ queue->queue_id);
+ ieee80211_stop_queue(stats->hw_priv->hw, queue->queue_id);
+ }
+}
+
+static inline void __xradio_queue_unlock(struct xradio_queue *queue)
+{
+ struct xradio_queue_stats *stats = queue->stats;
+ SYS_BUG(!queue->tx_locked_cnt);
+ if (--queue->tx_locked_cnt == 0) {
+ txrx_printk(XRADIO_DBG_MSG, "[TX] Queue %d is unlocked.\n",
+ queue->queue_id);
+ ieee80211_wake_queue(stats->hw_priv->hw, queue->queue_id);
+ }
+}
+
+static inline void xradio_queue_parse_id(u32 packetID, u8 *queue_generation,
+ u8 *queue_id,
+ u8 *item_generation,
+ u8 *item_id,
+ u8 *if_id,
+ u8 *link_id)
+{
+ *item_id = (packetID >> 0) & 0xFF;
+ *item_generation = (packetID >> 8) & 0xFF;
+ *queue_id = (packetID >> 16) & 0xF;
+ *if_id = (packetID >> 20) & 0xF;
+ *link_id = (packetID >> 24) & 0xF;
+ *queue_generation = (packetID >> 28) & 0xF;
+}
+
+static inline u32 xradio_queue_make_packet_id(u8 queue_generation, u8 queue_id,
+ u8 item_generation, u8 item_id,
+ u8 if_id, u8 link_id)
+{
+ /*TODO:COMBO: Add interfaceID to the packetID */
+ return ((u32)item_id << 0) |
+ ((u32)item_generation << 8) |
+ ((u32)queue_id << 16) |
+ ((u32)if_id << 20) |
+ ((u32)link_id << 24) |
+ ((u32)queue_generation << 28);
+}
+
+static void xradio_queue_post_gc(struct xradio_queue_stats *stats,
+ struct list_head *gc_list)
+{
+ struct xradio_queue_item *item;
+
+ while (!list_empty(gc_list)) {
+ item = list_first_entry(
+ gc_list, struct xradio_queue_item, head);
+ list_del(&item->head);
+ stats->skb_dtor(stats->hw_priv, item->skb, &item->txpriv);
+ kfree(item);
+ }
+}
+
+static void xradio_queue_register_post_gc(struct list_head *gc_list,
+ struct xradio_queue_item *item)
+{
+ struct xradio_queue_item *gc_item;
+ gc_item = kmalloc(sizeof(struct xradio_queue_item), GFP_KERNEL);
+ SYS_BUG(!gc_item);
+ memcpy(gc_item, item, sizeof(struct xradio_queue_item));
+ list_add_tail(&gc_item->head, gc_list);
+}
+
+static void __xradio_queue_gc(struct xradio_queue *queue,
+ struct list_head *head,
+ bool unlock)
+{
+ struct xradio_queue_stats *stats = queue->stats;
+ struct xradio_queue_item *item = NULL;
+ //struct xradio_vif *priv;
+ int if_id;
+ bool wakeup_stats = false;
+
+ while (!list_empty(&queue->queue)) {
+ struct xradio_txpriv *txpriv;
+ item = list_first_entry(
+ &queue->queue, struct xradio_queue_item, head);
+ if (time_before(jiffies, item->queue_timestamp+queue->ttl))
+ break;
+
+ txpriv = &item->txpriv;
+ if_id = txpriv->if_id;
+ --queue->num_queued;
+ --queue->num_queued_vif[if_id];
+ --queue->link_map_cache[if_id][txpriv->link_id];
+ spin_lock_bh(&stats->lock);
+ --stats->num_queued[if_id];
+ if (!--stats->link_map_cache[if_id][txpriv->link_id])
+ wakeup_stats = true;
+ spin_unlock_bh(&stats->lock);
+ //priv = xrwl_hwpriv_to_vifpriv(stats->hw_priv, if_id);
+ //if (priv) {
+ // xradio_debug_tx_ttl(priv);
+ // spin_unlock(&priv->vif_lock);
+ //}
+ xradio_queue_register_post_gc(head, item);
+ item->skb = NULL;
+ list_move_tail(&item->head, &queue->free_pool);
+ }
+
+ if (wakeup_stats)
+ wake_up(&stats->wait_link_id_empty);
+
+ //modified by yangfh for test WFD
+ if (queue->overfull) {
+ if (queue->num_queued <= ((stats->hw_priv->vif0_throttle +
+ stats->hw_priv->vif1_throttle+2)>>1)) {
+ queue->overfull = false;
+ if (unlock) {
+ __xradio_queue_unlock(queue);
+ }
+ } else if (item) {
+ unsigned long tmo = item->queue_timestamp + queue->ttl;
+ mod_timer(&queue->gc, tmo);
+ xradio_pm_stay_awake(&stats->hw_priv->pm_state,
+ tmo - jiffies);
+ }
+ }
+}
+
+static void xradio_queue_gc(unsigned long arg)
+{
+ LIST_HEAD(list);
+ struct xradio_queue *queue =
+ (struct xradio_queue *)arg;
+
+ spin_lock_bh(&queue->lock);
+ __xradio_queue_gc(queue, &list, true);
+ spin_unlock_bh(&queue->lock);
+ xradio_queue_post_gc(queue->stats, &list);
+}
+
+int xradio_queue_stats_init(struct xradio_queue_stats *stats,
+ size_t map_capacity,
+ xradio_queue_skb_dtor_t skb_dtor,
+ struct xradio_common *hw_priv)
+{
+ int i;
+
+ memset(stats, 0, sizeof(*stats));
+ stats->map_capacity = map_capacity;
+ stats->skb_dtor = skb_dtor;
+ stats->hw_priv = hw_priv;
+ spin_lock_init(&stats->lock);
+ init_waitqueue_head(&stats->wait_link_id_empty);
+ for (i = 0; i < XRWL_MAX_VIFS; i++) {
+ stats->link_map_cache[i] = kzalloc(sizeof(int[map_capacity]), GFP_KERNEL);
+ if (!stats->link_map_cache[i]) {
+ for (; i >= 0; i--)
+ kfree(stats->link_map_cache[i]);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+int xradio_queue_init(struct xradio_queue *queue,
+ struct xradio_queue_stats *stats,
+ u8 queue_id,
+ size_t capacity,
+ unsigned long ttl)
+{
+ int i;
+
+ memset(queue, 0, sizeof(*queue));
+ queue->stats = stats;
+ queue->capacity = capacity;
+ queue->queue_id = queue_id;
+ queue->ttl = ttl;
+ INIT_LIST_HEAD(&queue->queue);
+ INIT_LIST_HEAD(&queue->pending);
+ INIT_LIST_HEAD(&queue->free_pool);
+ spin_lock_init(&queue->lock);
+ init_timer(&queue->gc);
+ queue->gc.data = (unsigned long)queue;
+ queue->gc.function = xradio_queue_gc;
+
+ queue->pool = kzalloc(sizeof(struct xradio_queue_item) * capacity,
+ GFP_KERNEL);
+ if (!queue->pool)
+ return -ENOMEM;
+
+ for (i = 0; i < XRWL_MAX_VIFS; i++) {
+ queue->link_map_cache[i] =
+ kzalloc(sizeof(int[stats->map_capacity]), GFP_KERNEL);
+ if (!queue->link_map_cache[i]) {
+ for (; i >= 0; i--)
+ kfree(queue->link_map_cache[i]);
+ kfree(queue->pool);
+ queue->pool = NULL;
+ return -ENOMEM;
+ }
+ }
+
+ for (i = 0; i < capacity; ++i)
+ list_add_tail(&queue->pool[i].head, &queue->free_pool);
+
+ return 0;
+}
+
+/* TODO:COMBO: Flush only a particular interface specific parts */
+int xradio_queue_clear(struct xradio_queue *queue, int if_id)
+{
+ int i, cnt, iter;
+ struct xradio_queue_stats *stats = queue->stats;
+ LIST_HEAD(gc_list);
+
+ cnt = 0;
+ spin_lock_bh(&queue->lock);
+ queue->generation++;
+ queue->generation &= 0xf;
+ list_splice_tail_init(&queue->queue, &queue->pending);
+ while (!list_empty(&queue->pending)) {
+ struct xradio_queue_item *item = list_first_entry(
+ &queue->pending, struct xradio_queue_item, head);
+ SYS_WARN(!item->skb);
+ if (XRWL_ALL_IFS == if_id || item->txpriv.if_id == if_id) {
+ xradio_queue_register_post_gc(&gc_list, item);
+ item->skb = NULL;
+ list_move_tail(&item->head, &queue->free_pool);
+ cnt++;
+ }
+ }
+ queue->num_queued -= cnt;
+ queue->num_pending = 0;
+ if (XRWL_ALL_IFS != if_id) {
+ queue->num_queued_vif[if_id] = 0;
+ queue->num_pending_vif[if_id] = 0;
+ } else {
+ for (iter = 0; iter < XRWL_MAX_VIFS; iter++) {
+ queue->num_queued_vif[iter] = 0;
+ queue->num_pending_vif[iter] = 0;
+ }
+ }
+ spin_lock_bh(&stats->lock);
+ if (XRWL_ALL_IFS != if_id) {
+ for (i = 0; i < stats->map_capacity; ++i) {
+ stats->num_queued[if_id] -=
+ queue->link_map_cache[if_id][i];
+ stats->link_map_cache[if_id][i] -=
+ queue->link_map_cache[if_id][i];
+ queue->link_map_cache[if_id][i] = 0;
+ }
+ } else {
+ for (iter = 0; iter < XRWL_MAX_VIFS; iter++) {
+ for (i = 0; i < stats->map_capacity; ++i) {
+ stats->num_queued[iter] -=
+ queue->link_map_cache[iter][i];
+ stats->link_map_cache[iter][i] -=
+ queue->link_map_cache[iter][i];
+ queue->link_map_cache[iter][i] = 0;
+ }
+ }
+ }
+ spin_unlock_bh(&stats->lock);
+ if (unlikely(queue->overfull)) {
+ queue->overfull = false;
+ __xradio_queue_unlock(queue);
+ }
+ spin_unlock_bh(&queue->lock);
+ wake_up(&stats->wait_link_id_empty);
+ xradio_queue_post_gc(stats, &gc_list);
+ return 0;
+}
+
+void xradio_queue_stats_deinit(struct xradio_queue_stats *stats)
+{
+ int i;
+
+ for (i = 0; i < XRWL_MAX_VIFS ; i++) {
+ kfree(stats->link_map_cache[i]);
+ stats->link_map_cache[i] = NULL;
+ }
+}
+
+void xradio_queue_deinit(struct xradio_queue *queue)
+{
+ int i;
+
+ xradio_queue_clear(queue, XRWL_ALL_IFS);
+ del_timer_sync(&queue->gc);
+ INIT_LIST_HEAD(&queue->free_pool);
+ kfree(queue->pool);
+ for (i = 0; i < XRWL_MAX_VIFS; i++) {
+ kfree(queue->link_map_cache[i]);
+ queue->link_map_cache[i] = NULL;
+ }
+ queue->pool = NULL;
+ queue->capacity = 0;
+}
+
+size_t xradio_queue_get_num_queued(struct xradio_vif *priv,
+ struct xradio_queue *queue,
+ u32 link_id_map)
+{
+ size_t ret;
+ int i, bit;
+ size_t map_capacity = queue->stats->map_capacity;
+
+ if (!link_id_map)
+ return 0;
+
+ spin_lock_bh(&queue->lock);
+ if (likely(link_id_map == (u32) -1)) {
+ ret = queue->num_queued_vif[priv->if_id] -
+ queue->num_pending_vif[priv->if_id];
+ } else {
+ ret = 0;
+ for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) {
+ if (link_id_map & bit)
+ ret +=
+ queue->link_map_cache[priv->if_id][i];
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int xradio_queue_put(struct xradio_queue *queue, struct sk_buff *skb,
+ struct xradio_txpriv *txpriv)
+{
+ int ret = 0;
+#ifdef CONFIG_XRADIO_TESTMODE
+ struct timeval tmval;
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ LIST_HEAD(gc_list);
+ struct xradio_queue_stats *stats = queue->stats;
+ /* TODO:COMBO: Add interface ID info to queue item */
+
+ if (txpriv->link_id >= queue->stats->map_capacity)
+ return -EINVAL;
+
+ spin_lock_bh(&queue->lock);
+ if (!SYS_WARN(list_empty(&queue->free_pool))) {
+ struct xradio_queue_item *item = list_first_entry(
+ &queue->free_pool, struct xradio_queue_item, head);
+ SYS_BUG(item->skb);
+
+ list_move_tail(&item->head, &queue->queue);
+ item->skb = skb;
+ item->txpriv = *txpriv;
+ item->generation = 1; /* avoid packet ID is 0.*/
+ item->pack_stk_wr = 0;
+ item->packetID = xradio_queue_make_packet_id(
+ queue->generation, queue->queue_id,
+ item->generation, item - queue->pool,
+ txpriv->if_id, txpriv->raw_link_id);
+ item->queue_timestamp = jiffies;
+#ifdef CONFIG_XRADIO_TESTMODE
+ do_gettimeofday(&tmval);
+ item->qdelay_timestamp = tmval.tv_usec;
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ if (TES_P2P_0002_state == TES_P2P_0002_STATE_SEND_RESP) {
+ TES_P2P_0002_packet_id = item->packetID;
+ TES_P2P_0002_state = TES_P2P_0002_STATE_GET_PKTID;
+ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_GET_PKTID]\n");
+ }
+#endif
+
+ ++queue->num_queued;
+ ++queue->num_queued_vif[txpriv->if_id];
+ ++queue->link_map_cache[txpriv->if_id][txpriv->link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued[txpriv->if_id];
+ ++stats->link_map_cache[txpriv->if_id][txpriv->link_id];
+ spin_unlock_bh(&stats->lock);
+
+ /*
+ * TX may happen in parallel sometimes.
+ * Leave extra queue slots so we don't overflow.
+ */
+ if (queue->overfull == false &&
+ queue->num_queued >=
+ ((stats->hw_priv->vif0_throttle +stats->hw_priv->vif1_throttle)
+ - (num_present_cpus() - 1))) {
+ queue->overfull = true;
+ __xradio_queue_lock(queue);
+ mod_timer(&queue->gc, jiffies);
+ txrx_printk(XRADIO_DBG_NIY,"!lock queue\n");
+ }
+ } else {
+ ret = -ENOENT;
+ }
+#if 0
+ txrx_printk(XRADIO_DBG_ERROR, "queue_put queue %d, %d, %d\n",
+ queue->num_queued,
+ queue->link_map_cache[txpriv->if_id][txpriv->link_id],
+ queue->num_pending);
+ txrx_printk(XRADIO_DBG_ERROR, "queue_put stats %d, %d\n", stats->num_queued,
+ stats->link_map_cache[txpriv->if_id][txpriv->link_id]);
+#endif
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int xradio_queue_get(struct xradio_queue *queue,
+ int if_id,
+ u32 link_id_map,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info,
+ struct xradio_txpriv **txpriv)
+{
+ int ret = -ENOENT;
+ struct xradio_queue_item *item;
+ struct xradio_queue_stats *stats = queue->stats;
+ bool wakeup_stats = false;
+#ifdef CONFIG_XRADIO_TESTMODE
+ struct timeval tmval;
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+ spin_lock_bh(&queue->lock);
+ list_for_each_entry(item, &queue->queue, head) {
+ if ((item->txpriv.if_id == if_id) &&
+ (link_id_map & BIT(item->txpriv.link_id))) {
+ ret = 0;
+ break;
+ }
+ }
+
+ if (!SYS_WARN(ret)) {
+ *tx = (struct wsm_tx *)item->skb->data;
+ *tx_info = IEEE80211_SKB_CB(item->skb);
+ *txpriv = &item->txpriv;
+ (*tx)->packetID = __cpu_to_le32(item->packetID);
+ list_move_tail(&item->head, &queue->pending);
+ ++queue->num_pending;
+ ++queue->num_pending_vif[item->txpriv.if_id];
+ --queue->link_map_cache[item->txpriv.if_id]
+ [item->txpriv.link_id];
+ item->xmit_timestamp = jiffies;
+#ifdef CONFIG_XRADIO_TESTMODE
+ do_gettimeofday(&tmval);
+ item->mdelay_timestamp = tmval.tv_usec;
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+ spin_lock_bh(&stats->lock);
+ --stats->num_queued[item->txpriv.if_id];
+ if (!--stats->link_map_cache[item->txpriv.if_id]
+ [item->txpriv.link_id])
+ wakeup_stats = true;
+
+ spin_unlock_bh(&stats->lock);
+#if 0
+ txrx_printk(XRADIO_DBG_ERROR, "queue_get queue %d, %d, %d\n",
+ queue->num_queued,
+ queue->link_map_cache[item->txpriv.if_id][item->txpriv.link_id],
+ queue->num_pending);
+ txrx_printk(XRADIO_DBG_ERROR, "queue_get stats %d, %d\n", stats->num_queued,
+ stats->link_map_cache[item->txpriv.if_id]
+ [item->txpriv.link_id]);
+#endif
+ }
+ spin_unlock_bh(&queue->lock);
+ if (wakeup_stats)
+ wake_up(&stats->wait_link_id_empty);
+
+ return ret;
+}
+
+#ifdef CONFIG_XRADIO_TESTMODE
+int xradio_queue_requeue(struct xradio_common *hw_priv,
+ struct xradio_queue *queue, u32 packetID, bool check)
+#else
+int xradio_queue_requeue(struct xradio_queue *queue, u32 packetID, bool check)
+#endif
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+ struct xradio_queue_item *item;
+ struct xradio_queue_stats *stats = queue->stats;
+
+ xradio_queue_parse_id(packetID, &queue_generation, &queue_id,
+ &item_generation, &item_id, &if_id, &link_id);
+
+ item = &queue->pool[item_id];
+#ifdef P2P_MULTIVIF
+ if (check && item->txpriv.if_id == XRWL_GENERIC_IF_ID) {
+#else
+ if (check && item->txpriv.offchannel_if_id == XRWL_GENERIC_IF_ID) {
+#endif
+ txrx_printk(XRADIO_DBG_MSG, "Requeued frame dropped for "
+ "generic interface id.\n");
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_remove(hw_priv, queue, packetID);
+#else
+ xradio_queue_remove(queue, packetID);
+#endif
+ return 0;
+ }
+
+#ifndef P2P_MULTIVIF
+ if (!check)
+ item->txpriv.offchannel_if_id = XRWL_GENERIC_IF_ID;
+#endif
+
+ /*if_id = item->txpriv.if_id;*/
+
+ spin_lock_bh(&queue->lock);
+ SYS_BUG(queue_id != queue->queue_id);
+ if (unlikely(queue_generation != queue->generation)) {
+ ret = -ENOENT;
+ } else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+ SYS_WARN(1);
+ ret = -EINVAL;
+ } else if (unlikely(item->generation != item_generation)) {
+ SYS_WARN(1);
+ ret = -ENOENT;
+ } else {
+ --queue->num_pending;
+ --queue->num_pending_vif[if_id];
+ ++queue->link_map_cache[if_id][item->txpriv.link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued[item->txpriv.if_id];
+ ++stats->link_map_cache[if_id][item->txpriv.link_id];
+ spin_unlock_bh(&stats->lock);
+
+ item->generation = ++item_generation;
+ item->packetID = xradio_queue_make_packet_id(
+ queue_generation, queue_id, item_generation, item_id,
+ if_id, link_id);
+ list_move(&item->head, &queue->queue);
+#if 0
+ txrx_printk(XRADIO_DBG_ERROR, "queue_requeue queue %d, %d, %d\n",
+ queue->num_queued,
+ queue->link_map_cache[if_id][item->txpriv.link_id],
+ queue->num_pending);
+ txrx_printk(XRADIO_DBG_ERROR, "queue_requeue stats %d, %d\n",
+ stats->num_queued,
+ stats->link_map_cache[if_id][item->txpriv.link_id]);
+#endif
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+int xradio_queue_requeue_all(struct xradio_queue *queue)
+{
+ struct xradio_queue_stats *stats = queue->stats;
+ spin_lock_bh(&queue->lock);
+ while (!list_empty(&queue->pending)) {
+ struct xradio_queue_item *item = list_entry(
+ queue->pending.prev, struct xradio_queue_item, head);
+
+ --queue->num_pending;
+ --queue->num_pending_vif[item->txpriv.if_id];
+ ++queue->link_map_cache[item->txpriv.if_id]
+ [item->txpriv.link_id];
+
+ spin_lock_bh(&stats->lock);
+ ++stats->num_queued[item->txpriv.if_id];
+ ++stats->link_map_cache[item->txpriv.if_id]
+ [item->txpriv.link_id];
+ spin_unlock_bh(&stats->lock);
+
+ ++item->generation;
+ item->packetID = xradio_queue_make_packet_id(
+ queue->generation, queue->queue_id,
+ item->generation, item - queue->pool,
+ item->txpriv.if_id, item->txpriv.raw_link_id);
+ list_move(&item->head, &queue->queue);
+ }
+ spin_unlock_bh(&queue->lock);
+
+ return 0;
+}
+#ifdef CONFIG_XRADIO_TESTMODE
+int xradio_queue_remove(struct xradio_common *hw_priv,
+ struct xradio_queue *queue, u32 packetID)
+#else
+int xradio_queue_remove(struct xradio_queue *queue, u32 packetID)
+#endif /*CONFIG_XRADIO_TESTMODE*/
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+ struct xradio_queue_item *item;
+ struct xradio_queue_stats *stats = queue->stats;
+ struct sk_buff *gc_skb = NULL;
+ struct xradio_txpriv gc_txpriv;
+
+ xradio_queue_parse_id(packetID, &queue_generation, &queue_id,
+ &item_generation, &item_id, &if_id, &link_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ SYS_BUG(queue_id != queue->queue_id);
+ /*TODO:COMBO:Add check for interface ID also */
+ if (unlikely(queue_generation != queue->generation)) {
+ ret = -ENOENT;
+ } else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+ SYS_WARN(1);
+ ret = -EINVAL;
+ } else if (unlikely(item->generation != item_generation)) {
+ SYS_WARN(1);
+ ret = -ENOENT;
+ } else {
+ gc_txpriv = item->txpriv;
+ gc_skb = item->skb;
+ item->skb = NULL;
+ --queue->num_pending;
+ --queue->num_pending_vif[if_id];
+ --queue->num_queued;
+ --queue->num_queued_vif[if_id];
+ ++queue->num_sent;
+ ++item->generation;
+#ifdef CONFIG_XRADIO_TESTMODE
+ spin_lock_bh(&hw_priv->tsm_lock);
+ if (hw_priv->start_stop_tsm.start) {
+ if (queue_id == hw_priv->tsm_info.ac) {
+ struct timeval tmval;
+ unsigned long queue_delay;
+ unsigned long media_delay;
+ do_gettimeofday(&tmval);
+
+ if (tmval.tv_usec > item->qdelay_timestamp)
+ queue_delay = tmval.tv_usec -
+ item->qdelay_timestamp;
+ else
+ queue_delay = tmval.tv_usec +
+ 1000000 - item->qdelay_timestamp;
+
+ if (tmval.tv_usec > item->mdelay_timestamp)
+ media_delay = tmval.tv_usec -
+ item->mdelay_timestamp;
+ else
+ media_delay = tmval.tv_usec +
+ 1000000 - item->mdelay_timestamp;
+ hw_priv->tsm_info.sum_media_delay +=
+ media_delay;
+ hw_priv->tsm_info.sum_pkt_q_delay += queue_delay;
+ if (queue_delay <= 10000)
+ hw_priv->tsm_stats.bin0++;
+ else if (queue_delay <= 20000)
+ hw_priv->tsm_stats.bin1++;
+ else if (queue_delay <= 40000)
+ hw_priv->tsm_stats.bin2++;
+ else
+ hw_priv->tsm_stats.bin3++;
+ }
+ }
+ spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ /* Do not use list_move_tail here, but list_move:
+ * try to utilize cache row.
+ */
+ list_move(&item->head, &queue->free_pool);
+
+ if (unlikely(queue->overfull) &&
+ (queue->num_queued <= ((stats->hw_priv->vif0_throttle + stats->hw_priv->vif1_throttle + 2)>>1))) {
+ queue->overfull = false;
+ __xradio_queue_unlock(queue);
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+
+#if 0
+ txrx_printk(XRADIO_DBG_ERROR, "queue_drop queue %d, %d, %d\n",
+ queue->num_queued, queue->link_map_cache[if_id][0],
+ queue->num_pending);
+ txrx_printk(XRADIO_DBG_ERROR, "queue_drop stats %d, %d\n", stats->num_queued,
+ stats->link_map_cache[if_id][0]);
+#endif
+ if (gc_skb)
+ stats->skb_dtor(stats->hw_priv, gc_skb, &gc_txpriv);
+
+ return ret;
+}
+
+int xradio_queue_get_skb(struct xradio_queue *queue, u32 packetID,
+ struct sk_buff **skb,
+ const struct xradio_txpriv **txpriv)
+{
+ int ret = 0;
+ u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id;
+ struct xradio_queue_item *item;
+
+ xradio_queue_parse_id(packetID, &queue_generation, &queue_id,
+ &item_generation, &item_id, &if_id, &link_id);
+
+ item = &queue->pool[item_id];
+
+ spin_lock_bh(&queue->lock);
+ SYS_BUG(queue_id != queue->queue_id);
+ /* TODO:COMBO: Add check for interface ID here */
+ if (unlikely(queue_generation != queue->generation)) {
+ ret = -ENOENT;
+ } else if (unlikely(item_id >= (unsigned) queue->capacity)) {
+ SYS_WARN(1);
+ ret = -EINVAL;
+ } else if (unlikely(item->generation != item_generation)) {
+ SYS_WARN(1);
+ ret = -ENOENT;
+ } else {
+ *skb = item->skb;
+ *txpriv = &item->txpriv;
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+void xradio_queue_lock(struct xradio_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+ __xradio_queue_lock(queue);
+ spin_unlock_bh(&queue->lock);
+}
+
+void xradio_queue_unlock(struct xradio_queue *queue)
+{
+ spin_lock_bh(&queue->lock);
+ __xradio_queue_unlock(queue);
+ spin_unlock_bh(&queue->lock);
+}
+
+bool xradio_queue_get_xmit_timestamp(struct xradio_queue *queue,
+ unsigned long *timestamp, int if_id,
+ u32 pending_frameID, u32 *Old_frame_ID)
+{
+ struct xradio_queue_item *item;
+ bool ret;
+
+ spin_lock_bh(&queue->lock);
+ ret = !list_empty(&queue->pending);
+ if (ret) {
+ list_for_each_entry(item, &queue->pending, head) {
+ if (((if_id == XRWL_GENERIC_IF_ID) ||
+ (if_id == XRWL_ALL_IFS) ||
+ (item->txpriv.if_id == if_id)) &&
+ (item->packetID != pending_frameID)) {
+ if (time_before(item->xmit_timestamp,
+ *timestamp)) {
+ *timestamp = item->xmit_timestamp;
+ *Old_frame_ID = item->packetID;
+ }
+ }
+ }
+ }
+ spin_unlock_bh(&queue->lock);
+ return ret;
+}
+
+bool xradio_queue_stats_is_empty(struct xradio_queue_stats *stats,
+ u32 link_id_map, int if_id)
+{
+ bool empty = true;
+
+ spin_lock_bh(&stats->lock);
+ if (link_id_map == (u32)-1)
+ empty = stats->num_queued[if_id] == 0;
+ else {
+ int i, if_id;
+ for (if_id = 0; if_id < XRWL_MAX_VIFS; if_id++) {
+ for (i = 0; i < stats->map_capacity; ++i) {
+ if (link_id_map & BIT(i)) {
+ if (stats->link_map_cache[if_id][i]) {
+ empty = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ spin_unlock_bh(&stats->lock);
+
+ return empty;
+}
+
+bool xradio_query_txpkt_timeout(struct xradio_common *hw_priv, int if_id,
+ u32 pending_pkt_id, long *timeout)
+{
+ int i;
+ bool pending = false;
+ unsigned long timestamp = jiffies;
+ struct xradio_queue *queue = NULL;
+ struct xradio_queue_item *item = NULL;
+ struct xradio_queue *old_queue = NULL;
+ struct xradio_queue_item *old_item = NULL;
+ u8 pack_stk_wr = 0;
+
+ /* Get oldest frame.*/
+ for (i = 0; i < AC_QUEUE_NUM; ++i) {
+ queue = &hw_priv->tx_queue[i];
+ spin_lock_bh(&queue->lock);
+ if (!list_empty(&queue->pending)) {
+ list_for_each_entry(item, &queue->pending, head) {
+ if (((if_id == XRWL_GENERIC_IF_ID) ||
+ (if_id == XRWL_ALL_IFS) ||
+ (item->txpriv.if_id == if_id)) &&
+ (item->packetID != pending_pkt_id)) {
+ if (time_before(item->xmit_timestamp, timestamp)) {
+ timestamp = item->xmit_timestamp;
+ pack_stk_wr = item->pack_stk_wr;
+ old_queue = queue;
+ old_item = item;
+ }
+ }
+ }
+ pending = true;
+ }
+ spin_unlock_bh(&queue->lock);
+ }
+ if (!pending)
+ return false;
+
+ /* Check if frame transmission is timed out.
+ * add (WSM_CMD_LAST_CHANCE_TIMEOUT>>1) for stuck workaround.*/
+ *timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies;
+ if (unlikely(*timeout < 0) && !pack_stk_wr) {
+ struct ieee80211_hdr *frame = NULL;
+ const struct xradio_txpriv *txpriv = NULL;
+ u16 fctl = 0x0;
+ u32 len = 0x0;
+ u8 if_id = 0, link_id = 0, tid = 0;
+
+ /* query the timeout frame. */
+ spin_lock_bh(&old_queue->lock);
+ if (likely(old_item->skb && !hw_priv->query_packetID)) {
+ hw_priv->query_packetID = old_item->packetID;
+ old_item->pack_stk_wr = 1;
+ atomic_add(1, &hw_priv->query_cnt);
+
+ /* Info of stuck frames for debug.*/
+ txpriv = &old_item->txpriv;
+ frame = (struct ieee80211_hdr *)(&old_item->skb->data[txpriv->offset]);
+ fctl = frame->frame_control;
+ len = old_item->skb->len;
+ if_id = txpriv->if_id;
+ link_id = txpriv->link_id;
+ tid = txpriv->tid;
+ }
+ spin_unlock_bh(&old_queue->lock);
+ /* Dump Info of stuck frames. */
+ if (frame) {
+ txrx_printk(XRADIO_DBG_ERROR, "TX confirm timeout(%ds).\n",
+ WSM_CMD_LAST_CHANCE_TIMEOUT/HZ);
+ txrx_printk(XRADIO_DBG_ERROR, "if=%d, linkid=%d, tid=%d, " \
+ "old_packetID=0x%08x, fctl=0x%04x, len=%d, wr=%d\n",
+ if_id, link_id, tid, hw_priv->query_packetID, fctl, len,
+ pack_stk_wr);
+ }
+ /* Return half of timeout for query packet. */
+ *timeout = (WSM_CMD_LAST_CHANCE_TIMEOUT>>1);
+ } else if (unlikely(pack_stk_wr)){
+ *timeout = *timeout + (WSM_CMD_LAST_CHANCE_TIMEOUT>>1);
+ txrx_printk(XRADIO_DBG_MSG,"%s, wr and timeout=%ld\n", __func__, *timeout);
+ }
+ return pending;
+}
diff --git a/drivers/net/wireless/xradio/queue.h b/drivers/net/wireless/xradio/queue.h
new file mode 100644
index 00000000..db55c4ca
--- /dev/null
+++ b/drivers/net/wireless/xradio/queue.h
@@ -0,0 +1,153 @@
+/*
+ * queue operations for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#ifndef XRADIO_QUEUE_H_INCLUDED
+#define XRADIO_QUEUE_H_INCLUDED
+
+/* private */ struct xradio_queue_item;
+
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct xradio_common;
+/* extern */ struct xradio_vif;
+/* extern */ struct ieee80211_tx_queue_stats;
+/* extern */ struct xradio_txpriv;
+
+/* forward */ struct xradio_queue_stats;
+
+typedef void (*xradio_queue_skb_dtor_t)(struct xradio_common *priv,
+ struct sk_buff *skb,
+ const struct xradio_txpriv *txpriv);
+
+struct xradio_queue {
+ struct xradio_queue_stats *stats;
+ size_t capacity;
+ size_t num_queued;
+ size_t num_queued_vif[XRWL_MAX_VIFS];
+ size_t num_pending;
+ size_t num_pending_vif[XRWL_MAX_VIFS];
+ size_t num_sent;
+ struct xradio_queue_item *pool;
+ struct list_head queue;
+ struct list_head free_pool;
+ struct list_head pending;
+ int tx_locked_cnt;
+ int *link_map_cache[XRWL_MAX_VIFS];
+ bool overfull;
+ spinlock_t lock;
+ u8 queue_id;
+ u8 generation;
+ struct timer_list gc;
+ unsigned long ttl;
+};
+
+struct xradio_queue_stats {
+ spinlock_t lock;
+ int *link_map_cache[XRWL_MAX_VIFS];
+ int num_queued[XRWL_MAX_VIFS];
+ size_t map_capacity;
+ wait_queue_head_t wait_link_id_empty;
+ xradio_queue_skb_dtor_t skb_dtor;
+ struct xradio_common *hw_priv;
+};
+
+struct xradio_txpriv {
+ u8 link_id;
+ u8 raw_link_id;
+ u8 tid;
+ u8 rate_id;
+ u8 offset;
+ u8 if_id;
+#ifndef P2P_MULTIVIF
+ u8 offchannel_if_id;
+#else
+ u8 raw_if_id;
+#endif
+ u8 use_bg_rate;
+};
+
+int xradio_queue_stats_init(struct xradio_queue_stats *stats,
+ size_t map_capacity,
+ xradio_queue_skb_dtor_t skb_dtor,
+ struct xradio_common *priv);
+int xradio_queue_init(struct xradio_queue *queue,
+ struct xradio_queue_stats *stats,
+ u8 queue_id,
+ size_t capacity,
+ unsigned long ttl);
+int xradio_queue_clear(struct xradio_queue *queue, int if_id);
+void xradio_queue_stats_deinit(struct xradio_queue_stats *stats);
+void xradio_queue_deinit(struct xradio_queue *queue);
+
+size_t xradio_queue_get_num_queued(struct xradio_vif *priv,
+ struct xradio_queue *queue,
+ u32 link_id_map);
+int xradio_queue_put(struct xradio_queue *queue,
+ struct sk_buff *skb, struct xradio_txpriv *txpriv);
+int xradio_queue_get(struct xradio_queue *queue,
+ int if_id, u32 link_id_map,
+ struct wsm_tx **tx,
+ struct ieee80211_tx_info **tx_info,
+ struct xradio_txpriv **txpriv);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+int xradio_queue_requeue(struct xradio_common *hw_priv,
+ struct xradio_queue *queue,
+ u32 packetID, bool check);
+#else
+int xradio_queue_requeue(struct xradio_queue *queue, u32 packetID, bool check);
+#endif
+int xradio_queue_requeue_all(struct xradio_queue *queue);
+#ifdef CONFIG_XRADIO_TESTMODE
+int xradio_queue_remove(struct xradio_common *hw_priv,
+ struct xradio_queue *queue,
+ u32 packetID);
+#else
+int xradio_queue_remove(struct xradio_queue *queue,
+ u32 packetID);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+int xradio_queue_get_skb(struct xradio_queue *queue, u32 packetID,
+ struct sk_buff **skb,
+ const struct xradio_txpriv **txpriv);
+void xradio_queue_lock(struct xradio_queue *queue);
+void xradio_queue_unlock(struct xradio_queue *queue);
+bool xradio_queue_get_xmit_timestamp(struct xradio_queue *queue,
+ unsigned long *timestamp, int if_id,
+ u32 pending_frameID, u32 *Old_frame_ID);
+bool xradio_query_txpkt_timeout(struct xradio_common *hw_priv, int if_id,
+ u32 pending_pkt_id, long *timeout);
+
+
+bool xradio_queue_stats_is_empty(struct xradio_queue_stats *stats,
+ u32 link_id_map, int if_id);
+
+static inline u8 xradio_queue_get_queue_id(u32 packetID)
+{
+ return (packetID >> 16) & 0xF;
+}
+
+static inline u8 xradio_queue_get_if_id(u32 packetID)
+{
+ return (packetID >> 20) & 0xF;
+}
+
+static inline u8 xradio_queue_get_link_id(u32 packetID)
+{
+ return (packetID >> 24) & 0xF;
+}
+
+static inline u8 xradio_queue_get_generation(u32 packetID)
+{
+ return (packetID >> 8) & 0xFF;
+}
+
+#endif /* XRADIO_QUEUE_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/scan.c b/drivers/net/wireless/xradio/scan.c
new file mode 100644
index 00000000..aa181634
--- /dev/null
+++ b/drivers/net/wireless/xradio/scan.c
@@ -0,0 +1,1142 @@
+/*
+ * Scan implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include "xradio.h"
+#include "scan.h"
+#include "sta.h"
+#include "pm.h"
+
+static void xradio_scan_restart_delayed(struct xradio_vif *priv);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+static int xradio_advance_scan_start(struct xradio_common *hw_priv)
+{
+ int tmo = 0;
+
+
+ tmo += hw_priv->advanceScanElems.duration;
+ xradio_pm_stay_awake(&hw_priv->pm_state, tmo * HZ / 1000);
+ /* Invoke Advance Scan Duration Timeout Handler */
+ queue_delayed_work(hw_priv->workqueue,
+ &hw_priv->advance_scan_timeout, tmo * HZ / 1000);
+ return 0;
+}
+#endif
+
+static void xradio_remove_wps_p2p_ie(struct wsm_template_frame *frame)
+{
+ u8 *ies;
+ u32 ies_len;
+ u32 ie_len;
+ u32 p2p_ie_len = 0;
+ u32 wps_ie_len = 0;
+
+
+ ies = &frame->skb->data[sizeof(struct ieee80211_hdr_3addr)];
+ ies_len = frame->skb->len - sizeof(struct ieee80211_hdr_3addr);
+ while (ies_len >= 6) {
+ ie_len = ies[1] + 2;
+ ies_len -= ie_len;
+ if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) &&
+ (ies[2] == 0x00 && ies[3] == 0x50 &&
+ ies[4] == 0xf2 && ies[5] == 0x04)) {
+ wps_ie_len = ie_len;
+ memmove(ies, ies + ie_len, ies_len);
+ } else if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) &&
+ (ies[2] == 0x50 && ies[3] == 0x6f &&
+ ies[4] == 0x9a && ies[5] == 0x09)) {
+ p2p_ie_len = ie_len;
+ memmove(ies, ies + ie_len, ies_len);
+ } else {
+ ies += ie_len;
+ }
+ }
+
+ if (p2p_ie_len || wps_ie_len) {
+ skb_trim(frame->skb, frame->skb->len - (p2p_ie_len + wps_ie_len));
+ }
+}
+
+#ifdef CONFIG_XRADIO_TESTMODE
+static int xradio_disable_filtering(struct xradio_vif *priv)
+{
+ int ret = 0;
+ bool bssid_filtering = 0;
+ struct wsm_rx_filter rx_filter;
+ struct wsm_beacon_filter_control bf_control;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ /* RX Filter Disable */
+ rx_filter.promiscuous = 0;
+ rx_filter.bssid = 0;
+ rx_filter.fcs = 0;
+ ret = wsm_set_rx_filter(hw_priv, &rx_filter, priv->if_id);
+
+ /* Beacon Filter Disable */
+ bf_control.enabled = 0;
+ bf_control.bcn_count = 1;
+ if (!ret)
+ ret = wsm_beacon_filter_control(hw_priv, &bf_control, priv->if_id);
+
+ /* BSSID Filter Disable */
+ if (!ret)
+ ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, priv->if_id);
+
+ return ret;
+}
+#endif
+
+static int xradio_scan_start(struct xradio_vif *priv, struct wsm_scan *scan)
+{
+ int ret, i;
+#ifdef FPGA_SETUP
+ int tmo = 5000;
+#else
+ int tmo = 5000;
+#endif
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ for (i = 0; i < scan->numOfChannels; ++i)
+ tmo += scan->ch[i].maxChannelTime + 10;
+
+ atomic_set(&hw_priv->scan.in_progress, 1);
+ atomic_set(&hw_priv->recent_scan, 1);
+ xradio_pm_stay_awake(&hw_priv->pm_state, tmo * HZ / 1000);
+#ifdef P2P_MULTIVIF
+ ret = wsm_scan(hw_priv, scan, priv->if_id ? 1 : 0);
+#else
+ ret = wsm_scan(hw_priv, scan, priv->if_id);
+#endif
+ if (unlikely(ret)) {
+ scan_printk(XRADIO_DBG_WARN, "%s,wsm_scan failed!\n", __func__);
+ atomic_set(&hw_priv->scan.in_progress, 0);
+ xradio_scan_restart_delayed(priv);
+ } else {
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout,
+ tmo * HZ / 1000);
+ }
+ return ret;
+}
+
+#ifdef ROAM_OFFLOAD
+static int xradio_sched_scan_start(struct xradio_vif *priv, struct wsm_scan *scan)
+{
+ int ret;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ ret = wsm_scan(hw_priv, scan, priv->if_id);
+ if (unlikely(ret)) {
+ atomic_set(&hw_priv->scan.in_progress, 0);
+ scan_printk(XRADIO_DBG_WARN,"%s,wsm_scan failed!\n", __func__);
+ }
+ return ret;
+}
+#endif /*ROAM_OFFLOAD*/
+
+int xradio_hw_scan(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *hw_req)
+{
+ struct cfg80211_scan_request *req = &hw_req->req;
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ int i;
+#ifdef CONFIG_XRADIO_TESTMODE
+ int ret = 0;
+ u16 advance_scan_req_channel;
+#endif
+
+
+ /* Scan when P2P_GO corrupt firmware MiniAP mode */
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ scan_printk(XRADIO_DBG_WARN,"%s, can't scan in AP mode!\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ if (hw_priv->bh_error) {
+ scan_printk(XRADIO_DBG_NIY, "Ignoring scan bh error occur!\n");
+ return -EBUSY;
+ }
+
+ if (work_pending(&priv->offchannel_work) ||
+ (hw_priv->roc_if_id != -1)) {
+ scan_printk(XRADIO_DBG_WARN, "Offchannel work pending, "
+ "ignoring scan work %d\n", hw_priv->roc_if_id);
+ return -EBUSY;
+ }
+
+ if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+ req->n_ssids = 0;
+
+ scan_printk(XRADIO_DBG_NIY, "vif%d Scan request(%s-%dchs) for %d SSIDs.\n",
+ priv->if_id, (req->channels[0]->band==NL80211_BAND_2GHZ)?"2.4G":"5G",
+ req->n_channels, req->n_ssids);
+
+ /*delay multiple ssids scan of vif0 for 3s when connnetting to a node*/
+ if(hw_priv->connet_time[0] > 0 && req->n_ssids == 0 && priv->if_id == 0) {
+ long timeleft0 = hw_priv->connet_time[0] + SCAN_MAX_DELAY - jiffies;
+ if(jiffies >= hw_priv->connet_time[0] && timeleft0 > 0) {
+ scan_printk(XRADIO_DBG_NIY, "vif0 connetting, scan delay %ldms\n",
+ timeleft0*1000/HZ);
+ return -EBUSY;
+ }
+ hw_priv->connet_time[0] = 0;
+ }
+
+ if (req->n_ssids > hw->wiphy->max_scan_ssids){
+ scan_printk(XRADIO_DBG_ERROR, "%s: ssids is too much(%d)\n",
+ __func__, req->n_ssids);
+ return -EINVAL;
+ }
+
+ /* TODO by Icenowy: so strange function call */
+ frame.skb = ieee80211_probereq_get(hw, vif->addr, NULL, 0, 0);
+ if (!frame.skb) {
+ scan_printk(XRADIO_DBG_ERROR, "%s: ieee80211_probereq_get failed!\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+#ifdef ROAM_OFFLOAD
+ if (priv->join_status != XRADIO_JOIN_STATUS_STA) {
+ if (req->channels[0]->band == NL80211_BAND_2GHZ)
+ hw_priv->num_scanchannels = 0;
+ else
+ hw_priv->num_scanchannels = hw_priv->num_2g_channels;
+
+ for (i=0; i < req->n_channels; i++) {
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number = \
+ req->channels[i]->hw_value;
+ if (req->channels[i]->flags & IEEE80211_CHAN_NO_IR) {
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].minChannelTime = 50;
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].maxChannelTime = 110;
+ }
+ else {
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].minChannelTime = 10;
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].maxChannelTime = 40;
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \
+ XRADIO_SCAN_TYPE_ACTIVE;
+ }
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].txPowerLevel = \
+ req->channels[i]->max_power;
+ if (req->channels[0]->band == NL80211_BAND_5GHZ)
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \
+ XRADIO_SCAN_BAND_5G;
+ }
+ if (req->channels[0]->band == NL80211_BAND_2GHZ)
+ hw_priv->num_2g_channels = req->n_channels;
+ else
+ hw_priv->num_5g_channels = req->n_channels;
+ }
+ hw_priv->num_scanchannels = hw_priv->num_2g_channels + hw_priv->num_5g_channels;
+#endif /*ROAM_OFFLOAD*/
+
+ /* will be unlocked in xradio_scan_work() */
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ /* Active Scan - Serving Channel Request Handling */
+ advance_scan_req_channel = req->channels[0]->hw_value;
+ if (hw_priv->enable_advance_scan &&
+ (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_ACTIVE) &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (hw_priv->channel->hw_value == advance_scan_req_channel)) {
+ SYS_BUG(hw_priv->scan.req);
+ /* wsm_lock_tx(hw_priv); */
+ wsm_vif_lock_tx(priv);
+ hw_priv->scan.if_id = priv->if_id;
+ /* Disable Power Save */
+ if (priv->powersave_mode.pmMode & WSM_PSM_PS) {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ pm.pmMode = WSM_PSM_ACTIVE;
+ wsm_set_pm(hw_priv, &pm, priv->if_id);
+ }
+ /* Disable Rx Beacon and Bssid filter */
+ ret = xradio_disable_filtering(priv);
+ if (ret)
+ scan_printk(XRADIO_DBG_ERROR, "%s: Disable BSSID or Beacon filtering "
+ "failed: %d.\n", __func__, ret);
+ wsm_unlock_tx(hw_priv);
+ mutex_unlock(&hw_priv->conf_mutex);
+ /* Transmit Probe Request with Broadcast SSID */
+ xradio_tx(hw, frame.skb);
+ /* Start Advance Scan Timer */
+ xradio_advance_scan_start(hw_priv);
+ } else {
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+ if (frame.skb) {
+ int ret = 0;
+ if (priv->if_id == 0)
+ xradio_remove_wps_p2p_ie(&frame);
+#ifdef P2P_MULTIVIF
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id ? 1 : 0);
+#else
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+#endif
+ if (ret) {
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+ dev_kfree_skb(frame.skb);
+ scan_printk(XRADIO_DBG_ERROR, "%s: wsm_set_template_frame failed: %d.\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ wsm_vif_lock_tx(priv);
+
+ SYS_BUG(hw_priv->scan.req);
+ hw_priv->scan.req = req;
+ hw_priv->scan.n_ssids = 0;
+ hw_priv->scan.status = 0;
+ hw_priv->scan.begin = &req->channels[0];
+ hw_priv->scan.curr = hw_priv->scan.begin;
+ hw_priv->scan.end = &req->channels[req->n_channels];
+ hw_priv->scan.output_power = hw_priv->output_power;
+ hw_priv->scan.if_id = priv->if_id;
+ /* TODO:COMBO: Populate BIT4 in scanflags to decide on which MAC
+ * address the SCAN request will be sent */
+
+ for (i = 0; i < req->n_ssids; ++i) {
+ struct wsm_ssid *dst = &hw_priv->scan.ssids[hw_priv->scan.n_ssids];
+ SYS_BUG(req->ssids[i].ssid_len > sizeof(dst->ssid));
+ memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
+ dst->length = req->ssids[i].ssid_len;
+ ++hw_priv->scan.n_ssids;
+ }
+
+ mutex_unlock(&hw_priv->conf_mutex);
+
+ if (frame.skb)
+ dev_kfree_skb(frame.skb);
+ queue_work(hw_priv->workqueue, &hw_priv->scan.work);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ }
+#endif
+
+ return 0;
+}
+
+#ifdef ROAM_OFFLOAD
+int xradio_hw_sched_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_sched_scan_ies *ies)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ int i;
+
+
+ scan_printk(XRADIO_DBG_WARN, "Scheduled scan request-->.\n");
+ if (!priv->vif)
+ return -EINVAL;
+
+ /* Scan when P2P_GO corrupt firmware MiniAP mode */
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ scan_printk(XRADIO_DBG_WARN,"%s, can't scan in AP mode!\n", __func__);
+ return -EOPNOTSUPP;
+ }
+
+ scan_printk(XRADIO_DBG_WARN, "Scheduled scan: n_ssids %d, ssid[0].len = %d\n",
+ req->n_ssids, req->ssids[0].ssid_len);
+ if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
+ req->n_ssids = 0;
+
+ scan_printk(XRADIO_DBG_NIY, "[SCAN] Scan request for %d SSIDs.\n",
+ req->n_ssids);
+
+ if (req->n_ssids > hw->wiphy->max_scan_ssids) [
+ scan_printk(XRADIO_DBG_ERROR, "%s: ssids is too much(%d)\n",
+ __func__, req->n_ssids);
+ return -EINVAL;
+ }
+
+ frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0,
+ ies->ie[0], ies->len[0]);
+ if (!frame.skb) {
+ scan_printk(XRADIO_DBG_ERROR, "%s: ieee80211_probereq_get failed!\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ /* will be unlocked in xradio_scan_work() */
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+ if (frame.skb) {
+ int ret;
+ if (priv->if_id == 0)
+ xradio_remove_wps_p2p_ie(&frame);
+ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
+ if (0 == ret) {
+ /* Host want to be the probe responder. */
+ ret = wsm_set_probe_responder(priv, true);
+ }
+ if (ret) {
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+ dev_kfree_skb(frame.skb);
+ scan_printk(XRADIO_DBG_ERROR, "%s: wsm_set_probe_responder failed: %d.\n",
+ __func__, ret);
+ return ret;
+ }
+ }
+
+ wsm_lock_tx(hw_priv);
+ SYS_BUG(hw_priv->scan.req);
+ hw_priv->scan.sched_req = req;
+ hw_priv->scan.n_ssids = 0;
+ hw_priv->scan.status = 0;
+ hw_priv->scan.begin = &req->channels[0];
+ hw_priv->scan.curr = hw_priv->scan.begin;
+ hw_priv->scan.end = &req->channels[req->n_channels];
+ hw_priv->scan.output_power = hw_priv->output_power;
+
+ for (i = 0; i < req->n_ssids; ++i) {
+ u8 j;
+ struct wsm_ssid *dst = &hw_priv->scan.ssids[hw_priv->scan.n_ssids];
+ SYS_BUG(req->ssids[i].ssid_len > sizeof(dst->ssid));
+ memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid));
+ dst->length = req->ssids[i].ssid_len;
+ ++hw_priv->scan.n_ssids;
+ scan_printk(XRADIO_DBG_NIY, "SSID %d\n",i);
+ for(j=0; j<req->ssids[i].ssid_len; j++)
+ scan_printk(XRADIO_DBG_NIY, "0x%x\n", req->ssids[i].ssid[j]);
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+
+ if (frame.skb)
+ dev_kfree_skb(frame.skb);
+ queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
+ scan_printk(XRADIO_DBG_NIY, "<-- Scheduled scan request.\n");
+ return 0;
+}
+#endif /*ROAM_OFFLOAD*/
+
+void xradio_scan_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv = container_of(work,
+ struct xradio_common,
+ scan.work);
+ struct xradio_vif *priv;
+ struct ieee80211_channel **it;
+ struct wsm_scan scan = {
+ .scanType = WSM_SCAN_TYPE_FOREGROUND,
+ .scanFlags = 0, /* TODO:COMBO */
+ //.scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, /* TODO:COMBO */
+ };
+ bool first_run;
+ int i;
+ const u32 ProbeRequestTime = 2;
+ const u32 ChannelRemainTime = 15;
+ u32 maxChannelTime;
+#ifdef CONFIG_XRADIO_TESTMODE
+ int ret = 0;
+ u16 advance_scan_req_channel = hw_priv->scan.begin[0]->hw_value;
+#endif
+ struct cfg80211_scan_info scan_info;
+
+
+ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+
+ /*TODO: COMBO: introduce locking so vif is not removed in meanwhile */
+ if (!priv) {
+ scan_printk(XRADIO_DBG_WARN, "interface removed, "
+ "ignoring scan work\n");
+ return;
+ }
+
+ if (priv->if_id)
+ scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1;
+ else
+ scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1;
+
+ /* No need to set WSM_SCAN_FLAG_FORCE_BACKGROUND in BSS_LOSS work.
+ * yangfh 2015-11-11 18:45:02 */
+ //xradio_for_each_vif(hw_priv, vif, i) {
+ // if (!vif)
+ // continue;
+ // if (vif->bss_loss_status > XRADIO_BSS_LOSS_NONE)
+ // scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ //}
+
+ first_run = (hw_priv->scan.begin == hw_priv->scan.curr &&
+ hw_priv->scan.begin != hw_priv->scan.end);
+ if (first_run) {
+ /* Firmware gets crazy if scan request is sent
+ * when STA is joined but not yet associated.
+ * Force unjoin in this case. */
+ if (cancel_delayed_work_sync(&priv->join_timeout) > 0)
+ xradio_join_timeout(&priv->join_timeout.work);
+ }
+
+ mutex_lock(&hw_priv->conf_mutex);
+ if (first_run) {
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ /* Passive Scan - Serving Channel Request Handling */
+ if (hw_priv->enable_advance_scan &&
+ (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_PASSIVE) &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (hw_priv->channel->hw_value == advance_scan_req_channel)) {
+ /* If Advance Scan Request is for Serving Channel Device
+ * should be Active and Filtering Should be Disable */
+ if (priv->powersave_mode.pmMode & WSM_PSM_PS) {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ pm.pmMode = WSM_PSM_ACTIVE;
+ wsm_set_pm(hw_priv, &pm, priv->if_id);
+ }
+ /* Disable Rx Beacon and Bssid filter */
+ ret = xradio_disable_filtering(priv);
+ if (ret)
+ scan_printk(XRADIO_DBG_ERROR, "%s: Disable BSSID or Beacon"
+ "filtering failed: %d.\n", __func__, ret);
+ } else if (hw_priv->enable_advance_scan &&
+ (hw_priv->advanceScanElems.scanMode ==
+ XRADIO_SCAN_MEASUREMENT_PASSIVE) &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA)) {
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ pm.pmMode = WSM_PSM_PS;
+ xradio_set_pm(priv, &pm);
+ }
+ } else {
+#endif
+
+#if 0
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ pm.pmMode = WSM_PSM_PS;
+ xradio_set_pm(priv, &pm);
+ } else
+#endif
+ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) {
+ /* FW bug: driver has to restart p2p-dev mode
+ * after scan */
+ xradio_disable_listening(priv);
+ }
+#ifdef CONFIG_XRADIO_TESTMODE
+ }
+#endif
+ }
+
+ if (!hw_priv->scan.req || (hw_priv->scan.curr == hw_priv->scan.end)) {
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (hw_priv->enable_advance_scan &&
+ (hw_priv->advanceScanElems.scanMode ==
+ XRADIO_SCAN_MEASUREMENT_PASSIVE) &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (hw_priv->channel->hw_value == advance_scan_req_channel)) {
+ /* WSM Lock should be held here for WSM APIs */
+ wsm_vif_lock_tx(priv);
+
+ /* wsm_lock_tx(priv); */
+ /* Once Duration is Over, enable filtering
+ * and Revert Back Power Save */
+ if (priv->powersave_mode.pmMode & WSM_PSM_PS)
+ wsm_set_pm(hw_priv, &priv->powersave_mode, priv->if_id);
+ xradio_update_filtering(priv);
+ } else if (!hw_priv->enable_advance_scan) {
+#endif
+ if (hw_priv->scan.output_power != hw_priv->output_power) {
+ /* TODO:COMBO: Change when mac80211 implementation
+ * is available for output power also */
+#ifdef P2P_MULTIVIF
+ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->output_power * 10,
+ priv->if_id ? 1 : 0));
+#else
+ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->output_power * 10,
+ priv->if_id));
+#endif
+ }
+#ifdef CONFIG_XRADIO_TESTMODE
+ }
+#endif
+
+#if 0
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA &&
+ !(priv->powersave_mode.pmMode & WSM_PSM_PS))
+ xradio_set_pm(priv, &priv->powersave_mode);
+#endif
+
+ if (hw_priv->scan.status < 0)
+ scan_printk(XRADIO_DBG_ERROR, "Scan failed (%d).\n", hw_priv->scan.status);
+ else if (hw_priv->scan.req)
+ scan_printk(XRADIO_DBG_NIY, "Scan completed.\n");
+ else
+ scan_printk(XRADIO_DBG_NIY, "Scan canceled.\n");
+
+ hw_priv->scan.req = NULL;
+ xradio_scan_restart_delayed(priv);
+#ifdef CONFIG_XRADIO_TESTMODE
+ hw_priv->enable_advance_scan = false;
+#endif /* CONFIG_XRADIO_TESTMODE */
+ wsm_unlock_tx(hw_priv);
+ mutex_unlock(&hw_priv->conf_mutex);
+ memset(&scan_info, 0, sizeof(scan_info));
+ scan_info.aborted = hw_priv->scan.status ? 1 : 0;
+ ieee80211_scan_completed(hw_priv->hw, &scan_info);
+ up(&hw_priv->scan.lock);
+ return;
+
+ } else {
+ struct ieee80211_channel *first = *hw_priv->scan.curr;
+ for (it = hw_priv->scan.curr + 1, i = 1;
+ it != hw_priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
+ ++it, ++i) {
+ if ((*it)->band != first->band)
+ break;
+ if (((*it)->flags ^ first->flags) & IEEE80211_CHAN_NO_IR)
+ break;
+ if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
+ (*it)->max_power != first->max_power)
+ break;
+ }
+ scan.band = first->band;
+
+ if (hw_priv->scan.req->no_cck)
+ scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
+ else
+ scan.maxTransmitRate = WSM_TRANSMIT_RATE_1;
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (hw_priv->enable_advance_scan) {
+ if (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_PASSIVE)
+ scan.numOfProbeRequests = 0;
+ else
+ scan.numOfProbeRequests = 1;
+ } else {
+#endif
+ /* TODO: Is it optimal? */
+ scan.numOfProbeRequests = (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
+#ifdef CONFIG_XRADIO_TESTMODE
+ }
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+ scan.numOfSSIDs = hw_priv->scan.n_ssids;
+ scan.ssids = &hw_priv->scan.ssids[0];
+ scan.numOfChannels = it - hw_priv->scan.curr;
+ /* TODO: Is it optimal? */
+ scan.probeDelay = 100;
+ /* It is not stated in WSM specification, however
+ * FW team says that driver may not use FG scan
+ * when joined. */
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
+ scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ }
+ scan.ch = kzalloc(sizeof(struct wsm_scan_ch[it - hw_priv->scan.curr]), GFP_KERNEL);
+ if (!scan.ch) {
+ hw_priv->scan.status = -ENOMEM;
+ scan_printk(XRADIO_DBG_ERROR, "xr_kzalloc wsm_scan_ch failed.\n");
+ goto fail;
+ }
+ maxChannelTime = (scan.numOfSSIDs * scan.numOfProbeRequests *ProbeRequestTime) +
+ ChannelRemainTime;
+ maxChannelTime = (maxChannelTime < 35) ? 35 : maxChannelTime;
+ for (i = 0; i < scan.numOfChannels; ++i) {
+ scan.ch[i].number = hw_priv->scan.curr[i]->hw_value;
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (hw_priv->enable_advance_scan) {
+ scan.ch[i].minChannelTime = hw_priv->advanceScanElems.duration;
+ scan.ch[i].maxChannelTime = hw_priv->advanceScanElems.duration;
+ } else {
+#endif
+ if (hw_priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
+ scan.ch[i].minChannelTime = 50;
+ scan.ch[i].maxChannelTime = 110;
+ } else {
+ scan.ch[i].minChannelTime = 15;
+ scan.ch[i].maxChannelTime = maxChannelTime;
+ }
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ }
+#endif
+ }
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (!hw_priv->enable_advance_scan) {
+#endif
+ if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
+ hw_priv->scan.output_power != first->max_power) {
+ hw_priv->scan.output_power = first->max_power;
+ /* TODO:COMBO: Change after mac80211 implementation
+ * complete */
+#ifdef P2P_MULTIVIF
+ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->scan.output_power * 10,
+ priv->if_id ? 1 : 0));
+#else
+ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->scan.output_power * 10,
+ priv->if_id));
+#endif
+ }
+#ifdef CONFIG_XRADIO_TESTMODE
+ }
+#endif
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (hw_priv->enable_advance_scan &&
+ (hw_priv->advanceScanElems.scanMode ==
+ XRADIO_SCAN_MEASUREMENT_PASSIVE) &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (hw_priv->channel->hw_value == advance_scan_req_channel)) {
+ /* Start Advance Scan Timer */
+ hw_priv->scan.status = xradio_advance_scan_start(hw_priv);
+ wsm_unlock_tx(hw_priv);
+ } else
+#endif
+ down(&hw_priv->scan.status_lock);
+ hw_priv->scan.status = xradio_scan_start(priv, &scan);
+
+ kfree(scan.ch);
+ if (WARN_ON(hw_priv->scan.status)) {
+ scan_printk(XRADIO_DBG_ERROR, "scan failed, status=%d.\n",
+ hw_priv->scan.status);
+ up(&hw_priv->scan.status_lock);
+ goto fail;
+ }
+ up(&hw_priv->scan.status_lock);
+ hw_priv->scan.curr = it;
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ return;
+
+fail:
+ hw_priv->scan.curr = hw_priv->scan.end;
+ mutex_unlock(&hw_priv->conf_mutex);
+ queue_work(hw_priv->workqueue, &hw_priv->scan.work);
+ return;
+}
+
+#ifdef ROAM_OFFLOAD
+void xradio_sched_scan_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv = container_of(work, struct xradio_common,
+ scan.swork);
+ struct wsm_scan scan;
+ struct wsm_ssid scan_ssid;
+ int i;
+ struct xradio_vif *priv = NULL;
+
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+ if (unlikely(!priv)) {
+ WARN_ON(1);
+ return;
+ }
+
+ spin_unlock(&priv->vif_lock);
+ /* Firmware gets crazy if scan request is sent
+ * when STA is joined but not yet associated.
+ * Force unjoin in this case. */
+ if (cancel_delayed_work_sync(&priv->join_timeout) > 0) {
+ xradio_join_timeout(&priv->join_timeout.work);
+ }
+ mutex_lock(&hw_priv->conf_mutex);
+ hw_priv->auto_scanning = 1;
+ scan.band = 0;
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA)
+ scan.scanType = 3; /* auto background */
+ else
+ scan.scanType = 2; /* auto foreground */
+
+ scan.scanFlags = 0x01; /* bit 0 set => forced background scan */
+ scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
+ scan.autoScanInterval = (0xba << 24)|(30 * 1024); /* 30 seconds, -70 rssi */
+ scan.numOfProbeRequests = 1;
+ //scan.numOfChannels = 11;
+ scan.numOfChannels = hw_priv->num_scanchannels;
+ scan.numOfSSIDs = 1;
+ scan.probeDelay = 100;
+ scan_ssid.length = priv->ssid_length;
+ memcpy(scan_ssid.ssid, priv->ssid, priv->ssid_length);
+ scan.ssids = &scan_ssid;
+
+ scan.ch = xr_kzalloc(sizeof(struct wsm_scan_ch[scan.numOfChannels]), false);
+ if (!scan.ch) {
+ scan_printk(XRADIO_DBG_ERROR, "xr_kzalloc wsm_scan_ch failed.\n");
+ hw_priv->scan.status = -ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < scan.numOfChannels; i++) {
+ scan.ch[i].number = hw_priv->scan_channels[i].number;
+ scan.ch[i].minChannelTime = hw_priv->scan_channels[i].minChannelTime;
+ scan.ch[i].maxChannelTime = hw_priv->scan_channels[i].maxChannelTime;
+ scan.ch[i].txPowerLevel = hw_priv->scan_channels[i].txPowerLevel;
+ }
+
+#if 0
+ for (i = 1; i <= scan.numOfChannels; i++) {
+ scan.ch[i-1].number = i;
+ scan.ch[i-1].minChannelTime = 10;
+ scan.ch[i-1].maxChannelTime = 40;
+ }
+#endif
+
+ hw_priv->scan.status = xradio_sched_scan_start(priv, &scan);
+ kfree(scan.ch);
+ if (hw_priv->scan.status) {
+ scan_printk(XRADIO_DBG_ERROR, "scan failed, status=%d.\n",
+ hw_priv->scan.status);
+ goto fail;
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ return;
+
+fail:
+ mutex_unlock(&hw_priv->conf_mutex);
+ queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
+ return;
+}
+
+void xradio_hw_sched_scan_stop(struct xradio_common *hw_priv)
+{
+ struct xradio_vif *priv = NULL;
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv,hw_priv->scan.if_id);
+ if (unlikely(!priv))
+ return;
+
+ spin_unlock(&priv->vif_lock);
+ wsm_stop_scan(hw_priv, priv->if_id);
+
+ return;
+}
+#endif /*ROAM_OFFLOAD*/
+
+
+static void xradio_scan_restart_delayed(struct xradio_vif *priv)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ if (priv->delayed_link_loss) {
+ int tmo = hw_priv->scan.direct_probe ? 0 : priv->cqm_beacon_loss_count;
+
+ priv->delayed_link_loss = 0;
+ /* Restart beacon loss timer and requeue
+ BSS loss work. */
+ scan_printk(XRADIO_DBG_WARN, "[CQM] Requeue BSS loss in %d "
+ "beacons.\n", tmo);
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ queue_delayed_work(hw_priv->workqueue, &priv->bss_loss_work,
+ tmo * HZ / 10);
+
+ }
+
+ /* FW bug: driver has to restart p2p-dev mode after scan. */
+ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) {
+ /*xradio_enable_listening(priv);*/
+ WARN_ON(1);
+ xradio_update_filtering(priv);
+ scan_printk(XRADIO_DBG_WARN, "driver has to restart "
+ "p2p-dev mode after scan");
+ }
+
+ if (atomic_xchg(&priv->delayed_unjoin, 0)) {
+ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ }
+}
+
+static void xradio_scan_complete(struct xradio_common *hw_priv, int if_id)
+{
+ struct xradio_vif *priv;
+ atomic_xchg(&hw_priv->recent_scan, 0);
+
+
+ if (hw_priv->scan.direct_probe) {
+ mutex_lock(&hw_priv->conf_mutex);
+ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, if_id);
+ if (priv) {
+ scan_printk(XRADIO_DBG_MSG, "Direct probe complete.\n");
+ xradio_scan_restart_delayed(priv);
+ } else {
+ scan_printk(XRADIO_DBG_MSG, "Direct probe complete without interface!\n");
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ hw_priv->scan.direct_probe = 0;
+ up(&hw_priv->scan.lock);
+ wsm_unlock_tx(hw_priv);
+ } else {
+ xradio_scan_work(&hw_priv->scan.work);
+ }
+}
+
+void xradio_scan_complete_cb(struct xradio_common *hw_priv,
+ struct wsm_scan_complete *arg)
+{
+ struct xradio_vif *priv = xrwl_hwpriv_to_vifpriv(hw_priv,
+ hw_priv->scan.if_id);
+
+
+ if (unlikely(!priv))
+ return;
+
+#ifdef ROAM_OFFLOAD
+ if (hw_priv->auto_scanning)
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0);
+#endif /*ROAM_OFFLOAD*/
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+ /* STA is stopped. */
+ spin_unlock(&priv->vif_lock);
+ scan_printk(XRADIO_DBG_WARN, "%s: priv->mode UNSPECIFIED.\n", __func__);
+ return;
+ }
+ spin_unlock(&priv->vif_lock);
+
+ /*
+ if(hw_priv->scan.status == -ETIMEDOUT)
+ scan_printk(XRADIO_DBG_WARN, "Scan timeout already occured. "
+ "Don't cancel work");
+ if ((hw_priv->scan.status != -ETIMEDOUT) &&
+ (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0)) {
+ hw_priv->scan.status = 1;
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0);
+ }
+ */ // should not involve status as a condition
+
+ if (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0) {
+ down(&hw_priv->scan.status_lock);
+ hw_priv->scan.status = 1;
+ up(&hw_priv->scan.status_lock);
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0);
+ }
+}
+
+void xradio_scan_timeout(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, scan.timeout.work);
+
+
+ if (likely(atomic_xchg(&hw_priv->scan.in_progress, 0))) {
+ if (hw_priv->scan.status > 0)
+ hw_priv->scan.status = 0;
+ else if (!hw_priv->scan.status) {
+ scan_printk(XRADIO_DBG_WARN, "Timeout waiting for scan "
+ "complete notification.\n");
+ hw_priv->scan.status = -ETIMEDOUT;
+ hw_priv->scan.curr = hw_priv->scan.end;
+ WARN_ON(wsm_stop_scan(hw_priv, hw_priv->scan.if_id ? 1 : 0));
+ }
+ xradio_scan_complete(hw_priv, hw_priv->scan.if_id);
+#ifdef ROAM_OFFLOAD
+ } else if (hw_priv->auto_scanning) {
+ hw_priv->auto_scanning = 0;
+ ieee80211_sched_scan_results(hw_priv->hw);
+#endif /*ROAM_OFFLOAD*/
+ }
+}
+
+#ifdef CONFIG_XRADIO_TESTMODE
+void xradio_advance_scan_timeout(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, advance_scan_timeout.work);
+
+ struct xradio_vif *priv = xrwl_hwpriv_to_vifpriv(hw_priv,
+ hw_priv->scan.if_id);
+
+
+ if (WARN_ON(!priv))
+ return;
+ spin_unlock(&priv->vif_lock);
+
+ hw_priv->scan.status = 0;
+ if (hw_priv->advanceScanElems.scanMode ==
+ XRADIO_SCAN_MEASUREMENT_PASSIVE) {
+ /* Passive Scan on Serving Channel
+ * Timer Expire */
+ xradio_scan_complete(hw_priv, hw_priv->scan.if_id);
+ } else {
+ /* Active Scan on Serving Channel
+ * Timer Expire */
+ mutex_lock(&hw_priv->conf_mutex);
+ //wsm_lock_tx(priv);
+ wsm_vif_lock_tx(priv);
+ /* Once Duration is Over, enable filtering
+ * and Revert Back Power Save */
+ if ((priv->powersave_mode.pmMode & WSM_PSM_PS))
+ wsm_set_pm(hw_priv, &priv->powersave_mode, priv->if_id);
+ hw_priv->scan.req = NULL;
+ xradio_update_filtering(priv);
+ hw_priv->enable_advance_scan = false;
+ wsm_unlock_tx(hw_priv);
+ mutex_unlock(&hw_priv->conf_mutex);
+ ieee80211_scan_completed(hw_priv->hw, hw_priv->scan.status ? true : false);
+ up(&hw_priv->scan.lock);
+ }
+}
+#endif
+
+void xradio_probe_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, scan.probe_work.work);
+ struct xradio_vif *priv;
+ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id);
+ struct xradio_queue *queue = &hw_priv->tx_queue[queueId];
+ const struct xradio_txpriv *txpriv;
+ struct wsm_tx *wsm;
+ struct wsm_template_frame frame = {
+ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
+ };
+ struct wsm_ssid ssids[1] = {{
+ .length = 0,
+ } };
+ struct wsm_scan_ch ch[1] = {{
+ .minChannelTime = 0,
+ .maxChannelTime = 10,
+ } };
+ struct wsm_scan scan = {
+ .scanType = WSM_SCAN_TYPE_FOREGROUND,
+ .numOfProbeRequests = 1,
+ .probeDelay = 0,
+ .numOfChannels = 1,
+ .ssids = ssids,
+ .ch = ch,
+ };
+ u8 *ies;
+ size_t ies_len;
+ int ret = 1;
+ scan_printk(XRADIO_DBG_MSG, "%s:Direct probe.\n", __func__);
+
+ SYS_BUG(queueId >= 4);
+ SYS_BUG(!hw_priv->channel);
+
+ mutex_lock(&hw_priv->conf_mutex);
+ if (unlikely(down_trylock(&hw_priv->scan.lock))) {
+ /* Scan is already in progress. Requeue self. */
+ schedule();
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.probe_work,
+ HZ / 10);
+ mutex_unlock(&hw_priv->conf_mutex);
+ return;
+ }
+
+ if (xradio_queue_get_skb(queue, hw_priv->pending_frame_id, &frame.skb, &txpriv)) {
+ up(&hw_priv->scan.lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+ wsm_unlock_tx(hw_priv);
+ scan_printk(XRADIO_DBG_ERROR, "%s:xradio_queue_get_skb error!\n", __func__);
+ return;
+ }
+ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
+ if (!priv) {
+ up(&hw_priv->scan.lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+ scan_printk(XRADIO_DBG_ERROR, "%s:priv error!\n", __func__);
+ return;
+ }
+ wsm = (struct wsm_tx *)frame.skb->data;
+ scan.maxTransmitRate = wsm->maxTxRate;
+ scan.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
+ scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ if (priv->if_id)
+ scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1;
+ else
+ scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1;
+ }
+
+ /* No need to set WSM_SCAN_FLAG_FORCE_BACKGROUND in BSS_LOSS work.
+ * yangfh 2015-11-11 18:45:02 */
+ //xradio_for_each_vif(hw_priv, vif, i) {
+ // if (!vif)
+ // continue;
+ // if (vif->bss_loss_status > XRADIO_BSS_LOSS_NONE)
+ // scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND;
+ //}
+
+ ch[0].number = hw_priv->channel->hw_value;
+ skb_pull(frame.skb, txpriv->offset);
+ ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
+ ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
+
+ if (ies_len) {
+ u8 *ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
+ if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
+ u8 *nextie = &ssidie[2 + ssidie[1]];
+ /* Remove SSID from the IE list. It has to be provided
+ * as a separate argument in xradio_scan_start call */
+
+ /* Store SSID localy */
+ ssids[0].length = ssidie[1];
+ memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
+ scan.numOfSSIDs = 1;
+
+ /* Remove SSID from IE list */
+ ssidie[1] = 0;
+ memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
+ skb_trim(frame.skb, frame.skb->len - ssids[0].length);
+ }
+ }
+
+ if (priv->if_id == 0)
+ xradio_remove_wps_p2p_ie(&frame);
+
+ /* FW bug: driver has to restart p2p-dev mode after scan */
+ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) {
+ WARN_ON(1);
+ /*xradio_disable_listening(priv);*/
+ }
+ ret = WARN_ON(wsm_set_template_frame(hw_priv, &frame,
+ priv->if_id));
+
+ hw_priv->scan.direct_probe = 1;
+ hw_priv->scan.if_id = priv->if_id;
+ if (!ret) {
+ wsm_flush_tx(hw_priv);
+ ret = WARN_ON(xradio_scan_start(priv, &scan));
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+
+ skb_push(frame.skb, txpriv->offset);
+ if (!ret)
+ IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
+#ifdef CONFIG_XRADIO_TESTMODE
+ SYS_BUG(xradio_queue_remove(hw_priv, queue, hw_priv->pending_frame_id));
+#else
+ SYS_BUG(xradio_queue_remove(queue, hw_priv->pending_frame_id));
+#endif
+
+ if (ret) {
+ hw_priv->scan.direct_probe = 0;
+ up(&hw_priv->scan.lock);
+ wsm_unlock_tx(hw_priv);
+ }
+
+ return;
+}
diff --git a/drivers/net/wireless/xradio/scan.h b/drivers/net/wireless/xradio/scan.h
new file mode 100644
index 00000000..3a1f11d8
--- /dev/null
+++ b/drivers/net/wireless/xradio/scan.h
@@ -0,0 +1,75 @@
+/*
+ * Scan interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#ifndef SCAN_H_INCLUDED
+#define SCAN_H_INCLUDED
+
+#include <linux/semaphore.h>
+#include "wsm.h"
+
+/* external */ struct sk_buff;
+/* external */ struct cfg80211_scan_request;
+/* external */ struct ieee80211_channel;
+/* external */ struct ieee80211_hw;
+/* external */ struct work_struct;
+
+#define SCAN_MAX_DELAY (3*HZ) //3s, add by yangfh for connect
+
+struct xradio_scan {
+ struct semaphore lock;
+ struct work_struct work;
+#ifdef ROAM_OFFLOAD
+ struct work_struct swork; /* scheduled scan work */
+ struct cfg80211_sched_scan_request *sched_req;
+#endif /*ROAM_OFFLOAD*/
+ struct delayed_work timeout;
+ struct cfg80211_scan_request *req;
+ struct ieee80211_channel **begin;
+ struct ieee80211_channel **curr;
+ struct ieee80211_channel **end;
+ struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS];
+ int output_power;
+ int n_ssids;
+ //add by liwei, for h64 ping WS550 BUG
+ struct semaphore status_lock;
+ int status;
+ atomic_t in_progress;
+ /* Direct probe requests workaround */
+ struct delayed_work probe_work;
+ int direct_probe;
+ u8 if_id;
+};
+
+int xradio_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_scan_request *req);
+#ifdef ROAM_OFFLOAD
+int xradio_hw_sched_scan_start(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct cfg80211_sched_scan_request *req,
+ struct ieee80211_sched_scan_ies *ies);
+void xradio_hw_sched_scan_stop(struct xradio_common *priv);
+void xradio_sched_scan_work(struct work_struct *work);
+#endif /*ROAM_OFFLOAD*/
+void xradio_scan_work(struct work_struct *work);
+void xradio_scan_timeout(struct work_struct *work);
+void xradio_scan_complete_cb(struct xradio_common *priv,
+ struct wsm_scan_complete *arg);
+
+/* ******************************************************************** */
+/* Raw probe requests TX workaround */
+void xradio_probe_work(struct work_struct *work);
+#ifdef CONFIG_XRADIO_TESTMODE
+/* Advance Scan Timer */
+void xradio_advance_scan_timeout(struct work_struct *work);
+#endif
+
+#endif
diff --git a/drivers/net/wireless/xradio/sdio.c b/drivers/net/wireless/xradio/sdio.c
new file mode 100644
index 00000000..70218423
--- /dev/null
+++ b/drivers/net/wireless/xradio/sdio.c
@@ -0,0 +1,266 @@
+/*
+ * SDIO driver for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <asm/mach-types.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+
+#include "xradio.h"
+#include "sdio.h"
+#include "main.h"
+
+/* sdio vendor id and device id*/
+#define SDIO_VENDOR_ID_XRADIO 0x0020
+#define SDIO_DEVICE_ID_XRADIO 0x2281
+static const struct sdio_device_id xradio_sdio_ids[] = {
+ { SDIO_DEVICE(SDIO_VENDOR_ID_XRADIO, SDIO_DEVICE_ID_XRADIO) },
+ //{ SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) },
+ { /* end: all zeroes */ },
+};
+
+/* sbus_ops implemetation */
+int sdio_data_read(struct xradio_common* self, unsigned int addr,
+ void *dst, int count)
+{
+ int ret = sdio_memcpy_fromio(self->sdio_func, dst, addr, count);
+// printk("sdio_memcpy_fromio 0x%x:%d ret %d\n", addr, count, ret);
+// print_hex_dump_bytes("sdio read ", 0, dst, min(count,32));
+ return ret;
+}
+
+int sdio_data_write(struct xradio_common* self, unsigned int addr,
+ const void *src, int count)
+{
+ int ret = sdio_memcpy_toio(self->sdio_func, addr, (void *)src, count);
+// printk("sdio_memcpy_toio 0x%x:%d ret %d\n", addr, count, ret);
+// print_hex_dump_bytes("sdio write", 0, src, min(count,32));
+ return ret;
+}
+
+void sdio_lock(struct xradio_common* self)
+{
+ sdio_claim_host(self->sdio_func);
+}
+
+void sdio_unlock(struct xradio_common *self)
+{
+ sdio_release_host(self->sdio_func);
+}
+
+size_t sdio_align_len(struct xradio_common *self, size_t size)
+{
+ return sdio_align_size(self->sdio_func, size);
+}
+
+int sdio_set_blk_size(struct xradio_common *self, size_t size)
+{
+ return sdio_set_block_size(self->sdio_func, size);
+}
+
+extern void xradio_irq_handler(struct xradio_common*);
+
+static irqreturn_t sdio_irq_handler(int irq, void *dev_id)
+{
+ struct sdio_func *func = (struct sdio_func*) dev_id;
+ struct xradio_common *self = sdio_get_drvdata(func);
+ if (self != NULL)
+ xradio_irq_handler(self);
+ return IRQ_HANDLED;
+}
+
+static int sdio_enableint(struct sdio_func* func)
+{
+ int ret = 0;
+ u8 cccr;
+ int func_num;
+
+ sdio_claim_host(func);
+
+ /* Hack to access Fuction-0 */
+ func_num = func->num;
+ func->num = 0;
+ cccr = sdio_readb(func, SDIO_CCCR_IENx, &ret);
+ cccr |= BIT(0); /* Master interrupt enable ... */
+ cccr |= BIT(func_num); /* ... for our function */
+ sdio_writeb(func, cccr, SDIO_CCCR_IENx, &ret);
+
+ /* Restore the WLAN function number */
+ func->num = func_num;
+
+ sdio_release_host(func);
+
+ return ret;
+}
+
+int sdio_pm(struct xradio_common *self, bool suspend)
+{
+ int ret = 0;
+ if (suspend) {
+ /* Notify SDIO that XRADIO will remain powered during suspend */
+ ret = sdio_set_host_pm_flags(self->sdio_func, MMC_PM_KEEP_POWER);
+ if (ret)
+ dev_dbg(&self->sdio_func->dev, "Error setting SDIO pm flags: %i\n", ret);
+ }
+
+ return ret;
+}
+
+//for sdio debug 2015-5-26 11:01:21
+#if (defined(CONFIG_XRADIO_DEBUGFS))
+u32 dbg_sdio_clk = 0;
+static int sdio_set_clk(struct sdio_func *func, u32 clk)
+{
+ if (func) {
+ if (func->card->host->ops->set_ios && clk >= 1000000) { //set min to 1M
+ sdio_claim_host(func);
+ func->card->host->ios.clock = (clk < 50000000) ? clk : 50000000;
+ func->card->host->ops->set_ios(func->card->host, &func->card->host->ios);
+ sdio_release_host(func);
+ dev_dbg(&func->dev, "change mmc clk=%d\n",
+ func->card->host->ios.clock);
+ } else {
+ dev_dbg(&func->dev, "fail change mmc clk=%d\n", clk);
+ }
+ }
+ return 0;
+
+}
+#endif
+
+static const struct of_device_id xradio_sdio_of_match_table[] = {
+ { .compatible = "xradio,xr819" },
+ { }
+};
+
+static int xradio_probe_of(struct sdio_func *func)
+{
+ struct device *dev = &func->dev;
+ struct device_node *np = dev->of_node;
+ const struct of_device_id *of_id;
+ int irq;
+
+ of_id = of_match_node(xradio_sdio_of_match_table, np);
+ if (!of_id)
+ return -ENODEV;
+
+ //pdev_data->family = of_id->data;
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq) {
+ dev_err(dev, "No irq in platform data\n");
+ return -EINVAL;
+ }
+
+ devm_request_irq(dev, irq, sdio_irq_handler, 0, "xradio", func);
+
+ return 0;
+}
+
+/* Probe Function to be called by SDIO stack when device is discovered */
+static int sdio_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ dev_dbg(&func->dev, "XRadio Device:sdio clk=%d\n",
+ func->card->host->ios.clock);
+ dev_dbg(&func->dev, "sdio func->class=%x\n", func->class);
+ dev_dbg(&func->dev, "sdio_vendor: 0x%04x\n", func->vendor);
+ dev_dbg(&func->dev, "sdio_device: 0x%04x\n", func->device);
+ dev_dbg(&func->dev, "Function#: 0x%04x\n", func->num);
+
+#if (defined(CONFIG_XRADIO_DEBUGFS))
+ if (dbg_sdio_clk)
+ sdio_set_clk(func, dbg_sdio_clk);
+#endif
+
+#if 0 //for odly and sdly debug.
+{
+ u32 sdio_param = 0;
+ sdio_param = readl(__io_address(0x01c20088));
+ sdio_param &= ~(0xf<<8);
+ sdio_param |= 3<<8;
+ sdio_param &= ~(0xf<<20);
+ sdio_param |= s_dly<<20;
+ writel(sdio_param, __io_address(0x01c20088));
+ sbus_printk(XRADIO_DBG_ALWY, "%s: 0x01c20088=0x%08x\n", __func__, sdio_param);
+}
+#endif
+
+ xradio_probe_of(func);
+
+ func->card->quirks |= MMC_QUIRK_BROKEN_BYTE_MODE_512;
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ sdio_release_host(func);
+
+ sdio_enableint(func);
+
+ xradio_core_init(func);
+
+ try_module_get(func->dev.driver->owner);
+ return 0;
+}
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected */
+static void sdio_remove(struct sdio_func *func)
+{
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ module_put(func->dev.driver->owner);
+}
+
+static int sdio_suspend(struct device *dev)
+{
+ int ret = 0;
+ /*
+ struct sdio_func *func = dev_to_sdio_func(dev);
+ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+ if (ret)
+ sbus_printk(XRADIO_DBG_ERROR, "set MMC_PM_KEEP_POWER error\n");
+ */
+ return ret;
+}
+
+static int sdio_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops sdio_pm_ops = {
+ .suspend = sdio_suspend,
+ .resume = sdio_resume,
+};
+
+static struct sdio_driver sdio_driver = {
+ .name = "xradio_wlan",
+ .id_table = xradio_sdio_ids,
+ .probe = sdio_probe,
+ .remove = sdio_remove,
+ .drv = {
+ .owner = THIS_MODULE,
+ .pm = &sdio_pm_ops,
+ }
+};
+
+
+int xradio_sdio_register(){
+ return sdio_register_driver(&sdio_driver);
+}
+
+void xradio_sdio_unregister(){
+ sdio_unregister_driver(&sdio_driver);
+}
+
+MODULE_DEVICE_TABLE(sdio, xradio_sdio_ids);
diff --git a/drivers/net/wireless/xradio/sdio.h b/drivers/net/wireless/xradio/sdio.h
new file mode 100644
index 00000000..ea3c45a0
--- /dev/null
+++ b/drivers/net/wireless/xradio/sdio.h
@@ -0,0 +1,17 @@
+#ifndef __XRADIO_SDIO_H
+#define __XRADIO_SDIO_H
+
+size_t sdio_align_len(struct xradio_common *self, size_t size);
+void sdio_lock(struct xradio_common *self);
+void sdio_unlock(struct xradio_common *self);
+int sdio_set_blk_size(struct xradio_common *self, size_t size);
+int sdio_data_read(struct xradio_common *self, unsigned int addr, void *dst,
+ int count);
+int sdio_data_write(struct xradio_common *self, unsigned int addr, const void *src,
+ int count);
+int sdio_pm(struct xradio_common *self, bool suspend);
+
+int xradio_sdio_register(void);
+void xradio_sdio_unregister(void);
+
+#endif
diff --git a/drivers/net/wireless/xradio/sta.c b/drivers/net/wireless/xradio/sta.c
new file mode 100644
index 00000000..e9820b8a
--- /dev/null
+++ b/drivers/net/wireless/xradio/sta.c
@@ -0,0 +1,2987 @@
+/*
+ * STA APIs for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/if_arp.h>
+#include <linux/ipv6.h>
+#include <linux/icmpv6.h>
+#include <net/ndisc.h>
+
+#include "xradio.h"
+#include "sta.h"
+#include "ap.h"
+#include "keys.h"
+#include "fwio.h"
+#include "bh.h"
+#include "wsm.h"
+#ifdef ROAM_OFFLOAD
+#include <net/netlink.h>
+#endif /*ROAM_OFFLOAD*/
+#ifdef CONFIG_XRADIO_TESTMODE
+#include "nl80211_testmode_msg_copy.h"
+#include <net/netlink.h>
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+#include "net/mac80211.h"
+
+#ifdef TES_P2P_0002_ROC_RESTART
+#include <linux/time.h>
+#endif
+
+#define WEP_ENCRYPT_HDR_SIZE 4
+#define WEP_ENCRYPT_TAIL_SIZE 4
+#define WPA_ENCRYPT_HDR_SIZE 8
+#define WPA_ENCRYPT_TAIL_SIZE 12
+#define WPA2_ENCRYPT_HDR_SIZE 8
+#define WPA2_ENCRYPT_TAIL_SIZE 8
+#define WAPI_ENCRYPT_HDR_SIZE 18
+#define WAPI_ENCRYPT_TAIL_SIZE 16
+#define MAX_ARP_REPLY_TEMPLATE_SIZE 120
+#ifdef CONFIG_XRADIO_TESTMODE
+const int xradio_1d_to_ac[8] = {
+ IEEE80211_AC_BE,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BK,
+ IEEE80211_AC_BE,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VI,
+ IEEE80211_AC_VO,
+ IEEE80211_AC_VO
+};
+
+/**
+ * enum xradio_ac_numbers - AC numbers as used in xradio
+ * @XRADIO_AC_VO: voice
+ * @XRADIO_AC_VI: video
+ * @XRADIO_AC_BE: best effort
+ * @XRADIO_AC_BK: background
+ */
+enum xradio_ac_numbers {
+ XRADIO_AC_VO = 0,
+ XRADIO_AC_VI = 1,
+ XRADIO_AC_BE = 2,
+ XRADIO_AC_BK = 3,
+};
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+static inline void __xradio_free_event_queue(struct list_head *list)
+{
+ while (!list_empty(list)) {
+ struct xradio_wsm_event *event =
+ list_first_entry(list, struct xradio_wsm_event,link);
+ list_del(&event->link);
+ kfree(event);
+ }
+}
+
+#ifdef CONFIG_XRADIO_TESTMODE
+/* User priority to WSM queue mapping */
+const int xradio_priority_to_queueId[8] = {
+ WSM_QUEUE_BEST_EFFORT,
+ WSM_QUEUE_BACKGROUND,
+ WSM_QUEUE_BACKGROUND,
+ WSM_QUEUE_BEST_EFFORT,
+ WSM_QUEUE_VIDEO,
+ WSM_QUEUE_VIDEO,
+ WSM_QUEUE_VOICE,
+ WSM_QUEUE_VOICE
+};
+#endif /*CONFIG_XRADIO_TESTMODE*/
+static inline void __xradio_bf_configure(struct xradio_vif *priv)
+{
+ priv->bf_table.numOfIEs = __cpu_to_le32(3);
+ priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC;
+ priv->bf_table.entry[0].actionFlags =
+ WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED;
+
+ priv->bf_table.entry[0].oui[0] = 0x50;
+ priv->bf_table.entry[0].oui[1] = 0x6F;
+ priv->bf_table.entry[0].oui[2] = 0x9A;
+
+ priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO;
+ priv->bf_table.entry[1].actionFlags =
+ WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED;
+
+ priv->bf_table.entry[2].ieId = WLAN_EID_HT_OPERATION;
+ priv->bf_table.entry[2].actionFlags =
+ WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED;
+
+ priv->bf_control.enabled = WSM_BEACON_FILTER_ENABLE;
+}
+
+/* ******************************************************************** */
+/* STA API */
+
+int xradio_start(struct ieee80211_hw *dev)
+{
+ struct xradio_common *hw_priv = dev->priv;
+ int ret = 0;
+
+
+ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+ hw_priv->driver_ready, 3*HZ) <= 0) {
+ wiphy_err(dev->wiphy, "driver is not ready!\n");
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&hw_priv->conf_mutex);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ spin_lock_bh(&hw_priv->tsm_lock);
+ memset(&hw_priv->tsm_stats, 0, sizeof(struct xr_tsm_stats));
+ memset(&hw_priv->tsm_info, 0, sizeof(struct xradio_tsm_info));
+ spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ memcpy(hw_priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
+ hw_priv->softled_state = 0;
+
+ ret = xradio_setup_mac(hw_priv);
+ if (SYS_WARN(ret)) {
+ wiphy_err(dev->wiphy, "xradio_setup_mac failed(%d)\n", ret);
+ goto out;
+ }
+
+out:
+ mutex_unlock(&hw_priv->conf_mutex);
+ return ret;
+}
+
+void xradio_stop(struct ieee80211_hw *dev)
+{
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv = NULL;
+ LIST_HEAD(list);
+ int i;
+
+ wsm_lock_tx(hw_priv);
+ while (down_trylock(&hw_priv->scan.lock)) {
+ /* Scan is in progress. Force it to stop. */
+ hw_priv->scan.req = NULL;
+ schedule();
+ }
+ up(&hw_priv->scan.lock);
+
+ cancel_delayed_work_sync(&hw_priv->scan.probe_work);
+ cancel_delayed_work_sync(&hw_priv->scan.timeout);
+#ifdef CONFIG_XRADIO_TESTMODE
+ cancel_delayed_work_sync(&hw_priv->advance_scan_timeout);
+#endif
+ flush_workqueue(hw_priv->workqueue);
+ del_timer_sync(&hw_priv->ba_timer);
+
+ mutex_lock(&hw_priv->conf_mutex);
+
+ hw_priv->softled_state = 0;
+ /* xradio_set_leds(hw_priv); */
+
+ spin_lock(&hw_priv->event_queue_lock);
+ list_splice_init(&hw_priv->event_queue, &list);
+ spin_unlock(&hw_priv->event_queue_lock);
+ __xradio_free_event_queue(&list);
+
+ for (i = 0; i < 4; i++)
+ xradio_queue_clear(&hw_priv->tx_queue[i], XRWL_ALL_IFS);
+
+ /* HACK! */
+ if (atomic_xchg(&hw_priv->tx_lock, 1) != 1)
+ wiphy_debug(dev->wiphy, "TX is force-unlocked due to stop request.\n");
+
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (!priv)
+ continue;
+ priv->mode = NL80211_IFTYPE_UNSPECIFIED;
+ priv->listening = false;
+ priv->delayed_link_loss = 0;
+ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE;
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ cancel_delayed_work_sync(&priv->link_id_gc_work);
+ del_timer_sync(&priv->mcast_timeout);
+ }
+
+ wsm_unlock_tx(hw_priv);
+
+ mutex_unlock(&hw_priv->conf_mutex);
+}
+
+int xradio_add_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ int ret;
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv;
+ struct xradio_vif **drv_priv = (void *)vif->drv_priv;
+#ifndef P2P_MULTIVIF
+ int i;
+ if (atomic_read(&hw_priv->num_vifs) >= XRWL_MAX_VIFS)
+ return -EOPNOTSUPP;
+#endif
+
+ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+ hw_priv->driver_ready, 3*HZ) <= 0) {
+ wiphy_err(dev->wiphy, "driver is not ready!\n");
+ return -ETIMEDOUT;
+ }
+
+ /* fix the problem that when connected,then deauth */
+ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER;
+ vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD;
+
+ priv = xrwl_get_vif_from_ieee80211(vif);
+ atomic_set(&priv->enabled, 0);
+
+ *drv_priv = priv;
+ /* __le32 auto_calibration_mode = __cpu_to_le32(1); */
+
+ mutex_lock(&hw_priv->conf_mutex);
+
+ priv->mode = vif->type;
+
+ spin_lock(&hw_priv->vif_list_lock);
+ if (atomic_read(&hw_priv->num_vifs) < XRWL_MAX_VIFS) {
+#ifdef P2P_MULTIVIF
+ //~dgp in this mode it looks like virtual interface 2 shouldn't
+ // be used as other parts of the driver complain
+ // but when the second interface is added it gets id 2 here
+ if (!memcmp(vif->addr, hw_priv->addresses[0].addr, ETH_ALEN)) {
+ priv->if_id = 0;
+ } else if (!memcmp(vif->addr, hw_priv->addresses[1].addr,
+ ETH_ALEN)) {
+ priv->if_id = 1;//2;
+ } else if (!memcmp(vif->addr, hw_priv->addresses[2].addr,
+ ETH_ALEN)) {
+ priv->if_id = 2;//1;
+ }
+ wiphy_debug(dev->wiphy, "if_id %d mac %pM\n",
+ priv->if_id, vif->addr);
+#else
+ for (i = 0; i < XRWL_MAX_VIFS; i++)
+ if (!memcmp(vif->addr, hw_priv->addresses[i].addr, ETH_ALEN))
+ break;
+ if (i == XRWL_MAX_VIFS) {
+ spin_unlock(&hw_priv->vif_list_lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+ return -EINVAL;
+ }
+ priv->if_id = i;
+#endif
+ hw_priv->if_id_slot |= BIT(priv->if_id);
+ priv->hw_priv = hw_priv;
+ priv->hw = dev;
+ priv->vif = vif;
+ hw_priv->vif_list[priv->if_id] = vif;
+ atomic_inc(&hw_priv->num_vifs);
+ } else {
+ spin_unlock(&hw_priv->vif_list_lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+ return -EOPNOTSUPP;
+ }
+ spin_unlock(&hw_priv->vif_list_lock);
+ /* TODO:COMBO :Check if MAC address matches the one expected by FW */
+ memcpy(hw_priv->mac_addr, vif->addr, ETH_ALEN);
+
+ /* Enable auto-calibration */
+ /* Exception in subsequent channel switch; disabled.
+ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
+ &auto_calibration_mode, sizeof(auto_calibration_mode)));
+ */
+ wiphy_debug(dev->wiphy, "Interface ID:%d of type:%d added\n", priv->if_id, priv->mode);
+ mutex_unlock(&hw_priv->conf_mutex);
+
+ xradio_vif_setup(priv);
+
+ ret = SYS_WARN(xradio_setup_mac_pvif(priv));
+
+ return ret;
+}
+
+void xradio_remove_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif)
+{
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ int i;
+ bool is_htcapie = false;
+ struct xradio_vif *tmp_priv;
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+ wiphy_warn(dev->wiphy, "!!! vif_id=%d\n", priv->if_id);
+ atomic_set(&priv->enabled, 0);
+ down(&hw_priv->scan.lock);
+ if(priv->join_status == XRADIO_JOIN_STATUS_STA){
+ if (atomic_xchg(&priv->delayed_unjoin, 0)) {
+ wsm_unlock_tx(hw_priv);
+ wiphy_err(dev->wiphy, "delayed_unjoin exist!\n");
+ }
+ cancel_work_sync(&priv->unjoin_work);
+ wsm_lock_tx(hw_priv);
+ xradio_unjoin_work(&priv->unjoin_work);
+ }
+ mutex_lock(&hw_priv->conf_mutex);
+ xradio_tx_queues_lock(hw_priv);
+ wsm_lock_tx(hw_priv);
+ switch (priv->join_status) {
+ case XRADIO_JOIN_STATUS_AP:
+ for (i = 0; priv->link_id_map; ++i) {
+ if (priv->link_id_map & BIT(i)) {
+ xrwl_unmap_link(priv, i);
+ priv->link_id_map &= ~BIT(i);
+ }
+ }
+ memset(priv->link_id_db, 0,
+ sizeof(priv->link_id_db));
+ priv->sta_asleep_mask = 0;
+ priv->enable_beacon = false;
+ priv->tx_multicast = false;
+ priv->aid0_bit_set = false;
+ priv->buffered_multicasts = false;
+ priv->pspoll_mask = 0;
+ reset.link_id = 0;
+ wsm_reset(hw_priv, &reset, priv->if_id);
+ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
+ xradio_for_each_vif(hw_priv, tmp_priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv)
+#else
+ if (!tmp_priv)
+#endif
+ continue;
+ if ((tmp_priv->join_status == XRADIO_JOIN_STATUS_STA) && tmp_priv->htcap)
+ is_htcapie = true;
+ }
+
+ if (is_htcapie) {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE;
+ sta_printk(XRADIO_DBG_NIY, "AP REMOVE HTCAP 11N %d\n",hw_priv->vif0_throttle);
+ } else {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE;
+ sta_printk(XRADIO_DBG_NIY, "AP REMOVE 11BG %d\n",hw_priv->vif0_throttle);
+ }
+ break;
+ case XRADIO_JOIN_STATUS_MONITOR:
+ xradio_disable_listening(priv);
+ break;
+ default:
+ break;
+ }
+ /* TODO:COMBO: Change Queue Module */
+ __xradio_flush(hw_priv, false, priv->if_id);
+
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ cancel_delayed_work_sync(&priv->link_id_gc_work);
+ cancel_delayed_work_sync(&priv->join_timeout);
+ cancel_delayed_work_sync(&priv->set_cts_work);
+ cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
+
+ del_timer_sync(&priv->mcast_timeout);
+ /* TODO:COMBO: May be reset of these variables "delayed_link_loss and
+ * join_status to default can be removed as dev_priv will be freed by
+ * mac80211 */
+ priv->delayed_link_loss = 0;
+ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE;
+ wsm_unlock_tx(hw_priv);
+
+ if ((priv->if_id ==1) && (priv->mode == NL80211_IFTYPE_AP
+ || priv->mode == NL80211_IFTYPE_P2P_GO)) {
+ hw_priv->is_go_thru_go_neg = false;
+ }
+ spin_lock(&hw_priv->vif_list_lock);
+ spin_lock(&priv->vif_lock);
+ hw_priv->vif_list[priv->if_id] = NULL;
+ hw_priv->if_id_slot &= (~BIT(priv->if_id));
+ atomic_dec(&hw_priv->num_vifs);
+ if (atomic_read(&hw_priv->num_vifs) == 0) {
+ xradio_free_keys(hw_priv);
+ memset(hw_priv->mac_addr, 0, ETH_ALEN);
+ }
+ spin_unlock(&priv->vif_lock);
+ spin_unlock(&hw_priv->vif_list_lock);
+ priv->listening = false;
+
+ xradio_debug_release_priv(priv);
+
+ xradio_tx_queues_unlock(hw_priv);
+ mutex_unlock(&hw_priv->conf_mutex);
+
+ if (atomic_read(&hw_priv->num_vifs) == 0)
+ flush_workqueue(hw_priv->workqueue);
+ memset(priv, 0, sizeof(struct xradio_vif));
+ up(&hw_priv->scan.lock);
+}
+
+int xradio_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p)
+{
+ int ret = 0;
+ wiphy_debug(dev->wiphy, "changing interface type; new type=%d(%d), p2p=%d(%d)\n",
+ new_type, vif->type, p2p, vif->p2p);
+ if (new_type != vif->type || vif->p2p != p2p) {
+ xradio_remove_interface(dev, vif);
+ vif->type = new_type;
+ vif->p2p = p2p;
+ ret = xradio_add_interface(dev, vif);
+ }
+
+ return ret;
+}
+
+int xradio_config(struct ieee80211_hw *dev, u32 changed)
+{
+ int ret = 0;
+ struct xradio_common *hw_priv = dev->priv;
+ struct ieee80211_conf *conf = &dev->conf;
+#ifdef CONFIG_XRADIO_TESTMODE
+ int max_power_level = 0;
+ int min_power_level = 0;
+#endif
+ /* TODO:COMBO: adjust to multi vif interface
+ * IEEE80211_CONF_CHANGE_IDLE is still handled per xradio_vif*/
+ int if_id = 0;
+ struct xradio_vif *priv;
+
+
+ if (changed &
+ (IEEE80211_CONF_CHANGE_MONITOR|IEEE80211_CONF_CHANGE_IDLE)) {
+ /* TBD: It looks like it's transparent
+ * there's a monitor interface present -- use this
+ * to determine for example whether to calculate
+ * timestamps for packets or not, do not use instead
+ * of filter flags! */
+ wiphy_debug(dev->wiphy, "ignore IEEE80211_CONF_CHANGE_MONITOR (%d)"
+ "IEEE80211_CONF_CHANGE_IDLE (%d)\n",
+ (changed & IEEE80211_CONF_CHANGE_MONITOR) ? 1 : 0,
+ (changed & IEEE80211_CONF_CHANGE_IDLE) ? 1 : 0);
+ return ret;
+ }
+
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+ /* TODO: IEEE80211_CONF_CHANGE_QOS */
+ /* TODO:COMBO:Change when support is available mac80211*/
+ if (changed & IEEE80211_CONF_CHANGE_POWER) {
+ /*hw_priv->output_power = conf->power_level;*/
+ hw_priv->output_power = 20;
+#ifdef CONFIG_XRADIO_TESTMODE
+ /* Testing if Power Level to set is out of device power range */
+ if (conf->chan_conf->channel->band == NL80211_BAND_2GHZ) {
+ max_power_level = hw_priv->txPowerRange[0].max_power_level;
+ min_power_level = hw_priv->txPowerRange[0].min_power_level;
+ } else {
+ max_power_level = hw_priv->txPowerRange[1].max_power_level;
+ min_power_level = hw_priv->txPowerRange[1].min_power_level;
+ }
+ if (hw_priv->output_power > max_power_level)
+ hw_priv->output_power = max_power_level;
+ else if (hw_priv->output_power < min_power_level)
+ hw_priv->output_power = min_power_level;
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+ wiphy_debug(dev->wiphy, "Config Tx power=%d, but real=%d\n",
+ conf->power_level, hw_priv->output_power);
+ SYS_WARN(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, if_id));
+ }
+
+ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) &&
+ (hw_priv->channel != conf->chandef.chan)) {
+ /* Switch Channel commented for CC Mode */
+ struct ieee80211_channel *ch = conf->chandef.chan;
+ wiphy_debug(dev->wiphy, "Freq %d (wsm ch: %d).\n",
+ ch->center_freq, ch->hw_value);
+ /* Earlier there was a call to __xradio_flush().
+ Removed as deemed unnecessary */
+ hw_priv->channel = ch;
+ hw_priv->channel_changed = 1;
+ }
+
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+ return ret;
+}
+
+void xradio_update_filtering(struct xradio_vif *priv)
+{
+ int ret;
+ bool bssid_filtering = !priv->rx_filter.bssid;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ static struct wsm_beacon_filter_control bf_disabled = {
+ .enabled = 0,
+ .bcn_count = 1,
+ };
+ bool ap_mode = 0;
+ static struct wsm_beacon_filter_table bf_table_auto = {
+ .numOfIEs = __cpu_to_le32(2),
+ .entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC,
+ .entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ .entry[0].oui[0] = 0x50,
+ .entry[0].oui[1] = 0x6F,
+ .entry[0].oui[2] = 0x9A,
+
+ .entry[1].ieId = WLAN_EID_HT_OPERATION,
+ .entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
+ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
+ WSM_BEACON_FILTER_IE_HAS_APPEARED,
+ };
+ static struct wsm_beacon_filter_control bf_auto = {
+ .enabled = WSM_BEACON_FILTER_ENABLE |
+ WSM_BEACON_FILTER_AUTO_ERP,
+ .bcn_count = 1,
+ };
+
+
+ bf_auto.bcn_count = priv->bf_control.bcn_count;
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_PASSIVE)
+ return;
+ else if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR)
+ bssid_filtering = false;
+
+ if (priv->vif && (priv->vif->type == NL80211_IFTYPE_AP))
+ ap_mode = true;
+ /*
+ * When acting as p2p client being connected to p2p GO, in order to
+ * receive frames from a different p2p device, turn off bssid filter.
+ *
+ * WARNING: FW dependency!
+ * This can only be used with FW WSM371 and its successors.
+ * In that FW version even with bssid filter turned off,
+ * device will block most of the unwanted frames.
+ */
+ if (priv->vif && priv->vif->p2p)
+ bssid_filtering = false;
+
+ ret = wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
+ if (!ret && !ap_mode) {
+ if (priv->vif) {
+ if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type)
+ ret = wsm_set_beacon_filter_table(hw_priv, &priv->bf_table, priv->if_id);
+ else
+ ret = wsm_set_beacon_filter_table(hw_priv, &bf_table_auto, priv->if_id);
+ } else
+ SYS_WARN(1);
+ }
+ if (!ret && !ap_mode) {
+ if (priv->disable_beacon_filter)
+ ret = wsm_beacon_filter_control(hw_priv, &bf_disabled, priv->if_id);
+ else {
+ if (priv->vif) {
+ if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type)
+ ret = wsm_beacon_filter_control(hw_priv, &priv->bf_control,
+ priv->if_id);
+ else
+ ret = wsm_beacon_filter_control(hw_priv, &bf_auto, priv->if_id);
+ } else
+ SYS_WARN(1);
+ }
+ }
+
+ if (!ret)
+ ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, priv->if_id);
+#if 0
+ if (!ret) {
+ ret = wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id);
+ }
+#endif
+ if (ret)
+ wiphy_debug(priv->hw_priv->hw->wiphy, "Update filtering failed: %d.\n", ret);
+ return;
+}
+
+void xradio_update_filtering_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif,
+ update_filtering_work);
+
+ xradio_update_filtering(priv);
+}
+
+void xradio_set_beacon_wakeup_period_work(struct work_struct *work)
+{
+
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, set_beacon_wakeup_period_work);
+
+
+#ifdef XRADIO_USE_LONG_DTIM_PERIOD
+{
+ int join_dtim_period_extend;
+ if (priv->join_dtim_period <= 3) {
+ join_dtim_period_extend = priv->join_dtim_period * 3;
+ } else if (priv->join_dtim_period <= 5) {
+ join_dtim_period_extend = priv->join_dtim_period * 2;
+ } else {
+ join_dtim_period_extend = priv->join_dtim_period;
+ }
+ SYS_WARN(wsm_set_beacon_wakeup_period(priv->hw_priv,
+ priv->beacon_int * join_dtim_period_extend >
+ MAX_BEACON_SKIP_TIME_MS ? 1 : join_dtim_period_extend,
+ 0, priv->if_id));
+}
+#else
+ SYS_WARN(wsm_set_beacon_wakeup_period(priv->hw_priv,
+ priv->beacon_int * priv->join_dtim_period >
+ MAX_BEACON_SKIP_TIME_MS ? 1 :priv->join_dtim_period,
+ 0, priv->if_id));
+#endif
+}
+
+u64 xradio_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv = NULL;
+ static u8 broadcast_ipv6[ETH_ALEN] = {
+ 0x33, 0x33, 0x00, 0x00, 0x00, 0x01
+ };
+ static u8 broadcast_ipv4[ETH_ALEN] = {
+ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
+ };
+
+ int i= 0;
+ xradio_for_each_vif(hw_priv,priv,i) {
+ struct netdev_hw_addr *ha = NULL;
+ int count = 0;
+ if ((!priv))
+ continue;
+#ifdef P2P_MULTIVIF
+ if (priv->if_id ==XRWL_GENERIC_IF_ID)
+ return netdev_hw_addr_list_count(mc_list);
+#endif
+
+ /* Disable multicast filtering */
+ priv->has_multicast_subscription = false;
+ memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
+ if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
+ return 0;
+
+ /* Enable if requested */
+ netdev_hw_addr_list_for_each(ha, mc_list) {
+ sta_printk(XRADIO_DBG_MSG, "multicast: %pM\n", ha->addr);
+ memcpy(&priv->multicast_filter.macAddress[count], ha->addr, ETH_ALEN);
+ if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) &&
+ memcmp(ha->addr, broadcast_ipv6, ETH_ALEN))
+ priv->has_multicast_subscription = true;
+ count++;
+ }
+ if (count) {
+ priv->multicast_filter.enable = __cpu_to_le32(1);
+ priv->multicast_filter.numOfAddresses = __cpu_to_le32(count);
+ }
+ }
+ return netdev_hw_addr_list_count(mc_list);
+}
+
+void xradio_configure_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv = NULL;
+ int i = 0;
+
+ /* delete umac warning */
+ if (hw_priv->vif_list[0] == NULL && hw_priv->vif_list[1] == NULL
+#ifdef P2P_MULTIVIF
+ && hw_priv->vif_list[2] == NULL)
+#else
+ )
+#endif
+
+ *total_flags &= ~(1<<31);
+
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if(NULL == priv)
+ continue;
+#ifdef P2P_MULTIVIF
+ if (priv->if_id == XRWL_GENERIC_IF_ID) {
+ *total_flags &= ~(1<<31);
+ continue;
+ }
+#endif
+
+#if 0
+ bool listening = !!(*total_flags &
+ (FIF_PROMISC_IN_BSS |
+ FIF_OTHER_BSS |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ));
+#endif
+
+ *total_flags &= FIF_OTHER_BSS |
+ FIF_FCSFAIL |
+ FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ;
+
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+
+ priv->rx_filter.promiscuous = 0;
+ priv->rx_filter.bssid = (*total_flags &
+ (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 1 : 0;
+ priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
+ priv->bf_control.bcn_count = (*total_flags &
+ (FIF_BCN_PRBRESP_PROMISC |
+ FIF_PROBE_REQ)) ? 1 : 0;
+
+ /*add for handle ap FIF_PROBE_REQ message,*/
+ priv->rx_filter.promiscuous = 0;
+ priv->rx_filter.fcs = 0;
+ if(NL80211_IFTYPE_AP == priv->vif->type){
+ priv->bf_control.bcn_count = 1;
+ priv->rx_filter.bssid = 1;
+ }else{
+ priv->bf_control.bcn_count = 0;
+ priv->rx_filter.bssid = 0;
+ }
+#if 0
+ if (priv->listening ^ listening) {
+ priv->listening = listening;
+ wsm_lock_tx(hw_priv);
+ xradio_update_listening(priv, listening);
+ wsm_unlock_tx(hw_priv);
+ }
+#endif
+ xradio_update_filtering(priv);
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+ }
+}
+
+int xradio_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params)
+{
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ int ret = 0;
+ /* To prevent re-applying PM request OID again and again*/
+ bool old_uapsdFlags;
+
+ wiphy_debug(dev->wiphy, "configuring tx for vif %d\n", priv->if_id);
+
+ if (SYS_WARN(!priv))
+ return -EOPNOTSUPP;
+
+#ifdef P2P_MULTIVIF
+ if (priv->if_id == XRWL_GENERIC_IF_ID)
+ return 0;
+#endif
+
+ mutex_lock(&hw_priv->conf_mutex);
+
+ if (queue < dev->queues) {
+ old_uapsdFlags = priv->uapsd_info.uapsdFlags;
+
+ WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
+ ret = wsm_set_tx_queue_params(hw_priv,
+ &priv->tx_queue_params.params[queue],
+ queue, priv->if_id);
+ if (ret) {
+ wiphy_err(dev->wiphy, "wsm_set_tx_queue_params failed!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ WSM_EDCA_SET(&priv->edca, queue, params->aifs,
+ params->cw_min, params->cw_max,
+ params->txop, 0xc8, params->uapsd);
+ /* sta role is not support the uapsd */
+ if (priv->mode == NL80211_IFTYPE_STATION ||
+ priv->mode == NL80211_IFTYPE_P2P_CLIENT)
+ priv->edca.params[queue].uapsdEnable = 0;
+
+ ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
+ if (ret) {
+ wiphy_err(dev->wiphy, "wsm_set_edca_params failed!\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (priv->mode == NL80211_IFTYPE_STATION) {
+ ret = xradio_set_uapsd_param(priv, &priv->edca);
+ if (!ret && priv->setbssparams_done &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (old_uapsdFlags != priv->uapsd_info.uapsdFlags))
+ xradio_set_pm(priv, &priv->powersave_mode);
+ }
+ } else {
+ wiphy_err(dev->wiphy, "queue is to large!\n");
+ ret = -EINVAL;
+ }
+
+out:
+ mutex_unlock(&hw_priv->conf_mutex);
+ return ret;
+}
+
+int xradio_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct xradio_common *hw_priv = dev->priv;
+
+ memcpy(stats, &hw_priv->stats, sizeof(*stats));
+ return 0;
+}
+
+/*
+int xradio_get_tx_stats(struct ieee80211_hw *dev,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ int i;
+ struct xradio_common *priv = dev->priv;
+
+ for (i = 0; i < dev->queues; ++i)
+ xradio_queue_get_stats(&priv->tx_queue[i], &stats[i]);
+
+ return 0;
+}
+*/
+
+/* for ps debug */
+#ifdef CONFIG_XRADIO_DEBUGFS
+u8 ps_disable = 0;
+u8 ps_idleperiod = 0;
+u8 ps_changeperiod = 0;
+#endif
+
+int xradio_set_pm(struct xradio_vif *priv, const struct wsm_set_pm *arg)
+{
+ struct wsm_set_pm pm = *arg;
+
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+ if (ps_disable)
+ pm.pmMode = WSM_PSM_ACTIVE;
+ if (ps_idleperiod) {
+ pm.fastPsmIdlePeriod = ps_idleperiod << 1;
+ pm.apPsmChangePeriod = ps_changeperiod << 1;
+ }
+#endif
+
+ if (priv->uapsd_info.uapsdFlags != 0)
+ pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG;
+
+ if (memcmp(&pm, &priv->firmware_ps_mode, sizeof(struct wsm_set_pm))) {
+ priv->firmware_ps_mode = pm;
+ return wsm_set_pm(priv->hw_priv, &pm, priv->if_id);
+ } else {
+ return 0;
+ }
+}
+
+void xradio_wep_key_work(struct work_struct *work)
+{
+ struct xradio_vif *priv = container_of(work, struct xradio_vif , wep_key_work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id);
+ struct xradio_queue *queue = &hw_priv->tx_queue[queueId];
+ __le32 wep_default_key_id = __cpu_to_le32(priv->wep_default_key_id);
+
+
+ SYS_BUG(queueId >= 4);
+
+ sta_printk(XRADIO_DBG_MSG, "Setting default WEP key: %d\n",
+ priv->wep_default_key_id);
+
+ wsm_flush_tx(hw_priv);
+ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
+ &wep_default_key_id, sizeof(wep_default_key_id),
+ priv->if_id));
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_requeue(hw_priv, queue, hw_priv->pending_frame_id, true);
+#else
+ xradio_queue_requeue(queue, hw_priv->pending_frame_id, true);
+#endif
+ wsm_unlock_tx(hw_priv);
+}
+
+int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ int ret = 0;
+ __le32 val32;
+ struct xradio_vif *priv = NULL;
+ int i =0;
+ int if_id;
+
+
+ xradio_for_each_vif(hw_priv,priv,i) {
+ if (!priv)
+ continue;
+ if_id = priv->if_id;
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+
+ if (value != (u32) -1)
+ val32 = __cpu_to_le32(value);
+ else
+ val32 = 0; /* disabled */
+
+ /* mutex_lock(&priv->conf_mutex); */
+ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
+ &val32, sizeof(val32), if_id));
+ /* mutex_unlock(&priv->conf_mutex); */
+ }
+ return ret;
+}
+
+/* TODO: COMBO: Flush only a particular interface specific parts */
+int __xradio_flush(struct xradio_common *hw_priv, bool drop, int if_id)
+{
+ int i, ret;
+ struct xradio_vif *priv =
+ __xrwl_hwpriv_to_vifpriv(hw_priv, if_id);
+
+
+ for (;;) {
+ /* TODO: correct flush handling is required when dev_stop.
+ * Temporary workaround: 2s
+ */
+ if (drop) {
+ for (i = 0; i < 4; ++i)
+ xradio_queue_clear(&hw_priv->tx_queue[i],if_id);
+ } else if(!hw_priv->bh_error){
+ ret = wait_event_timeout(
+ hw_priv->tx_queue_stats.wait_link_id_empty,
+ xradio_queue_stats_is_empty(&hw_priv->tx_queue_stats, -1, if_id),
+ 2 * HZ);
+ } else { //add by yangfh, don't wait when bh error
+ sta_printk(XRADIO_DBG_ERROR, " %s:bh_error occur.\n", __func__);
+ ret = -1;
+ break;
+ }
+
+ if (!drop && unlikely(ret <= 0)) {
+ sta_printk(XRADIO_DBG_ERROR, " %s: timeout...\n", __func__);
+ ret = -ETIMEDOUT;
+ break;
+ } else {
+ ret = 0;
+ }
+
+ wsm_vif_lock_tx(priv);
+ if (unlikely(!xradio_queue_stats_is_empty(&hw_priv->tx_queue_stats,
+ -1, if_id))) {
+ /* Highly unlekely: WSM requeued frames. */
+ wsm_unlock_tx(hw_priv);
+ continue;
+ }
+ wsm_unlock_tx(hw_priv);
+ break;
+ }
+ return ret;
+}
+
+void xradio_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop)
+{
+ //struct xradio_vif *priv = NULL;
+ struct xradio_common *hw_priv = hw->priv;
+ int i = 0;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+
+ /*TODO:COMBO: reenable this part of code when flush callback
+ * is implemented per vif */
+ /*switch (hw_priv->mode) {
+ case NL80211_IFTYPE_MONITOR:
+ drop = true;
+ break;
+ case NL80211_IFTYPE_AP:
+ if (!hw_priv->enable_beacon)
+ drop = true;
+ break;
+ }*/
+
+ //if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
+ // return;
+
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if(NULL == priv)
+ continue;
+ if ((hw_priv->if_id_slot & BIT(priv->if_id)))
+ __xradio_flush(hw_priv, drop, priv->if_id);
+ }
+ return;
+}
+
+int xradio_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan,
+ int duration, enum ieee80211_roc_type type)
+{
+ int ret = 0;
+ struct xradio_common *hw_priv = hw->priv;
+ struct xradio_vif *priv = NULL;
+ int i = 0;
+ int if_id;
+#ifdef TES_P2P_0002_ROC_RESTART
+ struct timeval TES_P2P_0002_tmval;
+#endif
+
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ do_gettimeofday(&TES_P2P_0002_tmval);
+ TES_P2P_0002_roc_dur = (s32)duration;
+ TES_P2P_0002_roc_sec = (s32)TES_P2P_0002_tmval.tv_sec;
+ TES_P2P_0002_roc_usec = (s32)TES_P2P_0002_tmval.tv_usec;
+#endif
+
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if(NULL == priv)
+ continue;
+ if_id = priv->if_id;
+
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_WARN, "ROC IN %d ch %d\n",
+ priv->if_id, chan->hw_value);
+#endif
+ /* default only p2p interface if_id can remain on */
+ if((priv->if_id == 0) || (priv->if_id == 1))
+ continue;
+ hw_priv->roc_if_id = priv->if_id;
+ ret = SYS_WARN(__xradio_flush(hw_priv, false, if_id));
+ xradio_enable_listening(priv, chan);
+
+ if (!ret) {
+ atomic_set(&hw_priv->remain_on_channel, 1);
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->rem_chan_timeout,
+ duration * HZ / 1000);
+ priv->join_status = XRADIO_JOIN_STATUS_MONITOR;
+ ieee80211_ready_on_channel(hw);
+ } else {
+ hw_priv->roc_if_id = -1;
+ up(&hw_priv->scan.lock);
+ }
+
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_WARN, "ROC OUT %d\n", priv->if_id);
+#endif
+ }
+ /* set the channel to supplied ieee80211_channel pointer, if it
+ is not set. This is to remove the crash while sending a probe res
+ in listen state. Later channel will updated on
+ IEEE80211_CONF_CHANGE_CHANNEL event*/
+ if(!hw_priv->channel) {
+ hw_priv->channel = chan;
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ return ret;
+}
+
+int xradio_cancel_remain_on_channel(struct ieee80211_hw *hw)
+{
+ struct xradio_common *hw_priv = hw->priv;
+
+
+ sta_printk(XRADIO_DBG_NIY, "Cancel remain on channel\n");
+#ifdef TES_P2P_0002_ROC_RESTART
+ if (TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) {
+ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
+ sta_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE][Cancel ROC]\n");
+ }
+#endif
+
+ if (atomic_read(&hw_priv->remain_on_channel))
+ cancel_delayed_work_sync(&hw_priv->rem_chan_timeout);
+
+ if (atomic_read(&hw_priv->remain_on_channel))
+ xradio_rem_chan_timeout(&hw_priv->rem_chan_timeout.work);
+
+ return 0;
+}
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void xradio_channel_switch_cb(struct xradio_common *hw_priv)
+{
+ wsm_unlock_tx(hw_priv);
+}
+
+void xradio_free_event_queue(struct xradio_common *hw_priv)
+{
+ LIST_HEAD(list);
+
+
+ spin_lock(&hw_priv->event_queue_lock);
+ list_splice_init(&hw_priv->event_queue, &list);
+ spin_unlock(&hw_priv->event_queue_lock);
+
+ __xradio_free_event_queue(&list);
+}
+
+void xradio_event_handler(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, event_handler);
+ struct xradio_vif *priv;
+ struct xradio_wsm_event *event;
+ LIST_HEAD(list);
+
+
+ spin_lock(&hw_priv->event_queue_lock);
+ list_splice_init(&hw_priv->event_queue, &list);
+ spin_unlock(&hw_priv->event_queue_lock);
+
+ mutex_lock(&hw_priv->conf_mutex);
+ list_for_each_entry(event, &list, link) {
+ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, event->if_id);
+ if (!priv) {
+ sta_printk(XRADIO_DBG_WARN, "[CQM] Event for non existing "
+ "interface, ignoring.\n");
+ continue;
+ }
+ switch (event->evt.eventId) {
+ case WSM_EVENT_ERROR:
+ /* I even don't know what is it about.. */
+ //STUB();
+ break;
+ case WSM_EVENT_BSS_LOST:
+ {
+ spin_lock(&priv->bss_loss_lock);
+ if (priv->bss_loss_status > XRADIO_BSS_LOSS_NONE) {
+ spin_unlock(&priv->bss_loss_lock);
+ break;
+ }
+ priv->bss_loss_status = XRADIO_BSS_LOSS_CHECKING;
+ spin_unlock(&priv->bss_loss_lock);
+ sta_printk(XRADIO_DBG_WARN, "[CQM] BSS lost, Beacon miss=%d, event=%x.\n",
+ (event->evt.eventData>>8)&0xff, event->evt.eventData&0xff);
+
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ if (!down_trylock(&hw_priv->scan.lock)) {
+ up(&hw_priv->scan.lock);
+ priv->delayed_link_loss = 0;
+ queue_delayed_work(hw_priv->workqueue,
+ &priv->bss_loss_work, HZ/10); //100ms
+ } else {
+ /* Scan is in progress. Delay reporting. */
+ /* Scan complete will trigger bss_loss_work */
+ priv->delayed_link_loss = 1;
+ /* Also we're starting watchdog. */
+ queue_delayed_work(hw_priv->workqueue,
+ &priv->bss_loss_work, 10 * HZ);
+ }
+ break;
+ }
+ case WSM_EVENT_BSS_REGAINED:
+ {
+ sta_printk(XRADIO_DBG_WARN, "[CQM] BSS regained.\n");
+ priv->delayed_link_loss = 0;
+ spin_lock(&priv->bss_loss_lock);
+ priv->bss_loss_status = XRADIO_BSS_LOSS_NONE;
+ spin_unlock(&priv->bss_loss_lock);
+ cancel_delayed_work_sync(&priv->bss_loss_work);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ break;
+ }
+ case WSM_EVENT_RADAR_DETECTED:
+ //STUB();
+ break;
+ case WSM_EVENT_RCPI_RSSI:
+ {
+ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1
+ * RSSI = RCPI / 2 - 110 */
+ int rcpiRssi = (int)(event->evt.eventData & 0xFF);
+ int cqm_evt;
+ if (priv->cqm_use_rssi)
+ rcpiRssi = (s8)rcpiRssi;
+ else
+ rcpiRssi = rcpiRssi / 2 - 110;
+
+ cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ?
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
+ sta_printk(XRADIO_DBG_NIY, "[CQM] RSSI event: %d", rcpiRssi);
+ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt,
+ GFP_KERNEL);
+ break;
+ }
+ case WSM_EVENT_BT_INACTIVE:
+ //STUB();
+ break;
+ case WSM_EVENT_BT_ACTIVE:
+ //STUB();
+ break;
+ case WSM_EVENT_INACTIVITY:
+ {
+ int link_id = ffs((u32)(event->evt.eventData)) - 1;
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *deauth;
+ struct xradio_link_entry *entry = NULL;
+
+ sta_printk(XRADIO_DBG_WARN, "Inactivity Event Recieved for "
+ "link_id %d\n", link_id);
+ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
+ if (!skb)
+ break;
+ skb_reserve(skb, 64);
+ xrwl_unmap_link(priv, link_id);
+ deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
+ SYS_WARN(!deauth);
+ entry = &priv->link_id_db[link_id - 1];
+ deauth->duration = 0;
+ memcpy(deauth->da, priv->vif->addr, ETH_ALEN);
+ memcpy(deauth->sa, entry->mac/*priv->link_id_db[i].mac*/, ETH_ALEN);
+ memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN);
+ deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
+ IEEE80211_STYPE_DEAUTH |
+ IEEE80211_FCTL_TODS);
+ deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
+ deauth->seq_ctrl = 0;
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ sta_printk(XRADIO_DBG_WARN, " Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da);
+ queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
+ break;
+ }
+ case WSM_EVENT_PS_MODE_ERROR:
+ {
+ if (!priv->uapsd_info.uapsdFlags &&
+ (priv->user_pm_mode != WSM_PSM_PS))
+ {
+ struct wsm_set_pm pm = priv->powersave_mode;
+ int ret = 0;
+
+ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
+ ret = xradio_set_pm (priv, &priv->powersave_mode);
+ if(ret)
+ priv->powersave_mode = pm;
+ }
+ break;
+ }
+ }
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ __xradio_free_event_queue(&list);
+}
+
+void xradio_bss_loss_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, bss_loss_work.work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ int timeout; /* in beacons */
+
+
+ timeout = priv->cqm_link_loss_count - priv->cqm_beacon_loss_count;
+ /* Skip the confimration procedure in P2P case */
+ if (priv->vif->p2p)
+ goto report;
+
+ spin_lock(&priv->bss_loss_lock);
+ if (priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING) {
+ //do loss report next time.
+ priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMED;
+ spin_unlock(&priv->bss_loss_lock);
+ //wait for more 1s to loss confirm.
+ queue_delayed_work(hw_priv->workqueue, &priv->bss_loss_work, 1 * HZ);
+ return;
+ } else if (priv->bss_loss_status == XRADIO_BSS_LOSS_NONE) {
+ spin_unlock(&priv->bss_loss_lock);
+ //link is alive.
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ return;
+ } else if (priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING) {
+ /* it mean no confirming packets, just report loss. */
+ }
+ spin_unlock(&priv->bss_loss_lock);
+
+report:
+ if (priv->cqm_beacon_loss_count) {
+ sta_printk(XRADIO_DBG_WARN, "[CQM] Beacon loss.\n");
+ if (timeout <= 0)
+ timeout = 0;
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL);
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+ } else {
+ timeout = 0;
+ }
+
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ queue_delayed_work(hw_priv->workqueue, &priv->connection_loss_work,
+ timeout * HZ / 10);
+
+ spin_lock(&priv->bss_loss_lock);
+ priv->bss_loss_status = XRADIO_BSS_LOSS_NONE;
+ spin_unlock(&priv->bss_loss_lock);
+}
+
+void xradio_connection_loss_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, connection_loss_work.work);
+ sta_printk(XRADIO_DBG_ERROR, "[CQM] if%d Reporting connection loss.\n",
+ priv->if_id);
+ ieee80211_connection_loss(priv->vif);
+}
+
+void xradio_tx_failure_work(struct work_struct *work)
+{
+ //struct xradio_vif *priv =
+ // container_of(work, struct xradio_vif, tx_failure_work);
+ sta_printk(XRADIO_DBG_WARN, "[CQM] Reporting TX failure.\n");
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ //ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL);
+#else /* CONFIG_XRADIO_USE_EXTENSIONS */
+ //(void)priv;
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+}
+
+#ifdef CONFIG_XRADIO_TESTMODE
+/**
+ * xradio_device_power_calc- Device power calculation
+ * from values fetch from SDD File.
+ *
+ * @priv: the private structure
+ * @Max_output_power: Power fetch from SDD
+ * @fe_cor: front-end loss correction
+ * @band: Either 2GHz or 5GHz
+ *
+ */
+void xradio_device_power_calc(struct xradio_common *hw_priv,
+ s16 max_output_power, s16 fe_cor, u32 band)
+{
+ s16 power_calc;
+
+ power_calc = max_output_power - fe_cor;
+ if ((power_calc % 16) != 0)
+ power_calc += 16;
+
+ hw_priv->txPowerRange[band].max_power_level = power_calc/16;
+ /*
+ * 12dBm is control range supported by firmware.
+ * This means absolute min power is
+ * max_power_level - 12.
+ */
+ hw_priv->txPowerRange[band].min_power_level =
+ hw_priv->txPowerRange[band].max_power_level - 12;
+ hw_priv->txPowerRange[band].stepping = 1;
+
+}
+#endif
+/* ******************************************************************** */
+#ifdef CONFIG_XRADIO_TESTMODE
+#define SDD_MAX_OUTPUT_POWER_2G4_ELT_ID 0xE3
+#define SDD_MAX_OUTPUT_POWER_5G_ELT_ID 0xE4
+#define SDD_FE_COR_2G4_ELT_ID 0x30
+#define SDD_FE_COR_5G_ELT_ID 0x31
+#define MIN(x, y, z) (x < y ? (x < z ? x : z) : (y < z ? y : z))
+static int xradio_test_pwrlevel(struct xradio_common *hw_priv)
+{
+ int ret = -1;
+ int parsedLength = 0;
+ struct xradio_sdd *pElement = (struct xradio_sdd *)hw_priv->sdd->data;
+
+
+
+ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length);
+ pElement = FIND_NEXT_ELT(pElement);
+
+ while (parsedLength <= hw_priv->sdd->size) {
+ switch (pElement->id) {
+ case SDD_MAX_OUTPUT_POWER_2G4_ELT_ID:
+ max_output_power_2G = *((s16 *)pElement->data);
+ break;
+ case SDD_FE_COR_2G4_ELT_ID:
+ fe_cor_2G = *((s16 *)pElement->data);
+ break;
+ case SDD_MAX_OUTPUT_POWER_5G_ELT_ID:
+ max_output_power_5G = *((s16 *)(pElement->data + 4));
+ break;
+ case SDD_FE_COR_5G_ELT_ID:
+ fe_cor_5G = MIN(*((s16 *)pElement->data),
+ *((s16 *)(pElement->data + 2)),
+ *((s16 *)(pElement->data + 4)));
+ fe_cor_5G = MIN(fe_cor_5G,
+ *((s16 *)(pElement->data + 6)),
+ *((s16 *)(pElement->data + 8)));
+ break;
+ default:
+ break;
+ }
+ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length);
+ pElement = FIND_NEXT_ELT(pElement);
+ }
+
+ /* Max/Min Power Calculation for 2.4G */
+ xradio_device_power_calc(hw_priv, max_output_power_2G, fe_cor_2G, NL80211_BAND_2GHZ);
+ /* Max/Min Power Calculation for 5G */
+ xradio_device_power_calc(hw_priv, max_output_power_5G, fe_cor_5G, NL80211_BAND_5GHZ);
+ for (i = 0; i < 2; ++i) {
+ sta_printk(XRADIO_DBG_MSG, "Power Values Read from SDD %s:"
+ "min_power_level[%d]: %d max_power_level[%d]:"
+ "%d stepping[%d]: %d\n", __func__, i,
+ hw_priv->txPowerRange[i].min_power_level, i,
+ hw_priv->txPowerRange[i].max_power_level, i,
+ hw_priv->txPowerRange[i].stepping);
+ }
+ return 0;
+}
+#endif
+
+/* Internal API */
+int xradio_setup_mac(struct xradio_common *hw_priv)
+{
+ int ret = 0, if_id;
+
+
+ if (hw_priv->sdd) {
+ struct wsm_configuration cfg = {
+ .dot11StationId = &hw_priv->mac_addr[0],
+ .dpdData = hw_priv->sdd->data,
+ .dpdData_size = hw_priv->sdd->size,
+ };
+ for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv);
+ if_id++) {
+ /* Set low-power mode. */
+ ret |= SYS_WARN(wsm_configuration(hw_priv, &cfg,
+ if_id));
+ }
+#ifdef CONFIG_XRADIO_TESTMODE
+ /* Parse SDD file for power level test */
+ xradio_test_pwrlevel(hw_priv);
+#endif
+ /* wsm_configuration only once, so release it */
+ release_firmware(hw_priv->sdd);
+ hw_priv->sdd = NULL;
+ }
+
+ /* BUG:TX output power is not set untill config_xradio is called.
+ * This would lead to 0 power set in fw and would effect scan & p2p-find
+ * Setting to default value here from sdd which would be overwritten when
+ * we make connection to AP.This value is used only during scan & p2p-ops
+ * untill AP connection is made */
+ /*BUG:TX output power: Hardcoding to 20dbm if CCX is not enabled*/
+ /*TODO: This might change*/
+ if (!hw_priv->output_power)
+ hw_priv->output_power=20;
+ sta_printk(XRADIO_DBG_MSG, "%s output power %d\n",__func__,hw_priv->output_power);
+
+ return ret;
+}
+
+void xradio_pending_offchanneltx_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, pending_offchanneltx_work.work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ mutex_lock(&hw_priv->conf_mutex);
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_WARN, "OFFCHAN PEND IN\n");
+#endif
+ xradio_disable_listening(priv);
+ hw_priv->roc_if_id = -1;
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_WARN, "OFFCHAN PEND OUT\n");
+#endif
+ up(&hw_priv->scan.lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+}
+
+void xradio_offchannel_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, offchannel_work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id);
+ struct xradio_queue *queue = &hw_priv->tx_queue[queueId];
+
+
+ SYS_BUG(queueId >= 4);
+ SYS_BUG(!hw_priv->channel);
+
+ if (unlikely(down_trylock(&hw_priv->scan.lock))) {
+ int ret;
+ sta_printk(XRADIO_DBG_ERROR, "xradio_offchannel_work***** drop frame\n");
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_remove(hw_priv, queue,
+ hw_priv->pending_frame_id);
+#else
+ ret = xradio_queue_remove(queue, hw_priv->pending_frame_id);
+#endif
+ if (ret)
+ sta_printk(XRADIO_DBG_ERROR, "xradio_offchannel_work: "
+ "queue_remove failed %d\n", ret);
+ wsm_unlock_tx(hw_priv);
+ //workaround by yangfh
+ LOG_FILE(1, "xradio_offchannel_work error\n");
+ up(&hw_priv->scan.lock);
+ ieee80211_connection_loss(priv->vif);
+ sta_printk(XRADIO_DBG_ERROR,"lock %d\n", hw_priv->scan.lock.count);
+
+ return;
+ }
+ mutex_lock(&hw_priv->conf_mutex);
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_WARN, "OFFCHAN WORK IN %d\n", priv->if_id);
+#endif
+ hw_priv->roc_if_id = priv->if_id;
+ if (likely(!priv->join_status)) {
+ wsm_vif_flush_tx(priv);
+ xradio_enable_listening(priv, hw_priv->channel);
+ /* xradio_update_filtering(priv); */
+ }
+ if (unlikely(!priv->join_status))
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_remove(hw_priv, queue,
+ hw_priv->pending_frame_id);
+#else
+ xradio_queue_remove(queue, hw_priv->pending_frame_id);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ else
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_requeue(hw_priv, queue,
+ hw_priv->pending_frame_id, false);
+#else
+ xradio_queue_requeue(queue, hw_priv->pending_frame_id, false);
+#endif
+
+ queue_delayed_work(hw_priv->workqueue,
+ &priv->pending_offchanneltx_work, 204 * HZ/1000);
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_WARN, "OFFCHAN WORK OUT %d\n", priv->if_id);
+#endif
+ mutex_unlock(&hw_priv->conf_mutex);
+ wsm_unlock_tx(hw_priv);
+}
+
+void xradio_join_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, join_work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id);
+ struct xradio_queue *queue = &hw_priv->tx_queue[queueId];
+ const struct xradio_txpriv *txpriv = NULL;
+ struct sk_buff *skb = NULL;
+ const struct wsm_tx *wsm;
+ const struct ieee80211_hdr *frame;
+ const u8 *bssid;
+ struct cfg80211_bss *bss;
+ const u8 *ssidie;
+ const u8 *dtimie;
+ const struct ieee80211_tim_ie *tim = NULL;
+ struct wsm_protected_mgmt_policy mgmt_policy;
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+ //struct wsm_reset reset = {
+ // .reset_statistics = true,
+ //};
+
+
+
+ SYS_BUG(queueId >= 4);
+ if (xradio_queue_get_skb(queue, hw_priv->pending_frame_id,
+ &skb, &txpriv)) {
+ wsm_unlock_tx(hw_priv);
+ return;
+ }
+ wsm = (struct wsm_tx *)&skb->data[0];
+ frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset];
+ bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */
+
+ SYS_BUG(!wsm);
+ SYS_BUG(!hw_priv->channel);
+
+ if (unlikely(priv->join_status)) {
+ sta_printk(XRADIO_DBG_WARN, "%s, pre join_status=%d.\n",
+ __func__, priv->join_status);
+ wsm_lock_tx(hw_priv);
+ xradio_unjoin_work(&priv->unjoin_work);
+ }
+
+ cancel_delayed_work_sync(&priv->join_timeout);
+
+ bss = cfg80211_get_bss(hw_priv->hw->wiphy, hw_priv->channel,
+ bssid, NULL, 0, 0, 0);
+ if (!bss) {
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_remove(hw_priv, queue, hw_priv->pending_frame_id);
+#else
+ xradio_queue_remove(queue, hw_priv->pending_frame_id);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ wsm_unlock_tx(hw_priv);
+ return;
+ }
+ ssidie = cfg80211_find_ie(WLAN_EID_SSID,
+ bss->ies->data,
+ bss->ies->len);
+ dtimie = cfg80211_find_ie(WLAN_EID_TIM,
+ bss->ies->data,
+ bss->ies->len);
+ if (dtimie)
+ tim = (struct ieee80211_tim_ie *)&dtimie[2];
+
+ mutex_lock(&hw_priv->conf_mutex);
+ {
+ struct wsm_join join = {
+ .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ?
+ WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
+ /* default changed to LONG, by HuangLu, fix 2/5.5/11m tx fail*/
+ .preambleType = WSM_JOIN_PREAMBLE_LONG,
+ .probeForJoin = 1,
+ /* dtimPeriod will be updated after association */
+ .dtimPeriod = 1,
+ .beaconInterval = bss->beacon_interval,
+ };
+
+ if (priv->if_id)
+ join.flags |= WSM_FLAG_MAC_INSTANCE_1;
+ else
+ join.flags &= ~WSM_FLAG_MAC_INSTANCE_1;
+
+ /* BT Coex related changes */
+ if (hw_priv->is_BT_Present) {
+ if (((hw_priv->conf_listen_interval * 100) %
+ bss->beacon_interval) == 0)
+ priv->listen_interval =
+ ((hw_priv->conf_listen_interval * 100) /
+ bss->beacon_interval);
+ else
+ priv->listen_interval =
+ ((hw_priv->conf_listen_interval * 100) /
+ bss->beacon_interval + 1);
+ }
+
+ if (tim && tim->dtim_period > 1) {
+ join.dtimPeriod = tim->dtim_period;
+ priv->join_dtim_period = tim->dtim_period;
+ }
+ priv->beacon_int = bss->beacon_interval;
+ sta_printk(XRADIO_DBG_NIY, "Join DTIM: %d, interval: %d\n",
+ join.dtimPeriod, priv->beacon_int);
+
+ hw_priv->is_go_thru_go_neg = false;
+ join.channelNumber = hw_priv->channel->hw_value;
+
+ /* basicRateSet will be updated after association.
+ Currently these values are hardcoded */
+ if (hw_priv->channel->band == NL80211_BAND_5GHZ) {
+ join.band = WSM_PHY_BAND_5G;
+ join.basicRateSet = 64; /*6 mbps*/
+ }else{
+ join.band = WSM_PHY_BAND_2_4G;
+ join.basicRateSet = 7; /*1, 2, 5.5 mbps*/
+ }
+ memcpy(&join.bssid[0], bssid, sizeof(join.bssid));
+ memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid));
+
+ if (ssidie) {
+ join.ssidLength = ssidie[1];
+ if (SYS_WARN(join.ssidLength > sizeof(join.ssid)))
+ join.ssidLength = sizeof(join.ssid);
+ memcpy(&join.ssid[0], &ssidie[2], join.ssidLength);
+ if(strstr(&join.ssid[0],"5.1.4"))
+ msleep(200);
+#ifdef ROAM_OFFLOAD
+ if((priv->vif->type == NL80211_IFTYPE_STATION)) {
+ priv->ssid_length = join.ssidLength;
+ memcpy(priv->ssid, &join.ssid[0], priv->ssid_length);
+ }
+#endif /*ROAM_OFFLOAD*/
+ }
+
+ if (priv->vif->p2p) {
+ join.flags |= WSM_JOIN_FLAGS_P2P_GO;
+#ifdef P2P_MULTIVIF
+ join.flags |= (1 << 6);
+#endif
+ join.basicRateSet =
+ xradio_rate_mask_to_wsm(hw_priv, 0xFF0);
+ }
+
+ wsm_flush_tx(hw_priv);
+
+ /* Queue unjoin if not associated in 3 sec. */
+ queue_delayed_work(hw_priv->workqueue,
+ &priv->join_timeout, 3 * HZ);
+ /*Stay Awake for Join Timeout*/
+ xradio_pm_stay_awake(&hw_priv->pm_state, 3 * HZ);
+
+ xradio_disable_listening(priv);
+
+ //SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id));
+ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ 0, hw_priv->ba_tid_mask, priv->if_id));
+ spin_lock_bh(&hw_priv->ba_lock);
+ hw_priv->ba_ena = false;
+ hw_priv->ba_cnt = 0;
+ hw_priv->ba_acc = 0;
+ hw_priv->ba_hist = 0;
+ hw_priv->ba_cnt_rx = 0;
+ hw_priv->ba_acc_rx = 0;
+ spin_unlock_bh(&hw_priv->ba_lock);
+
+ mgmt_policy.protectedMgmtEnable = 0;
+ mgmt_policy.unprotectedMgmtFramesAllowed = 1;
+ mgmt_policy.encryptionForAuthFrame = 1;
+ wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, priv->if_id);
+
+ if (wsm_join(hw_priv, &join, priv->if_id)) {
+ memset(&priv->join_bssid[0],
+ 0, sizeof(priv->join_bssid));
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_remove(hw_priv, queue,
+ hw_priv->pending_frame_id);
+#else
+ xradio_queue_remove(queue, hw_priv->pending_frame_id);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ cancel_delayed_work_sync(&priv->join_timeout);
+ } else {
+ /* Upload keys */
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_requeue(hw_priv, queue,
+ hw_priv->pending_frame_id, true);
+#else
+ xradio_queue_requeue(queue, hw_priv->pending_frame_id,
+ true);
+#endif
+ priv->join_status = XRADIO_JOIN_STATUS_STA;
+
+ /* Due to beacon filtering it is possible that the
+ * AP's beacon is not known for the mac80211 stack.
+ * Disable filtering temporary to make sure the stack
+ * receives at least one */
+ priv->disable_beacon_filter = true;
+
+ }
+ xradio_update_filtering(priv);
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ cfg80211_put_bss(hw_priv->hw->wiphy,bss);
+ wsm_unlock_tx(hw_priv);
+}
+
+void xradio_join_timeout(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, join_timeout.work);
+ sta_printk(XRADIO_DBG_WARN, "[WSM] Issue unjoin command (TMO).\n");
+ wsm_lock_tx(priv->hw_priv);
+ xradio_unjoin_work(&priv->unjoin_work);
+}
+
+void xradio_unjoin_work(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, unjoin_work);
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+ bool is_htcapie = false;
+ int i;
+ struct xradio_vif *tmp_priv;
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+
+
+ //add by yangfh.
+ hw_priv->connet_time[priv->if_id] = 0;
+#ifdef AP_HT_COMPAT_FIX
+ priv->ht_compat_det &= ~1;
+ priv->ht_compat_cnt = 0;
+#endif
+
+ del_timer_sync(&hw_priv->ba_timer);
+ mutex_lock(&hw_priv->conf_mutex);
+ if (unlikely(atomic_read(&hw_priv->scan.in_progress))) {
+ if (atomic_xchg(&priv->delayed_unjoin, 1)) {
+ sta_printk(XRADIO_DBG_NIY,
+ "%s: Delayed unjoin "
+ "is already scheduled.\n",
+ __func__);
+ wsm_unlock_tx(hw_priv);
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ return;
+ }
+
+ if (priv->join_status &&
+ priv->join_status > XRADIO_JOIN_STATUS_STA) {
+ sta_printk(XRADIO_DBG_ERROR,
+ "%s: Unexpected: join status: %d\n",
+ __func__, priv->join_status);
+ SYS_BUG(1);
+ }
+ if (priv->join_status) {
+ cancel_work_sync(&priv->update_filtering_work);
+ cancel_work_sync(&priv->set_beacon_wakeup_period_work);
+ memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid));
+ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE;
+
+ /* Unjoin is a reset. */
+ wsm_flush_tx(hw_priv);
+ SYS_WARN(wsm_keep_alive_period(hw_priv, 0, priv->if_id));
+ SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id));
+ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
+ SYS_WARN(wsm_set_output_power(hw_priv,
+ hw_priv->output_power * 10, priv->if_id));
+ priv->join_dtim_period = 0;
+ priv->cipherType = 0;
+ SYS_WARN(xradio_setup_mac_pvif(priv));
+ xradio_free_event_queue(hw_priv);
+ cancel_work_sync(&hw_priv->event_handler);
+ cancel_delayed_work_sync(&priv->connection_loss_work);
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ 0, hw_priv->ba_tid_mask, priv->if_id));
+ priv->disable_beacon_filter = false;
+ xradio_update_filtering(priv);
+ priv->setbssparams_done = false;
+ memset(&priv->association_mode, 0,
+ sizeof(priv->association_mode));
+ memset(&priv->bss_params, 0, sizeof(priv->bss_params));
+ memset(&priv->firmware_ps_mode, 0,
+ sizeof(priv->firmware_ps_mode));
+ priv->htcap = false;
+ xradio_for_each_vif(hw_priv, tmp_priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv)
+#else
+ if (!tmp_priv)
+#endif
+ continue;
+ if ((tmp_priv->join_status == XRADIO_JOIN_STATUS_STA) && tmp_priv->htcap)
+ is_htcapie = true;
+ }
+
+ if (is_htcapie) {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE;
+ sta_printk(XRADIO_DBG_NIY, "UNJOIN HTCAP 11N %d\n",hw_priv->vif0_throttle);
+ } else {
+ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE;
+ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE;
+ sta_printk(XRADIO_DBG_NIY, "UNJOIN 11BG %d\n",hw_priv->vif0_throttle);
+ }
+ sta_printk(XRADIO_DBG_NIY, "Unjoin.\n");
+ }
+ mutex_unlock(&hw_priv->conf_mutex);
+ wsm_unlock_tx(hw_priv);
+}
+
+int xradio_enable_listening(struct xradio_vif *priv,
+ struct ieee80211_channel *chan)
+{
+ /* TODO:COMBO: Channel is common to HW currently in mac80211.
+ Change the code below once channel is made per VIF */
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct wsm_start start = {
+#ifdef P2P_MULTIVIF
+ .mode = WSM_START_MODE_P2P_DEV | (priv->if_id ? (1 << 4) : 0),
+#else
+ .mode = WSM_START_MODE_P2P_DEV | (priv->if_id << 4),
+#endif
+ .band = (chan->band == NL80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+ .channelNumber = chan->hw_value,
+ .beaconInterval = 100,
+ .DTIMPeriod = 1,
+ .probeDelay = 0,
+ .basicRateSet = 0x0F,
+ };
+
+
+ if(priv->if_id != 2) {
+ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR);
+ return 0;
+ }
+ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR)
+ return 0;
+ if (priv->join_status == XRADIO_JOIN_STATUS_PASSIVE)
+ priv->join_status = XRADIO_JOIN_STATUS_MONITOR;
+
+ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR);
+
+ return wsm_start(hw_priv, &start, XRWL_GENERIC_IF_ID);
+}
+
+int xradio_disable_listening(struct xradio_vif *priv)
+{
+ int ret;
+ struct wsm_reset reset = {
+ .reset_statistics = true,
+ };
+
+
+ if(priv->if_id != 2) {
+ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR);
+ return 0;
+ }
+ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE;
+
+ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR);
+
+ if (priv->hw_priv->roc_if_id == -1)
+ return 0;
+
+ ret = wsm_reset(priv->hw_priv, &reset, XRWL_GENERIC_IF_ID);
+ return ret;
+}
+
+/* TODO:COMBO:UAPSD will be supported only on one interface */
+int xradio_set_uapsd_param(struct xradio_vif *priv,
+ const struct wsm_edca_params *arg)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ int ret;
+ u16 uapsdFlags = 0;
+
+
+ /* Here's the mapping AC [queue, bit]
+ VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/
+
+ if (arg->params[0].uapsdEnable)
+ uapsdFlags |= 1 << 3;
+
+ if (arg->params[1].uapsdEnable)
+ uapsdFlags |= 1 << 2;
+
+ if (arg->params[2].uapsdEnable)
+ uapsdFlags |= 1 << 1;
+
+ if (arg->params[3].uapsdEnable)
+ uapsdFlags |= 1;
+
+ /* Currently pseudo U-APSD operation is not supported, so setting
+ * MinAutoTriggerInterval, MaxAutoTriggerInterval and
+ * AutoTriggerStep to 0 */
+
+ priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags);
+ priv->uapsd_info.minAutoTriggerInterval = 0;
+ priv->uapsd_info.maxAutoTriggerInterval = 0;
+ priv->uapsd_info.autoTriggerStep = 0;
+
+ ret = wsm_set_uapsd_info(hw_priv, &priv->uapsd_info,
+ priv->if_id);
+ return ret;
+}
+
+void xradio_ba_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, ba_work);
+ u8 tx_ba_tid_mask;
+
+
+ /* TODO:COMBO: reenable this part of code */
+/* if (priv->join_status != XRADIO_JOIN_STATUS_STA)
+ return;
+ if (!priv->setbssparams_done)
+ return;*/
+
+ sta_printk(XRADIO_DBG_WARN, "BA work****\n");
+ spin_lock_bh(&hw_priv->ba_lock);
+// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0;
+ tx_ba_tid_mask = hw_priv->ba_tid_mask;
+ spin_unlock_bh(&hw_priv->ba_lock);
+
+ wsm_lock_tx(hw_priv);
+
+ SYS_WARN(wsm_set_block_ack_policy(hw_priv,
+ tx_ba_tid_mask, hw_priv->ba_tid_mask, -1)); /*TODO:COMBO*/
+
+ wsm_unlock_tx(hw_priv);
+}
+
+void xradio_ba_timer(unsigned long arg)
+{
+ bool ba_ena;
+ struct xradio_common *hw_priv = (struct xradio_common *)arg;
+
+
+ spin_lock_bh(&hw_priv->ba_lock);
+ xradio_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
+ hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx);
+
+ if (atomic_read(&hw_priv->scan.in_progress)) {
+ hw_priv->ba_cnt = 0;
+ hw_priv->ba_acc = 0;
+ hw_priv->ba_cnt_rx = 0;
+ hw_priv->ba_acc_rx = 0;
+ goto skip_statistic_update;
+ }
+
+ if (hw_priv->ba_cnt >= XRADIO_BLOCK_ACK_CNT &&
+ (hw_priv->ba_acc / hw_priv->ba_cnt >= XRADIO_BLOCK_ACK_THLD ||
+ (hw_priv->ba_cnt_rx >= XRADIO_BLOCK_ACK_CNT &&
+ hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >=
+ XRADIO_BLOCK_ACK_THLD)))
+ ba_ena = true;
+ else
+ ba_ena = false;
+
+ hw_priv->ba_cnt = 0;
+ hw_priv->ba_acc = 0;
+ hw_priv->ba_cnt_rx = 0;
+ hw_priv->ba_acc_rx = 0;
+
+ if (ba_ena != hw_priv->ba_ena) {
+ if (ba_ena || ++hw_priv->ba_hist >= XRADIO_BLOCK_ACK_HIST) {
+ hw_priv->ba_ena = ba_ena;
+ hw_priv->ba_hist = 0;
+#if 0
+ sta_printk(XRADIO_DBG_NIY, "%s block ACK:\n",
+ ba_ena ? "enable" : "disable");
+ queue_work(hw_priv->workqueue, &hw_priv->ba_work);
+#endif
+ }
+ } else if (hw_priv->ba_hist)
+ --hw_priv->ba_hist;
+
+skip_statistic_update:
+ spin_unlock_bh(&hw_priv->ba_lock);
+}
+
+int xradio_vif_setup(struct xradio_vif *priv)
+{
+ struct xradio_common *hw_priv = priv->hw_priv;
+ int ret = 0;
+
+
+ //reset channel change flag, yangfh 2015-5-15 17:12:14
+ hw_priv->channel_changed = 0;
+ /* Setup per vif workitems and locks */
+ spin_lock_init(&priv->vif_lock);
+ INIT_WORK(&priv->join_work, xradio_join_work);
+ INIT_DELAYED_WORK(&priv->join_timeout, xradio_join_timeout);
+ INIT_WORK(&priv->unjoin_work, xradio_unjoin_work);
+ INIT_WORK(&priv->wep_key_work, xradio_wep_key_work);
+ INIT_WORK(&priv->offchannel_work, xradio_offchannel_work);
+ INIT_DELAYED_WORK(&priv->bss_loss_work, xradio_bss_loss_work);
+ INIT_DELAYED_WORK(&priv->connection_loss_work, xradio_connection_loss_work);
+ priv->bss_loss_status = XRADIO_BSS_LOSS_NONE;
+ spin_lock_init(&priv->bss_loss_lock);
+ INIT_WORK(&priv->tx_failure_work, xradio_tx_failure_work);
+ spin_lock_init(&priv->ps_state_lock);
+ INIT_DELAYED_WORK(&priv->set_cts_work, xradio_set_cts_work);
+ INIT_WORK(&priv->set_tim_work, xradio_set_tim_work);
+ INIT_WORK(&priv->multicast_start_work, xradio_multicast_start_work);
+ INIT_WORK(&priv->multicast_stop_work, xradio_multicast_stop_work);
+ INIT_WORK(&priv->link_id_work, xradio_link_id_work);
+ INIT_DELAYED_WORK(&priv->link_id_gc_work, xradio_link_id_gc_work);
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ INIT_WORK(&priv->linkid_reset_work, xradio_link_id_reset);
+#endif
+ INIT_WORK(&priv->update_filtering_work, xradio_update_filtering_work);
+ INIT_DELAYED_WORK(&priv->pending_offchanneltx_work,
+ xradio_pending_offchanneltx_work);
+ INIT_WORK(&priv->set_beacon_wakeup_period_work,
+ xradio_set_beacon_wakeup_period_work);
+#ifdef AP_HT_CAP_UPDATE
+ INIT_WORK(&priv->ht_oper_update_work, xradio_ht_oper_update_work);
+#endif
+ init_timer(&priv->mcast_timeout);
+ priv->mcast_timeout.data = (unsigned long)priv;
+ priv->mcast_timeout.function = xradio_mcast_timeout;
+ priv->setbssparams_done = false;
+ priv->power_set_true = 0;
+ priv->user_power_set_true = 0;
+ priv->user_pm_mode = 0;
+ SYS_WARN(xradio_debug_init_priv(hw_priv, priv));
+
+ /* Initialising the broadcast filter */
+ memset(priv->broadcast_filter.MacAddr, 0xFF, ETH_ALEN);
+ priv->broadcast_filter.nummacaddr = 1;
+ priv->broadcast_filter.address_mode = 1;
+ priv->broadcast_filter.filter_mode = 1;
+ priv->htcap = false;
+#ifdef AP_HT_COMPAT_FIX
+ priv->ht_compat_det = 0;
+ priv->ht_compat_cnt = 0;
+#endif
+
+ sta_printk(XRADIO_DBG_ALWY, "!!!%s: id=%d, type=%d, p2p=%d\n",
+ __func__, priv->if_id, priv->vif->type, priv->vif->p2p);
+
+ atomic_set(&priv->enabled, 1);
+
+#ifdef P2P_MULTIVIF
+ if (priv->if_id < 2) {
+#endif
+ /* default EDCA */
+ WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007,
+ 47, 0xc8, false);
+ WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f,
+ 94, 0xc8, false);
+
+// if(priv->vif->p2p == true) {
+ WSM_EDCA_SET(&priv->edca, 2, 0x0002, 0x0003, 0x0007,
+ 0, 0xc8, false);
+ sta_printk(XRADIO_DBG_MSG, "EDCA params Best effort for sta/p2p is " \
+ "aifs=%u, cw_min=%u, cw_max=%u \n",
+ priv->edca.params[2].aifns, priv->edca.params[2].cwMin,
+ priv->edca.params[2].cwMax);
+#if 0
+ }else {
+ WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff,
+ 0, 0xc8, false);
+ sta_printk(XRADIO_DBG_MSG, "EDCA params Best effort for sta is " \
+ "aifs=%u, cw_min=%u, cw_max=%u \n",
+ priv->edca.params[2].aifns, priv->edca.params[2].cwMin,
+ priv->edca.params[2].cwMax);
+ }
+#endif
+ WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff,
+ 0, 0xc8, false);
+
+ ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
+ if (SYS_WARN(ret))
+ goto out;
+
+ ret = xradio_set_uapsd_param(priv, &priv->edca);
+ if (SYS_WARN(ret))
+ goto out;
+
+ memset(priv->bssid, ~0, ETH_ALEN);
+ priv->wep_default_key_id = -1;
+ priv->cipherType = 0;
+ priv->cqm_link_loss_count = XRADIO_LINK_LOSS_THOLD_DEF;
+ priv->cqm_beacon_loss_count = XRADIO_BSS_LOSS_THOLD_DEF;
+
+ /* Temporary configuration - beacon filter table */
+ __xradio_bf_configure(priv);
+#ifdef P2P_MULTIVIF
+ }
+#endif
+out:
+ return ret;
+}
+
+int xradio_setup_mac_pvif(struct xradio_vif *priv)
+{
+ int ret = 0;
+ /* NOTE: There is a bug in FW: it reports signal
+ * as RSSI if RSSI subscription is enabled.
+ * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */
+ /* NOTE2: RSSI based reports have been switched to RCPI, since
+ * FW has a bug and RSSI reported values are not stable,
+ * what can leads to signal level oscilations in user-end applications */
+ struct wsm_rcpi_rssi_threshold threshold = {
+ .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
+ WSM_RCPI_RSSI_DONT_USE_UPPER |
+ WSM_RCPI_RSSI_DONT_USE_LOWER,
+ .rollingAverageCount = 16,
+ };
+
+
+ /* Remember the decission here to make sure, we will handle
+ * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */
+ if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
+ priv->cqm_use_rssi = true;
+
+
+ /* Configure RSSI/SCPI reporting as RSSI. */
+#ifdef P2P_MULTIVIF
+ ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, priv->if_id ? 1 : 0);
+#else
+ ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, priv->if_id);
+#endif
+ return ret;
+}
+
+void xradio_rem_chan_timeout(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, rem_chan_timeout.work);
+ int ret, if_id;
+ struct xradio_vif *priv;
+
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ if(TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) {
+ sta_printk(XRADIO_DBG_WARN, "[Restart rem_chan_timeout:Timeout]\n");
+ return;
+ }
+#endif
+
+ if (atomic_read(&hw_priv->remain_on_channel) == 0) {
+ return;
+ }
+ ieee80211_remain_on_channel_expired(hw_priv->hw);
+
+ mutex_lock(&hw_priv->conf_mutex);
+ if_id = hw_priv->roc_if_id;
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_ERROR, "ROC TO IN %d\n", if_id);
+#endif
+ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, if_id);
+ ret = SYS_WARN(__xradio_flush(hw_priv, false, if_id));
+ if (!ret) {
+ xradio_disable_listening(priv);
+ }
+ atomic_set(&hw_priv->remain_on_channel, 0);
+ hw_priv->roc_if_id = -1;
+
+#ifdef ROC_DEBUG
+ sta_printk(XRADIO_DBG_ERROR, "ROC TO OUT %d\n", if_id);
+#endif
+
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+}
+const u8 *xradio_get_ie(u8 *start, size_t len, u8 ie)
+{
+ u8 *end, *pos;
+
+
+ pos = start;
+ if (pos == NULL)
+ return NULL;
+ end = pos + len;
+
+ while (pos + 1 < end) {
+ if (pos + 2 + pos[1] > end)
+ break;
+ if (pos[0] == ie)
+ return pos;
+ pos += 2 + pos[1];
+ }
+
+ return NULL;
+}
+
+/**
+ * xradio_set_macaddrfilter -called when tesmode command
+ * is for setting mac address filter
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_set_macaddrfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data)
+{
+ struct wsm_mac_addr_filter *mac_addr_filter = NULL;
+ struct wsm_mac_addr_info *addr_info = NULL;
+ u8 action_mode = 0, no_of_mac_addr = 0, i = 0;
+ int ret = 0;
+ u16 macaddrfiltersize = 0;
+
+
+ /* Retrieving Action Mode */
+ action_mode = data[0];
+ /* Retrieving number of address entries */
+ no_of_mac_addr = data[1];
+
+ addr_info = (struct wsm_mac_addr_info *)&data[2];
+
+ /* Computing sizeof Mac addr filter */
+ macaddrfiltersize = sizeof(*mac_addr_filter) + \
+ (no_of_mac_addr * sizeof(struct wsm_mac_addr_info));
+
+ mac_addr_filter = kzalloc(macaddrfiltersize, GFP_KERNEL);
+ if (!mac_addr_filter) {
+ ret = -ENOMEM;
+ goto exit_p;
+ }
+ mac_addr_filter->action_mode = action_mode;
+ mac_addr_filter->numfilter = no_of_mac_addr;
+
+ for (i = 0; i < no_of_mac_addr; i++) {
+ mac_addr_filter->macaddrfilter[i].address_mode = \
+ addr_info[i].address_mode;
+ memcpy(mac_addr_filter->macaddrfilter[i].MacAddr, \
+ addr_info[i].MacAddr , ETH_ALEN);
+ mac_addr_filter->macaddrfilter[i].filter_mode = \
+ addr_info[i].filter_mode;
+ }
+ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_MAC_ADDR_FILTER, \
+ mac_addr_filter, macaddrfiltersize, priv->if_id));
+
+ kfree(mac_addr_filter);
+exit_p:
+ return ret;
+}
+
+/**
+ * xradio_set_arpreply -called for creating and
+ * configuring arp response template frame
+ *
+ * @hw: the hardware
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ struct xradio_common *hw_priv = (struct xradio_common *)hw->priv;
+ u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0;
+ bool encrypt = false;
+ int ret = 0;
+ u8 *template_frame = NULL;
+ struct ieee80211_hdr_3addr *dot11hdr = NULL;
+ struct ieee80211_snap_hdr *snaphdr = NULL;
+ struct arphdr *arp_hdr = NULL;
+
+
+ template_frame = kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, GFP_KERNEL);
+ if (!template_frame) {
+ sta_printk(XRADIO_DBG_ERROR, "Template frame memory failed\n");
+ ret = -ENOMEM;
+ goto exit_p;
+ }
+ dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4];
+
+ framehdrlen = sizeof(*dot11hdr);
+ if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
+ priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
+ switch (priv->cipherType) {
+
+ case WLAN_CIPHER_SUITE_WEP40:
+ case WLAN_CIPHER_SUITE_WEP104:
+ sta_printk(XRADIO_DBG_NIY, "WEP\n");
+ encrypthdr = WEP_ENCRYPT_HDR_SIZE;
+ encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
+ encrypt = 1;
+ break;
+
+
+ case WLAN_CIPHER_SUITE_TKIP:
+ sta_printk(XRADIO_DBG_NIY, "WPA\n");
+ encrypthdr = WPA_ENCRYPT_HDR_SIZE;
+ encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
+ encrypt = 1;
+ break;
+
+ case WLAN_CIPHER_SUITE_CCMP:
+ sta_printk(XRADIO_DBG_NIY, "WPA2\n");
+ encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
+ encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
+ encrypt = 1;
+ break;
+
+ case WLAN_CIPHER_SUITE_SMS4:
+ sta_printk(XRADIO_DBG_NIY, "WAPI\n");
+ encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
+ encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
+ encrypt = 1;
+ break;
+
+ default:
+ encrypthdr = 0;
+ encrypttailsize = 0;
+ encrypt = 0;
+ break;
+ }
+
+ framehdrlen += encrypthdr;
+ /* Filling the 802.11 Hdr */
+ dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
+ if (priv->vif->type == NL80211_IFTYPE_STATION)
+ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
+ else
+ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
+
+ if (encrypt)
+ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
+
+ if (priv->vif->bss_conf.qos) {
+ sta_printk(XRADIO_DBG_NIY, "QOS Enabled\n");
+ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
+ *(u16 *)(dot11hdr + 1) = 0x0;
+ framehdrlen += 2;
+ } else {
+ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
+ }
+
+ memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
+ memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
+ memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN);
+
+ /* Filling the LLC/SNAP Hdr */
+ snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
+ memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
+ sizeof(*snaphdr));
+ *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_ARP);
+ /* Updating the framebdylen with snaphdr and LLC hdr size */
+ framebdylen = sizeof(*snaphdr) + 2;
+
+ /* Filling the ARP Reply Payload */
+ arp_hdr = (struct arphdr *)((u8 *)dot11hdr + framehdrlen + framebdylen);
+ arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
+ arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
+ arp_hdr->ar_hln = ETH_ALEN;
+ arp_hdr->ar_pln = 4;
+ arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
+
+ /* Updating the frmbdylen with Arp Reply Hdr and Arp payload size(20) */
+ framebdylen += sizeof(*arp_hdr) + 20;
+
+ /* Updating the framebdylen with Encryption Tail Size */
+ framebdylen += encrypttailsize;
+
+ /* Filling the Template Frame Hdr */
+ template_frame[0] = WSM_FRAME_TYPE_ARP_REPLY; /* Template frame type */
+ template_frame[1] = 0xFF; /* Rate to be fixed */
+ ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen;
+
+ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \
+ template_frame, (framehdrlen+framebdylen+4),
+ priv->if_id));
+ kfree(template_frame);
+exit_p:
+ return ret;
+}
+
+#ifdef ROAM_OFFLOAD
+/**
+ * xradio_testmode_event -send asynchronous event
+ * to userspace
+ *
+ * @wiphy: the wiphy
+ * @msg_id: XR msg ID
+ * @data: data to be sent
+ * @len: data length
+ * @gfp: allocation flag
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_testmode_event(struct wiphy *wiphy, const u32 msg_id,
+ const void *data, int len, gfp_t gfp)
+{
+ struct sk_buff *skb = NULL;
+
+
+ skb = cfg80211_testmode_alloc_event_skb(wiphy,
+ nla_total_size(len+sizeof(msg_id)), gfp);
+
+ if (!skb)
+ return -ENOMEM;
+
+ cfg80211_testmode_event(skb, gfp);
+ return 0;
+}
+#endif /*ROAM_OFFLOAD*/
+
+
+#ifdef CONFIG_XRADIO_TESTMODE
+/**
+ * xradio_set_snap_frame -Set SNAP frame format
+ *
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_set_snap_frame(struct ieee80211_hw *hw,
+ u8 *data, int len)
+{
+ struct xr_msg_set_snap_frame *snap_frame =
+ (struct xr_msg_set_snap_frame *) data;
+ struct xradio_common *priv = (struct xradio_common *) hw->priv;
+ u8 frame_len = snap_frame->len;
+ u8 *frame = &snap_frame->frame[0];
+
+
+ /*
+ * Check length of incoming frame format:
+ * SNAP + SNAP_LEN (u8)
+ */
+ if (frame_len + sizeof(snap_frame->len) != len)
+ return -EINVAL;
+
+ if (frame_len > 0) {
+ priv->test_frame.data = (u8 *) xr_krealloc(priv->test_frame.data,
+ sizeof(u8) * frame_len, false);
+ if (priv->test_frame.data == NULL) {
+ sta_printk(XRADIO_DBG_ERROR, "xradio_set_snap_frame memory" \
+ "allocation failed");
+ priv->test_frame.len = 0;
+ return -EINVAL;
+ }
+ memcpy(priv->test_frame.data, frame, frame_len);
+ } else {
+ kfree(priv->test_frame.data);
+ priv->test_frame.data = NULL;
+ }
+ priv->test_frame.len = frame_len;
+ return 0;
+}
+
+#ifdef CONFIG_XRADIO_TESTMODE
+/**
+ * xradio_set_txqueue_params -Set txqueue params after successful TSPEC negotiation
+ *
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_set_txqueue_params(struct ieee80211_hw *hw,
+ u8 *data, int len)
+{
+ struct xr_msg_set_txqueue_params *txqueue_params =
+ (struct xr_msg_set_txqueue_params *) data;
+ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv;
+ struct xradio_vif *priv;
+ /* Interface ID is hard coded here, as interface is not
+ * passed in testmode command.
+ * Also it is assumed here that STA will be on interface
+ * 0 always.
+ */
+
+ int if_id = 0;
+ u16 queueId = xradio_priority_to_queueId[txqueue_params->user_priority];
+
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, if_id);
+
+ if (unlikely(!priv)) {
+ sta_printk(XRADIO_DBG_ERROR, "%s: Warning Priv is Null\n",
+ __func__);
+ return 0;
+ }
+ spin_unlock(&priv->vif_lock);
+
+ /* Default Ack policy is WSM_ACK_POLICY_NORMAL */
+ WSM_TX_QUEUE_SET(&priv->tx_queue_params,
+ queueId,
+ WSM_ACK_POLICY_NORMAL,
+ txqueue_params->medium_time,
+ txqueue_params->expiry_time);
+ return SYS_WARN(wsm_set_tx_queue_params(hw_priv,
+ &priv->tx_queue_params.params[queueId], queueId,
+ priv->if_id));
+}
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+/**
+ * xradio_tesmode_reply -called inside a testmode command
+ * handler to send a response to user space
+ *
+ * @wiphy: the wiphy
+ * @data: data to be send to user space
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_tesmode_reply(struct wiphy *wiphy,
+ const void *data, int len)
+{
+ int ret = 0;
+ struct sk_buff *skb = NULL;
+
+
+ skb = cfg80211_testmode_alloc_reply_skb(wiphy, nla_total_size(len));
+
+ if (!skb)
+ return -ENOMEM;
+
+ ret = nla_put(skb, XR_TM_MSG_DATA, len, data);
+ if (ret) {
+ kfree_skb(skb);
+ return ret;
+ }
+
+ return cfg80211_testmode_reply(skb);
+}
+
+/**
+ * xradio_tesmode_event -send asynchronous event
+ * to userspace
+ *
+ * @wiphy: the wiphy
+ * @msg_id: XR msg ID
+ * @data: data to be sent
+ * @len: data length
+ * @gfp: allocation flag
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_tesmode_event(struct wiphy *wiphy, const u32 msg_id,
+ const void *data, int len, gfp_t gfp)
+{
+ struct sk_buff *skb = NULL;
+
+
+ skb = cfg80211_testmode_alloc_event_skb(wiphy,
+ nla_total_size(len+sizeof(msg_id)), gfp);
+ if (!skb)
+ return -ENOMEM;
+
+ NLA_PUT_U32(skb, XR_TM_MSG_ID, msg_id);
+ if (data)
+ NLA_PUT(skb, XR_TM_MSG_DATA, len, data);
+
+ cfg80211_testmode_event(skb, gfp);
+ return 0;
+nla_put_failure:
+ kfree_skb(skb);
+ return -ENOBUFS;
+}
+
+/**
+ * example function for test purposes
+ * sends both: synchronous reply and asynchronous event
+ */
+static int xradio_test(struct ieee80211_hw *hw,
+ void *data, int len)
+{
+ struct xr_msg_test_t *test_p;
+ struct xr_reply_test_t reply;
+ struct xr_event_test_t event;
+
+
+ if (sizeof(struct xr_msg_test_t) != len)
+ return -EINVAL;
+
+ test_p = (struct xr_msg_test_t *) data;
+
+ reply.dummy = test_p->dummy + 10;
+
+ event.dummy = test_p->dummy + 20;
+
+ if (xradio_tesmode_event(hw->wiphy, XR_MSG_EVENT_TEST,
+ &event, sizeof(event), GFP_KERNEL))
+ return -1;
+
+ return xradio_tesmode_reply(hw->wiphy, &reply, sizeof(reply));
+}
+
+/**
+ * xradio_get_tx_power_level - send tx power level
+ * to userspace
+ *
+ * @hw: the hardware
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_get_tx_power_level(struct ieee80211_hw *hw)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ int get_power = 0;
+
+
+ get_power = hw_priv->output_power;
+ sta_printk(XRADIO_DBG_MSG, "%s: Power set on Device : %d",
+ __func__, get_power);
+ return xradio_tesmode_reply(hw->wiphy, &get_power, sizeof(get_power));
+}
+
+/**
+ * xradio_get_tx_power_range- send tx power range
+ * to userspace for each band
+ *
+ * @hw: the hardware
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_get_tx_power_range(struct ieee80211_hw *hw)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct wsm_tx_power_range txPowerRange[2];
+
+
+ size_t len = sizeof(txPowerRange);
+ memcpy(txPowerRange, hw_priv->txPowerRange, len);
+ return xradio_tesmode_reply(hw->wiphy, txPowerRange, len);
+}
+
+/**
+ * xradio_set_advance_scan_elems -Set Advcance Scan
+ * elements
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_set_advance_scan_elems(struct ieee80211_hw *hw,
+ u8 *data, int len)
+{
+ struct advance_scan_elems *scan_elems =
+ (struct advance_scan_elems *) data;
+ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv;
+ size_t elems_len = sizeof(struct advance_scan_elems);
+
+
+ if (elems_len != len)
+ return -EINVAL;
+
+ scan_elems = (struct advance_scan_elems *) data;
+
+ /* Locks required to prevent simultaneous scan */
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+
+ hw_priv->advanceScanElems.scanMode = scan_elems->scanMode;
+ hw_priv->advanceScanElems.duration = scan_elems->duration;
+ hw_priv->enable_advance_scan = true;
+
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+
+ return 0;
+}
+
+/**
+ * xradio_set_power_save -Set Power Save
+ * elements
+ * @hw: the hardware
+ * @data: data frame
+ * @len: data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_set_power_save(struct ieee80211_hw *hw,
+ u8 *data, int len)
+{
+ struct power_save_elems *ps_elems =
+ (struct power_save_elems *) data;
+ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv;
+ size_t elems_len = sizeof(struct power_save_elems);
+ struct xradio_vif *priv;
+ int if_id = 0;
+
+
+ /* Interface ID is hard coded here, as interface is not
+ * passed in testmode command.
+ * Also it is assumed here that STA will be on interface
+ * 0 always. */
+
+ if (elems_len != len)
+ return -EINVAL;
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, if_id);
+
+ if (unlikely(!priv)) {
+ sta_printk(XRADIO_DBG_ERROR, "%s: Warning Priv is Null\n",
+ __func__);
+ return 0;
+ }
+
+ spin_unlock(&priv->vif_lock);
+ mutex_lock(&hw_priv->conf_mutex);
+
+ ps_elems = (struct power_save_elems *) data;
+
+ if (ps_elems->powerSave == 1)
+ priv->user_pm_mode = WSM_PSM_PS;
+ else
+ priv->user_pm_mode = WSM_PSM_FAST_PS;
+
+ sta_printk(XRADIO_DBG_MSG, "Aid: %d, Joined: %s, Powersave: %s\n",
+ priv->bss_params.aid,
+ priv->join_status == XRADIO_JOIN_STATUS_STA ? "yes" : "no",
+ priv->user_pm_mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" :
+ priv->user_pm_mode == WSM_PSM_PS ? "WSM_PSM_PS" :
+ priv->user_pm_mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : "UNKNOWN");
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA &&
+ priv->bss_params.aid &&
+ priv->setbssparams_done &&
+ priv->filter4.enable) {
+ priv->powersave_mode.pmMode = priv->user_pm_mode;
+ xradio_set_pm(priv, &priv->powersave_mode);
+ }
+ else
+ priv->user_power_set_true = ps_elems->powerSave;
+ mutex_unlock(&hw_priv->conf_mutex);
+ return 0;
+}
+/**
+ * xradio_start_stop_tsm - starts/stops collecting TSM
+ *
+ * @hw: the hardware
+ * @data: data frame
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_start_stop_tsm(struct ieee80211_hw *hw, void *data)
+{
+ struct xr_msg_start_stop_tsm *start_stop_tsm =
+ (struct xr_msg_start_stop_tsm *) data;
+ struct xradio_common *hw_priv = hw->priv;
+
+
+ hw_priv->start_stop_tsm.start = start_stop_tsm->start;
+ hw_priv->start_stop_tsm.up = start_stop_tsm->up;
+ hw_priv->start_stop_tsm.packetization_delay =
+ start_stop_tsm->packetization_delay;
+ sta_printk(XRADIO_DBG_MSG, "%s: start : %u: up : %u",
+ __func__, hw_priv->start_stop_tsm.start,
+ hw_priv->start_stop_tsm.up);
+ hw_priv->tsm_info.ac = xradio_1d_to_ac[start_stop_tsm->up];
+
+ if (!hw_priv->start_stop_tsm.start) {
+ spin_lock_bh(&hw_priv->tsm_lock);
+ memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats));
+ memset(&hw_priv->tsm_info, 0, sizeof(hw_priv->tsm_info));
+ spin_unlock_bh(&hw_priv->tsm_lock);
+ }
+ return 0;
+}
+
+/**
+ * xradio_get_tsm_params - Retrieves TSM parameters
+ *
+ * @hw: the hardware
+ *
+ * Returns: TSM parameters collected
+ */
+int xradio_get_tsm_params(struct ieee80211_hw *hw)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ struct xr_tsm_stats tsm_stats;
+ u32 pkt_count;
+
+
+ spin_lock_bh(&hw_priv->tsm_lock);
+ pkt_count = hw_priv->tsm_stats.txed_msdu_count -
+ hw_priv->tsm_stats.msdu_discarded_count;
+ if (pkt_count) {
+ hw_priv->tsm_stats.avg_q_delay =
+ hw_priv->tsm_info.sum_pkt_q_delay/(pkt_count * 1000);
+ hw_priv->tsm_stats.avg_transmit_delay =
+ hw_priv->tsm_info.sum_media_delay/pkt_count;
+ } else {
+ hw_priv->tsm_stats.avg_q_delay = 0;
+ hw_priv->tsm_stats.avg_transmit_delay = 0;
+ }
+ sta_printk(XRADIO_DBG_MSG, "%s: Txed MSDU count : %u",
+ __func__, hw_priv->tsm_stats.txed_msdu_count);
+ sta_printk(XRADIO_DBG_MSG, "%s: Average queue delay : %u",
+ __func__, hw_priv->tsm_stats.avg_q_delay);
+ sta_printk(XRADIO_DBG_MSG, "%s: Average transmit delay : %u",
+ __func__, hw_priv->tsm_stats.avg_transmit_delay);
+ memcpy(&tsm_stats, &hw_priv->tsm_stats, sizeof(hw_priv->tsm_stats));
+ /* Reset the TSM statistics */
+ memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats));
+ hw_priv->tsm_info.sum_pkt_q_delay = 0;
+ hw_priv->tsm_info.sum_media_delay = 0;
+ spin_unlock_bh(&hw_priv->tsm_lock);
+ return xradio_tesmode_reply(hw->wiphy, &tsm_stats,
+ sizeof(hw_priv->tsm_stats));
+}
+
+/**
+ * xradio_get_roam_delay - Retrieves roam delay
+ *
+ * @hw: the hardware
+ *
+ * Returns: Returns the last measured roam delay
+ */
+int xradio_get_roam_delay(struct ieee80211_hw *hw)
+{
+ struct xradio_common *hw_priv = hw->priv;
+ u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
+ sta_printk(XRADIO_DBG_MSG, "%s: Roam delay : %u",
+ __func__, roam_delay);
+
+ spin_lock_bh(&hw_priv->tsm_lock);
+ hw_priv->tsm_info.roam_delay = 0;
+ hw_priv->tsm_info.use_rx_roaming = 0;
+ spin_unlock_bh(&hw_priv->tsm_lock);
+ return xradio_tesmode_reply(hw->wiphy, &roam_delay, sizeof(u16));
+}
+
+/**
+ * xradio_testmode_cmd -called when tesmode command
+ * reaches xradio
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ * @len: incoming data length
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_testmode_cmd(struct ieee80211_hw *hw, void *data, int len)
+{
+ int ret = 0;
+ struct nlattr *type_p = nla_find(data, len, XR_TM_MSG_ID);
+ struct nlattr *data_p = nla_find(data, len, XR_TM_MSG_DATA);
+
+
+ if (!type_p || !data_p)
+ return -EINVAL;
+
+ sta_printk(XRADIO_DBG_MSG, "%s: type: %i",
+ __func__, nla_get_u32(type_p));
+
+ switch (nla_get_u32(type_p)) {
+ case XR_MSG_TEST:
+ ret = xradio_test(hw,
+ nla_data(data_p), nla_len(data_p));
+ break;
+ case XR_MSG_SET_SNAP_FRAME:
+ ret = xradio_set_snap_frame(hw, (u8 *) nla_data(data_p),
+ nla_len(data_p));
+ break;
+ case XR_MSG_GET_TX_POWER_LEVEL:
+ ret = xradio_get_tx_power_level(hw);
+ break;
+ case XR_MSG_GET_TX_POWER_RANGE:
+ ret = xradio_get_tx_power_range(hw);
+ break;
+ case XR_MSG_SET_ADVANCE_SCAN_ELEMS:
+ ret = xradio_set_advance_scan_elems(hw, (u8 *) nla_data(data_p),
+ nla_len(data_p));
+ break;
+ case XR_MSG_SET_TX_QUEUE_PARAMS:
+ ret = xradio_set_txqueue_params(hw, (u8 *) nla_data(data_p),
+ nla_len(data_p));
+ break;
+ case XR_MSG_GET_TSM_PARAMS:
+ ret = xradio_get_tsm_params(hw);
+ break;
+ case XR_MSG_START_STOP_TSM:
+ ret = xradio_start_stop_tsm(hw, (u8 *) nla_data(data_p));
+ break;
+ case XR_MSG_GET_ROAM_DELAY:
+ ret = xradio_get_roam_delay(hw);
+ break;
+ case XR_MSG_SET_POWER_SAVE:
+ ret = xradio_set_power_save(hw, (u8 *) nla_data(data_p),
+ nla_len(data_p));
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+#endif /* CONFIG_XRADIO_TESTMODE */
diff --git a/drivers/net/wireless/xradio/sta.h b/drivers/net/wireless/xradio/sta.h
new file mode 100644
index 00000000..f663b63a
--- /dev/null
+++ b/drivers/net/wireless/xradio/sta.h
@@ -0,0 +1,131 @@
+/*
+ * sta interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef STA_H_INCLUDED
+#define STA_H_INCLUDED
+
+
+#ifdef XRADIO_USE_LONG_KEEP_ALIVE_PERIOD
+#define XRADIO_KEEP_ALIVE_PERIOD (28)
+#else
+/*For Samsung, it is defined as 4*/
+#define XRADIO_KEEP_ALIVE_PERIOD (4)
+#endif
+
+#ifdef XRADIO_USE_LONG_DTIM_PERIOD
+#define XRADIO_BSS_LOSS_THOLD_DEF 30
+#define XRADIO_LINK_LOSS_THOLD_DEF 50
+#else
+#define XRADIO_BSS_LOSS_THOLD_DEF 20
+#define XRADIO_LINK_LOSS_THOLD_DEF 40
+#endif
+
+/* ******************************************************************** */
+/* mac80211 API */
+
+int xradio_start(struct ieee80211_hw *dev);
+void xradio_stop(struct ieee80211_hw *dev);
+int xradio_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif);
+void xradio_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif);
+int xradio_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p);
+int xradio_config(struct ieee80211_hw *dev, u32 changed);
+int xradio_change_interface(struct ieee80211_hw *dev,
+ struct ieee80211_vif *vif,
+ enum nl80211_iftype new_type,
+ bool p2p);
+void xradio_configure_filter(struct ieee80211_hw *dev,
+ unsigned int changed_flags,
+ unsigned int *total_flags,
+ u64 multicast);
+int xradio_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
+ u16 queue, const struct ieee80211_tx_queue_params *params);
+int xradio_get_stats(struct ieee80211_hw *dev,
+ struct ieee80211_low_level_stats *stats);
+/* Not more a part of interface?
+int xradio_get_tx_stats(struct ieee80211_hw *dev,
+ struct ieee80211_tx_queue_stats *stats);
+*/
+int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void xradio_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop);
+
+
+int xradio_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_channel *chan,
+ int duration, enum ieee80211_roc_type type);
+int xradio_cancel_remain_on_channel(struct ieee80211_hw *hw);
+int xradio_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+u64 xradio_prepare_multicast(struct ieee80211_hw *hw,
+ struct netdev_hw_addr_list *mc_list);
+int xradio_set_pm(struct xradio_vif *priv, const struct wsm_set_pm *arg);
+void xradio_set_data_filter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void *data,
+ int len);
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+/* void xradio_set_pm_complete_cb(struct xradio_common *hw_priv,
+ struct wsm_set_pm_complete *arg); */
+void xradio_channel_switch_cb(struct xradio_common *hw_priv);
+
+/* ******************************************************************** */
+/* WSM events */
+
+void xradio_free_event_queue(struct xradio_common *hw_priv);
+void xradio_event_handler(struct work_struct *work);
+void xradio_bss_loss_work(struct work_struct *work);
+void xradio_connection_loss_work(struct work_struct *work);
+void xradio_keep_alive_work(struct work_struct *work);
+void xradio_tx_failure_work(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Internal API */
+
+int xradio_setup_mac(struct xradio_common *hw_priv);
+void xradio_join_work(struct work_struct *work);
+void xradio_join_timeout(struct work_struct *work);
+void xradio_unjoin_work(struct work_struct *work);
+void xradio_offchannel_work(struct work_struct *work);
+void xradio_wep_key_work(struct work_struct *work);
+void xradio_update_filtering(struct xradio_vif *priv);
+void xradio_update_filtering_work(struct work_struct *work);
+int __xradio_flush(struct xradio_common *hw_priv, bool drop, int if_id);
+void xradio_set_beacon_wakeup_period_work(struct work_struct *work);
+int xradio_enable_listening(struct xradio_vif *priv, struct ieee80211_channel *chan);
+int xradio_disable_listening(struct xradio_vif *priv);
+int xradio_set_uapsd_param(struct xradio_vif *priv, const struct wsm_edca_params *arg);
+void xradio_ba_work(struct work_struct *work);
+void xradio_ba_timer(unsigned long arg);
+const u8 *xradio_get_ie(u8 *start, size_t len, u8 ie);
+int xradio_vif_setup(struct xradio_vif *priv);
+int xradio_setup_mac_pvif(struct xradio_vif *priv);
+void xradio_iterate_vifs(void *data, u8 *mac, struct ieee80211_vif *vif);
+void xradio_rem_chan_timeout(struct work_struct *work);
+int xradio_set_macaddrfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data);
+#ifdef ROAM_OFFLOAD
+int xradio_testmode_event(struct wiphy *wiphy, const u32 msg_id,
+ const void *data, int len, gfp_t gfp);
+#endif /*ROAM_OFFLOAD*/
+#ifdef CONFIG_XRADIO_TESTMODE
+void xradio_device_power_calc(struct xradio_common *priv,
+ s16 max_output_power, s16 fe_cor, u32 band);
+int xradio_testmode_cmd(struct ieee80211_hw *hw, void *data, int len);
+int xradio_tesmode_event(struct wiphy *wiphy, const u32 msg_id,
+ const void *data, int len, gfp_t gfp);
+int xradio_get_tx_power_range(struct ieee80211_hw *hw);
+int xradio_get_tx_power_level(struct ieee80211_hw *hw);
+#endif /* CONFIG_XRADIO_TESTMODE */
+#endif
diff --git a/drivers/net/wireless/xradio/txrx.c b/drivers/net/wireless/xradio/txrx.c
new file mode 100644
index 00000000..1b3014a7
--- /dev/null
+++ b/drivers/net/wireless/xradio/txrx.c
@@ -0,0 +1,2210 @@
+/*
+ * Datapath implementation for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "xradio.h"
+#include "wsm.h"
+#include "bh.h"
+#include "ap.h"
+#include "sta.h"
+#include "sdio.h"
+#include "common.h"
+
+#define B_RATE_INDEX 0 //11b rate for important short frames in 2.4G.
+#define AG_RATE_INDEX 6 //11a/g rate for important short frames in 5G.
+#define XRADIO_INVALID_RATE_ID (0xFF)
+
+/* rate should fall quickly to avoid dropping frames by aps.
+ * Add by yangfh 2014-9-22 13:39:57
+ */
+#define HIGH_RATE_MAX_RETRY 7
+
+#ifdef CONFIG_XRADIO_TESTMODE
+#include "nl80211_testmode_msg_copy.h"
+#endif /* CONFIG_XRADIO_TESTMODE */
+#ifdef TES_P2P_0002_ROC_RESTART
+#include <linux/time.h>
+#endif
+static const struct ieee80211_rate *
+xradio_get_tx_rate(const struct xradio_common *hw_priv,
+ const struct ieee80211_tx_rate *rate);
+
+u32 TxedRateIdx_Map[24] = {0};
+u32 RxedRateIdx_Map[24] = {0};
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+//add by yangfh for tx rates debug.
+extern u8 rates_dbg_en;
+extern u32 rates_debug[3];
+extern u8 maxRate_dbg;
+extern u8 retry_dbg;
+#endif
+/* ******************************************************************** */
+/* TX policy cache implementation */
+
+static void tx_policy_dump(struct tx_policy *policy)
+{
+ txrx_printk(XRADIO_DBG_MSG, "[TX policy] "
+ "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X"
+ "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X"
+ "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n",
+ policy->raw[0] & 0x0F, policy->raw[0] >> 4,
+ policy->raw[1] & 0x0F, policy->raw[1] >> 4,
+ policy->raw[2] & 0x0F, policy->raw[2] >> 4,
+ policy->raw[3] & 0x0F, policy->raw[3] >> 4,
+ policy->raw[4] & 0x0F, policy->raw[4] >> 4,
+ policy->raw[5] & 0x0F, policy->raw[5] >> 4,
+ policy->raw[6] & 0x0F, policy->raw[6] >> 4,
+ policy->raw[7] & 0x0F, policy->raw[7] >> 4,
+ policy->raw[8] & 0x0F, policy->raw[8] >> 4,
+ policy->raw[9] & 0x0F, policy->raw[9] >> 4,
+ policy->raw[10] & 0x0F, policy->raw[10] >> 4,
+ policy->raw[11] & 0x0F, policy->raw[11] >> 4,
+ policy->defined);
+}
+
+static void xradio_check_go_neg_conf_success(struct xradio_common *hw_priv,
+ u8 *action)
+{
+
+ if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A &&
+ action[5] == 0x09 && action[6] == 0x02) {
+ if(action[17] == 0) {
+ hw_priv->is_go_thru_go_neg = true;
+ }
+ else {
+ hw_priv->is_go_thru_go_neg = false;
+ }
+ }
+}
+
+#ifdef TES_P2P_0002_ROC_RESTART
+///w, TES_P2P_0002 WorkAround:
+///w, P2P GO Neg Process and P2P FIND may be collision.
+///w, When P2P Device is waiting for GO NEG CFM in 30ms,
+///w, P2P FIND may end with p2p listen, and then goes to p2p search.
+///w, Then xradio scan will occupy phy on other channel in 3+ seconds.
+///w, P2P Device will not be able to receive the GO NEG CFM.
+///w, We extend the roc period to remaind phy to receive GO NEG CFM as WorkAround.
+
+s32 TES_P2P_0002_roc_dur;
+s32 TES_P2P_0002_roc_sec;
+s32 TES_P2P_0002_roc_usec;
+u32 TES_P2P_0002_packet_id;
+u32 TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
+
+static void xradio_frame_monitor(struct xradio_common *hw_priv, struct sk_buff *skb, bool tx) {
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+ u8 *action = (u8*)&mgmt->u.action.category;
+ u8 *category_code = &(action[0]);
+ u8 *action_code = &(action[1]);
+ u8 *oui = &(action[2]);
+ u8 *subtype = &(action[5]);
+ u8 *oui_subtype = &(action[6]);
+
+
+ if(ieee80211_is_action(frame->frame_control)) {
+ if( *category_code == WLAN_CATEGORY_PUBLIC) {
+ if (*action_code == 0x09) {
+ if((oui[0] == 0x50) && (oui[1] == 0x6F) &&
+ (oui[2] == 0x9A) && (*subtype == 0x09)) {
+ if ( *oui_subtype == 0x01 ) { ///w, GO Negotiation Response
+ if((TES_P2P_0002_state == TES_P2P_0002_STATE_IDLE) &&
+ (tx == true)) { ///w, p2p atturbute:status,id=0
+ u8 *go_neg_resp_res = &(action[17]);
+ if (*go_neg_resp_res == 0x0) {
+ TES_P2P_0002_state = TES_P2P_0002_STATE_SEND_RESP;
+ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_SEND_RESP]\n");
+ }
+ }
+ } else if ( *oui_subtype == 0x02 ) { ///w, GO Negotiation Confirmation
+ if( tx == false ) {
+ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
+ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE]"
+ "[GO Negotiation Confirmation]\n");
+ }
+ } else if ( *oui_subtype == 0x08 ) { ///w, Provision Discovery Response
+ if(tx == false) {
+ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
+ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE]"
+ "[Provision Discovery Response]\n");
+ }
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+
+static void xradio_check_prov_desc_req(struct xradio_common *hw_priv,
+ u8 *action)
+{
+
+ if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A &&
+ action[5] == 0x09 && action[6] == 0x07) {
+ hw_priv->is_go_thru_go_neg = false;
+ }
+}
+
+#ifdef AP_HT_COMPAT_FIX
+#define AP_COMPAT_THRESHOLD 2000
+#define AP_COMPAT_MIN_CNT 200
+u8 ap_compat_bssid[ETH_ALEN] = {0};
+static int xradio_apcompat_detect(struct xradio_vif *priv, u8 rx_rate)
+{
+ if (rx_rate < AG_RATE_INDEX) {
+ priv->ht_compat_cnt++;
+ txrx_printk(XRADIO_DBG_MSG,"%s:rate=%d.\n", __func__, rx_rate);
+ } else {
+ priv->ht_compat_det |= 1;
+ priv->ht_compat_cnt = 0;
+ txrx_printk(XRADIO_DBG_NIY,"%s:HT compat detect\n", __func__);
+ return 0;
+ }
+
+ /* Enhance compatibility with some illegal APs.*/
+ if (priv->ht_compat_cnt > AP_COMPAT_THRESHOLD ||
+ (priv->ht_compat_cnt > AP_COMPAT_MIN_CNT &&
+ priv->bssid[0] == 0xC8 &&
+ priv->bssid[1] == 0x3A &&
+ priv->bssid[2] == 0x35)) {
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ memcpy(ap_compat_bssid, priv->bssid, ETH_ALEN);
+ wms_send_disassoc_to_self(hw_priv, priv);
+ txrx_printk(XRADIO_DBG_WARN, "%s:SSID=%s, BSSID=" \
+ "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, priv->ssid,
+ ap_compat_bssid[0], ap_compat_bssid[1],
+ ap_compat_bssid[2], ap_compat_bssid[3],
+ ap_compat_bssid[4], ap_compat_bssid[5]);
+ return 1;
+ }
+ return 0;
+}
+
+static void xradio_remove_ht_ie(struct xradio_vif *priv, struct sk_buff *skb)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+ u8 *ies = NULL;
+ size_t ies_len = 0;
+ u8 *ht_ie = NULL;
+
+ if (!mgmt || memcmp(ap_compat_bssid, mgmt->bssid, ETH_ALEN))
+ return;
+
+ if (ieee80211_is_probe_resp(mgmt->frame_control))
+ ies = mgmt->u.probe_resp.variable;
+ else if (ieee80211_is_beacon(mgmt->frame_control))
+ ies = mgmt->u.beacon.variable;
+ else if (ieee80211_is_assoc_resp(mgmt->frame_control))
+ ies = mgmt->u.assoc_resp.variable;
+ else if (ieee80211_is_assoc_req(mgmt->frame_control))
+ ies = mgmt->u.assoc_req.variable;
+ else
+ return;
+
+ ies_len = skb->len - (ies - (u8 *)(skb->data));
+ ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY);
+ if (ht_ie) {
+ u8 ht_len = *(ht_ie + 1) + 2;
+ u8 move_len = (ies + ies_len) - (ht_ie + ht_len);
+ memmove(ht_ie, (ht_ie + ht_len), move_len);
+ skb_trim(skb, skb->len - ht_len);
+ ies_len = skb->len - (ies - (u8 *)(skb->data));
+ ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
+ if (ht_ie) {
+ ht_len = *(ht_ie + 1) + 2;
+ move_len = (ies + ies_len) - (ht_ie + ht_len);
+ memmove(ht_ie, (ht_ie + ht_len), move_len);
+ skb_trim(skb, skb->len - ht_len);
+ }
+ }
+ txrx_printk(XRADIO_DBG_WARN, "%s: BSSID=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__,
+ mgmt->bssid[0], mgmt->bssid[1],
+ mgmt->bssid[2], mgmt->bssid[3],
+ mgmt->bssid[4], mgmt->bssid[5]);
+}
+#endif //AP_HT_COMPAT_FIX
+
+//modified by yangfh
+static void tx_policy_build(const struct xradio_common *hw_priv,
+ /* [out] */ struct tx_policy *policy,
+ struct ieee80211_tx_rate *rates, size_t count)
+{
+ int i, j;
+ struct ieee80211_rate * tmp_rate = NULL;
+ unsigned limit = hw_priv->short_frame_max_tx_count;
+ unsigned max_rates_cnt = count;
+ unsigned total = 0;
+ SYS_BUG(rates[0].idx < 0);
+ memset(policy, 0, sizeof(*policy));
+
+
+ txrx_printk(XRADIO_DBG_NIY,"============================");
+#if 0
+ //debug yangfh
+ for (i = 0; i < count; ++i) {
+ if(rates[i].idx>=0) {
+ tmp_rate = xradio_get_tx_rate(hw_priv, &rates[i]);
+ txrx_printk(XRADIO_DBG_NIY,"[TX policy] Org %d.%dMps=%d",
+ tmp_rate->bitrate/10, tmp_rate->bitrate%10, rates[i].count);
+ }
+ }
+ txrx_printk(XRADIO_DBG_NIY,"----------------------------");
+#endif
+
+ /* minstrel is buggy a little bit, so distille
+ * incoming rates first.
+ */
+ /* Sort rates in descending order. */
+ total = rates[0].count;
+ for (i = 1; i < count; ++i) {
+ if (rates[i].idx > rates[i-1].idx) {
+ rates[i].idx = rates[i-1].idx>0?(rates[i-1].idx-1):-1;
+ }
+ if (rates[i].idx < 0 || i>=limit) {
+ count = i;
+ break;
+ } else {
+ total += rates[i].count;
+ }
+ }
+
+ /* Add lowest rate to the end when 11a/n.
+ * Don't apply in 11b/g because p2p unsupport 1Mbps.
+ * TODO: it's better to do this in rate control of mac80211.
+ */
+ if (((rates[0].flags & IEEE80211_TX_RC_MCS) ||
+ hw_priv->channel->band == NL80211_BAND_5GHZ) &&
+ count < max_rates_cnt && rates[count-1].idx != 0) {
+ rates[count].idx = 0;
+ rates[count].count = rates[0].count;
+ rates[count].flags = rates[0].flags;
+ total += rates[count].count;
+ count++;
+ }
+
+ /* adjust tx count to limit, rates should fall quickly
+ * and lower rates should be more retry, because reorder
+ * buffer of reciever will be timeout and clear probably.
+ */
+ if (count < 2) {
+ rates[0].count = limit;
+ total = limit;
+ } else {
+ u8 end_retry = 0; //the retry should be add to last rate.
+ if (limit > HIGH_RATE_MAX_RETRY) {
+ end_retry = limit - HIGH_RATE_MAX_RETRY;
+ limit = HIGH_RATE_MAX_RETRY;
+ }
+ for (i = 0; (limit != total) && (i < 100); ++i) { //i<100 to avoid dead loop
+ j = i % count;
+ if(limit < total) {
+ total += (rates[j].count > 1? -1 : 0);
+ rates[j].count += (rates[j].count > 1? -1 : 0);
+ } else {
+ j = count - 1 - j;
+ if (rates[j].count > 0) {
+ total++;
+ rates[j].count++;
+ }
+ }
+ }
+ if (end_retry) {
+ rates[count-1].count += end_retry;
+ limit += end_retry;
+ }
+ }
+
+ /* Eliminate duplicates. */
+ total = rates[0].count;
+ for (i = 0, j = 1; j < count; ++j) {
+ if (rates[j].idx == rates[i].idx) {
+ rates[i].count += rates[j].count;
+ } else if (rates[j].idx > rates[i].idx) {
+ break;
+ } else {
+ ++i;
+ if (i != j)
+ rates[i] = rates[j];
+ }
+ total += rates[j].count;
+ }
+ count = i + 1;
+
+ /* Re-fill policy trying to keep every requested rate and with
+ * respect to the global max tx retransmission count.
+ */
+ if (limit < count)
+ limit = count;
+ if (total > limit) {
+ for (i = 0; i < count; ++i) {
+ int left = count - i - 1;
+ if (rates[i].count > limit - left)
+ rates[i].count = limit - left;
+ limit -= rates[i].count;
+ }
+ }
+
+ /* HACK!!! Device has problems (at least) switching from
+ * 54Mbps CTS to 1Mbps. This switch takes enormous amount
+ * of time (100-200 ms), leading to valuable throughput drop.
+ * As a workaround, additional g-rates are injected to the
+ * policy.
+ */
+ if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) &&
+ rates[0].idx > 4 && rates[0].count > 2 &&
+ rates[1].idx < 2) {
+ /* ">> 1" is an equivalent of "/ 2", but faster */
+ int mid_rate = (rates[0].idx + 4) >> 1;
+
+ /* Decrease number of retries for the initial rate */
+ rates[0].count -= 2;
+
+ if (mid_rate != 4) {
+ /* Keep fallback rate at 1Mbps. */
+ rates[3] = rates[1];
+
+ /* Inject 1 transmission on lowest g-rate */
+ rates[2].idx = 4;
+ rates[2].count = 1;
+ rates[2].flags = rates[1].flags;
+
+ /* Inject 1 transmission on mid-rate */
+ rates[1].idx = mid_rate;
+ rates[1].count = 1;
+
+ /* Fallback to 1 Mbps is a really bad thing,
+ * so let's try to increase probability of
+ * successful transmission on the lowest g rate
+ * even more */
+ if (rates[0].count >= 3) {
+ --rates[0].count;
+ ++rates[2].count;
+ }
+
+ /* Adjust amount of rates defined */
+ count += 2;
+ } else {
+ /* Keep fallback rate at 1Mbps. */
+ rates[2] = rates[1];
+
+ /* Inject 2 transmissions on lowest g-rate */
+ rates[1].idx = 4;
+ rates[1].count = 2;
+
+ /* Adjust amount of rates defined */
+ count += 1;
+ }
+ }
+
+ tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[0]);
+ if(tmp_rate)
+ policy->defined = tmp_rate->hw_value + 1;
+
+ for (i = 0; i < count; ++i) {
+ register unsigned rateid, off, shift, retries;
+
+ tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[i]);
+ if(tmp_rate) {
+ rateid = tmp_rate->hw_value;
+ } else {
+ break;
+ }
+ off = rateid >> 3; /* eq. rateid / 8 */
+ shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */
+
+ retries = rates[i].count;
+ if (unlikely(retries > 0x0F))
+ rates[i].count = retries = 0x0F;
+ policy->tbl[off] |= __cpu_to_le32(retries << shift);
+ policy->retry_count += retries;
+ txrx_printk(XRADIO_DBG_NIY,"[TX policy] %d.%dMps=%d",
+ tmp_rate->bitrate/10, tmp_rate->bitrate%10, retries);
+ }
+
+ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Dst Policy (%d): " \
+ "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n",
+ count,
+ rates[0].idx, rates[0].count,
+ rates[1].idx, rates[1].count,
+ rates[2].idx, rates[2].count,
+ rates[3].idx, rates[3].count,
+ rates[4].idx, rates[4].count);
+}
+
+static inline bool tx_policy_is_equal(const struct tx_policy *wanted,
+ const struct tx_policy *cached)
+{
+ size_t count = wanted->defined >> 1;
+
+ if (wanted->defined > cached->defined)
+ return false;
+ if (count) {
+ if (memcmp(wanted->raw, cached->raw, count))
+ return false;
+ }
+ if (wanted->defined & 1) {
+ if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F))
+ return false;
+ }
+ return true;
+}
+
+static int tx_policy_find(struct tx_policy_cache *cache,
+ const struct tx_policy *wanted)
+{
+ /* O(n) complexity. Not so good, but there's only 8 entries in
+ * the cache.
+ * Also lru helps to reduce search time. */
+ struct tx_policy_cache_entry *it;
+ /* Search for policy in "used" list */
+ list_for_each_entry(it, &cache->used, link) {
+ if (tx_policy_is_equal(wanted, &it->policy))
+ return it - cache->cache;
+ }
+ /* Then - in "free list" */
+ list_for_each_entry(it, &cache->free, link) {
+ if (tx_policy_is_equal(wanted, &it->policy))
+ return it - cache->cache;
+ }
+ return -1;
+}
+
+static inline void tx_policy_use(struct tx_policy_cache *cache,
+ struct tx_policy_cache_entry *entry)
+{
+ ++entry->policy.usage_count;
+ list_move(&entry->link, &cache->used);
+}
+
+static inline int tx_policy_release(struct tx_policy_cache *cache,
+ struct tx_policy_cache_entry *entry)
+{
+ int ret = --entry->policy.usage_count;
+ if (!ret)
+ list_move(&entry->link, &cache->free);
+ return ret;
+}
+
+/* ******************************************************************** */
+/* External TX policy cache API */
+
+void tx_policy_init(struct xradio_common *hw_priv)
+{
+ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+ int i;
+
+
+ memset(cache, 0, sizeof(*cache));
+
+ spin_lock_init(&cache->lock);
+ INIT_LIST_HEAD(&cache->used);
+ INIT_LIST_HEAD(&cache->free);
+
+ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i)
+ list_add(&cache->cache[i].link, &cache->free);
+}
+
+static int tx_policy_get(struct xradio_common *hw_priv,
+ struct ieee80211_tx_rate *rates,
+ u8 use_bg_rate, bool *renew)
+{
+ int idx;
+ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+ struct tx_policy wanted;
+
+
+ if(use_bg_rate) {
+ u8 rate = (u8)(use_bg_rate & 0x3f);
+ u8 shitf = ((rate&0x7)<<2);
+ u8 off = (rate>>3);
+ memset(&wanted, 0, sizeof(wanted));
+ wanted.defined = rate + 1;
+ wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf);
+ wanted.tbl[off] = wanted.retry_count<<shitf;
+ txrx_printk(XRADIO_DBG_NIY, "[TX policy] robust rate=%d\n", rate);
+ } else
+ tx_policy_build(hw_priv, &wanted, rates, IEEE80211_TX_MAX_RATES);
+
+ /* use rate policy instead of minstel policy in debug mode*/
+#ifdef CONFIG_XRADIO_DEBUGFS
+ if (rates_dbg_en & 0x2) {
+ memset(&wanted, 0, sizeof(wanted));
+ wanted.defined = maxRate_dbg + 1;
+ wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf);
+ memcpy(&wanted.tbl[0], &rates_debug[0], sizeof(wanted.tbl));
+ }
+#endif
+
+ spin_lock_bh(&cache->lock);
+ idx = tx_policy_find(cache, &wanted);
+ if (idx >= 0) {
+ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Used TX policy: %d\n",
+ idx);
+ *renew = false;
+ } else {
+ struct tx_policy_cache_entry *entry;
+ if (WARN_ON_ONCE(list_empty(&cache->free))) {
+ spin_unlock_bh(&cache->lock);
+ txrx_printk(XRADIO_DBG_ERROR, "[TX policy] no policy cache\n");
+ return XRADIO_INVALID_RATE_ID;
+ }
+ /* If policy is not found create a new one
+ * using the oldest entry in "free" list */
+ *renew = true;
+ entry = list_entry(cache->free.prev,
+ struct tx_policy_cache_entry, link);
+ entry->policy = wanted;
+ idx = entry - cache->cache;
+ txrx_printk(XRADIO_DBG_MSG, "[TX policy] New TX policy: %d\n",
+ idx);
+ tx_policy_dump(&entry->policy);
+ }
+ tx_policy_use(cache, &cache->cache[idx]);
+ if (unlikely(list_empty(&cache->free))) {
+ /* Lock TX queues. */
+ txrx_printk(XRADIO_DBG_WARN, "[TX policy] policy cache used up\n");
+ xradio_tx_queues_lock(hw_priv);
+ }
+ spin_unlock_bh(&cache->lock);
+
+ /*force to upload retry limit when using debug rate policy */
+#ifdef CONFIG_XRADIO_DEBUGFS
+ if (retry_dbg & 0x2) {
+ retry_dbg &= ~0x2;
+ //retry dgb need to be applied to policy.
+ *renew = true;
+ cache->cache[idx].policy.uploaded = 0;
+ }
+#endif
+
+ return idx;
+}
+
+static void tx_policy_put(struct xradio_common *hw_priv, int idx)
+{
+ int usage, locked;
+ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+
+
+ spin_lock_bh(&cache->lock);
+ locked = list_empty(&cache->free);
+ usage = tx_policy_release(cache, &cache->cache[idx]);
+ if (unlikely(locked) && !usage) {
+ /* Unlock TX queues. */
+ xradio_tx_queues_unlock(hw_priv);
+ }
+ spin_unlock_bh(&cache->lock);
+}
+
+/*
+bool tx_policy_cache_full(struct xradio_common *hw_priv)
+{
+ bool ret;
+ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+ spin_lock_bh(&cache->lock);
+ ret = list_empty(&cache->free);
+ spin_unlock_bh(&cache->lock);
+ return ret;
+}
+*/
+extern u32 policy_upload;
+extern u32 policy_num;
+static int tx_policy_upload(struct xradio_common *hw_priv)
+{
+ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache;
+ int i;
+ struct wsm_set_tx_rate_retry_policy arg = {
+ .hdr = {
+ .numTxRatePolicies = 0,
+ }
+ };
+ int if_id = 0;
+
+
+ spin_lock_bh(&cache->lock);
+ /* Upload only modified entries. */
+ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) {
+ struct tx_policy *src = &cache->cache[i].policy;
+ if (src->retry_count && !src->uploaded) {
+ struct wsm_set_tx_rate_retry_policy_policy *dst =
+ &arg.tbl[arg.hdr.numTxRatePolicies];
+ dst->policyIndex = i;
+ dst->shortRetryCount = hw_priv->short_frame_max_tx_count-1;
+ //only RTS need use longRetryCount, should be short_frame.
+ dst->longRetryCount = hw_priv->short_frame_max_tx_count-1;
+
+ /* BIT(2) - Terminate retries when Tx rate retry policy
+ * finishes.
+ * BIT(3) - Count initial frame transmission as part of
+ * rate retry counting but not as a retry
+ * attempt */
+ dst->policyFlags = BIT(2) | BIT(3);
+ memcpy(dst->rateCountIndices, src->tbl,
+ sizeof(dst->rateCountIndices));
+ src->uploaded = 1;
+ ++arg.hdr.numTxRatePolicies;
+ }
+ }
+ spin_unlock_bh(&cache->lock);
+ atomic_set(&hw_priv->upload_count, 0);
+
+ xradio_debug_tx_cache_miss(hw_priv);
+ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Upload %d policies\n",
+ arg.hdr.numTxRatePolicies);
+#ifdef CONFIG_XRADIO_DEBUGFS
+ //test yangfh
+ if(arg.tbl[0].policyIndex == 7)
+ txrx_printk(XRADIO_DBG_MSG, "rate:0x%08x, 0x%08x, 0x%08x\n",
+ arg.tbl[0].rateCountIndices[2],
+ arg.tbl[0].rateCountIndices[1],
+ arg.tbl[0].rateCountIndices[0]);
+ policy_upload++;
+ policy_num += arg.hdr.numTxRatePolicies;
+#endif
+ /*TODO: COMBO*/
+ return wsm_set_tx_rate_retry_policy(hw_priv, &arg, if_id);
+}
+
+void tx_policy_upload_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, tx_policy_upload_work);
+
+
+ SYS_WARN(tx_policy_upload(hw_priv));
+ wsm_unlock_tx(hw_priv);
+}
+
+/* ******************************************************************** */
+/* xradio TX implementation */
+
+struct xradio_txinfo {
+ struct sk_buff *skb;
+ unsigned queue;
+ struct ieee80211_tx_info *tx_info;
+ const struct ieee80211_rate *rate;
+ struct ieee80211_hdr *hdr;
+ size_t hdrlen;
+ const u8 *da;
+ struct xradio_sta_priv *sta_priv;
+ struct xradio_txpriv txpriv;
+};
+
+u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv, u32 rates)
+{
+ u32 ret = 0;
+ int i;
+ u32 n_bitrates =
+ hw_priv->hw->wiphy->bands[hw_priv->channel->band]->n_bitrates;
+ struct ieee80211_rate * bitrates =
+ hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates;
+
+ for (i = 0; i < n_bitrates; ++i) {
+ if (rates & BIT(i))
+ ret |= BIT(bitrates[i].hw_value);
+ }
+ return ret;
+}
+
+static const struct ieee80211_rate *
+xradio_get_tx_rate(const struct xradio_common *hw_priv,
+ const struct ieee80211_tx_rate *rate)
+{
+ if (rate->idx < 0)
+ return NULL;
+ if (rate->flags & IEEE80211_TX_RC_MCS)
+ return &hw_priv->mcs_rates[rate->idx];
+ return &hw_priv->hw->wiphy->bands[hw_priv->channel->band]->
+ bitrates[rate->idx];
+}
+
+inline static s8
+xradio_get_rate_idx(const struct xradio_common *hw_priv, u8 flag, u16 hw_value)
+{
+ s16 ret = (s16)hw_value;
+ if(flag & IEEE80211_TX_RC_MCS) { //11n
+ if(hw_value <= hw_priv->mcs_rates[7].hw_value &&
+ hw_value >= hw_priv->mcs_rates[0].hw_value)
+ ret -= hw_priv->mcs_rates[0].hw_value;
+ else
+ ret = -1;
+ } else { //11b/g
+ if(hw_value>5 && hw_value<hw_priv->mcs_rates[0].hw_value) {
+ ret -= hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value;
+ if(hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value<5) //11a
+ ret -= 2;
+ } else if(hw_value<4) {
+ ret -= hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value;
+ } else {
+ ret = -1;
+ }
+ }
+ return (s8)ret;
+}
+
+static int
+xradio_tx_h_calc_link_ids(struct xradio_vif *priv,
+ struct ieee80211_tx_control *control,
+ struct xradio_txinfo *t)
+{
+#ifndef P2P_MULTIVIF
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+#endif
+
+
+#ifndef P2P_MULTIVIF
+ if ((t->tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) ||
+ (hw_priv->roc_if_id == priv->if_id))
+ t->txpriv.offchannel_if_id = 2;
+ else
+ t->txpriv.offchannel_if_id = 0;
+#endif
+
+ if (likely(control->sta && t->sta_priv->link_id))
+ t->txpriv.raw_link_id =
+ t->txpriv.link_id =
+ t->sta_priv->link_id;
+ else if (priv->mode != NL80211_IFTYPE_AP)
+ t->txpriv.raw_link_id =
+ t->txpriv.link_id = 0;
+ else if (is_multicast_ether_addr(t->da)) {
+ if (priv->enable_beacon) {
+ t->txpriv.raw_link_id = 0;
+ t->txpriv.link_id = priv->link_id_after_dtim;
+ } else {
+ t->txpriv.raw_link_id = 0;
+ t->txpriv.link_id = 0;
+ }
+ } else {
+ t->txpriv.link_id =
+ xradio_find_link_id(priv, t->da);
+ /* Do not assign valid link id for deauth/disassoc frame being
+ transmitted to an unassociated STA */
+ if (!(t->txpriv.link_id) &&
+ (ieee80211_is_deauth(t->hdr->frame_control) ||
+ ieee80211_is_disassoc(t->hdr->frame_control))) {
+ t->txpriv.link_id = 0;
+ } else {
+ if (!t->txpriv.link_id)
+ t->txpriv.link_id = xradio_alloc_link_id(priv, t->da);
+ if (!t->txpriv.link_id) {
+ txrx_printk(XRADIO_DBG_ERROR,
+ "%s: No more link IDs available.\n", __func__);
+ return -ENOENT;
+ }
+ }
+ t->txpriv.raw_link_id = t->txpriv.link_id;
+ }
+ if (t->txpriv.raw_link_id)
+ priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp =
+ jiffies;
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ if (control->sta &&
+ (control->sta->uapsd_queues & BIT(t->queue)))
+ t->txpriv.link_id = priv->link_id_uapsd;
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+ return 0;
+}
+
+static void
+xradio_tx_h_pm(struct xradio_vif *priv,
+ struct xradio_txinfo *t)
+{
+
+
+ if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) {
+ u32 mask = ~BIT(t->txpriv.raw_link_id);
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->sta_asleep_mask &= mask;
+ priv->pspoll_mask &= mask;
+ spin_unlock_bh(&priv->ps_state_lock);
+ }
+}
+
+static void
+xradio_tx_h_calc_tid(struct xradio_vif *priv,
+ struct xradio_txinfo *t)
+{
+ if (ieee80211_is_data_qos(t->hdr->frame_control)) {
+ u8 *qos = ieee80211_get_qos_ctl(t->hdr);
+ t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK;
+ } else if (ieee80211_is_data(t->hdr->frame_control)) {
+ t->txpriv.tid = 0;
+ }
+}
+
+/* IV/ICV injection. */
+/* TODO: Quite unoptimal. It's better co modify mac80211
+ * to reserve space for IV */
+static int
+xradio_tx_h_crypt(struct xradio_vif *priv,
+ struct xradio_txinfo *t)
+{
+ size_t iv_len;
+ size_t icv_len;
+ u8 *icv;
+
+
+ if (!t->tx_info->control.hw_key ||
+ !(t->hdr->frame_control &
+ __cpu_to_le32(IEEE80211_FCTL_PROTECTED)))
+ return 0;
+
+ iv_len = t->tx_info->control.hw_key->iv_len;
+ icv_len = t->tx_info->control.hw_key->icv_len;
+
+ if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP)
+ icv_len += 8; /* MIC */
+
+ if ((skb_headroom(t->skb) + skb_tailroom(t->skb) <
+ iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) ||
+ (skb_headroom(t->skb) <
+ iv_len + WSM_TX_EXTRA_HEADROOM)) {
+ txrx_printk(XRADIO_DBG_ERROR,
+ "Bug: no space allocated for crypto headers.\n"
+ "headroom: %d, tailroom: %d, "
+ "req_headroom: %d, req_tailroom: %d\n"
+ "Please fix it in xradio_get_skb().\n",
+ skb_headroom(t->skb), skb_tailroom(t->skb),
+ iv_len + WSM_TX_EXTRA_HEADROOM, icv_len);
+ return -ENOMEM;
+ } else if (skb_tailroom(t->skb) < icv_len) {
+ size_t offset = icv_len - skb_tailroom(t->skb);
+ u8 *p;
+ txrx_printk(XRADIO_DBG_ERROR,
+ "Slowpath: tailroom is not big enough. "
+ "Req: %d, got: %d.\n",
+ icv_len, skb_tailroom(t->skb));
+
+ p = skb_push(t->skb, offset);
+ memmove(p, &p[offset], t->skb->len - offset);
+ skb_trim(t->skb, t->skb->len - offset);
+ }
+ /* ccmp pkt from umac to driver,it has iv room,,so ccmp pkt do not add iv room */
+ if (t->tx_info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP){
+ u8 *newhdr;
+ newhdr = skb_push(t->skb, iv_len);
+ memmove(newhdr, newhdr + iv_len, t->hdrlen);
+ t->hdr = (struct ieee80211_hdr *) newhdr;
+ }
+ t->hdrlen += iv_len;
+ icv = skb_put(t->skb, icv_len);
+
+ return 0;
+}
+
+static int
+xradio_tx_h_align(struct xradio_vif *priv, struct xradio_txinfo *t,
+ u8 *flags)
+{
+ size_t offset = (size_t)t->skb->data & 3;
+ u8 *newhdr;//add by dingxh
+
+
+ if (!offset)
+ return 0;
+
+ if (skb_headroom(t->skb) < offset) {
+ txrx_printk(XRADIO_DBG_ERROR,
+ "Bug: no space allocated "
+ "for DMA alignment.\n"
+ "headroom: %d\n",
+ skb_headroom(t->skb));
+ return -ENOMEM;
+ }
+ //offset = 1or3 process add by dingxh
+ if (offset & 1) {
+ newhdr = skb_push(t->skb, offset);
+ memmove(newhdr, newhdr + offset, t->skb->len-offset);
+ skb_trim(t->skb, t->skb->len-offset);
+ t->hdr = (struct ieee80211_hdr *) newhdr;
+ xradio_debug_tx_align(priv);
+ return 0;
+ }
+ //add by dingxh
+ //offset=2 process
+ skb_push(t->skb, offset);
+ t->hdrlen += offset;
+ t->txpriv.offset += offset;
+ *flags |= WSM_TX_2BYTES_SHIFT;
+ xradio_debug_tx_align(priv);
+ return 0;
+}
+
+static int
+xradio_tx_h_action(struct xradio_vif *priv, struct xradio_txinfo *t)
+{
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)t->hdr;
+
+
+ if (ieee80211_is_action(t->hdr->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_BACK)
+ return 1;
+ else
+ return 0;
+}
+
+/* Add WSM header */
+static struct wsm_tx *
+xradio_tx_h_wsm(struct xradio_vif *priv, struct xradio_txinfo *t)
+{
+ struct wsm_tx *wsm;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) {
+ txrx_printk(XRADIO_DBG_ERROR,
+ "Bug: no space allocated "
+ "for WSM header.\n"
+ "headroom: %d\n",
+ skb_headroom(t->skb));
+ return NULL;
+ }
+
+ wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx));
+ t->txpriv.offset += sizeof(struct wsm_tx);
+ memset(wsm, 0, sizeof(*wsm));
+ wsm->hdr.len = __cpu_to_le16(t->skb->len);
+ wsm->hdr.id = __cpu_to_le16(0x0004);
+ wsm->queueId = (t->txpriv.raw_link_id << 2) | wsm_queue_id_to_wsm(t->queue);
+ if (wsm->hdr.len > hw_priv->wsm_caps.sizeInpChBuf) {
+ txrx_printk(XRADIO_DBG_ERROR,"%s,msg length too big=%d\n",
+ __func__, wsm->hdr.len);
+ wsm = NULL;
+ }
+
+ return wsm;
+}
+
+/* BT Coex specific handling */
+static void
+xradio_tx_h_bt(struct xradio_vif *priv, struct xradio_txinfo *t, struct wsm_tx *wsm)
+{
+ u8 priority = 0;
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+
+ if (!hw_priv->is_BT_Present)
+ return;
+
+ if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control)))
+ priority = WSM_EPTA_PRIORITY_MGT;
+ else if (ieee80211_is_data(t->hdr->frame_control)) {
+ /* Skip LLC SNAP header (+6) */
+ u8 *payload = &t->skb->data[t->hdrlen];
+ u16 *ethertype = (u16 *) &payload[6];
+ if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE)))
+ priority = WSM_EPTA_PRIORITY_EAPOL;
+ } else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) ||
+ ieee80211_is_reassoc_req(t->hdr->frame_control))) {
+ struct ieee80211_mgmt *mgt_frame =
+ (struct ieee80211_mgmt *)t->hdr;
+
+ if (mgt_frame->u.assoc_req.listen_interval <
+ priv->listen_interval) {
+ txrx_printk(XRADIO_DBG_MSG,
+ "Modified Listen Interval to %d from %d\n",
+ priv->listen_interval,
+ mgt_frame->u.assoc_req.listen_interval);
+ /* Replace listen interval derieved from
+ * the one read from SDD */
+ mgt_frame->u.assoc_req.listen_interval =
+ priv->listen_interval;
+ }
+ }
+
+ if (likely(!priority)) {
+ if (ieee80211_is_action(t->hdr->frame_control))
+ priority = WSM_EPTA_PRIORITY_ACTION;
+ else if (ieee80211_is_mgmt(t->hdr->frame_control))
+ priority = WSM_EPTA_PRIORITY_MGT;
+ else if ((wsm->queueId == WSM_QUEUE_VOICE))
+ priority = WSM_EPTA_PRIORITY_VOICE;
+ else if ((wsm->queueId == WSM_QUEUE_VIDEO))
+ priority = WSM_EPTA_PRIORITY_VIDEO;
+ else
+ priority = WSM_EPTA_PRIORITY_DATA;
+ }
+
+ txrx_printk(XRADIO_DBG_MSG, "[TX] EPTA priority %d.\n",
+ priority);
+
+ wsm->flags |= priority << 1;
+}
+
+static int
+xradio_tx_h_rate_policy(struct xradio_common *hw_priv, struct xradio_txinfo *t,
+ struct wsm_tx *wsm)
+{
+ bool tx_policy_renew = false;
+ struct xradio_vif *priv =
+ xrwl_get_vif_from_ieee80211(t->tx_info->control.vif);
+
+
+ /*use debug policy for data frames only*/
+#ifdef CONFIG_XRADIO_DEBUGFS
+ if((rates_dbg_en & 0x1) && ieee80211_is_data(t->hdr->frame_control)) {
+ rates_dbg_en |= 0x02;
+ }
+#endif
+
+ t->txpriv.rate_id = tx_policy_get(hw_priv,
+ t->tx_info->control.rates, t->txpriv.use_bg_rate,
+ &tx_policy_renew);
+ if (t->txpriv.rate_id == XRADIO_INVALID_RATE_ID)
+ return -EFAULT;
+
+ wsm->flags |= t->txpriv.rate_id << 4;
+ t->rate = xradio_get_tx_rate(hw_priv, &t->tx_info->control.rates[0]);
+ if (t->txpriv.use_bg_rate)
+ wsm->maxTxRate = (u8)(t->txpriv.use_bg_rate & 0x3f);
+ else
+ wsm->maxTxRate = t->rate->hw_value;
+
+ /*set the maxTxRate and clear the dataframe flag of rates_dbg_en */
+#ifdef CONFIG_XRADIO_DEBUGFS
+ if(rates_dbg_en & 0x02) {
+ wsm->maxTxRate = maxRate_dbg;
+ rates_dbg_en &= ~0x2;
+ }
+#endif
+
+ if (t->rate->flags & IEEE80211_TX_RC_MCS) {
+ if (priv->association_mode.greenfieldMode)
+ wsm->htTxParameters |=
+ __cpu_to_le32(WSM_HT_TX_GREENFIELD);
+ else
+ wsm->htTxParameters |=
+ __cpu_to_le32(WSM_HT_TX_MIXED);
+ }
+
+ if (tx_policy_renew) {
+ txrx_printk(XRADIO_DBG_MSG, "[TX] TX policy renew.\n");
+ /* It's not so optimal to stop TX queues every now and then.
+ * Maybe it's better to reimplement task scheduling with
+ * a counter. */
+ /* xradio_tx_queues_lock(priv); */
+ /* Definetly better. TODO. */
+ if (atomic_add_return(1, &hw_priv->upload_count) == 1) {
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue,
+ &hw_priv->tx_policy_upload_work) <= 0) {
+ atomic_set(&hw_priv->upload_count, 0);
+ wsm_unlock_tx(hw_priv);
+ }
+ }
+ }
+ return 0;
+}
+
+static bool
+xradio_tx_h_pm_state(struct xradio_vif *priv, struct xradio_txinfo *t)
+{
+ int was_buffered = 1;
+
+
+ if (t->txpriv.link_id == priv->link_id_after_dtim &&
+ !priv->buffered_multicasts) {
+ priv->buffered_multicasts = true;
+ if (priv->sta_asleep_mask)
+ queue_work(priv->hw_priv->workqueue,
+ &priv->multicast_start_work);
+ }
+
+ if (t->txpriv.raw_link_id && t->txpriv.tid < XRADIO_MAX_TID)
+ was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1]
+ .buffered[t->txpriv.tid]++;
+
+ return !was_buffered;
+}
+
+static void
+xradio_tx_h_ba_stat(struct xradio_vif *priv,
+ struct xradio_txinfo *t)
+{
+ struct xradio_common *hw_priv = priv->hw_priv;
+
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_STA)
+ return;
+ if (!xradio_is_ht(&hw_priv->ht_oper))
+ return;
+ if (!priv->setbssparams_done)
+ return;
+ if (!ieee80211_is_data(t->hdr->frame_control))
+ return;
+
+ spin_lock_bh(&hw_priv->ba_lock);
+ hw_priv->ba_acc += t->skb->len - t->hdrlen;
+ if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
+ mod_timer(&hw_priv->ba_timer,
+ jiffies + XRADIO_BLOCK_ACK_INTERVAL);
+ }
+ hw_priv->ba_cnt++;
+ spin_unlock_bh(&hw_priv->ba_lock);
+}
+
+static int
+xradio_tx_h_skb_pad(struct xradio_common *priv,
+ struct wsm_tx *wsm,
+ struct sk_buff *skb)
+{
+ size_t len = __le16_to_cpu(wsm->hdr.len);
+ size_t padded_len = sdio_align_len(priv, len);
+
+
+ if (SYS_WARN(skb_padto(skb, padded_len) != 0)) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/* ******************************************************************** */
+//#if (defined(CONFIG_XRADIO_DEBUG))
+u16 txparse_flags = 0;//PF_DHCP|PF_8021X|PF_MGMT;
+u16 rxparse_flags = 0;//PF_DHCP|PF_8021X|PF_MGMT;
+//#endif
+
+void xradio_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb)
+{
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_txinfo t = {
+ .skb = skb,
+ .queue = skb_get_queue_mapping(skb),
+ .tx_info = IEEE80211_SKB_CB(skb),
+ .hdr = (struct ieee80211_hdr *)skb->data,
+ .txpriv.tid = XRADIO_MAX_TID,
+ .txpriv.rate_id = XRADIO_INVALID_RATE_ID,
+#ifdef P2P_MULTIVIF
+ .txpriv.raw_if_id = 0,
+#endif
+ .txpriv.use_bg_rate = 0,
+ };
+ struct ieee80211_sta *sta;
+ struct wsm_tx *wsm;
+ bool tid_update = 0;
+ u8 flags = 0;
+ int ret = 0;
+ struct xradio_vif *priv;
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+
+ if (!skb->data)
+ SYS_BUG(1);
+
+ if (!(t.tx_info->control.vif)) {
+ ret = __LINE__;
+ goto drop;
+ }
+ priv = xrwl_get_vif_from_ieee80211(t.tx_info->control.vif);
+ if (!priv) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+ if (atomic_read(&priv->enabled) == 0) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+#if (defined(CONFIG_XRADIO_DEBUG))
+ /* parse frame for debug. */
+ if (txparse_flags){
+ u8 temp_iv_len ;
+ if(t.tx_info->control.hw_key &&
+ (t.hdr->frame_control & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) &&
+ (t.tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP))
+ temp_iv_len = t.tx_info->control.hw_key->iv_len;
+ else
+ temp_iv_len =0;
+ xradio_parse_frame(skb->data, temp_iv_len, txparse_flags, priv->if_id);
+ }
+#endif
+
+ //dhcp and 80211 frames are important, use b/g rate and delay scan.
+ //it can make sense, such as accelerate connect.
+ if (ieee80211_is_auth(frame->frame_control)) {
+ hw_priv->connet_time[priv->if_id] = jiffies;
+ } else if (ieee80211_is_data_present(frame->frame_control)) {
+ /* since Umac had already alloc IV space in ccmp skb, so we need to add this iv_len as the new offset to LLC */
+ u8* llc = NULL;
+ if(t.tx_info->control.hw_key &&
+ (t.hdr->frame_control & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) &&
+ (t.tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP))
+ llc = skb->data+ieee80211_hdrlen(frame->frame_control) + t.tx_info->control.hw_key->iv_len;
+ else
+ llc = skb->data+ieee80211_hdrlen(frame->frame_control);
+ if (is_dhcp(llc) || is_8021x(llc)) {
+ t.txpriv.use_bg_rate =
+ hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value;
+ if (priv->vif->p2p)
+ t.txpriv.use_bg_rate = AG_RATE_INDEX;
+ t.txpriv.use_bg_rate |= 0x80;
+ }
+ if (t.txpriv.use_bg_rate){
+ hw_priv->connet_time[priv->if_id] = jiffies;
+ }
+ } else if (ieee80211_is_deauth(frame->frame_control) ||
+ ieee80211_is_disassoc(frame->frame_control)) {
+ hw_priv->connet_time[priv->if_id] = 0;
+ }
+
+#ifdef AP_HT_COMPAT_FIX
+ if (ieee80211_is_assoc_req(frame->frame_control) &&
+ priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) {
+ xradio_remove_ht_ie(priv, skb);
+ }
+#endif
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ spin_lock_bh(&hw_priv->tsm_lock);
+ if (hw_priv->start_stop_tsm.start) {
+ if (hw_priv->tsm_info.ac == t.queue)
+ hw_priv->tsm_stats.txed_msdu_count++;
+ }
+ spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ xradio_frame_monitor(hw_priv,skb,true);
+#endif
+
+ if (ieee80211_is_action(frame->frame_control) &&
+ mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) {
+ u8 *action = (u8*)&mgmt->u.action.category;
+ xradio_check_go_neg_conf_success(hw_priv, action);
+ xradio_check_prov_desc_req(hw_priv, action);
+ }
+
+ t.txpriv.if_id = priv->if_id;
+ t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control);
+ t.da = ieee80211_get_DA(t.hdr);
+ t.sta_priv =
+ (struct xradio_sta_priv *)&control->sta->drv_priv;
+
+ if (SYS_WARN(t.queue >= 4)) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+ //spin_lock_bh(&hw_priv->tx_queue[t.queue].lock);
+ //if ((priv->if_id == 0) &&
+ // (hw_priv->tx_queue[t.queue].num_queued_vif[0] >=
+ // hw_priv->vif0_throttle)) {
+ // spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
+ //
+ // ret = __LINE__;
+ // goto drop;
+ //} else if ((priv->if_id == 1) &&
+ // (hw_priv->tx_queue[t.queue].num_queued_vif[1] >=
+ // hw_priv->vif1_throttle)) {
+ // spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
+ //
+ // ret = __LINE__;
+ // goto drop;
+ //}
+ //spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock);
+
+ ret = xradio_tx_h_calc_link_ids(priv, control, &t);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+ dev_dbg(hw_priv->pdev, "vif %d: tx, %d bytes queue %d, link_id %d(%d).\n",
+ priv->if_id, skb->len, t.queue, t.txpriv.link_id, t.txpriv.raw_link_id);
+ if(ieee80211_is_assoc_resp(frame->frame_control)){
+ dev_dbg(hw_priv->pdev, "vif %d: association response\n", priv->if_id);
+ }
+
+ xradio_tx_h_pm(priv, &t);
+ xradio_tx_h_calc_tid(priv, &t);
+ ret = xradio_tx_h_crypt(priv, &t);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+ ret = xradio_tx_h_align(priv, &t, &flags);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+ ret = xradio_tx_h_action(priv, &t);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+ wsm = xradio_tx_h_wsm(priv, &t);
+ if (!wsm) {
+ ret = __LINE__;
+ goto drop;
+ }
+#ifdef CONFIG_XRADIO_TESTMODE
+ flags |= WSM_TX_FLAG_EXPIRY_TIME;
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ wsm->flags |= flags;
+ xradio_tx_h_bt(priv, &t, wsm);
+ ret = xradio_tx_h_rate_policy(hw_priv, &t, wsm);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+ ret = xradio_tx_h_skb_pad(hw_priv, wsm, skb);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+ rcu_read_lock();
+ sta = rcu_dereference(control->sta);
+
+ xradio_tx_h_ba_stat(priv, &t);
+ spin_lock_bh(&priv->ps_state_lock);
+ {
+ tid_update = xradio_tx_h_pm_state(priv, &t);
+ SYS_BUG(xradio_queue_put(&hw_priv->tx_queue[t.queue],
+ t.skb, &t.txpriv));
+#ifdef ROC_DEBUG
+ txrx_printk(XRADIO_DBG_ERROR, "QPUT %x, %pM, if_id - %d\n",
+ t.hdr->frame_control, t.da, priv->if_id);
+#endif
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ if (tid_update && sta)
+ ieee80211_sta_set_buffered(sta,
+ t.txpriv.tid, true);
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+
+ rcu_read_unlock();
+
+ xradio_bh_wakeup(hw_priv);
+
+ return;
+
+drop:
+ dev_dbg(hw_priv->pdev, "dropped tx at line %d, fctl=0x%04x.\n", ret, frame->frame_control);
+ xradio_skb_dtor(hw_priv, skb, &t.txpriv);
+ return;
+}
+
+/* ******************************************************************** */
+
+static int xradio_handle_pspoll(struct xradio_vif *priv,
+ struct sk_buff *skb)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct ieee80211_sta *sta;
+ struct ieee80211_pspoll *pspoll =
+ (struct ieee80211_pspoll *) skb->data;
+ int link_id = 0;
+ u32 pspoll_mask = 0;
+ int drop = 1;
+ int i;
+
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_AP)
+ goto done;
+ if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN))
+ goto done;
+
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, pspoll->ta);
+ if (sta) {
+ struct xradio_sta_priv *sta_priv;
+ sta_priv = (struct xradio_sta_priv *)&sta->drv_priv;
+ link_id = sta_priv->link_id;
+ pspoll_mask = BIT(sta_priv->link_id);
+ }
+ rcu_read_unlock();
+ if (!link_id)
+ goto done;
+
+ priv->pspoll_mask |= pspoll_mask;
+ drop = 0;
+
+ /* Do not report pspols if data for given link id is
+ * queued already. */
+ for (i = 0; i < 4; ++i) {
+ if (xradio_queue_get_num_queued(priv,
+ &hw_priv->tx_queue[i],
+ pspoll_mask)) {
+ xradio_bh_wakeup(hw_priv);
+ drop = 1;
+ break;
+ }
+ }
+ txrx_printk(XRADIO_DBG_NIY, "[RX] PSPOLL: %s\n", drop ? "local" : "fwd");
+done:
+ return drop;
+}
+
+/* ******************************************************************** */
+//for test yangfh
+extern u32 tx_retrylimit;
+extern u32 tx_over_limit;
+extern u32 tx_lower_limit;
+extern int retry_mis;
+
+void xradio_tx_confirm_cb(struct xradio_common *hw_priv,
+ struct wsm_tx_confirm *arg)
+{
+ u8 queue_id = xradio_queue_get_queue_id(arg->packetID);
+ struct xradio_queue *queue = &hw_priv->tx_queue[queue_id];
+ struct sk_buff *skb;
+ const struct xradio_txpriv *txpriv;
+ struct xradio_vif *priv;
+ u32 feedback_retry = 0;
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg->if_id);
+ if (unlikely(!priv))
+ return;
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+ /* STA is stopped. */
+ spin_unlock(&priv->vif_lock);
+ return;
+ }
+
+ if (SYS_WARN(queue_id >= 4)) {
+ spin_unlock(&priv->vif_lock);
+ return;
+ }
+
+ dev_dbg(hw_priv->pdev, "vif %d: tx confirm status=%d, retry=%d, lastRate=%d\n",
+ priv->if_id, arg->status, arg->ackFailures, arg->txedRate);
+
+ if ((arg->status == WSM_REQUEUE) &&
+ (arg->flags & WSM_TX_STATUS_REQUEUE)) {
+ /* "Requeue" means "implicit suspend" */
+ struct wsm_suspend_resume suspend = {
+ .link_id = arg->link_id,
+ .stop = 1,
+ .multicast = !arg->link_id,
+ .if_id = arg->if_id,
+ };
+ xradio_suspend_resume(priv, &suspend);
+ txrx_printk(XRADIO_DBG_WARN, "Requeue for link_id %d (try %d)."
+ " STAs asleep: 0x%.8X\n",
+ arg->link_id,
+ xradio_queue_get_generation(arg->packetID) + 1,
+ priv->sta_asleep_mask);
+
+ SYS_WARN(xradio_queue_requeue(queue,
+ arg->packetID, true));
+
+ spin_lock_bh(&priv->ps_state_lock);
+ if (!arg->link_id) {
+ priv->buffered_multicasts = true;
+ if (priv->sta_asleep_mask) {
+ queue_work(hw_priv->workqueue,
+ &priv->multicast_start_work);
+ }
+ }
+ spin_unlock_bh(&priv->ps_state_lock);
+ spin_unlock(&priv->vif_lock);
+ } else if (!SYS_WARN(xradio_queue_get_skb(
+ queue, arg->packetID, &skb, &txpriv))) {
+ struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset];
+ int tx_count = arg->ackFailures;
+ u8 ht_flags = 0;
+ int i;
+
+ //yangfh add to reset if_0 in firmware when STA-unjoined,
+ //fix the errors when switch APs in combo mode.
+ if (unlikely(ieee80211_is_disassoc(frame->frame_control) ||
+ ieee80211_is_deauth(frame->frame_control))) {
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ wms_send_deauth_to_self(hw_priv, priv);
+ /* Shedule unjoin work */
+ txrx_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX) by self.\n");
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ }
+ }
+
+ if (priv->association_mode.greenfieldMode)
+ ht_flags |= IEEE80211_TX_RC_GREEN_FIELD;
+
+ //bss loss confirm.
+ if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING &&
+ priv->bss_loss_confirm_id == arg->packetID)) {
+ spin_lock(&priv->bss_loss_lock);
+ priv->bss_loss_status = arg->status?
+ XRADIO_BSS_LOSS_CONFIRMED : XRADIO_BSS_LOSS_NONE;
+ spin_unlock(&priv->bss_loss_lock);
+ }
+
+ if (likely(!arg->status)) {
+ tx->flags |= IEEE80211_TX_STAT_ACK;
+ priv->cqm_tx_failure_count = 0;
+ ++tx_count;
+ if (arg->txedRate<24)
+ TxedRateIdx_Map[arg->txedRate]++;
+ else
+ SYS_WARN(1);
+ xradio_debug_txed(priv);
+ if (arg->flags & WSM_TX_STATUS_AGGREGATION) {
+ /* Do not report aggregation to mac80211:
+ * it confuses minstrel a lot. */
+ /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */
+ xradio_debug_txed_agg(priv);
+ }
+ } else {
+ /* TODO: Update TX failure counters */
+ if (unlikely(priv->cqm_tx_failure_thold &&
+ (++priv->cqm_tx_failure_count >
+ priv->cqm_tx_failure_thold))) {
+ priv->cqm_tx_failure_thold = 0;
+ queue_work(hw_priv->workqueue,
+ &priv->tx_failure_work);
+ }
+ if (tx_count)
+ ++tx_count;
+ }
+ spin_unlock(&priv->vif_lock);
+
+ tx->status.ampdu_len = 1;
+ tx->status.ampdu_ack_len = 1;
+
+ txrx_printk(XRADIO_DBG_NIY,"feedback:%08x, %08x, %08x.\n",
+ arg->rate_try[2], arg->rate_try[1], arg->rate_try[0]);
+ if(txpriv->use_bg_rate) { //bg rates
+ tx->status.rates[0].count = arg->ackFailures+1;
+ tx->status.rates[0].idx = 0;
+ tx->status.rates[1].idx = -1;
+ tx->status.rates[2].idx = -1;
+ tx->status.rates[3].idx = -1;
+ } else {
+ int j;
+ s8 txed_idx;
+ register u8 rate_num=0, shift=0, retries=0;
+ u8 flag = tx->status.rates[0].flags;
+
+ //get retry rate idx.
+ for(i=2; i>=0;i--) {
+ if(arg->rate_try[i]) {
+ for(j=7; j>=0;j--) {
+ shift = j<<2;
+ retries = (arg->rate_try[i]>>shift)&0xf;
+ if(retries) {
+ feedback_retry += retries;
+ txed_idx = xradio_get_rate_idx(hw_priv,flag,((i<<3)+j));
+ txrx_printk(XRADIO_DBG_NIY, "rate_num=%d, hw=%d, idx=%d, "
+ "retries=%d, flag=%d", rate_num, ((i<<3)+j),
+ txed_idx, retries, flag);
+ if(likely(txed_idx>=0)) {
+ tx->status.rates[rate_num].idx = txed_idx;
+ tx->status.rates[rate_num].count = retries;
+ if (tx->status.rates[rate_num].flags & IEEE80211_TX_RC_MCS)
+ tx->status.rates[rate_num].flags |= ht_flags;
+ rate_num++;
+ if(rate_num>=IEEE80211_TX_MAX_RATES) {
+ i = -1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ //clear other rate.
+ for (i=rate_num; i < IEEE80211_TX_MAX_RATES; ++i) {
+ tx->status.rates[i].count = 0;
+ tx->status.rates[i].idx = -1;
+ }
+ //get successful rate idx.
+ if(!arg->status) {
+ txed_idx = xradio_get_rate_idx(hw_priv, flag, arg->txedRate);
+ if(rate_num == 0) {
+ tx->status.rates[0].idx = txed_idx;
+ tx->status.rates[0].count = 1;
+ } else if(rate_num <= IEEE80211_TX_MAX_RATES){
+ --rate_num;
+ if(txed_idx == tx->status.rates[rate_num].idx) {
+ tx->status.rates[rate_num].count += 1;
+ } else if(rate_num<(IEEE80211_TX_MAX_RATES-1)){
+ ++rate_num;
+ tx->status.rates[rate_num].idx = txed_idx;
+ tx->status.rates[rate_num].count = 1;
+ } else if(txed_idx >=0) {
+ tx->status.rates[rate_num].idx = txed_idx;
+ tx->status.rates[rate_num].count = 1;
+ }
+ }
+ }
+ }
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+ if (arg->status == WSM_STATUS_RETRY_EXCEEDED) {
+ tx_retrylimit++;
+ retry_mis += ((s32)hw_priv->short_frame_max_tx_count-arg->ackFailures-1);
+ if(arg->ackFailures != (hw_priv->short_frame_max_tx_count-1)) {
+ if(arg->ackFailures < (hw_priv->short_frame_max_tx_count-1))
+ tx_lower_limit++;
+ else
+ tx_over_limit++;
+ txrx_printk(XRADIO_DBG_WARN, "retry_err, ackFailures=%d, feedbk_retry=%d.\n",
+ arg->ackFailures, feedback_retry);
+ }
+ } else if (feedback_retry > hw_priv->short_frame_max_tx_count-1) {
+ txrx_printk(XRADIO_DBG_WARN, "status=%d, ackFailures=%d, feedbk_retry=%d.\n",
+ arg->status, arg->ackFailures, feedback_retry);
+ }
+#endif
+
+ dev_dbg(hw_priv->pdev, "[TX policy] Ack: " \
+ "%d:%d, %d:%d, %d:%d, %d:%d\n",
+ tx->status.rates[0].idx, tx->status.rates[0].count,
+ tx->status.rates[1].idx, tx->status.rates[1].count,
+ tx->status.rates[2].idx, tx->status.rates[2].count,
+ tx->status.rates[3].idx, tx->status.rates[3].count);
+
+
+ xradio_queue_remove(queue, arg->packetID);
+ }
+}
+
+static void xradio_notify_buffered_tx(struct xradio_vif *priv,
+ struct sk_buff *skb, int link_id, int tid)
+{
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ struct ieee80211_sta *sta;
+ struct ieee80211_hdr *hdr;
+ u8 *buffered;
+ u8 still_buffered = 0;
+
+
+ if (link_id && tid < XRADIO_MAX_TID) {
+ buffered = priv->link_id_db
+ [link_id - 1].buffered;
+
+ spin_lock_bh(&priv->ps_state_lock);
+ if (!SYS_WARN(!buffered[tid]))
+ still_buffered = --buffered[tid];
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (!still_buffered && tid < XRADIO_MAX_TID) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, hdr->addr1);
+ if (sta)
+ ieee80211_sta_set_buffered(sta, tid, false);
+ rcu_read_unlock();
+ }
+ }
+#endif /* CONFIG_XRADIO_USE_EXTENSIONS */
+}
+
+void xradio_skb_dtor(struct xradio_common *hw_priv,
+ struct sk_buff *skb,
+ const struct xradio_txpriv *txpriv)
+{
+ struct xradio_vif *priv =
+ __xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
+
+
+ skb_pull(skb, txpriv->offset);
+ if (priv && txpriv->rate_id != XRADIO_INVALID_RATE_ID) {
+ xradio_notify_buffered_tx(priv, skb,
+ txpriv->raw_link_id, txpriv->tid);
+ tx_policy_put(hw_priv, txpriv->rate_id);
+ }
+ ieee80211_tx_status(hw_priv->hw, skb);
+}
+#ifdef CONFIG_XRADIO_TESTMODE
+/* TODO It should be removed before official delivery */
+static void frame_hexdump(char *prefix, u8 *data, int len)
+{
+ int i;
+
+ txrx_printk(XRADIO_DBG_MSG, "%s hexdump:\n", prefix);
+ for (i = 0; i < len; i++) {
+ if (i + 10 < len) {
+ txrx_printk(XRADIO_DBG_MSG, "%.1X %.1X %.1X %.1X" \
+ "%.1X %.1X %.1X %.1X %.1X %.1X",
+ data[i], data[i+1], data[i+2],
+ data[i+3], data[i+4], data[i+5],
+ data[i+6], data[i+7], data[i+8],
+ data[i+9]);
+ i += 9;
+ } else {
+ txrx_printk(XRADIO_DBG_MSG, "%.1X ", data[i]);
+ }
+ }
+}
+/**
+ * c1200_tunnel_send_testmode_data - Send test frame to the driver
+ *
+ * @priv: pointer to xradio private structure
+ * @skb: skb with frame
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_tunnel_send_testmode_data(struct xradio_common *hw_priv,
+ struct sk_buff *skb)
+{
+
+ if (xradio_tesmode_event(hw_priv->hw->wiphy, AW_MSG_EVENT_FRAME_DATA,
+ skb->data, skb->len, GFP_ATOMIC))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * xradio_frame_test_detection - Detection frame_test
+ *
+ * @priv: pointer to xradio vif structure
+ * @frame: ieee80211 header
+ * @skb: skb with frame
+ *
+ * Returns: 1 - frame test detected, 0 - not detected
+ */
+static int xradio_frame_test_detection(struct xradio_vif *priv,
+ struct ieee80211_hdr *frame,
+ struct sk_buff *skb)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ int hdrlen = ieee80211_hdrlen(frame->frame_control);
+ int detected = 0;
+ int ret;
+
+
+ if (hdrlen + hw_priv->test_frame.len <= skb->len &&
+ memcmp(skb->data + hdrlen, hw_priv->test_frame.data,
+ hw_priv->test_frame.len) == 0) {
+ detected = 1;
+ txrx_printk(XRADIO_DBG_MSG, "TEST FRAME detected");
+ frame_hexdump("TEST FRAME original:", skb->data, skb->len);
+ ret = ieee80211_data_to_8023(skb, hw_priv->mac_addr,
+ priv->mode);
+ if (!ret) {
+ frame_hexdump("FRAME 802.3:", skb->data, skb->len);
+ ret = xradio_tunnel_send_testmode_data(hw_priv, skb);
+ }
+ if (ret)
+ txrx_printk(XRADIO_DBG_ERROR, "Send TESTFRAME failed(%d)", ret);
+ }
+ return detected;
+}
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+
+static void
+xradio_rx_h_ba_stat(struct xradio_vif *priv,
+ size_t hdrlen, size_t skb_len )
+{
+ struct xradio_common *hw_priv = priv->hw_priv;
+
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_STA)
+ return;
+ if (!xradio_is_ht(&hw_priv->ht_oper))
+ return;
+ if (!priv->setbssparams_done)
+ return;
+
+ spin_lock_bh(&hw_priv->ba_lock);
+ hw_priv->ba_acc_rx += skb_len - hdrlen;
+ if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) {
+ mod_timer(&hw_priv->ba_timer,
+ jiffies + XRADIO_BLOCK_ACK_INTERVAL);
+ }
+ hw_priv->ba_cnt_rx++;
+ spin_unlock_bh(&hw_priv->ba_lock);
+}
+
+#if 0
+u8 nettest_bssid[] = {0x00,0x02,0x03,0x04,0x05,0x06};
+u8 save_rate_ie;
+#endif
+
+void xradio_rx_cb(struct xradio_vif *priv,
+ struct wsm_rx *arg,
+ struct sk_buff **skb_p)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ struct sk_buff *skb = *skb_p;
+ struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb);
+ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data;
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data;
+#endif
+ struct xradio_link_entry *entry = NULL;
+ unsigned long grace_period;
+ bool early_data = false;
+ size_t hdrlen = 0;
+ u8 parse_iv_len = 0;
+
+ dev_dbg(hw_priv->pdev, "vif %d: rx, status %u flags 0x%.8x",
+ priv->if_id, arg->status, arg->flags);
+ if(ieee80211_is_deauth(frame->frame_control))
+ dev_dbg(hw_priv->pdev, "vif %d: deauth\n", priv->if_id);
+
+ hdr->flag = 0;
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+ /* STA is stopped. */
+ goto drop;
+ }
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ xradio_frame_monitor(hw_priv,skb,false);
+#endif
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ if ((ieee80211_is_action(frame->frame_control))
+ && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ u8 *action = (u8*)&mgmt->u.action.category;
+ xradio_check_go_neg_conf_success(hw_priv, action);
+ }
+#endif
+
+ if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED)
+ && (arg->link_id <= XRADIO_MAX_STA_IN_AP_MODE)) {
+ entry = &priv->link_id_db[arg->link_id - 1];
+ if (entry->status == XRADIO_LINK_SOFT &&
+ ieee80211_is_data(frame->frame_control))
+ early_data = true;
+ entry->timestamp = jiffies;
+ }
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ else if ((arg->link_id == XRADIO_LINK_ID_UNMAPPED)
+ && (priv->vif->p2p == WSM_START_MODE_P2P_GO)
+ && ieee80211_is_action(frame->frame_control)
+ && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ txrx_printk(XRADIO_DBG_NIY, "[RX] Going to MAP&RESET link ID\n");
+
+ if (work_pending(&priv->linkid_reset_work))
+ SYS_WARN(1);
+
+ memcpy(&priv->action_frame_sa[0],
+ ieee80211_get_SA(frame), ETH_ALEN);
+ priv->action_linkid = 0;
+ schedule_work(&priv->linkid_reset_work);
+ }
+
+ if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED)
+ && (priv->vif->p2p == WSM_START_MODE_P2P_GO)
+ && ieee80211_is_action(frame->frame_control)
+ && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) {
+ /* Link ID already exists for the ACTION frame.
+ * Reset and Remap */
+ if (work_pending(&priv->linkid_reset_work))
+ SYS_WARN(1);
+ memcpy(&priv->action_frame_sa[0],
+ ieee80211_get_SA(frame), ETH_ALEN);
+ priv->action_linkid = arg->link_id;
+ schedule_work(&priv->linkid_reset_work);
+ }
+#endif
+ if (unlikely(arg->status)) {
+ if (arg->status == WSM_STATUS_MICFAILURE) {
+ dev_err(priv->hw_priv->pdev, "[RX] IF=%d, MIC failure.\n",
+ priv->if_id);
+ hdr->flag |= RX_FLAG_MMIC_ERROR;
+ } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+ dev_warn(priv->hw_priv->pdev, "received frame has no key status\n");
+ //goto drop;
+ } else {
+ dev_err(priv->hw_priv->pdev, "[RX] IF=%d, Receive failure: %d.\n",
+ priv->if_id, arg->status);
+ goto drop;
+ }
+ }
+
+ if (skb->len < sizeof(struct ieee80211_pspoll)) {
+ dev_err(priv->hw_priv->pdev, "Malformed SDU rx'ed. "
+ "Size is lesser than IEEE header.\n");
+ goto drop;
+ }
+
+ if (unlikely(ieee80211_is_pspoll(frame->frame_control)))
+ if (xradio_handle_pspoll(priv, skb))
+ goto drop;
+
+ hdr->mactime = 0; /* Not supported by WSM */
+ hdr->band = (arg->channelNumber > 14) ?
+ NL80211_BAND_5GHZ : NL80211_BAND_2GHZ;
+ hdr->freq = ieee80211_channel_to_frequency(
+ arg->channelNumber,
+ hdr->band);
+
+#ifdef AP_HT_COMPAT_FIX
+ if (!priv->ht_compat_det && priv->htcap &&
+ ieee80211_is_data_qos(frame->frame_control)) {
+ if(xradio_apcompat_detect(priv, arg->rxedRate))
+ goto drop;
+ }
+#endif
+
+ if (arg->rxedRate<24)
+ RxedRateIdx_Map[arg->rxedRate]++;
+ else
+ SYS_WARN(1);
+
+ if (arg->rxedRate >= 14) {
+ hdr->flag |= RX_FLAG_HT;
+ hdr->rate_idx = arg->rxedRate - 14;
+ } else if (arg->rxedRate >= 4) {
+ if (hdr->band == NL80211_BAND_5GHZ)
+ hdr->rate_idx = arg->rxedRate - 6;
+ else
+ hdr->rate_idx = arg->rxedRate - 2;
+ } else {
+ hdr->rate_idx = arg->rxedRate;
+ }
+
+ hdr->signal = (s8)arg->rcpiRssi;
+ hdr->antenna = 0;
+
+ hdrlen = ieee80211_hdrlen(frame->frame_control);
+
+ if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+ size_t iv_len = 0, icv_len = 0;
+
+ hdr->flag |= RX_FLAG_DECRYPTED;
+
+ /* Oops... There is no fast way to ask mac80211 about
+ * IV/ICV lengths. Even defines are not exposed.*/
+ switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) {
+ case WSM_RX_STATUS_WEP:
+ iv_len = 4 /* WEP_IV_LEN */;
+ icv_len = 4 /* WEP_ICV_LEN */;
+ break;
+ case WSM_RX_STATUS_TKIP:
+ iv_len = 8 /* TKIP_IV_LEN */;
+ icv_len = 4 /* TKIP_ICV_LEN */
+ + 8 /*MICHAEL_MIC_LEN*/;
+ break;
+ case WSM_RX_STATUS_AES:
+ iv_len = 8 /* CCMP_HDR_LEN */;
+ icv_len = 8 /* CCMP_MIC_LEN */;
+ break;
+ case WSM_RX_STATUS_WAPI:
+ iv_len = 18 /* WAPI_HDR_LEN */;
+ icv_len = 16 /* WAPI_MIC_LEN */;
+ hdr->flag |= RX_FLAG_IV_STRIPPED;
+ break;
+ default:
+ SYS_WARN("Unknown encryption type");
+ goto drop;
+ }
+
+ /* Firmware strips ICV in case of MIC failure. */
+ if (arg->status == WSM_STATUS_MICFAILURE) {
+ icv_len = 0;
+ hdr->flag |= RX_FLAG_IV_STRIPPED;
+ }
+
+ if (skb->len < hdrlen + iv_len + icv_len) {
+ dev_err(priv->hw_priv->pdev, "Mailformed SDU rx'ed. "
+ "Size is lesser than crypto headers.\n");
+ goto drop;
+ }
+
+ if (WSM_RX_STATUS_ENCRYPTION(arg->flags) ==
+ WSM_RX_STATUS_TKIP) {
+ /* Remove TKIP MIC 8 bytes*/
+ memmove(skb->data + skb->len-icv_len,
+ skb->data + skb->len-icv_len+8, 4);
+ skb_trim(skb, skb->len - 8);
+ hdr->flag |= RX_FLAG_MMIC_STRIPPED;
+ } else if (unlikely(WSM_RX_STATUS_ENCRYPTION(arg->flags) ==
+ WSM_RX_STATUS_WAPI)) {
+ /* Protocols not defined in mac80211 should be
+ stripped/crypted in driver/firmware */
+ /* Remove IV, ICV and MIC */
+ skb_trim(skb, skb->len - icv_len);
+ memmove(skb->data + iv_len, skb->data, hdrlen);
+ skb_pull(skb, iv_len);
+ }
+ parse_iv_len = iv_len;
+ }
+
+ xradio_debug_rxed(priv);
+ if (arg->flags & WSM_RX_STATUS_AGGREGATE)
+ xradio_debug_rxed_agg(priv);
+
+ if (ieee80211_is_beacon(frame->frame_control) &&
+ !arg->status &&
+ !memcmp(ieee80211_get_SA(frame), priv->join_bssid,ETH_ALEN)) {
+ const u8 *tim_ie;
+ u8 *ies;
+ size_t ies_len;
+ priv->disable_beacon_filter = false;
+ queue_work(hw_priv->workqueue, &priv->update_filtering_work);
+ ies = ((struct ieee80211_mgmt *)
+ (skb->data))->u.beacon.variable;
+ ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+ tim_ie = xradio_get_ie(ies, ies_len, WLAN_EID_TIM);
+ if (tim_ie) {
+ struct ieee80211_tim_ie *tim =
+ (struct ieee80211_tim_ie *)&tim_ie[2];
+
+ if (priv->join_dtim_period != tim->dtim_period) {
+ priv->join_dtim_period = tim->dtim_period;
+ queue_work(hw_priv->workqueue,
+ &priv->set_beacon_wakeup_period_work);
+ }
+ }
+ if (unlikely(priv->disable_beacon_filter)) {
+ priv->disable_beacon_filter = false;
+ queue_work(hw_priv->workqueue,
+ &priv->update_filtering_work);
+ }
+ }
+#ifdef AP_HT_CAP_UPDATE
+ if (priv->mode == NL80211_IFTYPE_AP &&
+ ieee80211_is_beacon(frame->frame_control) &&
+ ((priv->ht_oper&HT_INFO_MASK) != 0x0011) &&
+ !arg->status){
+ u8 *ies;
+ size_t ies_len;
+ const u8 *ht_cap;
+ ies = ((struct ieee80211_mgmt *)(skb->data))->u.beacon.variable;
+ ies_len = skb->len - (ies - (u8 *)(skb->data));
+ ht_cap = xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY);
+ if(!ht_cap) {
+ priv->ht_oper |= 0x0011;
+ queue_work(hw_priv->workqueue, &priv->ht_oper_update_work);
+ }
+ }
+#endif
+
+#ifdef AP_HT_COMPAT_FIX
+ if (ieee80211_is_mgmt(frame->frame_control) &&
+ priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) {
+ xradio_remove_ht_ie(priv, skb);
+ }
+#endif
+
+#ifdef ROAM_OFFLOAD
+ if ((ieee80211_is_beacon(frame->frame_control)||ieee80211_is_probe_resp(frame->frame_control)) &&
+ !arg->status ) {
+ if (hw_priv->auto_scanning && !atomic_read(&hw_priv->scan.in_progress))
+ hw_priv->frame_rcvd = 1;
+
+ if (!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) {
+ if (hw_priv->beacon)
+ dev_kfree_skb(hw_priv->beacon);
+ hw_priv->beacon = skb_copy(skb, GFP_ATOMIC);
+ if (!hw_priv->beacon)
+ txrx_printk(XRADIO_DBG_ERROR, "sched_scan: own beacon storing failed\n");
+ }
+ }
+#endif /*ROAM_OFFLOAD*/
+
+ //don't delay scan before next connect, yangfh.
+ if (ieee80211_is_deauth(frame->frame_control) ||
+ ieee80211_is_disassoc(frame->frame_control))
+ hw_priv->connet_time[priv->if_id] = 0;
+
+ /* Stay awake for 1sec. after frame is received to give
+ * userspace chance to react and acquire appropriate
+ * wakelock. */
+ if (ieee80211_is_auth(frame->frame_control))
+ grace_period = 5 * HZ;
+ else if (ieee80211_is_deauth(frame->frame_control))
+ grace_period = 5 * HZ;
+ else
+ grace_period = HZ;
+
+ if (ieee80211_is_data(frame->frame_control))
+ xradio_rx_h_ba_stat(priv, hdrlen, skb->len);
+
+ xradio_pm_stay_awake(&hw_priv->pm_state, grace_period);
+
+ if(xradio_realloc_resv_skb(hw_priv, *skb_p)) {
+ *skb_p = NULL;
+ return;
+ }
+ /* Try to a packet for the case dev_alloc_skb failed in bh.*/
+ if (unlikely(early_data)) {
+ spin_lock_bh(&priv->ps_state_lock);
+ /* Double-check status with lock held */
+ if (entry->status == XRADIO_LINK_SOFT) {
+ skb_queue_tail(&entry->rx_queue, skb);
+ dev_warn(priv->hw_priv->pdev, "***skb_queue_tail\n");
+ } else
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ spin_unlock_bh(&priv->ps_state_lock);
+ } else {
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+ *skb_p = NULL;
+
+ return;
+
+drop:
+ dev_warn(priv->hw_priv->pdev, "dropped received frame\n");
+ return;
+}
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+/* Workaround for WFD test case 6.1.10 */
+void xradio_link_id_reset(struct work_struct *work)
+{
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, linkid_reset_work);
+ struct xradio_common *hw_priv = priv->hw_priv;
+ int temp_linkid;
+
+
+ if (!priv->action_linkid) {
+ /* In GO mode we can receive ACTION frames without a linkID */
+ temp_linkid = xradio_alloc_link_id(priv,
+ &priv->action_frame_sa[0]);
+ SYS_WARN(!temp_linkid);
+ if (temp_linkid) {
+ /* Make sure we execute the WQ */
+ flush_workqueue(hw_priv->workqueue);
+ /* Release the link ID */
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->link_id_db[temp_linkid - 1].prev_status =
+ priv->link_id_db[temp_linkid - 1].status;
+ priv->link_id_db[temp_linkid - 1].status =
+ XRADIO_LINK_RESET;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue,
+ &priv->link_id_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ }
+ } else {
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->link_id_db[priv->action_linkid - 1].prev_status =
+ priv->link_id_db[priv->action_linkid - 1].status;
+ priv->link_id_db[priv->action_linkid - 1].status =
+ XRADIO_LINK_RESET_REMAP;
+ spin_unlock_bh(&priv->ps_state_lock);
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ flush_workqueue(hw_priv->workqueue);
+ }
+}
+#endif
diff --git a/drivers/net/wireless/xradio/txrx.h b/drivers/net/wireless/xradio/txrx.h
new file mode 100644
index 00000000..f21bfb86
--- /dev/null
+++ b/drivers/net/wireless/xradio/txrx.h
@@ -0,0 +1,89 @@
+/*
+ * txrx interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef XRADIO_TXRX_H
+#define XRADIO_TXRX_H
+
+#include <linux/list.h>
+
+/* extern */ struct ieee80211_hw;
+/* extern */ struct sk_buff;
+/* extern */ struct wsm_tx;
+/* extern */ struct wsm_rx;
+/* extern */ struct wsm_tx_confirm;
+/* extern */ struct xradio_txpriv;
+/* extern */ struct xradio_vif;
+
+struct tx_policy {
+ union {
+ __le32 tbl[3];
+ u8 raw[12];
+ };
+ u8 defined; /* TODO: u32 or u8, profile and select best */
+ u8 usage_count; /* --// -- */
+ u8 retry_count; /* --// -- */
+ u8 uploaded;
+};
+
+struct tx_policy_cache_entry {
+ struct tx_policy policy;
+ struct list_head link;
+};
+
+#define TX_POLICY_CACHE_SIZE (8)
+struct tx_policy_cache {
+ struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE];
+ struct list_head used;
+ struct list_head free;
+ spinlock_t lock;
+};
+
+/* ******************************************************************** */
+/* TX policy cache */
+/* Intention of TX policy cache is an overcomplicated WSM API.
+ * Device does not accept per-PDU tx retry sequence.
+ * It uses "tx retry policy id" instead, so driver code has to sync
+ * linux tx retry sequences with a retry policy table in the device.
+ */
+void tx_policy_init(struct xradio_common *hw_priv);
+void tx_policy_upload_work(struct work_struct *work);
+
+/* ******************************************************************** */
+/* TX implementation */
+
+u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv,
+ u32 rates);
+void xradio_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb);
+void xradio_skb_dtor(struct xradio_common *hw_priv,
+ struct sk_buff *skb,
+ const struct xradio_txpriv *txpriv);
+
+/* ******************************************************************** */
+/* WSM callbacks */
+
+void xradio_tx_confirm_cb(struct xradio_common *hw_priv,
+ struct wsm_tx_confirm *arg);
+void xradio_rx_cb(struct xradio_vif *priv,
+ struct wsm_rx *arg,
+ struct sk_buff **skb_p);
+
+/* ******************************************************************** */
+/* Timeout */
+
+void xradio_tx_timeout(struct work_struct *work);
+
+/* ******************************************************************** */
+/* Workaround for WFD test case 6.1.10 */
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+void xradio_link_id_reset(struct work_struct *work);
+#endif
+
+#endif /* XRADIO_TXRX_H */
diff --git a/drivers/net/wireless/xradio/wsm.c b/drivers/net/wireless/xradio/wsm.c
new file mode 100644
index 00000000..7b67349b
--- /dev/null
+++ b/drivers/net/wireless/xradio/wsm.c
@@ -0,0 +1,3252 @@
+/*
+ * WSM host interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/random.h>
+
+#include "xradio.h"
+#include "wsm.h"
+#include "bh.h"
+#ifdef ROAM_OFFLOAD
+#include "sta.h"
+#endif /*ROAM_OFFLOAD*/
+
+
+#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */
+#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */
+#define WSM_CMD_START_TIMEOUT (7 * HZ)
+#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */
+#define WSM_CMD_DEFAULT_TIMEOUT (3 * HZ)
+#define WSM_SKIP(buf, size) \
+ do { \
+ if (unlikely((buf)->data + size > (buf)->end)) \
+ goto underflow; \
+ (buf)->data += size; \
+ } while (0)
+
+#define WSM_GET(buf, ptr, size) \
+ do { \
+ if (unlikely((buf)->data + size > (buf)->end)) \
+ goto underflow; \
+ memcpy(ptr, (buf)->data, size); \
+ (buf)->data += size; \
+ } while (0)
+
+#define __WSM_GET(buf, type, cvt) \
+ ({ \
+ type val; \
+ if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \
+ goto underflow; \
+ val = cvt(*(type *)(buf)->data); \
+ (buf)->data += sizeof(type); \
+ val; \
+ })
+
+#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8))
+#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu)
+#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu)
+
+#define WSM_PUT(buf, ptr, size) \
+ do { \
+ if (unlikely((buf)->data + size > (buf)->end)) \
+ if (unlikely(wsm_buf_reserve((buf), size))) \
+ goto nomem; \
+ memcpy((buf)->data, ptr, size); \
+ (buf)->data += size; \
+ } while (0)
+
+#define __WSM_PUT(buf, val, type, cvt) \
+ do { \
+ if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \
+ if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \
+ goto nomem; \
+ *(type *)(buf)->data = cvt(val); \
+ (buf)->data += sizeof(type); \
+ } while (0)
+
+#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8))
+#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16)
+#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32)
+
+static void wsm_buf_reset(struct wsm_buf *buf);
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size);
+static int get_interface_id_scanning(struct xradio_common *hw_priv);
+
+static int wsm_cmd_send(struct xradio_common *hw_priv,
+ struct wsm_buf *buf,
+ void *arg, u16 cmd, long tmo, int if_id);
+
+static struct xradio_vif
+ *wsm_get_interface_for_tx(struct xradio_common *hw_priv);
+
+static inline void wsm_cmd_lock(struct xradio_common *hw_priv)
+{
+ mutex_lock(&hw_priv->wsm_cmd_mux);
+}
+
+static inline void wsm_cmd_unlock(struct xradio_common *hw_priv)
+{
+ mutex_unlock(&hw_priv->wsm_cmd_mux);
+}
+
+static inline void wsm_oper_lock(struct xradio_common *hw_priv)
+{
+ mutex_lock(&hw_priv->wsm_oper_lock);
+}
+
+static inline void wsm_oper_unlock(struct xradio_common *hw_priv)
+{
+ mutex_unlock(&hw_priv->wsm_oper_lock);
+}
+
+/* ******************************************************************** */
+/* WSM API implementation */
+
+static int wsm_generic_confirm(struct xradio_common *hw_priv,
+ void *arg,
+ struct wsm_buf *buf)
+{
+ u32 status = WSM_GET32(buf);
+ if (status != WSM_STATUS_SUCCESS)
+ return -EINVAL;
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+#if defined(DGB_XRADIO_HWT)
+int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg, size_t arg_size)
+{
+ int ret = 0;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+ WSM_PUT(buf, arg, arg_size);
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0024, WSM_CMD_TIMEOUT, -1);
+ wsm_cmd_unlock(hw_priv);
+
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+#endif
+
+#ifdef XR_RRM//RadioResourceMeasurement
+static int wsm_start_measure_requset(struct xradio_common *hw_priv,
+ MEASUREMENT_PARAMETERS *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT(buf, arg, sizeof(*arg));
+ ret = wsm_cmd_send(hw_priv, buf, arg, 0x000E, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+ nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+
+}
+
+int wsm_11k_measure_requset(struct xradio_common *hw_priv,
+ u8 measure_type,
+ u16 ChannelNum,
+ u16 Duration)
+{
+ int ret;
+ u8 type, sub_type;
+ MEASUREMENT_PARAMETERS rrm_paras;
+ LMAC_MEAS_REQUEST *rrm_req = &rrm_paras.MeasurementRequest;
+// LMAC_MEAS_CHANNEL_LOAD_PARAMS *rrm_req = &rrm_paras.MeasurementRequest;
+ rrm_paras.TxPowerLevel = 0x11;
+ rrm_paras.DurationMandatory = 0x22;
+ rrm_paras.MeasurementRequestLength = 0x33;
+
+ type = (measure_type&0xf0)>>4;
+ sub_type = measure_type&0xf;
+ rrm_paras.MeasurementType = type;
+// if (measure_type == ChannelLoadMeasurement) {
+ if (type == ChannelLoadMeasurement) {
+ rrm_req->ChannelLoadParams.Reserved = 0;
+ rrm_req->ChannelLoadParams.ChannelLoadCCA = sub_type;
+ rrm_req->ChannelLoadParams.ChannelNum = ChannelNum;
+ //valid when channelload measure, interval bettween request&start
+ rrm_req->ChannelLoadParams.RandomInterval = 0;
+ //unit:1TU=1024us
+ rrm_req->ChannelLoadParams.MeasurementDuration = Duration;
+ rrm_req->ChannelLoadParams.MeasurementStartTimel = 0;
+ rrm_req->ChannelLoadParams.MeasurementStartTimeh = 0;
+ } else if (type == NoiseHistrogramMeasurement) {
+ rrm_req->NoisHistogramParams.Reserved = 0;
+ rrm_req->NoisHistogramParams.IpiRpi = sub_type;
+ rrm_req->NoisHistogramParams.ChannelNum = ChannelNum;
+ rrm_req->NoisHistogramParams.RandomInterval = 0;
+ rrm_req->NoisHistogramParams.MeasurementDuration = Duration;
+ rrm_req->NoisHistogramParams.MeasurementStartTimel = 0;
+ rrm_req->NoisHistogramParams.MeasurementStartTimeh = 0;
+ }
+ ret = wsm_start_measure_requset(hw_priv, &rrm_paras, 0);
+
+ return ret;
+}
+
+
+#endif//RadioResourceMeasurement
+int wsm_configuration(struct xradio_common *hw_priv,
+ struct wsm_configuration *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime);
+ WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime);
+ WSM_PUT32(buf, arg->dot11RtsThreshold);
+
+ /* DPD block. */
+ WSM_PUT16(buf, arg->dpdData_size + 12);
+ WSM_PUT16(buf, 1); /* DPD version */
+ WSM_PUT(buf, arg->dot11StationId, ETH_ALEN);
+ WSM_PUT16(buf, 5); /* DPD flags */
+ WSM_PUT(buf, arg->dpdData, arg->dpdData_size);
+
+ ret = wsm_cmd_send(hw_priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+static int wsm_configuration_confirm(struct xradio_common *hw_priv,
+ struct wsm_configuration *arg,
+ struct wsm_buf *buf)
+{
+ int i;
+ int status;
+
+ status = WSM_GET32(buf);
+ if (SYS_WARN(status != WSM_STATUS_SUCCESS))
+ return -EINVAL;
+
+ WSM_GET(buf, arg->dot11StationId, ETH_ALEN);
+ arg->dot11FrequencyBandsSupported = WSM_GET8(buf);
+ WSM_SKIP(buf, 1);
+ arg->supportedRateMask = WSM_GET32(buf);
+ for (i = 0; i < 2; ++i) {
+ arg->txPowerRange[i].min_power_level = WSM_GET32(buf);
+ arg->txPowerRange[i].max_power_level = WSM_GET32(buf);
+ arg->txPowerRange[i].stepping = WSM_GET32(buf);
+ }
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+/*forcing upper layer to restart wifi.*/
+void wsm_upper_restart(struct xradio_common *hw_priv)
+{
+ int i = 0;
+ struct xradio_vif *priv = NULL;
+
+#ifdef CONFIG_PM
+ xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
+#endif
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS) && 0
+ spin_lock(&hw_priv->vif_list_lock);
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (!priv)
+ continue;
+ ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
+ wsm_printk(XRADIO_DBG_WARN, "%s driver_hang_notify\n", __func__);
+ }
+ spin_unlock(&hw_priv->vif_list_lock);
+#elif defined(HW_RESTART)
+ if (!work_pending(&hw_priv->hw_restart_work)) {
+ bool reset = false;
+ spin_lock(&hw_priv->vif_list_lock);
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (priv) {
+ reset = true;
+ break;
+ }
+ }
+ spin_unlock(&hw_priv->vif_list_lock);
+ if (reset) {
+ wsm_cmd_lock(hw_priv);
+ hw_priv->hw_restart = true;
+ wsm_cmd_unlock(hw_priv);
+ /* wait for scan complete.*/
+ wsm_printk(XRADIO_DBG_WARN, "Wait for scan complete!\n");
+ down(&hw_priv->scan.lock);
+ mutex_lock(&hw_priv->conf_mutex);
+ /* Unlock wsm_oper_lock since no confirms of wsm_oper_locks.*/
+ if (!mutex_trylock(&hw_priv->wsm_oper_lock))
+ wsm_printk(XRADIO_DBG_WARN, "oper_lock may be locked!\n");
+ mutex_unlock(&hw_priv->wsm_oper_lock);
+ mutex_unlock(&hw_priv->conf_mutex);
+ up(&hw_priv->scan.lock);
+ msleep(200);
+ schedule_work(&hw_priv->hw_restart_work);
+ wsm_printk(XRADIO_DBG_WARN, "%s schedule restart_work!\n", __func__);
+ }
+ }
+#endif
+}
+
+void wsm_query_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, query_work);
+ u8 ret[100] = {0};
+
+
+ *(u32*)&ret[0] = hw_priv->query_packetID;
+ wsm_read_mib(hw_priv, WSM_MIB_ID_REQ_PKT_STATUS, (void*)&ret[0], sizeof(ret), 4);
+ if(!ret[4]) {
+ wsm_printk(XRADIO_DBG_ERROR,"QuerypktID=0x%08x, status=0x%x, retry=%d, flags=0x%x, PktDebug=0x%x\n" \
+ "pktqueue=0x%x, ext1=%d, ext2=%d, ext3=%d, ext4=0x%x, ext5=0x%x\n",
+ *(u32*)&ret[0], ret[6], ret[7], *(u32*)&ret[8], *(u32*)&ret[12],
+ ret[44], ret[45], ret[46], ret[47], ret[48], ret[49]);
+ wsm_printk(XRADIO_DBG_ERROR,"interdebug=0x%x, 0x%x, 0x%x, Soure=0x%x, 0x%x, 0x%x\n" \
+ "interuse=%d, external=%d, TxOutstanding=%d, QueueStatus=0x%x, BA0=0x%x, BA1=0x%x\n" \
+ "ScanStatus=0x%x, scanNULL=0x%x, wr_state=0x%x,0x%x,0x%x,0x%x," \
+ "wr_cnt=%d, %d, %d, %d\n",
+ *(u32*)&ret[16], *(u32*)&ret[20], *(u32*)&ret[24], ret[28], ret[29], ret[30],
+ ret[32], ret[33], ret[34], ret[35], *(u32*)&ret[36], *(u32*)&ret[40],
+ ret[50], ret[51], ret[52], ret[53], ret[54], ret[55],
+ *(u16*)&ret[56], *(u16*)&ret[58], *(u16*)&ret[60], *(u16*)&ret[62]);
+ } else {
+ ret[5] = 0;
+ wsm_printk(XRADIO_DBG_ERROR,"No req packid=0x%08x!\n", *(u32*)&ret[0]);
+ }
+ //hardware error occurs, try to restart wifi.
+ if(ret[5] & 0x4) {
+ wsm_printk(XRADIO_DBG_ERROR,"Hardware need to reset 0x%x.\n", ret[5]);
+ hw_priv->bh_error = 1;
+#ifdef BH_USE_SEMAPHORE
+ up(&hw_priv->bh_sem);
+#else
+ wake_up(&hw_priv->bh_wq);
+#endif
+ }
+ hw_priv->query_packetID = 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+ u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id);
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT32(buf, arg->reset_statistics ? 0 : 1);
+ ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT,
+ if_id);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+struct wsm_mib {
+ u16 mibId;
+ void *buf;
+ size_t buf_size;
+};
+
+int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf,
+ size_t buf_size, size_t arg_size)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+ struct wsm_mib mib_buf = {
+ .mibId = mibId,
+ .buf = _buf,
+ .buf_size = buf_size,
+ };
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT16(buf, mibId);
+ WSM_PUT16(buf, arg_size);
+ WSM_PUT(buf, _buf, arg_size);
+
+ ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT, -1);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+static int wsm_read_mib_confirm(struct xradio_common *hw_priv,
+ struct wsm_mib *arg,
+ struct wsm_buf *buf)
+{
+ u16 size;
+ if (SYS_WARN(WSM_GET32(buf) != WSM_STATUS_SUCCESS))
+ return -EINVAL;
+
+ if (SYS_WARN(WSM_GET16(buf) != arg->mibId))
+ return -EINVAL;
+
+ size = WSM_GET16(buf);
+ if (size > arg->buf_size)
+ size = arg->buf_size;
+
+ WSM_GET(buf, arg->buf, size);
+ arg->buf_size = size;
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf,
+ size_t buf_size, int if_id)
+{
+ int ret = 0;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+ struct wsm_mib mib_buf = {
+ .mibId = mibId,
+ .buf = _buf,
+ .buf_size = buf_size,
+ };
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT16(buf, mibId);
+ WSM_PUT16(buf, buf_size);
+ WSM_PUT(buf, _buf, buf_size);
+
+ ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT,
+ if_id);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+static int wsm_write_mib_confirm(struct xradio_common *hw_priv,
+ struct wsm_mib *arg,
+ struct wsm_buf *buf,
+ int interface_link_id)
+{
+ int ret;
+ int i;
+ struct xradio_vif *priv;
+ ret = wsm_generic_confirm(hw_priv, arg, buf);
+ if (ret)
+ return ret;
+
+ /*wsm_set_operational_mode confirm.*/
+ if (arg->mibId == 0x1006) {
+ const char *p = arg->buf;
+ bool powersave_enabled = (p[0] & 0x0F) ? true : false;
+
+ /* update vif PM status. */
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+ if (priv) {
+ xradio_enable_powersave(priv, powersave_enabled);
+ spin_unlock(&priv->vif_lock);
+ }
+
+ /* HW powersave base on vif except for generic vif. */
+ spin_lock(&hw_priv->vif_list_lock);
+ xradio_for_each_vif(hw_priv, priv, i) {
+#ifdef P2P_MULTIVIF
+ if ((i == (XRWL_MAX_VIFS - 1)) || !priv)
+#else
+ if (!priv)
+#endif
+ continue;
+ powersave_enabled &= !!priv->powersave_enabled;
+ }
+ hw_priv->powersave_enabled = powersave_enabled;
+ spin_unlock(&hw_priv->vif_list_lock);
+
+ }
+ return 0;
+}
+
+/* ******************************************************************** */
+
+int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg,
+ int if_id)
+{
+ int i;
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ if (unlikely(arg->numOfChannels > 48))
+ return -EINVAL;
+
+ if (unlikely(arg->numOfSSIDs > WSM_SCAN_MAX_NUM_OF_SSIDS))
+ return -EINVAL;
+
+ if (unlikely(arg->band > 1))
+ return -EINVAL;
+
+ wsm_oper_lock(hw_priv);
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT8(buf, arg->scanType);
+ WSM_PUT8(buf, arg->scanFlags);
+ WSM_PUT8(buf, arg->maxTransmitRate);
+ WSM_PUT32(buf, arg->autoScanInterval);
+ WSM_PUT8(buf, arg->numOfProbeRequests);
+ WSM_PUT8(buf, arg->numOfChannels);
+ WSM_PUT8(buf, arg->numOfSSIDs);
+ WSM_PUT8(buf, arg->probeDelay);
+
+ for (i = 0; i < arg->numOfChannels; ++i) {
+ WSM_PUT16(buf, arg->ch[i].number);
+ WSM_PUT16(buf, 0);
+ WSM_PUT32(buf, arg->ch[i].minChannelTime);
+ WSM_PUT32(buf, arg->ch[i].maxChannelTime);
+ WSM_PUT32(buf, 0);
+ }
+
+ for (i = 0; i < arg->numOfSSIDs; ++i) {
+ WSM_PUT32(buf, arg->ssids[i].length);
+ WSM_PUT(buf, &arg->ssids[i].ssid[0],
+ sizeof(arg->ssids[i].ssid));
+ }
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT,
+ if_id);
+ wsm_cmd_unlock(hw_priv);
+ if (ret)
+ wsm_oper_unlock(hw_priv);
+
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ wsm_oper_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_scan(struct xradio_common *hw_priv, int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+ wsm_cmd_lock(hw_priv);
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT,
+ if_id);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+}
+
+
+static int wsm_tx_confirm(struct xradio_common *hw_priv,
+ struct wsm_buf *buf,
+ int interface_link_id)
+{
+ struct wsm_tx_confirm tx_confirm;
+
+ tx_confirm.packetID = WSM_GET32(buf);
+ tx_confirm.status = WSM_GET32(buf);
+ tx_confirm.txedRate = WSM_GET8(buf);
+ tx_confirm.ackFailures = WSM_GET8(buf);
+ tx_confirm.flags = WSM_GET16(buf);
+ tx_confirm.rate_try[0] = WSM_GET32(buf);
+ tx_confirm.rate_try[1] = WSM_GET32(buf);
+ tx_confirm.rate_try[2] = WSM_GET32(buf);
+ tx_confirm.mediaDelay = WSM_GET32(buf);
+ tx_confirm.txQueueDelay = WSM_GET32(buf);
+
+ if (is_hardware_xradio(hw_priv)) {
+ /* TODO:COMBO:linkID will be stored in packetID*/
+ /* TODO:COMBO: Extract traffic resumption map */
+ tx_confirm.if_id = xradio_queue_get_if_id(tx_confirm.packetID);
+ tx_confirm.link_id = xradio_queue_get_link_id(
+ tx_confirm.packetID);
+ } else {
+ tx_confirm.link_id = interface_link_id;
+ tx_confirm.if_id = 0;
+ }
+
+ wsm_release_vif_tx_buffer(hw_priv, tx_confirm.if_id, 1);
+
+ if (hw_priv->wsm_cbc.tx_confirm)
+ hw_priv->wsm_cbc.tx_confirm(hw_priv, &tx_confirm);
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+static int wsm_multi_tx_confirm(struct xradio_common *hw_priv,
+ struct wsm_buf *buf, int interface_link_id)
+{
+ struct xradio_vif *priv;
+ int ret;
+ int count;
+ int i;
+
+ count = WSM_GET32(buf);
+ if (SYS_WARN(count <= 0))
+ return -EINVAL;
+ else if (count > 1) {
+ ret = wsm_release_tx_buffer(hw_priv, count - 1);
+ if (ret < 0)
+ return ret;
+ else if (ret > 0)
+ xradio_bh_wakeup(hw_priv);
+ }
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+ if (priv) {
+ xradio_debug_txed_multi(priv, count);
+ spin_unlock(&priv->vif_lock);
+ }
+ for (i = 0; i < count; ++i) {
+ ret = wsm_tx_confirm(hw_priv, buf, interface_link_id);
+ if (ret)
+ return ret;
+ }
+ return ret;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+/* ******************************************************************** */
+
+static int wsm_join_confirm(struct xradio_common *hw_priv,
+ struct wsm_join *arg,
+ struct wsm_buf *buf)
+{
+ if (WSM_GET32(buf) != WSM_STATUS_SUCCESS)
+ return -EINVAL;
+ arg->minPowerLevel = WSM_GET32(buf);
+ arg->maxPowerLevel = WSM_GET32(buf);
+
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg,
+ int if_id)
+/*TODO: combo: make it work per vif.*/
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_oper_lock(hw_priv);
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT16(buf, arg->channelNumber);
+ WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid));
+ WSM_PUT16(buf, arg->atimWindow);
+ WSM_PUT8(buf, arg->preambleType);
+ WSM_PUT8(buf, arg->probeForJoin);
+ WSM_PUT8(buf, arg->dtimPeriod);
+ WSM_PUT8(buf, arg->flags);
+ WSM_PUT32(buf, arg->ssidLength);
+ WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid));
+ WSM_PUT32(buf, arg->beaconInterval);
+ WSM_PUT32(buf, arg->basicRateSet);
+
+ hw_priv->tx_burst_idx = -1;
+ ret = wsm_cmd_send(hw_priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT,
+ if_id);
+ wsm_cmd_unlock(hw_priv);
+ wsm_oper_unlock(hw_priv); /*confirm, not indcation.*/
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ wsm_oper_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_bss_params(struct xradio_common *hw_priv,
+ const struct wsm_set_bss_params *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, 0);
+ WSM_PUT8(buf, arg->beaconLostCount);
+ WSM_PUT16(buf, arg->aid);
+ WSM_PUT32(buf, arg->operationalRateSet);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT,
+ if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT(buf, arg, sizeof(*arg));
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT,
+ if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_remove_key(struct xradio_common *hw_priv,
+ const struct wsm_remove_key *arg, int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, arg->entryIndex);
+ WSM_PUT8(buf, 0);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT,
+ if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_tx_queue_params(struct xradio_common *hw_priv,
+ const struct wsm_set_tx_queue_params *arg,
+ u8 id, int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+ u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1};
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, queue_id_to_wmm_aci[id]);
+ WSM_PUT8(buf, 0);
+ WSM_PUT8(buf, arg->ackPolicy);
+ WSM_PUT8(buf, 0);
+ WSM_PUT32(buf, arg->maxTransmitLifetime);
+ WSM_PUT16(buf, arg->allowedMediumTime);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_edca_params(struct xradio_common *hw_priv,
+ const struct wsm_edca_params *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ /* Implemented according to specification. */
+
+ WSM_PUT16(buf, arg->params[3].cwMin);
+ WSM_PUT16(buf, arg->params[2].cwMin);
+ WSM_PUT16(buf, arg->params[1].cwMin);
+ WSM_PUT16(buf, arg->params[0].cwMin);
+
+ WSM_PUT16(buf, arg->params[3].cwMax);
+ WSM_PUT16(buf, arg->params[2].cwMax);
+ WSM_PUT16(buf, arg->params[1].cwMax);
+ WSM_PUT16(buf, arg->params[0].cwMax);
+
+ WSM_PUT8(buf, arg->params[3].aifns);
+ WSM_PUT8(buf, arg->params[2].aifns);
+ WSM_PUT8(buf, arg->params[1].aifns);
+ WSM_PUT8(buf, arg->params[0].aifns);
+
+ WSM_PUT16(buf, arg->params[3].txOpLimit);
+ WSM_PUT16(buf, arg->params[2].txOpLimit);
+ WSM_PUT16(buf, arg->params[1].txOpLimit);
+ WSM_PUT16(buf, arg->params[0].txOpLimit);
+
+ WSM_PUT32(buf, arg->params[3].maxReceiveLifetime);
+ WSM_PUT32(buf, arg->params[2].maxReceiveLifetime);
+ WSM_PUT32(buf, arg->params[1].maxReceiveLifetime);
+ WSM_PUT32(buf, arg->params[0].maxReceiveLifetime);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT, if_id);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_switch_channel(struct xradio_common *hw_priv,
+ const struct wsm_switch_channel *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_lock_tx(hw_priv);
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, arg->channelMode);
+ WSM_PUT8(buf, arg->channelSwitchCount);
+ WSM_PUT16(buf, arg->newChannelNumber);
+
+ hw_priv->channel_switch_in_progress = 1;
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT, if_id);
+ wsm_cmd_unlock(hw_priv);
+ if (ret) {
+ wsm_unlock_tx(hw_priv);
+ hw_priv->channel_switch_in_progress = 0;
+ }
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ wsm_unlock_tx(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_oper_lock(hw_priv);
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, arg->pmMode);
+ WSM_PUT8(buf, arg->fastPsmIdlePeriod);
+ WSM_PUT8(buf, arg->apPsmChangePeriod);
+ WSM_PUT8(buf, arg->minAutoPsPollPeriod);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ if (ret)
+ wsm_oper_unlock(hw_priv);
+
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ wsm_oper_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT8(buf, arg->mode);
+ WSM_PUT8(buf, arg->band);
+ WSM_PUT16(buf, arg->channelNumber);
+ WSM_PUT32(buf, arg->CTWindow);
+ WSM_PUT32(buf, arg->beaconInterval);
+ WSM_PUT8(buf, arg->DTIMPeriod);
+ WSM_PUT8(buf, arg->preambleType);
+ WSM_PUT8(buf, arg->probeDelay);
+ WSM_PUT8(buf, arg->ssidLength);
+ WSM_PUT(buf, arg->ssid, sizeof(arg->ssid));
+ WSM_PUT32(buf, arg->basicRateSet);
+
+ hw_priv->tx_burst_idx = -1;
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT,
+ if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+#if 0
+/* This API is no longer present in WSC */
+/* ******************************************************************** */
+
+int wsm_beacon_transmit(struct xradio_common *hw_priv,
+ const struct wsm_beacon_transmit *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+#endif
+
+/* ******************************************************************** */
+
+int wsm_start_find(struct xradio_common *hw_priv, int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT, if_id);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_stop_find(struct xradio_common *hw_priv, int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT, if_id);
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+}
+
+/* ******************************************************************** */
+
+int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg,
+ int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+ u16 cmd = 0x001C;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr));
+
+ if (is_hardware_xradio(hw_priv)) {
+ WSM_PUT8(buf, arg->unmap);
+ WSM_PUT8(buf, arg->link_id);
+ } else {
+ cmd |= WSM_TX_LINK_ID(arg->link_id);
+ WSM_PUT16(buf, 0);
+ }
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+}
+
+/* ******************************************************************** */
+
+int wsm_update_ie(struct xradio_common *hw_priv,
+ const struct wsm_update_ie *arg, int if_id)
+{
+ int ret;
+ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(hw_priv);
+
+ WSM_PUT16(buf, arg->what);
+ WSM_PUT16(buf, arg->count);
+ WSM_PUT(buf, arg->ies, arg->length);
+
+ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT, if_id);
+
+ wsm_cmd_unlock(hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(hw_priv);
+ return -ENOMEM;
+
+}
+/* ******************************************************************** */
+#ifdef MCAST_FWDING
+/* 3.66 */
+static int wsm_give_buffer_confirm(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ wsm_printk(XRADIO_DBG_MSG, "HW Buf count %d\n", hw_priv->hw_bufs_used);
+ if (!hw_priv->hw_bufs_used)
+ wake_up(&hw_priv->bh_evt_wq);
+
+ return 0;
+}
+
+/* 3.65 */
+int wsm_init_release_buffer_request(struct xradio_common *hw_priv, u8 index)
+{
+ struct wsm_buf *buf = &hw_priv->wsm_release_buf[index];
+ u16 cmd = 0x0022; /* Buffer Request */
+ u8 flags;
+ size_t buf_len;
+
+ wsm_buf_init(buf);
+
+ flags = index ? 0: 0x1;
+
+ WSM_PUT8(buf, flags);
+ WSM_PUT8(buf, 0);
+ WSM_PUT16(buf, 0);
+
+ buf_len = buf->data - buf->begin;
+
+ /* Fill HI message header */
+ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+ ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd);
+
+ return 0;
+nomem:
+ return -ENOMEM;
+}
+
+/* 3.65 fixed memory leakage by yangfh*/
+int wsm_deinit_release_buffer(struct xradio_common *hw_priv)
+{
+ struct wsm_buf *buf = NULL;
+ int i, err = 0;
+
+ for (i = 0; i < WSM_MAX_BUF; i++) {
+ buf = &hw_priv->wsm_release_buf[i];
+ if(likely(buf)) {
+ if(likely(buf->begin))
+ kfree(buf->begin);
+ buf->begin = buf->data = buf->end = NULL;
+ } else {
+ err++;
+ }
+ }
+ if(err) wsm_printk(XRADIO_DBG_ERROR, "%s, NULL buf=%d!\n", __func__, err);
+ return 0;
+}
+
+/* 3.68 */
+static int wsm_request_buffer_confirm(struct xradio_vif *priv,
+ u8 *arg,
+ struct wsm_buf *buf)
+{
+ u8 count;
+ u32 sta_asleep_mask = 0;
+ int i;
+ u32 mask = 0;
+ u32 change_mask = 0;
+ struct xradio_common *hw_priv = priv->hw_priv;
+
+ /* There is no status field in this message */
+ sta_asleep_mask = WSM_GET32(buf);
+ count = WSM_GET8(buf);
+ count -= 1; /* Current workaround for FW issue */
+
+ spin_lock_bh(&priv->ps_state_lock);
+ change_mask = (priv->sta_asleep_mask ^ sta_asleep_mask);
+ wsm_printk(XRADIO_DBG_MSG, "CM %x, HM %x, FWM %x\n", change_mask,priv->sta_asleep_mask, sta_asleep_mask);
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (change_mask) {
+ struct ieee80211_sta *sta;
+ int ret = 0;
+
+
+ for (i = 0; i < MAX_STA_IN_AP_MODE ; ++i) {
+
+ if(XRADIO_LINK_HARD != priv->link_id_db[i].status)
+ continue;
+
+ mask = BIT(i + 1);
+
+ /* If FW state and host state for this link are different then notify OMAC */
+ if(change_mask & mask) {
+ wsm_printk(XRADIO_DBG_MSG, "PS State Changed %d for sta %pM\n", (sta_asleep_mask & mask) ? 1:0, priv->link_id_db[i].mac);
+ rcu_read_lock();
+ sta = ieee80211_find_sta(priv->vif, priv->link_id_db[i].mac);
+ if (!sta) {
+ wsm_printk(XRADIO_DBG_MSG, "WRBC - could not find sta %pM\n",
+ priv->link_id_db[i].mac);
+ } else {
+ ret = ieee80211_sta_ps_transition_ni(sta, (sta_asleep_mask & mask) ? true: false);
+ wsm_printk(XRADIO_DBG_MSG, "PS State NOTIFIED %d\n", ret);
+ SYS_WARN(ret);
+ }
+ rcu_read_unlock();
+ }
+ }
+ /* Replace STA mask with one reported by FW */
+ spin_lock_bh(&priv->ps_state_lock);
+ priv->sta_asleep_mask = sta_asleep_mask;
+ spin_unlock_bh(&priv->ps_state_lock);
+ }
+
+ wsm_printk(XRADIO_DBG_MSG, "WRBC - HW Buf count %d SleepMask %d\n",
+ hw_priv->hw_bufs_used, sta_asleep_mask);
+ hw_priv->buf_released = 0;
+ SYS_WARN(count != (hw_priv->wsm_caps.numInpChBufs - 1));
+
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+/* 3.67 */
+int wsm_request_buffer_request(struct xradio_vif *priv,
+ u8 *arg)
+{
+ int ret;
+ struct wsm_buf *buf = &priv->hw_priv->wsm_cmd_buf;
+
+ wsm_cmd_lock(priv->hw_priv);
+
+ WSM_PUT8(buf, (*arg));
+ WSM_PUT8(buf, 0);
+ WSM_PUT16(buf, 0);
+
+ ret = wsm_cmd_send(priv->hw_priv, buf, arg, 0x0023, WSM_CMD_JOIN_TIMEOUT,priv->if_id);
+
+ wsm_cmd_unlock(priv->hw_priv);
+ return ret;
+
+nomem:
+ wsm_cmd_unlock(priv->hw_priv);
+ return -ENOMEM;
+}
+
+#endif
+
+int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+ priv->rx_filter.keepalive = enable;
+ return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
+}
+
+int wsm_set_probe_responder(struct xradio_vif *priv, bool enable)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+
+ priv->rx_filter.probeResponder = enable;
+ return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
+}
+/* ******************************************************************** */
+/* WSM indication events implementation */
+
+static int wsm_startup_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ u16 status;
+#ifdef CONFIG_XRADIO_DEBUG
+ static const char * const fw_types[] = {
+ "ETF",
+ "WFM",
+ "WSM",
+ "HI test",
+ "Platform test"
+ };
+#endif
+
+ hw_priv->wsm_caps.numInpChBufs = WSM_GET16(buf);
+ hw_priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf);
+ hw_priv->wsm_caps.hardwareId = WSM_GET16(buf);
+ hw_priv->wsm_caps.hardwareSubId = WSM_GET16(buf);
+ status = WSM_GET16(buf);
+ hw_priv->wsm_caps.firmwareCap = WSM_GET16(buf);
+ hw_priv->wsm_caps.firmwareType = WSM_GET16(buf);
+ hw_priv->wsm_caps.firmwareApiVer = WSM_GET16(buf);
+ hw_priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf);
+ hw_priv->wsm_caps.firmwareVersion = WSM_GET16(buf);
+ WSM_GET(buf, &hw_priv->wsm_caps.fw_label[0], WSM_FW_LABEL);
+ hw_priv->wsm_caps.fw_label[WSM_FW_LABEL+1] = 0; /* Do not trust FW too much. */
+
+ if (SYS_WARN(status))
+ return -EINVAL;
+
+ if (SYS_WARN(hw_priv->wsm_caps.firmwareType > 4))
+ return -EINVAL;
+
+ wsm_printk(XRADIO_DBG_NIY, "%s\n"
+ " Input buffers: %d x %d bytes\n"
+ " Hardware: %d.%d\n"
+ " %s firmware ver: %d, build: %d,"
+ " api: %d, cap: 0x%.4X\n",
+ __func__,
+ hw_priv->wsm_caps.numInpChBufs,
+ hw_priv->wsm_caps.sizeInpChBuf,
+ hw_priv->wsm_caps.hardwareId,
+ hw_priv->wsm_caps.hardwareSubId,
+ fw_types[hw_priv->wsm_caps.firmwareType],
+ hw_priv->wsm_caps.firmwareVersion,
+ hw_priv->wsm_caps.firmwareBuildNumber,
+ hw_priv->wsm_caps.firmwareApiVer,
+ hw_priv->wsm_caps.firmwareCap);
+
+ wsm_printk(XRADIO_DBG_ALWY, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]);
+
+ hw_priv->wsm_caps.firmwareReady = 1;
+
+ wake_up(&hw_priv->wsm_startup_done);
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+//add by yangfh 2014-10-31 16:58:53
+void wms_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv)
+{
+ struct sk_buff *skb = NULL;
+ struct ieee80211_mgmt *deauth = NULL;
+
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ int i = 0;
+ wsm_printk(XRADIO_DBG_WARN, "AP mode, send_deauth_to_self\n");
+ for (i = 0; i<MAX_STA_IN_AP_MODE; i++) {
+ if (priv->link_id_db[i].status == XRADIO_LINK_HARD) {
+ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
+ if (!skb)
+ return;
+ skb_reserve(skb, 64);
+ deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
+ if(!deauth) {
+ SYS_WARN(1);
+ return;
+ }
+ deauth->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
+ deauth->duration = 0;
+ memcpy(deauth->da, priv->vif->addr, ETH_ALEN);
+ memcpy(deauth->sa, priv->link_id_db[i].mac, ETH_ALEN);
+ memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN);
+ deauth->seq_ctrl = 0;
+ deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+ }
+ } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ wsm_printk(XRADIO_DBG_WARN, "STA mode, send_deauth_to_self\n");
+ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
+ if (!skb)
+ return;
+ skb_reserve(skb, 64);
+ deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
+ if(!deauth) {
+ SYS_WARN(1);
+ return;
+ }
+ deauth->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH);
+ deauth->duration = 0;
+ memcpy(deauth->da, priv->vif->addr, ETH_ALEN);
+ memcpy(deauth->sa, priv->join_bssid, ETH_ALEN);
+ memcpy(deauth->bssid, priv->join_bssid, ETH_ALEN);
+ deauth->seq_ctrl = 0;
+ deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+}
+
+void wms_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv)
+{
+ struct sk_buff *skb = NULL;
+ struct ieee80211_mgmt *disassoc = NULL;
+ if (priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ int i = 0;
+ wsm_printk(XRADIO_DBG_WARN, "AP mode, wms_send_disassoc_to_self\n");
+ for (i = 0; i<MAX_STA_IN_AP_MODE; i++) {
+ if (priv->link_id_db[i].status == XRADIO_LINK_HARD) {
+ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
+ if (!skb)
+ return;
+ skb_reserve(skb, 64);
+ disassoc = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
+ if(!disassoc) {
+ SYS_WARN(1);
+ return;
+ }
+ disassoc->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC);
+ disassoc->duration = 0;
+ memcpy(disassoc->da, priv->vif->addr, ETH_ALEN);
+ memcpy(disassoc->sa, priv->link_id_db[i].mac, ETH_ALEN);
+ memcpy(disassoc->bssid, priv->vif->addr, ETH_ALEN);
+ disassoc->seq_ctrl = 0;
+ disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_STA_HAS_LEFT;
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+ }
+ } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ wsm_printk(XRADIO_DBG_WARN, "STA mode, wms_send_disassoc_to_self\n");
+ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
+ if (!skb)
+ return;
+ skb_reserve(skb, 64);
+ disassoc = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
+ if(!disassoc) {
+ SYS_WARN(1);
+ return;
+ }
+ disassoc->frame_control =
+ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC);
+ disassoc->duration = 0;
+ memcpy(disassoc->da, priv->vif->addr, ETH_ALEN);
+ memcpy(disassoc->sa, priv->join_bssid, ETH_ALEN);
+ memcpy(disassoc->bssid, priv->join_bssid, ETH_ALEN);
+ disassoc->seq_ctrl = 0;
+ disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY;
+ ieee80211_rx_irqsafe(priv->hw, skb);
+ }
+}
+
+static int wsm_receive_indication(struct xradio_common *hw_priv,
+ int interface_link_id,
+ struct wsm_buf *buf,
+ struct sk_buff **skb_p)
+{
+ struct xradio_vif *priv;
+
+ hw_priv->rx_timestamp = jiffies;
+
+ struct wsm_rx rx;
+ struct ieee80211_hdr *hdr;
+ size_t hdr_len;
+
+ rx.status = WSM_GET32(buf);
+ rx.channelNumber = WSM_GET16(buf);
+ rx.rxedRate = WSM_GET8(buf);
+ rx.rcpiRssi = WSM_GET8(buf);
+ rx.flags = WSM_GET32(buf);
+
+ /* TODO:COMBO: Frames received from scanning are received
+ * with interface ID == 2 */
+ if (is_hardware_xradio(hw_priv)) {
+ if (interface_link_id == XRWL_GENERIC_IF_ID) {
+ /* Frames received in response to SCAN
+ * Request */
+ interface_link_id =
+ get_interface_id_scanning(hw_priv);
+ if (interface_link_id == -1) {
+ interface_link_id = hw_priv->roc_if_id;
+ }
+#ifdef ROAM_OFFLOAD
+ if (hw_priv->auto_scanning) {
+ interface_link_id = hw_priv->scan.if_id;
+ }
+#endif/*ROAM_OFFLOAD*/
+ }
+ /* linkid (peer sta id is encoded in bit 25-28 of
+ flags field */
+ rx.link_id = ((rx.flags & (0xf << 25)) >> 25);
+ rx.if_id = interface_link_id;
+ } else {
+ rx.link_id = interface_link_id;
+ rx.if_id = 0;
+ }
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, rx.if_id);
+ if (!priv) {
+ dev_dbg(hw_priv->pdev, "got frame on a vif we don't have, dropped\n");
+ return 0;
+ }
+ //remove wsm hdr of skb
+ hdr_len = buf->data - buf->begin;
+ skb_pull(*skb_p, hdr_len);
+
+ /* FW Workaround: Drop probe resp or
+ beacon when RSSI is 0 */
+ hdr = (struct ieee80211_hdr *) (*skb_p)->data;
+
+ if (!rx.rcpiRssi &&
+ (ieee80211_is_probe_resp(hdr->frame_control) ||
+ ieee80211_is_beacon(hdr->frame_control))) {
+ spin_unlock(&priv->vif_lock);
+ return 0;
+ }
+
+ /* If no RSSI subscription has been made,
+ * convert RCPI to RSSI here */
+ if (!priv->cqm_use_rssi)
+ rx.rcpiRssi = rx.rcpiRssi / 2 - 110;
+
+ if (!rx.status && unlikely(ieee80211_is_deauth(hdr->frame_control))) {
+ if (priv->join_status == XRADIO_JOIN_STATUS_STA) {
+ /* Shedule unjoin work */
+ dev_dbg(hw_priv->pdev,
+ "Issue unjoin command (RX).\n");
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue,
+ &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ }
+ }
+ xradio_rx_cb(priv, &rx, skb_p);
+ if (*skb_p)
+ skb_push(*skb_p, hdr_len);
+ spin_unlock(&priv->vif_lock);
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_event_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf,
+ int interface_link_id)
+{
+ int first;
+ struct xradio_wsm_event *event = NULL;
+ struct xradio_vif *priv;
+
+ if (!is_hardware_xradio(hw_priv))
+ interface_link_id = 0;
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+
+ if (unlikely(!priv)) {
+ dev_warn(hw_priv->pdev, "Event: %d(%d) for removed "
+ "interface, ignoring\n", __le32_to_cpu(WSM_GET32(buf)),
+ __le32_to_cpu(WSM_GET32(buf)));
+ return 0;
+ }
+
+ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
+ /* STA is stopped. */
+ return 0;
+ }
+ spin_unlock(&priv->vif_lock);
+
+ event = kzalloc(sizeof(struct xradio_wsm_event), GFP_KERNEL);
+ if (event == NULL) {
+ dev_err(hw_priv->pdev, "xr_kzalloc failed!");
+ return -EINVAL;
+ }
+
+ event->evt.eventId = __le32_to_cpu(WSM_GET32(buf));
+ event->evt.eventData = __le32_to_cpu(WSM_GET32(buf));
+ event->if_id = interface_link_id;
+
+ dev_dbg(hw_priv->pdev, "Event: %d(%d)\n",
+ event->evt.eventId, event->evt.eventData);
+
+ spin_lock(&hw_priv->event_queue_lock);
+ first = list_empty(&hw_priv->event_queue);
+ list_add_tail(&event->link, &hw_priv->event_queue);
+ spin_unlock(&hw_priv->event_queue_lock);
+
+ if (first)
+ queue_work(hw_priv->workqueue, &hw_priv->event_handler);
+
+ return 0;
+
+underflow:
+ kfree(event);
+ return -EINVAL;
+}
+
+#define PRINT_11K_MEASRURE 1
+static int wsm_measure_cmpl_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ MEASUREMENT_COMPLETE measure_cmpl;
+ u8 cca_chanload;
+ u32 buf_len = 0;
+ u32 *data;
+
+ LMAC_MEAS_CHANNEL_LOAD_RESULTS *chanload_res;
+ LMAC_MEAS_NOISE_HISTOGRAM_RESULTS *noise_res;
+ WSM_GET(buf, &measure_cmpl, 12);
+
+ switch (measure_cmpl.MeasurementType) {
+ case ChannelLoadMeasurement:
+ buf_len = sizeof(LMAC_MEAS_CHANNEL_LOAD_RESULTS);
+ break;
+ case NoiseHistrogramMeasurement:
+ buf_len = sizeof(LMAC_MEAS_NOISE_HISTOGRAM_RESULTS);
+ break;
+ case BeaconReport:
+ buf_len = sizeof(LMAC_MEAS_BEACON_RESULTS);
+ break;
+ case STAstatisticsReport:
+ buf_len = sizeof(LMAC_MEAS_STA_STATS_RESULTS);
+ break;
+ case LinkMeasurement:
+ buf_len = sizeof(LMAC_MEAS_LINK_MEASUREMENT_RESULTS);
+ break;
+ }
+ wsm_printk(XRADIO_DBG_ERROR, "[11K]buf_len = %d\n", buf_len);
+ WSM_GET(buf, &measure_cmpl.MeasurementReport, buf_len);
+
+ data = (u32 *)(&measure_cmpl);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[0]=%08x\n", data[0]);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[1]=%08x\n", data[1]);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[2]=%08x\n", data[2]);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[3]=%08x\n", data[3]);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[4]=%08x\n", data[4]);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[5]=%08x\n", data[5]);
+// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[6]=%08x\n", data[6]);
+ wsm_printk(XRADIO_DBG_ERROR, "[***HL***]MeasurementType=%0d\n", measure_cmpl.MeasurementType);
+
+ if (measure_cmpl.Status == WSM_STATUS_SUCCESS){
+ switch (measure_cmpl.MeasurementType) {
+ case ChannelLoadMeasurement:
+ chanload_res = &measure_cmpl.MeasurementReport.ChannelLoadResults;
+ cca_chanload = (chanload_res->ChannelLoadCCA == MEAS_CCA) ?
+ chanload_res->CCAbusyFraction :
+ chanload_res->ChannelLoad;
+ #ifdef PRINT_11K_MEASRURE
+ wsm_printk(XRADIO_DBG_ERROR, "[11K] ChannelLoadMeasurement Result:\n"\
+ "ChannelLoadCCA = %d\n"\
+ "ChannelNum = %d\n"\
+ "Duration = %d\n"\
+ "Fraction = %d\n", \
+ chanload_res->ChannelLoadCCA,\
+ chanload_res->ChannelNum,\
+ chanload_res->MeasurementDuration,\
+ cca_chanload
+ );
+ #endif
+ break;
+ case NoiseHistrogramMeasurement:
+ noise_res = &measure_cmpl.MeasurementReport.NoiseHistogramResults;
+// IpiRpi = (noise_res->IpiRpi == MEAS_RPI) ?
+// chanload_res->CCAbusyFraction :
+// chanload_res->ChannelLoad;
+ #ifdef PRINT_11K_MEASRURE
+ wsm_printk(XRADIO_DBG_ERROR, "[11K] NoiseHistogramResults:\n"\
+ "IpiRpi = %d\n"\
+ "ChannelNum = %d\n"\
+ "PI_0__Density = %d\n"\
+ "PI_1__Density = %d\n"\
+ "PI_2__Density = %d\n"\
+ "PI_3__Density = %d\n"\
+ "PI_4__Density = %d\n"\
+ "PI_5__Density = %d\n"\
+ "PI_6__Density = %d\n"\
+ "PI_7__Density = %d\n"\
+ "PI_8__Density = %d\n"\
+ "PI_9__Density = %d\n"\
+ "PI_10_Density = %d\n", \
+ noise_res->IpiRpi,\
+ noise_res->ChannelNum,\
+ noise_res->PI_0_Density,\
+ noise_res->PI_1_Density,\
+ noise_res->PI_2_Density,\
+ noise_res->PI_3_Density,\
+ noise_res->PI_4_Density,\
+ noise_res->PI_5_Density,\
+ noise_res->PI_6_Density,\
+ noise_res->PI_7_Density,\
+ noise_res->PI_8_Density,\
+ noise_res->PI_9_Density,\
+ noise_res->PI_10_Density
+ );
+ #endif
+ break;
+ case BeaconReport:
+ break;
+ case STAstatisticsReport:
+ break;
+ case LinkMeasurement:
+ break;
+ }
+ } else {
+ wsm_printk(XRADIO_DBG_ERROR, "11K Measure(type=%d) Fail\n", measure_cmpl.MeasurementType);
+ }
+
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+/* TODO:COMBO:Make this perVIFF once mac80211 support is available */
+static int wsm_channel_switch_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ wsm_unlock_tx(hw_priv); /* Re-enable datapath */
+ SYS_WARN(WSM_GET32(buf));
+
+ hw_priv->channel_switch_in_progress = 0;
+ wake_up(&hw_priv->channel_switch_done);
+
+ if (hw_priv->wsm_cbc.channel_switch)
+ hw_priv->wsm_cbc.channel_switch(hw_priv);
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_set_pm_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ wsm_oper_unlock(hw_priv);
+ return 0;
+}
+
+static int wsm_scan_complete_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+#ifdef ROAM_OFFLOAD
+ if(hw_priv->auto_scanning == 0)
+ wsm_oper_unlock(hw_priv);
+#else
+ wsm_oper_unlock(hw_priv);
+#endif /*ROAM_OFFLOAD*/
+
+ if (hw_priv->wsm_cbc.scan_complete) {
+ struct wsm_scan_complete arg;
+ arg.status = WSM_GET32(buf);
+ arg.psm = WSM_GET8(buf);
+ arg.numChannels = WSM_GET8(buf);
+ hw_priv->wsm_cbc.scan_complete(hw_priv, &arg);
+ }
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+static int wsm_find_complete_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ /* TODO: Implement me. */
+ //STUB();
+ return 0;
+}
+
+static int wsm_suspend_resume_indication(struct xradio_common *hw_priv,
+ int interface_link_id,
+ struct wsm_buf *buf)
+{
+ if (hw_priv->wsm_cbc.suspend_resume) {
+ u32 flags;
+ struct wsm_suspend_resume arg;
+ struct xradio_vif *priv;
+
+ if (is_hardware_xradio(hw_priv)) {
+ int i;
+ arg.if_id = interface_link_id;
+ /* TODO:COMBO: Extract bitmap from suspend-resume
+ * TX indication */
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (!priv)
+ continue;
+ if (priv->join_status ==
+ XRADIO_JOIN_STATUS_AP) {
+ arg.if_id = priv->if_id;
+ break;
+ }
+ arg.link_id = 0;
+ }
+ } else {
+ arg.if_id = 0;
+ arg.link_id = interface_link_id;
+ }
+
+ flags = WSM_GET32(buf);
+ arg.stop = !(flags & 1);
+ arg.multicast = !!(flags & 8);
+ arg.queue = (flags >> 1) & 3;
+
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg.if_id);
+ if (unlikely(!priv)) {
+ wsm_printk(XRADIO_DBG_MSG, "suspend-resume indication"
+ " for removed interface!\n");
+ return 0;
+ }
+ hw_priv->wsm_cbc.suspend_resume(priv, &arg);
+ spin_unlock(&priv->vif_lock);
+ }
+ return 0;
+
+underflow:
+ return -EINVAL;
+}
+
+
+/* ******************************************************************** */
+/* WSM TX */
+
+int wsm_cmd_send(struct xradio_common *hw_priv,
+ struct wsm_buf *buf,
+ void *arg, u16 cmd, long tmo, int if_id)
+{
+ size_t buf_len = buf->data - buf->begin;
+ int ret;
+
+ if (cmd == 0x0006 || cmd == 0x0005) /* Write/Read MIB */
+ wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X [MIB: 0x%.4X] (%d)\n",
+ cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]),
+ buf_len);
+ else
+ wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%d)\n", cmd, buf_len);
+
+ if (unlikely(hw_priv->bh_error)) {
+ wsm_buf_reset(buf);
+ wsm_printk(XRADIO_DBG_ERROR, "bh error!>>> 0x%.4X (%d)\n", cmd, buf_len);
+ return -ETIMEDOUT;
+ }
+
+ /* Fill HI message header */
+ /* BH will add sequence number */
+
+ /* TODO:COMBO: Add if_id from to the WSM header */
+ /* if_id == -1 indicates that command is HW specific,
+ * eg. wsm_configuration which is called during driver initialzation
+ * (mac80211 .start callback called when first ifce is created. )*/
+
+ /* send hw specific commands on if 0 */
+ if (if_id == -1)
+ if_id = 0;
+
+ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len);
+ ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd |
+ ((is_hardware_xradio(hw_priv)) ? (if_id << 6) : 0));
+
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ SYS_BUG(hw_priv->wsm_cmd.ptr);
+ hw_priv->wsm_cmd.done = 0;
+ hw_priv->wsm_cmd.ptr = buf->begin;
+ hw_priv->wsm_cmd.len = buf_len;
+ hw_priv->wsm_cmd.arg = arg;
+ hw_priv->wsm_cmd.cmd = cmd;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+
+ xradio_bh_wakeup(hw_priv);
+
+ if (unlikely(hw_priv->bh_error)) {
+ /* Do not wait for timeout if BH is dead. Exit immediately. */
+ ret = 0;
+ } else {
+ unsigned long wsm_cmd_max_tmo;
+
+ /* Give start cmd a little more time */
+ if (unlikely(tmo == WSM_CMD_START_TIMEOUT))
+ wsm_cmd_max_tmo = WSM_CMD_START_TIMEOUT;
+ else
+ wsm_cmd_max_tmo = WSM_CMD_DEFAULT_TIMEOUT;
+
+ /*Set max timeout.*/
+ wsm_cmd_max_tmo = jiffies + wsm_cmd_max_tmo;
+
+ /* Firmware prioritizes data traffic over control confirm.
+ * Loop below checks if data was RXed and increases timeout
+ * accordingly. */
+ do {
+ /* It's safe to use unprotected access to wsm_cmd.done here */
+ ret = wait_event_timeout(hw_priv->wsm_cmd_wq, hw_priv->wsm_cmd.done, tmo);
+
+ /* check time since last rxed and max timeout.*/
+ } while (!ret &&
+ time_before_eq(jiffies, hw_priv->rx_timestamp+tmo) &&
+ time_before(jiffies, wsm_cmd_max_tmo));
+
+ }
+
+ if (unlikely(ret == 0)) {
+ u16 raceCheck;
+
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ raceCheck = hw_priv->wsm_cmd.cmd;
+ hw_priv->wsm_cmd.arg = NULL;
+ hw_priv->wsm_cmd.ptr = NULL;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+
+ dev_err(hw_priv->pdev, "***CMD timeout!>>> 0x%.4X (%d), buf_use=%d, bh_state=%d\n",
+ cmd, buf_len, hw_priv->hw_bufs_used, hw_priv->bh_error);
+ /* Race condition check to make sure _confirm is not called
+ * after exit of _send */
+ if (raceCheck == 0xFFFF) {
+ /* If wsm_handle_rx got stuck in _confirm we will hang
+ * system there. It's better than silently currupt
+ * stack or heap, isn't it? */
+ SYS_BUG(wait_event_timeout(
+ hw_priv->wsm_cmd_wq,
+ hw_priv->wsm_cmd.done,
+ WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0);
+ }
+
+ /* Kill BH thread to report the error to the top layer. */
+ hw_priv->bh_error = 1;
+#ifdef BH_USE_SEMAPHORE
+ up(&hw_priv->bh_sem);
+#else
+ wake_up(&hw_priv->bh_wq);
+#endif
+ ret = -ETIMEDOUT;
+ } else {
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ SYS_BUG(!hw_priv->wsm_cmd.done);
+ ret = hw_priv->wsm_cmd.ret;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+ }
+ wsm_buf_reset(buf);
+ return ret;
+}
+
+/* ******************************************************************** */
+/* WSM TX port control */
+
+void wsm_lock_tx(struct xradio_common *hw_priv)
+{
+ down(&hw_priv->tx_lock_sem);
+ atomic_add(1, &hw_priv->tx_lock);
+ /* always check event if wsm_vif_lock_tx.*/
+ if (wsm_flush_tx(hw_priv))
+ wsm_printk(XRADIO_DBG_MSG, "TX is locked.\n");
+ up(&hw_priv->tx_lock_sem);
+}
+
+void wsm_vif_lock_tx(struct xradio_vif *priv)
+{
+ struct xradio_common *hw_priv = priv->hw_priv;
+ down(&hw_priv->tx_lock_sem);
+ if (atomic_add_return(1, &hw_priv->tx_lock) == 1) {
+ if (wsm_vif_flush_tx(priv))
+ wsm_printk(XRADIO_DBG_MSG, "TX is locked for"
+ " if_id %d.\n", priv->if_id);
+ }
+ up(&hw_priv->tx_lock_sem);
+}
+
+void wsm_lock_tx_async(struct xradio_common *hw_priv)
+{
+ if (atomic_add_return(1, &hw_priv->tx_lock) == 1)
+ wsm_printk(XRADIO_DBG_MSG, "TX is locked (async).\n");
+}
+
+bool wsm_flush_tx(struct xradio_common *hw_priv)
+{
+ long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT;
+
+ /* Flush must be called with TX lock held. */
+ SYS_BUG(!atomic_read(&hw_priv->tx_lock));
+
+ /* First check if we really need to do something.
+ * It is safe to use unprotected access, as hw_bufs_used
+ * can only decrements. */
+ if (!hw_priv->hw_bufs_used)
+ return true;
+
+ if (hw_priv->bh_error) {
+ /* In case of failure do not wait for magic. */
+ wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, "
+ "will not flush TX.\n");
+ return false;
+ } else {
+ /* Get "oldest" frame, if any frames stuck in firmware,
+ query all of them until max timeout. */
+ int num = hw_priv->hw_bufs_used + 1;
+ while (xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS,
+ 0xffffffff, &timeout)) {
+ if (timeout < 0 || !num) {
+ /* Hmmm... Not good. Frame had stuck in firmware. */
+ wsm_printk(XRADIO_DBG_ERROR,
+ "%s:hw_bufs_used=%d, num=%d, timeout=%ld\n",
+ __func__, hw_priv->hw_bufs_used, num, timeout);
+ hw_priv->bh_error = 1;
+#ifdef BH_USE_SEMAPHORE
+ up(&hw_priv->bh_sem);
+#else
+ wake_up(&hw_priv->bh_wq);
+#endif
+ return false;
+ } else if (wait_event_timeout(hw_priv->bh_evt_wq,
+ !hw_priv->hw_bufs_used, timeout) > 0) {
+ return true;
+ }
+ --num;
+ }
+ if (hw_priv->hw_bufs_used)
+ wsm_printk(XRADIO_DBG_ERROR, "%s:No pengding, but hw_bufs_used=%d\n",
+ __func__, hw_priv->hw_bufs_used);
+ /* Ok, everything is flushed. */
+ return true;
+ }
+}
+
+bool wsm_vif_flush_tx(struct xradio_vif *priv)
+{
+ struct xradio_common *hw_priv = priv->hw_priv;
+ long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT;
+ int if_id = priv->if_id;
+
+ /* Flush must be called with TX lock held. */
+ SYS_BUG(!atomic_read(&hw_priv->tx_lock));
+
+ /* First check if we really need to do something.
+ * It is safe to use unprotected access, as hw_bufs_used
+ * can only decrements. */
+ if (!hw_priv->hw_bufs_used_vif[if_id])
+ return true;
+
+ if (hw_priv->bh_error) {
+ /* In case of failure do not wait for magic. */
+ wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, "
+ "will not flush TX.\n");
+ return false;
+ } else {
+ /* Get "oldest" frame, if any frames stuck in firmware,
+ query all of them until max timeout. */
+ int num = hw_priv->hw_bufs_used_vif[if_id] + 1;
+ while (xradio_query_txpkt_timeout(hw_priv, if_id, 0xffffffff, &timeout)) {
+ if (timeout < 0 || !num) {
+ /* Hmmm... Not good. Frame had stuck in firmware. */
+ wsm_printk(XRADIO_DBG_ERROR, "%s: if_id=%d, hw_bufs_used_vif=%d, num=%d\n",
+ __func__, if_id, hw_priv->hw_bufs_used_vif[priv->if_id],
+ num);
+ hw_priv->bh_error = 1;
+ #ifdef BH_USE_SEMAPHORE
+ up(&hw_priv->bh_sem);
+ #else
+ wake_up(&hw_priv->bh_wq);
+ #endif
+ return false;
+ } else if (wait_event_timeout(hw_priv->bh_evt_wq,
+ !hw_priv->hw_bufs_used_vif[if_id], timeout) > 0) {
+ return true;
+ }
+ --num;
+ }
+ if (hw_priv->hw_bufs_used_vif[if_id])
+ wsm_printk(XRADIO_DBG_ERROR, "%s:No pengding, but hw_bufs_used_vif=%d\n",
+ __func__, hw_priv->hw_bufs_used_vif[priv->if_id]);
+ /* Ok, everything is flushed. */
+ return true;
+ }
+}
+
+
+void wsm_unlock_tx(struct xradio_common *hw_priv)
+{
+ int tx_lock;
+ if (hw_priv->bh_error)
+ wsm_printk(XRADIO_DBG_ERROR, "bh_error=%d, wsm_unlock_tx is unsafe\n",
+ hw_priv->bh_error);
+ else {
+ tx_lock = atomic_sub_return(1, &hw_priv->tx_lock);
+ if (tx_lock < 0) {
+ SYS_BUG(1);
+ } else if (tx_lock == 0) {
+ xradio_bh_wakeup(hw_priv);
+ wsm_printk(XRADIO_DBG_MSG, "TX is unlocked.\n");
+ }
+ }
+}
+
+/* ******************************************************************** */
+/* WSM RX */
+
+int wsm_handle_exception(struct xradio_common *hw_priv, u8 *data, size_t len)
+{
+ struct wsm_buf buf;
+ u32 reason;
+ u32 reg[18];
+ char fname[48];
+ int i = 0;
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ struct xradio_vif *priv = NULL;
+#endif
+
+ static const char * const reason_str[] = {
+ "undefined instruction",
+ "prefetch abort",
+ "data abort",
+ "unknown error",
+ };
+
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ /* Send the event upwards on the FW exception */
+ xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ);
+
+ spin_lock(&hw_priv->vif_list_lock);
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (!priv)
+ continue;
+ //ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL);
+ }
+ spin_unlock(&hw_priv->vif_list_lock);
+#endif
+
+ buf.begin = buf.data = data;
+ buf.end = &buf.begin[len];
+
+ reason = WSM_GET32(&buf);
+ for (i = 0; i < ARRAY_SIZE(reg); ++i)
+ reg[i] = WSM_GET32(&buf);
+ WSM_GET(&buf, fname, sizeof(fname));
+
+ if (reason < 4) {
+ dev_err(hw_priv->pdev, "Firmware exception: %s.\n",
+ reason_str[reason]);
+ } else {
+ dev_err(hw_priv->pdev, "Firmware assert at %.*s, line %d, reason=0x%x\n",
+ sizeof(fname), fname, reg[1], reg[2]);
+ }
+
+ for (i = 0; i < 12; i += 4) {
+ dev_err(hw_priv->pdev, "Firmware:" \
+ "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n",
+ i + 0, reg[i + 0], i + 1, reg[i + 1],
+ i + 2, reg[i + 2], i + 3, reg[i + 3]);
+ }
+ dev_err(hw_priv->pdev, "Firmware:" \
+ "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n",
+ reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]);
+ i += 4;
+ dev_err(hw_priv->pdev, "Firmware:CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+ reg[i + 0], reg[i + 1]);
+
+ return 0;
+
+underflow:
+ dev_err(hw_priv->pdev, "Firmware exception.\n");
+ print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, data, len);
+ return -EINVAL;
+}
+
+static int wsm_debug_indication(struct xradio_common *hw_priv,
+ struct wsm_buf *buf)
+{
+ //for only one debug item.
+ u32 dbg_id, buf_data=0;
+ u16 dbg_buf_len;
+ u8 dbg_len;
+ u8 *dbg_buf;
+ dbg_id = WSM_GET32(buf);
+
+ dbg_buf_len = buf->end - buf->data;
+
+ if (dbg_id == 5) {
+ do {
+ dbg_buf_len = buf->end - buf->data;
+ dbg_len = WSM_GET8(buf);
+ if (dbg_len > dbg_buf_len - sizeof(dbg_len)) {
+ wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_len = %d\n", dbg_len);
+ wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_buf_len = %d\n", dbg_buf_len);
+ wsm_printk(XRADIO_DBG_ERROR, "[FW]debug ind err\n");
+ //ret = -EINVAL;
+ //return ret;
+
+
+ break;
+ }
+
+ dbg_buf = buf->data;
+ //print it;
+ wsm_printk(XRADIO_DBG_ALWY, "[FW-LOG] %s", dbg_buf);
+ //
+ buf->data += dbg_len;
+
+ } while (buf->data < buf->end);
+ } else {
+ if (dbg_buf_len >= 4) {
+ buf_data = WSM_GET32(buf);
+ wsm_printk(XRADIO_DBG_ALWY, "[FW-DEBUG] DbgId = %d, data = %d", dbg_id, buf_data);
+ } else {
+ wsm_printk(XRADIO_DBG_ALWY, "[FW-DEBUG] DbgId = %d", dbg_id);
+ }
+ }
+
+ return 0;
+
+underflow:
+ SYS_WARN(1);
+ return -EINVAL;
+}
+
+#if defined(DGB_XRADIO_HWT)
+extern u8 hwt_testing;
+extern u16 hwt_tx_len;
+extern u16 hwt_tx_num;
+extern int sent_num;
+extern struct timeval hwt_start_time;
+extern struct timeval hwt_end_time;
+int wsm_hwt_tx_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf)
+{
+ u32 *packetID = (u32 *)(buf->data+8);
+ u8 num = *(buf->data + 6);
+
+ wsm_printk(XRADIO_DBG_NIY, "%s, num=%d, hw_bufs_used=%d\n",
+ __func__, num, hw_priv->hw_bufs_used);
+
+ wsm_release_vif_tx_buffer(hw_priv, 0, num);
+ wsm_release_tx_buffer(hw_priv, num-1); //one release is in bh.
+
+ //confirm of last packet, so report the test results.
+ if (*(buf->data+7) & 0x01) { //last packet
+ u32 time_int = 0;
+ u32 total = hwt_tx_num*hwt_tx_len*8;
+ do_gettimeofday(&hwt_end_time);
+ time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \
+ (hwt_end_time.tv_usec-hwt_start_time.tv_usec);
+ wsm_printk(XRADIO_DBG_ALWY, "%s, HIF TX: time=%dms, throughput=%d.%dMbps, SW time=%dus\n",
+ __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int,
+ (*(u32 *)(buf->data+8)));
+ hwt_tx_len = 0;
+ hwt_tx_num = 0;
+ sent_num = 0; //reset the sent_num
+ hwt_testing = 0;
+ }
+ return 0;
+}
+
+u16 recv_num = 0;
+extern u8 hwt_rx_en;
+extern u16 hwt_rx_len;
+extern u16 hwt_rx_num;
+int wsm_hwt_rx_frames(struct xradio_common *hw_priv, struct wsm_buf *buf)
+{
+
+ wsm_printk(XRADIO_DBG_NIY, "%s, status=%d, len=%d\n", __func__,
+ *(u16 *)(buf->data+2), *(u16 *)(buf->data+4));
+ recv_num++;
+ if (recv_num >= hwt_rx_num) { //last packet
+ u32 time_int = 0;
+ u32 total = recv_num*hwt_rx_len*8;
+ do_gettimeofday(&hwt_end_time);
+ time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \
+ (hwt_end_time.tv_usec-hwt_start_time.tv_usec);
+ wsm_printk(XRADIO_DBG_ALWY, "%s, HIF RX: time=%dms, throughput=%d.%dMbps\n",
+ __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int);
+ hwt_rx_en = 0;
+ hwt_rx_num = 0;
+ recv_num = 0; //reset the recv_num
+ hwt_testing = 0;
+ }
+
+ return 0;
+}
+
+int wsm_hwt_enc_results(struct xradio_common *hw_priv, struct wsm_buf *buf)
+{
+ wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, enc throughput=%d.%02dMbps\n", __func__,
+ *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12));
+ hwt_testing = 0;
+ return 0;
+}
+
+int wsm_hwt_mic_results(struct xradio_common *hw_priv, struct wsm_buf *buf)
+{
+ wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, mic throughput=%d.%02dMbps\n", __func__,
+ *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12));
+ hwt_testing = 0;
+ return 0;
+}
+#endif //DGB_XRADIO_HWT
+
+
+int wsm_handle_rx(struct xradio_common *hw_priv, int id,
+ struct wsm_hdr *wsm, struct sk_buff **skb_p)
+{
+ int ret = 0;
+ struct wsm_buf wsm_buf;
+ struct xradio_vif *priv = NULL;
+ int i = 0;
+ int interface_link_id = (id >> 6) & 0x0F;
+#ifdef ROAM_OFFLOAD
+#if 0
+ struct xradio_vif *priv;
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id);
+ if (unlikely(!priv)) {
+ SYS_WARN(1);
+ return 0;
+ }
+ spin_unlock(&priv->vif_lock);
+#endif
+#endif/*ROAM_OFFLOAD*/
+
+ /* Strip link id. */
+ id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+
+ wsm_buf.begin = (u8 *)&wsm[0];
+ wsm_buf.data = (u8 *)&wsm[1];
+ wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)];
+
+ wsm_printk(XRADIO_DBG_MSG, "<<< 0x%.4X (%d)\n", id,
+ wsm_buf.end - wsm_buf.begin);
+
+#if defined(DGB_XRADIO_HWT)
+/***************************for HWT ********************************/
+ if (id == 0x0424) {
+ u16 TestID = *(u16 *)(wsm_buf.data);
+ if (TestID == 1) //test frame confirm.
+ wsm_hwt_tx_confirm(hw_priv, &wsm_buf);
+ else {
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ hw_priv->wsm_cmd.ret = *((u16 *)(wsm_buf.data) + 1);
+ hw_priv->wsm_cmd.done = 1;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+ wake_up(&hw_priv->wsm_cmd_wq);
+ wsm_printk(XRADIO_DBG_ALWY, "HWT TestID=0x%x Confirm ret=%d\n",
+ *(u16 *)(wsm_buf.data), hw_priv->wsm_cmd.ret);
+ }
+ return 0;
+ } else if (id == 0x0824) {
+ u16 TestID = *(u16 *)(wsm_buf.data);
+ switch (TestID) {
+ case 2: //recieve a test frame.
+ wsm_hwt_rx_frames(hw_priv, &wsm_buf);
+ break;
+ case 3: //enc test result.
+ wsm_hwt_enc_results(hw_priv, &wsm_buf);
+ break;
+ case 4: //mic test result.
+ wsm_hwt_mic_results(hw_priv, &wsm_buf);
+ break;
+ default:
+ wsm_printk(XRADIO_DBG_ERROR, "HWT ERROR Indication TestID=0x%x\n", TestID);
+ break;
+ }
+ return 0;
+ }
+/***************************for HWT ********************************/
+#endif //DGB_XRADIO_HWT
+
+ if (id == 0x404) {
+ ret = wsm_tx_confirm(hw_priv, &wsm_buf, interface_link_id);
+#ifdef MCAST_FWDING
+#if 1
+ } else if (id == 0x422) {
+ ret = wsm_give_buffer_confirm(hw_priv, &wsm_buf);
+#endif
+#endif
+
+ } else if (id == 0x41E) {
+ ret = wsm_multi_tx_confirm(hw_priv, &wsm_buf,
+ interface_link_id);
+ } else if (id & 0x0400) {
+ void *wsm_arg;
+ u16 wsm_cmd;
+
+ /* Do not trust FW too much. Protection against repeated
+ * response and race condition removal (see above). */
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ wsm_arg = hw_priv->wsm_cmd.arg;
+ wsm_cmd = hw_priv->wsm_cmd.cmd &
+ ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX);
+ hw_priv->wsm_cmd.cmd = 0xFFFF;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+
+ if (SYS_WARN((id & ~0x0400) != wsm_cmd)) {
+ /* Note that any non-zero is a fatal retcode. */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ switch (id) {
+ case 0x0409:
+ /* Note that wsm_arg can be NULL in case of timeout in
+ * wsm_cmd_send(). */
+ if (likely(wsm_arg))
+ ret = wsm_configuration_confirm(hw_priv,
+ wsm_arg,
+ &wsm_buf);
+ break;
+ case 0x0405:
+ if (likely(wsm_arg))
+ ret = wsm_read_mib_confirm(hw_priv, wsm_arg,
+ &wsm_buf);
+ break;
+ case 0x0406:
+ if (likely(wsm_arg))
+ ret = wsm_write_mib_confirm(hw_priv, wsm_arg,
+ &wsm_buf,
+ interface_link_id);
+ break;
+ case 0x040B:
+ if (likely(wsm_arg))
+ ret = wsm_join_confirm(hw_priv, wsm_arg, &wsm_buf);
+ if (ret)
+ wsm_printk(XRADIO_DBG_WARN, "Join confirm Failed!\n");
+ break;
+ case 0x040E: /* 11K measure*/
+ if (likely(wsm_arg))
+ ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf);
+ if (ret)
+ wsm_printk(XRADIO_DBG_ERROR, "[***HL***]11K Confirm Error\n");
+
+ break;
+
+#ifdef MCAST_FWDING
+ case 0x0423: /* req buffer cfm*/
+ if (likely(wsm_arg)){
+ xradio_for_each_vif(hw_priv, priv, i) {
+ if (priv && (priv->join_status == XRADIO_JOIN_STATUS_AP))
+ ret = wsm_request_buffer_confirm(priv,
+ wsm_arg, &wsm_buf);
+ }
+ }
+ break;
+#endif
+ case 0x0407: /* start-scan */
+#ifdef ROAM_OFFLOAD
+ if (hw_priv->auto_scanning) {
+ if (atomic_read(&hw_priv->scan.in_progress)) {
+ hw_priv->auto_scanning = 0;
+ }
+ else {
+ wsm_oper_unlock(hw_priv);
+ up(&hw_priv->scan.lock);
+ }
+ }
+#endif /*ROAM_OFFLOAD*/
+ case 0x0408: /* stop-scan */
+ case 0x040A: /* wsm_reset */
+ case 0x040C: /* add_key */
+ case 0x040D: /* remove_key */
+ case 0x0410: /* wsm_set_pm */
+ case 0x0411: /* set_bss_params */
+ case 0x0412: /* set_tx_queue_params */
+ case 0x0413: /* set_edca_params */
+ case 0x0416: /* switch_channel */
+ case 0x0417: /* start */
+ case 0x0418: /* beacon_transmit */
+ case 0x0419: /* start_find */
+ case 0x041A: /* stop_find */
+ case 0x041B: /* update_ie */
+ case 0x041C: /* map_link */
+ SYS_WARN(wsm_arg != NULL);
+ ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf);
+ if (ret)
+ wsm_printk(XRADIO_DBG_ERROR,
+ "wsm_generic_confirm "
+ "failed for request 0x%.4X.\n",
+ id & ~0x0400);
+ break;
+ default:
+ SYS_BUG(1);
+ }
+
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ hw_priv->wsm_cmd.ret = ret;
+ hw_priv->wsm_cmd.done = 1;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+ ret = 0; /* Error response from device should ne stop BH. */
+
+ wake_up(&hw_priv->wsm_cmd_wq);
+ } else if (id & 0x0800) {
+ switch (id) {
+ case 0x0801:
+ ret = wsm_startup_indication(hw_priv, &wsm_buf);
+ break;
+ case 0x0804:
+ ret = wsm_receive_indication(hw_priv, interface_link_id,
+ &wsm_buf, skb_p);
+ break;
+ case 0x0805:
+ ret = wsm_event_indication(hw_priv, &wsm_buf,
+ interface_link_id);
+ break;
+ case 0x0807:
+ wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm_measure_cmpl_indication\n");
+// wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm->len = %d\n",wsm->len);
+
+ ret = wsm_measure_cmpl_indication(hw_priv, &wsm_buf);
+ break;
+ case 0x080A:
+ ret = wsm_channel_switch_indication(hw_priv, &wsm_buf);
+ break;
+ case 0x0809:
+ ret = wsm_set_pm_indication(hw_priv, &wsm_buf);
+ break;
+ case 0x0806:
+#ifdef ROAM_OFFLOAD
+ if(hw_priv->auto_scanning && hw_priv->frame_rcvd) {
+ struct xradio_vif *priv;
+ hw_priv->frame_rcvd = 0;
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
+ if (unlikely(!priv)) {
+ SYS_WARN(1);
+ return 0;
+ }
+ spin_unlock(&priv->vif_lock);
+ if (hw_priv->beacon) {
+ struct wsm_scan_complete *scan_cmpl = \
+ (struct wsm_scan_complete *) \
+ ((u8 *)wsm + sizeof(struct wsm_hdr));
+ struct ieee80211_rx_status *rhdr = \
+ IEEE80211_SKB_RXCB(hw_priv->beacon);
+ rhdr->signal = (s8)scan_cmpl->reserved;
+ if (!priv->cqm_use_rssi) {
+ rhdr->signal = rhdr->signal / 2 - 110;
+ }
+ if (!hw_priv->beacon_bkp)
+ hw_priv->beacon_bkp = \
+ skb_copy(hw_priv->beacon, GFP_ATOMIC);
+ ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon);
+ hw_priv->beacon = hw_priv->beacon_bkp;
+
+ hw_priv->beacon_bkp = NULL;
+ }
+ wsm_printk(XRADIO_DBG_MSG, \
+ "Send Testmode Event.\n");
+ xradio_testmode_event(priv->hw->wiphy,
+ NL80211_CMD_NEW_SCAN_RESULTS, 0,
+ 0, GFP_KERNEL);
+
+ }
+#endif /*ROAM_OFFLOAD*/
+ ret = wsm_scan_complete_indication(hw_priv, &wsm_buf);
+ break;
+ case 0x080B:
+ ret = wsm_find_complete_indication(hw_priv, &wsm_buf);
+ break;
+ case 0x080C:
+ ret = wsm_suspend_resume_indication(hw_priv,
+ interface_link_id, &wsm_buf);
+ break;
+ case 0x080E:
+ wsm_printk(XRADIO_DBG_MSG, "wsm_debug_indication");
+ ret = wsm_debug_indication(hw_priv, &wsm_buf);
+ break;
+
+ default:
+ wsm_printk(XRADIO_DBG_ERROR, "unknown Indmsg ID=0x%04x,len=%d\n",
+ wsm->id, wsm->len);
+ break;
+ }
+ } else {
+ SYS_WARN(1);
+ ret = -EINVAL;
+ }
+out:
+ return ret;
+}
+
+static bool wsm_handle_tx_data(struct xradio_vif *priv,
+ const struct wsm_tx *wsm,
+ const struct ieee80211_tx_info *tx_info,
+ struct xradio_txpriv *txpriv,
+ struct xradio_queue *queue)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+#ifdef P2P_MULTIVIF
+ struct xradio_vif *p2p_if_vif = NULL;
+#endif
+ bool handled = false;
+ const struct ieee80211_hdr *frame =
+ (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset];
+ __le16 fctl = frame->frame_control;
+ enum {
+ doProbe,
+ doDrop,
+ doJoin,
+ doOffchannel,
+ doWep,
+ doTx,
+ } action = doTx;
+
+ hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+#ifdef P2P_MULTIVIF
+ if (priv->if_id == XRWL_GENERIC_IF_ID)
+ p2p_if_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1);
+#endif
+ frame = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset];
+ fctl = frame->frame_control;
+
+ switch (priv->mode) {
+ case NL80211_IFTYPE_STATION:
+ if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING &&
+ priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ ieee80211_is_data(fctl)) {
+ spin_lock(&priv->bss_loss_lock);
+ priv->bss_loss_confirm_id = wsm->packetID;
+ priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMING;
+ spin_unlock(&priv->bss_loss_lock);
+ } else if (unlikely((priv->join_status <= XRADIO_JOIN_STATUS_MONITOR) ||
+ memcmp(frame->addr1, priv->join_bssid,sizeof(priv->join_bssid)))) {
+#ifdef P2P_MULTIVIF
+ if (p2p_if_vif && (p2p_if_vif->join_status > XRADIO_JOIN_STATUS_MONITOR) &&
+ (priv->join_status < XRADIO_JOIN_STATUS_MONITOR)) {
+
+ /* Post group formation, frame transmission on p2p0
+ * interafce should not use offchannel/generic channel.
+ * Instead, the frame should be transmitted on interafce
+ * 1. This is needed by wsc fw.
+ */
+ action = doTx;
+ txpriv->raw_if_id = 1;
+ } else
+#endif
+ if (ieee80211_is_auth(fctl))
+ action = doJoin;
+ else if ((ieee80211_is_deauth(fctl) || ieee80211_is_disassoc(fctl))&&
+ priv->join_status < XRADIO_JOIN_STATUS_MONITOR)
+ action = doDrop; //no need to send deauth when STA-unjoined, yangfh 2014-10-31 16:32:16.
+ else if (ieee80211_is_probe_req(fctl))
+ action = doTx;
+ else if (memcmp(frame->addr1, priv->join_bssid,
+ sizeof(priv->join_bssid)) &&
+ (priv->join_status ==
+ XRADIO_JOIN_STATUS_STA) &&
+ (ieee80211_is_data(fctl))) {
+ action = doDrop;
+ }
+ else if (priv->join_status >=
+ XRADIO_JOIN_STATUS_MONITOR)
+ action = doTx;
+ else if (get_interface_id_scanning(hw_priv) != -1) {
+ wsm_printk(XRADIO_DBG_WARN, "Scan ONGOING dropping"
+ " offchannel eligible frame.\n");
+ action = doDrop;
+ } else {
+ if (ieee80211_is_probe_resp(fctl))
+ action = doDrop;
+ else
+ action = doOffchannel;
+ wsm_printk(XRADIO_DBG_WARN, "Offchannel fctl=0x%04x", fctl);
+ }
+ }
+ break;
+ case NL80211_IFTYPE_AP:
+ if (unlikely(!priv->join_status))
+ action = doDrop;
+ else if (unlikely(!(BIT(txpriv->raw_link_id) &
+ (BIT(0) | priv->link_id_map)))) {
+ wsm_printk(XRADIO_DBG_WARN,
+ "A frame with expired link id "
+ "is dropped.\n");
+ action = doDrop;
+ }
+ if (xradio_queue_get_generation(wsm->packetID) >
+ XRADIO_MAX_REQUEUE_ATTEMPTS) {
+ /* HACK!!! WSM324 firmware has tendency to requeue
+ * multicast frames in a loop, causing performance
+ * drop and high power consumption of the driver.
+ * In this situation it is better just to drop
+ * the problematic frame. */
+ wsm_printk(XRADIO_DBG_WARN,
+ "Too many attempts "
+ "to requeue a frame. "
+ "Frame is dropped, fctl=0x%04x.\n", fctl);
+ action = doDrop;
+ }
+ break;
+ case NL80211_IFTYPE_ADHOC:
+ case NL80211_IFTYPE_MESH_POINT:
+ //STUB();
+ case NL80211_IFTYPE_MONITOR:
+ default:
+ action = doDrop;
+ break;
+ }
+
+ if (action == doTx) {
+ if (unlikely(ieee80211_is_probe_req(fctl))) {
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (hw_priv->enable_advance_scan &&
+ (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (hw_priv->advanceScanElems.scanMode ==
+ XRADIO_SCAN_MEASUREMENT_ACTIVE))
+ /* If Advance Scan is Requested on Active Scan
+ * then transmit the Probe Request */
+ action = doTx;
+ else
+#endif
+ action = doProbe;
+ } else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) &&
+ tx_info->control.hw_key &&
+ unlikely(tx_info->control.hw_key->keyidx !=
+ priv->wep_default_key_id) &&
+ (tx_info->control.hw_key->cipher ==
+ WLAN_CIPHER_SUITE_WEP40 ||
+ tx_info->control.hw_key->cipher ==
+ WLAN_CIPHER_SUITE_WEP104)) {
+ action = doWep;
+ }
+ }
+
+ switch (action) {
+ case doProbe:
+ {
+ /* An interesting FW "feature". Device filters
+ * probe responses.
+ * The easiest way to get it back is to convert
+ * probe request into WSM start_scan command. */
+ wsm_printk(XRADIO_DBG_MSG, \
+ "Convert probe request to scan.\n");
+ wsm_lock_tx_async(hw_priv);
+ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+ queue_delayed_work(hw_priv->workqueue,
+ &hw_priv->scan.probe_work, 0);
+ handled = true;
+ }
+ break;
+ case doDrop:
+ {
+ /* See detailed description of "join" below.
+ * We are dropping everything except AUTH in non-joined mode. */
+ wsm_printk(XRADIO_DBG_MSG, "Drop frame (0x%.4X).\n", fctl);
+#ifdef CONFIG_XRADIO_TESTMODE
+ SYS_BUG(xradio_queue_remove(hw_priv, queue,
+ __le32_to_cpu(wsm->packetID)));
+#else
+ SYS_BUG(xradio_queue_remove(queue,
+ __le32_to_cpu(wsm->packetID)));
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ handled = true;
+ }
+ break;
+ case doJoin:
+ {
+ /* p2p should disconnect when sta try to join a different channel AP,
+ * because no good performance in this case.
+ */
+ struct xradio_vif *p2p_tmp_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1);
+ if (priv->if_id == 0 && p2p_tmp_vif) {
+ if (p2p_tmp_vif->join_status >= XRADIO_JOIN_STATUS_STA &&
+ hw_priv->channel_changed) {
+ wsm_printk(XRADIO_DBG_WARN, "combo with different channels, p2p disconnect.\n");
+ wms_send_disassoc_to_self(hw_priv, p2p_tmp_vif);
+ }
+ }
+
+ /* There is one more interesting "feature"
+ * in FW: it can't do RX/TX before "join".
+ * "Join" here is not an association,
+ * but just a syncronization between AP and STA.
+ * priv->join_status is used only in bh thread and does
+ * not require protection */
+ wsm_printk(XRADIO_DBG_NIY, "Issue join command.\n");
+ wsm_lock_tx_async(hw_priv);
+ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+ if (queue_work(hw_priv->workqueue, &priv->join_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ handled = true;
+ }
+ break;
+ case doOffchannel:
+ {
+ wsm_printk(XRADIO_DBG_MSG, "Offchannel TX request.\n");
+ wsm_lock_tx_async(hw_priv);
+ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+ if (queue_work(hw_priv->workqueue, &priv->offchannel_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ handled = true;
+ }
+ break;
+ case doWep:
+ {
+ wsm_printk(XRADIO_DBG_MSG, "Issue set_default_wep_key.\n");
+ wsm_lock_tx_async(hw_priv);
+ priv->wep_default_key_id = tx_info->control.hw_key->keyidx;
+ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID);
+ if (queue_work(hw_priv->workqueue, &priv->wep_key_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ handled = true;
+ }
+ break;
+ case doTx:
+ {
+#if 0
+ /* Kept for history. If you want to implement wsm->more,
+ * make sure you are able to send a frame after that. */
+ wsm->more = (count > 1) ? 1 : 0;
+ if (wsm->more) {
+ /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * It's undocumented in WSM spec, but XRADIO hangs
+ * if 'more' is set and no TX is performed due to TX
+ * buffers limitation. */
+ if (priv->hw_bufs_used + 1 ==
+ priv->wsm_caps.numInpChBufs)
+ wsm->more = 0;
+ }
+
+ /* BUG!!! FIXME: we can't use 'more' at all: we don't know
+ * future. It could be a request from upper layer with TX lock
+ * requirements (scan, for example). If "more" is set device
+ * will not send data and wsm_tx_lock() will fail...
+ * It's not obvious how to fix this deadlock. Any ideas?
+ * As a workaround more is set to 0. */
+ wsm->more = 0;
+#endif /* 0 */
+
+ if (ieee80211_is_deauth(fctl) &&
+ priv->mode != NL80211_IFTYPE_AP) {
+ /* Shedule unjoin work */
+ wsm_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX).\n");
+#if 0
+ wsm->more = 0;
+#endif /* 0 */
+ wsm_lock_tx_async(hw_priv);
+ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
+ wsm_unlock_tx(hw_priv);
+ }
+ }
+ break;
+ }
+ return handled;
+}
+
+static int xradio_get_prio_queue(struct xradio_vif *priv,
+ u32 link_id_map, int *total)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ static u32 urgent;
+ struct wsm_edca_queue_params *edca;
+ unsigned score, best = -1;
+ int winner = -1;
+ int queued;
+ int i;
+ urgent = BIT(priv->link_id_after_dtim) | BIT(priv->link_id_uapsd);
+
+ /* search for a winner using edca params */
+ for (i = 0; i < 4; ++i) {
+ queued = xradio_queue_get_num_queued(priv,
+ &hw_priv->tx_queue[i],
+ link_id_map);
+ if (!queued)
+ continue;
+ *total += queued;
+ edca = &priv->edca.params[i];
+ score = ((edca->aifns + edca->cwMin) << 16) +
+ (edca->cwMax - edca->cwMin) *
+ (prandom_u32() & 0xFFFF);
+ if (score < best && (winner < 0 || i != 3)) {
+ best = score;
+ winner = i;
+ }
+ }
+
+ /* override winner if bursting */
+ if (winner >= 0 && hw_priv->tx_burst_idx >= 0 &&
+ winner != hw_priv->tx_burst_idx &&
+ !xradio_queue_get_num_queued(priv,
+ &hw_priv->tx_queue[winner],
+ link_id_map & urgent) &&
+ xradio_queue_get_num_queued(priv,
+ &hw_priv->tx_queue[hw_priv->tx_burst_idx],
+ link_id_map))
+ winner = hw_priv->tx_burst_idx;
+
+ return winner;
+}
+
+static int wsm_get_tx_queue_and_mask(struct xradio_vif *priv,
+ struct xradio_queue **queue_p,
+ u32 *tx_allowed_mask_p,
+ bool *more)
+{
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+ int idx;
+ u32 tx_allowed_mask;
+ int total = 0;
+
+ /* Search for a queue with multicast frames buffered */
+ if (priv->tx_multicast) {
+ tx_allowed_mask = BIT(priv->link_id_after_dtim);
+ idx = xradio_get_prio_queue(priv,
+ tx_allowed_mask, &total);
+ if (idx >= 0) {
+ *more = total > 1;
+ goto found;
+ }
+ }
+
+ /* Search for unicast traffic */
+ tx_allowed_mask = ~priv->sta_asleep_mask;
+ tx_allowed_mask |= BIT(priv->link_id_uapsd);
+ if (priv->sta_asleep_mask) {
+ tx_allowed_mask |= priv->pspoll_mask;
+ tx_allowed_mask &= ~BIT(priv->link_id_after_dtim);
+ } else {
+ tx_allowed_mask |= BIT(priv->link_id_after_dtim);
+ }
+ idx = xradio_get_prio_queue(priv,
+ tx_allowed_mask, &total);
+ if (idx < 0)
+ return -ENOENT;
+
+found:
+ *queue_p = &hw_priv->tx_queue[idx];
+ *tx_allowed_mask_p = tx_allowed_mask;
+ return 0;
+}
+
+int wsm_get_tx(struct xradio_common *hw_priv, u8 **data,
+ size_t *tx_len, int *burst, int *vif_selected)
+{
+ struct wsm_tx *wsm = NULL;
+ struct ieee80211_tx_info *tx_info;
+ struct xradio_queue *queue = NULL;
+ int queue_num;
+ u32 tx_allowed_mask = 0;
+ struct xradio_txpriv *txpriv = NULL;
+#ifdef P2P_MULTIVIF
+ int first = 1;
+ int tmp_if_id = -1;
+#endif
+ /*
+ * Count was intended as an input for wsm->more flag.
+ * During implementation it was found that wsm->more
+ * is not usable, see details above. It is kept just
+ * in case you would like to try to implement it again.
+ */
+ int count = 0;
+#ifdef P2P_MULTIVIF
+ int if_pending = XRWL_MAX_VIFS - 1;
+#else
+ int if_pending = 1;
+#endif
+
+ /* More is used only for broadcasts. */
+ bool more = false;
+
+ if (hw_priv->wsm_cmd.ptr) {
+ ++count;
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ SYS_BUG(!hw_priv->wsm_cmd.ptr);
+ *data = hw_priv->wsm_cmd.ptr;
+ *tx_len = hw_priv->wsm_cmd.len;
+ *burst = 1;
+ *vif_selected = -1;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+ } else {
+ for (;;) {
+ int ret;
+ struct xradio_vif *priv;
+#if 0
+ int num_pending_vif0, num_pending_vif1;
+#endif
+ if (atomic_add_return(0, &hw_priv->tx_lock))
+ break;
+ /* Keep one buffer reserved for commands. Note
+ that, hw_bufs_used has already been incremented
+ before reaching here. */
+ if (hw_priv->hw_bufs_used >=
+ hw_priv->wsm_caps.numInpChBufs)
+ break;
+#ifdef P2P_MULTIVIF
+ if (first) {
+ tmp_if_id = hw_priv->if_id_selected;
+ hw_priv->if_id_selected = 2;
+ }
+#endif
+ priv = wsm_get_interface_for_tx(hw_priv);
+ /* go to next interface ID to select next packet */
+#ifdef P2P_MULTIVIF
+ if (first) {
+ hw_priv->if_id_selected = tmp_if_id;
+ first = 0;
+ } else
+#endif
+ hw_priv->if_id_selected ^= 1;
+
+ /* There might be no interface before add_interface
+ * call */
+ if (!priv) {
+ if (if_pending) {
+#ifdef P2P_MULTIVIF
+ if_pending--;
+#else
+ if_pending = 0;
+#endif
+ continue;
+ }
+ break;
+ }
+
+#if 0
+ if (((priv->if_id == 0) &&
+ (hw_priv->hw_bufs_used_vif[0] >=
+ XRWL_FW_VIF0_THROTTLE)) ||
+ ((priv->if_id == 1) &&
+ (hw_priv->hw_bufs_used_vif[1] >=
+ XRWL_FW_VIF1_THROTTLE))) {
+ spin_unlock(&priv->vif_lock);
+ if (if_pending) {
+ if_pending = 0;
+ continue;
+ }
+ break;
+ }
+#endif
+
+ /* This can be removed probably: xradio_vif will not
+ * be in hw_priv->vif_list (as returned from
+ * wsm_get_interface_for_tx) until it's fully
+ * enabled, so statement above will take case of that*/
+ if (!atomic_read(&priv->enabled)) {
+ spin_unlock(&priv->vif_lock);
+ break;
+ }
+
+ /* TODO:COMBO: Find the next interface for which
+ * packet needs to be found */
+ spin_lock_bh(&priv->ps_state_lock);
+ ret = wsm_get_tx_queue_and_mask(priv, &queue,
+ &tx_allowed_mask, &more);
+ queue_num = queue - hw_priv->tx_queue;
+
+ if (priv->buffered_multicasts &&
+ (ret || !more) &&
+ (priv->tx_multicast ||
+ !priv->sta_asleep_mask)) {
+ priv->buffered_multicasts = false;
+ if (priv->tx_multicast) {
+ priv->tx_multicast = false;
+ queue_work(hw_priv->workqueue,
+ &priv->multicast_stop_work);
+ }
+ }
+
+ spin_unlock_bh(&priv->ps_state_lock);
+
+ if (ret) {
+ spin_unlock(&priv->vif_lock);
+#ifdef P2P_MULTIVIF
+ if (if_pending) {
+#else
+ if (if_pending == 1) {
+#endif
+#ifdef P2P_MULTIVIF
+ if_pending--;
+#else
+ if_pending = 0;
+#endif
+ continue;
+ }
+ break;
+ }
+
+ if (xradio_queue_get(queue,
+ priv->if_id,
+ tx_allowed_mask,
+ &wsm, &tx_info, &txpriv)) {
+ spin_unlock(&priv->vif_lock);
+ if_pending = 0;
+ continue;
+ }
+
+#ifdef ROC_DEBUG
+#ifndef P2P_MULTIVIF
+ {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+
+ wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d,"
+ " if_id %d\n",
+ hdr->frame_control,
+ txpriv->offchannel_if_id,
+ priv->if_id);
+ }
+#else
+ {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+
+ wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d,"
+ " if_id %d\n",
+ hdr->frame_control,
+ txpriv->raw_if_id,
+ priv->if_id);
+ }
+#endif
+#endif
+
+ if (wsm_handle_tx_data(priv, wsm,
+ tx_info, txpriv, queue)) {
+ spin_unlock(&priv->vif_lock);
+ if_pending = 0;
+ continue; /* Handled by WSM */
+ }
+
+ wsm->hdr.id &= __cpu_to_le16(
+ ~WSM_TX_IF_ID(WSM_TX_IF_ID_MAX));
+#ifdef P2P_MULTIVIF
+ if (txpriv->raw_if_id)
+ wsm->hdr.id |= cpu_to_le16(
+ WSM_TX_IF_ID(txpriv->raw_if_id));
+#else
+ if (txpriv->offchannel_if_id)
+ wsm->hdr.id |= cpu_to_le16(
+ WSM_TX_IF_ID(txpriv->offchannel_if_id));
+#endif
+ else
+ wsm->hdr.id |= cpu_to_le16(
+ WSM_TX_IF_ID(priv->if_id));
+
+ *vif_selected = priv->if_id;
+#ifdef ROC_DEBUG
+/* remand the roc debug. */
+#ifndef P2P_MULTIVIF
+
+ {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+
+ wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d,"
+ " if_id %d\n",
+ hdr->frame_control,
+ txpriv->offchannel_if_id,
+ priv->if_id);
+ }
+#else
+ {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+
+ wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d,"
+ " if_id %d\n",
+ hdr->frame_control,
+ txpriv->raw_if_id,
+ priv->if_id);
+ }
+#endif
+#endif
+
+ priv->pspoll_mask &= ~BIT(txpriv->raw_link_id);
+
+ *data = (u8 *)wsm;
+ *tx_len = __le16_to_cpu(wsm->hdr.len);
+
+ /* allow bursting if txop is set */
+ if (priv->edca.params[queue_num].txOpLimit)
+ *burst = min(*burst,
+ (int)xradio_queue_get_num_queued(priv,
+ queue, tx_allowed_mask) + 1);
+ else
+ *burst = 1;
+
+ /* store index of bursting queue */
+ if (*burst > 1)
+ hw_priv->tx_burst_idx = queue_num;
+ else
+ hw_priv->tx_burst_idx = -1;
+
+ if (more) {
+ struct ieee80211_hdr *hdr =
+ (struct ieee80211_hdr *)
+ &((u8 *)wsm)[txpriv->offset];
+ if(strstr(&priv->ssid[0], "6.1.12")) {
+ if(hdr->addr1[0] & 0x01 ) {
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+ }
+ else {
+ /* more buffered multicast/broadcast frames
+ * ==> set MoreData flag in IEEE 802.11 header
+ * to inform PS STAs */
+ hdr->frame_control |=
+ cpu_to_le16(IEEE80211_FCTL_MOREDATA);
+ }
+ }
+ wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%d) %p %c\n",
+ 0x0004, *tx_len, *data,
+ wsm->more ? 'M' : ' ');
+ ++count;
+ spin_unlock(&priv->vif_lock);
+ break;
+ }
+ }
+
+ return count;
+}
+
+void wsm_txed(struct xradio_common *hw_priv, u8 *data)
+{
+ if (data == hw_priv->wsm_cmd.ptr) {
+ spin_lock(&hw_priv->wsm_cmd.lock);
+ hw_priv->wsm_cmd.ptr = NULL;
+ spin_unlock(&hw_priv->wsm_cmd.lock);
+ }
+}
+
+/* ******************************************************************** */
+/* WSM buffer */
+
+void wsm_buf_init(struct wsm_buf *buf)
+{
+ int size = (SDIO_BLOCK_SIZE<<1); //for sdd file big than SDIO_BLOCK_SIZE
+ SYS_BUG(buf->begin);
+ buf->begin = kmalloc(size, GFP_KERNEL);
+ buf->end = buf->begin ? &buf->begin[size] : buf->begin;
+ wsm_buf_reset(buf);
+}
+
+void wsm_buf_deinit(struct wsm_buf *buf)
+{
+ if(buf->begin)
+ kfree(buf->begin);
+ buf->begin = buf->data = buf->end = NULL;
+}
+
+static void wsm_buf_reset(struct wsm_buf *buf)
+{
+ if (buf->begin) {
+ buf->data = &buf->begin[4];
+ *(u32 *)buf->begin = 0;
+ } else
+ buf->data = buf->begin;
+}
+
+static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size)
+{
+ size_t pos = buf->data - buf->begin;
+ size_t size = pos + extra_size;
+
+
+ if (size & (SDIO_BLOCK_SIZE - 1)) {
+ size &= SDIO_BLOCK_SIZE;
+ size += SDIO_BLOCK_SIZE;
+ }
+
+ buf->begin = krealloc(buf->begin, size, GFP_KERNEL);
+ if (buf->begin) {
+ buf->data = &buf->begin[pos];
+ buf->end = &buf->begin[size];
+ return 0;
+ } else {
+ buf->end = buf->data = buf->begin;
+ return -ENOMEM;
+ }
+}
+
+static struct xradio_vif
+ *wsm_get_interface_for_tx(struct xradio_common *hw_priv)
+{
+ struct xradio_vif *priv = NULL, *i_priv;
+ int i = hw_priv->if_id_selected;
+
+ if ( 1 /*TODO:COMBO*/) {
+ spin_lock(&hw_priv->vif_list_lock);
+ i_priv = hw_priv->vif_list[i] ?
+ xrwl_get_vif_from_ieee80211(hw_priv->vif_list[i]) : NULL;
+ if (i_priv) {
+ priv = i_priv;
+ spin_lock(&priv->vif_lock);
+ }
+ /* TODO:COMBO:
+ * Find next interface based on TX bitmap announced by the FW
+ * Find next interface based on load balancing */
+ spin_unlock(&hw_priv->vif_list_lock);
+ } else {
+ priv = xrwl_hwpriv_to_vifpriv(hw_priv, 0);
+ }
+
+ return priv;
+}
+
+static inline int get_interface_id_scanning(struct xradio_common *hw_priv)
+{
+ if (hw_priv->scan.req || hw_priv->scan.direct_probe)
+ return hw_priv->scan.if_id;
+ else
+ return -1;
+}
diff --git a/drivers/net/wireless/xradio/wsm.h b/drivers/net/wireless/xradio/wsm.h
new file mode 100644
index 00000000..e88314ee
--- /dev/null
+++ b/drivers/net/wireless/xradio/wsm.h
@@ -0,0 +1,2396 @@
+/*
+ * wsm interfaces for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef XRADIO_WSM_H_INCLUDED
+#define XRADIO_WSM_H_INCLUDED
+
+#include <linux/spinlock.h>
+
+struct xradio_common;
+
+/* Bands */
+/* Radio band 2.412 -2.484 GHz. */
+#define WSM_PHY_BAND_2_4G (0)
+
+/* Radio band 4.9375-5.8250 GHz. */
+#define WSM_PHY_BAND_5G (1)
+
+/* Transmit rates */
+/* 1 Mbps ERP-DSSS */
+#define WSM_TRANSMIT_RATE_1 (0)
+
+/* 2 Mbps ERP-DSSS */
+#define WSM_TRANSMIT_RATE_2 (1)
+
+/* 5.5 Mbps ERP-CCK, ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_5 (2) */
+
+/* 11 Mbps ERP-CCK, ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_11 (3) */
+
+/* 22 Mbps ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_22 (4) */
+
+/* 33 Mbps ERP-PBCC (Not supported) */
+/* #define WSM_TRANSMIT_RATE_33 (5) */
+
+/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_6 (6)
+
+/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_9 (7)
+
+/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_12 (8)
+
+/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_18 (9)
+
+/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_24 (10)
+
+/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_36 (11)
+
+/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_48 (12)
+
+/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_54 (13)
+
+/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_6 (14)
+
+/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_13 (15)
+
+/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_19 (16)
+
+/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */
+#define WSM_TRANSMIT_RATE_HT_26 (17)
+
+/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_39 (18)
+
+/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */
+#define WSM_TRANSMIT_RATE_HT_52 (19)
+
+/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */
+#define WSM_TRANSMIT_RATE_HT_58 (20)
+
+/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */
+#define WSM_TRANSMIT_RATE_HT_65 (21)
+
+/* Scan types */
+/* Foreground scan */
+#define WSM_SCAN_TYPE_FOREGROUND (0)
+
+/* Background scan */
+#define WSM_SCAN_TYPE_BACKGROUND (1)
+
+/* Auto scan */
+#define WSM_SCAN_TYPE_AUTO (2)
+
+/* Scan flags */
+/* Forced background scan means if the station cannot */
+/* enter the power-save mode, it shall force to perform a */
+/* background scan. Only valid when ScanType is */
+/* background scan. */
+#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0))
+
+/* The WLAN device scans one channel at a time so */
+/* that disturbance to the data traffic is minimized. */
+#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1))
+
+/* Preamble Type. Long if not set. */
+#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2))
+
+/* 11n Tx Mode. Mixed if not set. */
+#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3))
+
+#define WSM_FLAG_MAC_INSTANCE_1 (BIT(4))
+
+#define WSM_FLAG_MAC_INSTANCE_0 (~(BIT(4)))
+
+/* Scan constraints */
+/* Maximum number of channels to be scanned. */
+#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48)
+
+/* The maximum number of SSIDs that the device can scan for. */
+#define WSM_SCAN_MAX_NUM_OF_SSIDS (2)
+#ifdef CONFIG_XRADIO_TESTMODE
+/* Transmit flags */
+/* Start Expiry time from the receipt of tx request */
+#define WSM_TX_FLAG_EXPIRY_TIME (BIT(0))
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+/* Power management modes */
+/* 802.11 Active mode */
+#define WSM_PSM_ACTIVE (0)
+
+/* 802.11 PS mode */
+#define WSM_PSM_PS BIT(0)
+
+/* Fast Power Save bit */
+#define WSM_PSM_FAST_PS_FLAG BIT(7)
+
+/* Dynamic aka Fast power save */
+#define WSM_PSM_FAST_PS (BIT(0) | BIT(7))
+
+/* Undetermined */
+/* Note : Undetermined status is reported when the */
+/* NULL data frame used to advertise the PM mode to */
+/* the AP at Pre or Post Background Scan is not Acknowledged */
+#define WSM_PSM_UNKNOWN BIT(1)
+
+/* Queue IDs */
+/* best effort/legacy */
+#define WSM_QUEUE_BEST_EFFORT (0)
+
+/* background */
+#define WSM_QUEUE_BACKGROUND (1)
+
+/* video */
+#define WSM_QUEUE_VIDEO (2)
+
+/* voice */
+#define WSM_QUEUE_VOICE (3)
+
+/* HT TX parameters */
+/* Non-HT */
+#define WSM_HT_TX_NON_HT (0)
+
+/* Mixed format */
+#define WSM_HT_TX_MIXED (1)
+
+/* Greenfield format */
+#define WSM_HT_TX_GREENFIELD (2)
+
+/* STBC allowed */
+#define WSM_HT_TX_STBC (BIT(7))
+
+/* EPTA prioirty flags for BT Coex */
+/* default epta priority */
+#define WSM_EPTA_PRIORITY_DEFAULT 4
+/* use for normal data */
+#define WSM_EPTA_PRIORITY_DATA 4
+/* use for connect/disconnect/roaming*/
+#define WSM_EPTA_PRIORITY_MGT 5
+/* use for action frames */
+#define WSM_EPTA_PRIORITY_ACTION 5
+/* use for AC_VI data */
+#define WSM_EPTA_PRIORITY_VIDEO 5
+/* use for AC_VO data */
+#define WSM_EPTA_PRIORITY_VOICE 6
+/* use for EAPOL exchange */
+#define WSM_EPTA_PRIORITY_EAPOL 7
+
+/* TX status */
+/* Frame was sent aggregated */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_AGGREGATION (BIT(0))
+
+/* Host should requeue this frame later. */
+/* Valid only when status is WSM_REQUEUE. */
+#define WSM_TX_STATUS_REQUEUE (BIT(1))
+
+/* Normal Ack */
+#define WSM_TX_STATUS_NORMAL_ACK (0<<2)
+
+/* No Ack */
+#define WSM_TX_STATUS_NO_ACK (1<<2)
+
+/* No explicit acknowledgement */
+#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2)
+
+/* Block Ack */
+/* Only valid for WSM_SUCCESS status. */
+#define WSM_TX_STATUS_BLOCK_ACK (3<<2)
+
+/* RX status */
+/* Unencrypted */
+#define WSM_RX_STATUS_UNENCRYPTED (0<<0)
+
+/* WEP */
+#define WSM_RX_STATUS_WEP (1<<0)
+
+/* TKIP */
+#define WSM_RX_STATUS_TKIP (2<<0)
+
+/* AES */
+#define WSM_RX_STATUS_AES (3<<0)
+
+/* WAPI */
+#define WSM_RX_STATUS_WAPI (4<<0)
+
+/* Macro to fetch encryption subfield. */
+#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07)
+
+/* Frame was part of an aggregation */
+#define WSM_RX_STATUS_AGGREGATE (BIT(3))
+
+/* Frame was first in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4))
+
+/* Frame was last in the aggregation */
+#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5))
+
+/* Indicates a defragmented frame */
+#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6))
+
+/* Indicates a Beacon frame */
+#define WSM_RX_STATUS_BEACON (BIT(7))
+
+/* Indicates STA bit beacon TIM field */
+#define WSM_RX_STATUS_TIM (BIT(8))
+
+/* Indicates Beacon frame's virtual bitmap contains multicast bit */
+#define WSM_RX_STATUS_MULTICAST (BIT(9))
+
+/* Indicates frame contains a matching SSID */
+#define WSM_RX_STATUS_MATCHING_SSID (BIT(10))
+
+/* Indicates frame contains a matching BSSI */
+#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11))
+
+/* Indicates More bit set in Framectl field */
+#define WSM_RX_STATUS_MORE_DATA (BIT(12))
+
+/* Indicates frame received during a measurement process */
+#define WSM_RX_STATUS_MEASUREMENT (BIT(13))
+
+/* Indicates frame received as an HT packet */
+#define WSM_RX_STATUS_HT (BIT(14))
+
+/* Indicates frame received with STBC */
+#define WSM_RX_STATUS_STBC (BIT(15))
+
+/* Indicates Address 1 field matches dot11StationId */
+#define WSM_RX_STATUS_ADDRESS1 (BIT(16))
+
+/* Indicates Group address present in the Address 1 field */
+#define WSM_RX_STATUS_GROUP (BIT(17))
+
+/* Indicates Broadcast address present in the Address 1 field */
+#define WSM_RX_STATUS_BROADCAST (BIT(18))
+
+/* Indicates group key used with encrypted frames */
+#define WSM_RX_STATUS_GROUP_KEY (BIT(19))
+
+/* Macro to fetch encryption key index. */
+#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F)
+
+/* Frame Control field starts at Frame offset + 2 */
+#define WSM_TX_2BYTES_SHIFT (BIT(7))
+
+/* Join mode */
+/* IBSS */
+#define WSM_JOIN_MODE_IBSS (0)
+
+/* BSS */
+#define WSM_JOIN_MODE_BSS (1)
+
+/* PLCP preamble type */
+/* For long preamble */
+#define WSM_JOIN_PREAMBLE_LONG (0)
+
+/* For short preamble (Long for 1Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT (1)
+
+/* For short preamble (Long for 1 and 2Mbps) */
+#define WSM_JOIN_PREAMBLE_SHORT_2 (2)
+
+/* Join flags */
+/* Unsynchronized */
+#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0)
+/* The BSS owner is a P2P GO */
+#define WSM_JOIN_FLAGS_P2P_GO BIT(1)
+/* Force to join BSS with the BSSID and the
+ * SSID specified without waiting for beacons. The
+ * ProbeForJoin parameter is ignored. */
+#define WSM_JOIN_FLAGS_FORCE BIT(2)
+/* Give probe request/response higher
+ * priority over the BT traffic */
+#define WSM_JOIN_FLAGS_PRIO BIT(3)
+
+/* Key types */
+#define WSM_KEY_TYPE_WEP_DEFAULT (0)
+#define WSM_KEY_TYPE_WEP_PAIRWISE (1)
+#define WSM_KEY_TYPE_TKIP_GROUP (2)
+#define WSM_KEY_TYPE_TKIP_PAIRWISE (3)
+#define WSM_KEY_TYPE_AES_GROUP (4)
+#define WSM_KEY_TYPE_AES_PAIRWISE (5)
+#define WSM_KEY_TYPE_WAPI_GROUP (6)
+#define WSM_KEY_TYPE_WAPI_PAIRWISE (7)
+
+/* Key indexes */
+#define WSM_KEY_MAX_INDEX (10)
+
+/* ACK policy */
+#define WSM_ACK_POLICY_NORMAL (0)
+#define WSM_ACK_POLICY_NO_ACK (1)
+
+/* Start modes */
+#define WSM_START_MODE_AP (0) /* Mini AP */
+#define WSM_START_MODE_P2P_GO (1) /* P2P GO */
+#define WSM_START_MODE_P2P_DEV (2) /* P2P device */
+
+/* SetAssociationMode MIB flags */
+#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0))
+#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1))
+#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2))
+#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3))
+#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4))
+
+/* RcpiRssiThreshold MIB flags */
+#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0))
+#define WSM_RCPI_RSSI_USE_RSSI (BIT(1))
+#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2))
+#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3))
+
+/* Update-ie constants */
+#define WSM_UPDATE_IE_BEACON (BIT(0))
+#define WSM_UPDATE_IE_PROBE_RESP (BIT(1))
+#define WSM_UPDATE_IE_PROBE_REQ (BIT(2))
+
+/* WSM events */
+/* Error */
+#define WSM_EVENT_ERROR (0)
+
+/* BSS lost */
+#define WSM_EVENT_BSS_LOST (1)
+
+/* BSS regained */
+#define WSM_EVENT_BSS_REGAINED (2)
+
+/* Radar detected */
+#define WSM_EVENT_RADAR_DETECTED (3)
+
+/* RCPI or RSSI threshold triggered */
+#define WSM_EVENT_RCPI_RSSI (4)
+
+/* BT inactive */
+#define WSM_EVENT_BT_INACTIVE (5)
+
+/* BT active */
+#define WSM_EVENT_BT_ACTIVE (6)
+
+#define WSM_EVENT_PS_MODE_ERROR (7)
+
+#define WSM_EVENT_INACTIVITY (9)
+
+/* MAC Addr Filter */
+#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030
+
+/* MIB IDs */
+/* 4.1 dot11StationId */
+#define WSM_MIB_ID_DOT11_STATION_ID 0x0000
+
+/* 4.2 dot11MaxtransmitMsduLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001
+
+/* 4.3 dot11MaxReceiveLifeTime */
+#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002
+
+/* 4.4 dot11SlotTime */
+#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003
+
+/* 4.5 dot11GroupAddressesTable */
+#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004
+#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8
+
+/* 4.6 dot11WepDefaultKeyId */
+#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005
+
+/* 4.7 dot11CurrentTxPowerLevel */
+#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006
+
+/* 4.8 dot11RTSThreshold */
+#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007
+
+/* Huanglu add for firmware debug control */
+#define WSM_MIB_ID_FW_DEBUG_CONTROL 0x0008
+
+/* yangfh add for read/write registers from firmware*/
+#define WSM_MIB_ID_RW_FW_REG 0x0009
+
+/* yangfh add for Set max number of mpdus in a-mpdu*/
+#define WSM_MIB_ID_SET_AMPDU_NUM 0x000a
+
+/* Huanglu add for tx-ampdu-len-adaption */
+#define WSM_MIB_ID_SET_TALA_PARA 0x000b
+
+/* yangfh add for set TPA param */
+#define WSM_MIB_ID_SET_TPA_PARAM 0x000c
+
+/* 4.9 NonErpProtection */
+#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000
+
+/* 4.10 ArpIpAddressesTable */
+#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001
+#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1
+
+/* 4.11 TemplateFrame */
+#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002
+
+/* 4.12 RxFilter */
+#define WSM_MIB_ID_RX_FILTER 0x1003
+
+/* 4.13 BeaconFilterTable */
+#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004
+
+/* 4.14 BeaconFilterEnable */
+#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005
+
+/* 4.15 OperationalPowerMode */
+#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006
+
+/* 4.16 BeaconWakeUpPeriod */
+#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007
+
+/* 4.17 RcpiRssiThreshold */
+#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009
+
+/* 4.18 StatisticsTable */
+#define WSM_MIB_ID_STATISTICS_TABLE 0x100A
+
+/* 4.19 IbssPsConfig */
+#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B
+
+/* 4.20 CountersTable */
+#define WSM_MIB_ID_COUNTERS_TABLE 0x100C
+#define WSM_MIB_ID_AMPDUCOUNTERS_TABLE 0x1036
+#define WSM_MIB_ID_TXPIPE_TABLE 0x1037
+#define WSM_MIB_ID_BACKOFF_DBG 0x1038
+#define WSM_MIB_ID_BACKOFF_CTRL 0x1039
+
+//add yangfh for requery packet status
+#define WSM_MIB_ID_REQ_PKT_STATUS 0x1040
+
+//add yangfh for TPA debug informations
+#define WSM_MIB_ID_TPA_DEBUG_INFO 0x1041
+
+//add yangfh for tx power informations
+#define WSM_MIB_ID_TX_POWER_INFO 0x1042
+
+//add yangfh for some hardware information
+#define WSM_MIB_ID_HW_INFO 0x1043
+
+/* 4.21 BlockAckPolicy */
+#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E
+
+/* 4.22 OverrideInternalTxRate */
+#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F
+
+/* 4.23 SetAssociationMode */
+#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010
+
+/* 4.24 UpdateEptaConfigData */
+#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011
+
+/* 4.25 SelectCcaMethod */
+#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012
+
+/* 4.26 SetUpasdInformation */
+#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013
+
+/* 4.27 SetAutoCalibrationMode WBF00004073 */
+#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015
+
+/* 4.28 SetTxRateRetryPolicy */
+#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016
+
+/* 4.29 SetHostMessageTypeFilter */
+#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017
+
+/* 4.30 P2PFindInfo */
+#define WSM_MIB_ID_P2P_FIND_INFO 0x1018
+
+/* 4.31 P2PPsModeInfo */
+#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A
+
+/* 4.33 SetUDPPortDataFrameFilter */
+#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B
+
+/* 4.34 SetMagicDataFrameFilter */
+#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C
+#define WSM_MIB_ID_SET_HOST_SLEEP 0x1050
+
+/* This is the end of specification. */
+
+/* 4.35 P2PDeviceInfo */
+#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D
+
+/* 4.36 SetWCDMABand */
+#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E
+
+/* 4.37 GroupTxSequenceCounter */
+#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F
+
+/* 4.38 ProtectedMgmtPolicy */
+#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020
+
+/* 4.39 SetHtProtection */
+#define WSM_MID_ID_SET_HT_PROTECTION 0x1021
+
+/* 4.40 GPIO Command */
+#define WSM_MIB_ID_GPIO_COMMAND 0x1022
+
+/* 4.41 TSF Counter Value */
+#define WSM_MIB_ID_TSF_COUNTER 0x1023
+
+/* Test Purposes Only */
+#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D
+
+/* 4.42 UseMultiTxConfMessage */
+#define WSM_MIB_USE_MULTI_TX_CONF 0x1024
+
+/* 4.43 Keep-alive period */
+#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025
+
+/* 4.44 Disable BSSID filter */
+#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026
+
+/* Inactivity */
+#define WSM_MIB_ID_SET_INACTIVITY 0x1035
+
+/* MAC Addr Filter */
+#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030
+
+#ifdef MCAST_FWDING
+/* 4.51 Set Forwarding Offload */
+#define WSM_MIB_ID_FORWARDING_OFFLOAD 0x1033
+#endif
+
+/* Frame template types */
+#define WSM_FRAME_TYPE_PROBE_REQUEST (0)
+#define WSM_FRAME_TYPE_BEACON (1)
+#define WSM_FRAME_TYPE_NULL (2)
+#define WSM_FRAME_TYPE_QOS_NULL (3)
+#define WSM_FRAME_TYPE_PS_POLL (4)
+#define WSM_FRAME_TYPE_PROBE_RESPONSE (5)
+#define WSM_FRAME_TYPE_ARP_REPLY (6)
+
+#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */
+
+/* Status */
+/* The WSM firmware has completed a request */
+/* successfully. */
+#define WSM_STATUS_SUCCESS (0)
+
+/* This is a generic failure code if other error codes do */
+/* not apply. */
+#define WSM_STATUS_FAILURE (1)
+
+/* A request contains one or more invalid parameters. */
+#define WSM_INVALID_PARAMETER (2)
+
+/* The request cannot perform because the device is in */
+/* an inappropriate mode. */
+#define WSM_ACCESS_DENIED (3)
+
+/* The frame received includes a decryption error. */
+#define WSM_STATUS_DECRYPTFAILURE (4)
+
+/* A MIC failure is detected in the received packets. */
+#define WSM_STATUS_MICFAILURE (5)
+
+/* The transmit request failed due to retry limit being */
+/* exceeded. */
+#define WSM_STATUS_RETRY_EXCEEDED (6)
+
+/* The transmit request failed due to MSDU life time */
+/* being exceeded. */
+#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7)
+
+/* The link to the AP is lost. */
+#define WSM_STATUS_LINK_LOST (8)
+
+/* No key was found for the encrypted frame */
+#define WSM_STATUS_NO_KEY_FOUND (9)
+
+/* Jammer was detected when transmitting this frame */
+#define WSM_STATUS_JAMMER_DETECTED (10)
+
+/* The message should be requeued later. */
+/* This is applicable only to Transmit */
+#define WSM_REQUEUE (11)
+
+/* Advanced filtering options */
+#define WSM_MAX_FILTER_ELEMENTS (4)
+
+#define WSM_FILTER_ACTION_IGNORE (0)
+#define WSM_FILTER_ACTION_FILTER_IN (1)
+#define WSM_FILTER_ACTION_FILTER_OUT (2)
+
+#define WSM_FILTER_PORT_TYPE_DST (0)
+#define WSM_FILTER_PORT_TYPE_SRC (1)
+
+
+
+struct wsm_hdr {
+ __le16 len;
+ __le16 id;
+};
+
+#define WSM_TX_SEQ_MAX (7)
+#define WSM_TX_SEQ(seq) \
+ ((seq & WSM_TX_SEQ_MAX) << 13)
+#define WSM_TX_LINK_ID_MAX (0x0F)
+#define WSM_TX_LINK_ID(link_id) \
+ ((link_id & WSM_TX_LINK_ID_MAX) << 6)
+
+#define WSM_TX_IF_ID_MAX (0x0F)
+#define WSM_TX_IF_ID(if_id) \
+ ((if_id & WSM_TX_IF_ID_MAX) << 6)
+
+#define MAX_BEACON_SKIP_TIME_MS 1000
+
+#ifdef FPGA_SETUP
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 9 / 2)
+#else
+#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 20 / 2)
+#endif
+#define WSM_CMD_EXTENDED_TIMEOUT (HZ * 20 / 2)
+
+#define WSM_RI_GET_PEER_ID_FROM_FLAGS(_f) (((_f)&(0xF<<25)>>25))
+
+
+/* ******************************************************************** */
+/* WSM capcbility */
+#define WSM_FW_LABEL 128
+struct wsm_caps {
+ u16 numInpChBufs;
+ u16 sizeInpChBuf;
+ u16 hardwareId;
+ u16 hardwareSubId;
+ u16 firmwareCap;
+ u16 firmwareType;
+ u16 firmwareApiVer;
+ u16 firmwareBuildNumber;
+ u16 firmwareVersion;
+ char fw_label[WSM_FW_LABEL+2];
+ int firmwareReady;
+};
+
+/* ******************************************************************** */
+/* WSM commands */
+
+struct wsm_tx_power_range {
+ int min_power_level;
+ int max_power_level;
+ u32 stepping;
+};
+
+/* 3.1 */
+struct wsm_configuration {
+ /* [in] */ u32 dot11MaxTransmitMsduLifeTime;
+ /* [in] */ u32 dot11MaxReceiveLifeTime;
+ /* [in] */ u32 dot11RtsThreshold;
+ /* [in, out] */ u8 *dot11StationId;
+ /* [in] */ const void *dpdData;
+ /* [in] */ size_t dpdData_size;
+ /* [out] */ u8 dot11FrequencyBandsSupported;
+ /* [out] */ u32 supportedRateMask;
+ /* [out] */ struct wsm_tx_power_range txPowerRange[2];
+};
+
+int wsm_configuration(struct xradio_common *hw_priv,
+ struct wsm_configuration *arg,
+ int if_id);
+
+/* 3.3 */
+struct wsm_reset {
+ /* [in] */ int link_id;
+ /* [in] */ bool reset_statistics;
+};
+
+int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg,
+ int if_id);
+
+//add by yangfh
+void wsm_upper_restart(struct xradio_common *hw_priv);
+
+//add by yangfh
+void wsm_query_work(struct work_struct *work);
+
+/* 3.5 */
+int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *buf,
+ size_t buf_size, size_t arg_size);
+
+/* 3.7 */
+int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *buf,
+ size_t buf_size, int if_id);
+
+/* 3.9 */
+struct wsm_ssid {
+ u8 ssid[32];
+ u32 length;
+};
+
+struct wsm_scan_ch {
+ u16 number;
+ u32 minChannelTime;
+ u32 maxChannelTime;
+ u32 txPowerLevel;
+};
+
+/* 3.13 */
+struct wsm_scan_complete {
+ /* WSM_STATUS_... */
+ u32 status;
+
+ /* WSM_PSM_... */
+ u8 psm;
+
+ /* Number of channels that the scan operation completed. */
+ u8 numChannels;
+#ifdef ROAM_OFFLOAD
+ u16 reserved;
+#endif /*ROAM_OFFLOAD*/
+};
+
+typedef void (*wsm_scan_complete_cb) (struct xradio_common *hw_priv,
+ struct wsm_scan_complete *arg);
+
+/* 3.9 */
+struct wsm_scan {
+ /* WSM_PHY_BAND_... */
+ /* [in] */ u8 band;
+
+ /* WSM_SCAN_TYPE_... */
+ /* [in] */ u8 scanType;
+
+ /* WSM_SCAN_FLAG_... */
+ /* [in] */ u8 scanFlags;
+
+ /* WSM_TRANSMIT_RATE_... */
+ /* [in] */ u8 maxTransmitRate;
+
+ /* Interval period in TUs that the device shall the re- */
+ /* execute the requested scan. Max value supported by the device */
+ /* is 256s. */
+ /* [in] */ u32 autoScanInterval;
+
+ /* Number of probe requests (per SSID) sent to one (1) */
+ /* channel. Zero (0) means that none is send, which */
+ /* means that a passive scan is to be done. Value */
+ /* greater than zero (0) means that an active scan is to */
+ /* be done. */
+ /* [in] */ u32 numOfProbeRequests;
+
+ /* Number of channels to be scanned. */
+ /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */
+ /* [in] */ u8 numOfChannels;
+
+ /* Number of SSID provided in the scan command (this */
+ /* is zero (0) in broadcast scan) */
+ /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */
+ /* [in] */ u8 numOfSSIDs;
+
+ /* The delay time (in microseconds) period */
+ /* before sending a probe-request. */
+ /* [in] */ u8 probeDelay;
+
+ /* SSIDs to be scanned [numOfSSIDs]; */
+ /* [in] */ struct wsm_ssid *ssids;
+
+ /* Channels to be scanned [numOfChannels]; */
+ /* [in] */ struct wsm_scan_ch *ch;
+};
+
+int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg,
+ int if_id);
+
+/* 3.11 */
+int wsm_stop_scan(struct xradio_common *hw_priv, int if_id);
+
+/* 3.14 */
+struct wsm_tx_confirm {
+ /* Packet identifier used in wsm_tx. */
+ /* [out] */ u32 packetID;
+
+ /* WSM_STATUS_... */
+ /* [out] */ u32 status;
+
+ /* WSM_TRANSMIT_RATE_... */
+ /* [out] */ u8 txedRate;
+
+ /* The number of times the frame was transmitted */
+ /* without receiving an acknowledgement. */
+ /* [out] */ u8 ackFailures;
+
+ /* WSM_TX_STATUS_... */
+ /* [out] */ u16 flags;
+
+ //rate feed back, add by yangfh
+ /* [out] */ u32 rate_try[3];
+
+ /* The total time in microseconds that the frame spent in */
+ /* the WLAN device before transmission as completed. */
+ /* [out] */ u32 mediaDelay;
+
+ /* The total time in microseconds that the frame spent in */
+ /* the WLAN device before transmission was started. */
+ /* [out] */ u32 txQueueDelay;
+
+ /* [out]*/ u32 link_id;
+
+ /*[out]*/ int if_id;
+};
+
+/* 3.15 */
+typedef void (*wsm_tx_confirm_cb) (struct xradio_common *hw_priv,
+ struct wsm_tx_confirm *arg);
+
+/* Note that ideology of wsm_tx struct is different against the rest of
+ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input
+ * argument for WSM call, but a prepared bytestream to be sent to firmware.
+ * It is filled partly in xradio_tx, partly in low-level WSM code.
+ * Please pay attention once again: ideology is different.
+ *
+ * Legend:
+ * - [in]: xradio_tx must fill this field.
+ * - [wsm]: the field is filled by low-level WSM.
+ */
+struct wsm_tx {
+ /* common WSM header */
+ /* [in/wsm] */ struct wsm_hdr hdr;
+
+ /* Packet identifier that meant to be used in completion. */
+ /* [in] */ __le32 packetID;
+
+ /* WSM_TRANSMIT_RATE_... */
+ /* [in] */ u8 maxTxRate;
+
+ /* WSM_QUEUE_... */
+ /* [in] */ u8 queueId;
+
+ /* True: another packet is pending on the host for transmission. */
+ /* [wsm] */ u8 more;
+
+ /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */
+ /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */
+ /* Bits 3:1 - PTA Priority */
+ /* Bits 6:4 - Tx Rate Retry Policy */
+ /* Bit 7 - Reserved */
+ /* [in] */ u8 flags;
+
+ /* Should be 0. */
+ /* [in] */ __le32 reserved;
+
+ /* The elapsed time in TUs, after the initial transmission */
+ /* of an MSDU, after which further attempts to transmit */
+ /* the MSDU shall be terminated. Overrides the global */
+ /* dot11MaxTransmitMsduLifeTime setting [optional] */
+ /* Device will set the default value if this is 0. */
+ /* [wsm] */ __le32 expireTime;
+
+ /* WSM_HT_TX_... */
+ /* [in] */ __le32 htTxParameters;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */
+#define WSM_TX_EXTRA_HEADROOM (28)
+
+/* 3.16 */
+struct wsm_rx {
+ /* WSM_STATUS_... */
+ /* [out] */ u32 status;
+
+ /* Specifies the channel of the received packet. */
+ /* [out] */ u16 channelNumber;
+
+ /* WSM_TRANSMIT_RATE_... */
+ /* [out] */ u8 rxedRate;
+
+ /* This value is expressed in signed Q8.0 format for */
+ /* RSSI and unsigned Q7.1 format for RCPI. */
+ /* [out] */ u8 rcpiRssi;
+
+ /* WSM_RX_STATUS_... */
+ /* [out] */ u32 flags;
+
+ /* An 802.11 frame. */
+ /* [out] */ void *frame;
+
+ /* Size of the frame */
+ /* [out] */ size_t frame_size;
+
+ /* Link ID */
+ /* [out] */ int link_id;
+ /* [out] */ int if_id;
+};
+
+/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */
+#define WSM_RX_EXTRA_HEADROOM (16)
+
+typedef void (*wsm_rx_cb) (struct xradio_vif *priv, struct wsm_rx *arg,
+ struct sk_buff **skb_p);
+
+/* 3.17 */
+struct wsm_event {
+ /* WSM_STATUS_... */
+ /* [out] */ u32 eventId;
+
+ /* Indication parameters. */
+ /* For error indication, this shall be a 32-bit WSM status. */
+ /* For RCPI or RSSI indication, this should be an 8-bit */
+ /* RCPI or RSSI value. */
+ /* [out] */ u32 eventData;
+};
+
+struct xradio_wsm_event {
+ struct list_head link;
+ struct wsm_event evt;
+ u8 if_id;
+};
+
+/* 3.18 - 3.22 */
+/* Measurement. Skipped for now. Irrelevent. */
+
+typedef void (*wsm_event_cb) (struct xradio_common *hw_priv,
+ struct wsm_event *arg);
+
+/* 3.23 */
+struct wsm_join {
+ /* WSM_JOIN_MODE_... */
+ /* [in] */ u8 mode;
+
+ /* WSM_PHY_BAND_... */
+ /* [in] */ u8 band;
+
+ /* Specifies the channel number to join. The channel */
+ /* number will be mapped to an actual frequency */
+ /* according to the band */
+ /* [in] */ u16 channelNumber;
+
+ /* Specifies the BSSID of the BSS or IBSS to be joined */
+ /* or the IBSS to be started. */
+ /* [in] */ u8 bssid[6];
+
+ /* ATIM window of IBSS */
+ /* When ATIM window is zero the initiated IBSS does */
+ /* not support power saving. */
+ /* [in] */ u16 atimWindow;
+
+ /* WSM_JOIN_PREAMBLE_... */
+ /* [in] */ u8 preambleType;
+
+ /* Specifies if a probe request should be send with the */
+ /* specified SSID when joining to the network. */
+ /* [in] */ u8 probeForJoin;
+
+ /* DTIM Period (In multiples of beacon interval) */
+ /* [in] */ u8 dtimPeriod;
+
+ /* WSM_JOIN_FLAGS_... */
+ /* [in] */ u8 flags;
+
+ /* Length of the SSID */
+ /* [in] */ u32 ssidLength;
+
+ /* Specifies the SSID of the IBSS to join or start */
+ /* [in] */ u8 ssid[32];
+
+ /* Specifies the time between TBTTs in TUs */
+ /* [in] */ u32 beaconInterval;
+
+ /* A bit mask that defines the BSS basic rate set. */
+ /* [in] */ u32 basicRateSet;
+
+ /* Minimum transmission power level in units of 0.1dBm */
+ /* [out] */ int minPowerLevel;
+
+ /* Maximum transmission power level in units of 0.1dBm */
+ /* [out] */ int maxPowerLevel;
+};
+
+int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg, int if_id);
+
+/* 3.25 */
+struct wsm_set_pm {
+ /* WSM_PSM_... */
+ /* [in] */ u8 pmMode;
+
+ /* in unit of 500us; 0 to use default */
+ /* [in] */ u8 fastPsmIdlePeriod;
+
+ /* in unit of 500us; 0 to use default */
+ /* [in] */ u8 apPsmChangePeriod;
+
+ /* in unit of 500us; 0 to disable auto-pspoll */
+ /* [in] */ u8 minAutoPsPollPeriod;
+};
+
+int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg,
+ int if_id);
+
+/* 3.27 */
+struct wsm_set_pm_complete {
+ u8 psm; /* WSM_PSM_... */
+};
+
+typedef void (*wsm_set_pm_complete_cb) (struct xradio_common *hw_priv,
+ struct wsm_set_pm_complete *arg);
+
+/* 3.28 */
+struct wsm_set_bss_params {
+ /* The number of lost consecutive beacons after which */
+ /* the WLAN device should indicate the BSS-Lost event */
+ /* to the WLAN host driver. */
+ u8 beaconLostCount;
+
+ /* The AID received during the association process. */
+ u16 aid;
+
+ /* The operational rate set mask */
+ u32 operationalRateSet;
+};
+
+int wsm_set_bss_params(struct xradio_common *hw_priv,
+ const struct wsm_set_bss_params *arg, int if_id);
+
+/* 3.30 */
+struct wsm_add_key {
+ u8 type; /* WSM_KEY_TYPE_... */
+ u8 entryIndex; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */
+ u16 reserved;
+ union {
+ struct {
+ u8 peerAddress[6]; /* MAC address of the
+ * peer station */
+ u8 reserved;
+ u8 keyLength; /* Key length in bytes */
+ u8 keyData[16]; /* Key data */
+ } __packed wepPairwiseKey;
+ struct {
+ u8 keyId; /* Unique per key identifier
+ * (0..3) */
+ u8 keyLength; /* Key length in bytes */
+ u16 reserved;
+ u8 keyData[16]; /* Key data */
+ } __packed wepGroupKey;
+ struct {
+ u8 peerAddress[6]; /* MAC address of the
+ * peer station */
+ u8 reserved[2];
+ u8 tkipKeyData[16]; /* TKIP key data */
+ u8 rxMicKey[8]; /* Rx MIC key */
+ u8 txMicKey[8]; /* Tx MIC key */
+ } __packed tkipPairwiseKey;
+ struct {
+ u8 tkipKeyData[16]; /* TKIP key data */
+ u8 rxMicKey[8]; /* Rx MIC key */
+ u8 keyId; /* Key ID */
+ u8 reserved[3];
+ u8 rxSeqCounter[8]; /* Receive Sequence Counter */
+ } __packed tkipGroupKey;
+ struct {
+ u8 peerAddress[6]; /* MAC address of the
+ * peer station */
+ u16 reserved;
+ u8 aesKeyData[16]; /* AES key data */
+ } __packed aesPairwiseKey;
+ struct {
+ u8 aesKeyData[16]; /* AES key data */
+ u8 keyId; /* Key ID */
+ u8 reserved[3];
+ u8 rxSeqCounter[8]; /* Receive Sequence Counter */
+ } __packed aesGroupKey;
+ struct {
+ u8 peerAddress[6]; /* MAC address of the
+ * peer station */
+ u8 keyId; /* Key ID */
+ u8 reserved;
+ u8 wapiKeyData[16]; /* WAPI key data */
+ u8 micKeyData[16]; /* MIC key data */
+ } __packed wapiPairwiseKey;
+ struct {
+ u8 wapiKeyData[16]; /* WAPI key data */
+ u8 micKeyData[16]; /* MIC key data */
+ u8 keyId; /* Key ID */
+ u8 reserved[3];
+ } __packed wapiGroupKey;
+ } __packed;
+} __packed;
+
+int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg,
+ int if_id);
+
+/* 3.32 */
+struct wsm_remove_key {
+ /* Key entry index : 0-10 */
+ u8 entryIndex;
+};
+
+int wsm_remove_key(struct xradio_common *hw_priv,
+ const struct wsm_remove_key *arg, int if_id);
+
+/* 3.34 */
+struct wsm_set_tx_queue_params {
+ /* WSM_ACK_POLICY_... */
+ u8 ackPolicy;
+
+ /* Medium Time of TSPEC (in 32us units) allowed per */
+ /* One Second Averaging Period for this queue. */
+ u16 allowedMediumTime;
+
+ /* dot11MaxTransmitMsduLifetime to be used for the */
+ /* specified queue. */
+ u32 maxTransmitLifetime;
+};
+
+struct wsm_tx_queue_params {
+ /* NOTE: index is a linux queue id. */
+ struct wsm_set_tx_queue_params params[4];
+};
+
+#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time, \
+ max_life_time) \
+do { \
+ struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \
+ p->ackPolicy = (ack_policy); \
+ p->allowedMediumTime = (allowed_time); \
+ p->maxTransmitLifetime = (max_life_time); \
+} while (0)
+
+int wsm_set_tx_queue_params(struct xradio_common *hw_priv,
+ const struct wsm_set_tx_queue_params *arg,
+ u8 id, int if_id);
+
+/* 3.36 */
+struct wsm_edca_queue_params {
+ /* CWmin (in slots) for the access class. */
+ /* [in] */ u16 cwMin;
+
+ /* CWmax (in slots) for the access class. */
+ /* [in] */ u16 cwMax;
+
+ /* AIFS (in slots) for the access class. */
+ /* [in] */ u8 aifns;
+
+ /* TX OP Limit (in microseconds) for the access class. */
+ /* [in] */ u16 txOpLimit;
+
+ /* dot11MaxReceiveLifetime to be used for the specified */
+ /* the access class. Overrides the global */
+ /* dot11MaxReceiveLifetime value */
+ /* [in] */ u32 maxReceiveLifetime;
+
+ /* UAPSD trigger support for the access class. */
+ /* [in] */ bool uapsdEnable;
+};
+
+struct wsm_edca_params {
+ /* NOTE: index is a linux queue id. */
+ struct wsm_edca_queue_params params[4];
+};
+
+#define TXOP_UNIT 32
+#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, life_time,\
+ uapsd) \
+ do { \
+ struct wsm_edca_queue_params *p = &(edca)->params[queue]; \
+ p->cwMin = (cw_min); \
+ p->cwMax = (cw_max); \
+ p->aifns = (aifs); \
+ p->txOpLimit = ((txop) * TXOP_UNIT); \
+ p->maxReceiveLifetime = (life_time); \
+ p->uapsdEnable = (uapsd); \
+ } while (0)
+
+int wsm_set_edca_params(struct xradio_common *hw_priv,
+ const struct wsm_edca_params *arg, int if_id);
+
+int wsm_set_uapsd_param(struct xradio_common *hw_priv,
+ const struct wsm_edca_params *arg);
+
+/* 3.38 */
+/* Set-System info. Skipped for now. Irrelevent. */
+
+/* 3.40 */
+struct wsm_switch_channel {
+ /* 1 - means the STA shall not transmit any further */
+ /* frames until the channel switch has completed */
+ /* [in] */ u8 channelMode;
+
+ /* Number of TBTTs until channel switch occurs. */
+ /* 0 - indicates switch shall occur at any time */
+ /* 1 - occurs immediately before the next TBTT */
+ /* [in] */ u8 channelSwitchCount;
+
+ /* The new channel number to switch to. */
+ /* Note this is defined as per section 2.7. */
+ /* [in] */ u16 newChannelNumber;
+};
+
+int wsm_switch_channel(struct xradio_common *hw_priv,
+ const struct wsm_switch_channel *arg, int if_id);
+
+typedef void (*wsm_channel_switch_cb) (struct xradio_common *hw_priv);
+
+struct wsm_start {
+ /* WSM_START_MODE_... */
+ /* [in] */ u8 mode;
+
+ /* WSM_PHY_BAND_... */
+ /* [in] */ u8 band;
+
+ /* Channel number */
+ /* [in] */ u16 channelNumber;
+
+ /* Client Traffic window in units of TU */
+ /* Valid only when mode == ..._P2P */
+ /* [in] */ u32 CTWindow;
+
+ /* Interval between two consecutive */
+ /* beacon transmissions in TU. */
+ /* [in] */ u32 beaconInterval;
+
+ /* DTIM period in terms of beacon intervals */
+ /* [in] */ u8 DTIMPeriod;
+
+ /* WSM_JOIN_PREAMBLE_... */
+ /* [in] */ u8 preambleType;
+
+ /* The delay time (in microseconds) period */
+ /* before sending a probe-request. */
+ /* [in] */ u8 probeDelay;
+
+ /* Length of the SSID */
+ /* [in] */ u8 ssidLength;
+
+ /* SSID of the BSS or P2P_GO to be started now. */
+ /* [in] */ u8 ssid[32];
+
+ /* The basic supported rates for the MiniAP. */
+ /* [in] */ u32 basicRateSet;
+};
+
+int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg,
+ int if_id);
+
+#if 0
+struct wsm_beacon_transmit {
+ /* 1: enable; 0: disable */
+ /* [in] */ u8 enableBeaconing;
+};
+
+int wsm_beacon_transmit(struct xradio_common *hw_priv,
+ const struct wsm_beacon_transmit *arg,
+ int if_id);
+#endif
+
+int wsm_start_find(struct xradio_common *hw_priv, int if_id);
+
+int wsm_stop_find(struct xradio_common *hw_priv, int if_id);
+
+typedef void (*wsm_find_complete_cb) (struct xradio_common *hw_priv,
+ u32 status);
+
+struct wsm_suspend_resume {
+ /* See 3.52 */
+ /* Link ID */
+ /* [out] */ int link_id;
+ /* Stop sending further Tx requests down to device for this link */
+ /* [out] */ bool stop;
+ /* Transmit multicast Frames */
+ /* [out] */ bool multicast;
+ /* The AC on which Tx to be suspended /resumed. */
+ /* This is applicable only for U-APSD */
+ /* WSM_QUEUE_... */
+ /* [out] */ int queue;
+ /* [out] */ int if_id;
+};
+
+typedef void (*wsm_suspend_resume_cb) (struct xradio_vif *priv,
+ struct wsm_suspend_resume *arg);
+
+/* 3.54 Update-IE request. */
+struct wsm_update_ie {
+ /* WSM_UPDATE_IE_... */
+ /* [in] */ u16 what;
+ /* [in] */ u16 count;
+ /* [in] */ u8 *ies;
+ /* [in] */ size_t length;
+};
+
+int wsm_update_ie(struct xradio_common *hw_priv,
+ const struct wsm_update_ie *arg, int if_id);
+
+/* 3.56 */
+struct wsm_map_link {
+ /* MAC address of the remote device */
+ /* [in] */ u8 mac_addr[6];
+ /* [in] */ u8 unmap;
+ /* [in] */ u8 link_id;
+};
+
+int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg,
+ int if_id);
+
+struct wsm_cbc {
+ wsm_scan_complete_cb scan_complete;
+ wsm_tx_confirm_cb tx_confirm;
+ wsm_event_cb event;
+ wsm_set_pm_complete_cb set_pm_complete;
+ wsm_channel_switch_cb channel_switch;
+ wsm_find_complete_cb find_complete;
+ wsm_suspend_resume_cb suspend_resume;
+};
+#ifdef MCAST_FWDING
+
+/* 3.65 Give Buffer Request */
+int wsm_init_release_buffer_request(struct xradio_common *priv, u8 index);
+
+/* 3.65 fixed memory leakage by yangfh*/
+int wsm_deinit_release_buffer(struct xradio_common *hw_priv);
+
+/* 3.67 Request Buffer Request */
+int wsm_request_buffer_request(struct xradio_vif *priv,
+ u8 *arg);
+#endif
+/* ******************************************************************** */
+/* MIB shortcats */
+#define XR_RRM 1
+#ifdef XR_RRM//RadioResourceMeasurement
+/* RadioResourceMeasurement Request*/
+#define MEAS_CCA 0
+#define MEAS_CHANNELLOAD 1
+typedef struct LMAC_MEAS_CHANNEL_LOAD_PARAMS_S
+{
+ u8 Reserved;
+ u8 ChannelLoadCCA;
+ u16 ChannelNum;
+ u16 RandomInterval;
+ u16 MeasurementDuration;
+ u32 MeasurementStartTimel;
+ u32 MeasurementStartTimeh;
+}LMAC_MEAS_CHANNEL_LOAD_PARAMS;
+
+#define MEAS_RPI 0
+#define MEAS_IPI 1
+
+typedef struct LMAC_MEAS_NOISE_HISTOGRAM_PARAMS_S
+{
+ u8 Reserved;
+ u8 IpiRpi;
+ u16 ChannelNum;
+ u16 RandomInterval;
+ u16 MeasurementDuration;
+ u32 MeasurementStartTimel;
+ u32 MeasurementStartTimeh;
+}LMAC_MEAS_NOISE_HISTOGRAM_PARAMS;
+
+#define LMAC_MAX_SSIDS 16
+#define LMAC_MAX_SSID_LENGTH 32
+typedef struct LMAC_CHANNELS_S
+{
+ u32 ChannelNum;
+ u32 MinChannelTime;
+ u32 MaxChannelTime;
+ s32 TxPowerLevel;
+}LMAC_CHANNELS;
+
+typedef struct LMAC_SSIDS_S
+{
+ u32 SSIDLength;
+ u8 SSID[LMAC_MAX_SSID_LENGTH];
+}LMAC_SSIDS;
+
+typedef struct LMAC_MEAS_BEACON_PARAMS_S
+{
+ //u8 RegulatoryClass;
+ //u8 MeasurementMode;
+ //u16 ChannelNum;
+ u16 RandomInterval;
+ //u16 MeasurementDuration;
+ //u8 Bssid[6];
+ u16 Reserved;
+ //SCAN_PARAMETERS ScanParameters;
+ u8 Band;
+ u8 ScanType;
+ u8 ScanFlags;
+ u8 MaxTransmitRate;
+ u32 AutoScanInterval;
+ u8 NumOfProbeRequests;
+ u8 NumOfChannels;
+ u8 NumOfSSIDs;
+ u8 ProbeDelay;
+ LMAC_CHANNELS Channels;
+ LMAC_SSIDS Ssids; // here for SCAN_PARAMETER sizing purposes
+}LMAC_MEAS_BEACON_PARAMS;
+
+typedef struct LMAC_MEAS_STA_STATS_PARAMS_S
+{
+ u8 PeerMacAddress[6];
+ u16 RandomInterval;
+ u16 MeasurementDuration;
+ u8 GroupId;
+ u8 Reserved;
+}LMAC_MEAS_STA_STATS_PARAMS;
+
+typedef struct LMAC_MEAS_LINK_MEASUREMENT_PARAMS_S
+{
+ u8 Reserved[4];
+}LMAC_MEAS_LINK_MEASUREMENT_PARAMS;
+
+typedef union LMAC_MEAS_REQUEST_U
+{
+ LMAC_MEAS_CHANNEL_LOAD_PARAMS ChannelLoadParams;
+ LMAC_MEAS_NOISE_HISTOGRAM_PARAMS NoisHistogramParams;
+ LMAC_MEAS_BEACON_PARAMS BeaconParams;
+ LMAC_MEAS_STA_STATS_PARAMS StaStatsParams;
+ LMAC_MEAS_LINK_MEASUREMENT_PARAMS LinkMeasurementParams;
+} LMAC_MEAS_REQUEST;
+
+// This struct is a copy of WSM_HI_START_MEASUREMENT_REQ, except that MsgLen and MsgId is not included
+typedef struct MEASUREMENT_PARAMETERS_S
+{
+// u16 MsgLen;
+// u16 MsgId;
+ s32 TxPowerLevel;
+ u8 DurationMandatory;
+ u8 MeasurementType;
+ u8 MeasurementRequestLength;
+ u8 Reserved[5];
+ LMAC_MEAS_REQUEST MeasurementRequest;
+}MEASUREMENT_PARAMETERS;
+
+/* RadioResourceMeasurement Result*/
+ typedef struct LMAC_MEAS_CHANNEL_LOAD_RESULTS_S
+{
+ u8 Reserved;
+ u8 ChannelLoadCCA;
+ u16 ChannelNum;
+ u32 ActualMeasurementStartTimel;
+ u32 ActualMeasurementStartTimeh;
+ u16 MeasurementDuration;
+ u8 CCAbusyFraction;
+ u8 ChannelLoad;
+}LMAC_MEAS_CHANNEL_LOAD_RESULTS;
+
+typedef struct LMAC_MEAS_NOISE_HISTOGRAM_RESULTS_S
+{
+ u16 Reserved;
+ u16 ChannelNum;
+ u32 ActualMeasurementStartTimel;
+ u32 ActualMeasurementStartTimeh;
+ u16 MeasurementDuration;
+ u8 AntennaID;
+ u8 IpiRpi;
+ u8 PI_0_Density;
+ u8 PI_1_Density;
+ u8 PI_2_Density;
+ u8 PI_3_Density;
+ u8 PI_4_Density;
+ u8 PI_5_Density;
+ u8 PI_6_Density;
+ u8 PI_7_Density;
+ u8 PI_8_Density;
+ u8 PI_9_Density;
+ u8 PI_10_Density;
+ u8 Reserved2;
+}LMAC_MEAS_NOISE_HISTOGRAM_RESULTS;
+
+typedef struct LMAC_MEAS_BEACON_RESULTS_S
+{
+ u16 MeasurementDuration;
+ u16 Reserved;
+ u32 StartTsfl;
+ u32 StartTsfh;
+ u32 Durationl;
+ u32 Durationh;
+ //SCAN_PARAMETERS ScanParameters;
+ u8 Band;
+ u8 ScanType;
+ u8 ScanFlags;
+ u8 MaxTransmitRate;
+ u32 AutoScanInterval;
+ u8 NumOfProbeRequests;
+ u8 NumOfChannels;
+ u8 NumOfSSIDs;
+ u8 ProbeDelay;
+ LMAC_CHANNELS Channels;
+ LMAC_SSIDS Ssids;
+}LMAC_MEAS_BEACON_RESULTS;
+
+typedef struct LMAC_MEAS_STA_STATS_RESULTS_S
+{
+ u16 MeasurementDuration;
+ u8 GroupId;
+ u8 StatisticsGroupDataLength;
+ u8 StatisticsGroupData[52];
+}LMAC_MEAS_STA_STATS_RESULTS;
+
+typedef struct LMAC_MEAS_LINK_MEASUREMENT_RESULTS_S
+{
+ s16 TransmitPower;
+ u8 RxAntennaID;
+ u8 TxAntennaID;
+ s32 NoiseLeveldBm;
+ s8 LatestRssi;
+ u8 Reserved1;
+ u8 Reserved2;
+ u8 Reserved3;
+}LMAC_MEAS_LINK_MEASUREMENT_RESULTS;
+
+typedef union LMAC_MEAS_REPORT_U
+{
+ LMAC_MEAS_CHANNEL_LOAD_RESULTS ChannelLoadResults;
+ LMAC_MEAS_NOISE_HISTOGRAM_RESULTS NoiseHistogramResults;
+ LMAC_MEAS_BEACON_RESULTS BeaconResults;
+ LMAC_MEAS_STA_STATS_RESULTS StaStatsResults;
+ LMAC_MEAS_LINK_MEASUREMENT_RESULTS LinkMeasurementResults;
+}LMAC_MEAS_REPORT;
+
+// Note: eMeasurementTypes MUST match the #define WSM_MEASURE_TYPE_XXX from wsm_api.h
+typedef enum {
+ ChannelLoadMeasurement=0,
+ NoiseHistrogramMeasurement,
+ BeaconReport,
+ STAstatisticsReport,
+ LinkMeasurement
+}eMeasurementTypes;
+
+typedef struct MEASUREMENT_COMPLETE_S
+{
+// u16 RandomInterval;
+// u16 Reserved0;
+ u8 Dot11PowerMgmtMode; // From here WSM_HI_MEASURE_CMPL_IND and MEASUREMENT_COMPLETE_S must be identical
+ u8 MeasurementType;
+ u16 MoreInd; // Set to 1 if more indications are to follow for this measurement, otherwise 0;
+ u32 Status;
+ u8 MeasurementReportLength;
+ u8 Reserved2[3];
+ LMAC_MEAS_REPORT MeasurementReport;
+}MEASUREMENT_COMPLETE; // Note: must be 32 bit aligned
+
+#endif
+int wsm_11k_measure_requset(struct xradio_common *hw_priv,
+ u8 measure_type,
+ u16 ChannelNum,
+ u16 Duration);
+
+
+static inline int wsm_set_fw_debug_control(struct xradio_common *hw_priv,
+ int debug_control, int if_id)
+{
+ __le32 val = __cpu_to_le32(debug_control);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_FW_DEBUG_CONTROL,
+ &val, sizeof(val), if_id);
+}
+
+static inline int wsm_set_host_sleep(struct xradio_common *hw_priv,
+ u8 host_sleep, int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_HOST_SLEEP,
+ &host_sleep, sizeof(host_sleep), if_id);
+}
+
+static inline int wsm_set_output_power(struct xradio_common *hw_priv,
+ int power_level, int if_id)
+{
+ __le32 val = __cpu_to_le32(power_level);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL,
+ &val, sizeof(val), if_id);
+}
+
+static inline int wsm_set_beacon_wakeup_period(struct xradio_common *hw_priv,
+ unsigned dtim_interval,
+ unsigned listen_interval,
+ int if_id)
+{
+ struct {
+ u8 numBeaconPeriods;
+ u8 reserved;
+ __le16 listenInterval;
+ } val = {
+ dtim_interval, 0, __cpu_to_le16(listen_interval)};
+ if (dtim_interval > 0xFF || listen_interval > 0xFFFF)
+ return -EINVAL;
+ else
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD,
+ &val, sizeof(val), if_id);
+}
+
+struct wsm_rcpi_rssi_threshold {
+ u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */
+ u8 lowerThreshold;
+ u8 upperThreshold;
+ u8 rollingAverageCount;
+};
+
+static inline int wsm_set_rcpi_rssi_threshold(struct xradio_common *hw_priv,
+ struct wsm_rcpi_rssi_threshold *arg,
+ int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg,
+ sizeof(*arg), if_id);
+}
+
+struct wsm_counters_table {
+ __le32 countPlcpErrors;
+ __le32 countFcsErrors;
+ __le32 countTxPackets;
+ __le32 countRxPackets;
+ __le32 countRxPacketErrors;
+ __le32 countRtsSuccess;
+ __le32 countRtsFailures;
+ __le32 countRxFramesSuccess;
+ __le32 countRxDecryptionFailures;
+ __le32 countRxMicFailures;
+ __le32 countRxNoKeyFailures;
+ __le32 countTxMulticastFrames;
+ __le32 countTxFramesSuccess;
+ __le32 countTxFrameFailures;
+ __le32 countTxFramesRetried;
+ __le32 countTxFramesMultiRetried;
+ __le32 countRxFrameDuplicates;
+ __le32 countAckFailures;
+ __le32 countRxMulticastFrames;
+ __le32 countRxCMACICVErrors;
+ __le32 countRxCMACReplays;
+ __le32 countRxMgmtCCMPReplays;
+ __le32 countRxBIPMICErrors;
+};
+
+
+struct wsm_ampducounters_table {
+ u32 countTxAMPDUs;
+ u32 countTxMPDUsInAMPDUs;
+ u32 countTxOctetsInAMPDUs_l32;
+ u32 countTxOctetsInAMPDUs_h32;
+ u32 countRxAMPDUs;
+ u32 countRxMPDUsInAMPDUs;
+ u32 countRxOctetsInAMPDUs_l32;
+ u32 countRxOctetsInAMPDUs_h32;
+ u32 countRxDelimeterCRCErrorCount;
+ u32 countImplictBARFailures;
+ u32 countExplictBARFailures;
+};
+
+struct wsm_txpipe_counter {
+ u32 count1;
+ u32 count2;
+ u32 count3;
+ u32 count4;
+ u32 count5;
+ u32 count6;
+ u32 count7;
+ u32 count8;
+ u32 count9;
+ u32 counta;
+};
+
+struct wsm_backoff_counter {
+ u32 count0;
+ u32 count1;
+ u32 count2;
+ u32 count3;
+ u32 count4;
+ u32 count5;
+ u32 count6;
+ u32 count7;
+ u32 count8;
+ u32 count9;
+};
+//add by yangfh for read/write fw registers
+#define WSM_REG_RW_F BIT(0) //0:read, 1:write
+#define WSM_REG_RET_F BIT(1) //results is valid.
+#define WSM_REG_BK_F BIT(4) //operate in block mode.
+
+struct reg_data {
+ u32 reg_addr;
+ u32 reg_val;
+};
+
+typedef struct tag_wsm_reg_w {
+ u16 flag;
+ u16 data_size;
+ struct reg_data arg[16];
+} WSM_REG_W;
+
+typedef struct tag_wsm_reg_r {
+ u16 flag;
+ u16 data_size;
+ u32 arg[16];
+} WSM_REG_R;
+
+struct wsm_backoff_ctrl {
+ u32 enable;
+ u32 min;
+ u32 max;
+};
+struct wsm_tala_para {
+ u32 para;
+ u32 thresh;
+};
+static inline int wsm_get_counters_table(struct xradio_common *hw_priv,
+ struct wsm_counters_table *arg)
+{
+ return wsm_read_mib(hw_priv, WSM_MIB_ID_COUNTERS_TABLE,
+ arg, sizeof(*arg), 0);
+}
+
+static inline int wsm_get_ampducounters_table(struct xradio_common *hw_priv,
+ struct wsm_ampducounters_table *arg)
+{
+ return wsm_read_mib(hw_priv, WSM_MIB_ID_AMPDUCOUNTERS_TABLE,
+ arg, sizeof(*arg), 0);
+}
+
+static inline int wsm_get_txpipe_table(struct xradio_common *hw_priv,
+ struct wsm_txpipe_counter *arg)
+{
+ return wsm_read_mib(hw_priv, WSM_MIB_ID_TXPIPE_TABLE,
+ arg, sizeof(*arg), 0);
+}
+
+static inline int wsm_get_backoff_dbg(struct xradio_common *hw_priv,
+ struct wsm_backoff_counter *arg)
+{
+ return wsm_read_mib(hw_priv, WSM_MIB_ID_BACKOFF_DBG,
+ arg, sizeof(*arg), 0);
+}
+
+static inline int wsm_set_backoff_ctrl(struct xradio_common *hw_priv,
+ struct wsm_backoff_ctrl *arg)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_BACKOFF_CTRL,
+ arg, sizeof(*arg), 0);
+}
+
+static inline int wsm_set_tala(struct xradio_common *hw_priv,
+ struct wsm_tala_para *arg)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TALA_PARA,
+ arg, sizeof(*arg), 0);
+}
+static inline int wsm_get_station_id(struct xradio_common *hw_priv, u8 *mac)
+{
+ return wsm_read_mib(hw_priv, WSM_MIB_ID_DOT11_STATION_ID, mac,
+ ETH_ALEN, 0);
+}
+
+struct wsm_rx_filter {
+ bool promiscuous;
+ bool bssid;
+ bool fcs;
+ bool probeResponder;
+ bool keepalive;
+};
+
+static inline int wsm_set_rx_filter(struct xradio_common *hw_priv,
+ const struct wsm_rx_filter *arg,
+ int if_id)
+{
+ __le32 val = 0;
+ if (arg->promiscuous)
+ val |= __cpu_to_le32(BIT(0));
+ if (arg->bssid)
+ val |= __cpu_to_le32(BIT(1));
+ if (arg->fcs)
+ val |= __cpu_to_le32(BIT(2));
+ if (arg->probeResponder)
+ val |= __cpu_to_le32(BIT(3));
+ if (arg->keepalive)
+ val |= __cpu_to_le32(BIT(4));
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val),
+ if_id);
+}
+
+int wsm_set_probe_responder(struct xradio_vif *priv, bool enable);
+int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable);
+
+#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0)
+#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1)
+#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2)
+
+struct wsm_beacon_filter_table_entry {
+ u8 ieId;
+ u8 actionFlags;
+ u8 oui[3];
+ u8 matchData[3];
+} __packed;
+
+struct wsm_beacon_filter_table {
+ __le32 numOfIEs;
+ struct wsm_beacon_filter_table_entry entry[10];
+} __packed;
+
+static inline int wsm_set_beacon_filter_table(struct xradio_common *hw_priv,
+ struct wsm_beacon_filter_table *ft,
+ int if_id)
+{
+ size_t size = __le32_to_cpu(ft->numOfIEs) *
+ sizeof(struct wsm_beacon_filter_table_entry) +
+ sizeof(__le32);
+
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size,
+ if_id);
+}
+
+#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */
+#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */
+
+struct wsm_beacon_filter_control {
+ int enabled;
+ int bcn_count;
+};
+
+static inline int wsm_beacon_filter_control(struct xradio_common *hw_priv,
+ struct wsm_beacon_filter_control *arg,
+ int if_id)
+{
+ struct {
+ __le32 enabled;
+ __le32 bcn_count;
+ } val;
+ val.enabled = __cpu_to_le32(arg->enabled);
+ val.bcn_count = __cpu_to_le32(arg->bcn_count);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val,
+ sizeof(val), if_id);
+}
+
+enum wsm_power_mode {
+ wsm_power_mode_active = 0,
+ wsm_power_mode_doze = 1,
+ wsm_power_mode_quiescent = 2,
+};
+
+struct wsm_operational_mode {
+ enum wsm_power_mode power_mode;
+ int disableMoreFlagUsage;
+ int performAntDiversity;
+};
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+extern u8 low_pwr_disable;
+#endif
+
+static inline int wsm_set_operational_mode(struct xradio_common *hw_priv,
+ const struct wsm_operational_mode *arg,
+ int if_id)
+{
+ u32 val = arg->power_mode;
+
+#ifdef CONFIG_XRADIO_DEBUGFS //add by yangfh for disable low_power mode.
+ if(low_pwr_disable)
+ val = wsm_power_mode_active;
+#endif
+
+ if (arg->disableMoreFlagUsage)
+ val |= BIT(4);
+ if (arg->performAntDiversity)
+ val |= BIT(5);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val,
+ sizeof(val), if_id);
+}
+
+struct wsm_inactivity {
+ u8 max_inactivity;
+ u8 min_inactivity;
+};
+
+static inline int wsm_set_inactivity(struct xradio_common *hw_priv,
+ const struct wsm_inactivity *arg,
+ int if_id)
+{
+ struct {
+ u8 min_inactive;
+ u8 max_inactive;
+ u16 reserved;
+ } val;
+
+ val.max_inactive = arg->max_inactivity;
+ val.min_inactive = arg->min_inactivity;
+ val.reserved = 0;
+
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_INACTIVITY, &val,
+ sizeof(val), if_id);
+}
+
+struct wsm_template_frame {
+ u8 frame_type;
+ u8 rate;
+ bool disable;
+ struct sk_buff *skb;
+};
+
+static inline int wsm_set_template_frame(struct xradio_common *hw_priv,
+ struct wsm_template_frame *arg,
+ int if_id)
+{
+ int ret;
+ u8 *p = skb_push(arg->skb, 4);
+ p[0] = arg->frame_type;
+ p[1] = arg->rate;
+ if (arg->disable)
+ ((u16 *) p)[1] = 0;
+ else
+ ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4);
+ ret = wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, p,
+ arg->skb->len, if_id);
+ skb_pull(arg->skb, 4);
+ return ret;
+}
+
+
+struct wsm_protected_mgmt_policy {
+ bool protectedMgmtEnable;
+ bool unprotectedMgmtFramesAllowed;
+ bool encryptionForAuthFrame;
+};
+
+static inline int
+wsm_set_protected_mgmt_policy(struct xradio_common *hw_priv,
+ struct wsm_protected_mgmt_policy *arg,
+ int if_id)
+{
+ __le32 val = 0;
+ int ret;
+ if (arg->protectedMgmtEnable)
+ val |= __cpu_to_le32(BIT(0));
+ if (arg->unprotectedMgmtFramesAllowed)
+ val |= __cpu_to_le32(BIT(1));
+ if (arg->encryptionForAuthFrame)
+ val |= __cpu_to_le32(BIT(2));
+ ret = wsm_write_mib(hw_priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, &val,
+ sizeof(val), if_id);
+ return ret;
+}
+
+static inline int wsm_set_block_ack_policy(struct xradio_common *hw_priv,
+ u8 blockAckTxTidPolicy,
+ u8 blockAckRxTidPolicy,
+ int if_id)
+{
+ struct {
+ u8 blockAckTxTidPolicy;
+ u8 reserved1;
+ u8 blockAckRxTidPolicy;
+ u8 reserved2;
+ } val = {
+ .blockAckTxTidPolicy = blockAckTxTidPolicy,
+ .blockAckRxTidPolicy = blockAckRxTidPolicy,
+ };
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val,
+ sizeof(val), if_id);
+}
+
+struct wsm_association_mode {
+ u8 flags; /* WSM_ASSOCIATION_MODE_... */
+ u8 preambleType; /* WSM_JOIN_PREAMBLE_... */
+ u8 greenfieldMode; /* 1 for greenfield */
+ u8 mpduStartSpacing;
+ __le32 basicRateSet;
+};
+
+static inline int wsm_set_association_mode(struct xradio_common *hw_priv,
+ struct wsm_association_mode *arg,
+ int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg,
+ sizeof(*arg), if_id);
+}
+
+struct wsm_set_tx_rate_retry_policy_header {
+ u8 numTxRatePolicies;
+ u8 reserved[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy_policy {
+ u8 policyIndex;
+ u8 shortRetryCount;
+ u8 longRetryCount;
+ u8 policyFlags;
+ u8 rateRecoveryCount;
+ u8 reserved[3];
+ __le32 rateCountIndices[3];
+} __packed;
+
+struct wsm_set_tx_rate_retry_policy {
+ struct wsm_set_tx_rate_retry_policy_header hdr;
+ struct wsm_set_tx_rate_retry_policy_policy tbl[8];
+} __packed;
+
+static inline int wsm_set_tx_rate_retry_policy(struct xradio_common *hw_priv,
+ struct wsm_set_tx_rate_retry_policy *arg,
+ int if_id)
+{
+ size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) +
+ arg->hdr.numTxRatePolicies *
+ sizeof(struct wsm_set_tx_rate_retry_policy_policy);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg,
+ size, if_id);
+}
+
+/* 4.32 SetEtherTypeDataFrameFilter */
+struct wsm_ether_type_filter_hdr {
+ u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */
+ u8 reserved[3];
+} __packed;
+
+struct wsm_ether_type_filter {
+ u8 filterAction; /* WSM_FILTER_ACTION_XXX */
+ u8 reserved;
+ __le16 etherType; /* Type of ethernet frame */
+} __packed;
+
+static inline int wsm_set_ether_type_filter(struct xradio_common *hw_priv,
+ struct wsm_ether_type_filter_hdr *arg,
+ int if_id)
+{
+ size_t size = sizeof(struct wsm_ether_type_filter_hdr) +
+ arg->nrFilters * sizeof(struct wsm_ether_type_filter);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER,
+ arg, size, if_id);
+}
+
+
+/* 4.33 SetUDPPortDataFrameFilter */
+struct wsm_udp_port_filter_hdr {
+ u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */
+ u8 reserved[3];
+} __packed;
+
+struct wsm_udp_port_filter {
+ u8 filterAction; /* WSM_FILTER_ACTION_XXX */
+ u8 portType; /* WSM_FILTER_PORT_TYPE_XXX */
+ __le16 udpPort; /* Port number */
+} __packed;
+
+static inline int wsm_set_udp_port_filter(struct xradio_common *hw_priv,
+ struct wsm_udp_port_filter_hdr *arg,
+ int if_id)
+{
+ size_t size = sizeof(struct wsm_udp_port_filter_hdr) +
+ arg->nrFilters * sizeof(struct wsm_udp_port_filter);
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER,
+ arg, size, if_id);
+}
+
+/* Undocumented MIBs: */
+/* 4.35 P2PDeviceInfo */
+#define D11_MAX_SSID_LEN (32)
+
+struct wsm_p2p_device_type {
+ __le16 categoryId;
+ u8 oui[4];
+ __le16 subCategoryId;
+} __packed;
+
+struct wsm_p2p_device_info {
+ struct wsm_p2p_device_type primaryDevice;
+ u8 reserved1[3];
+ u8 devNameSize;
+ u8 localDevName[D11_MAX_SSID_LEN];
+ u8 reserved2[3];
+ u8 numSecDevSupported;
+ struct wsm_p2p_device_type secondaryDevices[0];
+} __packed;
+
+/* 4.36 SetWCDMABand - WO */
+struct wsm_cdma_band {
+ u8 WCDMA_Band;
+ u8 reserved[3];
+} __packed;
+
+/* 4.37 GroupTxSequenceCounter - RO */
+struct wsm_group_tx_seq {
+ __le32 bits_47_16;
+ __le16 bits_15_00;
+ __le16 reserved;
+} __packed;
+
+/* 4.39 SetHtProtection - WO */
+#define WSM_DUAL_CTS_PROT_ENB (1 << 0)
+#define WSM_NON_GREENFIELD_STA PRESENT(1 << 1)
+#define WSM_HT_PROT_MODE__NO_PROT (0 << 2)
+#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2)
+#define WSM_HT_PROT_MODE__20_MHZ (2 << 2)
+#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2)
+#define WSM_LSIG_TXOP_PROT_FULL (1 << 4)
+#define WSM_LARGE_L_LENGTH_PROT (1 << 5)
+
+struct wsm_ht_protection {
+ __le32 flags;
+} __packed;
+
+/* 4.40 GPIO Command - R/W */
+#define WSM_GPIO_COMMAND_SETUP 0
+#define WSM_GPIO_COMMAND_READ 1
+#define WSM_GPIO_COMMAND_WRITE 2
+#define WSM_GPIO_COMMAND_RESET 3
+#define WSM_GPIO_ALL_PINS 0xFF
+
+struct wsm_gpio_command {
+ u8 GPIO_Command;
+ u8 pin;
+ __le16 config;
+} __packed;
+
+/* 4.41 TSFCounter - RO */
+struct wsm_tsf_counter {
+ __le64 TSF_Counter;
+} __packed;
+
+/* 4.43 Keep alive period */
+struct wsm_keep_alive_period {
+ __le16 keepAlivePeriod;
+ u8 reserved[2];
+} __packed;
+
+static inline int wsm_keep_alive_period(struct xradio_common *hw_priv,
+ int period, int if_id)
+{
+ struct wsm_keep_alive_period arg = {
+ .keepAlivePeriod = __cpu_to_le16(period),
+ };
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD,
+ &arg, sizeof(arg), if_id);
+};
+
+/* BSSID filtering */
+struct wsm_set_bssid_filtering {
+ u8 filter;
+ u8 reserved[3];
+} __packed;
+
+static inline int wsm_set_bssid_filtering(struct xradio_common *hw_priv,
+ bool enabled, int if_id)
+{
+ struct wsm_set_bssid_filtering arg = {
+ .filter = !enabled,
+ };
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_DISABLE_BSSID_FILTER,
+ &arg, sizeof(arg), if_id);
+}
+
+/* Multicat filtering - 4.5 */
+struct wsm_multicast_filter {
+ __le32 enable;
+ __le32 numOfAddresses;
+ u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN];
+} __packed;
+
+/* Mac Addr Filter Info */
+struct wsm_mac_addr_info {
+ u8 filter_mode;
+ u8 address_mode;
+ u8 MacAddr[6];
+} __packed;
+
+/* Mac Addr Filter */
+struct wsm_mac_addr_filter {
+ u8 numfilter;
+ u8 action_mode;
+ u8 Reserved[2];
+ struct wsm_mac_addr_info macaddrfilter[0];
+} __packed;
+
+/* Broadcast Addr Filter */
+struct wsm_broadcast_addr_filter {
+ u8 action_mode;
+ u8 nummacaddr;
+ u8 filter_mode;
+ u8 address_mode;
+ u8 MacAddr[6];
+} __packed;
+
+static inline int wsm_set_multicast_filter(struct xradio_common *hw_priv,
+ struct wsm_multicast_filter *fp,
+ int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE,
+ fp, sizeof(*fp), if_id);
+}
+
+/* ARP IPv4 filtering - 4.10 */
+struct wsm_arp_ipv4_filter {
+ __le32 enable;
+ __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+
+
+static inline int wsm_set_arp_ipv4_filter(struct xradio_common *hw_priv,
+ struct wsm_arp_ipv4_filter *fp,
+ int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE,
+ fp, sizeof(*fp), if_id);
+}
+
+/* P2P Power Save Mode Info - 4.31 */
+struct wsm_p2p_ps_modeinfo {
+ u8 oppPsCTWindow;
+ u8 count;
+ u8 reserved;
+ u8 dtimCount;
+ __le32 duration;
+ __le32 interval;
+ __le32 startTime;
+} __packed;
+
+static inline int wsm_set_p2p_ps_modeinfo(struct xradio_common *hw_priv,
+ struct wsm_p2p_ps_modeinfo *mi,
+ int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+ mi, sizeof(*mi), if_id);
+}
+
+static inline int wsm_get_p2p_ps_modeinfo(struct xradio_common *hw_priv,
+ struct wsm_p2p_ps_modeinfo *mi)
+{
+ return wsm_read_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO,
+ mi, sizeof(*mi), 0);
+}
+
+/* UseMultiTxConfMessage */
+
+static inline int wsm_use_multi_tx_conf(struct xradio_common *hw_priv,
+ bool enabled, int if_id)
+{
+ __le32 arg = enabled ? __cpu_to_le32(1) : 0;
+
+ return wsm_write_mib(hw_priv, WSM_MIB_USE_MULTI_TX_CONF,
+ &arg, sizeof(arg), if_id);
+}
+
+
+/* 4.26 SetUpasdInformation */
+struct wsm_uapsd_info {
+ __le16 uapsdFlags;
+ __le16 minAutoTriggerInterval;
+ __le16 maxAutoTriggerInterval;
+ __le16 autoTriggerStep;
+};
+
+static inline int wsm_set_uapsd_info(struct xradio_common *hw_priv,
+ struct wsm_uapsd_info *arg,
+ int if_id)
+{
+ /* TODO:COMBO:UAPSD will be supported only on one interface */
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UAPSD_INFORMATION,
+ arg, sizeof(*arg), if_id);
+}
+
+/* 4.22 OverrideInternalTxRate */
+struct wsm_override_internal_txrate {
+ u8 internalTxRate;
+ u8 nonErpInternalTxRate;
+ u8 reserved[2];
+} __packed;
+
+static inline int
+wsm_set_override_internal_txrate(struct xradio_common *hw_priv,
+ struct wsm_override_internal_txrate *arg,
+ int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
+ arg, sizeof(*arg), if_id);
+}
+#ifdef MCAST_FWDING
+/* 4.51 SetForwardingOffload */
+struct wsm_forwarding_offload {
+ u8 fwenable;
+ u8 flags;
+ u8 reserved[2];
+} __packed;
+
+static inline int wsm_set_forwarding_offlad(struct xradio_common *hw_priv,
+ struct wsm_forwarding_offload *arg,int if_id)
+{
+ return wsm_write_mib(hw_priv, WSM_MIB_ID_FORWARDING_OFFLOAD,
+ arg, sizeof(*arg),if_id);
+}
+
+#endif
+/* ******************************************************************** */
+/* WSM TX port control */
+
+void wsm_lock_tx(struct xradio_common *hw_priv);
+void wsm_vif_lock_tx(struct xradio_vif *priv);
+void wsm_lock_tx_async(struct xradio_common *hw_priv);
+bool wsm_flush_tx(struct xradio_common *hw_priv);
+bool wsm_vif_flush_tx(struct xradio_vif *priv);
+void wsm_unlock_tx(struct xradio_common *hw_priv);
+
+/* ******************************************************************** */
+/* WSM / BH API */
+
+int wsm_handle_exception(struct xradio_common *hw_priv, u8 * data, size_t len);
+int wsm_handle_rx(struct xradio_common *hw_priv, int id, struct wsm_hdr *wsm,
+ struct sk_buff **skb_p);
+void wms_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv);
+void wms_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv);
+
+/* ******************************************************************** */
+/* wsm_buf API */
+
+struct wsm_buf {
+ u8 *begin;
+ u8 *data;
+ u8 *end;
+};
+
+void wsm_buf_init(struct wsm_buf *buf);
+void wsm_buf_deinit(struct wsm_buf *buf);
+
+/* ******************************************************************** */
+/* wsm_cmd API */
+
+struct wsm_cmd {
+ spinlock_t lock;
+ int done;
+ u8 *ptr;
+ size_t len;
+ void *arg;
+ int ret;
+ u16 cmd;
+};
+
+/* ******************************************************************** */
+/* WSM TX buffer access */
+
+int wsm_get_tx(struct xradio_common *hw_priv, u8 **data,
+ size_t *tx_len, int *burst, int *vif_selected);
+void wsm_txed(struct xradio_common *hw_priv, u8 *data);
+
+/* ******************************************************************** */
+/* Queue mapping: WSM <---> linux */
+/* Linux: VO VI BE BK */
+/* WSM: BE BK VI VO */
+
+static inline u8 wsm_queue_id_to_linux(u8 queueId)
+{
+ static const u8 queue_mapping[] = {
+ 2, 3, 1, 0
+ };
+ return queue_mapping[queueId];
+}
+
+static inline u8 wsm_queue_id_to_wsm(u8 queueId)
+{
+ static const u8 queue_mapping[] = {
+ 3, 2, 0, 1
+ };
+ return queue_mapping[queueId];
+}
+
+#endif /* XRADIO_HWIO_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/xradio.h b/drivers/net/wireless/xradio/xradio.h
new file mode 100644
index 00000000..45393582
--- /dev/null
+++ b/drivers/net/wireless/xradio/xradio.h
@@ -0,0 +1,655 @@
+/*
+ * Common define of private data for XRadio drivers
+ *
+ * Copyright (c) 2013, XRadio
+ * Author: XRadio
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef XRADIO_H
+#define XRADIO_H
+
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/atomic.h>
+#include <net/mac80211.h>
+
+//Macroses for Driver parameters.
+#define XRWL_MAX_QUEUE_SZ (128)
+#define AC_QUEUE_NUM 4
+
+#ifdef P2P_MULTIVIF
+#define XRWL_MAX_VIFS (3)
+#else
+#define XRWL_MAX_VIFS (2)
+#endif
+#define XRWL_GENERIC_IF_ID (2)
+#define XRWL_HOST_VIF0_11N_THROTTLE (58) //(XRWL_MAX_QUEUE_SZ/(XRWL_MAX_VIFS-1))*0.9
+#define XRWL_HOST_VIF1_11N_THROTTLE (58) //(XRWL_MAX_QUEUE_SZ/(XRWL_MAX_VIFS-1))*0.9
+#define XRWL_HOST_VIF0_11BG_THROTTLE (35) //XRWL_HOST_VIF0_11N_THROTTLE*0.6 = 35
+#define XRWL_HOST_VIF1_11BG_THROTTLE (35) //XRWL_HOST_VIF0_11N_THROTTLE*0.6 = 35
+#if 0
+#define XRWL_FW_VIF0_THROTTLE (15)
+#define XRWL_FW_VIF1_THROTTLE (15)
+#endif
+
+#define IEEE80211_FCTL_WEP 0x4000
+#define IEEE80211_QOS_DATAGRP 0x0080
+#define WSM_KEY_MAX_IDX 20
+
+#include "queue.h"
+#include "wsm.h"
+#include "scan.h"
+#include "txrx.h"
+#include "ht.h"
+#include "pm.h"
+#include "fwio.h"
+#ifdef CONFIG_XRADIO_TESTMODE
+#include "nl80211_testmode_msg_copy.h"
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+/* #define ROC_DEBUG */
+/* hidden ssid is only supported when separate probe resp IE
+ configuration is supported */
+#ifdef PROBE_RESP_EXTRA_IE
+#define HIDDEN_SSID 1
+#endif
+
+#define XRADIO_MAX_CTRL_FRAME_LEN (0x1000)
+
+#define MAX_STA_IN_AP_MODE (14)
+#define WLAN_LINK_ID_MAX (MAX_STA_IN_AP_MODE + 3)
+
+#define XRADIO_MAX_STA_IN_AP_MODE (5)
+#define XRADIO_MAX_REQUEUE_ATTEMPTS (5)
+#define XRADIO_LINK_ID_UNMAPPED (15)
+#define XRADIO_MAX_TID (8)
+
+#define XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F)
+#define XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F)
+#define XRADIO_RX_BLOCK_ACK_ENABLED_FOR_BE_TID \
+ (XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID & 0x01)
+#define XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0)
+#define XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0)
+
+#define XRADIO_BLOCK_ACK_CNT (30)
+#define XRADIO_BLOCK_ACK_THLD (800)
+#define XRADIO_BLOCK_ACK_HIST (3)
+#define XRADIO_BLOCK_ACK_INTERVAL (1 * HZ / XRADIO_BLOCK_ACK_HIST)
+#define XRWL_ALL_IFS (-1)
+
+#ifdef ROAM_OFFLOAD
+#define XRADIO_SCAN_TYPE_ACTIVE 0x1000
+#define XRADIO_SCAN_BAND_5G 0x2000
+#endif /*ROAM_OFFLOAD*/
+
+#define IEEE80211_FCTL_WEP 0x4000
+#define IEEE80211_QOS_DATAGRP 0x0080
+#ifdef CONFIG_XRADIO_TESTMODE
+#define XRADIO_SCAN_MEASUREMENT_PASSIVE (0)
+#define XRADIO_SCAN_MEASUREMENT_ACTIVE (1)
+#endif
+
+#ifdef MCAST_FWDING
+#define WSM_MAX_BUF 30
+#endif
+
+#define MAX_RATES_STAGE 8 //
+#define MAX_RATES_RETRY 15
+
+#define XRADIO_WORKQUEUE "xradio_wq"
+#define WIFI_CONF_PATH "/data/xr_wifi.conf"
+
+/* extern */ struct task_struct;
+/* extern */ struct xradio_debug_priv;
+/* extern */ struct xradio_debug_common;
+/* extern */ struct firmware;
+
+/* Please keep order */
+enum xradio_join_status {
+ XRADIO_JOIN_STATUS_PASSIVE = 0,
+ XRADIO_JOIN_STATUS_MONITOR,
+ XRADIO_JOIN_STATUS_STA,
+ XRADIO_JOIN_STATUS_AP,
+};
+
+enum xradio_link_status {
+ XRADIO_LINK_OFF,
+ XRADIO_LINK_RESERVE,
+ XRADIO_LINK_SOFT,
+ XRADIO_LINK_HARD,
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ XRADIO_LINK_RESET,
+ XRADIO_LINK_RESET_REMAP,
+#endif
+};
+
+enum xradio_bss_loss_status {
+ XRADIO_BSS_LOSS_NONE,
+ XRADIO_BSS_LOSS_CHECKING,
+ XRADIO_BSS_LOSS_CONFIRMING,
+ XRADIO_BSS_LOSS_CONFIRMED,
+};
+
+struct xradio_link_entry {
+ unsigned long timestamp;
+ enum xradio_link_status status;
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ enum xradio_link_status prev_status;
+#endif
+ u8 mac[ETH_ALEN];
+ u8 buffered[XRADIO_MAX_TID];
+ struct sk_buff_head rx_queue;
+};
+
+#if defined(ROAM_OFFLOAD) || defined(CONFIG_XRADIO_TESTMODE)
+struct xradio_testframe {
+ u8 len;
+ u8 *data;
+};
+#endif
+#ifdef CONFIG_XRADIO_TESTMODE
+struct advance_scan_elems {
+ u8 scanMode;
+ u16 duration;
+};
+/**
+ * xradio_tsm_info - Keeps information about ongoing TSM collection
+ * @ac: Access category for which metrics to be collected
+ * @use_rx_roaming: Use received voice packets to compute roam delay
+ * @sta_associated: Set to 1 after association
+ * @sta_roamed: Set to 1 after successful roaming
+ * @roam_delay: Roam delay
+ * @rx_timestamp_vo: Timestamp of received voice packet
+ * @txconf_timestamp_vo: Timestamp of received tx confirmation for
+ * successfully transmitted VO packet
+ * @sum_pkt_q_delay: Sum of packet queue delay
+ * @sum_media_delay: Sum of media delay
+ *
+ */
+struct xradio_tsm_info {
+ u8 ac;
+ u8 use_rx_roaming;
+ u8 sta_associated;
+ u8 sta_roamed;
+ u16 roam_delay;
+ u32 rx_timestamp_vo;
+ u32 txconf_timestamp_vo;
+ u32 sum_pkt_q_delay;
+ u32 sum_media_delay;
+};
+
+/**
+ * xradio_start_stop_tsm - To start or stop collecting TSM metrics in
+ * xradio driver
+ * @start: To start or stop collecting TSM metrics
+ * @up: up for which metrics to be collected
+ * @packetization_delay: Packetization delay for this TID
+ *
+ */
+struct xradio_start_stop_tsm {
+ u8 start; /*1: To start, 0: To stop*/
+ u8 up;
+ u16 packetization_delay;
+};
+
+#endif /* CONFIG_XRADIO_TESTMODE */
+struct xradio_common {
+ struct xradio_debug_common *debug;
+ struct xradio_queue tx_queue[AC_QUEUE_NUM];
+ struct xradio_queue_stats tx_queue_stats;
+
+ struct ieee80211_hw *hw;
+ struct mac_address addresses[XRWL_MAX_VIFS];
+
+ /*Will be a pointer to a list of VIFs - Dynamically allocated */
+ struct ieee80211_vif *vif_list[XRWL_MAX_VIFS];
+ atomic_t num_vifs;
+ spinlock_t vif_list_lock;
+ u32 if_id_slot;
+ struct device *pdev;
+ struct workqueue_struct *workqueue;
+
+ struct mutex conf_mutex;
+
+ struct sdio_func *sdio_func;
+ int driver_ready;
+
+ /* HW/FW type (HIF_...) */
+ int hw_type;
+ int hw_revision;
+ int fw_revision;
+
+ /* firmware/hardware info */
+ unsigned int tx_hdr_len;
+
+ /* Radio data */
+ int output_power;
+ int noise;
+
+ /* calibration, output power limit and rssi<->dBm conversation data */
+
+ /* BBP/MAC state */
+ const struct firmware *sdd;
+ struct ieee80211_rate *rates;
+ struct ieee80211_rate *mcs_rates;
+ u8 mac_addr[ETH_ALEN];
+ /*TODO:COMBO: To be made per VIFF after mac80211 support */
+ struct ieee80211_channel *channel;
+ int channel_switch_in_progress;
+ wait_queue_head_t channel_switch_done;
+ u8 channel_changed; //add by yangfh 2015-5-15 16:57:38.
+ u8 long_frame_max_tx_count;
+ u8 short_frame_max_tx_count;
+ /* TODO:COMBO: According to Hong aggregation will happen per VIFF.
+ * Keeping in common structure for the time being. Will be moved to VIFF
+ * after the mechanism is clear */
+ u8 ba_tid_mask;
+ int ba_acc; /*TODO: Same as above */
+ int ba_cnt; /*TODO: Same as above */
+ int ba_cnt_rx; /*TODO: Same as above */
+ int ba_acc_rx; /*TODO: Same as above */
+ int ba_hist; /*TODO: Same as above */
+ struct timer_list ba_timer;/*TODO: Same as above */
+ spinlock_t ba_lock; /*TODO: Same as above */
+ bool ba_ena; /*TODO: Same as above */
+ struct work_struct ba_work; /*TODO: Same as above */
+ struct xradio_pm_state pm_state;
+ bool is_BT_Present;
+ bool is_go_thru_go_neg;
+ u8 conf_listen_interval;
+
+ /* BH */
+ atomic_t bh_tx;
+ atomic_t bh_term;
+ atomic_t bh_suspend;
+ struct task_struct *bh_thread;
+ int bh_error;
+#ifdef BH_USE_SEMAPHORE
+ struct semaphore bh_sem;
+ atomic_t bh_wk;
+#else
+ wait_queue_head_t bh_wq;
+#endif
+ wait_queue_head_t bh_evt_wq;
+
+
+ int buf_id_tx; /* byte */
+ int buf_id_rx; /* byte */
+ int wsm_rx_seq; /* byte */
+ int wsm_tx_seq; /* byte */
+ int hw_bufs_used;
+ int hw_bufs_used_vif[XRWL_MAX_VIFS];
+ struct sk_buff *skb_cache;
+ struct sk_buff *skb_reserved;
+ int skb_resv_len;
+ bool powersave_enabled;
+ bool device_can_sleep;
+ /* Keep xradio awake (WUP = 1) 1 second after each scan to avoid
+ * FW issue with sleeping/waking up. */
+ atomic_t recent_scan;
+ long connet_time[XRWL_MAX_VIFS];
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+ atomic_t suspend_state;
+#endif
+
+ /* WSM */
+ struct wsm_caps wsm_caps;
+ struct mutex wsm_cmd_mux;
+ struct wsm_buf wsm_cmd_buf;
+ struct wsm_cmd wsm_cmd;
+ wait_queue_head_t wsm_cmd_wq;
+ wait_queue_head_t wsm_startup_done;
+ struct wsm_cbc wsm_cbc;
+ struct semaphore tx_lock_sem;
+ atomic_t tx_lock;
+ u32 pending_frame_id;
+#ifdef CONFIG_XRADIO_TESTMODE
+ /* Device Power Range */
+ struct wsm_tx_power_range txPowerRange[2];
+ /* Advance Scan */
+ struct advance_scan_elems advanceScanElems;
+ bool enable_advance_scan;
+ struct delayed_work advance_scan_timeout;
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+ /* WSM debug */
+ int wsm_enable_wsm_dumps;
+ u32 wsm_dump_max_size;
+ u32 query_packetID;
+ atomic_t query_cnt;
+ struct work_struct query_work; /* for query packet */
+
+ /* Scan status */
+ struct xradio_scan scan;
+
+ /* TX/RX */
+ unsigned long rx_timestamp;
+
+ /* WSM events */
+ spinlock_t event_queue_lock;
+ struct list_head event_queue;
+ struct work_struct event_handler;
+
+ /* TX rate policy cache */
+ struct tx_policy_cache tx_policy_cache;
+ struct work_struct tx_policy_upload_work;
+ atomic_t upload_count;
+
+ /* cryptographic engine information */
+
+ /* bit field of glowing LEDs */
+ u16 softled_state;
+
+ /* statistics */
+ struct ieee80211_low_level_stats stats;
+
+ struct xradio_ht_oper ht_oper;
+ int tx_burst_idx;
+
+ struct ieee80211_iface_limit if_limits1[2];
+ struct ieee80211_iface_limit if_limits2[2];
+ struct ieee80211_iface_limit if_limits3[2];
+ struct ieee80211_iface_combination if_combs[3];
+
+ struct mutex wsm_oper_lock;
+ struct delayed_work rem_chan_timeout;
+ atomic_t remain_on_channel;
+ int roc_if_id;
+ u64 roc_cookie;
+ wait_queue_head_t offchannel_wq;
+ u16 offchannel_done;
+ u16 prev_channel;
+ int if_id_selected;
+ u32 key_map;
+ struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1];
+#ifdef MCAST_FWDING
+ struct wsm_buf wsm_release_buf[WSM_MAX_BUF];
+ u8 buf_released;
+#endif
+#ifdef ROAM_OFFLOAD
+ u8 auto_scanning;
+ u8 frame_rcvd;
+ u8 num_scanchannels;
+ u8 num_2g_channels;
+ u8 num_5g_channels;
+ struct wsm_scan_ch scan_channels[48];
+ struct sk_buff *beacon;
+ struct sk_buff *beacon_bkp;
+ struct xradio_testframe testframe;
+#endif /*ROAM_OFFLOAD*/
+#ifdef CONFIG_XRADIO_TESTMODE
+ struct xradio_testframe test_frame;
+ struct xr_tsm_stats tsm_stats;
+ struct xradio_tsm_info tsm_info;
+ spinlock_t tsm_lock;
+ struct xradio_start_stop_tsm start_stop_tsm;
+#endif /* CONFIG_XRADIO_TESTMODE */
+ u8 connected_sta_cnt;
+ u16 vif0_throttle;
+ u16 vif1_throttle;
+};
+
+/* Virtual Interface State. One copy per VIF */
+struct xradio_vif {
+ atomic_t enabled;
+ spinlock_t vif_lock;
+ int if_id;
+ /*TODO: Split into Common and VIF parts */
+ struct xradio_debug_priv *debug;
+ /* BBP/MAC state */
+ u8 bssid[ETH_ALEN];
+ struct wsm_edca_params edca;
+ struct wsm_tx_queue_params tx_queue_params;
+ struct wsm_association_mode association_mode;
+ struct wsm_set_bss_params bss_params;
+ struct wsm_set_pm powersave_mode;
+ struct wsm_set_pm firmware_ps_mode;
+ int power_set_true;
+ int user_power_set_true;
+ u8 user_pm_mode;
+ int cqm_rssi_thold;
+ unsigned cqm_rssi_hyst;
+ unsigned cqm_tx_failure_thold;
+ unsigned cqm_tx_failure_count;
+ bool cqm_use_rssi;
+ int cqm_link_loss_count;
+ int cqm_beacon_loss_count;
+ int mode;
+ bool enable_beacon;
+ int beacon_int;
+ size_t ssid_length;
+ u8 ssid[IEEE80211_MAX_SSID_LEN];
+#ifdef HIDDEN_SSID
+ bool hidden_ssid;
+#endif
+ bool listening;
+ struct wsm_rx_filter rx_filter;
+ struct wsm_beacon_filter_table bf_table;
+ struct wsm_beacon_filter_control bf_control;
+ struct wsm_multicast_filter multicast_filter;
+ bool has_multicast_subscription;
+ struct wsm_broadcast_addr_filter broadcast_filter;
+ bool disable_beacon_filter;
+ struct wsm_arp_ipv4_filter filter4;
+ struct work_struct update_filtering_work;
+ struct work_struct set_beacon_wakeup_period_work;
+ struct xradio_pm_state_vif pm_state_vif;
+ /*TODO: Add support in mac80211 for psmode info per VIF */
+ struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo;
+ struct wsm_uapsd_info uapsd_info;
+ bool setbssparams_done;
+ u32 listen_interval;
+ u32 erp_info;
+ bool powersave_enabled;
+
+ /* WSM Join */
+ enum xradio_join_status join_status;
+ u8 join_bssid[ETH_ALEN];
+ struct work_struct join_work;
+ struct delayed_work join_timeout;
+ struct work_struct unjoin_work;
+ struct work_struct offchannel_work;
+ int join_dtim_period;
+ atomic_t delayed_unjoin;
+
+ /* Security */
+ s8 wep_default_key_id;
+ struct work_struct wep_key_work;
+ unsigned long rx_timestamp;
+ u32 cipherType;
+
+
+ /* AP powersave */
+ u32 link_id_map;
+ u32 max_sta_ap_mode;
+ u32 link_id_after_dtim;
+ u32 link_id_uapsd;
+ u32 link_id_max;
+ u32 wsm_key_max_idx;
+ struct xradio_link_entry link_id_db[MAX_STA_IN_AP_MODE];
+ struct work_struct link_id_work;
+ struct delayed_work link_id_gc_work;
+ u32 sta_asleep_mask;
+ u32 pspoll_mask;
+ bool aid0_bit_set;
+ spinlock_t ps_state_lock;
+ bool buffered_multicasts;
+ bool tx_multicast;
+ u8 last_tim[8]; //for softap dtim, add by yangfh
+ struct work_struct set_tim_work;
+ struct delayed_work set_cts_work;
+ struct work_struct multicast_start_work;
+ struct work_struct multicast_stop_work;
+ struct timer_list mcast_timeout;
+
+ /* CQM Implementation */
+ struct delayed_work bss_loss_work;
+ struct delayed_work connection_loss_work;
+ struct work_struct tx_failure_work;
+ int delayed_link_loss;
+ spinlock_t bss_loss_lock;
+ int bss_loss_status;
+ int bss_loss_confirm_id;
+
+ struct ieee80211_vif *vif;
+ struct xradio_common *hw_priv;
+ struct ieee80211_hw *hw;
+
+ /* ROC implementation */
+ struct delayed_work pending_offchanneltx_work;
+#if defined(CONFIG_XRADIO_USE_EXTENSIONS)
+ /* Workaround for WFD testcase 6.1.10*/
+ struct work_struct linkid_reset_work;
+ u8 action_frame_sa[ETH_ALEN];
+ u8 action_linkid;
+#endif
+ bool htcap;
+#ifdef AP_HT_CAP_UPDATE
+ u16 ht_oper;
+ struct work_struct ht_oper_update_work;
+#endif
+
+#ifdef AP_HT_COMPAT_FIX
+ u16 ht_compat_cnt;
+ u16 ht_compat_det;
+#endif
+};
+struct xradio_sta_priv {
+ int link_id;
+ struct xradio_vif *priv;
+};
+enum xradio_data_filterid {
+ IPV4ADDR_FILTER_ID = 0,
+};
+
+/* Datastructure for LLC-SNAP HDR */
+#define P80211_OUI_LEN 3
+struct ieee80211_snap_hdr {
+ u8 dsap; /* always 0xAA */
+ u8 ssap; /* always 0xAA */
+ u8 ctrl; /* always 0x03 */
+ u8 oui[P80211_OUI_LEN]; /* organizational universal id */
+} __packed;
+
+
+#ifdef TES_P2P_0002_ROC_RESTART
+extern s32 TES_P2P_0002_roc_dur;
+extern s32 TES_P2P_0002_roc_sec;
+extern s32 TES_P2P_0002_roc_usec;
+extern u32 TES_P2P_0002_packet_id;
+extern u32 TES_P2P_0002_state;
+
+#define TES_P2P_0002_STATE_IDLE 0x00
+#define TES_P2P_0002_STATE_SEND_RESP 0x01
+#define TES_P2P_0002_STATE_GET_PKTID 0x02
+#endif
+
+/* debug.h must be here because refer to struct xradio_vif and
+ struct xradio_common.*/
+#include "debug.h"
+
+/*******************************************************
+ interfaces for operations of vif.
+********************************************************/
+static inline
+struct xradio_common *xrwl_vifpriv_to_hwpriv(struct xradio_vif *priv)
+{
+ return priv->hw_priv;
+}
+static inline
+struct xradio_vif *xrwl_get_vif_from_ieee80211(struct ieee80211_vif *vif)
+{
+ return (struct xradio_vif *)vif->drv_priv;
+}
+
+static inline
+struct xradio_vif *xrwl_hwpriv_to_vifpriv(struct xradio_common *hw_priv,
+ int if_id)
+{
+ struct xradio_vif *vif;
+
+ if (SYS_WARN((-1 == if_id) || (if_id > XRWL_MAX_VIFS)))
+ return NULL;
+ /* TODO:COMBO: During scanning frames can be received
+ * on interface ID 3 */
+ spin_lock(&hw_priv->vif_list_lock);
+ if (!hw_priv->vif_list[if_id]) {
+ spin_unlock(&hw_priv->vif_list_lock);
+ return NULL;
+ }
+
+ vif = xrwl_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
+ SYS_WARN(!vif);
+ if (vif)
+ spin_lock(&vif->vif_lock);
+ spin_unlock(&hw_priv->vif_list_lock);
+ return vif;
+}
+
+static inline
+struct xradio_vif *__xrwl_hwpriv_to_vifpriv(struct xradio_common *hw_priv,
+ int if_id)
+{
+ SYS_WARN((-1 == if_id) || (if_id > XRWL_MAX_VIFS));
+ /* TODO:COMBO: During scanning frames can be received
+ * on interface ID 3 */
+ if (!hw_priv->vif_list[if_id]) {
+ return NULL;
+ }
+
+ return xrwl_get_vif_from_ieee80211(hw_priv->vif_list[if_id]);
+}
+
+static inline
+struct xradio_vif *xrwl_get_activevif(struct xradio_common *hw_priv)
+{
+ return xrwl_hwpriv_to_vifpriv(hw_priv, ffs(hw_priv->if_id_slot)-1);
+}
+
+static inline bool is_hardware_xradio(struct xradio_common *hw_priv)
+{
+ return (hw_priv->hw_revision == XR819_HW_REV0);
+}
+
+static inline int xrwl_get_nr_hw_ifaces(struct xradio_common *hw_priv)
+{
+ switch(hw_priv->hw_revision) {
+ case XR819_HW_REV0:
+ default:
+ return 1;
+ }
+}
+
+#define xradio_for_each_vif(_hw_priv, _priv, _i) \
+ for( \
+ _i = 0; \
+ (_i < XRWL_MAX_VIFS) \
+ && ((_priv = _hw_priv->vif_list[_i] ? \
+ xrwl_get_vif_from_ieee80211(_hw_priv->vif_list[_i]) : NULL),1); \
+ _i++ \
+ )
+
+/*******************************************************
+ interfaces for operations of queue.
+********************************************************/
+static inline void xradio_tx_queues_lock(struct xradio_common *hw_priv)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ xradio_queue_lock(&hw_priv->tx_queue[i]);
+}
+
+static inline void xradio_tx_queues_unlock(struct xradio_common *hw_priv)
+{
+ int i;
+ for (i = 0; i < 4; ++i)
+ xradio_queue_unlock(&hw_priv->tx_queue[i]);
+}
+
+#endif /* XRADIO_H */