build/patch/kernel/sun8i-default/wireless-opi-zero.patch

28046 lines
820 KiB
Diff
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 65aa7b1..67d7b86 100755
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -299,2 +299,3 @@ source "drivers/net/wireless/wl12xx/Kconfig"
source "drivers/net/wireless/rtl8723bs/Kconfig"
source "drivers/net/wireless/rtl8703as/Kconfig"
+source "drivers/net/wireless/xradio/Kconfig"
endif # WLAN
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 8a0704a..d263d7b 100755
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -65,6 +64,6 @@ obj-$(CONFIG_IWM) += iwmc3200wifi/
obj-$(CONFIG_MWIFIEX) += mwifiex/
obj-$(CONFIG_BCMDHD) += bcmdhd/
-
+obj-$(CONFIG_XRADIO) += xradio/
obj-$(CONFIG_BRCMFMAC) += brcm80211/
obj-$(CONFIG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/xradio/Kconfig b/drivers/net/wireless/xradio/Kconfig
new file mode 100644
index 0000000..67b11c4
--- /dev/null
+++ b/drivers/net/wireless/xradio/Kconfig
@@ -0,0 +1,62 @@
+config 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.
+
+if XRADIO
+
+config XRADIO_SDIO
+ bool "SDIO bus wlan module"
+ depends on XRADIO
+ default y
+ ---help---
+ Say N here if module don't use sdio bus.
+ If unsure, say Y.
+
+config XRADIO_NON_POWER_OF_TWO_BLOCKSIZES
+ bool "Platform supports non-power-of-two SDIO transfer"
+ depends on 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_USE_GPIO_IRQ
+ bool "Use GPIO interrupt"
+ depends on XRADIO
+ default y
+ ---help---
+ Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ.
+ If unsure, say N.
+
+config XRADIO_5GHZ_SUPPORT
+ bool "5GHz band support"
+ depends on 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 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 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 0000000..235d117
--- /dev/null
+++ b/drivers/net/wireless/xradio/Makefile
@@ -0,0 +1,62 @@
+xradio_wlan-y := \
+ common.o \
+ fwio.o \
+ txrx.o \
+ main.o \
+ queue.o \
+ hwio.o \
+ bh.o \
+ wsm.o \
+ sta.o \
+ ap.o \
+ scan.o \
+ platform.o \
+ debug.o
+
+xradio_wlan-$(CONFIG_PM) += pm.o
+xradio_wlan-$(CONFIG_XRADIO_SDIO) += sdio.o
+xradio_wlan-$(CONFIG_XRADIO_ITP) += itp.o
+
+ccflags-y += -DP2P_MULTIVIF
+ccflags-y += -DMCAST_FWDING
+ccflags-y += -DXRADIO_SUSPEND_RESUME_FILTER_ENABLE
+ccflags-y += -DAP_AGGREGATE_FW_FIX
+ccflags-y += -DAP_HT_CAP_UPDATE
+ccflags-y += -DHW_RESTART
+ccflags-y += -DHW_ERROR_WIFI_RESET
+ccflags-y += -DAP_HT_COMPAT_FIX
+ccflags-y += -DCONFIG_XRADIO_DEBUG
+ccflags-y += -DCONFIG_XRADIO_DUMP_ON_ERROR
+ccflags-y += -DCONFIG_XRADIO_DEBUGFS
+
+ccflags-y += -DCONFIG_XRADIO_SUSPEND_POWER_OFF
+# Use vfs for firmware load when request_firmware
+# can't work on other platform.
+# ccflags-y += -DUSE_VFS_FIRMWARE
+
+# 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-y += -DPROBE_RESP_EXTRA_IE
+ccflags-y += -DIPV6_FILTERING
+
+# Modified by wzw
+ccflags-y += -DTES_P2P_0002_ROC_RESTART
+ccflags-y += -DTES_P2P_000B_EXTEND_INACTIVITY_CNT
+ccflags-y += -DTES_P2P_000B_DISABLE_EAPOL_FILTER
+ccflags-y += -DXRADIO_USE_LONG_DTIM_PERIOD
+ccflags-y += -DXRADIO_USE_LONG_KEEP_ALIVE_PERIOD
+
+# Mac addr config, disable hex for default.
+#ccflags-y += -DXRADIO_MACPARAM_HEX
+
+# Mac addr generate from chipid.
+ccflags-y += -DXRADIO_MACADDR_FROM_CHIPID
+
+# Use semaphore to sync bh txrx.
+#ccflags-y += -DBH_USE_SEMAPHORE
+
+ldflags-y += --strip-debug
+
+obj-$(CONFIG_XRADIO) += xradio_wlan.o
+
diff --git a/drivers/net/wireless/xradio/ap.c b/drivers/net/wireless/xradio/ap.c
new file mode 100644
index 0000000..3a6cd12
--- /dev/null
+++ b/drivers/net/wireless/xradio/ap.c
@@ -0,0 +1,1786 @@
+/*
+ * 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
+
+#ifdef IPV6_FILTERING
+#define XRADIO_ENABLE_NDP_FILTER_OFFLOAD 3
+#endif /*IPV6_FILTERING*/
+
+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
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#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 */
+ ap_printk(XRADIO_DBG_ERROR,"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;
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#ifdef P2P_MULTIVIF
+ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID);
+#endif
+
+ if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) {
+ ap_printk(XRADIO_DBG_NIY, "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;
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ /* 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;
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#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)
+{
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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);
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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;
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#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;
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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;
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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);
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#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 enabled: %d, cnt: %d\n",
+ info->arp_filter_enabled, info->arp_addr_cnt);
+
+ if (info->arp_filter_enabled){
+ 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;
+ }
+ }
+
+#ifdef IPV6_FILTERING
+ /*in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have BSS_CHANGED_NDP_FILTER enum value*/
+#if 0
+ if (changed & BSS_CHANGED_NDP_FILTER) {
+ int i;
+ struct wsm_ndp_ipv6_filter filter = {0};
+ u16 *ipv6addr = NULL;
+
+ ap_printk(XRADIO_DBG_MSG, "[STA] BSS_CHANGED_NDP_FILTER enabled: %d, cnt: %d\n",
+ info->ndp_filter_enabled, info->ndp_addr_cnt);
+
+ if (info->ndp_filter_enabled) {
+ if (vif->type == NL80211_IFTYPE_STATION)
+ filter.enable = (u32)XRADIO_ENABLE_NDP_FILTER_OFFLOAD;
+ else if ((vif->type == NL80211_IFTYPE_AP))
+ filter.enable = (u32)(1<<1);
+ else
+ filter.enable = 0;
+ }
+
+ /* Currently only one IP address is supported by firmware.
+ * In case of more IPs ndp filtering will be disabled. */
+ if (info->ndp_addr_cnt > 0 &&
+ info->ndp_addr_cnt <= WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES) {
+ for (i = 0; i < info->ndp_addr_cnt; i++) {
+ priv->filter6.ipv6Address[i] = filter.ipv6Address[i] = info->ndp_addr_list[i];
+ ipv6addr = (u16 *)(&filter.ipv6Address[i]);
+ ap_printk(XRADIO_DBG_MSG, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \
+ i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \
+ cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \
+ cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \
+ cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7)));
+ }
+ } else {
+ filter.enable = 0;
+ for (i = 0; i < info->ndp_addr_cnt; i++) {
+ ipv6addr = (u16 *)(&info->ndp_addr_list[i]);
+ ap_printk(XRADIO_DBG_MSG, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \
+ i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \
+ cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \
+ cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \
+ cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7)));
+ }
+ }
+
+ ap_printk(XRADIO_DBG_NIY, "[STA] ndp ip filter enable: %d\n",
+ __le32_to_cpu(filter.enable));
+
+ if (filter.enable)
+ xradio_set_na(dev, vif);
+
+ priv->filter6.enable = filter.enable;
+
+ if (wsm_set_ndp_ipv6_filter(hw_priv, &filter, priv->if_id))
+ SYS_WARN(1);
+#if 0 /*Commented out to disable Power Save in IPv6*/
+ if (filter.enable && (priv->join_status == XRADIO_JOIN_STATUS_STA) &&
+ (priv->vif->p2p) && !(priv->firmware_ps_mode.pmMode & WSM_PSM_FAST_PS)) {
+ if(priv->setbssparams_done) {
+ int ret = 0;
+ struct wsm_set_pm pm = priv->powersave_mode;
+
+ 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;
+ }
+ }
+#endif
+ }
+#endif
+#endif /*IPV6_FILTERING*/
+
+ 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_info.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]));
+ hw_priv->ht_info.channel_type = info->channel_type;
+ hw_priv->ht_info.operation_mode = info->ht_operation_mode;
+ } else {
+ memset(&hw_priv->ht_info, 0, sizeof(hw_priv->ht_info));
+ priv->bss_params.operationalRateSet = -1;
+ }
+ rcu_read_unlock();
+ priv->htcap = (sta && xradio_is_ht(&hw_priv->ht_info));
+ 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_info.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_info);
+ 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_info);
+
+#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;
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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);
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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_TRC,"%s\n", __FUNCTION__);
+
+ 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,
+ enum ieee80211_ampdu_mlme_action action,
+ struct ieee80211_sta *sta,
+ u16 tid, u16 *ssn, u8 buf_size)
+{
+ /* 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;
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ switch (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);
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#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_info;
+ u32 ies_len;
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if (priv->vif->p2p || hw_priv->channel->band == IEEE80211_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_info = (u8 *)cfg80211_find_ie( WLAN_EID_HT_INFORMATION, ies, ies_len);
+ if (ht_info) {
+ /* Enable RIFS*/
+ ht_info[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
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if (priv->vif->p2p || hw_priv->channel->band == IEEE80211_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,
+ };
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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,
+ };
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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,
+ };
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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 == IEEE80211_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);
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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;
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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;
+
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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,
+ };
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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_info_update_work(struct work_struct *work)
+{
+ struct sk_buff *skb;
+ struct ieee80211_mgmt *mgmt;
+ u8 *ht_info, *ies;
+ u32 ies_len;
+ struct xradio_vif *priv =
+ container_of(work, struct xradio_vif, ht_info_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,
+ };
+ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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_info= (u8 *)cfg80211_find_ie( WLAN_EID_HT_INFORMATION, ies, ies_len);
+ if(ht_info && priv->ht_info == HT_INFO_MASK) {
+ ht_info[HT_INFO_OFFSET] |= 0x11;
+ update_ie.ies = ht_info;
+ 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 0000000..be46c00
--- /dev/null
+++ b/drivers/net/wireless/xradio/ap.h
@@ -0,0 +1,62 @@
+/*
+ * 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,
+ 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_info_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 0000000..abb3610
--- /dev/null
+++ b/drivers/net/wireless/xradio/bh.c
@@ -0,0 +1,962 @@
+/*
+ * 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 "sbus.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 };
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ SYS_BUG(hw_priv->bh_thread);
+ atomic_set(&hw_priv->bh_rx, 0);
+ 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;
+#ifdef BH_USE_SEMAPHORE
+ sema_init(&hw_priv->bh_sem, 0);
+ atomic_set(&hw_priv->bh_wk, 0);
+#else
+ init_waitqueue_head(&hw_priv->bh_wq);
+#endif
+ 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;
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ if (SYS_WARN(!thread))
+ return;
+
+ hw_priv->bh_thread = NULL;
+ kthread_stop(thread);
+#ifdef HAS_PUT_TASK_STRUCT
+ put_task_struct(thread);
+#endif
+ bh_printk(XRADIO_DBG_NIY, "Unregister success.\n");
+}
+
+void xradio_irq_handler(struct xradio_common *hw_priv)
+{
+
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ DBG_BH_IRQ_ADD;
+ if (/* SYS_WARN */(hw_priv->bh_error))
+ return;
+#ifdef BH_USE_SEMAPHORE
+ atomic_add(1, &hw_priv->bh_rx);
+ if (atomic_add_return(1, &hw_priv->bh_wk) == 1) {
+ up(&hw_priv->bh_sem);
+ }
+#else
+ if (atomic_add_return(1, &hw_priv->bh_rx) == 1) {
+ wake_up(&hw_priv->bh_wq);
+ }
+#endif
+
+}
+
+void xradio_bh_wakeup(struct xradio_common *hw_priv)
+{
+ bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+ if (SYS_WARN(hw_priv->bh_error))
+ return;
+#ifdef BH_USE_SEMAPHORE
+ atomic_add(1, &hw_priv->bh_tx);
+ if (atomic_add_return(1, &hw_priv->bh_wk) == 1) {
+ up(&hw_priv->bh_sem);
+ }
+#else
+ if (atomic_add_return(1, &hw_priv->bh_tx) == 1) {
+ wake_up(&hw_priv->bh_wq);
+ }
+#endif
+}
+
+int xradio_bh_suspend(struct xradio_common *hw_priv)
+{
+
+#ifdef MCAST_FWDING
+ int i =0;
+ struct xradio_vif *priv = NULL;
+#endif
+
+ bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+ 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);
+#ifdef BH_USE_SEMAPHORE
+ up(&hw_priv->bh_sem);
+#else
+ wake_up(&hw_priv->bh_wq);
+#endif
+ 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
+
+ bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+ if (hw_priv->bh_error) {
+ return -EINVAL;
+ }
+
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUME);
+#ifdef BH_USE_SEMAPHORE
+ up(&hw_priv->bh_sem);
+#else
+ wake_up(&hw_priv->bh_wq);
+#endif
+
+#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));
+ bh_printk(XRADIO_DBG_NIY, "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;
+ bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+
+ 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.
+ */
+ bh_printk(XRADIO_DBG_ERROR,"%s, hw_bufs_used=%d, count=%d.\n",
+ __func__, 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;
+ bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+
+ 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;
+ bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__);
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_AP) {
+ return 0;
+ }
+ bh_printk(XRADIO_DBG_NIY, "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));
+
+ bh_printk(XRADIO_DBG_NIY, "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 */
+ bh_printk(XRADIO_DBG_ERROR,"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 */
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ hw_priv->skb_reserved = xr_alloc_skb(len);
+ if (hw_priv->skb_reserved) {
+ hw_priv->skb_resv_len = len;
+ } else {
+ bh_printk(XRADIO_DBG_WARN,"%s xr_alloc_skb failed(%d)\n",
+ __func__, len);
+ }
+ return 0;
+}
+
+void xradio_deinit_resv_skb(struct xradio_common *hw_priv)
+{
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ 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 = xr_alloc_skb(hw_priv->skb_resv_len);
+ if (!hw_priv->skb_reserved) {
+ hw_priv->skb_reserved = skb;
+ bh_printk(XRADIO_DBG_WARN, "%s xr_alloc_skb failed(%d)\n",
+ __FUNCTION__, 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;
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ /* 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 = xr_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) {
+ bh_printk(XRADIO_DBG_WARN,"%s get skb_reserved(%d)!\n",
+ __FUNCTION__, alloc_len);
+ skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */
+ - WSM_RX_EXTRA_HEADROOM);
+ } else {
+ bh_printk(XRADIO_DBG_ERROR,"%s xr_alloc_skb failed(%d)!\n",
+ __FUNCTION__, 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)
+{
+ bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ 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;
+ bh_printk(XRADIO_DBG_ERROR, "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;
+
+ bh_printk(XRADIO_DBG_MSG, "%s\n", __FUNCTION__);
+
+ /* 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)) {
+ bh_printk(XRADIO_DBG_ERROR, "Device cannot wakeup.\n");
+ return -1;
+ } else if (unlikely(i >= 50))
+ bh_printk(XRADIO_DBG_WARN, "Device wakeup time=%dms.\n", i);
+ bh_printk(XRADIO_DBG_NIY, "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;
+ bh_printk(XRADIO_DBG_NIY, "Powerave is %s.\n", enable ? "enabled" : "disabled");
+}
+
+
+static int xradio_bh(void *arg)
+{
+ struct xradio_common *hw_priv = arg;
+ struct sk_buff *skb_rx = NULL;
+ size_t read_len = 0;
+ int rx = 0, tx = 0, term, suspend;
+ struct wsm_hdr *wsm;
+ size_t wsm_len;
+ int wsm_id;
+ u8 wsm_seq;
+ int rx_resync = 1;
+ u16 ctrl_reg = 0;
+ int tx_allowed;
+ int pending_tx = 0;
+ int tx_burst;
+ int rx_burst = 0;
+ long status;
+ u32 dummy;
+ int vif_selected;
+
+ bh_printk(XRADIO_DBG_MSG, "%s\n", __FUNCTION__);
+
+ for (;;) {
+ /* Check if devices can sleep, and set time to wait for interrupt. */
+ if (!hw_priv->hw_bufs_used && !pending_tx &&
+ hw_priv->powersave_enabled && !hw_priv->device_can_sleep &&
+ !atomic_read(&hw_priv->recent_scan) &&
+ atomic_read(&hw_priv->bh_rx) == 0 &&
+ atomic_read(&hw_priv->bh_tx) == 0) {
+ bh_printk(XRADIO_DBG_MSG, "Device idle, can sleep.\n");
+ SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0));
+ hw_priv->device_can_sleep = true;
+ status = HZ/8; //125ms
+ } else if (hw_priv->hw_bufs_used) {
+ /* don't wait too long if some frames to confirm
+ * and miss interrupt.*/
+ status = HZ/20; //50ms.
+ } else {
+ status = HZ/8; //125ms
+ }
+
+ /* Dummy Read for SDIO retry mechanism*/
+ if (atomic_read(&hw_priv->bh_rx) == 0 &&
+ atomic_read(&hw_priv->bh_tx) == 0) {
+ xradio_reg_read(hw_priv, HIF_CONFIG_REG_ID, &dummy, sizeof(dummy));
+ }
+ /* If a packet has already been txed to the device then read the
+ * control register for a probable interrupt miss before going
+ * further to wait for interrupt; if the read length is non-zero
+ * then it means there is some data to be received */
+ if (hw_priv->hw_bufs_used) {
+ xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+ if(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) {
+ DBG_BH_FIX_RX_ADD;
+ rx = 1;
+ goto data_proc;
+ }
+ }
+
+ /* Wait for Events in HZ/8 */
+#ifdef BH_USE_SEMAPHORE
+ rx = atomic_xchg(&hw_priv->bh_rx, 0);
+ tx = atomic_xchg(&hw_priv->bh_tx, 0);
+ suspend = pending_tx ? 0 : atomic_read(&hw_priv->bh_suspend);
+ term = kthread_should_stop();
+ if (!(rx || tx || term || suspend || hw_priv->bh_error)) {
+ atomic_set(&hw_priv->bh_wk, 0);
+ status = (long)(down_timeout(&hw_priv->bh_sem, status) != -ETIME);
+ //if (status && !atomic_read(&hw_priv->bh_rx) && !atomic_read(&hw_priv->bh_tx))
+ // bh_printk(XRADIO_DBG_ALWY, "bh+\n");
+ }
+#else
+ status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({
+ rx = atomic_xchg(&hw_priv->bh_rx, 0);
+ tx = atomic_xchg(&hw_priv->bh_tx, 0);
+ term = kthread_should_stop();
+ suspend = pending_tx ? 0 : atomic_read(&hw_priv->bh_suspend);
+ (rx || tx || term || suspend || hw_priv->bh_error);}),
+ status);
+#endif
+
+ /* 0--bh is going to be shut down */
+ if(term) {
+ bh_printk(XRADIO_DBG_MSG, "xradio_bh exit!\n");
+ break;
+ }
+ /* 1--An fatal error occurs */
+ if (status < 0 || hw_priv->bh_error) {
+ bh_printk(XRADIO_DBG_ERROR, "bh_error=%d, status=%ld\n",
+ hw_priv->bh_error, status);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+
+ /* 2--Wait for interrupt time out */
+ if (!status) {
+ /* Check if miss interrupt. */
+ xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+ if(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) {
+ bh_printk(XRADIO_DBG_WARN, "miss interrupt!\n" );
+ DBG_BH_MISS_ADD;
+ rx = 1;
+ goto data_proc;
+ }
+
+ /* There are some frames to be confirmed. */
+ if (hw_priv->hw_bufs_used) {
+ long timeout = 0;
+ bool pending = 0;
+ bh_printk(XRADIO_DBG_NIY, "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) {
+ bh_printk(XRADIO_DBG_ERROR, "query_txpkt_timeout:%ld!\n", timeout);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ rx = 1; /* Go to check rx again. */
+ } else if (!pending_tx){
+ if (hw_priv->powersave_enabled && !hw_priv->device_can_sleep && !atomic_read(&hw_priv->recent_scan)) {
+ /* Device is idle, we can go to sleep. */
+ bh_printk(XRADIO_DBG_MSG, "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;
+ }
+
+ /* 3--Host suspend request. */
+ } else if (suspend) {
+ bh_printk(XRADIO_DBG_NIY, "Host suspend request.\n");
+ /* Check powersave setting again. */
+ if (hw_priv->powersave_enabled) {
+ bh_printk(XRADIO_DBG_MSG,
+ "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);
+#ifdef BH_USE_SEMAPHORE
+ do {
+ status = down_interruptible(&hw_priv->bh_sem);
+ } while (!status && XRADIO_BH_RESUME != atomic_read(&hw_priv->bh_suspend));
+ if (XRADIO_BH_RESUME != atomic_read(&hw_priv->bh_suspend))
+ status = -1;
+ else
+ status = 0;
+#else
+ status = wait_event_interruptible(hw_priv->bh_wq,
+ XRADIO_BH_RESUME == atomic_read(&hw_priv->bh_suspend));
+#endif
+ if (status < 0) {
+ bh_printk(XRADIO_DBG_ERROR,"ERR: Failed to wait for resume: %ld.\n", status);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ bh_printk(XRADIO_DBG_NIY, "Host resume.\n");
+ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED);
+ wake_up(&hw_priv->bh_evt_wq);
+ atomic_add(1, &hw_priv->bh_rx);
+ continue;
+ }
+ /* 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);
+ }
+
+ /* 4--Rx & Tx process. */
+data_proc:
+ tx += pending_tx;
+ pending_tx = 0;
+
+ if (rx) {
+ size_t alloc_len;
+ u8 *data;
+ /* Check ctrl_reg again. */
+ if(!(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK))
+ if (SYS_WARN(xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg))) {
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+rx:
+ read_len = (ctrl_reg & HIF_CTRL_NEXT_LEN_MASK)<<1; //read_len=ctrl_reg*2.
+ if (!read_len) {
+ rx_burst = 0;
+ goto tx;
+ }
+ if (SYS_WARN((read_len < sizeof(struct wsm_hdr)) ||
+ (read_len > EFFECTIVE_BUF_SIZE))) {
+ bh_printk(XRADIO_DBG_ERROR, "ERR: Invalid read len: %d", read_len);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+
+ /* 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 = hw_priv->sbus_ops->align_size(hw_priv->sbus_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)) {
+ bh_printk(XRADIO_DBG_MSG, "ERR: Read aligned len: %d\n", alloc_len);
+ }
+
+ /* Get skb buffer. */
+ skb_rx = xradio_get_skb(hw_priv, alloc_len);
+ if (SYS_WARN(!skb_rx)) {
+ bh_printk(XRADIO_DBG_ERROR, "ERR: xradio_get_skb failed.\n");
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ skb_trim(skb_rx, 0);
+ skb_put(skb_rx, read_len);
+ data = skb_rx->data;
+ if (SYS_WARN(!data)) {
+ bh_printk(XRADIO_DBG_ERROR, "ERR: skb data is NULL.\n");
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+
+ /* Read data from device. */
+ if (SYS_WARN(xradio_data_read(hw_priv, data, alloc_len))) {
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ DBG_BH_RX_TOTAL_ADD;
+
+ /* Piggyback */
+ ctrl_reg = __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)) {
+ bh_printk(XRADIO_DBG_ERROR, "wsm_len=%d.\n", wsm_len);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+
+ /* dump rx data. */
+#if defined(CONFIG_XRADIO_DEBUG)
+ if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
+ u16 msgid, ifid;
+ u16 *p = (u16 *)data;
+ msgid = (*(p + 1)) & 0xC3F;
+ ifid = (*(p + 1)) >> 6;
+ ifid &= 0xF;
+ bh_printk(XRADIO_DBG_ALWY, "[DUMP] msgid 0x%.4X ifid %d len %d\n",
+ msgid, ifid, *p);
+ print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE,
+ data, min(wsm_len, hw_priv->wsm_dump_max_size));
+ }
+#endif /* CONFIG_XRADIO_DEBUG */
+
+ /* 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 (unlikely(wsm_id == 0x0800)) {
+ bh_printk(XRADIO_DBG_ERROR, "firmware exception!\n");
+ wsm_handle_exception(hw_priv, &data[sizeof(*wsm)], wsm_len-sizeof(*wsm));
+ hw_priv->bh_error = __LINE__;
+ break;
+ } else if (unlikely(!rx_resync)) {
+ if (SYS_WARN(wsm_seq != hw_priv->wsm_rx_seq)) {
+ bh_printk(XRADIO_DBG_ERROR, "wsm_seq=%d.\n", wsm_seq);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ }
+ hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7;
+ rx_resync = 0;
+#if defined(DGB_XRADIO_HWT)
+ rx_resync = 1; //0 -> 1, HWT test, should not check this.
+#endif
+
+ /* Process tx frames confirm. */
+ if (wsm_id & 0x0400) {
+ int rc = wsm_release_tx_buffer(hw_priv, 1);
+ if (SYS_WARN(rc < 0)) {
+ bh_printk(XRADIO_DBG_ERROR, "tx buffer < 0.\n");
+ hw_priv->bh_error = __LINE__;
+ break;
+ } else if (rc > 0)
+ tx = 1;
+ }
+
+ /* WSM processing frames. */
+ if (SYS_WARN(wsm_handle_rx(hw_priv, wsm_id, wsm, &skb_rx))) {
+ bh_printk(XRADIO_DBG_ERROR, "wsm_handle_rx failed.\n");
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+
+ /* Reclaim the SKB buffer */
+ if (skb_rx) {
+ if(xradio_put_resv_skb(hw_priv, skb_rx))
+ xradio_put_skb(hw_priv, skb_rx);
+ skb_rx = NULL;
+ }
+ read_len = 0;
+
+ /* Check if rx burst */
+ if (rx_burst) {
+ xradio_debug_rx_burst(hw_priv);
+ --rx_burst;
+ goto rx;
+ }
+ }
+
+tx:
+ SYS_BUG(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs);
+ tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used;
+ tx_allowed = tx_burst > 0;
+ if (tx && tx_allowed) {
+ int ret;
+ u8 *data;
+ size_t tx_len;
+
+ /* Wake up the devices */
+ if (hw_priv->device_can_sleep) {
+ ret = xradio_device_wakeup(hw_priv);
+ if (SYS_WARN(ret < 0)) {
+ hw_priv->bh_error = __LINE__;
+ break;
+ } else if (ret) {
+ hw_priv->device_can_sleep = false;
+ } else { /* Wait for "awake" interrupt */
+ pending_tx = tx;
+ continue;
+ }
+ }
+ /* Increase Tx buffer*/
+ wsm_alloc_tx_buffer(hw_priv);
+
+#if defined(DGB_XRADIO_HWT)
+ //hardware test.
+ ret = get_hwt_hif_tx(hw_priv, &data, &tx_len, &tx_burst, &vif_selected);
+ if (ret <= 0)
+#endif //DGB_XRADIO_HWT
+ /* Get data to send and send it. */
+ ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst, &vif_selected);
+ if (ret <= 0) {
+ wsm_release_tx_buffer(hw_priv, 1);
+ if (SYS_WARN(ret < 0)) {
+ bh_printk(XRADIO_DBG_ERROR, "wsm_get_tx=%d.\n", ret);
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ } else {
+ wsm = (struct wsm_hdr *)data;
+ SYS_BUG(tx_len < sizeof(*wsm));
+ SYS_BUG(__le32_to_cpu(wsm->len) != tx_len);
+
+ /* Continue to send next data if have any. */
+ atomic_add(1, &hw_priv->bh_tx);
+
+ /* Align tx length and check it. */
+#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES)
+ if (tx_len <= 8)
+ tx_len = 16;
+ tx_len = hw_priv->sbus_ops->align_size(hw_priv->sbus_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) {
+ bh_printk(XRADIO_DBG_WARN, "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);
+ bh_printk(XRADIO_DBG_ERROR, "xradio_data_write failed\n");
+ hw_priv->bh_error = __LINE__;
+ break;
+ }
+ DBG_BH_TX_TOTAL_ADD;
+
+#if defined(CONFIG_XRADIO_DEBUG)
+ if (unlikely(hw_priv->wsm_enable_wsm_dumps)) {
+ u16 msgid, ifid;
+ u16 *p = (u16 *)data;
+ msgid = (*(p + 1)) & 0x3F;
+ ifid = (*(p + 1)) >> 6;
+ ifid &= 0xF;
+ if (msgid == 0x0006) {
+ bh_printk(XRADIO_DBG_ALWY, "[DUMP] >>> msgid 0x%.4X ifid %d"
+ "len %d MIB 0x%.4X\n", msgid, ifid,*p, *(p + 2));
+ } else {
+ bh_printk(XRADIO_DBG_ALWY, "[DUMP] >>> msgid 0x%.4X ifid %d "
+ "len %d\n", msgid, ifid, *p);
+ }
+ print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, data,
+ min(__le32_to_cpu(wsm->len),
+ hw_priv->wsm_dump_max_size));
+ }
+#endif /* CONFIG_XRADIO_DEBUG */
+
+ /* 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;
+
+ /* Check for burst. */
+ if (tx_burst > 1) {
+ xradio_debug_tx_burst(hw_priv);
+ ++rx_burst;
+ goto tx;
+ }
+ }
+ } else {
+ pending_tx = tx; //if not allow to tx, pending it.
+ }
+
+ /* Check if there are frames to be read. */
+ if (ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) {
+ DBG_BH_NEXT_RX_ADD;
+ goto rx;
+ } else {
+ xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg);
+ if(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) {
+ DBG_BH_FIX_RX_ADD;
+ goto rx;
+ }
+ }
+ } /* for (;;)*/
+
+ /* Reclaim the SKB buffer when exit. */
+ if (skb_rx) {
+ if(xradio_put_resv_skb(hw_priv, skb_rx))
+ xradio_put_skb(hw_priv, skb_rx);
+ skb_rx = NULL;
+ }
+
+ /* If BH Error, handle it. */
+ if (!term) {
+ bh_printk(XRADIO_DBG_ERROR, "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 (;;) {
+#ifdef BH_USE_SEMAPHORE
+ status = down_interruptible(&hw_priv->bh_sem);
+ term = kthread_should_stop();
+#else
+ int status = wait_event_interruptible(hw_priv->bh_wq, ({
+ term = kthread_should_stop();
+ (term);}));
+#endif
+ if (status || term)
+ break;
+ }
+#endif
+ }
+ atomic_add(1, &hw_priv->bh_term); //debug info, show bh status.
+ return 0;
+}
diff --git a/drivers/net/wireless/xradio/bh.h b/drivers/net/wireless/xradio/bh.h
new file mode 100644
index 0000000..ca9187e
--- /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.c b/drivers/net/wireless/xradio/common.c
new file mode 100644
index 0000000..ce0039e
--- /dev/null
+++ b/drivers/net/wireless/xradio/common.c
@@ -0,0 +1,166 @@
+/*
+ * 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_C
+#define XRADIO_COMMON_C
+#include <linux/init.h>
+#include <linux/vmalloc.h>
+
+#include "xradio.h"
+
+struct xr_file *xr_fileopen(const char *path, int open_mode, umode_t mode)
+{
+ struct xr_file *xr_fp = NULL;
+ if (!path) return NULL;
+
+ xr_fp = xr_kmalloc(sizeof(struct xr_file), false);
+ if (!xr_fp) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:xr_kmalloc failed,size=%d!\n",
+ __func__, sizeof(struct xr_file));
+ return NULL;
+ }
+ memset(xr_fp, 0, sizeof(struct xr_file));
+
+ //open file.
+ xr_fp->fp = filp_open(path, open_mode, mode);
+ if (IS_ERR(xr_fp->fp)) {
+ xradio_dbg(XRADIO_DBG_ERROR, "filp_open failed(%d)\n", (int)xr_fp->fp);
+ kfree(xr_fp);
+ return NULL;
+ }
+
+ //get file size.
+ if(xr_fp->fp->f_op->llseek != NULL) {
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ xr_fp->size = vfs_llseek(xr_fp->fp, 0, SEEK_END);
+ vfs_llseek(xr_fp->fp, 0, SEEK_SET);
+ set_fs(old_fs);
+ } else {
+ xr_fp->size = 0;
+ }
+ return xr_fp;
+}
+
+int xr_fileclose(const struct xr_file *xr_fp)
+{
+ if (xr_fp) {
+ if (!IS_ERR(xr_fp->fp))
+ filp_close(xr_fp->fp, NULL);
+ if (xr_fp->data)
+ kfree(xr_fp->data);
+ kfree(xr_fp);
+ return 0;
+ }
+ return -1;
+}
+
+int xr_fileread(const struct xr_file *xr_fp, char *buffer, int size)
+{
+ int ret = -1;
+ if(xr_fp && buffer && xr_fp->fp->f_op->read != NULL) {
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = vfs_read(xr_fp->fp, buffer, size, &xr_fp->fp->f_pos);
+ set_fs(old_fs);
+ }
+ return ret;
+}
+
+int xr_filewrite(const struct xr_file *xr_fp, char *buffer, int size)
+{
+ int ret = -1;
+ if(xr_fp && buffer && xr_fp->fp->f_op->write != NULL) {
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = vfs_write(xr_fp->fp, buffer, size, &xr_fp->fp->f_pos);
+ set_fs(old_fs);
+ }
+ return ret;
+}
+
+struct xr_file *xr_request_file(const char *path)
+{
+ int ret = -1;
+ struct xr_file *xr_fp = xr_fileopen(path, O_RDONLY, 0);
+ if (!xr_fp) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:xr_fileopen failed!\n", __func__);
+ return NULL;
+ }
+
+ //get file data.
+ if (xr_fp->size) {
+ xr_fp->data = xr_kmalloc(xr_fp->size, false);
+ if(xr_fp->data && xr_fp->fp->f_op->read != NULL) {
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = vfs_read(xr_fp->fp, xr_fp->data, xr_fp->size, &xr_fp->fp->f_pos);
+ set_fs(old_fs);
+ }
+ }
+ if (ret < 0) {
+ xr_fileclose(xr_fp);
+ xr_fp = NULL;
+ }
+ return xr_fp;
+}
+
+/* To use macaddr and ps mode of customers */
+int access_file(char *path, char *buffer, int size, int isRead)
+{
+ int ret=0;
+ struct file *fp;
+ mm_segment_t old_fs = get_fs();
+
+ if(isRead)
+ fp = filp_open(path, O_RDONLY, S_IRUSR);
+ else
+ fp = filp_open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
+
+ if (IS_ERR(fp)) {
+ if ((int)fp != -ENOENT)
+ xradio_dbg(XRADIO_DBG_ERROR, "can't open %s, failed(%d)\n", path, (int)fp);
+ return -1;
+ }
+
+ if(isRead)
+ {
+ if(fp->f_op->read == NULL) {
+ xradio_dbg(XRADIO_DBG_ERROR, "read %s Not allow.\n", path);
+ return -1;
+ }
+ else {
+ fp->f_pos = 0;
+ set_fs(KERNEL_DS);
+ ret = vfs_read(fp, buffer, size, &fp->f_pos);
+ set_fs(old_fs);
+ }
+ }
+ else
+ {
+ if(fp->f_op->write == NULL) {
+ xradio_dbg(XRADIO_DBG_ERROR, "write %s Not allow.\n", path);
+ return -1;
+ }
+ else {
+ fp->f_pos = 0;
+ set_fs(KERNEL_DS);
+ ret = vfs_write(fp, buffer, size, &fp->f_pos);
+ set_fs(old_fs);
+ }
+ }
+ filp_close(fp,NULL);
+
+ xradio_dbg(XRADIO_DBG_NIY, "Access_file %s(%d)\n", path, ret);
+ return ret;
+}
+
+#endif //XRADIO_COMMON_C
\ No newline at end of file
diff --git a/drivers/net/wireless/xradio/common.h b/drivers/net/wireless/xradio/common.h
new file mode 100644
index 0000000..10a265b
--- /dev/null
+++ b/drivers/net/wireless/xradio/common.h
@@ -0,0 +1,162 @@
+/*
+ * 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
+
+#include <linux/version.h>
+
+/*******************************************************
+ interfaces for file operation.
+********************************************************/
+struct xr_file {
+ struct file *fp;
+ char *data;
+ size_t size;
+};
+struct xr_file *xr_fileopen(const char *path, int open_mode, umode_t mode);
+struct xr_file *xr_request_file(const char *path);
+int xr_fileclose(const struct xr_file *fp);
+int xr_fileread(const struct xr_file *fp, char *buffer, int size);
+int xr_filewrite(const struct xr_file *fp, char *buffer, int size);
+int access_file(char *path, char *buffer, int size, int isRead);
+
+/*******************************************************
+ 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;
+}
+
+static inline struct sk_buff *xr_alloc_skb(unsigned int len)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
+ gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+ return __dev_alloc_skb(len, flags);
+#else
+ return dev_alloc_skb(len);
+#endif
+}
+
+static inline void *xr_kmalloc(unsigned int len, bool isDMA)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
+ gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+#else
+ gfp_t flags = GFP_ATOMIC;
+#endif
+ flags = isDMA ? (flags | GFP_DMA) : flags;
+ return kmalloc(len, flags);
+}
+
+static inline void *xr_kzalloc(unsigned int len, bool isDMA)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
+ gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+#else
+ gfp_t flags = GFP_ATOMIC;
+#endif
+ flags = isDMA ? (flags | GFP_DMA) : flags;
+ return kzalloc(len, flags);
+}
+
+
+static inline void *xr_krealloc(void *buf, unsigned int len, bool isDMA)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
+ gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
+#else
+ gfp_t flags = GFP_ATOMIC;
+#endif
+ flags = isDMA ? (flags | GFP_DMA) : flags;
+ return krealloc(buf, len, flags);
+}
+
+#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 0000000..395c574
--- /dev/null
+++ b/drivers/net/wireless/xradio/debug.c
@@ -0,0 +1,3273 @@
+/*
+ * 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 = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_sbus = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_bh = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_txrx = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR;
+u8 dbg_wsm = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_sta = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_scan = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_ap = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_pm = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+u8 dbg_itp = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN;
+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, "Driver Label:%s %s\n", DRV_VERSION, DRV_BUILDTIME);
+ 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_info) ? "on" : "off");
+ if (xradio_is_ht(&hw_priv->ht_info)) {
+ seq_printf(seq, "Greenfield: %s\n",
+ xradio_ht_greenfield(&hw_priv->ht_info) ? "yes" : "no");
+ seq_printf(seq, "AMPDU dens: %d\n",
+ xradio_ht_ampdu_density(&hw_priv->ht_info));
+ }
+ 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 RX: %d\n",
+ atomic_read(&hw_priv->bh_rx));
+ 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[IEEE80211_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[IEEE80211_BAND_2GHZ],
+ hw_priv->hw->wiphy->bands[IEEE80211_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 = xr_kzalloc(sizeof(struct xradio_debug_priv), false);
+ 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 = xr_kzalloc(sizeof(struct xradio_debug_common), false);
+ 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
+
+ ret = xradio_itp_init(hw_priv);
+ if (ret)
+ ERR_LINE;
+
+ 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) {
+ xradio_itp_release(hw_priv);
+ 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 0000000..e90708e
--- /dev/null
+++ b/drivers/net/wireless/xradio/debug.h
@@ -0,0 +1,525 @@
+/*
+ * 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
+
+#include "itp.h"
+
+#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_sbus ;
+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 "/tmp/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 sbus_printk(level, ...) \
+ do { \
+ if ((level) & dbg_sbus & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[SBUS_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_sbus & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[SBUS_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_sbus) \
+ printk(KERN_ERR "[SBUS] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[SBUS_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 bh_printk(level, ...) \
+ do { \
+ if ((level) & dbg_bh & XRADIO_DBG_ERROR) \
+ printk(KERN_ERR "[BH_ERR] " __VA_ARGS__); \
+ else if ((level) & dbg_bh & XRADIO_DBG_WARN) \
+ printk(KERN_ERR "[BH_WRN] " __VA_ARGS__); \
+ else if ((level) & dbg_bh) \
+ printk(KERN_ERR "[BH] " __VA_ARGS__); \
+ if ((level) & dbg_logfile) \
+ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \
+ "[BH_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 sbus_printk(level, ...)
+#define txrx_printk(level, ...)
+#define bh_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_IRQ_ADD
+#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 0000000..3eb58ca
--- /dev/null
+++ b/drivers/net/wireless/xradio/fwio.c
@@ -0,0 +1,650 @@
+/*
+ * 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 "sbus.h"
+#include "bh.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;
+
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ 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:
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: unknown hardware version.\n", __func__);
+ return ret;
+ }
+
+#ifdef USE_VFS_FIRMWARE
+ hw_priv->sdd = xr_request_file(sdd_path);
+ if (unlikely(!hw_priv->sdd)) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load sdd file %s.\n",
+ __func__, sdd_path);
+ return ret;
+ }
+#else
+ ret = request_firmware(&hw_priv->sdd, sdd_path, hw_priv->pdev);
+ if (unlikely(ret)) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load sdd file %s.\n",
+ __func__, sdd_path);
+ return ret;
+ }
+#endif
+
+ //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);
+ }
+
+ xradio_dbg(XRADIO_DBG_MSG, "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;
+#ifdef USE_VFS_FIRMWARE
+ const struct xr_file *firmware = NULL;
+#else
+ const struct firmware *firmware = NULL;
+#endif
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ switch (hw_priv->hw_revision) {
+ case XR819_HW_REV0:
+ fw_path = XR819_FIRMWARE;
+ break;
+ default:
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: invalid silicon revision %d.\n",
+ __func__, 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 */
+#ifdef USE_VFS_FIRMWARE
+ firmware = xr_fileopen(fw_path, O_RDONLY, 0);
+ if (!firmware) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load firmware file %s.\n",
+ __func__, fw_path);
+ ret = -1;
+ goto error;
+ }
+#else
+ ret = request_firmware(&firmware, fw_path, hw_priv->pdev);
+ if (ret) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load firmware file %s.\n",
+ __func__, fw_path);
+ goto error;
+ }
+ SYS_BUG(!firmware->data);
+#endif
+
+ buf = xr_kmalloc(DOWNLOAD_BLOCK_SIZE, true);
+ if (!buf) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't allocate firmware buffer.\n", __func__);
+ 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: bootloader is not ready.\n", __func__);
+ 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: bootloader reported error %d.\n",
+ __func__, 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)) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: Timeout waiting for FIFO.\n", __func__);
+ ret = -ETIMEDOUT;
+ goto error;
+ }
+
+ /* calculate the block size */
+ tx_size = block_size = min((size_t)(firmware->size - put), (size_t)DOWNLOAD_BLOCK_SIZE);
+#ifdef USE_VFS_FIRMWARE
+ ret = xr_fileread(firmware, buf, block_size);
+ if (ret < block_size) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: xr_fileread error %d.\n", __func__, ret);
+ goto error;
+ }
+#else
+ memcpy(buf, &firmware->data[put], block_size);
+#endif
+ 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: wait for download completion failed. " \
+ "Read: 0x%.8X\n", __func__, val32);
+ ret = -ETIMEDOUT;
+ goto error;
+ } else {
+ xradio_dbg(XRADIO_DBG_ALWY, "Firmware completed.\n");
+ ret = 0;
+ }
+
+error:
+ if(buf)
+ kfree(buf);
+ if (firmware) {
+#ifdef USE_VFS_FIRMWARE
+ xr_fileclose(firmware);
+#else
+ release_firmware(firmware);
+#endif
+ }
+ return ret;
+}
+
+static int xradio_bootloader(struct xradio_common *hw_priv)
+{
+ int ret = -1;
+ u32 i = 0;
+ const char *bl_path = XR819_BOOTLOADER;
+ u32 addr = AHB_MEMORY_ADDRESS;
+ u32 *data = NULL;
+#ifdef USE_VFS_FIRMWARE
+ const struct xr_file *bootloader = NULL;
+#else
+ const struct firmware *bootloader = NULL;
+#endif
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#ifdef USE_VFS_FIRMWARE
+ bootloader = xr_request_file(bl_path);
+ if (!bootloader) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load bootloader file %s.\n",
+ __func__, bl_path);
+ goto error;
+ }
+#else
+ /* Load a bootloader file */
+ ret = request_firmware(&bootloader, bl_path, hw_priv->pdev);
+ if (ret) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load bootloader file %s.\n",
+ __func__, bl_path);
+ goto error;
+ }
+#endif
+
+ xradio_dbg(XRADIO_DBG_NIY, "%s: bootloader size = %d, loopcount = %d\n",
+ __func__,bootloader->size, (bootloader->size)/4);
+
+ /* Down bootloader. */
+ data = (u32 *)bootloader->data;
+ for(i = 0; i < (bootloader->size)/4; i++) {
+ REG_WRITE(HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ REG_WRITE(HIF_AHB_DPORT_REG_ID,data[i]);
+ if(i == 100 || i == 200 || i == 300 || i == 400 || i == 500 || i == 600 )
+ xradio_dbg(XRADIO_DBG_NIY, "%s: addr = 0x%x,data = 0x%x\n",__func__,addr, data[i]);
+ addr += 4;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY, "Bootloader complete\n");
+
+error:
+ if(bootloader) {
+#ifdef USE_VFS_FIRMWARE
+ xr_fileclose(bootloader);
+#else
+ release_firmware(bootloader);
+#endif
+ }
+ 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;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ SYS_BUG(!hw_priv);
+
+ /* Read CONFIG Register Value - We will read 32 bits */
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't read config register, err=%d.\n",
+ __func__, 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:
+ xradio_dbg(XRADIO_DBG_NIY, "%s: HW_TYPE_XRADIO detected.\n", __func__);
+ break;
+ default:
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: Unknown hardware: %d.\n",
+ __func__, hw_priv->hw_type);
+ return -ENOTSUPP;
+ }
+ if (major_revision == 4) {
+ hw_priv->hw_revision = XR819_HW_REV0;
+ xradio_dbg(XRADIO_DBG_ALWY, "XRADIO_HW_REV 1.0 detected.\n");
+ } else {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: Unsupported major revision %d.\n",
+ __func__, 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't write DPLL register.\n", __func__);
+ goto out;
+ }
+ msleep(5);
+ ret = xradio_reg_read_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, &val32);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't read DPLL register.\n", __func__);
+ goto out;
+ }
+ if (val32 != dpll) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: unable to initialise " \
+ "DPLL register. Wrote 0x%.8X, read 0x%.8X.\n",
+ __func__, 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: set_wakeup: can't read control register.\n",
+ __func__);
+ goto out;
+ }
+ ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, val16 | HIF_CTRL_WUP_BIT);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: set_wakeup: can't write control register.\n",
+ __func__);
+ 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: Wait_for_wakeup: "
+ "can't read control register.\n", __func__);
+ goto out;
+ }
+ if (val16 & HIF_CTRL_RDY_BIT) {
+ break;
+ }
+ msleep(i);
+ }
+ if ((val16 & HIF_CTRL_RDY_BIT) == 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: Wait for wakeup:"
+ "device is not responding.\n", __func__);
+ ret = -ETIMEDOUT;
+ goto out;
+ } else {
+ xradio_dbg(XRADIO_DBG_NIY, "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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: check_access_mode: "
+ "can't read config register.\n", __func__);
+ goto out;
+ }
+ if (val32 & HIF_CONFIG_ACCESS_MODE_BIT) {
+ /* Down bootloader. */
+ ret = xradio_bootloader(hw_priv);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't download bootloader.\n", __func__);
+ goto out;
+ }
+ /* Down firmware. */
+ ret = xradio_firmware(hw_priv);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't download firmware.\n", __func__);
+ goto out;
+ }
+ } else {
+ xradio_dbg(XRADIO_DBG_WARN, "%s: check_access_mode: "
+ "device is already in QUEUE mode.\n", __func__);
+ /* TODO: verify this branch. Do we need something to do? */
+ }
+
+ /* Register Interrupt Handler */
+ ret = hw_priv->sbus_ops->irq_subscribe(hw_priv->sbus_priv,
+ (sbus_irq_handler)xradio_irq_handler,
+ hw_priv);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't register IRQ handler.\n", __func__);
+ goto out;
+ }
+
+ 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't read " \
+ "config register.\n", __func__);
+ goto unsubscribe;
+ }
+ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID,
+ val32 | HIF_CONF_IRQ_RDY_ENABLE);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't write " \
+ "config register.\n", __func__);
+ 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) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't read " \
+ "control register.\n", __func__);
+ goto unsubscribe;
+ }
+ ret = xradio_reg_write_16(hw_priv, HIF_CONFIG_REG_ID,
+ val16 | HIF_CTRL_IRQ_RDY_ENABLE);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't write " \
+ "control register.\n", __func__);
+ goto unsubscribe;
+ }
+
+ }
+
+ /* Configure device for MESSSAGE MODE */
+ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32);
+ if (ret < 0) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: set_mode: can't read config register.\n",
+ __func__);
+ goto unsubscribe;
+ }
+ 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: set_mode: can't write config register.\n",
+ __func__);
+ 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:
+ hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
+out:
+ if (hw_priv->sdd) {
+#ifdef USE_VFS_FIRMWARE
+ xr_fileclose(hw_priv->sdd);
+#else
+ release_firmware(hw_priv->sdd);
+#endif
+ hw_priv->sdd = NULL;
+ }
+ return ret;
+}
+
+int xradio_dev_deinit(struct xradio_common *hw_priv)
+{
+ hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
+ if (hw_priv->sdd) {
+ #ifdef USE_VFS_FIRMWARE
+ xr_fileclose(hw_priv->sdd);
+ #else
+ release_firmware(hw_priv->sdd);
+ #endif
+ hw_priv->sdd = NULL;
+ }
+ return 0;
+}
+#undef APB_WRITE
+#undef APB_READ
+#undef REG_WRITE
+#undef REG_READ
\ No newline at end of file
diff --git a/drivers/net/wireless/xradio/fwio.h b/drivers/net/wireless/xradio/fwio.h
new file mode 100644
index 0000000..4816322
--- /dev/null
+++ b/drivers/net/wireless/xradio/fwio.h
@@ -0,0 +1,39 @@
+/*
+ * 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)
+#ifdef USE_VFS_FIRMWARE
+#define XR819_BOOTLOADER ("/lib/firmware/xr819/boot_xr819.bin")
+#define XR819_FIRMWARE ("/lib/firmware/xr819/fw_xr819.bin")
+#define XR819_SDD_FILE ("/lib/firmware/xr819/sdd_xr819.bin")
+#else
+#define XR819_BOOTLOADER ("xr819/boot_xr819.bin")
+#define XR819_FIRMWARE ("xr819/fw_xr819.bin")
+#define XR819_SDD_FILE ("xr819/sdd_xr819.bin")
+#endif
+
+#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 0000000..827ce94
--- /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_info {
+ 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_info *ht_info)
+{
+ return ht_info->channel_type != NL80211_CHAN_NO_HT;
+}
+
+static inline int xradio_ht_greenfield(const struct xradio_ht_info *ht_info)
+{
+ return (xradio_is_ht(ht_info) &&
+ (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
+ !(ht_info->operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT));
+}
+
+static inline int xradio_ht_ampdu_density(const struct xradio_ht_info *ht_info)
+{
+ if (!xradio_is_ht(ht_info))
+ return 0;
+ return ht_info->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 0000000..9090f40
--- /dev/null
+++ b/drivers/net/wireless/xradio/hwio.c
@@ -0,0 +1,286 @@
+/*
+ * 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 "sbus.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))) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: buffer is not aligned.\n", __func__);
+ 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);
+ SYS_BUG(!hw_priv->sbus_ops);
+ return hw_priv->sbus_ops->sbus_data_read(hw_priv->sbus_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))) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: buffer is not aligned.\n", __func__);
+ 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);
+
+ SYS_BUG(!hw_priv->sbus_ops);
+ return hw_priv->sbus_ops->sbus_data_write(hw_priv->sbus_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;
+ SYS_BUG(!hw_priv->sbus_ops);
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+ ret = __xradio_read(hw_priv, addr, buf, buf_len, 0);
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+ return ret;
+}
+
+int xradio_reg_write(struct xradio_common *hw_priv, u16 addr,
+ const void *buf, size_t buf_len)
+{
+ int ret;
+ SYS_BUG(!hw_priv->sbus_ops);
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+ ret = __xradio_write(hw_priv, addr, buf, buf_len, 0);
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+ return ret;
+}
+
+int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len)
+{
+ int ret, retry = 1;
+ SYS_BUG(!hw_priv->sbus_ops);
+ hw_priv->sbus_ops->lock(hw_priv->sbus_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 {
+ retry++;
+ mdelay(1);
+ sbus_printk(XRADIO_DBG_ERROR, "%s, error :[%d]\n", __func__, ret);
+ }
+ }
+ }
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+ return ret;
+}
+
+int xradio_data_write(struct xradio_common *hw_priv, const void *buf,
+ size_t buf_len)
+{
+ int ret, retry = 1;
+ SYS_BUG(!hw_priv->sbus_ops);
+ hw_priv->sbus_ops->lock(hw_priv->sbus_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 {
+ retry++;
+ mdelay(1);
+ sbus_printk(XRADIO_DBG_ERROR, "%s,error :[%d]\n", __func__, ret);
+ }
+ }
+ }
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_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) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't read more than 0xfff words.\n",
+ __func__);
+ return -EINVAL;
+ goto out;
+ }
+
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+ /* Write address */
+ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write address register.\n", __func__);
+ 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) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't read config register.\n", __func__);
+ goto out;
+ }
+
+ /* Set PREFETCH bit */
+ ret = __xradio_write_reg32(hw_priv, HIF_CONFIG_REG_ID, val32 | prefetch);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write prefetch bit.\n", __func__);
+ 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) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't check prefetch bit.\n", __func__);
+ goto out;
+ }
+ if (!(val32 & prefetch))
+ break;
+ mdelay(i);
+ }
+
+ if (val32 & prefetch) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Prefetch bit is not cleared.\n", __func__);
+ goto out;
+ }
+
+ /* Read data port */
+ ret = __xradio_read(hw_priv, port_addr, buf, buf_len, 0);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't read data port.\n", __func__);
+ goto out;
+ }
+
+out:
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_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) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't wrire more than 0xfff words.\n", __func__);
+ return -EINVAL;
+ }
+
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+
+ /* Write address */
+ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write address register.\n", __func__);
+ goto out;
+ }
+
+ /* Write data port */
+ ret = __xradio_write(hw_priv, HIF_SRAM_DPORT_REG_ID, buf, buf_len, 0);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write data port.\n", __func__);
+ goto out;
+ }
+
+out:
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_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) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't wrire more than 0xfff words.\n", __func__);
+ return -EINVAL;
+ }
+
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+
+ /* Write address */
+ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write address register.\n", __func__);
+ goto out;
+ }
+
+ /* Write data port */
+ ret = __xradio_write(hw_priv, HIF_AHB_DPORT_REG_ID, buf, buf_len, 0);
+ if (ret < 0) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write data port.\n", __func__);
+ goto out;
+ }
+
+out:
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_priv);
+ return ret;
+}
diff --git a/drivers/net/wireless/xradio/hwio.h b/drivers/net/wireless/xradio/hwio.h
new file mode 100644
index 0000000..531c2b8
--- /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/index.html b/drivers/net/wireless/xradio/index.html
new file mode 100644
index 0000000..99f7b53
--- /dev/null
+++ b/drivers/net/wireless/xradio/index.html
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
+<head>
+<title>Index of /allwinner/h2/201609022/lichee/linux-3.4/drivers/net/wireless/xradio/</title>
+<style type="text/css">
+a, a:active {text-decoration: none; color: blue;}
+a:visited {color: #48468F;}
+a:hover, a:focus {text-decoration: underline; color: red;}
+body {background-color: #F5F5F5;}
+h2 {margin-bottom: 12px;}
+table {margin-left: 12px;}
+th, td { font: 90% monospace; text-align: left;}
+th { font-weight: bold; padding-right: 14px; padding-bottom: 3px;}
+td {padding-right: 14px;}
+td.s, th.s {text-align: right;}
+div.list { background-color: white; border-top: 1px solid #646464; border-bottom: 1px solid #646464; padding-top: 10px; padding-bottom: 14px;}
+div.foot { font: 90% monospace; color: #787878; padding-top: 4px;}
+</style>
+</head>
+<body>
+<h2>Index of /allwinner/h2/201609022/lichee/linux-3.4/drivers/net/wireless/xradio/</h2>
+<div class="list">
+<table summary="Directory Listing" cellpadding="0" cellspacing="0">
+<thead><tr><th class="n">Name</th><th class="m">Last Modified</th><th class="s">Size</th><th class="t">Type</th></tr></thead>
+<tbody>
+<tr><td class="n"><a href="../">Parent Directory</a>/</td><td class="m">&nbsp;</td><td class="s">- &nbsp;</td><td class="t">Directory</td></tr>
+<tr><td class="n"><a href="Kconfig">Kconfig</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.4K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="Makefile">Makefile</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.5K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="ap.c">ap.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">57.6K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="ap.h">ap.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">2.1K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="bh.c">bh.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">26.8K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="bh.h">bh.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.2K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="common.c">common.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">3.6K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="common.h">common.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">4.4K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="debug.c">debug.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">92.2K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="debug.h">debug.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">14.7K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="fwio.c">fwio.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">17.1K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="fwio.h">fwio.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.1K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="ht.h">ht.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.0K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="hwio.c">hwio.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">7.7K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="hwio.h">hwio.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">8.0K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="itp.c">itp.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">17.8K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="itp.h">itp.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">3.1K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="main.c">main.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">34.5K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="nl80211_testmode_msg_copy.h">nl80211_testmode_msg_copy.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">3.9K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="platform.c">platform.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">24.0K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="platform.h">platform.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.5K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="pm.c">pm.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">22.8K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="pm.h">pm.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.5K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="queue.c">queue.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">24.6K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="queue.h">queue.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">5.0K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="sbus.h">sbus.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">1.6K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="scan.c">scan.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">34.5K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="scan.h">scan.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">2.3K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="sdio.c">sdio.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">9.1K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="sta.c">sta.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">100.2K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="sta.h">sta.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">5.9K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="txrx.c">txrx.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">71.3K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="txrx.h">txrx.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">2.8K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="wsm.c">wsm.c</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">88.1K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="wsm.h">wsm.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">62.0K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="xr_version.h">xr_version.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">0.4K</td><td class="t">application/octet-stream</td></tr>
+<tr><td class="n"><a href="xradio.h">xradio.h</a></td><td class="m">2016-Sep-22 09:50:36</td><td class="s">18.0K</td><td class="t">application/octet-stream</td></tr>
+</tbody>
+</table>
+</div>
+<div class="foot">lighttpd/1.4.35</div>
+</body>
+</html>
diff --git a/drivers/net/wireless/xradio/itp.c b/drivers/net/wireless/xradio/itp.c
new file mode 100644
index 0000000..0ae8ea5
--- /dev/null
+++ b/drivers/net/wireless/xradio/itp.c
@@ -0,0 +1,751 @@
+/*
+ * Test code 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/module.h>
+#include <linux/debugfs.h>
+#include <linux/poll.h>
+#include <linux/time.h>
+#include <linux/kallsyms.h>
+#include <net/mac80211.h>
+#include "xradio.h"
+#include "itp.h"
+#include "sta.h"
+
+static int __xradio_itp_open(struct xradio_common *priv);
+static int __xradio_itp_close(struct xradio_common *priv);
+static void xradio_itp_rx_start(struct xradio_common *priv);
+static void xradio_itp_rx_stop(struct xradio_common *priv);
+static void xradio_itp_rx_stats(struct xradio_common *priv);
+static void xradio_itp_rx_reset(struct xradio_common *priv);
+static void xradio_itp_tx_stop(struct xradio_common *priv);
+static void xradio_itp_handle(struct xradio_common *priv,
+ struct sk_buff *skb);
+static void xradio_itp_err(struct xradio_common *priv,
+ int err,
+ int arg);
+static void __xradio_itp_tx_stop(struct xradio_common *priv);
+
+static ssize_t xradio_itp_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *priv = file->private_data;
+ struct xradio_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ int ret;
+
+ if (skb_queue_empty(&itp->log_queue))
+ return 0;
+
+ skb = skb_dequeue(&itp->log_queue);
+ ret = copy_to_user(user_buf, skb->data, skb->len);
+ *ppos += skb->len;
+ skb->data[skb->len] = 0;
+ itp_printk(XRADIO_DBG_MSG, "[ITP] >>> %s", skb->data);
+ consume_skb(skb);
+
+ return skb->len - ret;
+}
+
+static ssize_t xradio_itp_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct xradio_common *priv = file->private_data;
+ struct sk_buff *skb;
+
+ if (!count || count > 1024)
+ return -EINVAL;
+ skb = xr_alloc_skb(count + 1);
+ if (!skb)
+ return -ENOMEM;
+ skb_trim(skb, 0);
+ skb_put(skb, count + 1);
+ if (copy_from_user(skb->data, user_buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ skb->data[count] = 0;
+
+ xradio_itp_handle(priv, skb);
+ consume_skb(skb);
+ return count;
+}
+
+static unsigned int xradio_itp_poll(struct file *file, poll_table *wait)
+{
+ struct xradio_common *priv = file->private_data;
+ struct xradio_itp *itp = &priv->debug->itp;
+ unsigned int mask = 0;
+
+ poll_wait(file, &itp->read_wait, wait);
+
+ if (!skb_queue_empty(&itp->log_queue))
+ mask |= POLLIN | POLLRDNORM;
+
+ mask |= POLLOUT | POLLWRNORM;
+
+ return mask;
+}
+
+static int xradio_itp_open(struct inode *inode, struct file *file)
+{
+ struct xradio_common *priv = inode->i_private;
+ struct xradio_itp *itp = &priv->debug->itp;
+ int ret = 0;
+
+ file->private_data = priv;
+ if (atomic_inc_return(&itp->open_count) == 1) {
+ ret = __xradio_itp_open(priv);
+ if (ret && !atomic_dec_return(&itp->open_count))
+ __xradio_itp_close(priv);
+ } else {
+ atomic_dec(&itp->open_count);
+ ret = -EBUSY;
+ }
+
+ return ret;
+}
+
+static int xradio_itp_close(struct inode *inode, struct file *file)
+{
+ struct xradio_common *priv = file->private_data;
+ struct xradio_itp *itp = &priv->debug->itp;
+ if (!atomic_dec_return(&itp->open_count)) {
+ __xradio_itp_close(priv);
+ wake_up(&itp->close_wait);
+ }
+ return 0;
+}
+
+static const struct file_operations fops_itp = {
+ .open = xradio_itp_open,
+ .read = xradio_itp_read,
+ .write = xradio_itp_write,
+ .poll = xradio_itp_poll,
+ .release = xradio_itp_close,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
+static void xradio_itp_fill_pattern(u8 *data, int size,
+ enum xradio_itp_data_modes mode)
+{
+ u8 *p = data;
+
+ if (size <= 0)
+ return;
+
+ switch (mode) {
+ default:
+ case ITP_DATA_ZEROS:
+ memset(data, 0x0, size);
+ break;
+ case ITP_DATA_ONES:
+ memset(data, 0xff, size);
+ break;
+ case ITP_DATA_ZERONES:
+ memset(data, 0x55, size);
+ break;
+ case ITP_DATA_RANDOM:
+ while (p < data+size-sizeof(u32)) {
+ (*(u32 *)p) = random32();
+ p += sizeof(u32);
+ }
+ while (p < data+size) {
+ (*p) = random32() & 0xFF;
+ p++;
+ }
+ break;
+ }
+ return;
+}
+
+static void xradio_itp_tx_work(struct work_struct *work)
+{
+ struct xradio_itp *itp = container_of(work, struct xradio_itp,
+ tx_work.work);
+ struct xradio_common *priv = itp->priv;
+ atomic_set(&priv->bh_tx, 1);
+
+#ifdef BH_USE_SEMAPHORE
+ up(&priv->bh_sem);
+#else
+ wake_up(&priv->bh_wq);
+#endif
+}
+
+static void xradio_itp_tx_finish(struct work_struct *work)
+{
+ struct xradio_itp *itp = container_of(work, struct xradio_itp,
+ tx_finish.work);
+ __xradio_itp_tx_stop(itp->priv);
+}
+
+int xradio_itp_init(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+
+ itp->priv = priv;
+ atomic_set(&itp->open_count, 0);
+ atomic_set(&itp->stop_tx, 0);
+ atomic_set(&itp->awaiting_confirm, 0);
+ skb_queue_head_init(&itp->log_queue);
+ spin_lock_init(&itp->tx_lock);
+ init_waitqueue_head(&itp->read_wait);
+ init_waitqueue_head(&itp->write_wait);
+ init_waitqueue_head(&itp->close_wait);
+ INIT_DELAYED_WORK(&itp->tx_work, xradio_itp_tx_work);
+ INIT_DELAYED_WORK(&itp->tx_finish, xradio_itp_tx_finish);
+ itp->data = NULL;
+ itp->hdr_len = WSM_TX_EXTRA_HEADROOM +
+ sizeof(struct ieee80211_hdr_3addr);
+ itp->id = 0;
+
+ if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR,
+ priv->debug->debugfs_phy, priv, &fops_itp))
+ return -ENOMEM;
+
+ return 0;
+}
+
+void xradio_itp_release(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+
+ wait_event_interruptible(itp->close_wait,
+ !atomic_read(&itp->open_count));
+
+ SYS_WARN(atomic_read(&itp->open_count));
+
+ skb_queue_purge(&itp->log_queue);
+ xradio_itp_tx_stop(priv);
+}
+
+static int __xradio_itp_open(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+
+ if (!priv->vif)
+ return -EINVAL;
+ if (priv->join_status)
+ return -EINVAL;
+ itp->saved_channel = priv->channel;
+ if (!priv->channel)
+ priv->channel = &priv->hw->
+ wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0];
+ wsm_set_bssid_filtering(priv, false);
+ xradio_itp_rx_reset(priv);
+ return 0;
+}
+
+static int __xradio_itp_close(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST)
+ xradio_itp_rx_stop(priv);
+ xradio_itp_tx_stop(priv);
+ xradio_disable_listening(priv);
+ xradio_update_filtering(priv);
+ priv->channel = itp->saved_channel;
+ return 0;
+}
+
+bool xradio_is_itp(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ return atomic_read(&itp->open_count) != 0;
+}
+
+static void xradio_itp_rx_reset(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ itp->rx_cnt = 0;
+ itp->rx_rssi = 0;
+ itp->rx_rssi_max = -1000;
+ itp->rx_rssi_min = 1000;
+}
+
+static void xradio_itp_rx_start(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+
+ itp_printk(XRADIO_DBG_MSG, "[ITP] RX start, band = %d, ch = %d\n",
+ itp->band, itp->ch);
+ atomic_set(&itp->test_mode, TEST_MODE_RX_TEST);
+ xradio_disable_listening(priv, false);
+ priv->channel = &priv->hw->
+ wiphy->bands[itp->band]->channels[itp->ch];
+ xradio_enable_listening(priv, priv->channel);
+ wsm_set_bssid_filtering(priv, false);
+}
+
+static void xradio_itp_rx_stop(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ itp_printk(XRADIO_DBG_MSG, "[ITP] RX stop\n");
+ atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
+ xradio_itp_rx_reset(priv);
+}
+
+static void xradio_itp_rx_stats(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ char buf[128];
+ int len, ret;
+ struct wsm_counters_table counters;
+
+ ret = wsm_get_counters_table(priv, &counters);
+
+ if (ret)
+ xradio_itp_err(priv, -EBUSY, 20);
+
+ if (!itp->rx_cnt)
+ len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n",
+ counters.countRxPacketErrors);
+ else
+ len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n",
+ itp->rx_cnt,
+ itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0,
+ itp->rx_rssi_min, itp->rx_rssi_max,
+ counters.countRxPacketErrors);
+
+ if (len <= 0) {
+ xradio_itp_err(priv, -EBUSY, 21);
+ return;
+ }
+
+ skb = xr_alloc_skb(len);
+ if (!skb) {
+ xradio_itp_err(priv, -ENOMEM, 22);
+ return;
+ }
+
+ itp->rx_cnt = 0;
+ itp->rx_rssi = 0;
+ itp->rx_rssi_max = -1000;
+ itp->rx_rssi_min = 1000;
+
+ skb_trim(skb, 0);
+ skb_put(skb, len);
+
+ memcpy(skb->data, buf, len);
+ skb_queue_tail(&itp->log_queue, skb);
+ wake_up(&itp->read_wait);
+}
+
+static void xradio_itp_tx_start(struct xradio_common *priv)
+{
+ struct wsm_tx *tx;
+ struct ieee80211_hdr_3addr *hdr;
+ struct xradio_itp *itp = &priv->debug->itp;
+ struct wsm_association_mode assoc_mode = {
+ .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE,
+ .preambleType = itp->preamble,
+ };
+ int len;
+ u8 da_addr[6] = ITP_DEFAULT_DA_ADDR;
+
+ /* Rates index 4 and 5 are not supported */
+ if (itp->rate > 3)
+ itp->rate += 2;
+
+ itp_printk(XRADIO_DBG_MSG, "[ITP] TX start: band = %d, ch = %d, rate = %d,"
+ " preamble = %d, number = %d, data_mode = %d,"
+ " interval = %d, power = %d, data_len = %d\n",
+ itp->band, itp->ch, itp->rate, itp->preamble,
+ itp->number, itp->data_mode, itp->interval_us,
+ itp->power, itp->data_len);
+
+ len = itp->hdr_len + itp->data_len;
+
+ itp->data = xr_kmalloc(len, false);
+ tx = (struct wsm_tx *)itp->data;
+ tx->hdr.len = itp->data_len + itp->hdr_len;
+ tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6);
+ tx->maxTxRate = itp->rate;
+ tx->queueId = 3;
+ tx->more = 0;
+ tx->flags = 0xc;
+ tx->packetID = 0;
+ tx->reserved = 0;
+ tx->expireTime = 0;
+
+ if (itp->preamble == ITP_PREAMBLE_GREENFIELD)
+ tx->htTxParameters = WSM_HT_TX_GREENFIELD;
+ else if (itp->preamble == ITP_PREAMBLE_MIXED)
+ tx->htTxParameters = WSM_HT_TX_MIXED;
+
+ hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)];
+ memset(hdr, 0, sizeof(*hdr));
+ hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA |
+ IEEE80211_FCTL_TODS);
+ memcpy(hdr->addr1, da_addr, ETH_ALEN);
+ memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN);
+ memcpy(hdr->addr3, da_addr, ETH_ALEN);
+
+ xradio_itp_fill_pattern(&itp->data[itp->hdr_len],
+ itp->data_len, itp->data_mode);
+
+ xradio_disable_listening(priv);
+ priv->channel = &priv->hw->
+ wiphy->bands[itp->band]->channels[itp->ch];
+ SYS_WARN(wsm_set_output_power(priv, itp->power));
+ if (itp->preamble == ITP_PREAMBLE_SHORT ||
+ itp->preamble == ITP_PREAMBLE_LONG)
+ SYS_WARN(wsm_set_association_mode(priv,
+ &assoc_mode));
+ wsm_set_bssid_filtering(priv, false);
+ xradio_enable_listening(priv, priv->channel);
+
+ spin_lock_bh(&itp->tx_lock);
+ atomic_set(&itp->test_mode, TEST_MODE_TX_TEST);
+ atomic_set(&itp->awaiting_confirm, 0);
+ atomic_set(&itp->stop_tx, 0);
+ atomic_set(&priv->bh_tx, 1);
+ ktime_get_ts(&itp->last_sent);
+#ifdef BH_USE_SEMAPHORE
+ up(&priv->bh_sem);
+#else
+ wake_up(&priv->bh_wq);
+#endif
+ spin_unlock_bh(&itp->tx_lock);
+}
+
+void __xradio_itp_tx_stop(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ spin_lock_bh(&itp->tx_lock);
+ kfree(itp->data);
+ itp->data = NULL;
+ atomic_set(&itp->test_mode, TEST_MODE_NO_TEST);
+ spin_unlock_bh(&itp->tx_lock);
+}
+
+static void xradio_itp_tx_stop(struct xradio_common *priv)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ itp_printk(XRADIO_DBG_MSG, "[ITP] TX stop\n");
+ atomic_set(&itp->stop_tx, 1);
+ flush_workqueue(priv->workqueue);
+
+ /* time for FW to confirm all tx requests */
+ msleep(500);
+
+ __xradio_itp_tx_stop(priv);
+}
+
+static void xradio_itp_get_version(struct xradio_common *priv,
+ enum xradio_itp_version_type type)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ char buf[ITP_BUF_SIZE];
+ size_t size = 0;
+ int len;
+ itp_printk(XRADIO_DBG_MSG, "[ITP] print %s version\n", type == ITP_CHIP_ID ?
+ "chip" : "firmware");
+
+ len = snprintf(buf, ITP_BUF_SIZE, "2,");
+ if (len <= 0) {
+ xradio_itp_err(priv, -EINVAL, 40);
+ return;
+ }
+ size += len;
+
+ switch (type) {
+ case ITP_CHIP_ID:
+ len = xradio_print_fw_version(priv, buf+size,
+ ITP_BUF_SIZE - size);
+
+ if (len <= 0) {
+ xradio_itp_err(priv, -EINVAL, 41);
+ return;
+ }
+ size += len;
+ break;
+ case ITP_FW_VER:
+ len = snprintf(buf+size, ITP_BUF_SIZE - size,
+ "%d.%d", priv->wsm_caps.hardwareId,
+ priv->wsm_caps.hardwareSubId);
+ if (len <= 0) {
+ xradio_itp_err(priv, -EINVAL, 42);
+ return;
+ }
+ size += len;
+ break;
+ default:
+ xradio_itp_err(priv, -EINVAL, 43);
+ break;
+ }
+
+ len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n");
+ if (len <= 0) {
+ xradio_itp_err(priv, -EINVAL, 44);
+ return;
+ }
+ size += len;
+
+ skb = xr_alloc_skb(size);
+ if (!skb) {
+ xradio_itp_err(priv, -ENOMEM, 45);
+ return;
+ }
+
+ skb_trim(skb, 0);
+ skb_put(skb, size);
+
+ memcpy(skb->data, buf, size);
+ skb_queue_tail(&itp->log_queue, skb);
+ wake_up(&itp->read_wait);
+}
+
+int xradio_itp_get_tx(struct xradio_common *priv, u8 **data,
+ size_t *tx_len, int *burst)
+{
+ struct xradio_itp *itp;
+ struct wsm_tx *tx;
+ struct timespec now;
+ int time_left_us;
+
+ if (!priv->debug)
+ return 0;
+
+ itp = &priv->debug->itp;
+
+ if (!itp)
+ return 0;
+
+ spin_lock_bh(&itp->tx_lock);
+ if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST)
+ goto out;
+
+ if (atomic_read(&itp->stop_tx))
+ goto out;
+
+ if (itp->number == 0) {
+ atomic_set(&itp->stop_tx, 1);
+ queue_delayed_work(priv->workqueue, &itp->tx_finish,
+ HZ/10);
+ goto out;
+ }
+
+ if (!itp->data)
+ goto out;
+
+ if (priv->hw_bufs_used >= 2) {
+ if (!atomic_read(&priv->bh_rx))
+ atomic_set(&priv->bh_rx, 1);
+ atomic_set(&priv->bh_tx, 1);
+ goto out;
+ }
+
+ ktime_get_ts(&now);
+ time_left_us = (itp->last_sent.tv_sec -
+ now.tv_sec)*1000000 +
+ (itp->last_sent.tv_nsec - now.tv_nsec)/1000
+ + itp->interval_us;
+
+ if (time_left_us > ITP_TIME_THRES_US) {
+ queue_delayed_work(priv->workqueue, &itp->tx_work,
+ ITP_US_TO_MS(time_left_us)*HZ/1000);
+ goto out;
+ }
+
+ if (time_left_us > 50)
+ udelay(time_left_us);
+
+ if (itp->number > 0)
+ itp->number--;
+
+ *data = itp->data;
+ *tx_len = itp->data_len + itp->hdr_len;
+
+ if (itp->data_mode == ITP_DATA_RANDOM)
+ xradio_itp_fill_pattern(&itp->data[itp->hdr_len],
+ itp->data_len, itp->data_mode);
+
+ tx = (struct wsm_tx *)itp->data;
+ tx->packetID = __cpu_to_le32(itp->id++);
+ *burst = 2;
+ atomic_set(&priv->bh_tx, 1);
+ ktime_get_ts(&itp->last_sent);
+ atomic_add(1, &itp->awaiting_confirm);
+ spin_unlock_bh(&itp->tx_lock);
+ return 1;
+
+out:
+ spin_unlock_bh(&itp->tx_lock);
+ return 0;
+}
+
+bool xradio_itp_rxed(struct xradio_common *priv, struct sk_buff *skb)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb);
+ int signal;
+
+ if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST)
+ return xradio_is_itp(priv);
+ if (rx->freq != priv->channel->center_freq)
+ return true;
+
+ signal = rx->signal;
+ itp->rx_cnt++;
+ itp->rx_rssi += signal;
+ if (itp->rx_rssi_min > rx->signal)
+ itp->rx_rssi_min = rx->signal;
+ if (itp->rx_rssi_max < rx->signal)
+ itp->rx_rssi_max = rx->signal;
+
+ return true;
+}
+
+void xradio_itp_wake_up_tx(struct xradio_common *priv)
+{
+ wake_up(&priv->debug->itp.write_wait);
+}
+
+bool xradio_itp_tx_running(struct xradio_common *priv)
+{
+ if (atomic_read(&priv->debug->itp.awaiting_confirm) ||
+ atomic_read(&priv->debug->itp.test_mode) ==
+ TEST_MODE_TX_TEST) {
+ atomic_sub(1, &priv->debug->itp.awaiting_confirm);
+ return true;
+ }
+ return false;
+}
+
+static void xradio_itp_handle(struct xradio_common *priv,
+ struct sk_buff *skb)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ const struct wiphy *wiphy = priv->hw->wiphy;
+ int cmd;
+ int ret;
+
+ itp_printk(XRADIO_DBG_MSG, "[ITP] <<< %s", skb->data);
+ if (sscanf(skb->data, "%d", &cmd) != 1) {
+ xradio_itp_err(priv, -EINVAL, 1);
+ return;
+ }
+
+ switch (cmd) {
+ case 1: /* RX test */
+ if (atomic_read(&itp->test_mode)) {
+ xradio_itp_err(priv, -EBUSY, 0);
+ return;
+ }
+ ret = sscanf(skb->data, "%d,%d,%d",
+ &cmd, &itp->band, &itp->ch);
+ if (ret != 3) {
+ xradio_itp_err(priv, -EINVAL, ret + 1);
+ return;
+ }
+ if (itp->band >= 2)
+ xradio_itp_err(priv, -EINVAL, 2);
+ else if (!wiphy->bands[itp->band])
+ xradio_itp_err(priv, -EINVAL, 2);
+ else if (itp->ch >=
+ wiphy->bands[itp->band]->n_channels)
+ xradio_itp_err(priv, -EINVAL, 3);
+ else {
+ xradio_itp_rx_stats(priv);
+ xradio_itp_rx_start(priv);
+ }
+ break;
+ case 2: /* RX stat */
+ xradio_itp_rx_stats(priv);
+ break;
+ case 3: /* RX/TX stop */
+ if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) {
+ xradio_itp_rx_stats(priv);
+ xradio_itp_rx_stop(priv);
+ } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) {
+ xradio_itp_tx_stop(priv);
+ } else
+ xradio_itp_err(priv, -EBUSY, 0);
+ break;
+ case 4: /* TX start */
+ if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) {
+ xradio_itp_err(priv, -EBUSY, 0);
+ return;
+ }
+ ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
+ &cmd, &itp->band, &itp->ch, &itp->rate,
+ &itp->preamble, &itp->number, &itp->data_mode,
+ &itp->interval_us, &itp->power, &itp->data_len);
+ if (ret != 10) {
+ xradio_itp_err(priv, -EINVAL, ret + 1);
+ return;
+ }
+ if (itp->band >= 2)
+ xradio_itp_err(priv, -EINVAL, 2);
+ else if (!wiphy->bands[itp->band])
+ xradio_itp_err(priv, -EINVAL, 2);
+ else if (itp->ch >=
+ wiphy->bands[itp->band]->n_channels)
+ xradio_itp_err(priv, -EINVAL, 3);
+ else if (itp->rate >= 20)
+ xradio_itp_err(priv, -EINVAL, 4);
+ else if (itp->preamble >= ITP_PREAMBLE_MAX)
+ xradio_itp_err(priv, -EINVAL, 5);
+ else if (itp->data_mode >= ITP_DATA_MAX_MODE)
+ xradio_itp_err(priv, -EINVAL, 7);
+ else if (itp->data_len < ITP_MIN_DATA_SIZE ||
+ itp->data_len > priv->wsm_caps.sizeInpChBuf -
+ itp->hdr_len)
+ xradio_itp_err(priv, -EINVAL, 8);
+ else {
+ xradio_itp_tx_start(priv);
+ }
+ break;
+ case 5:
+ xradio_itp_get_version(priv, ITP_CHIP_ID);
+ break;
+ case 6:
+ xradio_itp_get_version(priv, ITP_FW_VER);
+ break;
+
+ }
+}
+
+static void xradio_itp_err(struct xradio_common *priv,
+ int err, int arg)
+{
+ struct xradio_itp *itp = &priv->debug->itp;
+ struct sk_buff *skb;
+ static char buf[255];
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "%d,%d\n",
+ err, arg);
+ if (len <= 0)
+ return;
+
+ skb = xr_alloc_skb(len);
+ if (!skb)
+ return;
+
+ skb_trim(skb, 0);
+ skb_put(skb, len);
+
+ memcpy(skb->data, buf, len);
+ skb_queue_tail(&itp->log_queue, skb);
+ wake_up(&itp->read_wait);
+
+ len = sprint_symbol(buf,
+ (unsigned long)__builtin_return_address(0));
+ if (len <= 0)
+ return;
+ itp_printk(XRADIO_DBG_MSG, "[ITP] error %d,%d from %s\n",
+ err, arg, buf);
+}
diff --git a/drivers/net/wireless/xradio/itp.h b/drivers/net/wireless/xradio/itp.h
new file mode 100644
index 0000000..0d4475e
--- /dev/null
+++ b/drivers/net/wireless/xradio/itp.h
@@ -0,0 +1,150 @@
+/*
+ * ITP 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_ITP_H_INCLUDED
+#define XRADIO_ITP_H_INCLUDED
+
+struct wsm_tx_confirm;
+struct dentry;
+
+#ifdef CONFIG_XRADIO_ITP
+
+/*extern*/ struct ieee80211_channel;
+
+#define TEST_MODE_NO_TEST (0)
+#define TEST_MODE_RX_TEST (1)
+#define TEST_MODE_TX_TEST (2)
+
+#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
+#define ITP_MIN_DATA_SIZE 6
+#define ITP_MAX_DATA_SIZE 1600
+#define ITP_TIME_THRES_US 10000
+#define ITP_US_TO_MS(x) ((x)/1000)
+#define ITP_MS_TO_US(x) ((x)*1000)
+#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1
+#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\
+precision with current HZ value !
+#endif
+#define ITP_BUF_SIZE 255
+
+
+enum xradio_itp_data_modes {
+ ITP_DATA_ZEROS,
+ ITP_DATA_ONES,
+ ITP_DATA_ZERONES,
+ ITP_DATA_RANDOM,
+ ITP_DATA_MAX_MODE,
+};
+
+enum xradio_itp_version_type {
+ ITP_CHIP_ID,
+ ITP_FW_VER,
+};
+
+enum xradio_itp_preamble_type {
+ ITP_PREAMBLE_LONG,
+ ITP_PREAMBLE_SHORT,
+ ITP_PREAMBLE_OFDM,
+ ITP_PREAMBLE_MIXED,
+ ITP_PREAMBLE_GREENFIELD,
+ ITP_PREAMBLE_MAX,
+};
+
+
+struct xradio_itp {
+ struct xradio_common *priv;
+ atomic_t open_count;
+ atomic_t awaiting_confirm;
+ struct sk_buff_head log_queue;
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+ wait_queue_head_t close_wait;
+ struct ieee80211_channel *saved_channel;
+ atomic_t stop_tx;
+ struct delayed_work tx_work;
+ struct delayed_work tx_finish;
+ spinlock_t tx_lock;
+ struct timespec last_sent;
+ atomic_t test_mode;
+ int rx_cnt;
+ long rx_rssi;
+ int rx_rssi_max;
+ int rx_rssi_min;
+ unsigned band;
+ unsigned ch;
+ unsigned rate;
+ unsigned preamble;
+ unsigned int number;
+ unsigned data_mode;
+ int interval_us;
+ int power;
+ u8 *data;
+ int hdr_len;
+ int data_len;
+ int id;
+};
+
+int xradio_itp_init(struct xradio_common *priv);
+void xradio_itp_release(struct xradio_common *priv);
+
+bool xradio_is_itp(struct xradio_common *priv);
+bool xradio_itp_rxed(struct xradio_common *priv, struct sk_buff *skb);
+void xradio_itp_wake_up_tx(struct xradio_common *priv);
+int xradio_itp_get_tx(struct xradio_common *priv, u8 **data,
+ size_t *tx_len, int *burst);
+bool xradio_itp_tx_running(struct xradio_common *priv);
+
+#else /* CONFIG_XRADIO_ITP */
+
+static inline int
+xradio_itp_init(struct xradio_common *priv)
+{
+ return 0;
+}
+
+static inline void xradio_itp_release(struct xradio_common *priv)
+{
+}
+
+static inline bool xradio_is_itp(struct xradio_common *priv)
+{
+ return false;
+}
+
+static inline bool xradio_itp_rxed(struct xradio_common *priv,
+ struct sk_buff *skb)
+{
+ return false;
+}
+
+
+static inline void xradio_itp_consume_txed(struct xradio_common *priv)
+{
+}
+
+static inline void xradio_itp_wake_up_tx(struct xradio_common *priv)
+{
+}
+
+static inline int xradio_itp_get_tx(struct xradio_common *priv, u8 **data,
+ size_t *tx_len, int *burst)
+{
+ return 0;
+}
+
+static inline bool xradio_itp_tx_running(struct xradio_common *priv)
+{
+ return false;
+}
+
+#endif /* CONFIG_XRADIO_ITP */
+
+#endif /* XRADIO_ITP_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/main.c b/drivers/net/wireless/xradio/main.c
new file mode 100644
index 0000000..098334b
--- /dev/null
+++ b/drivers/net/wireless/xradio/main.c
@@ -0,0 +1,1195 @@
+/*
+ * 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/module.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 "platform.h"
+#include "xradio.h"
+#include "txrx.h"
+#include "sbus.h"
+#include "fwio.h"
+#include "hwio.h"
+#include "bh.h"
+#include "sta.h"
+#include "ap.h"
+#include "scan.h"
+#include "pm.h"
+#include "xr_version.h"
+
+MODULE_AUTHOR("XRadioTech");
+MODULE_DESCRIPTION("XRadioTech WLAN driver core");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xradio_core");
+
+char *drv_version = XRADIO_VERSION;
+char *drv_buildtime = __DATE__" "__TIME__;
+
+#define XRADIO_MAC_CHARLEN 18
+#ifdef XRADIO_MACPARAM_HEX
+/* insmod xradio_wlan.ko macaddr=0xDC,0x44,0x6D,0x00,0x00,0x00 */
+static u8 xradio_macaddr_param[ETH_ALEN] = { 0x0 };
+module_param_array_named(macaddr, xradio_macaddr_param, byte, NULL, S_IRUGO);
+#else
+/* insmod xradio_wlan.ko macaddr=xx:xx:xx:xx:xx:xx */
+static char *xradio_macaddr_param = NULL;
+module_param_named(macaddr, xradio_macaddr_param, charp, S_IRUGO);
+#endif
+
+MODULE_PARM_DESC(macaddr, "First MAC address");
+
+#ifdef HW_RESTART
+void xradio_restart_work(struct work_struct *work);
+#endif
+
+/* 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 = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+#define CHAN5G(_channel, _flags) { \
+ .band = IEEE80211_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 IPV6_FILTERING
+ /*in linux3.4 mac,it does't have the api*/
+ //.set_data_filter = xradio_set_data_filter,
+#endif /*IPV6_FILTERING*/
+#ifdef CONFIG_XRADIO_TESTMODE
+ .testmode_cmd = xradio_testmode_cmd,
+#endif /* CONFIG_XRADIO_TESTMODE */
+};
+
+struct xradio_common *g_hw_priv;
+
+/*************************************** functions ***************************************/
+void xradio_version_show(void)
+{
+/* Show XRADIO version and compile time */
+ xradio_dbg(XRADIO_DBG_ALWY, "Driver Label:%s %s\n",
+ DRV_VERSION, DRV_BUILDTIME);
+
+/************* Linux Kernel config *************/
+#ifdef CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_USE_GPIO_IRQ
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_GPIO_IRQ]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_5GHZ_SUPPORT
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_5GHZ_SUPPORT]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_WAPI_SUPPORT
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_WAPI_SUPPORT]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_USE_EXTENSIONS
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_EXTENSIONS]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_USE_EXTENSIONS
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_EXTENSIONS]\n");
+#endif
+
+#ifdef CONFIG_PM
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_PM]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_SDIO
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_SDIO]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_DUMP_ON_ERROR
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_DUMP_ON_ERROR]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_DEBUGFS
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_DEBUGFS]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_ITP
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_ITP]\n");
+#endif
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_TESTMODE]\n");
+#endif
+
+/************ XRADIO Make File config ************/
+#ifdef P2P_MULTIVIF
+ xradio_dbg(XRADIO_DBG_NIY, "[P2P_MULTIVIF]\n");
+#endif
+
+#ifdef MCAST_FWDING
+ xradio_dbg(XRADIO_DBG_NIY, "[MCAST_FWDING]\n");
+#endif
+
+#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE
+ xradio_dbg(XRADIO_DBG_NIY, "[XRADIO_SUSPEND_RESUME_FILTER_ENABLE]\n");
+#endif
+
+#ifdef AP_AGGREGATE_FW_FIX
+ xradio_dbg(XRADIO_DBG_NIY, "[AP_AGGREGATE_FW_FIX]\n");
+#endif
+
+#ifdef AP_HT_CAP_UPDATE
+ xradio_dbg(XRADIO_DBG_NIY, "[AP_HT_CAP_UPDATE]\n");
+#endif
+
+#ifdef PROBE_RESP_EXTRA_IE
+ xradio_dbg(XRADIO_DBG_NIY, "[PROBE_RESP_EXTRA_IE]\n");
+#endif
+
+#ifdef IPV6_FILTERING
+ xradio_dbg(XRADIO_DBG_NIY, "[IPV6_FILTERING]\n");
+#endif
+
+#ifdef ROAM_OFFLOAD
+ xradio_dbg(XRADIO_DBG_NIY, "[ROAM_OFFLOAD]\n");
+#endif
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_0002_ROC_RESTART]\n");
+#endif
+
+#ifdef TES_P2P_000B_EXTEND_INACTIVITY_CNT
+ xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_000B_EXTEND_INACTIVITY_CNT]\n");
+#endif
+
+#ifdef TES_P2P_000B_DISABLE_EAPOL_FILTER
+ xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_000B_DISABLE_EAPOL_FILTER]\n");
+#endif
+
+#ifdef HAS_PUT_TASK_STRUCT
+ xradio_dbg(XRADIO_DBG_NIY, "[HAS_PUT_TASK_STRUCT]\n");
+#endif
+
+/************* XRADIO.h config *************/
+#ifdef HIDDEN_SSID
+ xradio_dbg(XRADIO_DBG_NIY, "[HIDDEN_SSID]\n");
+#endif
+
+#ifdef ROC_DEBUG
+ xradio_dbg(XRADIO_DBG_NIY, "[ROC_DEBUG]\n");
+#endif
+
+#ifdef XRADIO_RRM
+ xradio_dbg(XRADIO_DBG_NIY, "[XRADIO_RRM]\n");
+#endif
+}
+
+/* return 0: failed*/
+static inline int xradio_macaddr_val2char(char *c_mac, const u8* v_mac)
+{
+ SYS_BUG(!v_mac || !c_mac);
+ return sprintf(c_mac, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ v_mac[0], v_mac[1], v_mac[2],
+ v_mac[3], v_mac[4], v_mac[5]);
+}
+
+#ifndef XRADIO_MACPARAM_HEX
+static int xradio_macaddr_char2val(u8* v_mac, const char *c_mac)
+{
+ int i = 0;
+ const char *tmp_char = c_mac;
+ SYS_BUG(!v_mac || !c_mac);
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (*tmp_char != 0) {
+ v_mac[i] = simple_strtoul(tmp_char, (char **)&tmp_char, 16);
+ } else {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s, Len Error\n", __func__);
+ return -1;
+ }
+ if (i < ETH_ALEN -1 && *tmp_char != ':') {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s, Format or Len Error\n", __func__);
+ return -1;
+ }
+ tmp_char++;
+ }
+ return 0;
+}
+#endif
+
+#ifdef XRADIO_MACADDR_FROM_CHIPID
+extern void wifi_hwaddr_from_chipid(u8 *addr);
+#endif
+
+#define MACADDR_VAILID(a) ( \
+(a[0] != 0 || a[1] != 0 || \
+ a[2] != 0 || a[3] != 0 || \
+ a[4] != 0 || a[5] != 0) && \
+ !(a[0] & 0x3))
+
+static void xradio_get_mac_addrs(u8 *macaddr)
+{
+ int ret = 0;
+ SYS_BUG(!macaddr);
+ /* Check mac addrs param, if exsist, use it first.*/
+#ifdef XRADIO_MACPARAM_HEX
+ memcpy(macaddr, xradio_macaddr_param, ETH_ALEN);
+#else
+ if (xradio_macaddr_param) {
+ ret = xradio_macaddr_char2val(macaddr, xradio_macaddr_param);
+ }
+#endif
+
+#ifdef XRADIO_MACADDR_FROM_CHIPID
+ if (ret < 0 || !MACADDR_VAILID(macaddr)) {
+ wifi_hwaddr_from_chipid(macaddr);
+ }
+#endif
+ /* Use random value to set mac addr for the first time,
+ * and save it in wifi config file. TODO: read from product ID*/
+ if (ret < 0 || !MACADDR_VAILID(macaddr)) {
+#ifdef XRADIO_MACPARAM_HEX
+ ret = access_file(WIFI_CONF_PATH, macaddr, ETH_ALEN, 1);
+#else
+ char c_mac[XRADIO_MAC_CHARLEN+2] = {0};
+ ret = access_file(WIFI_CONF_PATH, c_mac, XRADIO_MAC_CHARLEN, 1);
+ if (ret >= 0) {
+ ret = xradio_macaddr_char2val(macaddr, c_mac);
+ }
+#endif
+ if(ret<0 || !MACADDR_VAILID(macaddr)) {
+ get_random_bytes(macaddr, 6);
+ macaddr[0] &= 0xFC;
+#ifdef XRADIO_MACPARAM_HEX
+ ret = access_file(WIFI_CONF_PATH, macaddr, ETH_ALEN, 0);
+#else
+ ret = xradio_macaddr_val2char(c_mac, macaddr);
+ ret = access_file(WIFI_CONF_PATH, c_mac, ret, 0);
+#endif
+ if(ret<0)
+ xradio_dbg(XRADIO_DBG_ERROR, "Access_file failed, path:%s!\n",
+ WIFI_CONF_PATH);
+ if (!MACADDR_VAILID(macaddr)) {
+ xradio_dbg(XRADIO_DBG_WARN, "Use default Mac addr!\n");
+ macaddr[0] = 0xDC;
+ macaddr[1] = 0x44;
+ macaddr[2] = 0x6D;
+ } else {
+ xradio_dbg(XRADIO_DBG_NIY, "Use random Mac addr!\n");
+ }
+ } else {
+ xradio_dbg(XRADIO_DBG_NIY, "Use Mac addr in file!\n");
+ }
+ }
+ xradio_dbg(XRADIO_DBG_NIY, "MACADDR=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ macaddr[0], macaddr[1], macaddr[2],
+ macaddr[3], macaddr[4], macaddr[5]);
+}
+
+static void xradio_set_ifce_comb(struct xradio_common *hw_priv,
+ struct ieee80211_hw *hw)
+{
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+#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;
+
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ /* 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;
+ xradio_dbg(XRADIO_DBG_ALWY, "Allocated hw_priv @ %p\n", hw_priv);
+ memset(hw_priv, 0, sizeof(*hw_priv));
+
+ /* Get MAC address. */
+ xradio_get_mac_addrs((u8 *)&hw_priv->addresses[0]);
+ memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN);
+ hw_priv->addresses[1].addr[5] += 0x01;
+#ifdef P2P_MULTIVIF
+ memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr, ETH_ALEN);
+ hw_priv->addresses[2].addr[4] ^= 0x80;
+#endif
+
+ /* 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);
+
+ hw->flags = IEEE80211_HW_SIGNAL_DBM |
+ IEEE80211_HW_SUPPORTS_PS |
+ IEEE80211_HW_SUPPORTS_DYNAMIC_PS |
+ IEEE80211_HW_REPORTS_TX_ACK_STATUS |
+ IEEE80211_HW_SUPPORTS_UAPSD |
+ 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 */
+ 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->channel_change_time = 500; /* TODO: find actual value */
+ hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
+ 8 /* TKIP IV */ +
+ 12 /* TKIP ICV and MIC */;
+ hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &xradio_band_2ghz;
+#ifdef CONFIG_XRADIO_5GHZ_SUPPORT
+ hw->wiphy->bands[IEEE80211_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 < IEEE80211_NUM_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 < IEEE80211_NUM_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 HW_RESTART
+ hw_priv->hw_restart = false;
+ INIT_WORK(&hw_priv->hw_restart_work, xradio_restart_work);
+#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);
+
+ if (!g_hw_priv) {
+ g_hw_priv = hw_priv;
+ return hw;
+ } else { //error:didn't release hw_priv last time.
+ ieee80211_free_hw(hw);
+ xradio_dbg(XRADIO_DBG_ERROR, "g_hw_priv is not NULL @ %p!\n", g_hw_priv);
+ return NULL;
+ }
+}
+
+void xradio_free_common(struct ieee80211_hw *dev)
+{
+ int i;
+ struct xradio_common *hw_priv = dev->priv;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+#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);
+ g_hw_priv = NULL;
+}
+
+int xradio_register_common(struct ieee80211_hw *dev)
+{
+ int err = 0;
+ struct xradio_common *hw_priv = dev->priv;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ SET_IEEE80211_DEV(dev, hw_priv->pdev);
+ err = ieee80211_register_hw(dev);
+ if (err) {
+ xradio_dbg(XRADIO_DBG_ERROR, "Cannot register device (%d).\n", err);
+ return err;
+ }
+ xradio_dbg(XRADIO_DBG_MSG, "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;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+
+ 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;
+}
+
+#ifdef HW_RESTART
+int xradio_core_reinit(struct xradio_common *hw_priv)
+{
+ int ret = 0;
+ u16 ctrl_reg;
+ int i = 0;
+ struct xradio_vif *priv = NULL;
+ struct wsm_operational_mode mode = {
+ .power_mode = wsm_power_mode_quiescent,
+ .disableMoreFlagUsage = true,
+ };
+
+ if (!hw_priv) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s hw_priv is NULL!\n", __func__);
+ return -1;
+ }
+
+ /* Need some time for restart hardware, don't suspend again.*/
+#ifdef CONFIG_PM
+ xradio_pm_lock_awake(&hw_priv->pm_state);
+#endif
+
+ xradio_dbg(XRADIO_DBG_ALWY, "%s %d!\n", __func__, __LINE__);
+ /* Disconnect with AP or STAs. */
+ 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->join_status == XRADIO_JOIN_STATUS_STA) {
+ ieee80211_connection_loss(priv->vif);
+ msleep(200);
+ } else if (priv->join_status == XRADIO_JOIN_STATUS_AP) {
+ wms_send_disassoc_to_self(hw_priv, priv);
+ msleep(200);
+ }
+ }
+ xradio_unregister_common(hw_priv->hw);
+
+ /*deinit dev */
+ xradio_dev_deinit(hw_priv);
+
+ /*reinit status refer to hif. */
+ hw_priv->powersave_enabled = false;
+ hw_priv->wsm_caps.firmwareReady = 0;
+ atomic_set(&hw_priv->bh_rx, 0);
+ atomic_set(&hw_priv->bh_tx, 0);
+ atomic_set(&hw_priv->bh_term, 0);
+ hw_priv->buf_id_tx = 0;
+ hw_priv->buf_id_rx = 0;
+ hw_priv->wsm_rx_seq = 0;
+ hw_priv->wsm_tx_seq = 0;
+ hw_priv->device_can_sleep = 0;
+ hw_priv->hw_bufs_used = 0;
+ memset(&hw_priv->hw_bufs_used_vif, 0, sizeof(hw_priv->hw_bufs_used_vif));
+ memset(&hw_priv->connet_time, 0, sizeof(hw_priv->connet_time));
+ atomic_set(&hw_priv->query_cnt, 0);
+ hw_priv->query_packetID = 0;
+ tx_policy_init(hw_priv);
+
+ /*reinit sdio sbus. */
+ sbus_sdio_deinit();
+ msleep(100);
+ hw_priv->pdev = sbus_sdio_init((struct sbus_ops **)&hw_priv->sbus_ops,
+ &hw_priv->sbus_priv);
+ if (!hw_priv->pdev) {
+ xradio_dbg(XRADIO_DBG_ERROR,"%s:sbus_sdio_init failed\n", __func__);
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+
+ /*wake up bh thread. */
+ if (hw_priv->bh_thread == NULL) {
+ hw_priv->bh_error = 0;
+ atomic_set(&hw_priv->tx_lock, 0);
+ xradio_register_bh(hw_priv);
+ } else {
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+ WARN_ON(xradio_bh_resume(hw_priv));
+#endif
+ }
+
+ /* Load firmware and register Interrupt Handler */
+
+ ret = xradio_load_firmware(hw_priv);
+ if (ret) {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s:xradio_load_firmware failed(%d).\n",
+ __func__, ret);
+ goto exit;
+ }
+
+ /* Set sdio blocksize. */
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+ SYS_WARN(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv,
+ SDIO_BLOCK_SIZE));
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_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, "%s:Firmware Startup Timeout!\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto exit;
+ }
+ xradio_dbg(XRADIO_DBG_ALWY, "%s:Firmware Startup Done.\n", __func__);
+
+ hw_priv->hw_restart = false;
+#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF
+ atomic_set(&hw_priv->suspend_state, XRADIO_RESUME);
+#endif
+
+ /* Keep device wake up. */
+ ret = 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))
+ ret = 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 (i = 0; i < xrwl_get_nr_hw_ifaces(hw_priv); i++) {
+ /* Set low-power mode. */
+ ret = wsm_set_operational_mode(hw_priv, &mode, i);
+ /* Enable multi-TX confirmation */
+ ret = wsm_use_multi_tx_conf(hw_priv, true, i);
+ }
+
+ /* re-Register wireless net device. */
+ if (!ret)
+ ret = xradio_register_common(hw_priv->hw);
+
+ /* unlock queue if need. */
+ for (i = 0; i < 4; ++i) {
+ struct xradio_queue *queue = &hw_priv->tx_queue[i];
+ spin_lock_bh(&queue->lock);
+ if (queue->tx_locked_cnt > 0) {
+ queue->tx_locked_cnt = 0;
+ ieee80211_wake_queue(hw_priv->hw, queue->queue_id);
+ xradio_dbg(XRADIO_DBG_WARN, "%s: unlock queue!\n", __func__);
+ }
+ spin_unlock_bh(&queue->lock);
+ }
+exit:
+#ifdef CONFIG_PM
+ xradio_pm_unlock_awake(&hw_priv->pm_state);
+#endif
+ xradio_dbg(XRADIO_DBG_ALWY, "%s end!\n", __func__);
+
+ return ret;
+}
+
+void xradio_restart_work(struct work_struct *work)
+{
+ struct xradio_common *hw_priv =
+ container_of(work, struct xradio_common, hw_restart_work);
+ xradio_dbg(XRADIO_DBG_ALWY, "%s\n", __func__);
+
+ if (hw_priv->bh_error) {
+ xradio_unregister_bh(hw_priv);
+ }
+ if (unlikely(xradio_core_reinit(hw_priv))) {
+ pm_printk(XRADIO_DBG_ALWY, "%s again!\n", __func__);
+ mutex_lock(&hw_priv->wsm_cmd_mux);
+ hw_priv->hw_restart = true;
+ mutex_unlock(&hw_priv->wsm_cmd_mux);
+ xradio_unregister_bh(hw_priv);
+ xradio_core_reinit(hw_priv);
+ }
+}
+#endif
+
+int xradio_core_init(void)
+{
+ 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,
+ };
+ xradio_version_show();
+
+ //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;
+
+ //init sdio sbus
+ hw_priv->pdev = sbus_sdio_init((struct sbus_ops **)&hw_priv->sbus_ops,
+ &hw_priv->sbus_priv);
+ if (!hw_priv->pdev) {
+ err = -ETIMEDOUT;
+ xradio_dbg(XRADIO_DBG_ERROR,"sbus_sdio_init failed\n");
+ goto err1;
+ }
+
+ /* 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.rx = xradio_rx_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. */
+ hw_priv->sbus_ops->lock(hw_priv->sbus_priv);
+ SYS_WARN(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv,
+ SDIO_BLOCK_SIZE));
+ hw_priv->sbus_ops->unlock(hw_priv->sbus_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:
+ sbus_sdio_deinit();
+err1:
+ xradio_free_common(dev);
+ return err;
+}
+EXPORT_SYMBOL_GPL(xradio_core_init);
+
+void xradio_core_deinit(void)
+{
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ if (g_hw_priv) {
+#ifdef HW_RESTART
+ cancel_work_sync(&g_hw_priv->hw_restart_work);
+#endif
+ xradio_unregister_common(g_hw_priv->hw);
+ xradio_dev_deinit(g_hw_priv);
+ xradio_unregister_bh(g_hw_priv);
+ xradio_pm_deinit(&g_hw_priv->pm_state);
+ xradio_free_common(g_hw_priv->hw);
+ sbus_sdio_deinit();
+ }
+ return;
+}
+EXPORT_SYMBOL_GPL(xradio_core_deinit);
+
+/* Init Module function -> Called by insmod */
+static int __init xradio_core_entry(void)
+{
+ int ret = 0;
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ ret = xradio_plat_init();
+ if (ret) {
+ xradio_dbg(XRADIO_DBG_ERROR,"xradio_plat_init failed(%d)!\n", ret);
+ }
+ ret = xradio_host_dbg_init();
+ ret = xradio_core_init();
+ return ret;
+}
+
+/* Called at Driver Unloading */
+static void __exit xradio_core_exit(void)
+{
+ xradio_core_deinit();
+ xradio_host_dbg_deinit();
+ xradio_plat_deinit();
+ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+}
+
+module_init(xradio_core_entry);
+module_exit(xradio_core_exit);
+
diff --git a/drivers/net/wireless/xradio/nl80211_testmode_msg_copy.h b/drivers/net/wireless/xradio/nl80211_testmode_msg_copy.h
new file mode 100644
index 0000000..0bd3286
--- /dev/null
+++ b/drivers/net/wireless/xradio/nl80211_testmode_msg_copy.h
@@ -0,0 +1,157 @@
+/*
+ * test mode 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 XR_NL80211_TESTMODE_MSG_COPY_H
+#define XR_NL80211_TESTMODE_MSG_COPY_H
+
+/* example command structure for test purposes */
+struct xr_msg_test_t {
+ int dummy;
+};
+
+/* example reply structure for test purposes */
+struct xr_reply_test_t {
+ int dummy;
+};
+
+/* example event structure for test purposes */
+struct xr_event_test_t {
+ int dummy;
+};
+
+enum xr_msg_id {
+ XR_MSG_TEST = 0, /* for test purposes */
+ XR_MSG_EVENT_TEST, /* for test purposes */
+ XR_MSG_SET_SNAP_FRAME,
+ XR_MSG_EVENT_FRAME_DATA,
+#ifdef CONFIG_XRADIO_TESTMODE
+ XR_MSG_GET_TX_POWER_LEVEL,
+ XR_MSG_GET_TX_POWER_RANGE,
+ XR_MSG_SET_ADVANCE_SCAN_ELEMS,
+ XR_MSG_SET_TX_QUEUE_PARAMS,
+ XR_MSG_START_STOP_TSM,
+ XR_MSG_GET_TSM_PARAMS,
+ XR_MSG_GET_ROAM_DELAY,
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ XR_MSG_SET_POWER_SAVE,
+ /* Add new IDs here */
+
+ XR_MSG_ID_MAX,
+};
+
+enum xr_nl80211_testmode_data_attributes {
+ XR_TM_MSG_ID = 0x0001, /* u32 type containing the XR message ID */
+ XR_TM_MSG_DATA, /* message payload */
+
+ /* Max indicator so module test may add its own attributes */
+ XR_TM_MSG_ATTR_MAX,
+};
+
+/**
+ * xr_msg_set_snap_frame - set SNAP frame format
+ * @len: length of SNAP frame, if 0 SNAP frame disabled
+ * @frame: SNAP frame format
+ *
+ * In this structure is difference between user space because
+ * format and length have to be hidden
+ *
+ */
+struct xr_msg_set_snap_frame {
+ u8 len;
+ u8 frame[0];
+};
+
+#ifdef CONFIG_XRADIO_TESTMODE
+/**
+ * xr_msg_set_txqueue_params - store Tx queue params
+ * @user_priority: User priority for which TSPEC negotiated
+ * @medium_time: Allowed medium time
+ * @expiry_time: The expiry time of MSDU
+ *
+ */
+struct xr_msg_set_txqueue_params {
+ u8 user_priority;
+ u16 medium_time;
+ u16 expiry_time;
+};
+
+/**
+ * xr_tsm_stats - To retrieve the Transmit Stream Measurement stats
+ * @actual_msrmt_start_time: The TSF at the time at which the measurement
+ * started
+ * @msrmt_duration: Duration for measurement
+ * @peer_sta_addr: Peer STA address
+ * @tid: TID for which measurements were made
+ * @reporting_reason: Reason for report sent
+ * @txed_msdu_count: The number of MSDUs transmitted for the specified TID
+ * @msdu_discarded_count: The number of discarded MSDUs for the specified TID
+ * @msdu_failed_count: The number of failed MSDUs for the specified TID
+ * @multi_retry_count: The number of MSDUs which were retried
+ * @qos_cfpolls_lost_count: The number of QOS CF polls frames lost
+ * @avg_q_delay: Average queue delay
+ * @avg_transmit_delay: Average transmit delay
+ * @bin0_range: Delay range of the first bin (Bin 0)
+ * @bin0: bin0 transmit delay histogram
+ * @bin1: bin1 transmit delay histogram
+ * @bin2: bin2 transmit delay histogram
+ * @bin3: bin3 transmit delay histogram
+ * @bin4: bin4 transmit delay histogram
+ * @bin5: bin5 transmit delay histogram
+ *
+ */
+struct xr_tsm_stats {
+ u64 actual_msrmt_start_time;
+ u16 msrmt_duration;
+ u8 peer_sta_addr[6];
+ u8 tid;
+ u8 reporting_reason;
+ u32 txed_msdu_count;
+ u32 msdu_discarded_count;
+ u32 msdu_failed_count;
+ u32 multi_retry_count;
+ u32 qos_cfpolls_lost_count;
+ u32 avg_q_delay;
+ u32 avg_transmit_delay;
+ u8 bin0_range;
+ u32 bin0;
+ u32 bin1;
+ u32 bin2;
+ u32 bin3;
+ u32 bin4;
+ u32 bin5;
+} __packed;
+
+
+/**
+ * xr_msg_set_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 period for this TID
+ *
+ */
+struct xr_msg_start_stop_tsm {
+ u8 start; /*1: To start, 0: To stop*/
+ u8 up;
+ u16 packetization_delay;
+};
+
+/**
+ * power_save_elems - To enable/disable legacy power Save
+ */
+struct power_save_elems {
+ int powerSave;
+};
+#endif /*CONFIG_XRADIO_TESTMODE*/
+
+#define XR_TM_MAX_ATTRIB_SIZE 1024
+
+#endif /* XR_NL80211_TESTMODE_MSG_COPY_H*/
diff --git a/drivers/net/wireless/xradio/platform.c b/drivers/net/wireless/xradio/platform.c
new file mode 100644
index 0000000..fc33a8b
--- /dev/null
+++ b/drivers/net/wireless/xradio/platform.c
@@ -0,0 +1,942 @@
+/*
+ * Platform interfaces for XRadio drivers
+ *
+ * Implemented by platform vendor(such as AllwinnerTech).
+ *
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/ioport.h>
+
+#include <linux/regulator/consumer.h>
+#include <asm/mach-types.h>
+#include <mach/sys_config.h>
+
+#include "xradio.h"
+#include "platform.h"
+#include "sbus.h"
+
+#define PLATFORM_RFKILL_PM 1 //if 1, Use system rf-kill for wifi power manage, default 0.
+#define PLATFORM_SYSCONFIG 1 //if 1, Get Hardware settting from sysconfig.fex, default 0.
+#define PMU_POWER_WLAN_RETAIN 1 //if 1, PMU power still up when wifi is closed, default 0.
+
+//hardware platform config.
+static int wlan_bus_id = 0;
+static u32 wlan_irq_gpio = 0;
+static u32 wlan_reset_gpio = 0;
+
+#if (PLATFORM_SYSCONFIG)
+#define WIFI_CONFIG "wifi_para"
+#define MAX_POWER_NUM 3
+static char * axp_name[MAX_POWER_NUM] = {0};
+#endif
+
+//--------------------------------------------------
+//Select one platform if PLATFORM_RFKILL_PM=0.
+//And you can platforms too.
+//--------------------------------------------------
+//#define V819MINI
+//#define HP8_G2
+//#define EVB_A31_CARD0
+//#define EVB_A31_CARD1
+//#define EVB_A83T_ONBOARD
+#define EVB_A33_XR819
+//#define EVB_A33W_XR819
+//#define A33_CHIPHD
+//#define A33_YHK
+//#define A33W_SUNFLOWER_S1
+
+#if (!PLATFORM_SYSCONFIG)
+//--------------------------------------------------
+// sdio and gpio settings of platforms
+// use following settings if PLATFORM_SYSCONFIG=0
+// You can add configs for other platforms
+//--------------------------------------------------
+//V819MINI
+#ifdef V819MINI
+#define PLAT_GPIO_WRESET (GPIOM(7))
+#define PLAT_GPIO_WLAN_INT (GPIOL(6))
+#define PLAT_MMC_ID 1
+
+//HP8 G2
+#elif defined(HP8_G2)
+#define PLAT_GPIO_WRESET (GPIOL(6))
+#define PLAT_GPIO_WLAN_INT (GPIOL(7))
+#define PLAT_MMC_ID 1
+
+//EVB_A83T_ONBOARD
+#elif defined(EVB_A83T_ONBOARD)
+#define PLAT_GPIO_WRESET (GPIOL(9))
+#define PLAT_GPIO_WLAN_INT (GPIOL(11))
+#define PLAT_MMC_ID 1
+
+//EVB_A33_XR819_test
+#elif defined(EVB_A33_XR819)
+#define PLAT_GPIO_WRESET (GPIOL(10))
+#define PLAT_GPIO_WLAN_INT (GPIOL(8))
+#define PLAT_MMC_ID 1
+
+//EVB_A33W_XR819_test
+#elif defined(EVB_A33W_XR819)
+#define PLAT_GPIO_WRESET (GPIOL(6))
+#define PLAT_GPIO_WLAN_INT (GPIOL(7))
+#define PLAT_MMC_ID 1
+
+//A33W_SUNFLOWER_S1
+#elif defined(A33W_SUNFLOWER_S1)
+#define PLAT_GPIO_WRESET (GPIOL(8))
+#define PLAT_GPIO_WLAN_INT (GPIOL(9))
+#define PLAT_MMC_ID 1
+
+//A33_CHIPHD
+#elif defined(A33_CHIPHD)
+#define PLAT_GPIO_WRESET (GPIOL(6))
+#define PLAT_GPIO_WLAN_INT (GPIOL(7))
+#define PLAT_MMC_ID 1
+
+//A33_YHK
+#elif defined(A33_YHK)
+#define PLAT_GPIO_WRESET (GPIOL(5))
+#define PLAT_GPIO_WLAN_INT (GPIOL(7))
+#define PLAT_MMC_ID 1
+
+//defualt EVB_A31_CARD0.
+#elif defined(EVB_A31_CARD0)
+#define PLAT_GPIO_WRESET (GPIOM(4))
+#define PLAT_GPIO_WLAN_INT (GPIOM(6))
+#define PLAT_MMC_ID 0
+
+#else //defualt EVB_A31_CARD1.
+#define PLAT_GPIO_WRESET (GPIOM(4))
+#define PLAT_GPIO_WLAN_INT (GPIOM(6))
+#define PLAT_MMC_ID 1
+
+#endif //end of V819MINI
+
+#endif //PLATFORM_SYSCONFIG
+
+/*********************Interfaces on Allwinner platform. *********************/
+#if (!PLATFORM_RFKILL_PM)
+
+#if (defined(EVB_A31_CARD0)||defined(EVB_A31_CARD1))
+static int plat_evb_a31_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ unsigned int clk;
+ int val;
+ int ret = 0;
+
+ if (!on) return ret; //no need power down on evb_a31.
+
+#ifdef EVB_A31_CARD0
+ printk(KERN_ERR "evb card0 (red card) present!.\n");
+ //set sd card 0 to 1.8v
+ ldo = regulator_get(NULL, "axp22_eldo2");
+ if(ldo) {
+ //set new voltage 1.8v
+ val = 1800000;
+ if(regulator_set_voltage(ldo, val, val) < 0)
+ printk(KERN_ERR "regulator_set_voltage failed.\n");
+ //enable regulator
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ //check voltage
+ val = regulator_get_voltage(ldo);
+ printk(KERN_ERR "sdio_vol(eldo2)=%duV.\n", val);
+ } else {
+ printk(KERN_ERR "regulator get eldo2 failed.\n");
+ }
+ regulator_put(ldo);
+
+ //check pm_io voltage
+ ldo = regulator_get(NULL, "axp22_aldo2");
+ if(ldo) {
+ //try to set 1.8v to pm voltage
+ val = 1800000;
+ regulator_set_voltage(ldo, val, val);
+ regulator_enable(ldo);
+
+ val = regulator_get_voltage(ldo);
+ printk(KERN_ERR "pm_io_vol(aldo2)=%duV.\n", val);
+ } else {
+ printk(KERN_ERR "regulator get aldo2 failed.\n");
+ }
+ regulator_put(ldo);
+
+#else
+ printk(KERN_ERR "evb card1 (green card) present!.\n");
+
+ //check pm_io voltage
+ ldo = regulator_get(NULL, "axp22_aldo2");
+ if(ldo) {
+ //try to set 1.8v to pm voltage
+ val = 1800000;
+ regulator_set_voltage(ldo, val, val);
+ regulator_enable(ldo);
+
+ val = regulator_get_voltage(ldo);
+ printk(KERN_ERR "pm_io_vol(aldo2)=%duV.\n", val);
+ } else {
+ printk(KERN_ERR "regulator get aldo2 failed.\n");
+ }
+ regulator_put(ldo);
+
+ /* pm7Ƥ׃Ϊrtc_clk0 */
+ clk = ioread32(0xf1f02c24);
+ clk &= ~(0x3 << 28);
+ clk |= 0x3 << 28;
+ iowrite32(clk, 0xf1f02c24);
+
+ /* loscƤԉexternal 32.768KHz */
+ clk = ioread32(0xf1f00004);
+ clk &= ~0x1;
+ clk |= 0x1;
+ iowrite32(clk, 0xf1f00004);
+
+ /* clk_outputdʹŜ */
+ clk = ioread32(0xf1f014f0);
+ clk = 0x80000000;
+ iowrite32(clk, 0xf1f014f0);
+
+ printk(KERN_ERR "[clock] clock init RTC-CKO 32768Hz," \
+ " 0xf1f02c24=%#x, 0xf1f00004=%#x, 0xf1f014f0=%#x\n",
+ ioread32(0xf1f02c24), ioread32(0xf1f00004), ioread32(0xf1f014f0));
+
+ gpio_request(GPIOM(3), "pmu_en");
+ gpio_direction_output(GPIOM(3), 0);
+ gpio_set_value(GPIOM(3), 1);
+ udelay(300);
+
+ gpio_request(GPIOG(12), "gbf_ena_reset");
+ gpio_direction_output(GPIOG(12), 0);
+ gpio_set_value(GPIOG(12), 1);
+ udelay(300);
+
+ gpio_free(GPIOM(3));
+ gpio_free(GPIOG(12));
+#endif //EVB_CARD0
+
+ return ret;
+}
+#endif
+
+#ifdef V819MINI
+static int plat_v819mini_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ int ret = 0;
+
+ xradio_dbg(XRADIO_DBG_ERROR, "V819mini Power %s!.\n", on? "Up":"Down");
+
+ if (on) {
+ //wifi vcc 3.3v for XR819
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ //pg sdio pin 3.3v for XR819
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi reset 3.3v for XR819
+ ldo = regulator_get(NULL, "axp22_dldo4");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo4=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ } else {
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ }
+
+ return ret;
+}
+#endif
+
+#if defined(HP8_G2)
+static int plat_hp8_g2_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ int ret = 0;
+
+ printk(KERN_ERR "hp8_g2 Power %s!.\n", on? "Up":"Down");
+
+ if (on) {
+ //wifi vcc 3.3v for XR819.
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi vcc 1.8v for XR819.
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_eldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable eldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi io pin 1.8~3.3v for XR819.
+ ldo = regulator_get(NULL, "axp22_aldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3000000, 3000000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable aldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ //wifi reset & interrupt 1.8~3.3v for XR819.
+ } else {
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_eldo2");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ }
+
+ return ret;
+}
+#endif
+
+#if defined(EVB_A83T_ONBOARD)
+static int plat_evb_a83t_onboard_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ int ret = 0;
+ printk(KERN_ERR "evb_a83t_onboard Power %s!.\n", on? "Up":"Down");
+ if (on) {
+ //wifi vcc 3.3v for XR819.
+ ldo = regulator_get(NULL, "axp81x_dldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi vcc 1.8v for XR819.
+ ldo = regulator_get(NULL, "axp81x_aldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable aldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi io pin 1.8~3.3v for XR819b.
+ ldo = regulator_get(NULL, "axp81x_aldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3000000, 3000000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable aldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi reset & interrupt 1.8~3.3v for XR819.
+ ldo = regulator_get(NULL, "axp81x_aldo3");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3000000, 3000000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable aldo3=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ } else {
+ ldo = regulator_get(NULL, "axp81x_dldo2");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp81x_aldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ }
+
+ return ret;
+}
+#endif
+
+#if (defined(EVB_A33_XR819) || defined(EVB_A33W_XR819))
+static int plat_a33_xr819_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ int ret = 0;
+
+ printk(KERN_ERR "plat_a33_xr819_pwr Power %s!.\n", on? "Up":"Down");
+ if (on) {
+ //wifi vcc 3.3v for XR819.
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo4");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo4=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi vcc 1.8v for XR819.
+ ldo = regulator_get(NULL, "axp22_eldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable eldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_eldo3");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable eldo3=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ //wifi reset & interrupt 1.8~3.3v for XR819.
+ } else {
+ //wifi vcc 3.3v
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo4");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ //wifi vcc 1.8v
+ ldo = regulator_get(NULL, "axp22_eldo2");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_eldo3");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ }
+ return ret;
+}
+#endif
+
+#if (defined(A33W_SUNFLOWER_S1))
+static int plat_a33w_sunflower_s1_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ int ret = 0;
+ struct clk *ap_32k = NULL;
+
+ printk(KERN_ERR "plat_a33w_sunflower_s1_pwr Power %s!.\n", on? "Up":"Down");
+ if (on) {
+ //wifi vcc 3.3v for XR819.
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ //wifi vcc 1.8v for XR819.
+ ldo = regulator_get(NULL, "axp22_eldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable eldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ } else {
+ //wifi vcc 3.3v
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ //wifi vcc 1.8v
+ ldo = regulator_get(NULL, "axp22_eldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ }
+ //use external 32k clock.
+ ap_32k = clk_get(NULL, "losc_out");
+ if (!ap_32k){
+ printk(KERN_ERR "Get ap 32k clk out failed!\n");
+ return -1;
+ }
+ ret = clk_prepare_enable(ap_32k);
+ if (ret){
+ printk(KERN_ERR "losc out enable failed!\n");
+ }
+ return ret;
+}
+#endif
+
+#if (defined(A33_CHIPHD) || defined(A33_YHK))
+static int plat_a33_chiphd_pwr(int on)
+{
+ struct regulator *ldo = NULL;
+ int ret = 0;
+ struct clk *ap_32k = NULL;
+
+ printk(KERN_ERR "plat_a33_chiphd_pwr Power %s!.\n", on? "Up":"Down");
+ if (on) {
+ //wifi vcc 3.3v for XR819.
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ regulator_set_voltage(ldo, 3300000, 3300000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+
+ //wifi vcc 1.8v for XR819.
+ ldo = regulator_get(NULL, "axp22_eldo1");
+ if (ldo) {
+ regulator_set_voltage(ldo, 1800000, 1800000);
+ ret = regulator_enable(ldo);
+ if(ret < 0){
+ printk(KERN_ERR "regulator_enable failed.\n");
+ }
+ printk(KERN_ERR "regulator_enable eldo1=%d.\n", regulator_get_voltage(ldo));
+ regulator_put(ldo);
+ }
+ //wifi reset & interrupt 1.8~3.3v for XR819.
+ } else {
+ //wifi vcc 3.3v
+ ldo = regulator_get(NULL, "axp22_dldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ ldo = regulator_get(NULL, "axp22_dldo2");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ //wifi vcc 1.8v
+ ldo = regulator_get(NULL, "axp22_eldo1");
+ if (ldo) {
+ ret = regulator_disable(ldo);
+ regulator_put(ldo);
+ }
+ }
+
+ //use external 32k clock.
+ ap_32k = clk_get(NULL, "losc_out");
+ if (!ap_32k){
+ printk(KERN_ERR "Get ap 32k clk out failed!\n");
+ return -1;
+ }
+#if 1
+ ret = clk_prepare_enable(ap_32k);
+ if (ret){
+ printk(KERN_ERR "losc out enable failed!\n");
+ }
+#else
+ clk_disable(ap_32k); //disable 32k clk.
+#endif
+
+ return ret;
+}
+#endif
+
+int plat_module_power(int enable)
+{
+ int ret = 0;
+
+#ifdef V819MINI
+ ret = plat_v819mini_pwr(enable);
+#elif defined(HP8_G2)
+ ret = plat_hp8_g2_pwr(enable);
+#elif defined(EVB_A83T_ONBOARD)
+ ret = plat_evb_a83t_onboard_pwr(enable);
+#elif (defined(EVB_A33_XR819) || defined(EVB_A33W_XR819))
+ ret = plat_a33_xr819_pwr(enable);
+#elif (defined(A33W_SUNFLOWER_S1))
+ ret = plat_a33w_sunflower_s1_pwr(enable);
+#elif (defined(A33_CHIPHD) || defined(A33_YHK))
+ ret = plat_a33_chiphd_pwr(enable);
+#elif (defined(EVB_A31_CARD0)||defined(EVB_A31_CARD1))
+ ret = plat_evb_a31_pwr(enable);
+#else
+ printk(KERN_ERR "plat_module_power: Fail to setup power!\n");
+#endif
+
+ if (enable) { //delay for power stable period.
+ mdelay(50);
+ }
+ return ret;
+}
+#endif //PLATFORM_RFKILL_PM
+
+/*********************Interfaces called by xradio core. *********************/
+#if (PLATFORM_SYSCONFIG)
+static int xradio_get_syscfg(void)
+{
+ script_item_u val;
+ script_item_value_type_e type;
+
+ /* Get SDIO/USB config. */
+#if defined(CONFIG_XRADIO_SDIO)
+ type = script_get_item(WIFI_CONFIG, "wifi_sdc_id", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
+ printk(KERN_ERR "failed to fetch sdio card's sdcid\n");
+ return -1;
+ }
+#elif defined(CONFIG_XRADIO_USB)
+ type = script_get_item(WIFI_CONFIG, "wifi_usbc_id", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_INT != type) {
+ printk(KERN_ERR "failed to fetch usb's id\n");
+ return -1;
+ }
+#endif
+ wlan_bus_id = val.val;
+
+ /* Get GPIO config. */
+ type = script_get_item(WIFI_CONFIG, "wl_reg_on", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_PIO != type) {
+ printk(KERN_ERR "failed to fetch wl_reg_on\n");
+ return -1;
+ }
+ wlan_reset_gpio = val.gpio.gpio;
+
+ type = script_get_item(WIFI_CONFIG, "wl_host_wake", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_PIO != type) {
+ printk(KERN_ERR "failed to fetch xradio_wl_host_wake\n");
+ return -1;
+ }
+ wlan_irq_gpio = val.gpio.gpio;
+
+ /* Get Power config. */
+#if 0
+ type = script_get_item(WIFI_CONFIG, "wifi_power", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_STR == type) {
+ axp_name[0] = val.str;
+ printk("wifi_power=%s\n", axp_name[0]);
+ } else {
+ printk(KERN_ERR "wifi_power not config\n");
+ }
+ type = script_get_item(WIFI_CONFIG, "wifi_power_ext1", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_STR == type) {
+ axp_name[1] = val.str;
+ printk("wifi_power_ext1=%s\n", axp_name[1]);
+ } else {
+ printk(KERN_ERR "wifi_power_ext1 not config\n");
+ }
+ type = script_get_item(WIFI_CONFIG, "wifi_power_ext2", &val);
+ if (SCIRPT_ITEM_VALUE_TYPE_STR == type) {
+ axp_name[2] = val.str;
+ printk("wifi_power_ext2=%s\n", axp_name[2]);
+ } else {
+ printk(KERN_ERR "wifi_power_ext2 not config\n");
+ }
+#endif
+ return 0;
+}
+#endif
+
+int xradio_plat_init(void)
+{
+ int ret = 0;
+#if (!PLATFORM_SYSCONFIG)
+ wlan_bus_id = PLAT_MMC_ID;
+ wlan_irq_gpio = PLAT_GPIO_WLAN_INT;
+ wlan_reset_gpio = PLAT_GPIO_WRESET;
+#else //initialize from sys config.
+ ret = xradio_get_syscfg();
+#endif //PLATFORM_SYSCONFIG
+
+#if (PMU_POWER_WLAN_RETAIN && !PLATFORM_RFKILL_PM)
+ plat_module_power(1);
+#endif
+ return ret;
+}
+
+void xradio_plat_deinit(void)
+{
+#if (PMU_POWER_WLAN_RETAIN && !PLATFORM_RFKILL_PM)
+ plat_module_power(0);
+#endif
+}
+
+#if (PLATFORM_RFKILL_PM)
+//extern void wifi_pm_power(int on);
+//void wifi_pm_power(int on)
+void wifi_pm(int on)
+{
+ printk(KERN_ERR "xradio wlan power %s\n", on?"on":"off");
+ if (on) {
+ wifi_pm_gpio_ctrl("wl_reg_on", 1);
+ mdelay(50);
+ wifi_pm_gpio_ctrl("wl_reg_on", 0);
+ mdelay(2);
+ wifi_pm_gpio_ctrl("wl_reg_on", 1);
+ mdelay(50);
+ } else {
+ wifi_pm_gpio_ctrl("wl_reg_on", 0);
+ }
+}
+#endif
+int xradio_wlan_power(int on)
+{
+ int ret = 0;
+
+#if (PLATFORM_RFKILL_PM)
+ //wifi_pm_power(on);
+ wifi_pm(on);
+ mdelay(50);
+#else //PLATFORM_RFKILL_PM
+ ret = gpio_request(wlan_reset_gpio, "wlan_reset");
+ if (!ret) {
+ if (on) { //power up.
+#if (!PMU_POWER_WLAN_RETAIN)
+ plat_module_power(1);
+ mdelay(50);
+#endif
+ gpio_direction_output(wlan_reset_gpio, 1);
+ mdelay(50);
+ gpio_direction_output(wlan_reset_gpio, 0);
+ mdelay(2);
+ gpio_direction_output(wlan_reset_gpio, 1);
+ mdelay(50);
+ } else { //power down.
+ gpio_direction_output(wlan_reset_gpio, 0);
+#if (!PMU_POWER_WLAN_RETAIN)
+ plat_module_power(0);
+#endif
+ }
+ gpio_free(wlan_reset_gpio);
+ } else {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: gpio_request err: %d\n", __func__, ret);
+ }
+#endif
+ return ret;
+}
+
+int xradio_sdio_detect(int enable)
+{
+ int insert = enable;
+ MCI_RESCAN_CARD(wlan_bus_id, insert);
+ xradio_dbg(XRADIO_DBG_ALWY, "%s SDIO card %d\n",
+ enable?"Detect":"Remove", wlan_bus_id);
+ mdelay(10);
+ return 0;
+}
+
+#ifdef CONFIG_XRADIO_USE_GPIO_IRQ
+static u32 gpio_irq_handle = 0;
+#ifdef PLAT_ALLWINNER_SUNXI
+static irqreturn_t xradio_gpio_irq_handler(int irq, void *sbus_priv)
+{
+ struct sbus_priv *self = (struct sbus_priv *)sbus_priv;
+ unsigned long flags;
+
+ SYS_BUG(!self);
+ spin_lock_irqsave(&self->lock, flags);
+ if (self->irq_handler)
+ self->irq_handler(self->irq_priv);
+ spin_unlock_irqrestore(&self->lock, flags);
+ return IRQ_HANDLED;
+}
+#else //PLAT_ALLWINNER_SUN6I
+static u32 xradio_gpio_irq_handler(void *sbus_priv)
+{
+ struct sbus_priv *self = (struct sbus_priv *)sbus_priv;
+ unsigned long flags;
+
+ SYS_BUG(!self);
+ spin_lock_irqsave(&self->lock, flags);
+ if (self->irq_handler)
+ self->irq_handler(self->irq_priv);
+ spin_unlock_irqrestore(&self->lock, flags);
+ return 0;
+}
+#endif
+
+int xradio_request_gpio_irq(struct device *dev, void *sbus_priv)
+{
+ int ret = -1;
+ if(!gpio_irq_handle) {
+
+#ifdef PLAT_ALLWINNER_SUNXI
+ gpio_request(wlan_irq_gpio, "xradio_irq");
+ gpio_direction_input(wlan_irq_gpio);
+ gpio_irq_handle = gpio_to_irq(wlan_irq_gpio);
+ ret = devm_request_irq(dev, gpio_irq_handle,
+ (irq_handler_t)xradio_gpio_irq_handler,
+ IRQF_TRIGGER_RISING, "xradio_irq", sbus_priv);
+ if (IS_ERR_VALUE(ret)) {
+ gpio_irq_handle = 0;
+ }
+#else//PLAT_ALLWINNER_SUN6I
+ gpio_irq_handle = sw_gpio_irq_request(wlan_irq_gpio, TRIG_EDGE_POSITIVE,
+ (peint_handle)xradio_gpio_irq_handler, sbus_priv);
+#endif // PLAT_ALLWINNER_SUNXI
+ } else {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: error, irq exist already!\n", __func__);
+ }
+
+ if (gpio_irq_handle) {
+ xradio_dbg(XRADIO_DBG_NIY, "%s: request_irq sucess! irq=0x%08x\n",
+ __func__, gpio_irq_handle);
+ ret = 0;
+ } else {
+ xradio_dbg(XRADIO_DBG_ERROR, "%s: request_irq err: %d\n", __func__, ret);
+ ret = -1;
+ }
+ return ret;
+}
+
+void xradio_free_gpio_irq(struct device *dev, void *sbus_priv)
+{
+ struct sbus_priv *self = (struct sbus_priv *)sbus_priv;
+#ifdef PLAT_ALLWINNER_SUNXI
+ if(gpio_irq_handle) {
+ //for linux3.4
+ devm_free_irq(dev, gpio_irq_handle, self);
+ gpio_free(wlan_irq_gpio);
+ gpio_irq_handle = 0;
+ }
+#else// PLAT_ALLWINNER_SUN6I
+ if(gpio_irq_handle) {
+ sw_gpio_irq_free(gpio_irq_handle);
+ gpio_irq_handle = 0;
+ }
+#endif//PLAT_ALLWINNER_SUNXI
+}
+#endif /* CONFIG_XRADIO_USE_GPIO_IRQ */
+
+/******************************************************************************************/
\ No newline at end of file
diff --git a/drivers/net/wireless/xradio/platform.h b/drivers/net/wireless/xradio/platform.h
new file mode 100644
index 0000000..71333a7
--- /dev/null
+++ b/drivers/net/wireless/xradio/platform.h
@@ -0,0 +1,49 @@
+/*
+ * platform interfaces for XRadio drivers
+ *
+ * Implemented by platform vendor(such as AllwinnerTech).
+ *
+ * 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_PLAT_H_INCLUDED
+#define XRADIO_PLAT_H_INCLUDED
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/mmc/host.h>
+
+/* Select hardware platform.*/
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0))
+#define PLAT_ALLWINNER_SUNXI
+#define MCI_RESCAN_CARD(id, ins) sunxi_mci_rescan_card(id, ins)
+#define MCI_CHECK_READY(h, t) sunxi_mci_check_r1_ready(h, t)
+
+extern void sunxi_mci_rescan_card(unsigned id, unsigned insert);
+extern int sunxi_mci_check_r1_ready(struct mmc_host* mmc, unsigned ms);
+
+#else
+#define PLAT_ALLWINNER_SUN6I
+#define MCI_RESCAN_CARD(id, ins) sw_mci_rescan_card(id, ins)
+#define MCI_CHECK_READY(h, t) sw_mci_check_r1_ready(h, t)
+
+extern void sw_mci_rescan_card(unsigned id, unsigned insert);
+extern int sw_mci_check_r1_ready(struct mmc_host* mmc, unsigned ms);
+#endif
+/* the rf_pm api */
+extern int wifi_pm_gpio_ctrl(char *name, int level);
+/* platform interfaces */
+int xradio_plat_init(void);
+void xradio_plat_deinit(void);
+int xradio_sdio_detect(int enable);
+int xradio_request_gpio_irq(struct device *dev, void *sbus_priv);
+void xradio_free_gpio_irq(struct device *dev, void *sbus_priv);
+int xradio_wlan_power(int on);
+
+#endif /* XRADIO_PLAT_H_INCLUDED */
diff --git a/drivers/net/wireless/xradio/pm.c b/drivers/net/wireless/xradio/pm.c
new file mode 100644
index 0000000..f3c74dd
--- /dev/null
+++ b/drivers/net/wireless/xradio/pm.c
@@ -0,0 +1,821 @@
+/*
+ * 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 "sbus.h"
+#include "platform.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)
+{
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ 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)
+{
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ 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)
+{
+ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__);
+ 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;
+ pm_printk(XRADIO_DBG_MSG, "%s\n", __func__);
+
+ 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)
+{
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ 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)
+{
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ 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;
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+#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;
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+
+ if(hw_priv->bh_error) return -EBUSY;
+ WARN_ON(!atomic_read(&hw_priv->num_vifs));
+
+#ifdef HW_RESTART
+ if (work_pending(&hw_priv->hw_restart_work))
+ return -EBUSY;
+#endif
+ 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 = hw_priv->sbus_ops->power_mgmt(hw_priv->sbus_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
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+
+ /* 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 = xr_kzalloc(sizeof(struct xradio_suspend_state), false);
+ 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;
+
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ 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 */
+ hw_priv->sbus_ops->power_mgmt(hw_priv->sbus_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
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+
+ /* 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)
+{
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ //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;
+ }
+
+ /* Going to sleep with wifi power down. */
+ xradio_wlan_power(0);
+ return 0;
+}
+
+static int xradio_poweroff_resume(struct xradio_common *hw_priv)
+{
+ pm_printk(XRADIO_DBG_NIY, "%s\n", __func__);
+ /* 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 0000000..ec77ff4
--- /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;
+ 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 0000000..366cced
--- /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 = xr_kmalloc(sizeof(struct xradio_queue_item), false);
+ 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] = xr_kzalloc(sizeof(int[map_capacity]), false);
+ 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 = xr_kzalloc(sizeof(struct xradio_queue_item) * capacity,
+ false);
+ if (!queue->pool)
+ return -ENOMEM;
+
+ for (i = 0; i < XRWL_MAX_VIFS; i++) {
+ queue->link_map_cache[i] =
+ xr_kzalloc(sizeof(int[stats->map_capacity]), false);
+ 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 0000000..db55c4c
--- /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/sbus.h b/drivers/net/wireless/xradio/sbus.h
new file mode 100644
index 0000000..67c1c6c
--- /dev/null
+++ b/drivers/net/wireless/xradio/sbus.h
@@ -0,0 +1,59 @@
+/*
+ * Sbus 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 __SBUS_H
+#define __SBUS_H
+
+#include <linux/version.h>
+#include <linux/module.h>
+/*
+ * sbus priv forward definition.
+ * Implemented and instantiated in particular modules.
+ */
+
+struct xradio_common;
+/*sdio bus private struct*/
+#define SDIO_UNLOAD 0
+#define SDIO_LOAD 1
+
+typedef void (*sbus_irq_handler)(void *priv);
+struct sbus_priv {
+ struct sdio_func *func;
+ spinlock_t lock;
+ sbus_irq_handler irq_handler;
+ void *irq_priv;
+ wait_queue_head_t init_wq;
+ int load_state;
+};
+
+struct sbus_ops {
+ int (*sbus_data_read)(struct sbus_priv *self, unsigned int addr,
+ void *dst, int count);
+ int (*sbus_data_write)(struct sbus_priv *self, unsigned int addr,
+ const void *src, int count);
+ void (*lock)(struct sbus_priv *self);
+ void (*unlock)(struct sbus_priv *self);
+ size_t (*align_size)(struct sbus_priv *self, size_t size);
+ int (*set_block_size)(struct sbus_priv *self, size_t size);
+ int (*irq_subscribe)(struct sbus_priv *self,
+ sbus_irq_handler handler, void *priv);
+ int (*irq_unsubscribe)(struct sbus_priv *self);
+ int (*power_mgmt)(struct sbus_priv *self, bool suspend);
+ int (*reset)(struct sbus_priv *self);
+};
+
+//sbus init functions
+struct device * sbus_sdio_init(struct sbus_ops **sdio_ops,
+ struct sbus_priv **sdio_priv);
+void sbus_sdio_deinit(void);
+
+#endif /* __SBUS_H */
diff --git a/drivers/net/wireless/xradio/scan.c b/drivers/net/wireless/xradio/scan.c
new file mode 100644
index 0000000..6ab5c78
--- /dev/null
+++ b/drivers/net/wireless/xradio/scan.c
@@ -0,0 +1,1144 @@
+/*
+ * 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;
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 cfg80211_scan_request *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_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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;
+ }
+
+#ifdef HW_RESTART
+ if (hw_priv->hw_restart) {
+ scan_printk(XRADIO_DBG_NIY, "Ignoring scan in hw reset!\n");
+ return -EBUSY;
+ }
+#endif
+
+ 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==IEEE80211_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;
+ }
+
+ frame.skb = ieee80211_probereq_get(hw, vif, NULL, 0, req->ie, req->ie_len);
+ 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 == IEEE80211_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_PASSIVE_SCAN) {
+ 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 == IEEE80211_BAND_5GHZ)
+ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \
+ XRADIO_SCAN_BAND_5G;
+ }
+ if (req->channels[0]->band == IEEE80211_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_TRC,"%s\n", __func__);
+
+ 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
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ ieee80211_scan_completed(hw_priv->hw, hw_priv->scan.status ? 1 : 0);
+ 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_PASSIVE_SCAN)
+ break;
+ if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
+ (*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_PASSIVE_SCAN) ? 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 = xr_kzalloc(sizeof(struct wsm_scan_ch[it - hw_priv->scan.curr]), false);
+ 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_PASSIVE_SCAN) {
+ 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_PASSIVE_SCAN) &&
+ 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;
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ scan_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 == IEEE80211_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 0000000..191b4b8
--- /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 cfg80211_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 0000000..e5a95b2
--- /dev/null
+++ b/drivers/net/wireless/xradio/sdio.c
@@ -0,0 +1,359 @@
+/*
+ * 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/version.h>
+#include <linux/module.h>
+#include <linux/wait.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+#include <linux/spinlock.h>
+#include <asm/mach-types.h>
+#include <net/mac80211.h>
+
+#include "platform.h"
+#include "xradio.h"
+#include "sbus.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 */
+static int sdio_data_read(struct sbus_priv *self, unsigned int addr,
+ void *dst, int count)
+{
+ return sdio_memcpy_fromio(self->func, dst, addr, count);
+}
+
+static int sdio_data_write(struct sbus_priv *self, unsigned int addr,
+ const void *src, int count)
+{
+ return sdio_memcpy_toio(self->func, addr, (void *)src, count);
+}
+
+static void sdio_lock(struct sbus_priv *self)
+{
+ sdio_claim_host(self->func);
+}
+
+static void sdio_unlock(struct sbus_priv *self)
+{
+ sdio_release_host(self->func);
+}
+
+static size_t sdio_align_len(struct sbus_priv *self, size_t size)
+{
+ return sdio_align_size(self->func, size);
+}
+
+static int sdio_set_blk_size(struct sbus_priv *self, size_t size)
+{
+ return sdio_set_block_size(self->func, size);
+}
+
+#ifndef CONFIG_XRADIO_USE_GPIO_IRQ
+static void sdio_irq_handler(struct sdio_func *func)
+{
+ struct sbus_priv *self = sdio_get_drvdata(func);
+ unsigned long flags;
+ sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__);
+
+ SYS_BUG(!self);
+ spin_lock_irqsave(&self->lock, flags);
+ if (self->irq_handler)
+ self->irq_handler(self->irq_priv);
+ spin_unlock_irqrestore(&self->lock, flags);
+}
+#endif
+
+static int sdio_irq_subscribe(struct sbus_priv *self,
+ sbus_irq_handler handler,
+ void *priv)
+{
+ int ret = 0;
+ unsigned long flags;
+
+
+ if (!handler)
+ return -EINVAL;
+ sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__);
+
+ spin_lock_irqsave(&self->lock, flags);
+ self->irq_priv = priv;
+ self->irq_handler = handler;
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ sdio_claim_host(self->func);
+#ifndef CONFIG_XRADIO_USE_GPIO_IRQ
+ ret = sdio_claim_irq(self->func, sdio_irq_handler);
+#else
+ ret = xradio_request_gpio_irq(&(self->func->dev), self);
+ if (!ret) {
+ /* Hack to access Fuction-0 */
+ u8 cccr;
+ int func_num = self->func->num;
+ self->func->num = 0;
+ cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret);
+ cccr |= BIT(0); /* Master interrupt enable ... */
+ cccr |= BIT(func_num); /* ... for our function */
+ sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret);
+ if (ret) {
+ xradio_free_gpio_irq(&(self->func->dev), self);
+ if (MCI_CHECK_READY(self->func->card->host, 1000) != 0)
+ sbus_printk(XRADIO_DBG_ERROR, "%s:MCI_CHECK_READY timeout\n", __func__);
+ }
+ /* Restore the WLAN function number */
+ self->func->num = func_num;
+ }
+#endif
+ sdio_release_host(self->func);
+
+ return ret;
+}
+
+static int sdio_irq_unsubscribe(struct sbus_priv *self)
+{
+ int ret = 0;
+ unsigned long flags;
+
+ sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__);
+
+ if (!self->irq_handler) {
+ sbus_printk(XRADIO_DBG_ERROR, "%s:irq_handler is NULL!\n", __FUNCTION__);
+ return 0;
+ }
+
+#ifndef CONFIG_XRADIO_USE_GPIO_IRQ
+ sdio_claim_host(self->func);
+ ret = sdio_release_irq(self->func);
+ sdio_release_host(self->func);
+#else
+ xradio_free_gpio_irq(&(self->func->dev), self);
+#endif //CONFIG_XRADIO_USE_GPIO_IRQ
+
+ spin_lock_irqsave(&self->lock, flags);
+ self->irq_priv = NULL;
+ self->irq_handler = NULL;
+ spin_unlock_irqrestore(&self->lock, flags);
+
+ return ret;
+}
+
+static int sdio_pm(struct sbus_priv *self, bool suspend)
+{
+ int ret = 0;
+ if (suspend) {
+ /* Notify SDIO that XRADIO will remain powered during suspend */
+ ret = sdio_set_host_pm_flags(self->func, MMC_PM_KEEP_POWER);
+ if (ret)
+ sbus_printk(XRADIO_DBG_ERROR,
+ "Error setting SDIO pm flags: %i\n", ret);
+ }
+
+ return ret;
+}
+
+static int sdio_reset(struct sbus_priv *self)
+{
+ return 0;
+}
+
+static struct sbus_ops sdio_sbus_ops = {
+ .sbus_data_read = sdio_data_read,
+ .sbus_data_write = sdio_data_write,
+ .lock = sdio_lock,
+ .unlock = sdio_unlock,
+ .align_size = sdio_align_len,
+ .set_block_size = sdio_set_blk_size,
+ .irq_subscribe = sdio_irq_subscribe,
+ .irq_unsubscribe = sdio_irq_unsubscribe,
+ .power_mgmt = sdio_pm,
+ .reset = sdio_reset,
+};
+static struct sbus_priv sdio_self;
+
+//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);
+ sbus_printk(XRADIO_DBG_ALWY, "%s:change mmc clk=%d\n", __func__,
+ func->card->host->ios.clock);
+ } else {
+ sbus_printk(XRADIO_DBG_ALWY, "%s:fail change mmc clk=%d\n", __func__, clk);
+ }
+ }
+ return 0;
+ sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__);
+}
+#endif
+
+/* 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)
+{
+ sbus_printk(XRADIO_DBG_ALWY, "XRadio Device:sdio clk=%d\n",
+ func->card->host->ios.clock);
+ sbus_printk(XRADIO_DBG_NIY, "sdio func->class=%x\n", func->class);
+ sbus_printk(XRADIO_DBG_NIY, "sdio_vendor: 0x%04x\n", func->vendor);
+ sbus_printk(XRADIO_DBG_NIY, "sdio_device: 0x%04x\n", func->device);
+ sbus_printk(XRADIO_DBG_NIY, "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
+
+ sdio_self.func = func;
+ sdio_self.func->card->quirks |= MMC_QUIRK_BROKEN_BYTE_MODE_512;
+ sdio_set_drvdata(func, &sdio_self);
+ sdio_claim_host(func);
+ sdio_enable_func(func);
+ sdio_release_host(func);
+
+ sdio_self.load_state = SDIO_LOAD;
+ wake_up(&sdio_self.init_wq);
+
+ return 0;
+}
+/* Disconnect Function to be called by SDIO stack when
+ * device is disconnected */
+static void sdio_remove(struct sdio_func *func)
+{
+ struct sbus_priv *self = sdio_get_drvdata(func);
+ sdio_claim_host(func);
+ sdio_disable_func(func);
+ sdio_release_host(func);
+ sdio_set_drvdata(func, NULL);
+ if (self) {
+ self->func = NULL;
+ }
+}
+
+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 = {
+ .pm = &sdio_pm_ops,
+ }
+};
+
+/* Init Module function -> Called by insmod */
+struct device * sbus_sdio_init(struct sbus_ops **sdio_ops,
+ struct sbus_priv **sdio_priv)
+{
+ int ret = 0;
+ struct device * sdio_dev = NULL;
+ sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__);
+
+ //initialize sbus_priv.
+ if (sdio_self.load_state == SDIO_UNLOAD) {
+ spin_lock_init(&sdio_self.lock);
+ init_waitqueue_head(&sdio_self.init_wq);
+
+ //setup sdio driver.
+ ret = sdio_register_driver(&sdio_driver);
+ if (ret) {
+ sbus_printk(XRADIO_DBG_ERROR,"sdio_register_driver failed!\n");
+ return NULL;
+ }
+
+ //module power up.
+ xradio_wlan_power(1);
+ //detect sdio card.
+ xradio_sdio_detect(1);
+ if (wait_event_interruptible_timeout(sdio_self.init_wq,
+ sdio_self.load_state == SDIO_LOAD, 2*HZ) <= 0) {
+ sdio_unregister_driver(&sdio_driver);
+ sdio_self.load_state = SDIO_UNLOAD;
+
+ xradio_wlan_power(0); //power down.
+ xradio_sdio_detect(0);
+ sbus_printk(XRADIO_DBG_ERROR,"sdio probe timeout!\n");
+ return NULL;
+ }
+ }
+
+ //register sbus.
+ sdio_dev = &(sdio_self.func->dev);
+ *sdio_ops = &sdio_sbus_ops;
+ *sdio_priv = &sdio_self;
+
+ return sdio_dev;
+}
+
+/* SDIO Driver Unloading */
+void sbus_sdio_deinit()
+{
+ sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__);
+ if (sdio_self.load_state != SDIO_UNLOAD) {
+ sdio_unregister_driver(&sdio_driver);
+ memset(&sdio_self, 0, sizeof(sdio_self));
+ sdio_self.load_state = SDIO_UNLOAD;
+
+ xradio_wlan_power(0); //power down.
+ xradio_sdio_detect(0);
+ mdelay(10);
+ }
+}
diff --git a/drivers/net/wireless/xradio/sta.c b/drivers/net/wireless/xradio/sta.c
new file mode 100644
index 0000000..8c86564
--- /dev/null
+++ b/drivers/net/wireless/xradio/sta.c
@@ -0,0 +1,3442 @@
+/*
+ * 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 "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*/
+
+#ifdef IPV6_FILTERING
+#define MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE 144
+#endif /*IPV6_FILTERING*/
+
+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_INFORMATION;
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
+ hw_priv->driver_ready, 3*HZ) <= 0) {
+ sta_printk(XRADIO_DBG_ERROR,"%s driver is not ready!\n", __func__);
+ 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)) {
+ sta_printk(XRADIO_DBG_ERROR,"%s, xradio_setup_mac failed(%d)\n",
+ __func__, 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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)
+ sta_printk(XRADIO_DBG_WARN, "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) {
+ sta_printk(XRADIO_DBG_ERROR,"%s driver is not ready!\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ /* fix the problem that when connected,then deauth */
+ vif->driver_flags = vif->driver_flags | IEEE80211_VIF_BEACON_FILTER;
+ 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
+ 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 = 2;
+ } else if (!memcmp(vif->addr, hw_priv->addresses[2].addr,
+ ETH_ALEN)) {
+ priv->if_id = 1;
+ }
+ sta_printk(XRADIO_DBG_MSG, "%s: if_id %d mac %pM\n",
+ __func__, 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)));
+ */
+ sta_printk(XRADIO_DBG_MSG, "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,
+ };
+ sta_printk(XRADIO_DBG_WARN, "!!! %s: vif_id=%d\n", __func__, 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);
+ sta_printk(XRADIO_DBG_ERROR, "%s:delayed_unjoin exist!\n", __func__);
+ }
+ 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;
+ sta_printk(XRADIO_DBG_WARN, "%s: new type=%d(%d), p2p=%d(%d)\n",
+ __func__, 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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! */
+ sta_printk(XRADIO_DBG_NIY, "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 == IEEE80211_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 */
+
+ sta_printk(XRADIO_DBG_NIY, "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->channel)) {
+ /* Switch Channel commented for CC Mode */
+ struct ieee80211_channel *ch = conf->channel;
+ sta_printk(XRADIO_DBG_WARN, "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_INFORMATION,
+ .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,
+ };
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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)
+ sta_printk(XRADIO_DBG_ERROR, "%s: Update filtering failed: %d.\n",
+ __func__, 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+#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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ /* delete umac warning */
+ if (hw_priv->vif_list[0] == NULL && hw_priv->vif_list[1] == NULL &&
+ hw_priv->vif_list[2] == NULL)
+ *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_PROMISC_IN_BSS |
+ 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 = (*total_flags & FIF_PROMISC_IN_BSS)? 1 : 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_PROMISC_IN_BSS |
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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) {
+ sta_printk(XRADIO_DBG_ERROR,"%s:wsm_set_tx_queue_params failed!\n", __func__);
+ 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) {
+ sta_printk(XRADIO_DBG_ERROR,"%s:wsm_set_edca_params failed!\n", __func__);
+ 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 {
+ sta_printk(XRADIO_DBG_ERROR,"%s:queue is to large!\n", __func__);
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+#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;
+ }
+}
+
+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)
+{
+ int ret = -EOPNOTSUPP;
+ struct xradio_common *hw_priv = dev->priv;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+#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) {
+ sta_printk(XRADIO_DBG_ERROR,"%s:xradio_alloc_key failed!\n",
+ __func__);
+ 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);
+ sta_printk(XRADIO_DBG_ERROR,"%s: keylen too long=%d!\n",
+ __func__, 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;
+ sta_printk(XRADIO_DBG_NIY,"%s: WEP_PAIRWISE keylen=%d!\n",
+ __func__, 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;
+ sta_printk(XRADIO_DBG_NIY,"%s: WEP_GROUP keylen=%d!\n",
+ __func__, 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);
+ sta_printk(XRADIO_DBG_NIY,"%s: TKIP_PAIRWISE keylen=%d!\n",
+ __func__, 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;
+ sta_printk(XRADIO_DBG_NIY,"%s: TKIP_GROUP keylen=%d!\n",
+ __func__, 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);
+ sta_printk(XRADIO_DBG_NIY,"%s: CCMP_PAIRWISE keylen=%d!\n",
+ __func__, 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;
+ sta_printk(XRADIO_DBG_NIY,"%s: CCMP_GROUP keylen=%d!\n",
+ __func__, 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:
+ sta_printk(XRADIO_DBG_ERROR,"%s: key->cipher unknown(%d)!\n",
+ __func__, 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);
+#ifdef IPV6_FILTERING
+ if (!ret && (pairwise || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) &&
+ (priv->filter6.enable & 0x2))
+ xradio_set_na(dev, vif);
+#endif /*IPV6_FILTERING*/
+
+ } 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 {
+ sta_printk(XRADIO_DBG_ERROR, "%s: Unsupported command", __func__);
+ }
+
+finally:
+ mutex_unlock(&hw_priv->conf_mutex);
+ return ret;
+}
+
+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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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, 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ 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_channel *chan,
+ enum nl80211_channel_type channel_type,
+ int duration)
+{
+ 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
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+#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_TRC,"%s\n", __func__);
+
+ 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 = xr_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 */
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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, IEEE80211_BAND_2GHZ);
+ /* Max/Min Power Calculation for 5G */
+ xradio_device_power_calc(hw_priv, max_output_power_5G, fe_cor_5G, IEEE80211_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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 */
+#ifdef USE_VFS_FIRMWARE
+ xr_fileclose(hw_priv->sdd);
+#else
+ release_firmware(hw_priv->sdd);
+#endif
+ 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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];
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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,
+ //};
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+
+ 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->information_elements,
+ bss->len_information_elements);
+ dtimie = cfg80211_find_ie(WLAN_EID_TIM,
+ bss->information_elements,
+ bss->len_information_elements);
+ 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 == IEEE80211_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(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,
+ };
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ //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 == IEEE80211_BAND_5GHZ) ?
+ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
+ .channelNumber = chan->hw_value,
+ .beaconInterval = 100,
+ .DTIMPeriod = 1,
+ .probeDelay = 0,
+ .basicRateSet = 0x0F,
+ };
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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,
+ };
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ //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_info_update_work, xradio_ht_info_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,
+ };
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+#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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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 = xr_kzalloc(macaddrfiltersize, false);
+ 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;
+}
+
+#if 0
+/**
+ * xradio_set_multicastaddrfilter -called when tesmode command
+ * is for setting the ipv4 address filter
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_set_multicastfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data)
+{
+ u8 i = 0;
+ int ret = 0;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ memset(&priv->multicast_filter, 0, sizeof(priv->multicast_filter));
+ priv->multicast_filter.enable = (u32)data[0];
+ priv->multicast_filter.numOfAddresses = (u32)data[1];
+
+ for (i = 0; i < priv->multicast_filter.numOfAddresses; i++) {
+ memcpy(&priv->multicast_filter.macAddress[i], \
+ &data[2+(i*ETH_ALEN)], ETH_ALEN);
+ }
+ /* Configure the multicast mib in case of drop all multicast */
+ if (priv->multicast_filter.enable != 2)
+ return ret;
+
+ ret = wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, \
+ &priv->multicast_filter, sizeof(priv->multicast_filter), priv->if_id);
+
+ return ret;
+}
+#endif
+
+#ifdef IPV6_FILTERING
+/**
+ * xradio_set_ipv6addrfilter -called when tesmode command
+ * is for setting the ipv6 address filter
+ *
+ * @hw: the hardware
+ * @data: incoming data
+ * @if_id: interface id
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+static int xradio_set_ipv6addrfilter(struct ieee80211_hw *hw,
+ u8 *data, int if_id)
+{
+ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv;
+ struct wsm_ipv6_filter *ipv6_filter = NULL;
+ struct ipv6_addr_info *ipv6_info = NULL;
+ u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0;
+ u16 ipaddrfiltersize = 0;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* Retrieving Action Mode */
+ action_mode = data[0];
+ /* Retrieving number of ipv4 address entries */
+ no_of_ip_addr = data[1];
+
+ ipv6_info = (struct ipv6_addr_info *)&data[2];
+
+ /* Computing sizeof Mac addr filter */
+ ipaddrfiltersize = sizeof(*ipv6_filter) + \
+ (no_of_ip_addr * sizeof(struct wsm_ip6_addr_info));
+
+
+ ipv6_filter = xr_kzalloc(ipaddrfiltersize, false);
+ if (!ipv6_filter) {
+ ret = -ENOMEM;
+ goto exit_p;
+ }
+ ipv6_filter->action_mode = action_mode;
+ ipv6_filter->numfilter = no_of_ip_addr;
+
+ for (i = 0; i < no_of_ip_addr; i++) {
+ ipv6_filter->ipv6filter[i].address_mode = \
+ ipv6_info[i].address_mode;
+ ipv6_filter->ipv6filter[i].filter_mode = \
+ ipv6_info[i].filter_mode;
+ memcpy(ipv6_filter->ipv6filter[i].ipv6, \
+ (u8 *)(ipv6_info[i].ipv6), 16);
+ }
+
+ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_IP_IPV6_ADDR_FILTER, \
+ ipv6_filter, ipaddrfiltersize, \
+ if_id));
+
+ kfree(ipv6_filter);
+exit_p:
+ return ret;
+}
+#endif /*IPV6_FILTERING*/
+
+/**
+ * xradio_set_data_filter -configure data filter in device
+*
+ * @hw: the hardware
+ * @vif: vif
+ * @data: incoming data
+ * @len: incoming data length
+ *
+ */
+void xradio_set_data_filter(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ void *data, int len)
+{
+ int ret = 0;
+ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif);
+ int filter_id;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ if (!data) {
+ ret = -EINVAL;
+ goto exit_p;
+ }
+ filter_id=*((enum xradio_data_filterid*)data);
+
+ switch (filter_id) {
+#ifdef IPV6_FILTERING
+ case IPV6ADDR_FILTER_ID:
+ ret = xradio_set_ipv6addrfilter(hw, \
+ &((u8 *)data)[4], priv->if_id);
+ break;
+#endif /*IPV6_FILTERING*/
+ default:
+ ret = -EINVAL;
+ break;
+ }
+exit_p:
+
+ return ;
+}
+
+/**
+ * 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ template_frame = xr_kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, false);
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 IPV6_FILTERING
+/**
+ * xradio_set_na -called for creating and
+ * configuring NDP Neighbor Advertisement (NA) template frame
+ *
+ * @hw: the hardware
+ * @vif: vif
+ *
+ * Returns: 0 on success or non zero value on failure
+ */
+int xradio_set_na(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 ipv6hdr *ipv6_hdr = NULL;
+ struct icmp6hdr *icmp6_hdr = NULL;
+ struct nd_msg *na = NULL;
+ struct nd_opt_hdr *opt_hdr = NULL;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ template_frame = xr_kzalloc(MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE, false);
+ 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_MSG, "QOS Enabled\n");
+ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
+ /* Filling QOS Control Field */
+ *(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_IPV6);
+ /* Updating the framebdylen with snaphdr and LLC hdr size */
+ framebdylen = sizeof(*snaphdr) + 2;
+
+ /* Filling the ipv6 header */
+ ipv6_hdr = (struct ipv6hdr *)((u8 *)dot11hdr + framehdrlen + framebdylen);
+ ipv6_hdr->version = 6;
+ ipv6_hdr->priority = 0;
+ ipv6_hdr->payload_len = cpu_to_be16(32); /* ??? check the be or le ??? whether to use cpu_to_be16(32)*/
+ ipv6_hdr->nexthdr = 58;
+ ipv6_hdr->hop_limit = 255;
+
+ /* Updating the framebdylen with ipv6 Hdr */
+ framebdylen += sizeof(*ipv6_hdr);
+
+ /* Filling the Neighbor Advertisement */
+ na = (struct nd_msg *)((u8 *)dot11hdr + framehdrlen + framebdylen);
+ icmp6_hdr = (struct icmp6hdr *)(&na->icmph);
+ icmp6_hdr->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
+ icmp6_hdr->icmp6_code = 0;
+ /* checksum (2 bytes), RSO fields (4 bytes) and target IP address (16 bytes) shall be filled by firmware */
+
+ /* Filling the target link layer address in the optional field */
+ opt_hdr = (struct nd_opt_hdr *)(&na->opt[0]);
+ opt_hdr->nd_opt_type = 2;
+ opt_hdr->nd_opt_len = 1;
+ /* optional target link layer address (6 bytes) shall be filled by firmware */
+
+ /* Updating the framebdylen with the ipv6 payload length */
+ framebdylen += 32;
+
+ /* Updating the framebdylen with Encryption Tail Size */
+ framebdylen += encrypttailsize;
+
+ /* Filling the Template Frame Hdr */
+ template_frame[0] = WSM_FRAME_TYPE_NA; /* 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;
+}
+#endif /*IPV6_FILTERING*/
+
+#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];
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /*
+ * 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];
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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];
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /* 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ sta_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 0000000..8e48923
--- /dev/null
+++ b/drivers/net/wireless/xradio/sta.h
@@ -0,0 +1,137 @@
+/*
+ * 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_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
+ struct ieee80211_vif *vif, struct ieee80211_sta *sta,
+ struct ieee80211_key_conf *key);
+int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value);
+
+void xradio_flush(struct ieee80211_hw *hw, bool drop);
+
+
+int xradio_remain_on_channel(struct ieee80211_hw *hw,
+ struct ieee80211_channel *chan,
+ enum nl80211_channel_type channel_type,
+ int duration);
+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 IPV6_FILTERING
+int xradio_set_na(struct ieee80211_hw *hw, struct ieee80211_vif *vif);
+#endif /*IPV6_FILTERING*/
+#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 0000000..a89a829
--- /dev/null
+++ b/drivers/net/wireless/xradio/txrx.c
@@ -0,0 +1,2470 @@
+/*
+ * 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 "sbus.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)
+{
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ 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]);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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)
+{
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ 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_INFORMATION);
+ 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_TRC,"%s\n", __func__);
+
+ 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 == IEEE80211_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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 xradio_txinfo *t)
+{
+#ifndef P2P_MULTIVIF
+ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv);
+#endif
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+#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(t->tx_info->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 (t->tx_info->control.sta &&
+ (t->tx_info->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)
+{
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ /*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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_STA)
+ return;
+ if (!xradio_is_ht(&hw_priv->ht_info))
+ 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 = priv->sbus_ops->align_size(priv->sbus_priv, len);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 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);
+
+#ifdef HW_RESTART
+ if (hw_priv->hw_restart) {
+ txrx_printk(XRADIO_DBG_WARN, "%s, hw in reset.\n", __func__);
+ ret = __LINE__;
+ goto drop;
+ }
+#endif
+
+ 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 8021 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 *)&t.tx_info->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, &t);
+ if (ret) {
+ ret = __LINE__;
+ goto drop;
+ }
+
+ txrx_printk(XRADIO_DBG_MSG, "[TX] TX %d bytes (if_id: %d,"
+ " queue: %d, link_id: %d (%d)).\n",
+ skb->len, priv->if_id, t.queue, t.txpriv.link_id,
+ t.txpriv.raw_link_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(t.tx_info->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:
+ txrx_printk(XRADIO_DBG_WARN, "drop=%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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+
+ if(arg->status) {
+ txrx_printk(XRADIO_DBG_WARN,"status=%d, retry=%d, lastRate=%d\n",
+ arg->status, arg->ackFailures, arg->txedRate);
+ } else {
+ txrx_printk(XRADIO_DBG_MSG,"status=%d, retry=%d, lastRate=%d\n",
+ arg->status, arg->ackFailures, arg->txedRate);
+ }
+
+#ifdef TES_P2P_0002_ROC_RESTART
+ if( (TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) &&
+ (arg->packetID == TES_P2P_0002_packet_id) ) {
+ if (arg->status == 0x00) {
+
+ struct timeval TES_P2P_0002_tmval;
+ s32 TES_P2P_0002_roc_time;
+ s32 TES_P2P_0002_now_sec;
+ s32 TES_P2P_0002_now_usec;
+ bool TES_P2P_0002_roc_rst_need;
+
+ do_gettimeofday(&TES_P2P_0002_tmval);
+ TES_P2P_0002_roc_rst_need = false;
+ TES_P2P_0002_now_sec = (s32)(TES_P2P_0002_tmval.tv_sec);
+ TES_P2P_0002_now_usec = (s32)(TES_P2P_0002_tmval.tv_usec);
+ TES_P2P_0002_roc_time = TES_P2P_0002_roc_dur -
+ (((TES_P2P_0002_now_sec - TES_P2P_0002_roc_sec) * 1000) +
+ (( TES_P2P_0002_now_usec - TES_P2P_0002_roc_usec ) / 1000) );
+
+ ///w, tx rsp to rx cfm will need more than 60ms
+ if( TES_P2P_0002_roc_time < 100 ) {
+ TES_P2P_0002_roc_time = 100;
+ TES_P2P_0002_roc_rst_need = true;
+ }
+
+ if( TES_P2P_0002_roc_rst_need == true ) {
+ txrx_printk(XRADIO_DBG_WARN, "[ROC RESTART ACTIVE ON][Confirm CallBack]");
+ cancel_delayed_work_sync(&hw_priv->rem_chan_timeout);
+ if(atomic_read(&hw_priv->remain_on_channel)) {
+ queue_delayed_work(hw_priv->workqueue, &hw_priv->rem_chan_timeout,
+ (TES_P2P_0002_roc_time) * HZ / 1000);
+ }
+ }
+ }
+ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE;
+ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE][Confirm CallBack]");
+ }
+#endif
+
+ if (unlikely(xradio_itp_tx_running(hw_priv)))
+ return;
+
+ 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;
+ }
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ spin_lock_bh(&hw_priv->tsm_lock);
+ if ((arg->status == WSM_STATUS_RETRY_EXCEEDED) ||
+ (arg->status == WSM_STATUS_TX_LIFETIME_EXCEEDED)) {
+ hw_priv->tsm_stats.msdu_discarded_count++;
+ } else if ((hw_priv->start_stop_tsm.start) &&
+ (arg->status == WSM_STATUS_SUCCESS)) {
+ if (queue_id == hw_priv->tsm_info.ac) {
+ struct timeval tmval;
+ do_gettimeofday(&tmval);
+ u16 pkt_delay =
+ hw_priv->start_stop_tsm.packetization_delay;
+ if (hw_priv->tsm_info.sta_roamed &&
+ !hw_priv->tsm_info.use_rx_roaming) {
+ hw_priv->tsm_info.roam_delay = tmval.tv_usec -
+ hw_priv->tsm_info.txconf_timestamp_vo;
+ if (hw_priv->tsm_info.roam_delay > pkt_delay)
+ hw_priv->tsm_info.roam_delay -= pkt_delay;
+ txrx_printk(XRADIO_DBG_MSG, "[TX] txConf"
+ "Roaming: roam_delay = %u\n",
+ hw_priv->tsm_info.roam_delay);
+ hw_priv->tsm_info.sta_roamed = 0;
+ }
+ hw_priv->tsm_info.txconf_timestamp_vo = tmval.tv_usec;
+ }
+ }
+ spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ 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);
+#ifdef CONFIG_XRADIO_TESTMODE
+ SYS_WARN(xradio_queue_requeue(hw_priv, queue,
+ arg->packetID, true));
+#else
+ SYS_WARN(xradio_queue_requeue(queue,
+ arg->packetID, true));
+#endif
+ 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);
+ }
+ }
+
+#ifdef ROC_DEBUG
+#ifndef P2P_MULTIVIF
+ if (txpriv->offchannel_if_id)
+ txrx_printk(XRADIO_DBG_ERROR, "TX CONFIRM %x - %d - %d\n",
+ skb->data[txpriv->offset],
+ txpriv->offchannel_if_id, arg->status);
+#else
+ if (txpriv->if_id)
+ txrx_printk(XRADIO_DBG_ERROR, "TX CONFIRM %x - %d - %d\n",
+ skb->data[txpriv->offset],
+ txpriv->raw_if_id, arg->status);
+#endif
+#endif
+ 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;
+
+#if 0
+ tx_count = arg->ackFailures+1;
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) {
+ if (tx->status.rates[i].count >= tx_count) {
+ tx->status.rates[i].count = tx_count;
+ if(likely(!arg->status)) {
+ s8 txed_idx = xradio_get_rate_idx(hw_priv, tx->status.rates[i].flags,
+ arg->txedRate);
+ //if(txed_idx != tx->status.rates[i].idx) {
+ // tx_policy_err++;
+ //}
+ //tx->status.rates[i].idx = (txed_idx >=0)?
+ // txed_idx:tx->status.rates[i].idx;
+ if(tx->status.rates[i].idx != txed_idx) {
+ if(i<(IEEE80211_TX_MAX_RATES-1)) {
+ i++;
+ tx->status.rates[i].idx = txed_idx;
+ tx->status.rates[i].count = 1;
+ } else if(txed_idx >=0) {
+ tx->status.rates[i].idx = txed_idx;
+ tx->status.rates[i].count = 1;
+ }
+ }
+ }
+ break;
+ }
+ tx_count -= tx->status.rates[i].count;
+ if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS)
+ tx->status.rates[i].flags |= ht_flags;
+ }
+
+ for (++i; i < IEEE80211_TX_MAX_RATES; ++i) {
+ tx->status.rates[i].count = 0;
+ tx->status.rates[i].idx = -1;
+ }
+
+#else
+ 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;
+ tx->status.rates[4].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;
+ }
+ }
+ }
+ }
+#endif
+
+#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
+
+ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Ack: " \
+ "%d:%d, %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,
+ tx->status.rates[4].idx, tx->status.rates[4].count);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ xradio_queue_remove(hw_priv, queue, arg->packetID);
+#else
+ xradio_queue_remove(queue, arg->packetID);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ }
+}
+
+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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ }
+ if (likely(!xradio_is_itp(hw_priv)))
+ 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)
+{
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ if (priv->join_status != XRADIO_JOIN_STATUS_STA)
+ return;
+ if (!xradio_is_ht(&hw_priv->ht_info))
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ spin_lock_bh(&hw_priv->tsm_lock);
+ if (hw_priv->start_stop_tsm.start) {
+ unsigned queue_id = skb_get_queue_mapping(skb);
+ if (queue_id == 0) {
+ struct timeval tmval;
+ do_gettimeofday(&tmval);
+ if (hw_priv->tsm_info.sta_roamed &&
+ hw_priv->tsm_info.use_rx_roaming) {
+ hw_priv->tsm_info.roam_delay = tmval.tv_usec -
+ hw_priv->tsm_info.rx_timestamp_vo;
+ txrx_printk(XRADIO_DBG_NIY, "[RX] RxInd Roaming:"
+ "roam_delay = %u\n", hw_priv->tsm_info.roam_delay);
+ hw_priv->tsm_info.sta_roamed = 0;
+ }
+ hw_priv->tsm_info.rx_timestamp_vo = tmval.tv_usec;
+ }
+ }
+ spin_unlock_bh(&hw_priv->tsm_lock);
+#endif /*CONFIG_XRADIO_TESTMODE*/
+ 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) {
+ txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, MIC failure.\n",
+ priv->if_id);
+ hdr->flag |= RX_FLAG_MMIC_ERROR;
+ } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) {
+ txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, No key found.\n",
+ priv->if_id);
+ goto drop;
+ } else {
+ txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, Receive failure: %d.\n",
+ priv->if_id, arg->status);
+ goto drop;
+ }
+ }
+
+ if (skb->len < sizeof(struct ieee80211_pspoll)) {
+ txrx_printk(XRADIO_DBG_WARN, "Mailformed 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) ?
+ IEEE80211_BAND_5GHZ : IEEE80211_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 == IEEE80211_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 defineas 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) {
+ txrx_printk(XRADIO_DBG_WARN, "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 0
+ /*huanglu, for nettest*/
+ if (ieee80211_is_probe_resp(frame->frame_control) &&
+ !arg->status &&
+ !memcmp(ieee80211_get_SA(frame), nettest_bssid, ETH_ALEN)) {
+ const u8 *supp_rate_ie;
+ u8 *ies = ((struct ieee80211_mgmt *)
+ (skb->data))->u.probe_resp.variable;
+ size_t ies_len = skb->len - (ies - (u8 *)(skb->data));
+
+ supp_rate_ie = xradio_get_ie(ies, ies_len, WLAN_EID_SUPP_RATES);
+ save_rate_ie=supp_rate_ie[2];
+ txrx_printk(XRADIO_DBG_WARN, "[netest]: save_rate_ie=%2x\n",
+ save_rate_ie);
+ }
+
+ if (ieee80211_is_assoc_resp(frame->frame_control) &&
+ !arg->status &&
+ !memcmp(ieee80211_get_SA(frame), nettest_bssid, ETH_ALEN)) {
+ u8 *supp_rate_ie2;
+ size_t ies_len;
+ u8 *ies = ((struct ieee80211_mgmt *)
+ (skb->data))->u.assoc_resp.variable;
+ ies_len = skb->len - (ies - (u8 *)(skb->data));
+ supp_rate_ie2 = xradio_get_ie(ies, ies_len, WLAN_EID_SUPP_RATES);
+
+ if ((supp_rate_ie2[1]==1) && (supp_rate_ie2[2]==0x80)) {
+ supp_rate_ie2[2] = save_rate_ie;
+ txrx_printk(XRADIO_DBG_WARN, "[netest]: rate_ie modified=%2x\n", supp_rate_ie2[2]);
+ }
+ }
+ /*huanglu, for test*/
+#endif
+
+ 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_info&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_info |= 0x0011;
+ queue_work(hw_priv->workqueue, &priv->ht_info_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);
+
+#ifdef CONFIG_XRADIO_TESTMODE
+ if (hw_priv->test_frame.len > 0 &&
+ priv->mode == NL80211_IFTYPE_STATION) {
+ if (xradio_frame_test_detection(priv, frame, skb) == 1) {
+ consume_skb(skb);
+ *skb_p = NULL;
+ return;
+ }
+ }
+#endif /* CONFIG_XRADIO_TESTMODE */
+
+#if (defined(CONFIG_XRADIO_DEBUG))
+ //parsse frame here for debug.
+ if(rxparse_flags)
+ xradio_parse_frame(skb->data, parse_iv_len, rxparse_flags|PF_RX, priv->if_id);
+#endif
+
+ 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(xradio_itp_rxed(hw_priv, skb)))
+ consume_skb(skb);
+ else 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);
+ txrx_printk(XRADIO_DBG_WARN, "***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:
+ /* TODO: update failure counters */
+ return;
+}
+
+/* ******************************************************************** */
+/* Security */
+
+int xradio_alloc_key(struct xradio_common *hw_priv)
+{
+ int idx;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_NIY,"%s, idx=%d\n", __func__, idx);
+ return idx;
+}
+
+void xradio_free_key(struct xradio_common *hw_priv, int idx)
+{
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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);
+ txrx_printk(XRADIO_DBG_NIY,"%s, idx=%d\n", __func__, idx);
+}
+
+void xradio_free_keys(struct xradio_common *hw_priv)
+{
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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;
+}
+#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;
+ txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__);
+
+ 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 0000000..7bec1fc
--- /dev/null
+++ b/drivers/net/wireless/xradio/txrx.h
@@ -0,0 +1,96 @@
+/*
+ * 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 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);
+
+/* ******************************************************************** */
+/* Security */
+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);
+
+/* ******************************************************************** */
+/* 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 0000000..1acc259
--- /dev/null
+++ b/drivers/net/wireless/xradio/wsm.c
@@ -0,0 +1,3274 @@
+/*
+ * 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"
+#include "itp.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;
+ wsm_printk(XRADIO_DBG_ERROR, "%s\n", __func__);
+#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};
+ wsm_printk(XRADIO_DBG_ERROR, "%s\n", __func__);
+
+ *(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;
+ 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);
+#ifdef HW_RESTART
+ else if (hw_priv->hw_restart)
+ wsm_oper_unlock(hw_priv);
+#endif
+ 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);
+#ifdef HW_RESTART
+ else if (hw_priv->hw_restart)
+ wsm_oper_unlock(hw_priv);
+#endif
+ 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 = xr_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 = xr_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 = xr_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 = xr_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;
+ if (hw_priv->wsm_cbc.rx) {
+ 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) {
+ wsm_printk(XRADIO_DBG_ERROR, "wsm_receive_indication: NULL priv drop frame\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 */
+ wsm_printk(XRADIO_DBG_WARN, \
+ "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);
+ }
+ }
+ hw_priv->wsm_cbc.rx(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)) {
+ wsm_printk(XRADIO_DBG_WARN, "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 = xr_kzalloc(sizeof(struct xradio_wsm_event), false);
+ if (event == NULL) {
+ wsm_printk(XRADIO_DBG_ERROR, "%s:xr_kzalloc failed!", __func__);
+ 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;
+
+ wsm_printk(XRADIO_DBG_MSG, "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);
+
+#ifdef HW_RESTART
+ if (hw_priv->hw_restart) {
+ wsm_printk(XRADIO_DBG_NIY, "hw reset!>>> 0x%.4X (%d)\n", cmd, buf_len);
+ wsm_buf_reset(buf);
+ return 0; /*return success, don't process cmd in power off.*/
+ }
+#endif
+
+ 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);
+
+ wsm_printk(XRADIO_DBG_ERROR,
+ "***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
+
+#ifdef CONFIG_XRADIO_DEBUG
+ static const char * const reason_str[] = {
+ "undefined instruction",
+ "prefetch abort",
+ "data abort",
+ "unknown error",
+ };
+#endif
+
+#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) {
+ wsm_printk(XRADIO_DBG_ERROR, "Firmware exception: %s.\n",
+ reason_str[reason]);
+ } else {
+ wsm_printk(XRADIO_DBG_ERROR, "Firmware assert at %.*s, line %d, reason=0x%x\n",
+ sizeof(fname), fname, reg[1], reg[2]);
+ }
+
+ for (i = 0; i < 12; i += 4) {
+ wsm_printk(XRADIO_DBG_ERROR, "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]);
+ }
+ wsm_printk(XRADIO_DBG_ERROR, "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;
+ wsm_printk(XRADIO_DBG_ERROR, "Firmware:CPSR: 0x%.8X, SPSR: 0x%.8X\n",
+ reg[i + 0], reg[i + 1]);
+
+ return 0;
+
+underflow:
+ wiphy_err(hw_priv->hw->wiphy, "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) *
+ (random32() & 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;
+
+ count = xradio_itp_get_tx(hw_priv, data, tx_len, burst);
+ if (count)
+ return count;
+
+ 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 = xr_kmalloc(size, true);
+ 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 = xr_krealloc(buf->begin, size, true);
+ 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 0000000..e2015c7
--- /dev/null
+++ b/drivers/net/wireless/xradio/wsm.h
@@ -0,0 +1,2441 @@
+/*
+ * 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
+
+#ifdef IPV6_FILTERING
+/* IpV6 Addr Filter */
+/* 4.52 Neighbor solicitation IPv6 address table */
+#define WSM_MIB_IP_IPV6_ADDR_FILTER 0x1032
+#define WSM_MIB_ID_NS_IP_ADDRESSES_TABLE 0x1034
+#define WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES 1
+#endif /*IPV6_FILTERING*/
+
+/* 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)
+
+#ifdef IPV6_FILTERING
+#define WSM_FRAME_TYPE_NA (7)
+#endif /*IPV6_FILTERING*/
+
+#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_rx_cb rx;
+ 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;
+
+#ifdef IPV6_FILTERING
+/* NDP IPv6 filtering */
+struct wsm_ndp_ipv6_filter {
+ __le32 enable;
+ struct in6_addr ipv6Address[WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES];
+} __packed;
+/* IPV6 Addr Filter Info */
+struct wsm_ip6_addr_info {
+ u8 filter_mode;
+ u8 address_mode;
+ u8 Reserved[2];
+ u8 ipv6[16];
+};
+
+/* IPV6 Addr Filter */
+struct wsm_ipv6_filter {
+ u8 numfilter;
+ u8 action_mode;
+ u8 Reserved[2];
+ struct wsm_ip6_addr_info ipv6filter[0];
+} __packed;
+#endif /*IPV6_FILTERING*/
+
+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);
+}
+
+#ifdef IPV6_FILTERING
+static inline int wsm_set_ndp_ipv6_filter(struct xradio_common *priv,
+ struct wsm_ndp_ipv6_filter *fp,
+ int if_id)
+{
+ return wsm_write_mib(priv, WSM_MIB_ID_NS_IP_ADDRESSES_TABLE,
+ fp, sizeof(*fp), if_id);
+}
+#endif /*IPV6_FILTERING*/
+
+/* 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/xr_version.h b/drivers/net/wireless/xradio/xr_version.h
new file mode 100644
index 0000000..9e99992
--- /dev/null
+++ b/drivers/net/wireless/xradio/xr_version.h
@@ -0,0 +1,18 @@
+/*
+ * 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 XR_VERSION_H
+#define XR_VERSION_H
+
+#define XRADIO_VERSION "L34M.01.08.0002"
+#define XRADIO_MAIN_VER 1
+#define XRADIO_SUB_VER 8
+
+#endif
diff --git a/drivers/net/wireless/xradio/xradio.h b/drivers/net/wireless/xradio/xradio.h
new file mode 100644
index 0000000..32632a6
--- /dev/null
+++ b/drivers/net/wireless/xradio/xradio.h
@@ -0,0 +1,688 @@
+/*
+ * 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 "common.h"
+#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 char *drv_version;
+extern char *drv_buildtime;
+#define DRV_VERSION drv_version
+#define DRV_BUILDTIME drv_buildtime
+
+/* extern */ struct sbus_ops;
+/* 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;
+
+ const struct sbus_ops *sbus_ops;
+ struct sbus_priv *sbus_priv;
+ 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 */
+#ifdef USE_VFS_FIRMWARE
+ const struct xr_file *sdd;
+#else
+ const struct firmware *sdd;
+#endif
+ 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_rx;
+ 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
+#ifdef HW_RESTART
+ bool hw_restart;
+ struct work_struct hw_restart_work;
+#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_info ht_info;
+ 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;
+#ifdef IPV6_FILTERING
+ struct wsm_ndp_ipv6_filter filter6;
+#endif /*IPV6_FILTERING*/
+ 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_info;
+ struct work_struct ht_info_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,
+#ifdef IPV6_FILTERING
+ IPV6ADDR_FILTER_ID,
+#endif /*IPV6_FILTERING*/
+};
+
+#ifdef IPV6_FILTERING
+/* IPV6 host addr info */
+struct ipv6_addr_info {
+ u8 filter_mode;
+ u8 address_mode;
+ u16 ipv6[8];
+};
+#endif /*IPV6_FILTERING*/
+
+/* 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 */