From 2f280c881390ab3b9ea444cec28cbd86ea08b6a0 Mon Sep 17 00:00:00 2001 From: zador-blood-stained Date: Thu, 29 Dec 2016 13:10:47 +0300 Subject: [PATCH] Add Xradio driver to sun8i-dev --- config/kernel/linux-sun8i-dev.config | 17 +- .../add-opi-zero-dts-with-wireless.patch | 87 + .../add-xradio-wireless-driver.patch | 24127 ++++++++++++++++ 3 files changed, 24230 insertions(+), 1 deletion(-) create mode 100644 patch/kernel/sun8i-dev/add-opi-zero-dts-with-wireless.patch create mode 100644 patch/kernel/sun8i-dev/add-xradio-wireless-driver.patch diff --git a/config/kernel/linux-sun8i-dev.config b/config/kernel/linux-sun8i-dev.config index 6af734f96..7fce3f158 100644 --- a/config/kernel/linux-sun8i-dev.config +++ b/config/kernel/linux-sun8i-dev.config @@ -269,6 +269,7 @@ CONFIG_MODULES=y # CONFIG_MODULE_FORCE_LOAD is not set CONFIG_MODULE_UNLOAD=y # CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set # CONFIG_MODULE_SRCVERSION_ALL is not set # CONFIG_MODULE_SIG is not set # CONFIG_MODULE_COMPRESS is not set @@ -1298,7 +1299,12 @@ CONFIG_NET_SCH_FIFO=y CONFIG_DCB=y CONFIG_DNS_RESOLVER=y CONFIG_BATMAN_ADV=m -# CONFIG_BATMAN_ADV_DEBUG is not set +# CONFIG_BATMAN_ADV_BATMAN_V is not set +CONFIG_BATMAN_ADV_BLA=y +# CONFIG_BATMAN_ADV_DAT is not set +# CONFIG_BATMAN_ADV_NC is not set +# CONFIG_BATMAN_ADV_MCAST is not set +# CONFIG_BATMAN_ADV_DEBUGFS is not set CONFIG_OPENVSWITCH=m CONFIG_OPENVSWITCH_GRE=m CONFIG_OPENVSWITCH_VXLAN=m @@ -2213,6 +2219,13 @@ CONFIG_WLAN_VENDOR_TI=y CONFIG_WLAN_VENDOR_ZYDAS=y # CONFIG_USB_ZD1201 is not set # CONFIG_ZD1211RW is not set +CONFIG_WLAN_VENDOR_XRADIO=m +CONFIG_XRADIO_SDIO=y +CONFIG_XRADIO_USE_EXTENSIONS=y +CONFIG_XRADIO_5GHZ_SUPPORT=y +CONFIG_XRADIO_XR819=y +CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES=y +CONFIG_XRADIO_WAPI_SUPPORT=y CONFIG_MAC80211_HWSIM=m CONFIG_USB_NET_RNDIS_WLAN=m @@ -2454,6 +2467,8 @@ CONFIG_SERIAL_CORE=y CONFIG_SERIAL_CORE_CONSOLE=y # CONFIG_SERIAL_SCCNXP is not set CONFIG_SERIAL_SC16IS7XX=m +# CONFIG_SERIAL_SC16IS7XX_I2C is not set +# CONFIG_SERIAL_SC16IS7XX_SPI is not set # CONFIG_SERIAL_BCM63XX is not set # CONFIG_SERIAL_ALTERA_JTAGUART is not set # CONFIG_SERIAL_ALTERA_UART is not set diff --git a/patch/kernel/sun8i-dev/add-opi-zero-dts-with-wireless.patch b/patch/kernel/sun8i-dev/add-opi-zero-dts-with-wireless.patch new file mode 100644 index 000000000..f444f535e --- /dev/null +++ b/patch/kernel/sun8i-dev/add-opi-zero-dts-with-wireless.patch @@ -0,0 +1,87 @@ +diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile +index c558ba75..f1ff8b90 100644 +--- a/arch/arm/boot/dts/Makefile ++++ b/arch/arm/boot/dts/Makefile +@@ -818,6 +818,7 @@ dtb-$(CONFIG_MACH_SUN8I) += \ + sun8i-a33-sinlinx-sina33.dtb \ + sun8i-a83t-allwinner-h8homlet-v2.dtb \ + sun8i-a83t-cubietruck-plus.dtb \ ++ sun8i-h2plus-orangepi-zero.dtb \ + sun8i-h3-bananapi-m2-plus.dtb \ + sun8i-h3-nanopi-neo.dtb \ + sun8i-h3-orangepi-2.dtb \ +diff --git a/arch/arm/boot/dts/sun8i-h2plus-orangepi-zero.dts b/arch/arm/boot/dts/sun8i-h2plus-orangepi-zero.dts +new file mode 100644 +index 00000000..d262a251 +--- /dev/null ++++ b/arch/arm/boot/dts/sun8i-h2plus-orangepi-zero.dts +@@ -0,0 +1,68 @@ ++ ++#include "sun8i-h3-orangepi-one.dts" ++ ++/ { ++ model = "Xunlong Orange Pi Zero"; ++ compatible = "xunlong,orangepi-zero", "allwinner,sun8i-h2plus"; ++ ++ aliases { ++ ethernet1 = &xr819wifi; ++ }; ++ ++ vdd_wifi: vdd_wifi@0 { ++ compatible = "regulator-fixed"; ++ regulator-name = "wifi"; ++ regulator-min-microvolt = <1800000>; ++ regulator-max-microvolt = <1800000>; ++ gpio = <&pio 0 20 GPIO_ACTIVE_HIGH>; ++ startup-delay-us = <70000>; ++ enable-active-high; ++ }; ++ ++ pwrseq_wifi: pwrseq_wifi@0 { ++ compatible = "mmc-pwrseq-simple"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wifi_rst>; ++ reset-gpios = <&r_pio 0 7 GPIO_ACTIVE_LOW>; ++ post-power-on-delay-ms = <50>; ++ }; ++}; ++ ++&mmc1 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&mmc1_pins_a>; ++ vmmc-supply = <®_vcc3v3>; ++ vqmmc-supply = <&vdd_wifi>; ++ mmc-pwrseq = <&pwrseq_wifi>; ++ bus-width = <4>; ++ non-removable; ++ status = "okay"; ++ ++ xr819wifi: xr819wifi@1 { ++ reg = <1>; ++ compatible = "xradio,xr819"; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&wifi_wake>; ++ interrupt-parent = <&pio>; ++ interrupts = <6 10 IRQ_TYPE_EDGE_RISING>; ++ interrupt-names = "host-wake"; ++ local-mac-address = [dc 44 6d c0 ff ee]; ++ }; ++}; ++ ++&pio { ++ wifi_wake: wifi_wake@0 { ++ allwinner,pins = "PG10"; ++ allwinner,function = "irq"; ++ allwinner,pull = ; ++ }; ++}; ++ ++&r_pio { ++ wifi_rst: wifi_rst@0 { ++ allwinner,pins = "PL7"; ++ allwinner,function = "gpio_out"; ++ allwinner,drive = ; ++ allwinner,pull = ; ++ }; ++}; +\ No newline at end of file diff --git a/patch/kernel/sun8i-dev/add-xradio-wireless-driver.patch b/patch/kernel/sun8i-dev/add-xradio-wireless-driver.patch new file mode 100644 index 000000000..049737cfe --- /dev/null +++ b/patch/kernel/sun8i-dev/add-xradio-wireless-driver.patch @@ -0,0 +1,24127 @@ +diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig +index 8c8edaf1..122b980b 100644 +--- a/drivers/net/wireless/Kconfig ++++ b/drivers/net/wireless/Kconfig +@@ -32,6 +32,7 @@ source "drivers/net/wireless/rsi/Kconfig" + source "drivers/net/wireless/st/Kconfig" + source "drivers/net/wireless/ti/Kconfig" + source "drivers/net/wireless/zydas/Kconfig" ++source "drivers/net/wireless/xradio/Kconfig" + + config PCMCIA_RAYCS + tristate "Aviator/Raytheon 2.4GHz wireless support" +diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile +index f00d4295..50db6f2b 100644 +--- a/drivers/net/wireless/Makefile ++++ b/drivers/net/wireless/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_WLAN_VENDOR_RSI) += rsi/ + obj-$(CONFIG_WLAN_VENDOR_ST) += st/ + obj-$(CONFIG_WLAN_VENDOR_TI) += ti/ + obj-$(CONFIG_WLAN_VENDOR_ZYDAS) += zydas/ ++obj-$(CONFIG_WLAN_VENDOR_XRADIO) += xradio/ + + # 16-bit wireless PCMCIA client drivers + obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o +diff --git a/drivers/net/wireless/xradio/Kconfig b/drivers/net/wireless/xradio/Kconfig +new file mode 100644 +index 00000000..85ff4604 +--- /dev/null ++++ b/drivers/net/wireless/xradio/Kconfig +@@ -0,0 +1,63 @@ ++config WLAN_VENDOR_XRADIO ++ tristate "XRADIO WLAN support" ++ depends on MAC80211 ++ default n ++ help ++ ++ This is an experimental driver for the XRADIO chip-set. ++ Enabling this option enables the generic driver without ++ any platform support. ++ Please select the appropriate platform below. ++ ++config XRADIO_SDIO ++ bool ++ ++config XRADIO_USE_EXTENSIONS ++ bool ++ ++config XRADIO_5GHZ_SUPPORT ++ bool ++ ++if WLAN_VENDOR_XRADIO ++ ++config XRADIO_XR819 ++ bool "XRADIO XR819 support" ++ depends on WLAN_VENDOR_XRADIO ++ select XRADIO_SDIO ++ select XRADIO_USE_EXTENSIONS ++ select XRADIO_5GHZ_SUPPORT ++ default y ++ ++config XRADIO_NON_POWER_OF_TWO_BLOCKSIZES ++ bool "Platform supports non-power-of-two SDIO transfer" ++ depends on WLAN_VENDOR_XRADIO ++ default y ++ help ++ Say N here only if you are running the driver on a platform ++ which does not have support for non-power-of-two SDIO transfer. ++ If unsure, say Y. ++ ++config XRADIO_5GHZ_SUPPORT ++ bool "5GHz band support" ++ depends on WLAN_VENDOR_XRADIO ++ default n ++ help ++ Say Y if your device supports 5GHz band. If unsure, say N. ++ ++config XRADIO_WAPI_SUPPORT ++ bool "WAPI support" ++ depends on WLAN_VENDOR_XRADIO ++ default n ++ help ++ Say Y if your compat-wireless support WAPI. ++ If unsure, say N. ++ ++config XRADIO_USE_EXTENSIONS ++ bool "Extensions for WFD and PS mode" ++ depends on WLAN_VENDOR_XRADIO ++ default y ++ help ++ Say Y if you want to include XR extensions ++ If unsure, say Y. ++ ++endif +diff --git a/drivers/net/wireless/xradio/Makefile b/drivers/net/wireless/xradio/Makefile +new file mode 100644 +index 00000000..b2b0badc +--- /dev/null ++++ b/drivers/net/wireless/xradio/Makefile +@@ -0,0 +1,52 @@ ++ ++obj-$(CONFIG_WLAN_VENDOR_XRADIO) += xradio_wlan.o ++ ++xradio_wlan-objs := \ ++ fwio.o \ ++ txrx.o \ ++ main.o \ ++ queue.o \ ++ hwio.o \ ++ bh.o \ ++ wsm.o \ ++ sta.o \ ++ ap.o \ ++ keys.o \ ++ scan.o \ ++ debug.o \ ++ module.o \ ++ sdio.o \ ++ pm.o ++ ++#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DP2P_MULTIVIF ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DMCAST_FWDING ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_SUSPEND_RESUME_FILTER_ENABLE ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DAP_AGGREGATE_FW_FIX ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DAP_HT_CAP_UPDATE ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DHW_ERROR_WIFI_RESET ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DAP_HT_COMPAT_FIX ++#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_DEBUG ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_DUMP_ON_ERROR ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_DEBUGFS ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES ++ ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DCONFIG_XRADIO_SUSPEND_POWER_OFF ++ ++# Extra IE for probe response from upper layer is needed in P2P GO ++# For offloading probe response to FW, the extra IE must be included ++# in the probe response template ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DPROBE_RESP_EXTRA_IE ++ ++# Modified by wzw ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DTES_P2P_0002_ROC_RESTART ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DTES_P2P_000B_EXTEND_INACTIVITY_CNT ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DTES_P2P_000B_DISABLE_EAPOL_FILTER ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_USE_LONG_DTIM_PERIOD ++ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_USE_LONG_KEEP_ALIVE_PERIOD ++ ++#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DDEBUG ++ ++#~dgp ++#ccflags-$(CONFIG_WLAN_VENDOR_XRADIO) += -DXRADIO_DISABLE_HW_CRYPTO ++ ++ldflags-$(CONFIG_WLAN_VENDOR_XRADIO) += --strip-debug +diff --git a/drivers/net/wireless/xradio/ap.c b/drivers/net/wireless/xradio/ap.c +new file mode 100644 +index 00000000..a51ed77e +--- /dev/null ++++ b/drivers/net/wireless/xradio/ap.c +@@ -0,0 +1,1670 @@ ++/* ++ * STA and AP APIs for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include "xradio.h" ++#include "sta.h" ++#include "ap.h" ++#include "bh.h" ++#include "net/mac80211.h" ++ ++#define XRADIO_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) ++#define XRADIO_ENABLE_ARP_FILTER_OFFLOAD 3 ++ ++#ifndef ERP_INFO_BYTE_OFFSET ++#define ERP_INFO_BYTE_OFFSET 2 ++#endif ++ ++static int xradio_upload_beacon(struct xradio_vif *priv); ++#ifdef PROBE_RESP_EXTRA_IE ++static int xradio_upload_proberesp(struct xradio_vif *priv); ++#endif ++static int xradio_upload_pspoll(struct xradio_vif *priv); ++static int xradio_upload_null(struct xradio_vif *priv); ++static int xradio_upload_qosnull(struct xradio_vif *priv); ++static int xradio_start_ap(struct xradio_vif *priv); ++static int xradio_update_beaconing(struct xradio_vif *priv); ++/* ++static int xradio_enable_beaconing(struct xradio_vif *priv, ++ bool enable); ++*/ ++static void __xradio_sta_notify(struct xradio_vif *priv, ++ enum sta_notify_cmd notify_cmd, ++ int link_id); ++ ++/* ******************************************************************** */ ++/* AP API */ ++int xradio_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct xradio_link_entry *entry; ++ struct sk_buff *skb; ++#ifdef AP_AGGREGATE_FW_FIX ++ struct xradio_common *hw_priv = hw->priv; ++#endif ++ ++#ifdef P2P_MULTIVIF ++ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); ++#endif ++ ++ if (priv->mode != NL80211_IFTYPE_AP) { ++ return 0; ++ } ++ ++ sta_priv->priv = priv; ++ sta_priv->link_id = xradio_find_link_id(priv, sta->addr); ++ if (SYS_WARN(!sta_priv->link_id)) { ++ /* Impossible error */ ++ wiphy_debug(hw->wiphy, "No more link IDs available.\n"); ++ return -ENOENT; ++ } ++ ++ entry = &priv->link_id_db[sta_priv->link_id - 1]; ++ spin_lock_bh(&priv->ps_state_lock); ++ if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == ++ IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) { ++ priv->sta_asleep_mask |= BIT(sta_priv->link_id); ++ } ++ entry->status = XRADIO_LINK_HARD; ++ while ((skb = skb_dequeue(&entry->rx_queue))) ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ spin_unlock_bh(&priv->ps_state_lock); ++ ++#ifdef AP_AGGREGATE_FW_FIX ++ hw_priv->connected_sta_cnt++; ++ if(hw_priv->connected_sta_cnt>1) { ++ wsm_lock_tx(hw_priv); ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, ++ XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, ++ priv->if_id)); ++ wsm_unlock_tx(hw_priv); ++ } ++#endif ++ ++ return 0; ++} ++ ++int xradio_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_sta_priv *sta_priv = ++ (struct xradio_sta_priv *)&sta->drv_priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct xradio_link_entry *entry; ++ ++#ifdef P2P_MULTIVIF ++ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); ++#endif ++ ++ if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) { ++ wiphy_warn(hw->wiphy, "no station to remove\n"); ++ return 0; ++ } ++ ++ entry = &priv->link_id_db[sta_priv->link_id - 1]; ++ spin_lock_bh(&priv->ps_state_lock); ++ entry->status = XRADIO_LINK_RESERVE; ++ entry->timestamp = jiffies; ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ spin_unlock_bh(&priv->ps_state_lock); ++ flush_workqueue(hw_priv->workqueue); ++ ++#ifdef AP_AGGREGATE_FW_FIX ++ hw_priv->connected_sta_cnt--; ++ if(hw_priv->connected_sta_cnt <= 1) { ++ if ((priv->if_id != 1) || ++ ((priv->if_id == 1) && hw_priv->is_go_thru_go_neg)) { ++ wsm_lock_tx(hw_priv); ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, ++ XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, ++ priv->if_id)); ++ wsm_unlock_tx(hw_priv); ++ } ++ } ++#endif ++ ++ return 0; ++} ++ ++static void __xradio_sta_notify(struct xradio_vif *priv, ++ enum sta_notify_cmd notify_cmd, ++ int link_id) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ u32 bit, prev; ++ ++ /* Zero link id means "for all link IDs" */ ++ if (link_id) ++ bit = BIT(link_id); ++ else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) ++ bit = 0; ++ else ++ bit = priv->link_id_map; ++ prev = priv->sta_asleep_mask & bit; ++ ++ switch (notify_cmd) { ++ case STA_NOTIFY_SLEEP: ++ if (!prev) { ++ if (priv->buffered_multicasts && ++ !priv->sta_asleep_mask) ++ queue_work(hw_priv->workqueue, ++ &priv->multicast_start_work); ++ priv->sta_asleep_mask |= bit; ++ } ++ break; ++ case STA_NOTIFY_AWAKE: ++ if (prev) { ++ priv->sta_asleep_mask &= ~bit; ++ priv->pspoll_mask &= ~bit; ++ if (priv->tx_multicast && link_id && ++ !priv->sta_asleep_mask) ++ queue_work(hw_priv->workqueue, ++ &priv->multicast_stop_work); ++ xradio_bh_wakeup(hw_priv); ++ } ++ break; ++ } ++} ++ ++void xradio_sta_notify(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif, ++ enum sta_notify_cmd notify_cmd, ++ struct ieee80211_sta *sta) ++{ ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; ++ ++#ifdef P2P_MULTIVIF ++ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); ++#endif ++ spin_lock_bh(&priv->ps_state_lock); ++ __xradio_sta_notify(priv, notify_cmd, sta_priv->link_id); ++ spin_unlock_bh(&priv->ps_state_lock); ++} ++ ++static void xradio_ps_notify(struct xradio_vif *priv, ++ int link_id, bool ps) ++{ ++ if (link_id > MAX_STA_IN_AP_MODE) { ++ ap_printk(XRADIO_DBG_WARN,"link_id is invalid=%d\n", link_id); ++ return; ++ } ++ ++ ap_printk(XRADIO_DBG_NIY, "%s for LinkId: %d. STAs asleep: %.8X\n", ++ ps ? "Stop" : "Start", link_id, priv->sta_asleep_mask); ++ ++ /* TODO:COMBO: __xradio_sta_notify changed. */ ++ __xradio_sta_notify(priv, ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); ++} ++ ++static int xradio_set_tim_impl(struct xradio_vif *priv, bool aid0_bit_set) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct sk_buff *skb; ++ struct wsm_update_ie update_ie = { ++ .what = WSM_UPDATE_IE_BEACON, ++ .count = 1, ++ }; ++ u16 tim_offset, tim_length; ++ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ap_printk(XRADIO_DBG_MSG, "%s mcast: %s.\n", __func__, ++ aid0_bit_set ? "ena" : "dis"); ++ ++ skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length); ++ if (!skb) { ++ __xradio_flush(hw_priv, true, priv->if_id); ++ return -ENOENT; ++ } ++ ++ if (tim_offset && tim_length >= 6) { ++ /* Ignore DTIM count from mac80211: ++ * firmware handles DTIM internally. */ ++ skb->data[tim_offset + 2] = 0; ++ ++ /* Set/reset aid0 bit */ ++ if (aid0_bit_set) ++ skb->data[tim_offset + 4] |= 1; ++ else ++ skb->data[tim_offset + 4] &= ~1; ++ } ++ ++ update_ie.ies = &skb->data[tim_offset]; ++ update_ie.length = tim_length; ++ //filter same tim info, yangfh ++ if(memcmp(priv->last_tim, update_ie.ies, tim_length)) { ++ SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); ++ memcpy(priv->last_tim, update_ie.ies, tim_length); ++ ap_printk(XRADIO_DBG_MSG,"%02x %02x %02x %02x %02x %02x\n", ++ update_ie.ies[0], update_ie.ies[1], update_ie.ies[2], ++ update_ie.ies[3], update_ie.ies[4], update_ie.ies[5]); ++ } ++ ++ dev_kfree_skb(skb); ++ ++ return 0; ++} ++ ++void xradio_set_tim_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = container_of(work, struct xradio_vif, set_tim_work); ++ xradio_set_tim_impl(priv, priv->aid0_bit_set); ++} ++ ++int xradio_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, ++ bool set) ++{ ++ struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; ++ struct xradio_vif *priv = sta_priv->priv; ++ ++#ifdef P2P_MULTIVIF ++ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); ++#endif ++ SYS_WARN(priv->mode != NL80211_IFTYPE_AP); ++ queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); ++ return 0; ++} ++ ++void xradio_set_cts_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, set_cts_work.work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; ++ struct wsm_update_ie update_ie = { ++ .what = WSM_UPDATE_IE_BEACON, ++ .count = 1, ++ .ies = erp_ie, ++ .length = 3, ++ }; ++ u32 erp_info; ++ __le32 use_cts_prot; ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ erp_info = priv->erp_info; ++ mutex_unlock(&hw_priv->conf_mutex); ++ use_cts_prot = (erp_info & WLAN_ERP_USE_PROTECTION)? __cpu_to_le32(1) : 0; ++ ++ erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; ++ ++ ap_printk(XRADIO_DBG_MSG, "ERP information 0x%x\n", erp_info); ++ ++ /* TODO:COMBO: If 2 interfaces are on the same channel they share ++ the same ERP values */ ++ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION, ++ &use_cts_prot, sizeof(use_cts_prot), priv->if_id)); ++ /* If STA Mode update_ie is not required */ ++ if (priv->mode != NL80211_IFTYPE_STATION) { ++ SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); ++ } ++ ++ return; ++} ++ ++static int xradio_set_btcoexinfo(struct xradio_vif *priv) ++{ ++ struct wsm_override_internal_txrate arg; ++ int ret = 0; ++ ++ if (priv->mode == NL80211_IFTYPE_STATION) { ++ /* Plumb PSPOLL and NULL template */ ++ SYS_WARN(xradio_upload_pspoll(priv)); ++ SYS_WARN(xradio_upload_null(priv)); ++ } else { ++ return 0; ++ } ++ ++ memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); ++ ++ if (!priv->vif->p2p) { ++ /* STATION mode */ ++ if (priv->bss_params.operationalRateSet & ~0xF) { ++ ap_printk(XRADIO_DBG_NIY, "STA has ERP rates\n"); ++ /* G or BG mode */ ++ arg.internalTxRate = (__ffs( ++ priv->bss_params.operationalRateSet & ~0xF)); ++ } else { ++ ap_printk(XRADIO_DBG_NIY, "STA has non ERP rates\n"); ++ /* B only mode */ ++ arg.internalTxRate = (__ffs( ++ priv->association_mode.basicRateSet)); ++ } ++ arg.nonErpInternalTxRate = (__ffs( ++ priv->association_mode.basicRateSet)); ++ } else { ++ /* P2P mode */ ++ arg.internalTxRate = (__ffs( ++ priv->bss_params.operationalRateSet & ~0xF)); ++ arg.nonErpInternalTxRate = (__ffs( ++ priv->bss_params.operationalRateSet & ~0xF)); ++ } ++ ++ ap_printk(XRADIO_DBG_NIY, "BTCOEX_INFO" "MODE %d, internalTxRate : %x," ++ "nonErpInternalTxRate: %x\n", priv->mode, arg.internalTxRate, ++ arg.nonErpInternalTxRate); ++ ++ ret = SYS_WARN(wsm_write_mib(xrwl_vifpriv_to_hwpriv(priv), ++ WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, ++ &arg, sizeof(arg), priv->if_id)); ++ ++ return ret; ++} ++ ++void xradio_bss_info_changed(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif, ++ struct ieee80211_bss_conf *info, ++ u32 changed) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ ++#ifdef P2P_MULTIVIF ++ if (priv->if_id == XRWL_GENERIC_IF_ID) ++ return; ++#endif ++ mutex_lock(&hw_priv->conf_mutex); ++ if (changed & BSS_CHANGED_BSSID) { ++#ifdef CONFIG_XRADIO_TESTMODE ++ spin_lock_bh(&hw_priv->tsm_lock); ++ if (hw_priv->tsm_info.sta_associated) { ++ unsigned now = jiffies; ++ hw_priv->tsm_info.sta_roamed = 1; ++ if ((now - hw_priv->tsm_info.txconf_timestamp_vo) > ++ (now - hw_priv->tsm_info.rx_timestamp_vo)) ++ hw_priv->tsm_info.use_rx_roaming = 1; ++ } else { ++ hw_priv->tsm_info.sta_associated = 1; ++ } ++ spin_unlock_bh(&hw_priv->tsm_lock); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ memcpy(priv->bssid, info->bssid, ETH_ALEN); ++ xradio_setup_mac_pvif(priv); ++ } ++ ++ /* TODO: BSS_CHANGED_IBSS */ ++ if (changed & BSS_CHANGED_ARP_FILTER) { ++ struct wsm_arp_ipv4_filter filter = {0}; ++ int i; ++ ap_printk(XRADIO_DBG_MSG, "[STA] BSS_CHANGED_ARP_FILTER cnt: %d\n", ++ info->arp_addr_cnt); ++ ++ if (info->arp_addr_cnt){ ++ if (vif->type == NL80211_IFTYPE_STATION) ++ filter.enable = (u32)XRADIO_ENABLE_ARP_FILTER_OFFLOAD; ++ else if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ filter.enable = (u32)(1<<1); ++ else ++ filter.enable = 0; ++ } ++ ++ /* Currently only one IP address is supported by firmware. ++ * In case of more IPs arp filtering will be disabled. */ ++ if (info->arp_addr_cnt > 0 && ++ info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { ++ for (i = 0; i < info->arp_addr_cnt; i++) { ++ filter.ipv4Address[i] = info->arp_addr_list[i]; ++ ap_printk(XRADIO_DBG_NIY, "[STA]addr[%d]: 0x%X\n", i, filter.ipv4Address[i]); ++ } ++ } else ++ filter.enable = 0; ++ ++ if (filter.enable) ++ xradio_set_arpreply(dev, vif); ++ ++ priv->filter4.enable = filter.enable; ++ ap_printk(XRADIO_DBG_NIY, "[STA]arp ip filter enable: %d\n", __le32_to_cpu(filter.enable)); ++ ++ if (wsm_set_arp_ipv4_filter(hw_priv, &filter, priv->if_id)) ++ SYS_WARN(1); ++ ++ if (filter.enable && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA)) { ++ /* Firmware requires that value for this 1-byte field must ++ * be specified in units of 500us. Values above the 128ms ++ * threshold are not supported. */ ++ //if (info->dynamic_ps_timeout >= 0x80) ++ // priv->powersave_mode.fastPsmIdlePeriod = 0xFF; ++ //else ++ // priv->powersave_mode.fastPsmIdlePeriod = info->dynamic_ps_timeout << 1; ++ ++ priv->powersave_mode.fastPsmIdlePeriod = 200;//when connected,the dev->conf.dynamic_ps_timeout value is 0 ++ priv->powersave_mode.apPsmChangePeriod = 200; //100ms, add by yangfh ++ ap_printk(XRADIO_DBG_NIY, "[STA]fastPsmIdle=%d, apPsmChange=%d\n", ++ priv->powersave_mode.fastPsmIdlePeriod, ++ priv->powersave_mode.apPsmChangePeriod); ++ ++ if (priv->setbssparams_done) { ++ int ret = 0; ++ struct wsm_set_pm pm = priv->powersave_mode; ++ if (priv->user_power_set_true) ++ priv->powersave_mode.pmMode = priv->user_pm_mode; ++ else if ((priv->power_set_true && ++ ((priv->powersave_mode.pmMode == WSM_PSM_ACTIVE) || ++ (priv->powersave_mode.pmMode == WSM_PSM_PS))) || ++ !priv->power_set_true) ++ priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; ++ ++ ret = xradio_set_pm (priv, &priv->powersave_mode); ++ if(ret) ++ priv->powersave_mode = pm; ++ } else { ++ priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; ++ } ++ priv->power_set_true = 0; ++ priv->user_power_set_true = 0; ++ } ++ } ++ ++ if (changed & BSS_CHANGED_BEACON) { ++ ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_BEACON\n"); ++#ifdef HIDDEN_SSID ++ if(priv->join_status != XRADIO_JOIN_STATUS_AP) { ++ priv->hidden_ssid = info->hidden_ssid; ++ priv->ssid_length = info->ssid_len; ++ memcpy(priv->ssid, info->ssid, info->ssid_len); ++ } else ++ ap_printk(XRADIO_DBG_NIY, "priv->join_status=%d\n", priv->join_status); ++#endif ++ SYS_WARN(xradio_upload_beacon(priv)); ++ SYS_WARN(xradio_update_beaconing(priv)); ++ } ++ ++ if (changed & BSS_CHANGED_BEACON_ENABLED) { ++ ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_BEACON_ENABLED dummy\n"); ++ priv->enable_beacon = info->enable_beacon; ++ } ++ ++ if (changed & BSS_CHANGED_BEACON_INT) { ++ ap_printk(XRADIO_DBG_NIY, "CHANGED_BEACON_INT\n"); ++ /* Restart AP only when connected */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ SYS_WARN(xradio_update_beaconing(priv)); ++ } ++ ++ ++ if (changed & BSS_CHANGED_ASSOC) { ++ wsm_lock_tx(hw_priv); ++ priv->wep_default_key_id = -1; ++ wsm_unlock_tx(hw_priv); ++ ++ if (!info->assoc /* && !info->ibss_joined */) { ++ priv->cqm_link_loss_count = XRADIO_LINK_LOSS_THOLD_DEF; ++ priv->cqm_beacon_loss_count = XRADIO_BSS_LOSS_THOLD_DEF; ++ priv->cqm_tx_failure_thold = 0; ++ } ++ priv->cqm_tx_failure_count = 0; ++ } ++ ++ if (changed & ++ (BSS_CHANGED_ASSOC | ++ BSS_CHANGED_BASIC_RATES | ++ BSS_CHANGED_ERP_PREAMBLE | ++ BSS_CHANGED_HT | ++ BSS_CHANGED_ERP_SLOT)) { ++ int is_combo = 0; ++ int i; ++ struct xradio_vif *tmp_priv; ++ ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_ASSOC.\n"); ++ if (info->assoc) { /* TODO: ibss_joined */ ++ struct ieee80211_sta *sta = NULL; ++ if (info->dtim_period) ++ priv->join_dtim_period = info->dtim_period; ++ priv->beacon_int = info->beacon_int; ++ ++ /* Associated: kill join timeout */ ++ cancel_delayed_work_sync(&priv->join_timeout); ++ ++ rcu_read_lock(); ++ if (info->bssid) ++ sta = ieee80211_find_sta(vif, info->bssid); ++ if (sta) { ++ /* TODO:COMBO:Change this once ++ * mac80211 changes are available */ ++ SYS_BUG(!hw_priv->channel); ++ hw_priv->ht_oper.ht_cap = sta->ht_cap; ++ priv->bss_params.operationalRateSet =__cpu_to_le32( ++ xradio_rate_mask_to_wsm(hw_priv, sta->supp_rates[hw_priv->channel->band])); ++ /* TODO by Icenowy: I think this may lead to some problems. */ ++// hw_priv->ht_oper.channel_type = info->channel_type; ++ hw_priv->ht_oper.operation_mode = info->ht_operation_mode; ++ } else { ++ memset(&hw_priv->ht_oper, 0, sizeof(hw_priv->ht_oper)); ++ priv->bss_params.operationalRateSet = -1; ++ } ++ rcu_read_unlock(); ++ priv->htcap = (sta && xradio_is_ht(&hw_priv->ht_oper)); ++ xradio_for_each_vif(hw_priv, tmp_priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv) ++#else ++ if (!tmp_priv) ++#endif ++ continue; ++ if (tmp_priv->join_status >= XRADIO_JOIN_STATUS_STA) ++ is_combo++; ++ } ++ ++ if (is_combo > 1) { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; ++ ap_printk(XRADIO_DBG_WARN, "%sASSOC is_combo %d\n", ++ (priv->join_status == XRADIO_JOIN_STATUS_STA)?"[STA] ":"", ++ hw_priv->vif0_throttle); ++ } else if ((priv->join_status == XRADIO_JOIN_STATUS_STA) && priv->htcap) { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE; ++ ap_printk(XRADIO_DBG_WARN, "[STA] ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle); ++ } else { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; ++ ap_printk(XRADIO_DBG_WARN, "ASSOC not_combo 11BG %d\n",hw_priv->vif0_throttle); ++ } ++ ++ if (sta) { ++ __le32 val = 0; ++ if (hw_priv->ht_oper.operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) { ++ ap_printk(XRADIO_DBG_NIY,"[STA] Non-GF STA present\n"); ++ /* Non Green field capable STA */ ++ val = __cpu_to_le32(BIT(1)); ++ } ++ SYS_WARN(wsm_write_mib(hw_priv, WSM_MID_ID_SET_HT_PROTECTION, ++ &val, sizeof(val), priv->if_id)); ++ } ++ ++ priv->association_mode.greenfieldMode = xradio_ht_greenfield(&hw_priv->ht_oper); ++ priv->association_mode.flags = ++ WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | ++ WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | ++ WSM_ASSOCIATION_MODE_USE_HT_MODE | ++ WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | ++ WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; ++ ++ priv->association_mode.preambleType = ++ (info->use_short_preamble ? WSM_JOIN_PREAMBLE_SHORT : WSM_JOIN_PREAMBLE_LONG); ++ priv->association_mode.basicRateSet = __cpu_to_le32( ++ xradio_rate_mask_to_wsm(hw_priv,info->basic_rates)); ++ priv->association_mode.mpduStartSpacing = ++ xradio_ht_ampdu_density(&hw_priv->ht_oper); ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold; ++ //priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; ++ //priv->cqm_tx_failure_count = 0; ++ cancel_delayed_work_sync(&priv->bss_loss_work); ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ ++ priv->bss_params.beaconLostCount = (priv->cqm_beacon_loss_count ? ++ priv->cqm_beacon_loss_count : priv->cqm_link_loss_count); ++ ++ priv->bss_params.aid = info->aid; ++ ++ if (priv->join_dtim_period < 1) ++ priv->join_dtim_period = 1; ++ ++ ap_printk(XRADIO_DBG_MSG, "[STA] DTIM %d, interval: %d\n", ++ priv->join_dtim_period, priv->beacon_int); ++ ap_printk(XRADIO_DBG_MSG, "[STA] Preamble: %d, " \ ++ "Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", ++ priv->association_mode.preambleType, ++ priv->association_mode.greenfieldMode, ++ priv->bss_params.aid, ++ priv->bss_params.operationalRateSet, ++ priv->association_mode.basicRateSet); ++ SYS_WARN(wsm_set_association_mode(hw_priv, &priv->association_mode, priv->if_id)); ++ SYS_WARN(wsm_keep_alive_period(hw_priv, XRADIO_KEEP_ALIVE_PERIOD /* sec */, ++ priv->if_id)); ++ SYS_WARN(wsm_set_bss_params(hw_priv, &priv->bss_params, priv->if_id)); ++ priv->setbssparams_done = true; ++#ifdef XRADIO_USE_LONG_DTIM_PERIOD ++{ ++ int join_dtim_period_extend; ++ if (priv->join_dtim_period <= 3) { ++ join_dtim_period_extend = priv->join_dtim_period * 3; ++ } else if (priv->join_dtim_period <= 5) { ++ join_dtim_period_extend = priv->join_dtim_period * 2; ++ } else { ++ join_dtim_period_extend = priv->join_dtim_period; ++ } ++ SYS_WARN(wsm_set_beacon_wakeup_period(hw_priv, ++ ((priv->beacon_int * join_dtim_period_extend) > MAX_BEACON_SKIP_TIME_MS ++ ? 1 : join_dtim_period_extend) , 0, priv->if_id)); ++} ++#else ++ SYS_WARN(wsm_set_beacon_wakeup_period(hw_priv, ++ ((priv->beacon_int * priv->join_dtim_period) > MAX_BEACON_SKIP_TIME_MS ++ ? 1 : priv->join_dtim_period) , 0, priv->if_id)); ++#endif ++ if (priv->htcap) { ++ wsm_lock_tx(hw_priv); ++ /* Statically enabling block ack for TX/RX */ ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, hw_priv->ba_tid_mask, ++ hw_priv->ba_tid_mask, priv->if_id)); ++ wsm_unlock_tx(hw_priv); ++ } ++ /*set ps active,avoid that when connecting process,the device sleeps,then can't receive pkts.*/ ++ if (changed & BSS_CHANGED_ASSOC) ++ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; ++ xradio_set_pm(priv, &priv->powersave_mode); ++ if (priv->vif->p2p) { ++ ap_printk(XRADIO_DBG_NIY, "[STA] Setting p2p powersave configuration.\n"); ++ SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, &priv->p2p_ps_modeinfo, priv->if_id)); ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //xradio_notify_noa(priv, XRADIO_NOA_NOTIFICATION_DELAY); ++#endif ++ } ++ ++ if (priv->mode == NL80211_IFTYPE_STATION) ++ SYS_WARN(xradio_upload_qosnull(priv)); ++ ++ if (hw_priv->is_BT_Present) ++ SYS_WARN(xradio_set_btcoexinfo(priv)); ++#if 0 ++ /* It's better to override internal TX rete; otherwise ++ * device sends RTS at too high rate. However device ++ * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a ++ * good choice for RTS/CTS, but that means PS poll ++ * will be sent at the same rate - impact on link ++ * budget. Not sure what is better.. */ ++ ++ /* Update: internal rate selection algorythm is not ++ * bad: if device is not receiving CTS at high rate, ++ * it drops RTS rate. ++ * So, conclusion: if-0 the code. Keep code just for ++ * information: ++ * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */ ++ ++ /* ~3 is a bug in device: RTS/CTS is not working at ++ * low rates */ ++ __le32 internal_tx_rate = __cpu_to_le32( ++ __ffs(priv->association_mode.basicRateSet & ~3)); ++ SYS_WARN(wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, ++ &internal_tx_rate,sizeof(internal_tx_rate))); ++#endif ++ } else { ++ memset(&priv->association_mode, 0, sizeof(priv->association_mode)); ++ memset(&priv->bss_params, 0, sizeof(priv->bss_params)); ++ } ++ } ++ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { ++ u32 prev_erp_info = priv->erp_info; ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) { ++ if (info->use_cts_prot) ++ priv->erp_info |= WLAN_ERP_USE_PROTECTION; ++ else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) ++ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; ++ ++ if (prev_erp_info != priv->erp_info) ++ queue_delayed_work(hw_priv->workqueue, &priv->set_cts_work, 0*HZ); ++ } ++ } ++ ++ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { ++ __le32 slot_time = info->use_short_slot ? __cpu_to_le32(9) : __cpu_to_le32(20); ++ ap_printk(XRADIO_DBG_MSG, "[STA] Slot time :%d us.\n", __le32_to_cpu(slot_time)); ++ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_SLOT_TIME, &slot_time, ++ sizeof(slot_time), priv->if_id)); ++ } ++ if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { ++ struct wsm_rcpi_rssi_threshold threshold = { ++ .rollingAverageCount = 8, ++ }; ++ ++#if 0 ++ /* For verification purposes */ ++ info->cqm_rssi_thold = -50; ++ info->cqm_rssi_hyst = 4; ++#endif /* 0 */ ++ ++ ap_printk(XRADIO_DBG_NIY, "[CQM] RSSI threshold subscribe: %d(+-%d)\n", ++ info->cqm_rssi_thold, info->cqm_rssi_hyst); ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ priv->cqm_rssi_thold = info->cqm_rssi_thold; ++ priv->cqm_rssi_hyst = info->cqm_rssi_hyst; ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ ++ if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { ++ /* RSSI subscription enabled */ ++ /* TODO: It's not a correct way of setting threshold. ++ * Upper and lower must be set equal here and adjusted ++ * in callback. However current implementation is much ++ * more relaible and stable. */ ++ if (priv->cqm_use_rssi) { ++ threshold.upperThreshold = info->cqm_rssi_thold + info->cqm_rssi_hyst; ++ threshold.lowerThreshold = info->cqm_rssi_thold; ++ } else { ++ /* convert RSSI to RCPI, RCPI = (RSSI + 110) * 2 */ ++ threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110)<<1; ++ threshold.lowerThreshold = (info->cqm_rssi_thold + 110)<<1; ++ } ++ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; ++ } else { ++ /* There is a bug in FW, see sta.c. We have to enable ++ * dummy subscription to get correct RSSI values. */ ++ threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE | ++ WSM_RCPI_RSSI_DONT_USE_UPPER | ++ WSM_RCPI_RSSI_DONT_USE_LOWER; ++ } ++ SYS_WARN(wsm_set_rcpi_rssi_threshold(hw_priv, &threshold, priv->if_id)); ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; ++ //priv->cqm_tx_failure_count = 0; ++ ++ //if (priv->cqm_beacon_loss_count != info->cqm_beacon_miss_thold) { ++ // priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold; ++ // priv->bss_params.beaconLostCount = (priv->cqm_beacon_loss_count? ++ // priv->cqm_beacon_loss_count : priv->cqm_link_loss_count); ++ /* Make sure we are associated before sending ++ * set_bss_params to firmware */ ++ if (priv->bss_params.aid) { ++ SYS_WARN(wsm_set_bss_params(hw_priv, &priv->bss_params, priv->if_id)); ++ priv->setbssparams_done = true; ++ } ++ //} ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ } ++ /* ++ * in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have ++ * BSS_CHANGED_PS and BSS_CHANGED_RETRY_LIMITS enum value. ++ */ ++#if 0 ++ if (changed & BSS_CHANGED_PS) { ++ if (info->ps_enabled == false) ++ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; ++ else if (info->dynamic_ps_timeout <= 0) ++ priv->powersave_mode.pmMode = WSM_PSM_PS; ++ else ++ priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; ++ ++ ap_printk(XRADIO_DBG_MSG, "[STA] Aid: %d, Joined: %s, Powersave: %s\n", ++ priv->bss_params.aid, ++ priv->join_status == XRADIO_JOIN_STATUS_STA ? "yes" : "no", ++ (priv->powersave_mode.pmMode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : ++ priv->powersave_mode.pmMode == WSM_PSM_PS ? "WSM_PSM_PS" : ++ priv->powersave_mode.pmMode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : ++ "UNKNOWN")); ++ ++ /* Firmware requires that value for this 1-byte field must ++ * be specified in units of 500us. Values above the 128ms ++ * threshold are not supported. */ ++ if (info->dynamic_ps_timeout >= 0x80) ++ priv->powersave_mode.fastPsmIdlePeriod = 0xFF; ++ else ++ priv->powersave_mode.fastPsmIdlePeriod = info->dynamic_ps_timeout << 1; ++ ap_printk(XRADIO_DBG_NIY, "[STA]CHANGED_PS fastPsmIdle=%d, apPsmChange=%d\n", ++ priv->powersave_mode.fastPsmIdlePeriod, ++ priv->powersave_mode.apPsmChangePeriod); ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA && priv->bss_params.aid && ++ priv->setbssparams_done && priv->filter4.enable) ++ xradio_set_pm(priv, &priv->powersave_mode); ++ else ++ priv->power_set_true = 1; ++ } ++ ++ if (changed & BSS_CHANGED_RETRY_LIMITS) { ++ ap_printk(XRADIO_DBG_NIY, "Retry limits: %d (long), %d (short).\n", ++ info->retry_long, info->retry_short); ++ spin_lock_bh(&hw_priv->tx_policy_cache.lock); ++ /*TODO:COMBO: for now it's still handled per hw and kept ++ * in xradio_common */ ++ hw_priv->long_frame_max_tx_count = info->retry_long; ++ hw_priv->short_frame_max_tx_count = ++ (info->retry_short < 0x0F ? info->retry_short : 0x0F); ++ hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count; ++ spin_unlock_bh(&hw_priv->tx_policy_cache.lock); ++ /* TBD: I think we don't need tx_policy_force_upload(). ++ * Outdated policies will leave cache in a normal way. */ ++ /* SYS_WARN(tx_policy_force_upload(priv)); */ ++ } ++#endif ++ /*in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have BSS_CHANGED_P2P_PS enum value*/ ++#if 0 ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ if (changed & BSS_CHANGED_P2P_PS) { ++ struct wsm_p2p_ps_modeinfo *modeinfo; ++ modeinfo = &priv->p2p_ps_modeinfo; ++ ap_printk(XRADIO_DBG_NIY, "[AP] BSS_CHANGED_P2P_PS\n"); ++ ap_printk(XRADIO_DBG_NIY, "[AP] Legacy PS: %d for AID %d in %d mode.\n", ++ info->p2p_ps.legacy_ps, priv->bss_params.aid, priv->join_status); ++ ++ if (info->p2p_ps.legacy_ps >= 0) { ++ if (info->p2p_ps.legacy_ps > 0) ++ priv->powersave_mode.pmMode = WSM_PSM_PS; ++ else ++ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; ++ ++ if(info->p2p_ps.ctwindow && info->p2p_ps.opp_ps) ++ priv->powersave_mode.pmMode = WSM_PSM_PS; ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) ++ xradio_set_pm(priv, &priv->powersave_mode); ++ } ++ ++ ap_printk(XRADIO_DBG_MSG, "[AP] CTWindow: %d\n", info->p2p_ps.ctwindow); ++ if (info->p2p_ps.ctwindow >= 128) ++ modeinfo->oppPsCTWindow = 127; ++ else if (info->p2p_ps.ctwindow >= 0) ++ modeinfo->oppPsCTWindow = info->p2p_ps.ctwindow; ++ ++ ap_printk(XRADIO_DBG_MSG, "[AP] Opportunistic: %d\n", info->p2p_ps.opp_ps); ++ switch (info->p2p_ps.opp_ps) { ++ case 0: ++ modeinfo->oppPsCTWindow &= ~(BIT(7)); ++ break; ++ case 1: ++ modeinfo->oppPsCTWindow |= BIT(7); ++ break; ++ default: ++ break; ++ } ++ ++ ap_printk(XRADIO_DBG_MSG, "[AP] NOA: %d, %d, %d, %d\n", ++ info->p2p_ps.count, info->p2p_ps.start, ++ info->p2p_ps.duration, info->p2p_ps.interval); ++ /* Notice of Absence */ ++ modeinfo->count = info->p2p_ps.count; ++ ++ if (info->p2p_ps.count) { ++ /* In case P2P_GO we need some extra time to be sure ++ * we will update beacon/probe_resp IEs correctly */ ++#define NOA_DELAY_START_MS 300 ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ modeinfo->startTime = __cpu_to_le32(info->p2p_ps.start + NOA_DELAY_START_MS); ++ else ++ modeinfo->startTime = __cpu_to_le32(info->p2p_ps.start); ++ modeinfo->duration = __cpu_to_le32(info->p2p_ps.duration); ++ modeinfo->interval = __cpu_to_le32(info->p2p_ps.interval); ++ modeinfo->dtimCount = 1; ++ modeinfo->reserved = 0; ++ } else { ++ modeinfo->dtimCount = 0; ++ modeinfo->startTime = 0; ++ modeinfo->reserved = 0; ++ modeinfo->duration = 0; ++ modeinfo->interval = 0; ++ } ++ ++#if defined(CONFIG_XRADIO_DEBUG) ++ print_hex_dump_bytes("p2p_set_ps_modeinfo: ", DUMP_PREFIX_NONE, ++ (u8 *)modeinfo, sizeof(*modeinfo)); ++#endif /* CONFIG_XRADIO_DEBUG */ ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA || ++ priv->join_status == XRADIO_JOIN_STATUS_AP) { ++ SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, modeinfo, priv->if_id)); ++ } ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ /* Temporary solution while firmware don't support NOA change ++ * notification yet */ ++ xradio_notify_noa(priv, 10); ++#endif ++ } ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++#endif ++ mutex_unlock(&hw_priv->conf_mutex); ++} ++ ++void xradio_multicast_start_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, multicast_start_work); ++ long tmo = priv->join_dtim_period * (priv->beacon_int + 20) * HZ / 1024; ++ ++ cancel_work_sync(&priv->multicast_stop_work); ++ if (!priv->aid0_bit_set) { ++ wsm_lock_tx(priv->hw_priv); ++ xradio_set_tim_impl(priv, true); ++ priv->aid0_bit_set = true; ++ mod_timer(&priv->mcast_timeout, jiffies + tmo); ++ wsm_unlock_tx(priv->hw_priv); ++ } ++} ++ ++void xradio_multicast_stop_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, multicast_stop_work); ++ ++ if (priv->aid0_bit_set) { ++ del_timer_sync(&priv->mcast_timeout); ++ wsm_lock_tx(priv->hw_priv); ++ priv->aid0_bit_set = false; ++ xradio_set_tim_impl(priv, false); ++ wsm_unlock_tx(priv->hw_priv); ++ } ++} ++ ++void xradio_mcast_timeout(unsigned long arg) ++{ ++ struct xradio_vif *priv = (struct xradio_vif *)arg; ++ ++ ap_printk(XRADIO_DBG_WARN, "Multicast delivery timeout.\n"); ++ spin_lock_bh(&priv->ps_state_lock); ++ priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; ++ if (priv->tx_multicast) ++ xradio_bh_wakeup(xrwl_vifpriv_to_hwpriv(priv)); ++ spin_unlock_bh(&priv->ps_state_lock); ++} ++ ++int xradio_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ struct ieee80211_ampdu_params *params) ++{ ++ /* Aggregation is implemented fully in firmware, ++ * including block ack negotiation. ++ * In case of AMPDU aggregation in RX direction ++ * re-ordering of packets takes place on host. mac80211 ++ * needs the ADDBA Request to setup reodering.mac80211 also ++ * sends ADDBA Response which is discarded in the driver as ++ * FW generates the ADDBA Response on its own.*/ ++ int ret; ++ ++ switch (params->action) { ++ case IEEE80211_AMPDU_RX_START: ++ case IEEE80211_AMPDU_RX_STOP: ++ /* Just return OK to mac80211 */ ++ ret = 0; ++ break; ++ default: ++ ret = -ENOTSUPP; ++ } ++ return ret; ++} ++ ++/* ******************************************************************** */ ++/* WSM callback */ ++void xradio_suspend_resume(struct xradio_vif *priv, struct wsm_suspend_resume *arg) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++#if 0 ++ ap_printk(XRADIO_DBG_MSG, "[AP] %s: %s\n", ++ arg->stop ? "stop" : "start", ++ arg->multicast ? "broadcast" : "unicast"); ++#endif ++ if (arg->multicast) { ++ bool cancel_tmo = false; ++ spin_lock_bh(&priv->ps_state_lock); ++ if (arg->stop) { ++ priv->tx_multicast = false; ++ } else { ++ /* Firmware sends this indication every DTIM if there ++ * is a STA in powersave connected. There is no reason ++ * to suspend, following wakeup will consume much more ++ * power than it could be saved. */ ++ xradio_pm_stay_awake(&hw_priv->pm_state, (priv->join_dtim_period * ++ (priv->beacon_int + 20) * HZ / 1024)); ++ priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; ++ if (priv->tx_multicast) { ++ cancel_tmo = true; ++ xradio_bh_wakeup(hw_priv); ++ } ++ } ++ spin_unlock_bh(&priv->ps_state_lock); ++ if (cancel_tmo) ++ del_timer_sync(&priv->mcast_timeout); ++ } else { ++ spin_lock_bh(&priv->ps_state_lock); ++ xradio_ps_notify(priv, arg->link_id, arg->stop); ++ spin_unlock_bh(&priv->ps_state_lock); ++ if (!arg->stop) ++ xradio_bh_wakeup(hw_priv); ++ } ++ return; ++} ++ ++/* ******************************************************************** */ ++/* AP privates */ ++ ++static int xradio_upload_beacon(struct xradio_vif *priv) ++{ ++ int ret = 0; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_BEACON, ++ }; ++ struct ieee80211_mgmt *mgmt; ++ u8 *erp_inf, *ies, *ht_oper; ++ u32 ies_len; ++ ++ if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ) ++ frame.rate = WSM_TRANSMIT_RATE_6; ++ ++ frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); ++ if (SYS_WARN(!frame.skb)) ++ return -ENOMEM; ++ ++ mgmt = (void *)frame.skb->data; ++ ies = mgmt->u.beacon.variable; ++ ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt); ++ ++ ht_oper = (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len); ++ if (ht_oper) { ++ /* Enable RIFS*/ ++ ht_oper[3] |= 8; ++ } ++ ++ erp_inf = (u8 *)cfg80211_find_ie(WLAN_EID_ERP_INFO, ies, ies_len); ++ if (erp_inf) { ++ if (erp_inf[ERP_INFO_BYTE_OFFSET] ++ & WLAN_ERP_BARKER_PREAMBLE) ++ priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; ++ else ++ priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; ++ ++ if (erp_inf[ERP_INFO_BYTE_OFFSET] ++ & WLAN_ERP_NON_ERP_PRESENT) { ++ priv->erp_info |= WLAN_ERP_USE_PROTECTION; ++ priv->erp_info |= WLAN_ERP_NON_ERP_PRESENT; ++ } else { ++ priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; ++ priv->erp_info &= ~WLAN_ERP_NON_ERP_PRESENT; ++ } ++ } ++ ++#ifdef HIDDEN_SSID ++ if (priv->hidden_ssid) { ++ u8 *ssid_ie; ++ u8 ssid_len; ++ ++ ap_printk(XRADIO_DBG_NIY, "%s: hidden_ssid set\n", __func__); ++ ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); ++ SYS_WARN(!ssid_ie); ++ ssid_len = ssid_ie[1]; ++ if (ssid_len) { ++ ap_printk(XRADIO_DBG_MSG, "hidden_ssid with zero content ssid\n"); ++ ssid_ie[1] = 0; ++ memmove(ssid_ie + 2, ssid_ie + 2 + ssid_len, ++ (ies + ies_len - ++ (ssid_ie + 2 + ssid_len))); ++ frame.skb->len -= ssid_len; ++ } else { ++ ap_printk(XRADIO_DBG_WARN, "hidden ssid with ssid len 0 not supported"); ++ dev_kfree_skb(frame.skb); ++ return -1; ++ } ++ } ++#endif ++ ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); ++ if (!ret) { ++#ifdef PROBE_RESP_EXTRA_IE ++ ret = xradio_upload_proberesp(priv); ++#else ++ /* TODO: Distille probe resp; remove TIM ++ * and other beacon-specific IEs */ ++ *(__le16 *)frame.skb->data = __cpu_to_le16( ++ IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); ++ frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; ++ /* TODO: Ideally probe response template should separately ++ configured by supplicant through openmac. This is a ++ temporary work-around known to fail p2p group info ++ attribute related tests ++ */ ++ if (0 /* priv->vif->p2p */) ++ ret = wsm_set_probe_responder(priv, true); ++ else { ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); ++ SYS_WARN(wsm_set_probe_responder(priv, false)); ++ } ++#endif ++ } ++ dev_kfree_skb(frame.skb); ++ ++ return ret; ++} ++ ++#ifdef PROBE_RESP_EXTRA_IE ++static int xradio_upload_proberesp(struct xradio_vif *priv) ++{ ++ int ret = 0; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE, ++ }; ++#ifdef HIDDEN_SSID ++ u8 *ssid_ie; ++#endif ++ ++ if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ) ++ frame.rate = WSM_TRANSMIT_RATE_6; ++ ++ frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif); ++ if (SYS_WARN(!frame.skb)) ++ return -ENOMEM; ++ ++#ifdef HIDDEN_SSID ++ if (priv->hidden_ssid) { ++ int offset; ++ u8 ssid_len; ++ /* we are assuming beacon from upper layer will always contain ++ zero filled ssid for hidden ap. The beacon shall never have ++ ssid len = 0. ++ */ ++ ++ offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); ++ ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, frame.skb->data + offset, ++ frame.skb->len - offset); ++ ssid_len = ssid_ie[1]; ++ if (ssid_len && (ssid_len == priv->ssid_length)) { ++ memcpy(ssid_ie + 2, priv->ssid, ssid_len); ++ } else { ++ ap_printk(XRADIO_DBG_ERROR, "%s: hidden ssid with mismatched ssid_len %d\n", ++ __func__, ssid_len); ++ dev_kfree_skb(frame.skb); ++ return -1; ++ } ++ } ++#endif ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); ++ SYS_WARN(wsm_set_probe_responder(priv, false)); ++ ++ dev_kfree_skb(frame.skb); ++ ++ return ret; ++} ++#endif ++ ++static int xradio_upload_pspoll(struct xradio_vif *priv) ++{ ++ int ret = 0; ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_PS_POLL, ++ .rate = 0xFF, ++ }; ++ ++ frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); ++ if (SYS_WARN(!frame.skb)) ++ return -ENOMEM; ++ ret = wsm_set_template_frame(xrwl_vifpriv_to_hwpriv(priv), &frame, priv->if_id); ++ dev_kfree_skb(frame.skb); ++ return ret; ++} ++ ++static int xradio_upload_null(struct xradio_vif *priv) ++{ ++ int ret = 0; ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_NULL, ++ .rate = 0xFF, ++ }; ++ ++ frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); ++ if (SYS_WARN(!frame.skb)) ++ return -ENOMEM; ++ ++ ret = wsm_set_template_frame(xrwl_vifpriv_to_hwpriv(priv), &frame, priv->if_id); ++ dev_kfree_skb(frame.skb); ++ return ret; ++} ++ ++static int xradio_upload_qosnull(struct xradio_vif *priv) ++{ ++ struct ieee80211_qos_hdr* qos_null_template; ++ struct sk_buff* skb; ++ int ret = 0; ++ struct xradio_common *hw_priv =xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_QOS_NULL, ++ .rate = 0xFF, ++ }; ++ if (!hw_priv) ++ ap_printk(XRADIO_DBG_ERROR,"%s: Cannot find xradio_common pointer!\n",__FUNCTION__); ++ /*set qos template*/ ++ skb = dev_alloc_skb(hw_priv->hw->extra_tx_headroom + sizeof(struct ieee80211_qos_hdr)); ++ if (!skb) { ++ ap_printk(XRADIO_DBG_ERROR,"%s: failed to allocate buffer for qos nullfunc template!\n",__FUNCTION__); ++ return -1; ++ } ++ skb_reserve(skb, hw_priv->hw->extra_tx_headroom); ++ qos_null_template = (struct ieee80211_qos_hdr *)skb_put(skb,sizeof(struct ieee80211_qos_hdr)); ++ memset(qos_null_template, 0, sizeof(struct ieee80211_qos_hdr)); ++ memcpy(qos_null_template->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); ++ memcpy(qos_null_template->addr2, priv->vif->addr, ETH_ALEN); ++ memcpy(qos_null_template->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); ++ qos_null_template->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | ++ IEEE80211_STYPE_QOS_NULLFUNC | ++ IEEE80211_FCTL_TODS); ++ frame.skb = skb; ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); ++ dev_kfree_skb(frame.skb); ++ return ret; ++} ++ ++/* This API is nolonegr present in WSC */ ++#if 0 ++static int xradio_enable_beaconing(struct xradio_vif *priv, ++ bool enable) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_beacon_transmit transmit = { ++ .enableBeaconing = enable, ++ }; ++ ++ return wsm_beacon_transmit(hw_priv, &transmit, priv->if_id); ++} ++#endif ++ ++static int xradio_start_ap(struct xradio_vif *priv) ++{ ++ int ret; ++#ifndef HIDDEN_SSID ++ const u8 *ssidie; ++ struct sk_buff *skb; ++ int offset; ++#endif ++ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_start start = { ++ .mode = priv->vif->p2p ? WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, ++ /* TODO:COMBO:Change once mac80211 support is available */ ++ .band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ? ++ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, ++ .channelNumber = hw_priv->channel->hw_value, ++ .beaconInterval = conf->beacon_int, ++ .DTIMPeriod = conf->dtim_period, ++ .preambleType = conf->use_short_preamble ? ++ WSM_JOIN_PREAMBLE_SHORT :WSM_JOIN_PREAMBLE_LONG, ++ .probeDelay = 100, ++ .basicRateSet = xradio_rate_mask_to_wsm(hw_priv, conf->basic_rates), ++#ifdef P2P_MULTIVIF ++ .CTWindow = priv->vif->p2p ? 0xFFFFFFFF : 0, ++#endif ++ }; ++ struct wsm_operational_mode mode = { ++ .power_mode = wsm_power_mode_quiescent, ++ .disableMoreFlagUsage = true, ++ }; ++ ++#ifdef TES_P2P_000B_EXTEND_INACTIVITY_CNT ++ ///w, TES_P2P_000B WorkAround: ++ ///w, when inactivity count of a peer device is zero, ++ ///w, which will reset while receiving a peer device frame, ++ ///w, firmware will disconnect with it. ++ ///w, due to some reason, such as scan/phy error, we miss these frame. ++ ///w, then we can't keep connection with peer device. ++ ///w, we set the min_inactivity value to large as WorkAround. ++ //min_inactivity be modified to 20, yangfh. ++ struct wsm_inactivity inactivity = { ++ .min_inactivity = 20, ++ .max_inactivity = 10, ++ }; ++#else ++ struct wsm_inactivity inactivity = { ++ .min_inactivity = 9, ++ .max_inactivity = 1, ++ }; ++#endif ++ ++ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ if (priv->if_id) ++ start.mode |= WSM_FLAG_MAC_INSTANCE_1; ++ else ++ start.mode &= ~WSM_FLAG_MAC_INSTANCE_1; ++ ++ hw_priv->connected_sta_cnt = 0; ++ ++#ifndef HIDDEN_SSID ++ /* Get SSID */ ++ skb = ieee80211_beacon_get(priv->hw, priv->vif); ++ if (SYS_WARN(!skb)) { ++ ap_printk(XRADIO_DBG_ERROR,"%s, ieee80211_beacon_get failed\n", __func__); ++ return -ENOMEM; ++ } ++ ++ offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); ++ ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, skb->len - offset); ++ ++ memset(priv->ssid, 0, sizeof(priv->ssid)); ++ if (ssidie) { ++ priv->ssid_length = ssidie[1]; ++ if (SYS_WARN(priv->ssid_length > sizeof(priv->ssid))) ++ priv->ssid_length = sizeof(priv->ssid); ++ memcpy(priv->ssid, &ssidie[2], priv->ssid_length); ++ } else { ++ priv->ssid_length = 0; ++ } ++ dev_kfree_skb(skb); ++#endif ++ ++ priv->beacon_int = conf->beacon_int; ++ priv->join_dtim_period = conf->dtim_period; ++ memset(&priv->last_tim[0], 0, sizeof(priv->last_tim)); //yangfh ++ ++ start.ssidLength = priv->ssid_length; ++ memcpy(&start.ssid[0], priv->ssid, start.ssidLength); ++ ++ memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); ++ ++ ap_printk(XRADIO_DBG_NIY, "[AP] ch: %d(%d), bcn: %d(%d)," ++ "bss_rate: 0x%.8X, ssid: %.*s.\n", ++ start.channelNumber, start.band, ++ start.beaconInterval, start.DTIMPeriod, ++ start.basicRateSet, start.ssidLength, start.ssid); ++ ret = SYS_WARN(wsm_start(hw_priv, &start, priv->if_id)); ++ ++ if (!ret && priv->vif->p2p) { ++ ap_printk(XRADIO_DBG_NIY,"[AP] Setting p2p powersave configuration.\n"); ++ SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, ++ &priv->p2p_ps_modeinfo, priv->if_id)); ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //xradio_notify_noa(priv, XRADIO_NOA_NOTIFICATION_DELAY); ++#endif ++ } ++ ++ /*Set Inactivity time*/ ++ if(!(strstr(&start.ssid[0], "6.1.12"))) { ++ wsm_set_inactivity(hw_priv, &inactivity, priv->if_id); ++ } ++ if (!ret) { ++#ifndef AP_AGGREGATE_FW_FIX ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, ++ XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, priv->if_id)); ++#else ++ if ((priv->if_id ==1) && !hw_priv->is_go_thru_go_neg) ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, //modified for WFD by yangfh ++ XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, priv->if_id)); ++ else ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, ++ XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, priv->if_id)); ++#endif ++ priv->join_status = XRADIO_JOIN_STATUS_AP; ++ /* xradio_update_filtering(priv); */ ++ } ++ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; ++ ap_printk(XRADIO_DBG_WARN, "vif%d, AP/GO mode THROTTLE=%d\n", priv->if_id, ++ priv->if_id==0?hw_priv->vif0_throttle:hw_priv->vif1_throttle); ++ return ret; ++} ++ ++static int xradio_update_beaconing(struct xradio_vif *priv) ++{ ++ struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_reset reset = { ++ .link_id = 0, ++ .reset_statistics = true, ++ }; ++ ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ if (priv->mode == NL80211_IFTYPE_AP) { ++ /* TODO: check if changed channel, band */ ++ if (priv->join_status != XRADIO_JOIN_STATUS_AP || ++ priv->beacon_int != conf->beacon_int) { ++ ap_printk(XRADIO_DBG_WARN, "ap restarting!\n"); ++ wsm_lock_tx(hw_priv); ++ if (priv->join_status != XRADIO_JOIN_STATUS_PASSIVE) ++ SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id)); ++ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; ++ SYS_WARN(xradio_start_ap(priv)); ++ wsm_unlock_tx(hw_priv); ++ } else ++ ap_printk(XRADIO_DBG_NIY, "ap started join_status: %d\n", priv->join_status); ++ } ++ return 0; ++} ++ ++int xradio_find_link_id(struct xradio_vif *priv, const u8 *mac) ++{ ++ int i, ret = 0; ++ spin_lock_bh(&priv->ps_state_lock); ++ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { ++ if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && ++ priv->link_id_db[i].status) { ++ priv->link_id_db[i].timestamp = jiffies; ++ ret = i + 1; ++ break; ++ } ++ } ++ spin_unlock_bh(&priv->ps_state_lock); ++ return ret; ++} ++ ++int xradio_alloc_link_id(struct xradio_vif *priv, const u8 *mac) ++{ ++ int i, ret = 0; ++ unsigned long max_inactivity = 0; ++ unsigned long now = jiffies; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ spin_lock_bh(&priv->ps_state_lock); ++ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { ++ if (!priv->link_id_db[i].status) { ++ ret = i + 1; ++ break; ++ } else if (priv->link_id_db[i].status != XRADIO_LINK_HARD && ++ !hw_priv->tx_queue_stats.link_map_cache[i + 1]) { ++ unsigned long inactivity = now - priv->link_id_db[i].timestamp; ++ if (inactivity < max_inactivity) ++ continue; ++ max_inactivity = inactivity; ++ ret = i + 1; ++ } ++ } ++ if (ret) { ++ struct xradio_link_entry *entry = &priv->link_id_db[ret - 1]; ++ ap_printk(XRADIO_DBG_NIY, "STA added, link_id: %d\n", ret); ++ entry->status = XRADIO_LINK_RESERVE; ++ memcpy(&entry->mac, mac, ETH_ALEN); ++ memset(&entry->buffered, 0, XRADIO_MAX_TID); ++ skb_queue_head_init(&entry->rx_queue); ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ } else { ++ ap_printk(XRADIO_DBG_WARN, "Early: no more link IDs available.\n"); ++ } ++ ++ spin_unlock_bh(&priv->ps_state_lock); ++ return ret; ++} ++ ++void xradio_link_id_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = container_of(work, struct xradio_vif, link_id_work); ++ struct xradio_common *hw_priv = priv->hw_priv; ++ ++ wsm_flush_tx(hw_priv); ++ xradio_link_id_gc_work(&priv->link_id_gc_work.work); ++ wsm_unlock_tx(hw_priv); ++} ++ ++void xradio_link_id_gc_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, link_id_gc_work.work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_map_link map_link = { ++ .link_id = 0, ++ }; ++ unsigned long now = jiffies; ++ unsigned long next_gc = -1; ++ long ttl; ++ bool need_reset; ++ u32 mask; ++ int i; ++ ++ if (priv->join_status != XRADIO_JOIN_STATUS_AP) ++ return; ++ ++ wsm_lock_tx(hw_priv); ++ spin_lock_bh(&priv->ps_state_lock); ++ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { ++ need_reset = false; ++ mask = BIT(i + 1); ++ if (priv->link_id_db[i].status == XRADIO_LINK_RESERVE || ++ (priv->link_id_db[i].status == XRADIO_LINK_HARD && ++ !(priv->link_id_map & mask))) { ++ if (priv->link_id_map & mask) { ++ priv->sta_asleep_mask &= ~mask; ++ priv->pspoll_mask &= ~mask; ++ need_reset = true; ++ } ++ priv->link_id_map |= mask; ++ if (priv->link_id_db[i].status != XRADIO_LINK_HARD) ++ priv->link_id_db[i].status = XRADIO_LINK_SOFT; ++ memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN); ++ spin_unlock_bh(&priv->ps_state_lock); ++ if (need_reset) { ++ SYS_WARN(xrwl_unmap_link(priv, i + 1)); ++ } ++ map_link.link_id = i + 1; ++ SYS_WARN(wsm_map_link(hw_priv, &map_link, priv->if_id)); ++ next_gc = min(next_gc, XRADIO_LINK_ID_GC_TIMEOUT); ++ spin_lock_bh(&priv->ps_state_lock); ++ } else if (priv->link_id_db[i].status == XRADIO_LINK_SOFT) { ++ ttl = priv->link_id_db[i].timestamp - now + XRADIO_LINK_ID_GC_TIMEOUT; ++ if (ttl <= 0) { ++ need_reset = true; ++ priv->link_id_db[i].status = XRADIO_LINK_OFF; ++ priv->link_id_map &= ~mask; ++ priv->sta_asleep_mask &= ~mask; ++ priv->pspoll_mask &= ~mask; ++ memset(map_link.mac_addr, 0, ETH_ALEN); ++ spin_unlock_bh(&priv->ps_state_lock); ++ SYS_WARN(xrwl_unmap_link(priv, i + 1)); ++ spin_lock_bh(&priv->ps_state_lock); ++ } else { ++ next_gc = min_t(unsigned long, next_gc, ttl); ++ } ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ } else if (priv->link_id_db[i].status == XRADIO_LINK_RESET || ++ priv->link_id_db[i].status == XRADIO_LINK_RESET_REMAP) { ++ int status = priv->link_id_db[i].status; ++ priv->link_id_db[i].status = XRADIO_LINK_OFF; ++ priv->link_id_db[i].timestamp = now; ++ spin_unlock_bh(&priv->ps_state_lock); ++ SYS_WARN(xrwl_unmap_link(priv, i + 1)); ++ if (status == XRADIO_LINK_RESET_REMAP) { ++ memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN); ++ map_link.link_id = i + 1; ++ SYS_WARN(wsm_map_link(hw_priv, &map_link, priv->if_id)); ++ next_gc = min(next_gc, XRADIO_LINK_ID_GC_TIMEOUT); ++ priv->link_id_db[i].status = priv->link_id_db[i].prev_status; ++ } ++ spin_lock_bh(&priv->ps_state_lock); ++#endif ++ } ++ if (need_reset) { ++ skb_queue_purge(&priv->link_id_db[i].rx_queue); ++ ap_printk(XRADIO_DBG_NIY, "STA removed, link_id: %d\n", i + 1); ++ } ++ } ++ spin_unlock_bh(&priv->ps_state_lock); ++ if (next_gc != -1) ++ queue_delayed_work(hw_priv->workqueue, &priv->link_id_gc_work, next_gc); ++ wsm_unlock_tx(hw_priv); ++} ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++#if 0 ++void xradio_notify_noa(struct xradio_vif *priv, int delay) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct cfg80211_p2p_ps p2p_ps = {0}; ++ struct wsm_p2p_ps_modeinfo *modeinfo; ++ modeinfo = &priv->p2p_ps_modeinfo; ++ ++ if (priv->join_status != XRADIO_JOIN_STATUS_AP) ++ return; ++ ++ if (delay) ++ msleep(delay); ++ ++ if (!SYS_WARN(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo))) { ++#if defined(CONFIG_XRADIO_DEBUG) ++ print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ", DUMP_PREFIX_NONE, ++ (u8 *)modeinfo, sizeof(*modeinfo)); ++#endif /* CONFIG_XRADIO_DEBUG */ ++ p2p_ps.opp_ps = !!(modeinfo->oppPsCTWindow & BIT(7)); ++ p2p_ps.ctwindow = modeinfo->oppPsCTWindow & (~BIT(7)); ++ p2p_ps.count = modeinfo->count; ++ p2p_ps.start = __le32_to_cpu(modeinfo->startTime); ++ p2p_ps.duration = __le32_to_cpu(modeinfo->duration); ++ p2p_ps.interval = __le32_to_cpu(modeinfo->interval); ++ p2p_ps.index = modeinfo->reserved; ++ ++ ieee80211_p2p_noa_notify(priv->vif, &p2p_ps, GFP_KERNEL); ++ } ++} ++#endif ++#endif ++int xrwl_unmap_link(struct xradio_vif *priv, int link_id) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ int ret = 0; ++ struct wsm_operational_mode mode = { ++ .power_mode = wsm_power_mode_quiescent, ++ .disableMoreFlagUsage = true, ++ }; ++ ++ if (is_hardware_xradio(hw_priv)) { ++ struct wsm_map_link maplink = { ++ .link_id = link_id, ++ .unmap = true, ++ }; ++ if (link_id) ++ memcpy(&maplink.mac_addr[0], priv->link_id_db[link_id - 1].mac, ETH_ALEN); ++ return wsm_map_link(hw_priv, &maplink, priv->if_id); ++ } else { ++ struct wsm_reset reset = { ++ .link_id = link_id, ++ .reset_statistics = true, ++ }; ++ ret = wsm_reset(hw_priv, &reset, priv->if_id); ++ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); ++ return ret; ++ } ++} ++#ifdef AP_HT_CAP_UPDATE ++void xradio_ht_oper_update_work(struct work_struct *work) ++{ ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *mgmt; ++ u8 *ht_oper, *ies; ++ u32 ies_len; ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, ht_oper_update_work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_update_ie update_ie = { ++ .what = WSM_UPDATE_IE_BEACON, ++ .count = 1, ++ }; ++ ++ skb = ieee80211_beacon_get(priv->hw, priv->vif); ++ if (SYS_WARN(!skb)) ++ return; ++ ++ mgmt = (void *)skb->data; ++ ies = mgmt->u.beacon.variable; ++ ies_len = skb->len - (u32)(ies - (u8 *)mgmt); ++ ht_oper= (u8 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, ies, ies_len); ++ if(ht_oper && priv->ht_oper == HT_INFO_MASK) { ++ ht_oper[HT_INFO_OFFSET] |= 0x11; ++ update_ie.ies = ht_oper; ++ update_ie.length = HT_INFO_IE_LEN; ++ SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); ++ } ++ dev_kfree_skb(skb); ++} ++#endif +diff --git a/drivers/net/wireless/xradio/ap.h b/drivers/net/wireless/xradio/ap.h +new file mode 100644 +index 00000000..ca05bffe +--- /dev/null ++++ b/drivers/net/wireless/xradio/ap.h +@@ -0,0 +1,63 @@ ++/* ++ * STA and AP APIs for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef AP_H_INCLUDED ++#define AP_H_INCLUDED ++ ++#define XRADIO_NOA_NOTIFICATION_DELAY 10 ++ ++#ifdef AP_HT_CAP_UPDATE ++#define HT_INFO_OFFSET 4 ++#define HT_INFO_MASK 0x0011 ++#define HT_INFO_IE_LEN 22 ++#endif ++ ++int xradio_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, ++ bool set); ++int xradio_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta); ++int xradio_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ struct ieee80211_sta *sta); ++void xradio_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, ++ enum sta_notify_cmd notify_cmd, ++ struct ieee80211_sta *sta); ++void xradio_bss_info_changed(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif, ++ struct ieee80211_bss_conf *info, ++ u32 changed); ++int xradio_ampdu_action(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_ampdu_params *params); ++/* enum ieee80211_ampdu_mlme_action action, ++ struct ieee80211_sta *sta, u16 tid, u16 *ssn, ++ u8 buf_size);*/ ++ ++void xradio_suspend_resume(struct xradio_vif *priv, ++ struct wsm_suspend_resume *arg); ++void xradio_set_tim_work(struct work_struct *work); ++void xradio_set_cts_work(struct work_struct *work); ++void xradio_multicast_start_work(struct work_struct *work); ++void xradio_multicast_stop_work(struct work_struct *work); ++void xradio_mcast_timeout(unsigned long arg); ++int xradio_find_link_id(struct xradio_vif *priv, const u8 *mac); ++int xradio_alloc_link_id(struct xradio_vif *priv, const u8 *mac); ++void xradio_link_id_work(struct work_struct *work); ++void xradio_link_id_gc_work(struct work_struct *work); ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++/*in linux3.4 mac,it does't have the noa pass*/ ++//void xradio_notify_noa(struct xradio_vif *priv, int delay); ++#endif ++int xrwl_unmap_link(struct xradio_vif *priv, int link_id); ++#ifdef AP_HT_CAP_UPDATE ++void xradio_ht_oper_update_work(struct work_struct *work); ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/xradio/bh.c b/drivers/net/wireless/xradio/bh.c +new file mode 100644 +index 00000000..220f85a2 +--- /dev/null ++++ b/drivers/net/wireless/xradio/bh.c +@@ -0,0 +1,880 @@ ++/* ++ * Data Transmission thread implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++ ++#include "xradio.h" ++#include "bh.h" ++#include "hwio.h" ++#include "wsm.h" ++#include "sdio.h" ++ ++/* TODO: Verify these numbers with WSM specification. */ ++#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) ++/* an SPI message cannot be bigger than (2"12-1)*2 bytes ++ * "*2" to cvt to bytes */ ++#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) ++#define PIGGYBACK_CTRL_REG (2) ++#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) ++ ++/* Suspend state privates */ ++enum xradio_bh_pm_state { ++ XRADIO_BH_RESUMED = 0, ++ XRADIO_BH_SUSPEND, ++ XRADIO_BH_SUSPENDED, ++ XRADIO_BH_RESUME, ++}; ++typedef int (*xradio_wsm_handler)(struct xradio_common *hw_priv, u8 *data, size_t size); ++ ++#ifdef MCAST_FWDING ++int wsm_release_buffer_to_fw(struct xradio_vif *priv, int count); ++#endif ++static int xradio_bh(void *arg); ++ ++int xradio_register_bh(struct xradio_common *hw_priv) ++{ ++ int err = 0; ++ struct sched_param param = { .sched_priority = 1 }; ++ ++ SYS_BUG(hw_priv->bh_thread); ++ atomic_set(&hw_priv->bh_tx, 0); ++ atomic_set(&hw_priv->bh_term, 0); ++ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED); ++ hw_priv->buf_id_tx = 0; ++ hw_priv->buf_id_rx = 0; ++ init_waitqueue_head(&hw_priv->bh_wq); ++ init_waitqueue_head(&hw_priv->bh_evt_wq); ++ ++ hw_priv->bh_thread = kthread_create(&xradio_bh, hw_priv, XRADIO_BH_THREAD); ++ if (IS_ERR(hw_priv->bh_thread)) { ++ err = PTR_ERR(hw_priv->bh_thread); ++ hw_priv->bh_thread = NULL; ++ } else { ++ SYS_WARN(sched_setscheduler(hw_priv->bh_thread, SCHED_FIFO, ¶m)); ++#ifdef HAS_PUT_TASK_STRUCT ++ get_task_struct(hw_priv->bh_thread); ++#endif ++ wake_up_process(hw_priv->bh_thread); ++ } ++ return err; ++} ++ ++void xradio_unregister_bh(struct xradio_common *hw_priv) ++{ ++ struct task_struct *thread = hw_priv->bh_thread; ++ ++ if (SYS_WARN(!thread)) ++ return; ++ ++ hw_priv->bh_thread = NULL; ++ kthread_stop(thread); ++#ifdef HAS_PUT_TASK_STRUCT ++ put_task_struct(thread); ++#endif ++ dev_dbg(hw_priv->pdev, "Unregister success.\n"); ++} ++ ++void xradio_irq_handler(struct xradio_common *hw_priv) ++{ ++ xradio_bh_wakeup(hw_priv); ++} ++ ++void xradio_bh_wakeup(struct xradio_common *hw_priv) ++{ ++ atomic_set(&hw_priv->bh_tx, 1); ++ wake_up(&hw_priv->bh_wq); ++} ++ ++int xradio_bh_suspend(struct xradio_common *hw_priv) ++{ ++#ifdef MCAST_FWDING ++ int i =0; ++ struct xradio_vif *priv = NULL; ++#endif ++ ++ if (hw_priv->bh_error) { ++ return -EINVAL; ++ } ++ ++#ifdef MCAST_FWDING ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (!priv) ++ continue; ++ if ( (priv->multicast_filter.enable) ++ && (priv->join_status == XRADIO_JOIN_STATUS_AP) ) { ++ wsm_release_buffer_to_fw(priv, ++ (hw_priv->wsm_caps.numInpChBufs - 1)); ++ break; ++ } ++ } ++#endif ++ ++ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_SUSPEND); ++ wake_up(&hw_priv->bh_wq); ++ return wait_event_timeout(hw_priv->bh_evt_wq, (hw_priv->bh_error || ++ XRADIO_BH_SUSPENDED == atomic_read(&hw_priv->bh_suspend)), ++ 1 * HZ)? 0 : -ETIMEDOUT; ++} ++ ++int xradio_bh_resume(struct xradio_common *hw_priv) ++{ ++#ifdef MCAST_FWDING ++ int ret; ++ int i =0; ++ struct xradio_vif *priv =NULL; ++#endif ++ ++ ++ if (hw_priv->bh_error) { ++ return -EINVAL; ++ } ++ ++ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUME); ++ wake_up(&hw_priv->bh_wq); ++ ++#ifdef MCAST_FWDING ++ ret = wait_event_timeout(hw_priv->bh_evt_wq, (hw_priv->bh_error || ++ XRADIO_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)) ++ ,1 * HZ)? 0 : -ETIMEDOUT; ++ ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (!priv) ++ continue; ++ if ((priv->join_status == XRADIO_JOIN_STATUS_AP) && ++ (priv->multicast_filter.enable)) { ++ u8 count = 0; ++ SYS_WARN(wsm_request_buffer_request(priv, &count)); ++ dev_dbg(hw_priv->pdev, "Reclaim Buff %d \n",count); ++ break; ++ } ++ } ++ ++ return ret; ++#else ++ return wait_event_timeout(hw_priv->bh_evt_wq,hw_priv->bh_error || ++ (XRADIO_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)), ++ 1 * HZ) ? 0 : -ETIMEDOUT; ++#endif ++ ++} ++ ++static inline void wsm_alloc_tx_buffer(struct xradio_common *hw_priv) ++{ ++ ++hw_priv->hw_bufs_used; ++} ++ ++int wsm_release_tx_buffer(struct xradio_common *hw_priv, int count) ++{ ++ int ret = 0; ++ int hw_bufs_used = hw_priv->hw_bufs_used; ++ ++ hw_priv->hw_bufs_used -= count; ++ if (SYS_WARN(hw_priv->hw_bufs_used < 0)) { ++ /* Tx data patch stops when all but one hw buffers are used. ++ So, re-start tx path in case we find hw_bufs_used equals ++ numInputChBufs - 1. ++ */ ++ dev_err(hw_priv->pdev, "hw_bufs_used=%d, count=%d.\n", ++ hw_priv->hw_bufs_used, count); ++ ret = -1; ++ } else if (hw_bufs_used >= (hw_priv->wsm_caps.numInpChBufs - 1)) ++ ret = 1; ++ if (!hw_priv->hw_bufs_used) ++ wake_up(&hw_priv->bh_evt_wq); ++ return ret; ++} ++ ++int wsm_release_vif_tx_buffer(struct xradio_common *hw_priv, int if_id, int count) ++{ ++ int ret = 0; ++ ++ hw_priv->hw_bufs_used_vif[if_id] -= count; ++ if (!hw_priv->hw_bufs_used_vif[if_id]) ++ wake_up(&hw_priv->bh_evt_wq); ++ ++ if (SYS_WARN(hw_priv->hw_bufs_used_vif[if_id] < 0)) ++ ret = -1; ++ return ret; ++} ++#ifdef MCAST_FWDING ++int wsm_release_buffer_to_fw(struct xradio_vif *priv, int count) ++{ ++ int i; ++ u8 flags; ++ struct wsm_buf *buf; ++ size_t buf_len; ++ struct wsm_hdr *wsm; ++ struct xradio_common *hw_priv = priv->hw_priv; ++ ++ if (priv->join_status != XRADIO_JOIN_STATUS_AP) { ++ return 0; ++ } ++ dev_dbg(hw_priv->pdev, "Rel buffer to FW %d, %d\n", count, hw_priv->hw_bufs_used); ++ ++ for (i = 0; i < count; i++) { ++ if ((hw_priv->hw_bufs_used + 1) < hw_priv->wsm_caps.numInpChBufs) { ++ flags = i ? 0: 0x1; ++ ++ wsm_alloc_tx_buffer(hw_priv); ++ buf = &hw_priv->wsm_release_buf[i]; ++ buf_len = buf->data - buf->begin; ++ ++ /* Add sequence number */ ++ wsm = (struct wsm_hdr *)buf->begin; ++ SYS_BUG(buf_len < sizeof(*wsm)); ++ ++ wsm->id &= __cpu_to_le32(~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); ++ wsm->id |= cpu_to_le32(WSM_TX_SEQ(hw_priv->wsm_tx_seq)); ++ ++ dev_dbg(hw_priv->pdev, "REL %d\n", hw_priv->wsm_tx_seq); ++ if (SYS_WARN(xradio_data_write(hw_priv, buf->begin, buf_len))) { ++ break; ++ } ++ hw_priv->buf_released = 1; ++ hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; ++ } else ++ break; ++ } ++ ++ if (i == count) { ++ return 0; ++ } ++ ++ /* Should not be here */ ++ dev_dbg(hw_priv->pdev, "Error, Less HW buf %d,%d.\n", ++ hw_priv->hw_bufs_used, hw_priv->wsm_caps.numInpChBufs); ++ SYS_WARN(1); ++ return -1; ++} ++#endif ++ ++/* reserve a packet for the case dev_alloc_skb failed in bh.*/ ++int xradio_init_resv_skb(struct xradio_common *hw_priv) ++{ ++ int len = (SDIO_BLOCK_SIZE<<2) + WSM_TX_EXTRA_HEADROOM + \ ++ 8 + 12; /* TKIP IV + ICV and MIC */ ++ ++ hw_priv->skb_reserved = dev_alloc_skb(len); ++ if (hw_priv->skb_reserved) { ++ hw_priv->skb_resv_len = len; ++ } else { ++ dev_warn(hw_priv->pdev, "xr_alloc_skb failed(%d)\n", len); ++ } ++ return 0; ++} ++ ++void xradio_deinit_resv_skb(struct xradio_common *hw_priv) ++{ ++ if (hw_priv->skb_reserved) { ++ dev_kfree_skb(hw_priv->skb_reserved); ++ hw_priv->skb_reserved = NULL; ++ hw_priv->skb_resv_len = 0; ++ } ++} ++ ++int xradio_realloc_resv_skb(struct xradio_common *hw_priv, ++ struct sk_buff *skb) ++{ ++ if (!hw_priv->skb_reserved && hw_priv->skb_resv_len) { ++ hw_priv->skb_reserved = dev_alloc_skb(hw_priv->skb_resv_len); ++ if (!hw_priv->skb_reserved) { ++ hw_priv->skb_reserved = skb; ++ dev_warn(hw_priv->pdev, "xr_alloc_skb failed(%d)\n", ++ hw_priv->skb_resv_len); ++ return -1; ++ } ++ } ++ return 0; /* realloc sbk success, deliver to upper.*/ ++} ++ ++static inline struct sk_buff *xradio_get_resv_skb(struct xradio_common *hw_priv, ++ size_t len) ++{ struct sk_buff *skb = NULL; ++ if (hw_priv->skb_reserved && len <= hw_priv->skb_resv_len) { ++ skb = hw_priv->skb_reserved; ++ hw_priv->skb_reserved = NULL; ++ } ++ return skb; ++} ++ ++static inline int xradio_put_resv_skb(struct xradio_common *hw_priv, ++ struct sk_buff *skb) ++{ ++ if (!hw_priv->skb_reserved && hw_priv->skb_resv_len) { ++ hw_priv->skb_reserved = skb; ++ return 0; ++ } ++ return 1; /* sbk not put to reserve*/ ++} ++ ++static struct sk_buff *xradio_get_skb(struct xradio_common *hw_priv, size_t len) ++{ ++ struct sk_buff *skb = NULL; ++ size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; ++ ++ /* TKIP IV + TKIP ICV and MIC - Piggyback.*/ ++ alloc_len += WSM_TX_EXTRA_HEADROOM + 8 + 12- 2; ++ if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) { ++ skb = dev_alloc_skb(alloc_len); ++ /* In AP mode RXed SKB can be looped back as a broadcast. ++ * Here we reserve enough space for headers. */ ++ if (skb) { ++ skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */ ++ - WSM_RX_EXTRA_HEADROOM); ++ } else { ++ skb = xradio_get_resv_skb(hw_priv, alloc_len); ++ if (skb) { ++ dev_dbg(hw_priv->pdev, "get skb_reserved(%d)!\n", alloc_len); ++ skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */ ++ - WSM_RX_EXTRA_HEADROOM); ++ } else { ++ dev_dbg(hw_priv->pdev, "xr_alloc_skb failed(%d)!\n", alloc_len); ++ } ++ } ++ } else { ++ skb = hw_priv->skb_cache; ++ hw_priv->skb_cache = NULL; ++ } ++ return skb; ++} ++ ++static void xradio_put_skb(struct xradio_common *hw_priv, struct sk_buff *skb) ++{ ++ if (hw_priv->skb_cache) ++ dev_kfree_skb(skb); ++ else ++ hw_priv->skb_cache = skb; ++} ++ ++static int xradio_bh_read_ctrl_reg(struct xradio_common *hw_priv, ++ u16 *ctrl_reg) ++{ ++ int ret; ++ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, ctrl_reg); ++ if (ret) { ++ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, ctrl_reg); ++ if (ret) { ++ hw_priv->bh_error = 1; ++ dev_err(hw_priv->pdev, "Failed to read control register.\n"); ++ } ++ } ++ ++ return ret; ++} ++ ++static int xradio_device_wakeup(struct xradio_common *hw_priv) ++{ ++ u16 ctrl_reg; ++ int ret, i=0; ++ ++ /* To force the device to be always-on, the host sets WLAN_UP to 1 */ ++ ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT); ++ if (SYS_WARN(ret)) ++ return ret; ++ ++ ret = xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); ++ if (SYS_WARN(ret)) ++ return ret; ++ ++ /* If the device returns WLAN_RDY as 1, the device is active and will ++ * remain active. */ ++ while (!(ctrl_reg & HIF_CTRL_RDY_BIT) && i < 500) { ++ ret = xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); ++ msleep(1); ++ i++; ++ } ++ if (unlikely(i >= 500)) { ++ dev_err(hw_priv->pdev, "Device cannot wakeup.\n"); ++ return -1; ++ } else if (unlikely(i >= 50)) ++ dev_warn(hw_priv->pdev, "Device wakeup time=%dms.\n", i); ++ dev_dbg(hw_priv->pdev, "Device awake, t=%dms.\n", i); ++ return 1; ++} ++ ++/* Must be called from BH thraed. */ ++void xradio_enable_powersave(struct xradio_vif *priv, ++ bool enable) ++{ ++ priv->powersave_enabled = enable; ++} ++ ++static void xradio_bh_rx_dump(struct device *dev, u8 *data, size_t len){ ++#ifdef DEBUG ++ static const char *msgnames[0xffff] = { ++ // 0x4?? is a sync response to a command ++ [0x0404] = "tx confirm", ++ [0x0406] = "mib confirm", ++ [0x0407] = "scan started", ++ [0x0409] = "configuration confirm", ++ [0x040a] = "reset confirm", ++ [0x040b] = "join confirm", ++ [0x040c] = "key added", ++ [0x040d] = "key removed", ++ [0x0410] = "pm confirm", ++ [0x0411] = "set bss params", ++ [0x0412] = "tx queue params", ++ [0x0413] = "edca confirm", ++ [0x0417] = "start confirm", ++ [0x041b] = "update ie confirm", ++ [0x041c] = "map link confirm", ++ // 0x8?? seem to be async responses or events ++ [0x0801] = "firmware startup complete", ++ [0x0804] = "rx", ++ [0x0805] = "event", ++ [0x0806] = "scan complete", ++ [0x0810] = "set pm indication" ++ }; ++ ++ u16 msgid, ifid; ++ u16 *p = (u16 *)data; ++ msgid = (*(p + 1)) & 0xC3F; ++ ifid = (*(p + 1)) >> 6; ++ ifid &= 0xF; ++ const char *msgname = msgnames[msgid]; ++ if(msgid == 0x804 && ifid == 2){ ++ msgname = "scan result"; ++ } ++ ++ dev_dbg(dev, "vif %d: sdio rx, msgid %s(0x%.4X) len %d\n", ++ ifid, msgname, msgid, *p); ++// print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, ++// data, min(len, (size_t) 64)); ++#endif ++} ++ ++#define READLEN(ctrl) ((ctrl & HIF_CTRL_NEXT_LEN_MASK) << 1) //read_len=ctrl_reg*2. ++ ++static int xradio_bh_rx_availlen(struct xradio_common *hw_priv){ ++ u16 ctrl_reg = 0; ++ if (xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg)) { ++ return -EIO; ++ } ++ return READLEN(ctrl_reg); ++} ++ ++static int xradio_bh_rx(struct xradio_common *hw_priv, u16* nextlen) { ++ size_t read_len = 0; ++ struct sk_buff *skb_rx = NULL; ++ struct wsm_hdr *wsm; ++ size_t wsm_len; ++ int wsm_id; ++ u8 wsm_seq; ++ size_t alloc_len; ++ u8 *data; ++ int ret; ++ ++ read_len = *nextlen > 0 ? *nextlen : xradio_bh_rx_availlen(hw_priv); ++ if(read_len <= 0) ++ return read_len; ++ ++ if (read_len < sizeof(struct wsm_hdr) || (read_len > EFFECTIVE_BUF_SIZE)) { ++ dev_err(hw_priv->pdev, "Invalid read len: %d", read_len); ++ return -1; ++ } ++ ++ /* Add SIZE of PIGGYBACK reg (CONTROL Reg) ++ * to the NEXT Message length + 2 Bytes for SKB */ ++ read_len = read_len + 2; ++ ++#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES) ++ alloc_len = sdio_align_len(hw_priv, read_len); ++#else ++ /* Platform's SDIO workaround */ ++ alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); ++ if (read_len & (SDIO_BLOCK_SIZE - 1)) ++ alloc_len += SDIO_BLOCK_SIZE; ++#endif /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */ ++ /* Check if not exceeding XRADIO capabilities */ ++ if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { ++ dev_err(hw_priv->pdev, "Read aligned len: %d\n", alloc_len); ++ } ++ ++ /* Get skb buffer. */ ++ skb_rx = xradio_get_skb(hw_priv, alloc_len); ++ if (!skb_rx) { ++ dev_err(hw_priv->pdev, "xradio_get_skb failed.\n"); ++ return -ENOMEM; ++ } ++ skb_trim(skb_rx, 0); ++ skb_put(skb_rx, read_len); ++ data = skb_rx->data; ++ if (!data) { ++ dev_err(hw_priv->pdev, "skb data is NULL.\n"); ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ /* Read data from device. */ ++ if (xradio_data_read(hw_priv, data, alloc_len)) { ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* the ctrl register is appened to the end of the wsm frame ++ * so we can use this to avoid reading the control register ++ * again for the next read .. but if this is invalid we'll ++ * do an invalid read and the firmware will crash so this ++ * probably needs some sort of validation */ ++ *nextlen = READLEN(__le16_to_cpu(((__le16 *) data)[(alloc_len >> 1) - 1])); ++ ++ /* check wsm length. */ ++ wsm = (struct wsm_hdr *) data; ++ wsm_len = __le32_to_cpu(wsm->len); ++ ++ if (SYS_WARN(wsm_len > read_len)) { ++ dev_err(hw_priv->pdev, "wsm_len=%d.\n", wsm_len); ++ ret = -1; ++ goto out; ++ } ++ ++ /* dump rx data. */ ++ xradio_bh_rx_dump(hw_priv->pdev, data, wsm_len); ++ ++ /* extract wsm id and seq. */ ++ wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; ++ wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; ++ skb_trim(skb_rx, wsm_len); ++ ++ /* process exceptions. */ ++ if (wsm_id == 0) { ++ printk("wtf?\n"); ++ ret = 0; ++ goto out; ++ } else if (unlikely(wsm_id == 0x0800)) { ++ dev_err(hw_priv->pdev, "firmware exception!\n"); ++ wsm_handle_exception(hw_priv, &data[sizeof(*wsm)], ++ wsm_len - sizeof(*wsm)); ++ ret = -1; ++ goto out; ++ } ++ ++ hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7; ++ ++ /* Process tx frames confirm. */ ++ if (wsm_id & 0x0400) { ++ if (wsm_release_tx_buffer(hw_priv, 1) < 0) { ++ dev_err(hw_priv->pdev, "tx buffer < 0.\n"); ++ ret = -1; ++ goto out; ++ } ++ } ++ ++ /* WSM processing frames. */ ++ if (wsm_handle_rx(hw_priv, wsm_id, wsm, &skb_rx)) { ++ dev_err(hw_priv->pdev, "wsm_handle_rx failed.\n"); ++ ret = -1; ++ goto out; ++ } ++ ++ ret = 1; ++ ++out: ++ /* Reclaim the SKB buffer */ ++ if (skb_rx) { ++ if (xradio_put_resv_skb(hw_priv, skb_rx)) ++ xradio_put_skb(hw_priv, skb_rx); ++ } ++ ++ return ret; ++} ++ ++static void xradio_bh_tx_dump(struct device *dev, u8 *data, size_t len){ ++#ifdef DEBUG ++ static const char *msgnames[0xffff] = { ++ [0x0004] = "tx", ++ [0x0006] = "MIB", ++ [0x0007] = "start scan", ++ [0x0009] = "configure", ++ [0x000A] = "reset", ++ [0x000B] = "join", ++ [0x000C] = "add key", ++ [0x000D] = "remove key", ++ [0x0010] = "set pm", ++ [0x0011] = "set bss params", ++ [0x0012] = "set tx queue params", ++ [0x0013] = "set edca", ++ [0x0017] = "start", ++ [0x001b] = "update ie", ++ [0x001c] = "map link", ++ }; ++ static const char *mibnames[0xffff] = { ++ [0x0003] = "DOT11_SLOT_TIME", ++ [0x1002] = "TEMPLATE_FRAME", ++ [0x1003] = "RX_FILTER", ++ [0x1004] = "BEACON_FILTER_TABLE", ++ [0x1005] = "BEACON_FILTER_ENABLE", ++ [0x1006] = "OPERATIONAL POWER MODE", ++ [0x1007] = "BEACON_WAKEUP_PERIOD", ++ [0x1009] = "RCPI_RSSI_THRESHOLD", ++ [0x1010] = "SET_ASSOCIATION_MODE", ++ [0x100e] = "BLOCK_ACK_POLICY", ++ [0x100f] = "OVERRIDE_INTERNAL_TX_RATE", ++ [0x1013] = "SET_UAPSD_INFORMATION", ++ [0x1016] = "SET_TX_RATE_RETRY_POLICY", ++ [0x1020] = "PROTECTED_MGMT_POLICY", ++ [0x1021] = "SET_HT_PROTECTION", ++ [0x1024] = "USE_MULTI_TX_CONF", ++ [0x1025] = "KEEP_ALIVE_PERIOD", ++ [0x1026] = "DISABLE_BSSID_FILTER", ++ [0x1035] = "SET_INACTIVITY", ++ }; ++ ++ u16 msgid, ifid, mib; ++ u16 *p = (u16 *)data; ++ msgid = (*(p + 1)) & 0x3F; ++ ifid = (*(p + 1)) >> 6; ++ ifid &= 0xF; ++ mib = *(p + 2); ++ ++ WARN_ON(msgnames[msgid] == NULL); ++ ++ if (msgid == 0x0006) { ++ dev_dbg(dev, "vif %d: sdio tx, msgid %s(0x%.4X) len %d MIB %s(0x%.4X)\n", ++ ifid, msgnames[msgid], msgid,*p, mibnames[mib], mib); ++ } else { ++ dev_dbg(dev, "vif %d: sdio tx, msgid %s(0x%.4X) len %d\n", ifid, msgnames[msgid], msgid, *p); ++ } ++ ++// print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, data, ++// min(len, (size_t) 64)); ++#endif ++} ++ ++static int xradio_bh_tx(struct xradio_common *hw_priv){ ++ int txavailable; ++ int txburst; ++ int vif_selected; ++ struct wsm_hdr *wsm; ++ size_t tx_len; ++ int ret; ++ u8 *data; ++ ++ SYS_BUG(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); ++ txavailable = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used; ++ if (txavailable) { ++ /* Wake up the devices */ ++ if (hw_priv->device_can_sleep) { ++ ret = xradio_device_wakeup(hw_priv); ++ if (SYS_WARN(ret < 0)) { ++ return -1; ++ } else if (ret) { ++ hw_priv->device_can_sleep = false; ++ } else { /* Wait for "awake" interrupt */ ++ dev_dbg(hw_priv->pdev, ++ "need to wait for device to wake before doing tx\n"); ++ return 0; ++ } ++ } ++ /* Increase Tx buffer*/ ++ wsm_alloc_tx_buffer(hw_priv); ++ ++ /* Get data to send and send it. */ ++ ret = wsm_get_tx(hw_priv, &data, &tx_len, &txburst, &vif_selected); ++ if (ret <= 0) { ++ wsm_release_tx_buffer(hw_priv, 1); ++ if (SYS_WARN(ret < 0)) { ++ dev_err(hw_priv->pdev, "wsm_get_tx=%d.\n", ret); ++ return -ENOMEM; ++ } else { ++ return 0; ++ } ++ } else { ++ wsm = (struct wsm_hdr *) data; ++ SYS_BUG(tx_len < sizeof(*wsm)); ++ SYS_BUG(__le32_to_cpu(wsm->len) != tx_len); ++ ++ /* Align tx length and check it. */ ++#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES) ++ if (tx_len <= 8) ++ tx_len = 16; ++ tx_len = sdio_align_len(hw_priv, tx_len); ++#else /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */ ++ /* HACK!!! Platform limitation. ++ * It is also supported by upper layer: ++ * there is always enough space at the end of the buffer. */ ++ if (tx_len & (SDIO_BLOCK_SIZE - 1)) { ++ tx_len &= ~(SDIO_BLOCK_SIZE - 1); ++ tx_len += SDIO_BLOCK_SIZE; ++ } ++#endif /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */ ++ /* Check if not exceeding XRADIO capabilities */ ++ if (tx_len > EFFECTIVE_BUF_SIZE) { ++ dev_warn(hw_priv->pdev, "Write aligned len: %d\n", tx_len); ++ } ++ ++ /* Make sequence number. */ ++ wsm->id &= __cpu_to_le32(~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); ++ wsm->id |= cpu_to_le32(WSM_TX_SEQ(hw_priv->wsm_tx_seq)); ++ ++ /* Send the data to devices. */ ++ if (SYS_WARN(xradio_data_write(hw_priv, data, tx_len))) { ++ wsm_release_tx_buffer(hw_priv, 1); ++ dev_err(hw_priv->pdev, "xradio_data_write failed\n"); ++ return -EIO; ++ } ++ ++ xradio_bh_tx_dump(hw_priv->pdev, data, tx_len); ++ ++ /* Process after data have sent. */ ++ if (vif_selected != -1) { ++ hw_priv->hw_bufs_used_vif[vif_selected]++; ++ } ++ wsm_txed(hw_priv, data); ++ hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; ++ ++ return 1; ++ } ++ } else ++ return 0; ++} ++ ++static int xradio_bh_exchange(struct xradio_common *hw_priv) { ++ int rxdone; ++ int txdone; ++ u16 nextlen = 0; ++ ++ /* query stuck frames in firmware. */ ++ if (atomic_xchg(&hw_priv->query_cnt, 0)) { ++ if (schedule_work(&hw_priv->query_work) <= 0) ++ atomic_add(1, &hw_priv->query_cnt); ++ } ++ ++ /* keep doing tx and rx until they both stop */ ++ do { ++ txdone = xradio_bh_tx(hw_priv); ++ if (txdone < 0) { ++ break; ++ } ++ rxdone = xradio_bh_rx(hw_priv, &nextlen); ++ if (rxdone < 0) { ++ break; ++ } ++ } while (txdone > 0 || rxdone > 0); ++ return 0; ++} ++ ++static int xradio_bh(void *arg) ++{ ++ struct xradio_common *hw_priv = arg; ++ int term, suspend; ++ int wake = 0; ++ long timeout; ++ long status; ++ ++ for (;;) { ++ timeout = HZ / 30; ++ ++ // wait for something to happen or a timeout ++ status = wait_event_interruptible_timeout(hw_priv->bh_wq, ( { ++ wake = atomic_xchg(&hw_priv->bh_tx, 0); ++ term = kthread_should_stop(); ++ suspend = atomic_read(&hw_priv->bh_suspend); ++ (wake || term || suspend);}), timeout); ++ ++ if (wake) { ++ if(xradio_bh_exchange(hw_priv) < 0){ ++ break; ++ } ++ } else if (term) { ++ dev_dbg(hw_priv->pdev, "xradio_bh exit!\n"); ++ break; ++ } else if (status < 0) { ++ dev_err(hw_priv->pdev, "bh_error=%d, status=%ld\n", ++ hw_priv->bh_error, status); ++ break; ++ } else if (!status) { ++ /* check if there is data waiting but we missed the interrupt*/ ++ if (xradio_bh_rx_availlen(hw_priv) > 0) { ++ dev_warn(hw_priv->pdev, "missed interrupt\n"); ++ if(xradio_bh_exchange(hw_priv) < 0){ ++ break; ++ } ++ } ++ /* There are some frames to be confirmed. */ ++ else if (hw_priv->hw_bufs_used) { ++ long timeout = 0; ++ bool pending = 0; ++ dev_dbg(hw_priv->pdev, "Need confirm:%d!\n", ++ hw_priv->hw_bufs_used); ++ /* Check if frame transmission is timed out. */ ++ pending = xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS, ++ hw_priv->pending_frame_id, &timeout); ++ /* There are some frames confirm time out. */ ++ if (pending && timeout < 0) { ++ dev_err(hw_priv->pdev, "query_txpkt_timeout:%ld!\n", ++ timeout); ++ break; ++ } ++ } //else if (!txpending){ ++ //if (hw_priv->powersave_enabled && !hw_priv->device_can_sleep && !atomic_read(&hw_priv->recent_scan)) { ++ // /* Device is idle, we can go to sleep. */ ++ // dev_dbg(hw_priv->pdev, "Device idle(timeout), can sleep.\n"); ++ // SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0)); ++ // hw_priv->device_can_sleep = true; ++ //} ++ //continue; ++ //} ++ } else if (suspend) { ++ dev_dbg(hw_priv->pdev, "Host suspend request.\n"); ++ /* Check powersave setting again. */ ++ if (hw_priv->powersave_enabled) { ++ dev_dbg(hw_priv->pdev, ++ "Device idle(host suspend), can sleep.\n"); ++ SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0)); ++ hw_priv->device_can_sleep = true; ++ } ++ ++ /* bh thread go to suspend. */ ++ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_SUSPENDED); ++ wake_up(&hw_priv->bh_evt_wq); ++ status = wait_event_interruptible(hw_priv->bh_wq, ++ XRADIO_BH_RESUME == atomic_read(&hw_priv->bh_suspend)); ++ ++ if (status < 0) { ++ dev_err(hw_priv->pdev, "Failed to wait for resume: %ld.\n", ++ status); ++ break; ++ } ++ dev_dbg(hw_priv->pdev, "Host resume.\n"); ++ atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED); ++ wake_up(&hw_priv->bh_evt_wq); ++ } ++ } /* for (;;)*/ ++ ++ dev_err(hw_priv->pdev, "bh thread exiting\n"); ++ ++ /* If BH Error, handle it. */ ++ if (!term) { ++ dev_err(hw_priv->pdev, "Fatal error, exitting code=%d.\n", ++ hw_priv->bh_error); ++ ++#ifdef HW_ERROR_WIFI_RESET ++ /* notify upper layer to restart wifi. ++ * don't do it in debug version. */ ++ wsm_upper_restart(hw_priv); ++#endif ++ /* TODO: schedule_work(recovery) */ ++#ifndef HAS_PUT_TASK_STRUCT ++ /* The only reason of having this stupid code here is ++ * that __put_task_struct is not exported by kernel. */ ++ for (;;) { ++ int status = wait_event_interruptible(hw_priv->bh_wq, ({ ++ term = kthread_should_stop(); ++ (term);})); ++ if (status || term) ++ break; ++ } ++#endif ++ } ++ return 0; ++} +diff --git a/drivers/net/wireless/xradio/bh.h b/drivers/net/wireless/xradio/bh.h +new file mode 100644 +index 00000000..ca9187e2 +--- /dev/null ++++ b/drivers/net/wireless/xradio/bh.h +@@ -0,0 +1,36 @@ ++/* ++ * Data Transmission thread for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef XRADIO_BH_H ++#define XRADIO_BH_H ++ ++#define XRADIO_BH_THREAD "xradio_bh" ++ ++/* extern */ struct xradio_common; ++ ++#define SDIO_BLOCK_SIZE (528) ++ ++int xradio_register_bh(struct xradio_common *hw_priv); ++void xradio_unregister_bh(struct xradio_common *hw_priv); ++void xradio_irq_handler(struct xradio_common *hw_priv); ++void xradio_bh_wakeup(struct xradio_common *hw_priv); ++int xradio_bh_suspend(struct xradio_common *hw_priv); ++int xradio_bh_resume(struct xradio_common *hw_priv); ++/* Must be called from BH thread. */ ++void xradio_enable_powersave(struct xradio_vif *priv, bool enable); ++int wsm_release_tx_buffer(struct xradio_common *hw_priv, int count); ++int wsm_release_vif_tx_buffer(struct xradio_common *hw_priv, int if_id, ++ int count); ++int xradio_init_resv_skb(struct xradio_common *hw_priv); ++void xradio_deinit_resv_skb(struct xradio_common *hw_priv); ++int xradio_realloc_resv_skb(struct xradio_common *hw_priv, ++ struct sk_buff *skb); ++#endif /* XRADIO_BH_H */ +diff --git a/drivers/net/wireless/xradio/common.h b/drivers/net/wireless/xradio/common.h +new file mode 100644 +index 00000000..1ce23dee +--- /dev/null ++++ b/drivers/net/wireless/xradio/common.h +@@ -0,0 +1,101 @@ ++/* ++ * Common interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef XRADIO_COMMON_H ++#define XRADIO_COMMON_H ++ ++/******************************************************* ++ interfaces for parse frame protocol info. ++********************************************************/ ++#define LLC_LEN 8 ++#define LLC_TYPE_OFF 6 //Ether type offset ++#define IP_PROTO_OFF 9 //protocol offset ++#define IP_S_ADD_OFF 12 ++#define IP_D_ADD_OFF 16 ++#define UDP_LEN 8 ++//DHCP ++#define DHCP_BOOTP_C 68 ++#define DHCP_BOOTP_S 67 ++#define UDP_BOOTP_LEN 236 //exclude "Options:64" ++#define BOOTP_OPS_LEN 64 ++#define DHCP_MAGIC 0x63825363 ++#define DHCP_DISCOVER 0x01 ++#define DHCP_OFFER 0x02 ++#define DHCP_REQUEST 0x03 ++#define DHCP_DECLINE 0x04 ++#define DHCP_ACK 0x05 ++#define DHCP_NACK 0x06 ++#define DHCP_RELEASE 0x07 ++ ++//LLC layer. ++static inline bool is_SNAP(u8* llc_data) ++{ ++ return (bool)(*(u16*)(llc_data) == 0xAAAA && llc_data[2] == 0x03); //0xAA, 0xAA, 0x03. ++} ++ ++static inline bool is_STP(u8* llc_data) ++{ ++ return (bool)(*(u16*)(llc_data) == 0xAAAA && llc_data[2] == 0x03); //0x42, 0x42, 0x03. ++} ++ ++//IP/IPV6/ARP layer... ++static inline bool is_ip(u8* llc_data) ++{ ++ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IP)); //0x0800 ++} ++static inline bool is_ipv6(u8* llc_data) ++{ ++ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IPV6)); //0x08dd ++} ++static inline bool is_arp(u8* llc_data) ++{ ++ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_ARP)); //0x0806 ++} ++static inline bool is_8021x(u8* llc_data) ++{ ++ return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_PAE)); //0x888E ++} ++ ++//TCP/UDP layer... ++static inline bool is_tcp(u8* llc_data) ++{ ++ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_TCP); // ++} ++ ++static inline bool is_udp(u8* llc_data) ++{ ++ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_UDP); // ++} ++ ++static inline bool is_icmp(u8* llc_data) ++{ ++ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_ICMP); // ++} ++ ++static inline bool is_igmp(u8* llc_data) ++{ ++ return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_IGMP); // ++} ++ ++static inline bool is_dhcp(u8* llc_data) ++{ ++ u8* ip_hdr = llc_data+LLC_LEN; ++ if(!is_ip(llc_data)) ++ return (bool)0; ++ if(ip_hdr[IP_PROTO_OFF] == IPPROTO_UDP) { ++ u8* udp_hdr = ip_hdr+((ip_hdr[0]&0xf)<<2); //ihl:words ++ return (bool)((((udp_hdr[0]<<8)|udp_hdr[1]) == DHCP_BOOTP_C) || //DHCP client ++ (((udp_hdr[0]<<8)|udp_hdr[1]) == DHCP_BOOTP_S)); //DHCP server ++ } ++ return (bool)0; ++} ++ ++#endif //XRADIO_COMMON_H +diff --git a/drivers/net/wireless/xradio/debug.c b/drivers/net/wireless/xradio/debug.c +new file mode 100644 +index 00000000..dc57641d +--- /dev/null ++++ b/drivers/net/wireless/xradio/debug.c +@@ -0,0 +1,3265 @@ ++/* ++ * Debug code for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/*Linux version 3.4.0 compilation*/ ++//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) ++#include ++//#endif ++#include ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "hwio.h" ++#include "debug.h" ++ ++/*added by yangfh, for host debuglevel*/ ++u8 dbg_common = 0xff; ++u8 dbg_sbus = 0xff; ++u8 dbg_bh = 0xff; ++u8 dbg_txrx = 0xff; ++u8 dbg_wsm = 0xff; ++u8 dbg_sta = 0xff; ++u8 dbg_scan = 0xff; ++u8 dbg_ap = 0xff; ++u8 dbg_pm = 0xff; ++u8 dbg_itp = 0xff; ++u8 dbg_logfile = XRADIO_DBG_ERROR; ++ ++#ifdef CONFIG_XRADIO_DEBUGFS ++/* join_status */ ++static const char * const xradio_debug_join_status[] = { ++ "passive", ++ "monitor", ++ "station", ++ "access point", ++}; ++ ++/* WSM_JOIN_PREAMBLE_... */ ++static const char * const xradio_debug_preamble[] = { ++ "long", ++ "short", ++ "long on 1 and 2 Mbps", ++}; ++ ++static const char * const xradio_debug_fw_types[] = { ++ "ETF", ++ "WFM", ++ "WSM", ++ "HI test", ++ "Platform test", ++}; ++ ++static const char * const xradio_debug_link_id[] = { ++ "OFF", ++ "REQ", ++ "SOFT", ++ "HARD", ++}; ++ ++static const char *xradio_debug_mode(int mode) ++{ ++ switch (mode) { ++ case NL80211_IFTYPE_UNSPECIFIED: ++ return "unspecified"; ++ case NL80211_IFTYPE_MONITOR: ++ return "monitor"; ++ case NL80211_IFTYPE_STATION: ++ return "station"; ++ case NL80211_IFTYPE_ADHOC: ++ return "ad-hok"; ++ case NL80211_IFTYPE_MESH_POINT: ++ return "mesh point"; ++ case NL80211_IFTYPE_AP: ++ return "access point"; ++ case NL80211_IFTYPE_P2P_CLIENT: ++ return "p2p client"; ++ case NL80211_IFTYPE_P2P_GO: ++ return "p2p go"; ++ default: ++ return "unsupported"; ++ } ++} ++ ++static void xradio_queue_status_show(struct seq_file *seq, ++ struct xradio_queue *q) ++{ ++ int i, if_id; ++ seq_printf(seq, "Queue %d:\n", q->queue_id); ++ seq_printf(seq, " capacity: %d\n", q->capacity); ++ seq_printf(seq, " queued: %d\n", q->num_queued); ++ seq_printf(seq, " pending: %d\n", q->num_pending); ++ seq_printf(seq, " sent: %d\n", q->num_sent); ++ seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); ++ seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); ++ seq_puts(seq, " link map: 0-> "); ++ for (if_id = 0; if_id < XRWL_MAX_VIFS; if_id++) { ++ for (i = 0; i < q->stats->map_capacity; ++i) ++ seq_printf(seq, "%.2d ", q->link_map_cache[if_id][i]); ++ seq_printf(seq, "<-%d\n", q->stats->map_capacity); ++ } ++} ++ ++static void xradio_debug_print_map(struct seq_file *seq, ++ struct xradio_vif *priv, ++ const char *label, ++ u32 map) ++{ ++ int i; ++ seq_printf(seq, "%s0-> ", label); ++ for (i = 0; i < priv->hw_priv->tx_queue_stats.map_capacity; ++i) ++ seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); ++ seq_printf(seq, "<-%d\n", ++ priv->hw_priv->tx_queue_stats.map_capacity - 1); ++} ++ ++static int xradio_version_show(struct seq_file *seq, void *v) ++{ ++ struct xradio_common *hw_priv = seq->private; ++ seq_printf(seq, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]); ++ return 0; ++} ++ ++static int xradio_version_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_version_show, inode->i_private); ++} ++ ++static const struct file_operations fops_version = { ++ .open = xradio_version_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++#if defined(DGB_XRADIO_QC) ++static int xradio_hwinfo_show(struct seq_file *seq, void *v) ++{ ++ int ret; ++ struct xradio_common *hw_priv = seq->private; ++ u32 hw_arry[8] = { 0 }; ++ ++ // ++ wsm_read_mib(hw_priv, WSM_MIB_ID_HW_INFO, (void*)&hw_arry, sizeof(hw_arry), 4); ++ ++ //get_random_bytes((u8 *)&hw_arry[0], 8*sizeof(u32)); ++ //hw_arry[0] = 0x0B140D4; ++ //hw_arry[1] &= ~0xF803FFFF; ++ //hw_arry[2] &= ~0xC0001FFF; ++ //hw_arry[5] &= ~0xFFFFF000; ++ //hw_arry[7] &= ~0xFFFC07C0; ++ ++ seq_printf(seq, "0x%08x,0x%08x,0x%08x,0x%08x," ++ "0x%08x,0x%08x,0x%08x,0x%08x\n", ++ hw_arry[0], hw_arry[1], hw_arry[2], hw_arry[3], ++ hw_arry[4], hw_arry[5], hw_arry[6], hw_arry[7]); ++ return 0; ++} ++ ++static int xradio_hwinfo_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_hwinfo_show, inode->i_private); ++} ++ ++static const struct file_operations fops_hwinfo = { ++ .open = xradio_hwinfo_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++#endif ++ ++static int xradio_status_show_common(struct seq_file *seq, void *v) ++{ ++ int i; ++ struct list_head *item; ++ struct xradio_common *hw_priv = seq->private; ++ struct xradio_debug_common *d = hw_priv->debug; ++ int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0; ++ bool ba_ena; ++ ++ spin_lock_bh(&hw_priv->ba_lock); ++ ba_cnt = hw_priv->debug->ba_cnt; ++ ba_acc = hw_priv->debug->ba_acc; ++ ba_cnt_rx = hw_priv->debug->ba_cnt_rx; ++ ba_acc_rx = hw_priv->debug->ba_acc_rx; ++ ba_ena = hw_priv->ba_ena; ++ if (ba_cnt) ++ ba_avg = ba_acc / ba_cnt; ++ if (ba_cnt_rx) ++ ba_avg_rx = ba_acc_rx / ba_cnt_rx; ++ spin_unlock_bh(&hw_priv->ba_lock); ++ ++ seq_puts(seq, "XRADIO Wireless LAN driver status\n"); ++ seq_printf(seq, "Hardware: %d.%d\n", ++ hw_priv->wsm_caps.hardwareId, ++ hw_priv->wsm_caps.hardwareSubId); ++ seq_printf(seq, "Firmware: %s %d.%d\n", ++ xradio_debug_fw_types[hw_priv->wsm_caps.firmwareType], ++ hw_priv->wsm_caps.firmwareVersion, ++ hw_priv->wsm_caps.firmwareBuildNumber); ++ seq_printf(seq, "FW API: %d\n", ++ hw_priv->wsm_caps.firmwareApiVer); ++ seq_printf(seq, "FW caps: 0x%.4X\n", ++ hw_priv->wsm_caps.firmwareCap); ++ if (hw_priv->channel) ++ seq_printf(seq, "Channel: %d%s\n", ++ hw_priv->channel->hw_value, ++ hw_priv->channel_switch_in_progress ? ++ " (switching)" : ""); ++ seq_printf(seq, "HT: %s\n", ++ xradio_is_ht(&hw_priv->ht_oper) ? "on" : "off"); ++ if (xradio_is_ht(&hw_priv->ht_oper)) { ++ seq_printf(seq, "Greenfield: %s\n", ++ xradio_ht_greenfield(&hw_priv->ht_oper) ? "yes" : "no"); ++ seq_printf(seq, "AMPDU dens: %d\n", ++ xradio_ht_ampdu_density(&hw_priv->ht_oper)); ++ } ++ spin_lock_bh(&hw_priv->tx_policy_cache.lock); ++ i = 0; ++ list_for_each(item, &hw_priv->tx_policy_cache.used) ++ ++i; ++ spin_unlock_bh(&hw_priv->tx_policy_cache.lock); ++ seq_printf(seq, "RC in use: %d\n", i); ++ seq_printf(seq, "BA stat: %d, %d (%d)\n", ++ ba_cnt, ba_acc, ba_avg); ++ seq_printf(seq, "BA RX stat: %d, %d (%d)\n", ++ ba_cnt_rx, ba_acc_rx, ba_avg_rx); ++ seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off"); ++ ++ seq_puts(seq, "\n"); ++ for (i = 0; i < 4; ++i) { ++ xradio_queue_status_show(seq, &hw_priv->tx_queue[i]); ++ seq_puts(seq, "\n"); ++ } ++ seq_printf(seq, "TX burst: %d\n", ++ d->tx_burst); ++ seq_printf(seq, "RX burst: %d\n", ++ d->rx_burst); ++ seq_printf(seq, "TX miss: %d\n", ++ d->tx_cache_miss); ++ seq_printf(seq, "Long retr: %d\n", ++ hw_priv->long_frame_max_tx_count); ++ seq_printf(seq, "Short retr: %d\n", ++ hw_priv->short_frame_max_tx_count); ++ ++ seq_printf(seq, "BH status: %s, errcode=%d\n", ++ atomic_read(&hw_priv->bh_term) ? "terminated" : "alive", ++ hw_priv->bh_error); ++ seq_printf(seq, "Pending TX: %d\n", ++ atomic_read(&hw_priv->bh_tx)); ++ ++ seq_printf(seq, "TX bufs: %d x %d bytes\n", ++ hw_priv->wsm_caps.numInpChBufs, ++ hw_priv->wsm_caps.sizeInpChBuf); ++ seq_printf(seq, "Used bufs: %d\n", ++ hw_priv->hw_bufs_used); ++ seq_printf(seq, "Powersavemode:%s\n", ++ hw_priv->powersave_enabled ? "enable" : "disable"); ++ seq_printf(seq, "Device: %s\n", ++ hw_priv->device_can_sleep ? "alseep" : "awake"); ++ ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ seq_printf(seq, "WSM status: %s\n", ++ hw_priv->wsm_cmd.done ? "idle" : "active"); ++ seq_printf(seq, "WSM cmd: 0x%.4X (%d bytes)\n", ++ hw_priv->wsm_cmd.cmd, hw_priv->wsm_cmd.len); ++ seq_printf(seq, "WSM retval: %d\n", ++ hw_priv->wsm_cmd.ret); ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ ++ seq_printf(seq, "Datapath: %s\n", ++ atomic_read(&hw_priv->tx_lock) ? "locked" : "unlocked"); ++ if (atomic_read(&hw_priv->tx_lock)) ++ seq_printf(seq, "TXlock cnt: %d\n", ++ atomic_read(&hw_priv->tx_lock)); ++ ++ seq_printf(seq, "Scan: %s\n", ++ atomic_read(&hw_priv->scan.in_progress) ? "active" : "idle"); ++ seq_printf(seq, "Led state: 0x%.2X\n", ++ hw_priv->softled_state); ++ ++ return 0; ++} ++ ++static int xradio_status_open_common(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_status_show_common, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_status_common = { ++ .open = xradio_status_open_common, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int xradio_counters_show(struct seq_file *seq, void *v) ++{ ++ int ret; ++ struct xradio_common *hw_priv = seq->private; ++ struct wsm_counters_table counters; ++ ++ ret = wsm_get_counters_table(hw_priv, &counters); ++ if (ret) ++ return ret; ++ ++#define CAT_STR(x, y) x ## y ++#define PUT_COUNTER(tab, name) \ ++ seq_printf(seq, "%s:" tab "%d\n", #name, \ ++ __le32_to_cpu(counters.CAT_STR(count, name))) ++ ++ PUT_COUNTER("\t\t\t\t", PlcpErrors); ++ PUT_COUNTER("\t\t\t\t", FcsErrors); ++ PUT_COUNTER("\t\t\t\t", TxPackets); ++ PUT_COUNTER("\t\t\t\t", RxPackets); ++ PUT_COUNTER("\t\t\t", RxPacketErrors); ++ PUT_COUNTER("\t\t\t\t", RtsSuccess); ++ PUT_COUNTER("\t\t\t", RtsFailures); ++ PUT_COUNTER("\t\t", RxFramesSuccess); ++ PUT_COUNTER("\t", RxDecryptionFailures); ++ PUT_COUNTER("\t\t\t", RxMicFailures); ++ PUT_COUNTER("\t\t", RxNoKeyFailures); ++ PUT_COUNTER("\t\t", TxMulticastFrames); ++ PUT_COUNTER("\t\t", TxFramesSuccess); ++ PUT_COUNTER("\t\t", TxFrameFailures); ++ PUT_COUNTER("\t\t", TxFramesRetried); ++ PUT_COUNTER("\t", TxFramesMultiRetried); ++ PUT_COUNTER("\t\t", RxFrameDuplicates); ++ PUT_COUNTER("\t\t\t", AckFailures); ++ PUT_COUNTER("\t\t", RxMulticastFrames); ++ PUT_COUNTER("\t\t", RxCMACICVErrors); ++ PUT_COUNTER("\t\t\t", RxCMACReplays); ++ PUT_COUNTER("\t\t", RxMgmtCCMPReplays); ++ PUT_COUNTER("\t\t\t", RxBIPMICErrors); ++ ++ ++#undef PUT_COUNTER ++#undef CAT_STR ++ ++ return 0; ++} ++ ++static int xradio_counters_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_counters_show, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_counters = { ++ .open = xradio_counters_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int xradio_backoff_show(struct seq_file *seq, void *v) ++{ ++ int ret; ++ struct xradio_common *hw_priv = seq->private; ++ struct wsm_backoff_counter counters; ++ ++ ret = wsm_get_backoff_dbg(hw_priv, &counters); ++ if (ret) ++ return ret; ++ ++#define CAT_STR(x, y) x ## y ++#define PUT_COUNTER(tab, name) \ ++ seq_printf(seq, tab"%s:\t%d\n", #name, \ ++ (__le32_to_cpu(counters.CAT_STR(count, name))&0xffff)) ++ ++ PUT_COUNTER("backoff_max ", 0); ++ PUT_COUNTER("[0,7] ", 1); ++ PUT_COUNTER("[~,15] ", 2); ++ PUT_COUNTER("[~,31] ", 3); ++ PUT_COUNTER("[~,63] ", 4); ++ PUT_COUNTER("[~,127] ", 5); ++ PUT_COUNTER("[~,255] ", 6); ++ PUT_COUNTER("[~,511] ", 7); ++ PUT_COUNTER("[~,1023] ", 8); ++ ++ ++#undef PUT_COUNTER ++#undef CAT_STR ++ ++ return 0; ++} ++ ++static int xradio_backoff_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_backoff_show, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_backoff = { ++ .open = xradio_backoff_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++extern u32 TxedRateIdx_Map[24]; ++extern u32 RxedRateIdx_Map[24]; ++ ++static int xradio_ratemap_show(struct seq_file *seq, void *v) ++{ ++ //int ret; ++ //struct xradio_common *hw_priv = seq->private; ++ ++ seq_printf(seq, "\nRateMap for Tx & RX:\n"); ++#define PUT_RATE_COUNT(name,idx) \ ++ seq_printf(seq, "%s\t" "%d, %d\n", #name, \ ++ __le32_to_cpu(TxedRateIdx_Map[idx]),\ ++ __le32_to_cpu(RxedRateIdx_Map[idx]));\ ++ TxedRateIdx_Map[idx]=0;\ ++ RxedRateIdx_Map[idx]=0; ++ ++ PUT_RATE_COUNT("65 Mbps:", 21); ++ PUT_RATE_COUNT("58.5 Mbps:", 20); ++ PUT_RATE_COUNT("52 Mbps:", 19); ++ PUT_RATE_COUNT("39 Mbps:", 18); ++ PUT_RATE_COUNT("26 Mbps:", 17); ++ PUT_RATE_COUNT("19.5 Mbps:", 16); ++ PUT_RATE_COUNT("13 Mbps:",15); ++ PUT_RATE_COUNT("6.5 Mbps:", 14); ++ ++ PUT_RATE_COUNT("54 Mbps:", 13); ++ PUT_RATE_COUNT("48 Mbps:", 12); ++ PUT_RATE_COUNT("36 Mbps:", 11); ++ PUT_RATE_COUNT("24 Mbps:", 10); ++ PUT_RATE_COUNT("18 Mbps:", 9); ++ PUT_RATE_COUNT("12 Mbps:", 8); ++ PUT_RATE_COUNT("9 Mbps:", 7); ++ PUT_RATE_COUNT("6 Mbps:", 6); ++ ++ PUT_RATE_COUNT("11 Mbps:", 3); ++ PUT_RATE_COUNT("5.5 Mbps:", 2); ++ PUT_RATE_COUNT("2 Mbps:", 1); ++ PUT_RATE_COUNT("1 Mbps:", 0); ++ ++#undef PUT_RATE_COUNT ++ ++ return 0; ++} ++ ++static int xradio_ratemap_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_ratemap_show, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_ratemap= { ++ .open = xradio_ratemap_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int xradio_ampducounters_show(struct seq_file *seq, void *v) ++{ ++ int ret; ++ struct xradio_common *hw_priv = seq->private; ++ struct wsm_ampducounters_table counters; ++ ++ ret = wsm_get_ampducounters_table(hw_priv, &counters); ++ if (ret) ++ return ret; ++ ++#define CAT_STR(x, y) x ## y ++#define PUT_COUNTER(tab, name) \ ++ seq_printf(seq, "%s:" tab "%d\n", #name, \ ++ __le32_to_cpu(counters.CAT_STR(count, name))) ++ ++ PUT_COUNTER("\t\t\t\t\t", TxAMPDUs); ++ PUT_COUNTER("\t\t\t", TxMPDUsInAMPDUs); ++ PUT_COUNTER("\t\t", TxOctetsInAMPDUs_l32); ++ PUT_COUNTER("\t\t", TxOctetsInAMPDUs_h32); ++ PUT_COUNTER("\t\t\t\t\t", RxAMPDUs); ++ PUT_COUNTER("\t\t\t", RxMPDUsInAMPDUs); ++ PUT_COUNTER("\t\t", RxOctetsInAMPDUs_l32); ++ PUT_COUNTER("\t\t", RxOctetsInAMPDUs_h32); ++ PUT_COUNTER("\t", RxDelimeterCRCErrorCount); ++ PUT_COUNTER("\t\t\t", ImplictBARFailures); ++ PUT_COUNTER("\t\t\t", ExplictBARFailures); ++ ++ ++#undef PUT_COUNTER ++#undef CAT_STR ++ ++ return 0; ++} ++ ++static int xradio_ampducounters_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_ampducounters_show, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_ampducounters = { ++ .open = xradio_ampducounters_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int xradio_txpipe_show(struct seq_file *seq, void *v) ++{ ++ int ret; ++ struct xradio_common *hw_priv = seq->private; ++ struct wsm_txpipe_counter counters; ++ ++ ret = wsm_get_txpipe_table(hw_priv, &counters); ++ if (ret) ++ return ret; ++ ++#define CAT_STR(x, y) x ## y ++#define PUT_COUNTER(tab, name) \ ++ seq_printf(seq, tab":\t%d\n", \ ++ __le32_to_cpu(counters.CAT_STR(count, name))) ++ ++ PUT_COUNTER("tx-aggr ", 1); ++ PUT_COUNTER("retx-aggr ", 2); ++ PUT_COUNTER("retry_type1 ", 3); ++ PUT_COUNTER("retry_type2 ", 4); ++ PUT_COUNTER("retry_type3 ", 5); ++ PUT_COUNTER("rx-aggr-event ", 6); ++ PUT_COUNTER("rx-aggr-end ", 7); ++ PUT_COUNTER("rx-ba ", 8); ++ PUT_COUNTER("tx_ampdu_len ", 9); ++ PUT_COUNTER("fail_by_rts ", a); ++ ++ ++#undef PUT_COUNTER ++#undef CAT_STR ++ ++ return 0; ++} ++ ++static int xradio_txpipe_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_txpipe_show, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_txpipe = { ++ .open = xradio_txpipe_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++static int count_idx=0; ++static int xradio_dbgstats_show(struct seq_file *seq, void *v) ++{ ++ int ret; ++ int avg_ampdu_len=0; ++ int FrameFail_ratio=0; ++ int FailByRts_ratio=0; ++ int FrameRetry_ratio=0; ++ int AMPDURetry_ratio=0; ++ int Retry1_ratio=0; ++ int Retry2_ratio=0; ++ int Retry3_ratio=0; ++ struct xradio_common *hw_priv = seq->private; ++ struct wsm_counters_table counters; ++ struct wsm_ampducounters_table ampdu_counters; ++ struct wsm_txpipe_counter txpipe; ++ ++ ret = wsm_get_counters_table(hw_priv, &counters); ++ if (ret) ++ return ret; ++ ++ ret = wsm_get_txpipe_table(hw_priv, &txpipe); ++ if (ret) ++ return ret; ++ ++ ret = wsm_get_ampducounters_table(hw_priv, &du_counters); ++ if (ret) ++ return ret; ++ ++#define CAT_STR(x, y) x ## y ++ ++#define PUT_COUNTER(tab, name) \ ++ seq_printf(seq, tab "%d\n", \ ++ __le32_to_cpu(counters.CAT_STR(count, name))) ++ ++#define PUT_AMPDU_COUNTER(tab, name) \ ++ seq_printf(seq, tab "%d\n", \ ++ __le32_to_cpu(ampdu_counters.CAT_STR(count, name))) ++ ++#define PUT_TXPIPE(tab, name) \ ++ seq_printf(seq, tab "%d\n", \ ++ __le32_to_cpu(txpipe.CAT_STR(count, name))) ++ ++#define PUT_RATE_COUNT(name,idx) \ ++ seq_printf(seq, "%s\t%d\n", #name, \ ++ __le32_to_cpu(TxedRateIdx_Map[idx]));\ ++ TxedRateIdx_Map[idx]=0; ++ ++ if (ampdu_counters.countTxAMPDUs) { ++ avg_ampdu_len = (int)((ampdu_counters.countTxMPDUsInAMPDUs+(ampdu_counters.countTxAMPDUs>>1)) \ ++ /ampdu_counters.countTxAMPDUs); ++ AMPDURetry_ratio = (int)(ampdu_counters.countImplictBARFailures*100/ampdu_counters.countTxAMPDUs); ++ } ++ if (counters.countAckFailures) { ++ Retry1_ratio= (int)(txpipe.count3*100/counters.countAckFailures); ++ Retry2_ratio= (int)(txpipe.count4*100/counters.countAckFailures); ++ Retry3_ratio= (int)(txpipe.count5*100/counters.countAckFailures); ++ } ++ if (ampdu_counters.countTxMPDUsInAMPDUs) { ++ FrameFail_ratio= (int)(counters.countTxFrameFailures*1000/ampdu_counters.countTxMPDUsInAMPDUs); ++ FrameRetry_ratio= (int)(counters.countAckFailures*100/ampdu_counters.countTxMPDUsInAMPDUs); ++ } ++ ++ if (counters.countTxFrameFailures) ++ FailByRts_ratio = (int)(txpipe.counta*100/counters.countTxFrameFailures); ++ ++ seq_printf(seq, "===========================================\n"); ++ seq_printf(seq, " %02d\n", count_idx); ++ seq_printf(seq, "===========================================\n"); ++ count_idx++; ++ count_idx = count_idx%100; ++ PUT_COUNTER ("RtsSuccess: ", RtsSuccess); ++ PUT_COUNTER ("RtsFailures: ", RtsFailures); ++ seq_printf(seq, "Avg_AMPDU_Len: %d\n",__le32_to_cpu(avg_ampdu_len)); ++ PUT_AMPDU_COUNTER("TxAMPDUs: ", TxAMPDUs); ++ PUT_AMPDU_COUNTER("TxMPDUsInAMPDUs: ", TxMPDUsInAMPDUs); ++// PUT_COUNTER ("TxFrameRetries: ", AckFailures); ++// PUT_COUNTER ("TxFrameFailures: ", TxFrameFailures); ++ PUT_TXPIPE ("Failure_By_Rts: ", a); ++/* PUT_AMPDU_COUNTER("BA-RX-Fails ", ImplictBARFailures); ++// PUT_AMPDU_COUNTER("TxAMPDUs ", TxAMPDUs); ++// PUT_TXPIPE ("ReTx-AMPDUs ", 2); ++// PUT_TXPIPE ("Retry_type1 ", 3); ++// PUT_TXPIPE ("Retry_type2 ", 4); ++// PUT_TXPIPE ("Retry_type3 ", 5); ++*/ ++ seq_printf(seq, "==============\n"); ++ seq_printf(seq, "FrameFail_ratio: %d%%%%\n", __le32_to_cpu(FrameFail_ratio)); ++ seq_printf(seq, "FailByRts_ratio: %d%%\n", __le32_to_cpu(FailByRts_ratio)); ++ seq_printf(seq, "FrameRetry_ratio: %d%%\n", __le32_to_cpu(FrameRetry_ratio)); ++ seq_printf(seq, "AMPDURetry_ratio: %d%%\n", __le32_to_cpu(AMPDURetry_ratio)); ++ seq_printf(seq, "Retry1_ratio: %d%%\n", __le32_to_cpu(Retry1_ratio)); ++ seq_printf(seq, "Retry2_ratio: %d%%\n", __le32_to_cpu(Retry2_ratio)); ++ seq_printf(seq, "Retry3_ratio: %d%%\n", __le32_to_cpu(Retry3_ratio)); ++ ++ seq_printf(seq, "==============\n"); ++ PUT_RATE_COUNT("65 Mbps:", 21); ++ PUT_RATE_COUNT("58.5 Mbps:", 20); ++ PUT_RATE_COUNT("52 Mbps:", 19); ++ PUT_RATE_COUNT("39 Mbps:", 18); ++ PUT_RATE_COUNT("26 Mbps:", 17); ++ PUT_RATE_COUNT("19.5 Mbps:", 16); ++ PUT_RATE_COUNT("13 Mbps:", 15); ++ PUT_RATE_COUNT("6.5 Mbps:", 14); ++ ++#undef PUT_COUNTER ++#undef PUT_AMPDU_COUNTER ++#undef PUT_TXPIPE ++#undef PUT_RATE_COUNT ++#undef CAT_STR ++ ++ return 0; ++} ++ ++static int xradio_dbgstats_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_dbgstats_show, inode->i_private); ++} ++ ++static const struct file_operations fops_dbgstats = { ++ .open = xradio_dbgstats_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++static int xradio_generic_open(struct inode *inode, struct file *file) ++{ ++ file->private_data = inode->i_private; ++ return 0; ++} ++ ++static ssize_t xradio_11n_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ struct ieee80211_supported_band *band = ++ hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ]; ++ return simple_read_from_buffer(user_buf, count, ppos, ++ band->ht_cap.ht_supported ? "1\n" : "0\n", 2); ++} ++ ++static ssize_t xradio_11n_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ struct ieee80211_supported_band *band[2] = { ++ hw_priv->hw->wiphy->bands[NL80211_BAND_2GHZ], ++ hw_priv->hw->wiphy->bands[NL80211_BAND_5GHZ], ++ }; ++ char buf[1]; ++ int ena = 0; ++ ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, 1)) ++ return -EFAULT; ++ if (buf[0] == 1) ++ ena = 1; ++ ++ band[0]->ht_cap.ht_supported = ena; ++#ifdef CONFIG_XRADIO_5GHZ_SUPPORT ++ band[1]->ht_cap.ht_supported = ena; ++#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ ++ ++ return count; ++} ++ ++static const struct file_operations fops_11n = { ++ .open = xradio_generic_open, ++ .read = xradio_11n_read, ++ .write = xradio_11n_write, ++ .llseek = default_llseek, ++}; ++ ++ ++static u32 fwdbg_ctrl; ++ ++static ssize_t xradio_fwdbg_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[12] = {0}; ++ char* endptr = NULL; ++ ++ count = (count > 11 ? 11 : count); ++ ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ fwdbg_ctrl = simple_strtoul(buf, &endptr, 16); ++ xradio_dbg(XRADIO_DBG_ALWY,"fwdbg_ctrl = %d\n", fwdbg_ctrl); ++ SYS_WARN(wsm_set_fw_debug_control(hw_priv, fwdbg_ctrl, 0)); ++ ++ return count; ++} ++ ++static ssize_t xradio_fwdbg_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ //struct xradio_common *hw_priv = file->private_data; ++ char buf[50]; ++ size_t size = 0; ++ ++ sprintf(buf, "fwdbg_ctrl = %u\n", fwdbg_ctrl); ++ size = strlen(buf); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++static const struct file_operations fops_fwdbg = { ++ .open = xradio_generic_open, ++ .write = xradio_fwdbg_write, ++ .read = xradio_fwdbg_read, ++ .llseek = default_llseek, ++}; ++ ++//add by yangfh for read/write fw registers ++static ssize_t xradio_fwreg_rw(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[256] = {0}; ++ u16 buf_size = (count > 255 ? 255 : count); ++ char* startptr = &buf[0]; ++ char* endptr = NULL; ++ u16 flag = 0; ++ int i, end = 16; ++ ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, buf_size)) ++ return -EFAULT; ++ ++ flag = simple_strtoul(startptr, &endptr, 16); ++ startptr = endptr+1; ++ if(flag & WSM_REG_RW_F) { //write ++ WSM_REG_W reg_w; ++ reg_w.flag = flag; ++ reg_w.data_size = 0; ++ if(flag&WSM_REG_BK_F) end = 2; ++ ++ for(i=0; (iendptr); 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*)®_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; (iendptr); 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*)®_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; iprivate_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; (iendptr); 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; (iendptr); 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, &du_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; ii_private); ++} ++ ++static const struct file_operations fops_tpa_debug = { ++ .open = xradio_tpa_debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++//policy_info ++u32 tx_retrylimit = 0; ++u32 tx_lower_limit = 0; ++u32 tx_over_limit = 0; ++int retry_mis = 0; ++u32 policy_upload = 0; ++u32 policy_num = 0; ++ ++static ssize_t xradio_policy_info(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ //struct xradio_common *hw_priv = file->private_data; ++ char buf[256]; ++ size_t size = 0; ++ sprintf(buf, "tx_retrylimit=%d, tx_lower_limit=%d, tx_over_limit=%d, retry_mis=%d\n" \ ++ "policy_upload=%d, policy_num=%d\n", ++ tx_retrylimit, tx_lower_limit, tx_over_limit, retry_mis, ++ policy_upload, policy_num); ++ size = strlen(buf); ++ ++ //clear counters ++ tx_retrylimit = 0; ++ tx_lower_limit = 0; ++ tx_over_limit = 0; ++ retry_mis = 0; ++ policy_upload = 0; ++ policy_num = 0; ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++ ++static const struct file_operations fops_policy_info = { ++ .open = xradio_generic_open, ++ .read = xradio_policy_info, ++ .llseek = default_llseek, ++}; ++ ++//info of interruption ++u32 irq_count = 0; ++u32 int_miss_cnt = 0; ++u32 fix_miss_cnt = 0; ++u32 next_rx_cnt = 0; ++u32 rx_total_cnt = 0; ++u32 tx_total_cnt = 0; ++ ++static ssize_t xradio_bh_statistic(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ //struct xradio_common *hw_priv = file->private_data; ++ char buf[256]; ++ size_t size = 0; ++ sprintf(buf, "irq_count=%d, rx_total=%d, miss=%d, fix=%d, next=%d, " ++ "tx_total=%d\n", ++ irq_count, rx_total_cnt, int_miss_cnt, fix_miss_cnt, next_rx_cnt, ++ tx_total_cnt); ++ size = strlen(buf); ++ ++ //clear counters ++ irq_count = 0; ++ int_miss_cnt = 0; ++ fix_miss_cnt = 0; ++ next_rx_cnt = 0; ++ rx_total_cnt = 0; ++ tx_total_cnt = 0; ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++ ++static const struct file_operations fops_bh_stat = { ++ .open = xradio_generic_open, ++ .read = xradio_bh_statistic, ++ .llseek = default_llseek, ++}; ++ ++//add by yangfh for disable low power mode. ++extern u16 txparse_flags; ++extern u16 rxparse_flags; ++static ssize_t xradio_parse_flags_get(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ //struct xradio_common *hw_priv = file->private_data; ++ char buf[100]; ++ size_t size = 0; ++ ++ sprintf(buf, "txparse=0x%04x, rxparse=0x%04x\n", txparse_flags, rxparse_flags); ++ size = strlen(buf); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++ ++static ssize_t xradio_parse_flags_set(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ //struct xradio_common *hw_priv = file->private_data; ++ char buf[30] = {0}; ++ char* start = &buf[0]; ++ char* endptr = NULL; ++ ++ count = (count > 29 ? 29 : count); ++ ++ if(!count) ++ return -EINVAL; ++ if(copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ txparse_flags = simple_strtoul(buf, &endptr, 16); ++ start = endptr+1; ++ if(start < buf+count) ++ rxparse_flags = simple_strtoul(start, &endptr, 16); ++ ++ txparse_flags &= 0x7fff; ++ rxparse_flags &= 0x7fff; ++ ++ xradio_dbg(XRADIO_DBG_ALWY, "txparse=0x%04x, rxparse=0x%04x\n", ++ txparse_flags, rxparse_flags); ++ return count; ++} ++ ++static const struct file_operations fops_parse_flags = { ++ .open = xradio_generic_open, ++ .write = xradio_parse_flags_set, ++ .read = xradio_parse_flags_get, ++ .llseek = default_llseek, ++}; ++ ++#if defined(DGB_XRADIO_HWT) ++extern u8 hwt_testing = 0; ++//HIF TX test ++u8 hwt_tx_en = 0; ++u8 hwt_tx_cfm = 0; //confirm interval ++u16 hwt_tx_len = 0; // ++u16 hwt_tx_num = 0; // ++struct timeval hwt_start_time = {0}; ++struct timeval hwt_end_time = {0}; ++int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg, ++ size_t arg_size); ++ ++static ssize_t xradio_hwt_hif_tx(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[100] = {0}; ++ char* start = &buf[0]; ++ char* endptr = NULL; ++ u8 test_en = 0; ++ ++ if (hwt_testing) { ++ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n"); ++ return count; ++ } ++ ++ count = (count > 99 ? 99 : count); ++ if(!count) ++ return -EINVAL; ++ if(copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ if(simple_strtoul(start, &endptr, 10)) { ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_tx_len = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_tx_num = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_tx_cfm = simple_strtoul(start, &endptr, 10); ++ hwt_tx_en = 1; ++ hwt_testing = 1; ++ } else { ++ hwt_tx_en = 0; ++ } ++ xradio_dbg(XRADIO_DBG_ALWY,"hwt_tx_en=%d, hwt_tx_len=%d, hwt_tx_num=%d, hwt_tx_cfm=%d\n", ++ hwt_tx_en, hwt_tx_len, hwt_tx_num, hwt_tx_cfm); ++ ++ if (!hw_priv->bh_error && ++ atomic_add_return(1, &hw_priv->bh_tx) == 1) ++ wake_up(&hw_priv->bh_wq); ++ return count; ++} ++static const struct file_operations fops_hwt_hif_tx = { ++ .open = xradio_generic_open, ++ .write = xradio_hwt_hif_tx, ++ .llseek = default_llseek, ++}; ++ ++//HIF RX test ++u8 hwt_rx_en = 0; ++u16 hwt_rx_len = 0; // ++u16 hwt_rx_num = 0; // ++static ssize_t xradio_hwt_hif_rx(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[100] = {0}; ++ char* start = &buf[0]; ++ char* endptr = NULL; ++ u8 test_en = 0; ++ struct wsm_buf *wsm_buf = &hw_priv->wsm_cmd_buf; ++ ++ if (hwt_testing) { ++ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n"); ++ return count; ++ } ++ ++ count = (count > 99 ? 99 : count); ++ if(!count) ++ return -EINVAL; ++ if(copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ if(simple_strtoul(start, &endptr, 10)) { ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_rx_len = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_rx_num = simple_strtoul(start, &endptr, 10); ++ ++ hwt_rx_en = 1; ++ } else { ++ hwt_rx_en = 0; ++ } ++ xradio_dbg(XRADIO_DBG_ALWY,"hwt_rx_en=%d, hwt_rx_len=%d, hwt_rx_num=%d\n", ++ hwt_rx_en, hwt_rx_len, hwt_rx_num); ++ ++ //check the parameters. ++ if (hwt_rx_len < 100 || hwt_rx_len > 1500) ++ hwt_rx_len = 1500; ++ if (hwt_rx_en && hwt_rx_num) { ++ HWT_PARAMETERS hwt_hdr = { ++ .TestID = 0x0002, ++ .Params = hwt_rx_num, ++ .Data = hwt_rx_len ++ }; ++ hwt_testing = 1; ++ wsm_hwt_cmd(hw_priv, (void *)&hwt_hdr.TestID, sizeof(hwt_hdr)-4); ++ do_gettimeofday(&hwt_start_time); ++ } ++ ++ return count; ++} ++static const struct file_operations fops_hwt_hif_rx = { ++ .open = xradio_generic_open, ++ .write = xradio_hwt_hif_rx, ++ .llseek = default_llseek, ++}; ++ ++//ENC test ++u8 hwt_enc_type = 0; ++u8 hwt_key_len = 0; // ++u16 hwt_enc_len = 0; // ++u16 hwt_enc_cnt = 0; // ++static ssize_t xradio_hwt_enc(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[100] = {0}; ++ char* start = &buf[0]; ++ char* endptr = NULL; ++ u8 test_en = 0; ++ struct wsm_buf *wsm_buf = &hw_priv->wsm_cmd_buf; ++ ++ if (hwt_testing) { ++ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n"); ++ return count; ++ } ++ ++ count = (count > 99 ? 99 : count); ++ if(!count) ++ return -EINVAL; ++ if(copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ hwt_enc_type = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_key_len = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_enc_len = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_enc_cnt = simple_strtoul(start, &endptr, 10); ++ ++ xradio_dbg(XRADIO_DBG_ALWY,"enc_type=%d, key_len=%d, enc_len=%d, enc_cnt=%d\n", ++ hwt_enc_type, hwt_key_len, hwt_enc_len, hwt_enc_cnt); ++ ++ //check the parameters. ++ if (hwt_enc_type < 10 && hwt_key_len <= 16 || ++ hwt_enc_len <= 1500 || hwt_enc_cnt > 0) { ++ HWT_PARAMETERS hwt_hdr = { ++ .TestID = 0x0003, ++ .Params = (hwt_key_len<<8) | hwt_enc_type, ++ .Datalen = hwt_enc_len, ++ .Data = hwt_enc_cnt ++ }; ++ hwt_testing = 1; ++ wsm_hwt_cmd(hw_priv, (void *)&hwt_hdr.TestID, sizeof(hwt_hdr)-4); ++ } ++ ++ return count; ++} ++static const struct file_operations fops_hwt_enc = { ++ .open = xradio_generic_open, ++ .write = xradio_hwt_enc, ++ .llseek = default_llseek, ++}; ++ ++//MIC test ++u16 hwt_mic_len = 0; // ++u16 hwt_mic_cnt = 0; // ++static ssize_t xradio_hwt_mic(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[100] = {0}; ++ char* start = &buf[0]; ++ char* endptr = NULL; ++ u8 test_en = 0; ++ struct wsm_buf *wsm_buf = &hw_priv->wsm_cmd_buf; ++ ++ if (hwt_testing) { ++ xradio_dbg(XRADIO_DBG_ALWY,"cmd refuse, hwt is testing!\n"); ++ return count; ++ } ++ ++ count = (count > 99 ? 99 : count); ++ if(!count) ++ return -EINVAL; ++ if(copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ hwt_mic_len = simple_strtoul(start, &endptr, 10); ++ start = endptr+1; ++ if(start < buf+count) ++ hwt_mic_cnt = simple_strtoul(start, &endptr, 10); ++ ++ xradio_dbg(XRADIO_DBG_ALWY,"mic_len=%d, mic_cnt=%d\n", hwt_mic_len, hwt_mic_cnt); ++ ++ //check the parameters. ++ if (hwt_mic_len <= 1500 || hwt_mic_cnt > 0) { ++ HWT_PARAMETERS hwt_hdr = { ++ .TestID = 0x0004, ++ .Params = 0, ++ .Datalen = hwt_mic_len, ++ .Data = hwt_mic_cnt ++ }; ++ hwt_testing = 1; ++ wsm_hwt_cmd(hw_priv, (void *)&hwt_hdr.TestID, sizeof(hwt_hdr)-4); ++ } ++ ++ return count; ++} ++static const struct file_operations fops_hwt_mic = { ++ .open = xradio_generic_open, ++ .write = xradio_hwt_mic, ++ .llseek = default_llseek, ++}; ++#endif //DGB_XRADIO_HWT ++ ++static u32 measure_type; ++ ++static ssize_t xradio_measure_type_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[12] = {0}; ++ char* endptr = NULL; ++ count = (count > 11 ? 11 : count); ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ measure_type = simple_strtoul(buf, &endptr, 16); ++ ++ xradio_dbg(XRADIO_DBG_ALWY,"measure_type = %08x\n", measure_type); ++ SYS_WARN(wsm_11k_measure_requset(hw_priv, (measure_type&0xff), ++ ((measure_type&0xff00)>>8), ++ ((measure_type&0xffff0000)>>16))); ++ return count; ++} ++ ++static ssize_t xradio_measure_type_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ //struct xradio_common *hw_priv = file->private_data; ++ char buf[20]; ++ size_t size = 0; ++ ++ sprintf(buf, "measure_type = %u\n", measure_type); ++ size = strlen(buf); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++static const struct file_operations fops_11k = { ++ .open = xradio_generic_open, ++ .write = xradio_measure_type_write, ++ .read = xradio_measure_type_read, ++ .llseek = default_llseek, ++}; ++ ++ ++static ssize_t xradio_wsm_dumps(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[1]; ++ ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, 1)) ++ return -EFAULT; ++ ++ if (buf[0] == '1') ++ hw_priv->wsm_enable_wsm_dumps = 1; ++ else ++ hw_priv->wsm_enable_wsm_dumps = 0; ++ ++ return count; ++} ++ ++static const struct file_operations fops_wsm_dumps = { ++ .open = xradio_generic_open, ++ .write = xradio_wsm_dumps, ++ .llseek = default_llseek, ++}; ++ ++static ssize_t xradio_short_dump_read(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *hw_priv = file->private_data; ++ char buf[20]; ++ size_t size = 0; ++ ++ sprintf(buf, "Size: %u\n", hw_priv->wsm_dump_max_size); ++ size = strlen(buf); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++ ++static ssize_t xradio_short_dump_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_common *priv = file->private_data; ++ char buf[20]; ++ unsigned long dump_size = 0; ++ ++ if (!count || count > 20) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, count)) ++ return -EFAULT; ++ ++ if (kstrtoul(buf, 10, &dump_size)) ++ return -EINVAL; ++ xradio_dbg(XRADIO_DBG_ALWY,"%s get %lu\n", __func__, dump_size); ++ ++ priv->wsm_dump_max_size = dump_size; ++ ++ return count; ++} ++ ++static const struct file_operations fops_short_dump = { ++ .open = xradio_generic_open, ++ .write = xradio_short_dump_write, ++ .read = xradio_short_dump_read, ++ .llseek = default_llseek, ++}; ++ ++ ++static int xradio_status_show_priv(struct seq_file *seq, void *v) ++{ ++ int i; ++ struct xradio_vif *priv = seq->private; ++ struct xradio_debug_priv *d = priv->debug; ++ ++ seq_printf(seq, "Mode: %s%s\n", ++ xradio_debug_mode(priv->mode), ++ priv->listening ? " (listening)" : ""); ++ seq_printf(seq, "Assoc: %s\n", ++ xradio_debug_join_status[priv->join_status]); ++ if (priv->rx_filter.promiscuous) ++ seq_puts(seq, "Filter: promisc\n"); ++ else if (priv->rx_filter.fcs) ++ seq_puts(seq, "Filter: fcs\n"); ++ if (priv->rx_filter.bssid) ++ seq_puts(seq, "Filter: bssid\n"); ++ if (priv->bf_control.bcn_count) ++ seq_puts(seq, "Filter: beacons\n"); ++ ++ if (priv->enable_beacon || ++ priv->mode == NL80211_IFTYPE_AP || ++ priv->mode == NL80211_IFTYPE_ADHOC || ++ priv->mode == NL80211_IFTYPE_MESH_POINT || ++ priv->mode == NL80211_IFTYPE_P2P_GO) ++ seq_printf(seq, "Beaconing: %s\n", ++ priv->enable_beacon ? ++ "enabled" : "disabled"); ++ if (priv->ssid_length || ++ priv->mode == NL80211_IFTYPE_AP || ++ priv->mode == NL80211_IFTYPE_ADHOC || ++ priv->mode == NL80211_IFTYPE_MESH_POINT || ++ priv->mode == NL80211_IFTYPE_P2P_GO) ++ seq_printf(seq, "SSID: %.*s\n", ++ priv->ssid_length, priv->ssid); ++ ++ for (i = 0; i < 4; ++i) { ++ seq_printf(seq, "EDCA(%d): %d, %d, %d, %d, %d\n", i, ++ priv->edca.params[i].cwMin, ++ priv->edca.params[i].cwMax, ++ priv->edca.params[i].aifns, ++ priv->edca.params[i].txOpLimit, ++ priv->edca.params[i].maxReceiveLifetime); ++ } ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ static const char *pmMode = "unknown"; ++ switch (priv->powersave_mode.pmMode) { ++ case WSM_PSM_ACTIVE: ++ pmMode = "off"; ++ break; ++ case WSM_PSM_PS: ++ pmMode = "on"; ++ break; ++ case WSM_PSM_FAST_PS: ++ pmMode = "dynamic"; ++ break; ++ } ++ seq_printf(seq, "Preamble: %s\n", ++ xradio_debug_preamble[ ++ priv->association_mode.preambleType]); ++ seq_printf(seq, "AMPDU spcn: %d\n", ++ priv->association_mode.mpduStartSpacing); ++ seq_printf(seq, "Basic rate: 0x%.8X\n", ++ le32_to_cpu(priv->association_mode.basicRateSet)); ++ seq_printf(seq, "Bss lost: %d beacons\n", ++ priv->bss_params.beaconLostCount); ++ seq_printf(seq, "AID: %d\n", ++ priv->bss_params.aid); ++ seq_printf(seq, "Rates: 0x%.8X\n", ++ priv->bss_params.operationalRateSet); ++ seq_printf(seq, "Powersave: %s\n", pmMode); ++ } ++ seq_printf(seq, "RSSI thold: %d\n", ++ priv->cqm_rssi_thold); ++ seq_printf(seq, "RSSI hyst: %d\n", ++ priv->cqm_rssi_hyst); ++ seq_printf(seq, "TXFL thold: %d\n", ++ priv->cqm_tx_failure_thold); ++ seq_printf(seq, "Linkloss: %d\n", ++ priv->cqm_link_loss_count); ++ seq_printf(seq, "Bcnloss: %d\n", ++ priv->cqm_beacon_loss_count); ++ ++ xradio_debug_print_map(seq, priv, "Link map: ", ++ priv->link_id_map); ++ xradio_debug_print_map(seq, priv, "Asleep map: ", ++ priv->sta_asleep_mask); ++ xradio_debug_print_map(seq, priv, "PSPOLL map: ", ++ priv->pspoll_mask); ++ ++ seq_puts(seq, "\n"); ++ ++ for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { ++ if (priv->link_id_db[i].status) { ++ seq_printf(seq, "Link %d: %s, %pM\n", ++ i + 1, xradio_debug_link_id[ ++ priv->link_id_db[i].status], ++ priv->link_id_db[i].mac); ++ } ++ } ++ ++ seq_puts(seq, "\n"); ++ ++ seq_printf(seq, "Powermgmt: %s\n", ++ priv->powersave_enabled ? "on" : "off"); ++ ++ seq_printf(seq, "TXed: %d\n", ++ d->tx); ++ seq_printf(seq, "AGG TXed: %d\n", ++ d->tx_agg); ++ seq_printf(seq, "MULTI TXed: %d (%d)\n", ++ d->tx_multi, d->tx_multi_frames); ++ seq_printf(seq, "RXed: %d\n", ++ d->rx); ++ seq_printf(seq, "AGG RXed: %d\n", ++ d->rx_agg); ++ seq_printf(seq, "TX align: %d\n", ++ d->tx_align); ++ seq_printf(seq, "TX TTL: %d\n", ++ d->tx_ttl); ++ return 0; ++} ++ ++static int xradio_status_open_priv(struct inode *inode, struct file *file) ++{ ++ return single_open(file, &xradio_status_show_priv, ++ inode->i_private); ++} ++ ++static const struct file_operations fops_status_priv = { ++ .open = xradio_status_open_priv, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ ++static ssize_t xradio_hang_write(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_vif *priv = file->private_data; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ char buf[1]; ++ ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, 1)) ++ return -EFAULT; ++ ++ if (priv->vif) { ++ xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ); ++ //ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); ++ } else ++ return -ENODEV; ++ ++ return count; ++} ++ ++static const struct file_operations fops_hang = { ++ .open = xradio_generic_open, ++ .write = xradio_hang_write, ++ .llseek = default_llseek, ++}; ++#endif ++ ++#ifdef AP_HT_COMPAT_FIX ++extern u8 ap_compat_bssid[ETH_ALEN]; ++static ssize_t xradio_ht_compat_show(struct file *file, ++ char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_vif *priv = file->private_data; ++ char buf[100]; ++ size_t size = 0; ++ sprintf(buf, "ht_compat_det=0x%x, BSSID=%02x:%02x:%02x:%02x:%02x:%02x\n", ++ priv->ht_compat_det, ++ ap_compat_bssid[0], ap_compat_bssid[1], ++ ap_compat_bssid[2], ap_compat_bssid[3], ++ ap_compat_bssid[4], ap_compat_bssid[5]); ++ size = strlen(buf); ++ ++ return simple_read_from_buffer(user_buf, count, ppos, buf, size); ++} ++ ++static ssize_t xradio_ht_compat_disalbe(struct file *file, ++ const char __user *user_buf, size_t count, loff_t *ppos) ++{ ++ struct xradio_vif *priv = file->private_data; ++ char buf[2]; ++ char *endptr = NULL; ++ ++ if (!count) ++ return -EINVAL; ++ if (copy_from_user(buf, user_buf, 2)) ++ return -EFAULT; ++ ++ if (simple_strtoul(buf, &endptr, 10)) ++ priv->ht_compat_det |= 0x10; ++ else ++ priv->ht_compat_det &= ~0x10; ++ return count; ++} ++ ++static const struct file_operations fops_ht_compat_dis = { ++ .open = xradio_generic_open, ++ .read = xradio_ht_compat_show, ++ .write = xradio_ht_compat_disalbe, ++ .llseek = default_llseek, ++}; ++#endif ++ ++#define VIF_DEBUGFS_NAME_S 10 ++int xradio_debug_init_priv(struct xradio_common *hw_priv, ++ struct xradio_vif *priv) ++{ ++ int ret = -ENOMEM; ++ struct xradio_debug_priv *d; ++ char name[VIF_DEBUGFS_NAME_S]; ++ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ if (SYS_WARN(!hw_priv)) ++ return ret; ++ ++ if (SYS_WARN(!hw_priv->debug)) ++ return ret; ++ ++ d = kzalloc(sizeof(struct xradio_debug_priv), GFP_KERNEL); ++ priv->debug = d; ++ if (SYS_WARN(!d)) ++ return ret; ++ ++ memset(name, 0, VIF_DEBUGFS_NAME_S); ++ ret = snprintf(name, VIF_DEBUGFS_NAME_S, "vif_%d", priv->if_id); ++ if (SYS_WARN(ret < 0)) ++ goto err; ++ ++ d->debugfs_phy = debugfs_create_dir(name, ++ hw_priv->debug->debugfs_phy); ++ if (SYS_WARN(!d->debugfs_phy)) ++ goto err; ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ if (SYS_WARN(!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy, ++ priv, &fops_hang))) ++ goto err; ++#endif ++ ++#if defined(AP_HT_COMPAT_FIX) ++ if (SYS_WARN(!debugfs_create_file("htcompat_disable", S_IWUSR, d->debugfs_phy, ++ priv, &fops_ht_compat_dis))) ++ goto err; ++#endif ++ ++ if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, ++ priv, &fops_status_priv)) ++ goto err; ++ ++ return 0; ++err: ++ priv->debug = NULL; ++ debugfs_remove_recursive(d->debugfs_phy); ++ kfree(d); ++ return ret; ++ ++} ++ ++void xradio_debug_release_priv(struct xradio_vif *priv) ++{ ++ struct xradio_debug_priv *d = priv->debug; ++ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ if (d) { ++ priv->debug = NULL; ++ debugfs_remove_recursive(d->debugfs_phy); ++ kfree(d); ++ } ++} ++ ++int xradio_print_fw_version(struct xradio_common *hw_priv, u8* buf, size_t len) ++{ ++ return snprintf(buf, len, "%s %d.%d", ++ xradio_debug_fw_types[hw_priv->wsm_caps.firmwareType], ++ hw_priv->wsm_caps.firmwareVersion, ++ hw_priv->wsm_caps.firmwareBuildNumber); ++} ++ ++/*added by yangfh, for host debuglevel*/ ++struct dentry *debugfs_host = NULL; ++#if defined(DGB_XRADIO_QC) ++struct dentry *debugfs_hwinfo = NULL; ++#endif ++int xradio_host_dbg_init(void) ++{ ++ int line = 0; ++ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ if(!debugfs_initialized()) { ++ xradio_dbg(XRADIO_DBG_ERROR, "debugfs isnot initialized\n"); ++ return 0; ++ } ++ ++#define ERR_LINE do { line = __LINE__; goto err;} while(0) ++ ++ debugfs_host = debugfs_create_dir("xradio_host_dbg", NULL); ++ if (!debugfs_host) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_common", S_IRUSR | S_IWUSR, debugfs_host, &dbg_common)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_sbus", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sbus)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_ap", S_IRUSR | S_IWUSR, debugfs_host, &dbg_ap)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_sta", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sta)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_scan", S_IRUSR | S_IWUSR, debugfs_host, &dbg_scan)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_bh", S_IRUSR | S_IWUSR, debugfs_host, &dbg_bh)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_txrx", S_IRUSR | S_IWUSR, debugfs_host, &dbg_txrx)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_wsm", S_IRUSR | S_IWUSR, debugfs_host, &dbg_wsm)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_pm", S_IRUSR | S_IWUSR, debugfs_host, &dbg_pm)) ++ ERR_LINE; ++ ++#ifdef CONFIG_XRADIO_ITP ++ if (!debugfs_create_x8("dbg_itp", S_IRUSR | S_IWUSR, debugfs_host, &dbg_itp)) ++ ERR_LINE; ++#endif ++ ++ if (!debugfs_create_x8("dbg_logfile", S_IRUSR | S_IWUSR, debugfs_host, &dbg_logfile)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x8("dbg_tpa_node", S_IRUSR | S_IWUSR, debugfs_host, &tpa_node_dbg)) ++ ERR_LINE; ++ ++ if (!debugfs_create_x32("set_sdio_clk", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sdio_clk)) ++ ERR_LINE; ++ ++ return 0; ++ ++#undef ERR_LINE ++err: ++ xradio_dbg(XRADIO_DBG_ERROR, "xradio_host_dbg_init failed=%d\n", line); ++ if (debugfs_host) ++ debugfs_remove_recursive(debugfs_host); ++#if defined(DGB_XRADIO_QC) ++ debugfs_hwinfo = NULL; ++#endif ++ debugfs_host = NULL; ++ return 0; ++} ++ ++void xradio_host_dbg_deinit(void) ++{ ++ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ if(debugfs_host) ++ debugfs_remove_recursive(debugfs_host); ++#if defined(DGB_XRADIO_QC) ++ debugfs_hwinfo = NULL; ++#endif ++ debugfs_host = NULL; ++} ++ ++int xradio_debug_init_common(struct xradio_common *hw_priv) ++{ ++ int ret = -ENOMEM; ++ int line = 0; ++ struct xradio_debug_common *d = NULL; ++ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ //init some debug variables here. ++ retry_dbg = 0; ++ tpa_node_dbg = 0; ++ ++#define ERR_LINE do { line = __LINE__; goto err;} while(0) ++ ++ d = kzalloc(sizeof(struct xradio_debug_common), GFP_KERNEL); ++ hw_priv->debug = d; ++ if (!d) { ++ xradio_dbg(XRADIO_DBG_ERROR,"%s, xr_kzalloc failed!\n", __FUNCTION__); ++ return ret; ++ } ++ ++ d->debugfs_phy = debugfs_create_dir("xradio", hw_priv->hw->wiphy->debugfsdir); ++ if (!d->debugfs_phy) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("version", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_version)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_status_common)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_counters)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("backoff", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_backoff)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("txpipe", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_txpipe)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("ampdu", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_ampducounters)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("ratemap", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_ratemap)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("dbgstats", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_dbgstats)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR, ++ d->debugfs_phy, hw_priv, &fops_11n)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_wsm_dumps)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("set_fwdbg", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_fwdbg)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("rw_fwreg", S_IWUSR, d->debugfs_phy, hw_priv, ++ &fops_rw_fwreg)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("rw_fwreg_direct", S_IWUSR, d->debugfs_phy, hw_priv, ++ &fops_rw_fwreg_direct)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("set_ampdu_len", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_ampdu_len)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("set_rts_threshold", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_rts_threshold)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("low_pwr_disable", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_low_pwr)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("ps_disable", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_ps_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("retry_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_retry_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("rates_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_rates_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("backoff_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_backoff_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("tala_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_tala_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("tx_pwr_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_tx_pwr_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("tpa_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_tpa_ctrl)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("tpa_debug", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_tpa_debug)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("policy_info", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_policy_info)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("bh_stat", S_IRUSR, d->debugfs_phy, ++ hw_priv, &fops_bh_stat)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("parse_flags", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_parse_flags)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("set_measure_type", S_IRUSR | S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_11k)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR,d->debugfs_phy, ++ hw_priv, &fops_short_dump)) ++ ERR_LINE; ++ ++#if defined(DGB_XRADIO_HWT) ++ //hardware test ++ if (!debugfs_create_file("hwt_hif_tx", S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_hwt_hif_tx)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("hwt_hif_rx", S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_hwt_hif_rx)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("hwt_enc", S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_hwt_enc)) ++ ERR_LINE; ++ ++ if (!debugfs_create_file("hwt_mic", S_IWUSR, d->debugfs_phy, ++ hw_priv, &fops_hwt_mic)) ++ ERR_LINE; ++#endif //DGB_XRADIO_HWT ++ ++#if defined(DGB_XRADIO_QC) ++ //for QC apk read. ++ if (debugfs_host && !debugfs_hwinfo) { ++ debugfs_hwinfo = debugfs_create_file("hwinfo", 0666, debugfs_host, ++ hw_priv, &fops_hwinfo); ++ if (!debugfs_hwinfo) ++ ERR_LINE; ++ } ++#endif ++ ++ return 0; ++ ++#undef ERR_LINE ++ ++err: ++ xradio_dbg(XRADIO_DBG_ERROR, "xradio_debug_init_common failed=%d\n", line); ++ hw_priv->debug = NULL; ++ debugfs_remove_recursive(d->debugfs_phy); ++ kfree(d); ++ return ret; ++} ++ ++void xradio_debug_release_common(struct xradio_common *hw_priv) ++{ ++ struct xradio_debug_common *d = hw_priv->debug; ++ xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++#if defined(DGB_XRADIO_QC) ++ if (debugfs_hwinfo) { ++ debugfs_remove(debugfs_hwinfo); ++ debugfs_hwinfo = NULL; ++ } ++#endif ++ if (d) { ++ hw_priv->debug = NULL; ++ //removed by mac80211, don't remove it again, fixed wifi on/off by yangfh ++ //debugfs_remove_recursive(d->debugfs_phy); ++ kfree(d); ++ } ++} ++#endif //CONFIG_XRADIO_DEBUGFS ++ ++#define FRAME_TYPE(xx) ieee80211_is_ ## xx(fctl) ++#define FT_MSG_PUT(f, ...) do{ \ ++ if(flags&f) frame_msg += sprintf(frame_msg, __VA_ARGS__); \ ++ }while(0) ++ ++#define PT_MSG_PUT(f, ...) do{ \ ++ if(flags&f) proto_msg += sprintf(proto_msg, __VA_ARGS__); \ ++ }while(0) ++ ++#define FRAME_PARSE(f, name) do{ \ ++ if(FRAME_TYPE(name)) { FT_MSG_PUT(f, "%s", #name); goto outprint;} \ ++ } while(0) ++ ++#define IS_FRAME_PRINT (frame_msg != (char *)&framebuf[0]) ++#define IS_PROTO_PRINT (proto_msg != (char *)&protobuf[0]) ++ ++ ++char framebuf[512] = {0}; ++char protobuf[512] = {0}; ++ ++char * p2p_frame_type[] = { ++ "GO Negotiation Request", ++ "GO Negotiation Response", ++ "GO Negotiation Confirmation", ++ "P2P Invitation Request", ++ "P2P Invitation Response", ++ "Device Discoverability Request", ++ "Device Discoverability Response", ++ "Provision Discovery Request", ++ "Provision Discovery Response", ++ "Reserved" ++}; ++#if (defined(CONFIG_XRADIO_DEBUG)) ++void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id) ++{ ++ char * frame_msg = &framebuf[0]; ++ char * proto_msg = &protobuf[0]; ++ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)mac_data; ++ u16 fctl = frame->frame_control; ++ ++ memset(frame_msg, 0, sizeof(frame_msg)); ++ memset(proto_msg, 0, sizeof(proto_msg)); ++ ++ if(ieee80211_is_data(fctl)) { ++ u8 machdrlen = ieee80211_hdrlen(fctl); ++ u8* llc_data = mac_data+machdrlen+iv_len; ++ ++ if(ieee80211_is_qos_nullfunc(fctl)){ ++ FT_MSG_PUT(PF_DATA, "QoS-NULL"); ++ goto outprint; ++ } ++ if(ieee80211_is_data_qos(fctl)) ++ FT_MSG_PUT(PF_DATA, "QoS"); ++ if(ieee80211_is_nullfunc(fctl)) { ++ FT_MSG_PUT(PF_DATA, "NULL(ps=%d)", !!(fctl&IEEE80211_FCTL_PM)); ++ goto outprint; ++ } ++ FT_MSG_PUT(PF_DATA, "data(TDFD=%d%d,R=%d,P=%d)", ++ !!(fctl&IEEE80211_FCTL_TODS), !!(fctl&IEEE80211_FCTL_FROMDS), ++ !!(fctl&IEEE80211_FCTL_RETRY),!!(fctl&IEEE80211_FCTL_PROTECTED)); ++ ++ if (is_SNAP(llc_data)) { ++ if(is_ip(llc_data)) { ++ u8 * ip_hdr = llc_data+LLC_LEN; ++ u8 * ipaddr_s = ip_hdr+IP_S_ADD_OFF; ++ u8 * ipaddr_d = ip_hdr+IP_D_ADD_OFF; ++ u8 * proto_hdr = ip_hdr+((ip_hdr[0]&0xf)<<2); //ihl:words ++ ++ if(is_tcp(llc_data)) { ++ PT_MSG_PUT(PF_TCP, "TCP%s%s, src=%d, dest=%d, seq=0x%08x, ack=0x%08x", ++ (proto_hdr[13]&0x01)?"(S)":"", (proto_hdr[13]&0x02)?"(F)":"", ++ (proto_hdr[0]<<8)|proto_hdr[1], (proto_hdr[2]<<8)|proto_hdr[3], ++ (proto_hdr[4]<<24)|(proto_hdr[5]<<16)|(proto_hdr[6]<<8)|proto_hdr[7], ++ (proto_hdr[8]<<24)|(proto_hdr[9]<<16)|(proto_hdr[10]<<8)|proto_hdr[11]); ++ ++ } else if(is_udp(llc_data)) { ++ if(is_dhcp(llc_data)) { ++ u8 Options_len = BOOTP_OPS_LEN; ++ u32 dhcp_magic = cpu_to_be32(DHCP_MAGIC); ++ u8 * dhcphdr = proto_hdr+UDP_LEN+UDP_BOOTP_LEN; ++ while(Options_len) { ++ if(*(u32*)dhcphdr == dhcp_magic) ++ break; ++ dhcphdr++; ++ Options_len--; ++ } ++ PT_MSG_PUT(PF_DHCP, "DHCP, Opt=%d, MsgType=%d", *(dhcphdr+4), *(dhcphdr+6)); ++ } else { ++ PT_MSG_PUT(PF_UDP, "UDP, source=%d, dest=%d", ++ (proto_hdr[0]<<8)|proto_hdr[1], (proto_hdr[2]<<8)|proto_hdr[3]); ++ } ++ } else if(is_icmp(llc_data)) { ++ PT_MSG_PUT(PF_ICMP,"ICMP%s%s, Seq=%d", (8 == proto_hdr[0])?"(ping)":"", ++ (0 == proto_hdr[0])?"(reply)":"", (proto_hdr[6]<<8)|proto_hdr[7]); ++ } else if(is_igmp(llc_data)) { ++ PT_MSG_PUT(PF_UNKNWN, "IGMP, type=0x%x", proto_hdr[0]); ++ } else { ++ PT_MSG_PUT(PF_UNKNWN, "unknown IP type=%d", *(ip_hdr+IP_PROTO_OFF)); ++ } ++ if (IS_PROTO_PRINT) { ++ PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(s)", \ ++ ipaddr_s[0], ipaddr_s[1], ipaddr_s[2], ipaddr_s[3]); ++ PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(d)", \ ++ ipaddr_d[0], ipaddr_d[1], ipaddr_d[2], ipaddr_d[3]); ++ } ++ ++ } else if (is_8021x(llc_data)) { ++ PT_MSG_PUT(PF_8021X, "8021X"); ++ } else { //other protol, no detail. ++ switch(cpu_to_be16(*(u16*)(llc_data+LLC_TYPE_OFF))) { ++ case ETH_P_IPV6: //0x08dd ++ PT_MSG_PUT(PF_UNKNWN, "IPv6"); ++ break; ++ case ETH_P_ARP: //0x0806 ++ PT_MSG_PUT(PF_UNKNWN, "ARP"); ++ break; ++ case ETH_P_RARP: //0x8035 ++ PT_MSG_PUT(PF_UNKNWN, "RARP"); ++ break; ++ case ETH_P_DNA_RC: //0x6002 ++ PT_MSG_PUT(PF_UNKNWN, "DNA Remote Console"); ++ break; ++ case ETH_P_DNA_RT: //0x6003 ++ PT_MSG_PUT(PF_UNKNWN, "DNA Routing"); ++ break; ++ case ETH_P_8021Q: //0x8100 ++ PT_MSG_PUT(PF_UNKNWN, "802.1Q VLAN"); ++ break; ++ case ETH_P_LINK_CTL: //0x886c ++ PT_MSG_PUT(PF_UNKNWN, "wlan link local tunnel(HPNA)"); ++ break; ++ case ETH_P_PPP_DISC: //0x8863 ++ PT_MSG_PUT(PF_UNKNWN, "PPPoE discovery"); ++ break; ++ case ETH_P_PPP_SES: //0x8864 ++ PT_MSG_PUT(PF_UNKNWN, "PPPoE session"); ++ break; ++ case ETH_P_MPLS_UC: //0x8847 ++ PT_MSG_PUT(PF_UNKNWN, "MPLS Unicast"); ++ break; ++ case ETH_P_MPLS_MC: //0x8848 ++ PT_MSG_PUT(PF_UNKNWN, "MPLS Multicast"); ++ break; ++ default: ++ PT_MSG_PUT(PF_UNKNWN, "unknown Ethernet type=0x%04x", cpu_to_be16(*(u16*)(llc_data+LLC_TYPE_OFF))); ++ break; ++ } ++ } ++ } else if (is_STP(llc_data)){ ++ //spanning tree proto. ++ PT_MSG_PUT(PF_UNKNWN, "spanning tree"); ++ } else { ++ PT_MSG_PUT(PF_UNKNWN, "unknown LLC type=0x%08x,0x%08x", *(u32*)(llc_data), *((u32*)(llc_data)+1)); ++ } ++ ++ } else if(ieee80211_is_mgmt(fctl) && (PF_MGMT&flags)) { ++ ++ FRAME_PARSE(PF_MGMT, auth ); ++ FRAME_PARSE(PF_MGMT, deauth ); ++ FRAME_PARSE(PF_MGMT, assoc_req ); ++ FRAME_PARSE(PF_MGMT, assoc_resp); ++ FRAME_PARSE(PF_MGMT, disassoc ); ++ FRAME_PARSE(PF_MGMT, atim ); ++ ++ //for more information about action frames. ++ if (FRAME_TYPE(action)) { ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame; ++ FT_MSG_PUT(PF_MGMT, "%s", "action"); ++ ++ if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { ++ u8 *action = (u8*)&mgmt->u.action.category; ++ u32 oui = *(u32 *)&action[2]; ++ u8 oui_subtype = action[6] > 8? 9 : action[6]; ++ if (action[1] == 0x09 && oui == 0x099A6F50) ++ FT_MSG_PUT(PF_MGMT, "(%s)", p2p_frame_type[oui_subtype]); ++ } else if (mgmt->u.action.category == WLAN_CATEGORY_BACK && ++ mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) { ++ FT_MSG_PUT(PF_MGMT, "(ADDBA_REQ-%d)", mgmt->u.action.u.addba_req.start_seq_num); ++ } else if (mgmt->u.action.category == WLAN_CATEGORY_BACK && ++ mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_RESP) { ++ FT_MSG_PUT(PF_MGMT, "(ADDBA_RESP-%d)", mgmt->u.action.u.addba_resp.status); ++ } else { ++ FT_MSG_PUT(PF_MGMT, "(%d)", mgmt->u.action.category); ++ } ++ goto outprint; ++ } ++ ++ //too much scan results, don't print if no need. ++ FRAME_PARSE(PF_SCAN, probe_req ); ++ FRAME_PARSE(PF_SCAN, probe_resp ); ++ FRAME_PARSE(PF_SCAN, beacon ); ++ //must be last. ++ FT_MSG_PUT(PF_UNKNWN, "unknown mgmt"); ++ ++ } else if (ieee80211_is_ctl(fctl) && (PF_CTRL&flags)){ ++ ++ flags &= (~PF_MAC_SN); //no seq ctrl in ctrl frames. ++ FRAME_PARSE(PF_CTRL, back ); ++ FRAME_PARSE(PF_CTRL, back_req); ++ FRAME_PARSE(PF_CTRL, ack ); ++ FRAME_PARSE(PF_CTRL, rts ); ++ FRAME_PARSE(PF_CTRL, cts ); ++ FRAME_PARSE(PF_CTRL, pspoll ); ++ //must be last. ++ FT_MSG_PUT(PF_UNKNWN, "unknown ctrl"); ++ } else { ++ FT_MSG_PUT(PF_UNKNWN, "unknown mac frame, fctl=0x%04x\n", fctl); ++ } ++ ++outprint: ++ ++ FT_MSG_PUT(PF_MAC_SN, "-SN=%d(%d)", (frame->seq_ctrl>>4), (frame->seq_ctrl&0xf)); ++ ++ //output all msg. ++ if(IS_FRAME_PRINT || IS_PROTO_PRINT) { ++ u8 *related = NULL; ++ u8 *own = NULL; ++ char *r_type = NULL; ++ char *o_type = NULL; ++ u8 machdrlen = ieee80211_hdrlen(fctl); ++ u8 *sa = ieee80211_get_SA(frame); ++ u8 *da = ieee80211_get_DA(frame); ++ ++ if (flags&PF_RX) { ++ related = frame->addr2; ++ own = frame->addr1; ++ r_type = "TA"; ++ o_type = "RA"; ++ } else { ++ related = frame->addr1; ++ own = frame->addr2; ++ r_type = "RA"; ++ o_type = "TA"; ++ } ++ ++ if (machdrlen >= 16) { //if ACK or BA, don't print. ++ FT_MSG_PUT(PF_MACADDR, "-%02x:%02x:%02x:%02x:%02x:%02x(%s)", ++ related[0], related[1], related[2], ++ related[3], related[4], related[5], ++ r_type); ++ FT_MSG_PUT(PF_OWNMAC, "-%02x:%02x:%02x:%02x:%02x:%02x(%s)", ++ own[0],own[1],own[2],own[3],own[4],own[5], ++ o_type); ++ FT_MSG_PUT(PF_SA_DA, "-%02x:%02x:%02x:%02x:%02x:%02x(DA)", ++ da[0], da[1], da[2], da[3], da[4], da[5]); ++ FT_MSG_PUT(PF_SA_DA, "-%02x:%02x:%02x:%02x:%02x:%02x(SA)", ++ sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); ++ } ++ ++ xradio_dbg(XRADIO_DBG_ALWY, "if%d-%s%s--%s\n", if_id, ++ (PF_RX&flags)?"RX-":"TX-",framebuf, protobuf); ++ } ++} ++#endif ++#undef FT_MSG_PUT ++#undef PT_MSG_PUT ++#undef FRAME_PARSE ++#undef FRAME_TYPE ++ ++#ifdef DGB_LOG_FILE ++u8 log_buffer[DGB_LOG_BUF_LEN]; ++u16 log_pos = 0; ++struct file *fp_log = NULL; ++atomic_t file_ref = {0}; ++#define T_LABEL_LEN 32 ++char last_time_label[T_LABEL_LEN] = {0}; ++ ++int xradio_logfile(char *buffer, int buf_len, u8 b_time) ++{ ++ int ret=-1; ++ int size = buf_len; ++ mm_segment_t old_fs = get_fs(); ++ ++ if (!buffer) ++ return ret; ++ ++ if (buf_len < 0) ++ size = strlen(buffer); ++ if (!size) ++ return ret; ++ ++ if (atomic_add_return(1, &file_ref) == 1) { ++ fp_log = filp_open(DGB_LOG_PATH0, O_CREAT|O_WRONLY, 0666); ++ if (IS_ERR(fp_log)) { ++ printk(KERN_ERR "[XRADIO] ERR, can't open %s(%d).\n", ++ DGB_LOG_PATH0, (int)fp_log); ++ goto exit; ++ } ++ } ++ //printk(KERN_ERR "[XRADIO] file_ref=%d\n", atomic_read(&file_ref)); ++ ++ if(fp_log->f_op->write == NULL) { ++ printk(KERN_ERR "[XRADIO] ERR, %s:File is not allow to write!\n", ++ __FUNCTION__); ++ goto exit; ++ } else { ++ set_fs(KERNEL_DS); ++ if (fp_log->f_op->llseek != NULL) { ++ vfs_llseek(fp_log, 0, SEEK_END); ++ } else { ++ fp_log->f_pos = 0; ++ } ++ if (b_time) { ++ struct timeval time_now = {0}; ++ struct rtc_time tm; ++ int hour = 0; ++ char time_label[T_LABEL_LEN] = {0}; ++ do_gettimeofday(&time_now); ++ rtc_time_to_tm(time_now.tv_sec, &tm); ++ hour = tm.tm_hour-sys_tz.tz_minuteswest/60; ++ sprintf(time_label,"\n%d-%02d-%02d_%02d-%02d-%02d\n", ++ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday+hour/24, ++ hour%24, tm.tm_min-sys_tz.tz_minuteswest%60, tm.tm_sec); ++ if (memcmp(last_time_label, time_label, T_LABEL_LEN)) { ++ memcpy(last_time_label, time_label, T_LABEL_LEN); ++ ret = vfs_write(fp_log, time_label, strlen(time_label), &fp_log->f_pos); ++ } ++ } ++ ret = vfs_write(fp_log, buffer, size, &fp_log->f_pos); ++ set_fs(old_fs); ++ } ++ ++exit: ++ if (atomic_read(&file_ref) == 1) { ++ if (!IS_ERR(fp_log)) { ++ filp_close(fp_log, NULL); ++ fp_log = (struct file *)-ENOENT; ++ } ++ } ++ atomic_sub(1, &file_ref); ++ return ret; ++} ++#endif ++ ++#if defined(DGB_XRADIO_HWT) ++/***************************for HWT, yangfh********************************/ ++struct sk_buff *hwt_skb = NULL; ++int sent_num = 0; ++int get_hwt_hif_tx(struct xradio_common *hw_priv, u8 **data, ++ size_t *tx_len, int *burst, int *vif_selected) ++{ ++ ++ HWT_PARAMETERS *hwt_tx_hdr = NULL; ++ if (!hwt_tx_en || !hwt_tx_len || !hwt_tx_num || ++ sent_num >= hwt_tx_num) { ++ if (hwt_skb) { ++ dev_kfree_skb(hwt_skb); ++ hwt_skb = NULL; ++ } ++ return 0; ++ } ++ ++ if (!hwt_skb) { ++ hwt_skb = xr_alloc_skb(1504); ++ if (!hwt_skb) { ++ xradio_dbg(XRADIO_DBG_ERROR, "%s:skb is NULL!\n", __func__); ++ return 0; ++ } ++ if ((u32)hwt_skb->data & 3) { ++ u8 align = 4-((u32)hwt_skb->data & 3); ++ skb_reserve(hwt_skb, align); ++ } ++ skb_put(hwt_skb, 1500); ++ } ++ ++ //fill the header info ++ if (hwt_tx_len < sizeof(HWT_PARAMETERS)) ++ hwt_tx_len = sizeof(HWT_PARAMETERS); ++ if (hwt_tx_len > 1500) ++ hwt_tx_len = 1500; ++ hwt_tx_hdr = hwt_skb->data; ++ hwt_tx_hdr->MsgID = 0x0024; ++ hwt_tx_hdr->Msglen = hwt_tx_len; ++ hwt_tx_hdr->TestID = 0x0001; ++ hwt_tx_hdr->Data = 0x1234; ++ ++ //send the packet ++ *data = hwt_skb->data; ++ *tx_len = hwt_tx_hdr->Msglen; ++ *vif_selected = 0; ++ *burst = 2; //burst > 1 for continuous tx. ++ sent_num++; ++ ++ //first packet. ++ if (sent_num == 1) { ++ do_gettimeofday(&hwt_start_time); ++ } ++ ++ //set confirm ++ hwt_tx_hdr->Params = 0; ++ if (sent_num >= hwt_tx_num) { ++ hwt_tx_hdr->Params = 0x101; //last packet ++ hwt_tx_en = 0; //disable hwt_tx_en ++ xradio_dbg(XRADIO_DBG_ALWY, "%s:sent last packet!\n", __func__); ++ } else if (hwt_tx_cfm) { ++ hwt_tx_hdr->Params = !(sent_num%hwt_tx_cfm); ++ } ++ ++ return 1; ++} ++#endif +diff --git a/drivers/net/wireless/xradio/debug.h b/drivers/net/wireless/xradio/debug.h +new file mode 100644 +index 00000000..4e29bb88 +--- /dev/null ++++ b/drivers/net/wireless/xradio/debug.h +@@ -0,0 +1,493 @@ ++/* ++ * DebugFS code for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef XRADIO_DEBUG_H_INCLUDED ++#define XRADIO_DEBUG_H_INCLUDED ++ ++#define XRADIO_DBG_ALWY 0x01 /* Message always need to be present even in release version. */ ++#define XRADIO_DBG_ERROR 0x02 /* Error message to report an error, it can hardly works. */ ++#define XRADIO_DBG_WARN 0x04 /* Warning message to inform us of something unnormal or ++ * something very important, but it still work. */ ++#define XRADIO_DBG_NIY 0x08 /* Important message we need to know in unstable version. */ ++#define XRADIO_DBG_MSG 0x10 /* Normal message just for debug in developing stage. */ ++#define XRADIO_DBG_TRC 0x20 /* Trace of functions, for sequence of functions called. Normally, ++ * don't set this level because there are too more print. */ ++#define XRADIO_DBG_LEVEL 0xFF ++ ++/*added by yangfh, for host debuglevel*/ ++extern u8 dbg_common ; ++extern u8 dbg_bh ; ++extern u8 dbg_txrx ; ++extern u8 dbg_wsm ; ++extern u8 dbg_sta ; ++extern u8 dbg_scan ; ++extern u8 dbg_ap ; ++extern u8 dbg_pm ; ++extern u8 dbg_itp ; ++extern u8 dbg_logfile; ++ ++/*for sdio clk debug*/ ++extern u32 dbg_sdio_clk; ++ ++/* for ps debug */ ++extern u8 ps_disable; ++extern u8 ps_idleperiod; ++extern u8 ps_changeperiod; ++//info of bh thread ++extern u32 irq_count; ++extern u32 int_miss_cnt; ++extern u32 fix_miss_cnt; ++extern u32 next_rx_cnt; ++extern u32 rx_total_cnt; ++extern u32 tx_total_cnt; ++ ++#define WSM_DUMP_MAX_SIZE 20 ++ ++#if defined(CONFIG_XRADIO_DEBUG) ++//#define DGB_LOG_FILE //Don't log more than 500byte once. ++//#define DGB_XRADIO_QC //Enable this only in QC test. ++//#define DGB_XRADIO_HWT //Enable this only in hardware testmode using test fw. ++#endif ++ ++#ifdef DGB_LOG_FILE ++#define DGB_LOG_BUF_LEN 1500 ++#define DGB_LOG_PATH0 "/data/xradio_err.log" ++ ++extern u8 log_buffer[DGB_LOG_BUF_LEN]; ++extern u16 log_pos; ++int xradio_logfile(char *buffer, int buf_len, u8 b_time); ++ ++#define LOG_FILE(b_time, msg) xradio_logfile(msg, -1, b_time) ++#define LOG_FILE_VARS(b_time, ...) do { \ ++ if (!log_pos) \ ++ memset(log_buffer, 0, DGB_LOG_BUF_LEN); \ ++ if (log_pos <= 1000) \ ++ log_pos += sprintf((char *)(log_buffer+log_pos), __VA_ARGS__); \ ++ if (xradio_logfile(log_buffer, log_pos, b_time) >= 0) \ ++ log_pos = 0; \ ++} while (0) ++ ++#else //DGB_LOG_FILE disable ++ ++#define LOG_FILE(b_time, msg) ++#define LOG_FILE_VARS(b_time, ...) ++#endif ++ ++ ++#if (defined(CONFIG_XRADIO_DEBUG)) ++/****************************** debug version *******************************/ ++#if (defined(CONFIG_XRADIO_DUMP_ON_ERROR)) ++#define SYS_BUG(c) BUG_ON(c) ++#define SYS_WARN(c) WARN_ON(c) ++#else ++#define SYS_BUG(c) WARN_ON(c) ++#define SYS_WARN(c) WARN_ON(c) ++#endif ++ ++#define xradio_dbg(level, ...) \ ++ do { \ ++ if ((level) & dbg_common & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[XRADIO_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_common & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[XRADIO_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_common) \ ++ printk(KERN_ERR "[XRADIO] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[XRADIO_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define txrx_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_txrx & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[TXRX_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_txrx & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[TXRX_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_txrx) \ ++ printk(KERN_ERR "[TXRX] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[TXRX_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define wsm_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_wsm & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[WSM_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_wsm & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[WSM_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_wsm) \ ++ printk(KERN_ERR "[WSM] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[WSM_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define sta_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_sta & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[STA_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_sta & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[STA_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_sta) \ ++ printk(KERN_ERR "[STA] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[STA_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define scan_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_scan & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[SCAN_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_scan & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[SCAN_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_scan) \ ++ printk(KERN_ERR "[SCAN] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[SCAN_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define ap_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_ap & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[AP_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_ap & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[AP_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_ap) \ ++ printk(KERN_ERR "[AP] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[AP_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define pm_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_pm & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[PM_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_pm & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[PM_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_pm) \ ++ printk(KERN_ERR "[PM] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[PM_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++#define itp_printk(level, ...) \ ++ do { \ ++ if ((level) & dbg_itp & XRADIO_DBG_ERROR) \ ++ printk(KERN_ERR "[ITP_ERR] " __VA_ARGS__); \ ++ else if ((level) & dbg_itp & XRADIO_DBG_WARN) \ ++ printk(KERN_ERR "[ITP_WRN] " __VA_ARGS__); \ ++ else if ((level) & dbg_itp) \ ++ printk(KERN_ERR "[ITP] " __VA_ARGS__); \ ++ if ((level) & dbg_logfile) \ ++ LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ ++ "[ITP_ERR] " __VA_ARGS__); \ ++ } while (0) ++ ++ ++#define DBG_FUN_LINE printk(KERN_ERR "%s,line=%d", __FUNCTION__, __LINE__) ++#define PARAM_CHECK_FALSE(p) \ ++ do { \ ++ if (!p) DBG_FUN_LINE; \ ++ } while (0) ++ ++#define PARAM_CHECK_TRUE(p) \ ++ do { \ ++ if (p) DBG_FUN_LINE; \ ++ } while (0) ++ ++ ++//interfaces to debug packet's information. ++//for802.11 ++#define PF_CTRL 0x0001 ++#define PF_MGMT 0x0002 ++#define PF_DATA 0x0004 ++#define PF_SCAN 0x0008 ++ ++//for ip data ++#define PF_TCP 0x0010 ++#define PF_UDP 0x0020 ++#define PF_DHCP 0x0040 ++#define PF_ICMP 0x0080 ++ ++//for special frames or info. ++#define PF_8021X 0x0100 //action frames ++#define PF_MAC_SN 0x0200 //mac seq ++#define PF_OWNMAC 0x0400 //TA in Tx, RA in Rx. ++#define PF_SA_DA 0x0800 //SA, DA for Ethernet. ++ ++#define PF_MACADDR 0x1000 //RA in Tx, TA in Rx. ++#define PF_IPADDR 0x2000 //ip address of ip packets. ++#define PF_UNKNWN 0x4000 //print unknown type frames of 802.11 flag you set. ++#define PF_RX 0x8000 //0:TX, 1:RX. So, need to add PF_RX in Rx path. ++void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id); ++ ++#if defined(DGB_XRADIO_HWT) //hardware test ++typedef struct HWT_PARAMETERS_S { ++ u16 Msglen; ++ u16 MsgID; ++ u16 TestID; ++ u16 Params; ++ u16 Datalen; ++ u16 Data; ++} HWT_PARAMETERS; ++int get_hwt_hif_tx(struct xradio_common *hw_priv, u8 **data, ++ size_t *tx_len, int *burst, int *vif_selected); ++#endif //DGB_XRADIO_HWT ++ ++#else ++/****************************** release version *******************************/ ++#define SYS_BUG(c) BUG_ON(c) ++#define SYS_WARN(c) WARN_ON(c) ++ ++#define xradio_dbg(level, ...) ++#define txrx_printk(level, ...) ++#define wsm_printk(level, ...) ++#define sta_printk(level, ...) ++#define scan_printk(level, ...) ++#define ap_printk(level, ...) ++#define pm_printk(level, ...) ++#define itp_printk(level, ...) ++ ++#define DBG_FUN_LINE ++#define PARAM_CHECK_FALSE ++#define PARAM_CHECK_TRUE ++ ++static inline void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id) ++{ ++} ++#endif //CONFIG_XRADIO_DEBUG ++ ++#ifdef CONFIG_XRADIO_DEBUGFS ++/****************************** debugfs version *******************************/ ++struct xradio_debug_common { ++ struct dentry *debugfs_phy; ++ int tx_cache_miss; ++ int tx_burst; ++ int rx_burst; ++ int ba_cnt; ++ int ba_acc; ++ int ba_cnt_rx; ++ int ba_acc_rx; ++#ifdef CONFIG_XRADIO_ITP ++ struct xradio_itp itp; ++#endif /* CONFIG_XRADIO_ITP */ ++}; ++ ++struct xradio_debug_priv { ++ struct dentry *debugfs_phy; ++ int tx; ++ int tx_agg; ++ int rx; ++ int rx_agg; ++ int tx_multi; ++ int tx_multi_frames; ++ int tx_align; ++ int tx_ttl; ++}; ++ ++ ++#define DBG_BH_IRQ_ADD irq_count++ ++#define DBG_BH_MISS_ADD int_miss_cnt++ ++#define DBG_BH_FIX_RX_ADD fix_miss_cnt++ ++#define DBG_BH_NEXT_RX_ADD next_rx_cnt++ ++#define DBG_BH_RX_TOTAL_ADD rx_total_cnt++ ++#define DBG_BH_TX_TOTAL_ADD tx_total_cnt++ ++ ++int xradio_debug_init_common(struct xradio_common *hw_priv); ++int xradio_debug_init_priv(struct xradio_common *hw_priv, ++ struct xradio_vif *priv); ++void xradio_debug_release_common(struct xradio_common *hw_priv); ++void xradio_debug_release_priv(struct xradio_vif *priv); ++ ++static inline void xradio_debug_txed(struct xradio_vif *priv) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->tx; ++} ++ ++static inline void xradio_debug_txed_agg(struct xradio_vif *priv) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->tx_agg; ++} ++ ++static inline void xradio_debug_txed_multi(struct xradio_vif *priv, ++ int count) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->tx_multi; ++ priv->debug->tx_multi_frames += count; ++} ++ ++static inline void xradio_debug_rxed(struct xradio_vif *priv) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->rx; ++} ++ ++static inline void xradio_debug_rxed_agg(struct xradio_vif *priv) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->rx_agg; ++} ++ ++static inline void xradio_debug_tx_cache_miss(struct xradio_common *hw_priv) ++{ ++ if (!hw_priv->debug) ++ return; ++ ++hw_priv->debug->tx_cache_miss; ++} ++ ++static inline void xradio_debug_tx_align(struct xradio_vif *priv) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->tx_align; ++} ++ ++static inline void xradio_debug_tx_ttl(struct xradio_vif *priv) ++{ ++ if (!priv->debug) ++ return; ++ ++priv->debug->tx_ttl; ++} ++ ++static inline void xradio_debug_tx_burst(struct xradio_common *hw_priv) ++{ ++ if (!hw_priv->debug) ++ return; ++ ++hw_priv->debug->tx_burst; ++} ++ ++static inline void xradio_debug_rx_burst(struct xradio_common *hw_priv) ++{ ++ if (!hw_priv->debug) ++ return; ++ ++hw_priv->debug->rx_burst; ++} ++ ++static inline void xradio_debug_ba(struct xradio_common *hw_priv, ++ int ba_cnt, int ba_acc, int ba_cnt_rx, ++ int ba_acc_rx) ++{ ++ if (!hw_priv->debug) ++ return; ++ hw_priv->debug->ba_cnt = ba_cnt; ++ hw_priv->debug->ba_acc = ba_acc; ++ hw_priv->debug->ba_cnt_rx = ba_cnt_rx; ++ hw_priv->debug->ba_acc_rx = ba_acc_rx; ++} ++ ++int xradio_print_fw_version(struct xradio_common *hw_priv, u8* buf, size_t len); ++ ++int xradio_host_dbg_init(void); ++void xradio_host_dbg_deinit(void); ++ ++#else /* CONFIG_XRADIO_DEBUGFS */ ++/****************************** no debugfs version *******************************/ ++#define DBG_BH_MISS_ADD ++#define DBG_BH_FIX_RX_ADD ++#define DBG_BH_NEXT_RX_ADD ++#define DBG_BH_RX_TOTAL_ADD ++#define DBG_BH_TX_TOTAL_ADD ++ ++static inline int xradio_debug_init_common(struct xradio_common *hw_priv) ++{ ++ return 0; ++} ++ ++static inline int xradio_debug_init_priv(struct xradio_common *hw_priv, ++ struct xradio_vif *priv) ++{ ++ return 0; ++} ++ ++static inline void xradio_debug_release_common(struct xradio_common *hw_priv) ++{ ++} ++ ++static inline void xradio_debug_release_priv(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_txed(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_txed_agg(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_txed_multi(struct xradio_vif *priv, ++ int count) ++{ ++} ++ ++static inline void xradio_debug_rxed(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_rxed_agg(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_tx_cache_miss(struct xradio_common *common) ++{ ++} ++ ++static inline void xradio_debug_tx_align(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_tx_ttl(struct xradio_vif *priv) ++{ ++} ++ ++static inline void xradio_debug_tx_burst(struct xradio_common *hw_priv) ++{ ++} ++ ++static inline void xradio_debug_rx_burst(struct xradio_common *hw_priv) ++{ ++} ++ ++static inline void xradio_debug_ba(struct xradio_common *hw_priv, ++ int ba_cnt, int ba_acc, int ba_cnt_rx, ++ int ba_acc_rx) ++{ ++} ++ ++static inline int xradio_print_fw_version(struct xradio_vif *priv, ++ u8* buf, size_t len) ++{ ++ return 0; ++} ++ ++static inline int xradio_host_dbg_init(void) ++{ ++ return 0; ++} ++ ++static inline void xradio_host_dbg_deinit(void) ++{ ++} ++#endif /* CONFIG_XRADIO_DEBUGFS */ ++ ++#endif /* XRADIO_DEBUG_H_INCLUDED */ +diff --git a/drivers/net/wireless/xradio/fwio.c b/drivers/net/wireless/xradio/fwio.c +new file mode 100644 +index 00000000..47660d1f +--- /dev/null ++++ b/drivers/net/wireless/xradio/fwio.c +@@ -0,0 +1,560 @@ ++/* ++ * Firmware I/O implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "fwio.h" ++#include "hwio.h" ++#include "bh.h" ++#include "sdio.h" ++ ++/* Macroses are local. */ ++#define APB_WRITE(reg, val) \ ++ do { \ ++ ret = xradio_apb_write_32(hw_priv, APB_ADDR(reg), (val)); \ ++ if (ret < 0) { \ ++ xradio_dbg(XRADIO_DBG_ERROR, \ ++ "%s: can't write %s at line %d.\n", \ ++ __func__, #reg, __LINE__); \ ++ goto error; \ ++ } \ ++ } while (0) ++#define APB_READ(reg, val) \ ++ do { \ ++ ret = xradio_apb_read_32(hw_priv, APB_ADDR(reg), &(val)); \ ++ if (ret < 0) { \ ++ xradio_dbg(XRADIO_DBG_ERROR, \ ++ "%s: can't read %s at line %d.\n", \ ++ __func__, #reg, __LINE__); \ ++ goto error; \ ++ } \ ++ } while (0) ++#define REG_WRITE(reg, val) \ ++ do { \ ++ ret = xradio_reg_write_32(hw_priv, (reg), (val)); \ ++ if (ret < 0) { \ ++ xradio_dbg(XRADIO_DBG_ERROR, \ ++ "%s: can't write %s at line %d.\n", \ ++ __func__, #reg, __LINE__); \ ++ goto error; \ ++ } \ ++ } while (0) ++#define REG_READ(reg, val) \ ++ do { \ ++ ret = xradio_reg_read_32(hw_priv, (reg), &(val)); \ ++ if (ret < 0) { \ ++ xradio_dbg(XRADIO_DBG_ERROR, \ ++ "%s: can't read %s at line %d.\n", \ ++ __func__, #reg, __LINE__); \ ++ goto error; \ ++ } \ ++ } while (0) ++ ++ ++static int xradio_get_hw_type(u32 config_reg_val, int *major_revision) ++{ ++ int hw_type = -1; ++ u32 hif_type = (config_reg_val >> 24) & 0x4; ++ //u32 hif_vers = (config_reg_val >> 31) & 0x1; ++ ++ /* Check if we have XRADIO*/ ++ if (hif_type == 0x4) { ++ *major_revision = 0x4; ++ hw_type = HIF_HW_TYPE_XRADIO; ++ } else { ++ //hw type unknown. ++ *major_revision = 0x0; ++ } ++ return hw_type; ++} ++ ++/* ++ * This function is called to Parse the SDD file ++ * to extract some informations ++ */ ++static int xradio_parse_sdd(struct xradio_common *hw_priv, u32 *dpll) ++{ ++ int ret = 0; ++ const char *sdd_path = NULL; ++ struct xradio_sdd *pElement = NULL; ++ int parsedLength = 0; ++ ++ SYS_BUG(hw_priv->sdd != NULL); ++ ++ /* select and load sdd file depend on hardware version. */ ++ switch (hw_priv->hw_revision) { ++ case XR819_HW_REV0: ++ sdd_path = XR819_SDD_FILE; ++ break; ++ default: ++ dev_dbg(hw_priv->pdev, "unknown hardware version.\n"); ++ return ret; ++ } ++ ++ ret = request_firmware(&hw_priv->sdd, sdd_path, hw_priv->pdev); ++ if (unlikely(ret)) { ++ dev_dbg(hw_priv->pdev, "can't load sdd file %s.\n", ++ sdd_path); ++ return ret; ++ } ++ ++ //parse SDD config. ++ hw_priv->is_BT_Present = false; ++ pElement = (struct xradio_sdd *)hw_priv->sdd->data; ++ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); ++ pElement = FIND_NEXT_ELT(pElement); ++ ++ while (parsedLength < hw_priv->sdd->size) { ++ switch (pElement->id) { ++ case SDD_PTA_CFG_ELT_ID: ++ hw_priv->conf_listen_interval = (*((u16 *)pElement->data+1) >> 7) & 0x1F; ++ hw_priv->is_BT_Present = true; ++ xradio_dbg(XRADIO_DBG_NIY, "PTA element found.Listen Interval %d\n", ++ hw_priv->conf_listen_interval); ++ break; ++ case SDD_REFERENCE_FREQUENCY_ELT_ID: ++ switch(*((uint16_t*)pElement->data)) { ++ case 0x32C8: ++ *dpll = 0x1D89D241; ++ break; ++ case 0x3E80: ++ *dpll = 0x1E1; ++ break; ++ case 0x41A0: ++ *dpll = 0x124931C1; ++ break; ++ case 0x4B00: ++ *dpll = 0x191; ++ break; ++ case 0x5DC0: ++ *dpll = 0x141; ++ break; ++ case 0x6590: ++ *dpll = 0x0EC4F121; ++ break; ++ case 0x8340: ++ *dpll = 0x92490E1; ++ break; ++ case 0x9600: ++ *dpll = 0x100010C1; ++ break; ++ case 0x9C40: ++ *dpll = 0xC1; ++ break; ++ case 0xBB80: ++ *dpll = 0xA1; ++ break; ++ case 0xCB20: ++ *dpll = 0x7627091; ++ break; ++ default: ++ *dpll = DPLL_INIT_VAL_XRADIO; ++ xradio_dbg(XRADIO_DBG_WARN, "Unknown Reference clock frequency." ++ "Use default DPLL value=0x%08x.", DPLL_INIT_VAL_XRADIO); ++ break; ++ } ++ default: ++ break; ++ } ++ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); ++ pElement = FIND_NEXT_ELT(pElement); ++ } ++ ++ dev_dbg(hw_priv->pdev, "sdd size=%d parse len=%d.\n", ++ hw_priv->sdd->size, parsedLength); ++ ++ // ++ if (hw_priv->is_BT_Present == false) { ++ hw_priv->conf_listen_interval = 0; ++ xradio_dbg(XRADIO_DBG_NIY, "PTA element NOT found.\n"); ++ } ++ return ret; ++} ++ ++static int xradio_firmware(struct xradio_common *hw_priv) ++{ ++ int ret, block, num_blocks; ++ unsigned i; ++ u32 val32; ++ u32 put = 0, get = 0; ++ u8 *buf = NULL; ++ const char *fw_path; ++ const struct firmware *firmware = NULL; ++ ++ switch (hw_priv->hw_revision) { ++ case XR819_HW_REV0: ++ fw_path = XR819_FIRMWARE; ++ break; ++ default: ++ dev_dbg(hw_priv->pdev, "invalid silicon revision %d.\n", ++ hw_priv->hw_revision); ++ return -EINVAL; ++ } ++ /* Initialize common registers */ ++ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); ++ APB_WRITE(DOWNLOAD_PUT_REG, 0); ++ APB_WRITE(DOWNLOAD_GET_REG, 0); ++ APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); ++ APB_WRITE(DOWNLOAD_FLAGS_REG, 0); ++ ++ /* Release CPU from RESET */ ++ REG_READ(HIF_CONFIG_REG_ID, val32); ++ val32 &= ~HIF_CONFIG_CPU_RESET_BIT; ++ REG_WRITE(HIF_CONFIG_REG_ID, val32); ++ ++ /* Enable Clock */ ++ val32 &= ~HIF_CONFIG_CPU_CLK_DIS_BIT; ++ REG_WRITE(HIF_CONFIG_REG_ID, val32); ++ ++ /* Load a firmware file */ ++ ret = request_firmware(&firmware, fw_path, hw_priv->pdev); ++ if (ret) { ++ dev_dbg(hw_priv->pdev, "can't load firmware file %s.\n", ++ fw_path); ++ goto error; ++ } ++ SYS_BUG(!firmware->data); ++ ++ buf = kmalloc(DOWNLOAD_BLOCK_SIZE, GFP_KERNEL); ++ if (!buf) { ++ dev_dbg(hw_priv->pdev, "can't allocate firmware buffer.\n"); ++ ret = -ENOMEM; ++ goto error; ++ } ++ ++ /* Check if the bootloader is ready */ ++ for (i = 0; i < 100; i++/*= 1 + i / 2*/) { ++ APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); ++ if (val32 == DOWNLOAD_I_AM_HERE) ++ break; ++ mdelay(10); ++ } /* End of for loop */ ++ if (val32 != DOWNLOAD_I_AM_HERE) { ++ dev_dbg(hw_priv->pdev, "bootloader is not ready.\n"); ++ ret = -ETIMEDOUT; ++ goto error; ++ } ++ ++ /* Calculcate number of download blocks */ ++ num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; ++ ++ /* Updating the length in Download Ctrl Area */ ++ val32 = firmware->size; /* Explicit cast from size_t to u32 */ ++ APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); ++ ++ /* Firmware downloading loop */ ++ for (block = 0; block < num_blocks ; block++) { ++ size_t tx_size; ++ size_t block_size; ++ ++ /* check the download status */ ++ APB_READ(DOWNLOAD_STATUS_REG, val32); ++ if (val32 != DOWNLOAD_PENDING) { ++ dev_dbg(hw_priv->pdev, "bootloader reported error %d.\n", ++ val32); ++ ret = -EIO; ++ goto error; ++ } ++ ++ /* loop until put - get <= 24K */ ++ for (i = 0; i < 100; i++) { ++ APB_READ(DOWNLOAD_GET_REG, get); ++ if ((put - get) <= (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) ++ break; ++ mdelay(i); ++ } ++ ++ if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { ++ dev_dbg(hw_priv->pdev, "Timeout waiting for FIFO.\n"); ++ ret = -ETIMEDOUT; ++ goto error; ++ } ++ ++ /* calculate the block size */ ++ tx_size = block_size = min((size_t)(firmware->size - put), (size_t)DOWNLOAD_BLOCK_SIZE); ++ memcpy(buf, &firmware->data[put], block_size); ++ ++ if (block_size < DOWNLOAD_BLOCK_SIZE) { ++ memset(&buf[block_size], 0, DOWNLOAD_BLOCK_SIZE - block_size); ++ tx_size = DOWNLOAD_BLOCK_SIZE; ++ } ++ ++ /* send the block to sram */ ++ ret = xradio_apb_write(hw_priv, APB_ADDR(DOWNLOAD_FIFO_OFFSET + (put & (DOWNLOAD_FIFO_SIZE - 1))), ++ buf, tx_size); ++ if (ret < 0) { ++ xradio_dbg(XRADIO_DBG_ERROR, "%s: can't write block at line %d.\n", __func__, __LINE__); ++ goto error; ++ } ++ ++ /* update the put register */ ++ put += block_size; ++ APB_WRITE(DOWNLOAD_PUT_REG, put); ++ } /* End of firmware download loop */ ++ ++ /* Wait for the download completion */ ++ for (i = 0; i < 300; i += 1 + i / 2) { ++ APB_READ(DOWNLOAD_STATUS_REG, val32); ++ if (val32 != DOWNLOAD_PENDING) ++ break; ++ mdelay(i); ++ } ++ if (val32 != DOWNLOAD_SUCCESS) { ++ dev_dbg(hw_priv->pdev, "wait for download completion failed. " \ ++ "Read: 0x%.8X\n", val32); ++ ret = -ETIMEDOUT; ++ goto error; ++ } else { ++ dev_dbg(hw_priv->pdev, "Firmware completed.\n"); ++ ret = 0; ++ } ++ ++error: ++ if(buf) ++ kfree(buf); ++ if (firmware) { ++ release_firmware(firmware); ++ } ++ return ret; ++} ++ ++static int xradio_bootloader(struct xradio_common *hw_priv) ++{ ++ const char *bl_path = XR819_BOOTLOADER; ++ u32 addr = AHB_MEMORY_ADDRESS; ++ int ret; ++ u32 i; ++ u32 *data; ++ const struct firmware *bootloader; ++ ++ /* Load a bootloader file */ ++ ret = request_firmware(&bootloader, bl_path, hw_priv->pdev); ++ if (ret) { ++ dev_dbg(hw_priv->pdev, "can't load bootloader file %s.\n", ++ bl_path); ++ goto error; ++ } ++ ++ /* Down bootloader. */ ++ data = (u32 *)bootloader->data; ++ for(i = 0; i < (bootloader->size)/4; i++, addr+=4) { ++ REG_WRITE(HIF_SRAM_BASE_ADDR_REG_ID, addr); ++ REG_WRITE(HIF_AHB_DPORT_REG_ID,data[i]); ++ } ++ dev_dbg(hw_priv->pdev, "Bootloader complete\n"); ++ ++error: ++ if(bootloader) { ++ release_firmware(bootloader); ++ } ++ return ret; ++} ++ ++bool test_retry = false; ++int xradio_load_firmware(struct xradio_common *hw_priv) ++{ ++ int ret; ++ int i; ++ u32 val32; ++ u16 val16; ++ u32 dpll = 0; ++ int major_revision; ++ ++ /* Read CONFIG Register Value - We will read 32 bits */ ++ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "can't read config register, err=%d.\n", ++ ret); ++ return ret; ++ } ++ ++ //check hardware type and revision. ++ hw_priv->hw_type = xradio_get_hw_type(val32, &major_revision); ++ switch (hw_priv->hw_type) { ++ case HIF_HW_TYPE_XRADIO: ++ dev_dbg(hw_priv->pdev, "HW_TYPE_XRADIO detected.\n"); ++ break; ++ default: ++ dev_dbg(hw_priv->pdev, "Unknown hardware: %d.\n", ++ hw_priv->hw_type); ++ return -ENOTSUPP; ++ } ++ if (major_revision == 4) { ++ hw_priv->hw_revision = XR819_HW_REV0; ++ dev_dbg(hw_priv->pdev, "XRADIO_HW_REV 1.0 detected.\n"); ++ } else { ++ dev_dbg(hw_priv->pdev, "Unsupported major revision %d.\n", ++ major_revision); ++ return -ENOTSUPP; ++ } ++ ++ //load sdd file, and get config from it. ++ ret = xradio_parse_sdd(hw_priv, &dpll); ++ if (ret < 0) { ++ return ret; ++ } ++ ++ //set dpll initial value and check. ++ ret = xradio_reg_write_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, dpll); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "can't write DPLL register.\n"); ++ goto out; ++ } ++ msleep(5); ++ ret = xradio_reg_read_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, &val32); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "can't read DPLL register.\n"); ++ goto out; ++ } ++ if (val32 != dpll) { ++ dev_dbg(hw_priv->pdev, "unable to initialise " \ ++ "DPLL register. Wrote 0x%.8X, read 0x%.8X.\n", ++ dpll, val32); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* Set wakeup bit in device */ ++ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &val16); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "set_wakeup: can't read control register.\n"); ++ goto out; ++ } ++ ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, val16 | HIF_CTRL_WUP_BIT); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "set_wakeup: can't write control register.\n"); ++ goto out; ++ } ++ ++ /* Wait for wakeup */ ++ for (i = 0 ; i < 300 ; i += 1 + i / 2) { ++ ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &val16); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "Wait_for_wakeup: " ++ "can't read control register.\n"); ++ goto out; ++ } ++ if (val16 & HIF_CTRL_RDY_BIT) { ++ break; ++ } ++ msleep(i); ++ } ++ if ((val16 & HIF_CTRL_RDY_BIT) == 0) { ++ dev_dbg(hw_priv->pdev, "Wait for wakeup:" ++ "device is not responding.\n"); ++ ret = -ETIMEDOUT; ++ goto out; ++ } else { ++ dev_dbg(hw_priv->pdev, "WLAN device is ready.\n"); ++ } ++ ++ /* Checking for access mode and download firmware. */ ++ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "check_access_mode: " ++ "can't read config register.\n"); ++ goto out; ++ } ++ if (val32 & HIF_CONFIG_ACCESS_MODE_BIT) { ++ /* Down bootloader. */ ++ ret = xradio_bootloader(hw_priv); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "can't download bootloader.\n"); ++ goto out; ++ } ++ /* Down firmware. */ ++ ret = xradio_firmware(hw_priv); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "can't download firmware.\n"); ++ goto out; ++ } ++ } else { ++ dev_dbg(hw_priv->pdev, "check_access_mode: " ++ "device is already in QUEUE mode.\n"); ++ /* TODO: verify this branch. Do we need something to do? */ ++ } ++ ++ if (HIF_HW_TYPE_XRADIO == hw_priv->hw_type) { ++ /* If device is XRADIO the IRQ enable/disable bits ++ * are in CONFIG register */ ++ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "enable_irq: can't read " \ ++ "config register.\n"); ++ goto unsubscribe; ++ } ++ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID, ++ val32 | HIF_CONF_IRQ_RDY_ENABLE); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "enable_irq: can't write " \ ++ "config register.\n"); ++ goto unsubscribe; ++ } ++ } else { ++ /* If device is XRADIO the IRQ enable/disable bits ++ * are in CONTROL register */ ++ /* Enable device interrupts - Both DATA_RDY and WLAN_RDY */ ++ ret = xradio_reg_read_16(hw_priv, HIF_CONFIG_REG_ID, &val16); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "enable_irq: can't read " \ ++ "control register.\n"); ++ goto unsubscribe; ++ } ++ ret = xradio_reg_write_16(hw_priv, HIF_CONFIG_REG_ID, ++ val16 | HIF_CTRL_IRQ_RDY_ENABLE); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "enable_irq: can't write " \ ++ "control register.\n"); ++ goto unsubscribe; ++ } ++ ++ } ++ ++ /* Configure device for MESSSAGE MODE */ ++ ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "set_mode: can't read config register.\n"); ++ goto unsubscribe; ++ } ++ ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID, ++ val32 & ~HIF_CONFIG_ACCESS_MODE_BIT); ++ if (ret < 0) { ++ dev_dbg(hw_priv->pdev, "set_mode: can't write config register.\n"); ++ goto unsubscribe; ++ } ++ ++ /* Unless we read the CONFIG Register we are ++ * not able to get an interrupt */ ++ mdelay(10); ++ xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ return 0; ++ ++unsubscribe: ++out: ++ if (hw_priv->sdd) { ++ release_firmware(hw_priv->sdd); ++ hw_priv->sdd = NULL; ++ } ++ return ret; ++} ++ ++int xradio_dev_deinit(struct xradio_common *hw_priv) ++{ ++ if (hw_priv->sdd) { ++ release_firmware(hw_priv->sdd); ++ hw_priv->sdd = NULL; ++ } ++ return 0; ++} +diff --git a/drivers/net/wireless/xradio/fwio.h b/drivers/net/wireless/xradio/fwio.h +new file mode 100644 +index 00000000..b5efeb15 +--- /dev/null ++++ b/drivers/net/wireless/xradio/fwio.h +@@ -0,0 +1,33 @@ ++/* ++ * Firmware APIs for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef FWIO_H_INCLUDED ++#define FWIO_H_INCLUDED ++ ++#define XR819_HW_REV0 (8190) ++#define XR819_BOOTLOADER ("xr819/boot_xr819.bin") ++#define XR819_FIRMWARE ("xr819/fw_xr819.bin") ++#define XR819_SDD_FILE ("xr819/sdd_xr819.bin") ++ ++#define SDD_PTA_CFG_ELT_ID 0xEB ++#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xC5 ++#define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0) ++#define FIND_NEXT_ELT(e) (struct xradio_sdd *)((u8 *)&e->data + e->length) ++struct xradio_sdd { ++ u8 id; ++ u8 length; ++ u8 data[]; ++}; ++ ++struct xradio_common; ++int xradio_load_firmware(struct xradio_common *hw_priv); ++int xradio_dev_deinit(struct xradio_common *hw_priv); ++ ++#endif +diff --git a/drivers/net/wireless/xradio/ht.h b/drivers/net/wireless/xradio/ht.h +new file mode 100644 +index 00000000..f2a21cce +--- /dev/null ++++ b/drivers/net/wireless/xradio/ht.h +@@ -0,0 +1,41 @@ ++/* ++ * HT-related code for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef XRADIO_HT_H_INCLUDED ++#define XRADIO_HT_H_INCLUDED ++ ++#include ++ ++struct xradio_ht_oper { ++ struct ieee80211_sta_ht_cap ht_cap; ++ enum nl80211_channel_type channel_type; ++ u16 operation_mode; ++}; ++ ++static inline int xradio_is_ht(const struct xradio_ht_oper *ht_oper) ++{ ++ return ht_oper->channel_type != NL80211_CHAN_NO_HT; ++} ++ ++static inline int xradio_ht_greenfield(const struct xradio_ht_oper *ht_oper) ++{ ++ return (xradio_is_ht(ht_oper) && ++ (ht_oper->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && ++ !(ht_oper->operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)); ++} ++ ++static inline int xradio_ht_ampdu_density(const struct xradio_ht_oper *ht_oper) ++{ ++ if (!xradio_is_ht(ht_oper)) ++ return 0; ++ return ht_oper->ht_cap.ampdu_density; ++} ++ ++#endif /* XRADIO_HT_H_INCLUDED */ +diff --git a/drivers/net/wireless/xradio/hwio.c b/drivers/net/wireless/xradio/hwio.c +new file mode 100644 +index 00000000..a6b59175 +--- /dev/null ++++ b/drivers/net/wireless/xradio/hwio.c +@@ -0,0 +1,280 @@ ++/* ++ * Hardware I/O implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++ ++#include "xradio.h" ++#include "hwio.h" ++#include "sdio.h" ++ ++#define CHECK_ADDR_LEN 1 ++ ++ /* Sdio addr is 4*spi_addr */ ++#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) ++#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ ++ ((((buf_id) & 0x1F) << 7) \ ++ | (((mpf) & 1) << 6) \ ++ | (((rfu) & 1) << 5) \ ++ | (((reg_id_ofs) & 0x1F) << 0)) ++#define MAX_RETRY 3 ++ ++ ++static int __xradio_read(struct xradio_common *hw_priv, u16 addr, ++ void *buf, size_t buf_len, int buf_id) ++{ ++ u16 addr_sdio; ++ u32 sdio_reg_addr_17bit ; ++ ++#if (CHECK_ADDR_LEN) ++ /* Check if buffer is aligned to 4 byte boundary */ ++ if (SYS_WARN(((unsigned long)buf & 3) && (buf_len > 4))) { ++ dev_dbg(hw_priv->pdev, "buffer is not aligned.\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ /* Convert to SDIO Register Address */ ++ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); ++ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); ++ return sdio_data_read(hw_priv, ++ sdio_reg_addr_17bit, ++ buf, buf_len); ++} ++ ++static int __xradio_write(struct xradio_common *hw_priv, u16 addr, ++ const void *buf, size_t buf_len, int buf_id) ++{ ++ u16 addr_sdio; ++ u32 sdio_reg_addr_17bit ; ++ ++#if (CHECK_ADDR_LEN) ++ /* Check if buffer is aligned to 4 byte boundary */ ++ if (SYS_WARN(((unsigned long)buf & 3) && (buf_len > 4))) { ++ dev_err(hw_priv->pdev, "buffer is not aligned.\n"); ++ return -EINVAL; ++ } ++#endif ++ ++ /* Convert to SDIO Register Address */ ++ addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); ++ sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); ++ ++ return sdio_data_write(hw_priv, ++ sdio_reg_addr_17bit, ++ buf, buf_len); ++} ++ ++static inline int __xradio_read_reg32(struct xradio_common *hw_priv, ++ u16 addr, u32 *val) ++{ ++ return __xradio_read(hw_priv, addr, val, sizeof(val), 0); ++} ++ ++static inline int __xradio_write_reg32(struct xradio_common *hw_priv, ++ u16 addr, u32 val) ++{ ++ return __xradio_write(hw_priv, addr, &val, sizeof(val), 0); ++} ++ ++int xradio_reg_read(struct xradio_common *hw_priv, u16 addr, ++ void *buf, size_t buf_len) ++{ ++ int ret; ++ sdio_lock(hw_priv); ++ ret = __xradio_read(hw_priv, addr, buf, buf_len, 0); ++ sdio_unlock(hw_priv); ++ return ret; ++} ++ ++int xradio_reg_write(struct xradio_common *hw_priv, u16 addr, ++ const void *buf, size_t buf_len) ++{ ++ int ret; ++ sdio_lock(hw_priv); ++ ret = __xradio_write(hw_priv, addr, buf, buf_len, 0); ++ sdio_unlock(hw_priv); ++ return ret; ++} ++ ++int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len) ++{ ++ int ret, retry = 1; ++ sdio_lock(hw_priv); ++ { ++ int buf_id_rx = hw_priv->buf_id_rx; ++ while (retry <= MAX_RETRY) { ++ ret = __xradio_read(hw_priv, HIF_IN_OUT_QUEUE_REG_ID, buf, ++ buf_len, buf_id_rx + 1); ++ if (!ret) { ++ buf_id_rx = (buf_id_rx + 1) & 3; ++ hw_priv->buf_id_rx = buf_id_rx; ++ break; ++ } else { ++ //~dgp this retrying stuff is silly as it can crash the fw if there is nothing to read ++ dev_err(hw_priv->pdev, "data read error :%d - attempt %d of %d\n", ret, retry, MAX_RETRY); ++ retry++; ++ mdelay(1); ++ } ++ } ++ } ++ sdio_unlock(hw_priv); ++ return ret; ++} ++ ++int xradio_data_write(struct xradio_common *hw_priv, const void *buf, ++ size_t buf_len) ++{ ++ int ret, retry = 1; ++ sdio_lock(hw_priv); ++ { ++ int buf_id_tx = hw_priv->buf_id_tx; ++ while (retry <= MAX_RETRY) { ++ ret = __xradio_write(hw_priv, HIF_IN_OUT_QUEUE_REG_ID, buf, ++ buf_len, buf_id_tx); ++ if (!ret) { ++ buf_id_tx = (buf_id_tx + 1) & 31; ++ hw_priv->buf_id_tx = buf_id_tx; ++ break; ++ } else { ++ dev_err(hw_priv->pdev, "data write error :%d - attempt %d - %d\n", ret, retry, MAX_RETRY); ++ retry++; ++ mdelay(1); ++ } ++ } ++ } ++ sdio_unlock(hw_priv); ++ return ret; ++} ++ ++int xradio_indirect_read(struct xradio_common *hw_priv, u32 addr, void *buf, ++ size_t buf_len, u32 prefetch, u16 port_addr) ++{ ++ u32 val32 = 0; ++ int i, ret; ++ ++ if ((buf_len / 2) >= 0x1000) { ++ dev_err(hw_priv->pdev, "Can't read more than 0xfff words.\n"); ++ return -EINVAL; ++ goto out; ++ } ++ ++ sdio_lock(hw_priv); ++ /* Write address */ ++ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't write address register.\n"); ++ goto out; ++ } ++ ++ /* Read CONFIG Register Value - We will read 32 bits */ ++ ret = __xradio_read_reg32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't read config register.\n"); ++ goto out; ++ } ++ ++ /* Set PREFETCH bit */ ++ ret = __xradio_write_reg32(hw_priv, HIF_CONFIG_REG_ID, val32 | prefetch); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't write prefetch bit.\n"); ++ goto out; ++ } ++ ++ /* Check for PRE-FETCH bit to be cleared */ ++ for (i = 0; i < 20; i++) { ++ ret = __xradio_read_reg32(hw_priv, HIF_CONFIG_REG_ID, &val32); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't check prefetch bit.\n"); ++ goto out; ++ } ++ if (!(val32 & prefetch)) ++ break; ++ mdelay(i); ++ } ++ ++ if (val32 & prefetch) { ++ dev_err(hw_priv->pdev, "Prefetch bit is not cleared.\n"); ++ goto out; ++ } ++ ++ /* Read data port */ ++ ret = __xradio_read(hw_priv, port_addr, buf, buf_len, 0); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't read data port.\n"); ++ goto out; ++ } ++ ++out: ++ sdio_unlock(hw_priv); ++ return ret; ++} ++ ++int xradio_apb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, ++ size_t buf_len) ++{ ++ int ret; ++ ++ if ((buf_len / 2) >= 0x1000) { ++ dev_err(hw_priv->pdev, "Can't wrire more than 0xfff words.\n"); ++ return -EINVAL; ++ } ++ ++ sdio_lock(hw_priv); ++ ++ /* Write address */ ++ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't write address register.\n"); ++ goto out; ++ } ++ ++ /* Write data port */ ++ ret = __xradio_write(hw_priv, HIF_SRAM_DPORT_REG_ID, buf, buf_len, 0); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't write data port.\n"); ++ goto out; ++ } ++ ++out: ++ sdio_unlock(hw_priv); ++ return ret; ++} ++ ++int xradio_ahb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, ++ size_t buf_len) ++{ ++ int ret; ++ ++ if ((buf_len / 2) >= 0x1000) { ++ dev_err(hw_priv->pdev, "Can't wrire more than 0xfff words.\n"); ++ return -EINVAL; ++ } ++ ++ sdio_lock(hw_priv); ++ ++ /* Write address */ ++ ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't write address register.\n"); ++ goto out; ++ } ++ ++ /* Write data port */ ++ ret = __xradio_write(hw_priv, HIF_AHB_DPORT_REG_ID, buf, buf_len, 0); ++ if (ret < 0) { ++ dev_err(hw_priv->pdev, "Can't write data port.\n"); ++ goto out; ++ } ++ ++out: ++ sdio_unlock(hw_priv); ++ return ret; ++} +diff --git a/drivers/net/wireless/xradio/hwio.h b/drivers/net/wireless/xradio/hwio.h +new file mode 100644 +index 00000000..531c2b8c +--- /dev/null ++++ b/drivers/net/wireless/xradio/hwio.h +@@ -0,0 +1,229 @@ ++/* ++ * hardware interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef XRADIO_HWIO_H_INCLUDED ++#define XRADIO_HWIO_H_INCLUDED ++ ++/* extern */ struct xradio_common; ++ ++/* DPLL initial values */ ++#define DPLL_INIT_VAL_XRADIO (0x0EC4F121) ++ ++/* Hardware Type Definitions */ ++#define HIF_HW_TYPE_XRADIO (1) ++ ++ ++/* boot loader start address in SRAM */ ++#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) ++/* 32K, 0x4000 to 0xDFFF */ ++#define DOWNLOAD_FIFO_OFFSET (0x00004000) ++/* 32K */ ++#define DOWNLOAD_FIFO_SIZE (0x00008000) ++/* 128 bytes, 0xFF80 to 0xFFFF */ ++#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) ++#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) ++ ++/* Download control area */ ++struct download_cntl_t { ++ /* size of whole firmware file (including Cheksum), host init */ ++ u32 ImageSize; ++ /* downloading flags */ ++ u32 Flags; ++ /* No. of bytes put into the download, init & updated by host */ ++ u32 Put; ++ /* last traced program counter, last ARM reg_pc */ ++ u32 TracePc; ++ /* No. of bytes read from the download, host init, device updates */ ++ u32 Get; ++ /* r0, boot losader status, host init to pending, device updates */ ++ u32 Status; ++ /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ ++ u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS]; ++}; ++ ++#define DOWNLOAD_IMAGE_SIZE_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize)) ++#define DOWNLOAD_FLAGS_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags)) ++#define DOWNLOAD_PUT_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put)) ++#define DOWNLOAD_TRACE_PC_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc)) ++#define DOWNLOAD_GET_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get)) ++#define DOWNLOAD_STATUS_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status)) ++#define DOWNLOAD_DEBUG_DATA_REG \ ++ (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData)) ++ ++#define DOWNLOAD_DEBUG_DATA_LEN (108) ++#define DOWNLOAD_BLOCK_SIZE (1024) ++ ++/* For boot loader detection */ ++#define DOWNLOAD_ARE_YOU_HERE (0x87654321) ++#define DOWNLOAD_I_AM_HERE (0x12345678) ++ ++/* Download error code */ ++#define DOWNLOAD_PENDING (0xFFFFFFFF) ++#define DOWNLOAD_SUCCESS (0) ++#define DOWNLOAD_EXCEPTION (1) ++#define DOWNLOAD_ERR_MEM_1 (2) ++#define DOWNLOAD_ERR_MEM_2 (3) ++#define DOWNLOAD_ERR_SOFTWARE (4) ++#define DOWNLOAD_ERR_FILE_SIZE (5) ++#define DOWNLOAD_ERR_CHECKSUM (6) ++#define DOWNLOAD_ERR_OVERFLOW (7) ++#define DOWNLOAD_ERR_IMAGE (8) ++#define DOWNLOAD_ERR_HOST (9) ++#define DOWNLOAD_ERR_ABORT (10) ++ ++#define SYS_BASE_ADDR_SILICON (0) ++#define AHB_MEMORY_ADDRESS (SYS_BASE_ADDR_SILICON + 0x08000000) ++#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) ++#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) ++#define APB_ADDR(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) ++ ++/* *************************************************************** ++*Device register definitions ++*************************************************************** */ ++/* WBF - SPI Register Addresses */ ++#define HIF_ADDR_ID_BASE (0x0000) ++/* 16/32 bits */ ++#define HIF_CONFIG_REG_ID (0x0000) ++/* 16/32 bits */ ++#define HIF_CONTROL_REG_ID (0x0001) ++/* 16 bits, Q mode W/R */ ++#define HIF_IN_OUT_QUEUE_REG_ID (0x0002) ++/* 32 bits, AHB bus R/W */ ++#define HIF_AHB_DPORT_REG_ID (0x0003) ++/* 16/32 bits */ ++#define HIF_SRAM_BASE_ADDR_REG_ID (0x0004) ++/* 32 bits, APB bus R/W */ ++#define HIF_SRAM_DPORT_REG_ID (0x0005) ++/* 32 bits, t_settle/general */ ++#define HIF_TSET_GEN_R_W_REG_ID (0x0006) ++/* 16 bits, Q mode read, no length */ ++#define HIF_FRAME_OUT_REG_ID (0x0007) ++#define HIF_ADDR_ID_MAX (HIF_FRAME_OUT_REG_ID) ++ ++/* WBF - Control register bit set */ ++/* next o/p length, bit 11 to 0 */ ++#define HIF_CTRL_NEXT_LEN_MASK (0x0FFF) ++#define HIF_CTRL_WUP_BIT (BIT(12)) ++#define HIF_CTRL_RDY_BIT (BIT(13)) ++#define HIF_CTRL_IRQ_ENABLE (BIT(14)) ++#define HIF_CTRL_RDY_ENABLE (BIT(15)) ++#define HIF_CTRL_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) ++ ++/* SPI Config register bit set */ ++#define HIF_CONFIG_FRAME_BIT (BIT(2)) ++#define HIF_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) ++#define HIF_CONFIG_WORD_MODE_1 (BIT(3)) ++#define HIF_CONFIG_WORD_MODE_2 (BIT(4)) ++#define HIF_CONFIG_ERROR_0_BIT (BIT(5)) ++#define HIF_CONFIG_ERROR_1_BIT (BIT(6)) ++#define HIF_CONFIG_ERROR_2_BIT (BIT(7)) ++/* TBD: Sure??? */ ++#define HIF_CONFIG_CSN_FRAME_BIT (BIT(7)) ++#define HIF_CONFIG_ERROR_3_BIT (BIT(8)) ++#define HIF_CONFIG_ERROR_4_BIT (BIT(9)) ++/* QueueM */ ++#define HIF_CONFIG_ACCESS_MODE_BIT (BIT(10)) ++/* AHB bus */ ++#define HIF_CONFIG_AHB_PFETCH_BIT (BIT(11)) ++#define HIF_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) ++/* APB bus */ ++#define HIF_CONFIG_PFETCH_BIT (BIT(13)) ++/* cpu reset */ ++#define HIF_CONFIG_CPU_RESET_BIT (BIT(14)) ++#define HIF_CONFIG_CLEAR_INT_BIT (BIT(15)) ++ ++/* For XRADIO the IRQ Enable and Ready Bits are in CONFIG register */ ++#define HIF_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) ++ ++int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len); ++int xradio_data_write(struct xradio_common *hw_priv, const void *buf, size_t buf_len); ++int xradio_reg_read(struct xradio_common *hw_priv, u16 addr, void *buf, size_t buf_len); ++int xradio_reg_write(struct xradio_common *hw_priv, u16 addr, const void *buf, size_t buf_len); ++int xradio_indirect_read(struct xradio_common *hw_priv, u32 addr, void *buf, ++ size_t buf_len, u32 prefetch, u16 port_addr); ++int xradio_apb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, size_t buf_len); ++int xradio_ahb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, size_t buf_len); ++ ++ ++static inline int xradio_reg_read_16(struct xradio_common *hw_priv, ++ u16 addr, u16 *val) ++{ ++ int ret = 0; ++ u32 bigVal = 0; ++ ret = xradio_reg_read(hw_priv, addr, &bigVal, sizeof(bigVal)); ++ *val = (u16)bigVal; ++ return ret; ++} ++ ++static inline int xradio_reg_write_16(struct xradio_common *hw_priv, ++ u16 addr, u16 val) ++{ ++ u32 bigVal = (u32)val; ++ return xradio_reg_write(hw_priv, addr, &bigVal, sizeof(bigVal)); ++} ++ ++static inline int xradio_reg_read_32(struct xradio_common *hw_priv, ++ u16 addr, u32 *val) ++{ ++ return xradio_reg_read(hw_priv, addr, val, sizeof(val)); ++} ++ ++static inline int xradio_reg_write_32(struct xradio_common *hw_priv, ++ u16 addr, u32 val) ++{ ++ return xradio_reg_write(hw_priv, addr, &val, sizeof(val)); ++} ++ ++static inline int xradio_apb_read(struct xradio_common *hw_priv, u32 addr, ++ void *buf, size_t buf_len) ++{ ++ return xradio_indirect_read(hw_priv, addr, buf, buf_len, HIF_CONFIG_PFETCH_BIT, ++ HIF_SRAM_DPORT_REG_ID); ++} ++ ++static inline int xradio_ahb_read(struct xradio_common *hw_priv, u32 addr, ++ void *buf, size_t buf_len) ++{ ++ return xradio_indirect_read(hw_priv, addr, buf, buf_len, HIF_CONFIG_AHB_PFETCH_BIT, ++ HIF_AHB_DPORT_REG_ID); ++} ++ ++static inline int xradio_apb_read_32(struct xradio_common *hw_priv, ++ u32 addr, u32 *val) ++{ ++ return xradio_apb_read(hw_priv, addr, val, sizeof(val)); ++} ++ ++static inline int xradio_apb_write_32(struct xradio_common *hw_priv, ++ u32 addr, u32 val) ++{ ++ return xradio_apb_write(hw_priv, addr, &val, sizeof(val)); ++} ++ ++static inline int xradio_ahb_read_32(struct xradio_common *hw_priv, ++ u32 addr, u32 *val) ++{ ++ return xradio_ahb_read(hw_priv, addr, val, sizeof(val)); ++} ++ ++static inline int xradio_ahb_write_32(struct xradio_common *hw_priv, ++ u32 addr, u32 val) ++{ ++ return xradio_ahb_write(hw_priv, addr, &val, sizeof(val)); ++} ++ ++#endif /* XRADIO_HWIO_H_INCLUDED */ +diff --git a/drivers/net/wireless/xradio/keys.c b/drivers/net/wireless/xradio/keys.c +new file mode 100644 +index 00000000..9b3e14fd +--- /dev/null ++++ b/drivers/net/wireless/xradio/keys.c +@@ -0,0 +1,206 @@ ++#include ++ ++#include "xradio.h" ++#include "sta.h" ++#include "keys.h" ++ ++int xradio_alloc_key(struct xradio_common *hw_priv) ++{ ++ int idx; ++ ++ idx = ffs(~hw_priv->key_map) - 1; ++ if (idx < 0 || idx > WSM_KEY_MAX_INDEX) ++ return -1; ++ ++ hw_priv->key_map |= BIT(idx); ++ hw_priv->keys[idx].entryIndex = idx; ++ return idx; ++} ++ ++void xradio_free_key(struct xradio_common *hw_priv, int idx) ++{ ++ SYS_BUG(!(hw_priv->key_map & BIT(idx))); ++ memset(&hw_priv->keys[idx], 0, sizeof(hw_priv->keys[idx])); ++ hw_priv->key_map &= ~BIT(idx); ++} ++ ++void xradio_free_keys(struct xradio_common *hw_priv) ++{ ++ memset(&hw_priv->keys, 0, sizeof(hw_priv->keys)); ++ hw_priv->key_map = 0; ++} ++ ++int xradio_upload_keys(struct xradio_vif *priv) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ int idx, ret = 0; ++ ++ ++ for (idx = 0; idx <= WSM_KEY_MAX_IDX; ++idx) ++ if (hw_priv->key_map & BIT(idx)) { ++ ret = wsm_add_key(hw_priv, &hw_priv->keys[idx], priv->if_id); ++ if (ret < 0) ++ break; ++ } ++ return ret; ++} ++ ++int xradio_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, ++ struct ieee80211_vif *vif, struct ieee80211_sta *sta, ++ struct ieee80211_key_conf *key) ++{ ++#ifdef XRADIO_DISABLE_HW_CRYPTO ++ wiphy_info(dev->wiphy, "hw crypto is disabled, ignoring key request\n"); ++ return -EOPNOTSUPP; ++#else ++ int ret = -EOPNOTSUPP; ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ ++ wiphy_info(dev->wiphy, "vif %d: set_key cmd %d\n", priv->if_id, (int) cmd); ++ ++#ifdef P2P_MULTIVIF ++ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); ++#endif ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ if (cmd == SET_KEY) { ++ u8 *peer_addr = NULL; ++ int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? 1 : 0; ++ int idx = xradio_alloc_key(hw_priv); ++ struct wsm_add_key *wsm_key = &hw_priv->keys[idx]; ++ ++ if (idx < 0) { ++ wiphy_err(dev->wiphy, "xradio_alloc_key failed!\n"); ++ ret = -EINVAL; ++ goto finally; ++ } ++ ++ SYS_BUG(pairwise && !sta); ++ if (sta) ++ peer_addr = sta->addr; ++ ++ key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; ++ ++ priv->cipherType = key->cipher; ++ switch (key->cipher) { ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ if (key->keylen > 16) { ++ xradio_free_key(hw_priv, idx); ++ wiphy_err(dev->wiphy, "keylen too long=%d!\n", key->keylen); ++ ret = -EINVAL; ++ goto finally; ++ } ++ ++ if (pairwise) { ++ wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; ++ memcpy(wsm_key->wepPairwiseKey.peerAddress, peer_addr, ETH_ALEN); ++ memcpy(wsm_key->wepPairwiseKey.keyData, &key->key[0], key->keylen); ++ wsm_key->wepPairwiseKey.keyLength = key->keylen; ++ wiphy_debug(dev->wiphy, "WEP_PAIRWISE keylen=%d!\n", ++ key->keylen); ++ } else { ++ wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; ++ memcpy(wsm_key->wepGroupKey.keyData, &key->key[0], key->keylen); ++ wsm_key->wepGroupKey.keyLength = key->keylen; ++ wsm_key->wepGroupKey.keyId = key->keyidx; ++ wiphy_debug(dev->wiphy, "WEP_GROUP keylen=%d!\n", ++ key->keylen); ++ } ++ break; ++ case WLAN_CIPHER_SUITE_TKIP: ++ if (pairwise) { ++ wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; ++ memcpy(wsm_key->tkipPairwiseKey.peerAddress, peer_addr, ETH_ALEN); ++ memcpy(wsm_key->tkipPairwiseKey.tkipKeyData, &key->key[0], 16); ++ memcpy(wsm_key->tkipPairwiseKey.txMicKey, &key->key[16], 8); ++ memcpy(wsm_key->tkipPairwiseKey.rxMicKey, &key->key[24], 8); ++ wiphy_debug(dev->wiphy,"TKIP_PAIRWISE keylen=%d!\n", ++ key->keylen); ++ } else { ++ size_t mic_offset = (priv->mode == NL80211_IFTYPE_AP) ? 16 : 24; ++ wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; ++ memcpy(wsm_key->tkipGroupKey.tkipKeyData,&key->key[0], 16); ++ memcpy(wsm_key->tkipGroupKey.rxMicKey, &key->key[mic_offset], 8); ++ ++ /* TODO: Where can I find TKIP SEQ? */ ++ memset(wsm_key->tkipGroupKey.rxSeqCounter, 0, 8); ++ wsm_key->tkipGroupKey.keyId = key->keyidx; ++ wiphy_debug(dev->wiphy,"TKIP_GROUP keylen=%d!\n", ++ key->keylen); ++ } ++ break; ++ case WLAN_CIPHER_SUITE_CCMP: ++ if (pairwise) { ++ wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; ++ memcpy(wsm_key->aesPairwiseKey.peerAddress, peer_addr, ETH_ALEN); ++ memcpy(wsm_key->aesPairwiseKey.aesKeyData, &key->key[0], 16); ++ wiphy_debug(dev->wiphy, "CCMP_PAIRWISE keylen=%d!\n", ++ key->keylen); ++ } else { ++ wsm_key->type = WSM_KEY_TYPE_AES_GROUP; ++ memcpy(wsm_key->aesGroupKey.aesKeyData, &key->key[0], 16); ++ /* TODO: Where can I find AES SEQ? */ ++ memset(wsm_key->aesGroupKey.rxSeqCounter, 0, 8); ++ wsm_key->aesGroupKey.keyId = key->keyidx; ++ wiphy_debug(dev->wiphy, "CCMP_GROUP keylen=%d!\n", ++ key->keylen); ++ } ++ break; ++#ifdef CONFIG_XRADIO_WAPI_SUPPORT ++ case WLAN_CIPHER_SUITE_SMS4: ++ if (pairwise) { ++ wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; ++ memcpy(wsm_key->wapiPairwiseKey.peerAddress, peer_addr, ETH_ALEN); ++ memcpy(wsm_key->wapiPairwiseKey.wapiKeyData, &key->key[0], 16); ++ memcpy(wsm_key->wapiPairwiseKey.micKeyData, &key->key[16], 16); ++ wsm_key->wapiPairwiseKey.keyId = key->keyidx; ++ sta_printk(XRADIO_DBG_NIY,"%s: WAPI_PAIRWISE keylen=%d!\n", ++ __func__, key->keylen); ++ } else { ++ wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; ++ memcpy(wsm_key->wapiGroupKey.wapiKeyData, &key->key[0], 16); ++ memcpy(wsm_key->wapiGroupKey.micKeyData, &key->key[16], 16); ++ wsm_key->wapiGroupKey.keyId = key->keyidx; ++ sta_printk(XRADIO_DBG_NIY,"%s: WAPI_GROUP keylen=%d!\n", ++ __func__, key->keylen); ++ } ++ break; ++#endif /* CONFIG_XRADIO_WAPI_SUPPORT */ ++ default: ++ wiphy_err(dev->wiphy, "key->cipher unknown(%d)!\n", key->cipher); ++ xradio_free_key(hw_priv, idx); ++ ret = -EOPNOTSUPP; ++ goto finally; ++ } ++ ret = SYS_WARN(wsm_add_key(hw_priv, wsm_key, priv->if_id)); ++ if (!ret) ++ key->hw_key_idx = idx; ++ else ++ xradio_free_key(hw_priv, idx); ++ ++ if (!ret && (pairwise || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) && ++ (priv->filter4.enable & 0x2)) ++ xradio_set_arpreply(dev, vif); ++ } else if (cmd == DISABLE_KEY) { ++ struct wsm_remove_key wsm_key = { ++ .entryIndex = key->hw_key_idx, ++ }; ++ ++ if (wsm_key.entryIndex > WSM_KEY_MAX_IDX) { ++ ret = -EINVAL; ++ goto finally; ++ } ++ ++ xradio_free_key(hw_priv, wsm_key.entryIndex); ++ ret = wsm_remove_key(hw_priv, &wsm_key, priv->if_id); ++ } else { ++ wiphy_err(dev->wiphy, "Unsupported command\n"); ++ } ++ ++finally: ++ mutex_unlock(&hw_priv->conf_mutex); ++ return ret; ++#endif // XRADIO_DISABLE_HW_CRYPTO ++} +diff --git a/drivers/net/wireless/xradio/keys.h b/drivers/net/wireless/xradio/keys.h +new file mode 100644 +index 00000000..23c5880e +--- /dev/null ++++ b/drivers/net/wireless/xradio/keys.h +@@ -0,0 +1,12 @@ ++#ifndef __KEYS_H_ ++#define __KEYS_H_INCLUDED ++ ++int xradio_alloc_key(struct xradio_common *hw_priv); ++void xradio_free_key(struct xradio_common *hw_priv, int idx); ++void xradio_free_keys(struct xradio_common *hw_priv); ++int xradio_upload_keys(struct xradio_vif *priv); ++int xradio_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, ++ struct ieee80211_vif *vif, struct ieee80211_sta *sta, ++ struct ieee80211_key_conf *key); ++ ++#endif +\ No newline at end of file +diff --git a/drivers/net/wireless/xradio/main.c b/drivers/net/wireless/xradio/main.c +new file mode 100644 +index 00000000..d41a75c9 +--- /dev/null ++++ b/drivers/net/wireless/xradio/main.c +@@ -0,0 +1,730 @@ ++/* ++ * Main code of XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++/*Linux version 3.4.0 compilation*/ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "txrx.h" ++#include "fwio.h" ++#include "hwio.h" ++#include "bh.h" ++#include "sta.h" ++#include "ap.h" ++#include "keys.h" ++#include "scan.h" ++#include "pm.h" ++#include "sdio.h" ++ ++/* TODO: use rates and channels from the device */ ++#define RATETAB_ENT(_rate, _rateid, _flags) \ ++ { \ ++ .bitrate = (_rate), \ ++ .hw_value = (_rateid), \ ++ .flags = (_flags), \ ++ } ++ ++static struct ieee80211_rate xradio_rates[] = { ++ RATETAB_ENT(10, 0, 0), ++ RATETAB_ENT(20, 1, 0), ++ RATETAB_ENT(55, 2, 0), ++ RATETAB_ENT(110, 3, 0), ++ RATETAB_ENT(60, 6, 0), ++ RATETAB_ENT(90, 7, 0), ++ RATETAB_ENT(120, 8, 0), ++ RATETAB_ENT(180, 9, 0), ++ RATETAB_ENT(240, 10, 0), ++ RATETAB_ENT(360, 11, 0), ++ RATETAB_ENT(480, 12, 0), ++ RATETAB_ENT(540, 13, 0), ++}; ++ ++static struct ieee80211_rate xradio_mcs_rates[] = { ++ RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), ++ RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), ++}; ++ ++#define xradio_g_rates (xradio_rates + 0) ++#define xradio_a_rates (xradio_rates + 4) ++#define xradio_n_rates (xradio_mcs_rates) ++ ++#define xradio_g_rates_size (ARRAY_SIZE(xradio_rates)) ++#define xradio_a_rates_size (ARRAY_SIZE(xradio_rates) - 4) ++#define xradio_n_rates_size (ARRAY_SIZE(xradio_mcs_rates)) ++ ++#define CHAN2G(_channel, _freq, _flags) { \ ++ .band = NL80211_BAND_2GHZ, \ ++ .center_freq = (_freq), \ ++ .hw_value = (_channel), \ ++ .flags = (_flags), \ ++ .max_antenna_gain = 0, \ ++ .max_power = 30, \ ++} ++ ++#define CHAN5G(_channel, _flags) { \ ++ .band = NL80211_BAND_5GHZ, \ ++ .center_freq = 5000 + (5 * (_channel)), \ ++ .hw_value = (_channel), \ ++ .flags = (_flags), \ ++ .max_antenna_gain = 0, \ ++ .max_power = 30, \ ++} ++ ++static struct ieee80211_channel xradio_2ghz_chantable[] = { ++ CHAN2G(1, 2412, 0), ++ CHAN2G(2, 2417, 0), ++ CHAN2G(3, 2422, 0), ++ CHAN2G(4, 2427, 0), ++ CHAN2G(5, 2432, 0), ++ CHAN2G(6, 2437, 0), ++ CHAN2G(7, 2442, 0), ++ CHAN2G(8, 2447, 0), ++ CHAN2G(9, 2452, 0), ++ CHAN2G(10, 2457, 0), ++ CHAN2G(11, 2462, 0), ++ CHAN2G(12, 2467, 0), ++ CHAN2G(13, 2472, 0), ++ CHAN2G(14, 2484, 0), ++}; ++ ++#ifdef CONFIG_XRADIO_5GHZ_SUPPORT ++static struct ieee80211_channel xradio_5ghz_chantable[] = { ++ CHAN5G(34, 0), CHAN5G(36, 0), ++ CHAN5G(38, 0), CHAN5G(40, 0), ++ CHAN5G(42, 0), CHAN5G(44, 0), ++ CHAN5G(46, 0), CHAN5G(48, 0), ++ CHAN5G(52, 0), CHAN5G(56, 0), ++ CHAN5G(60, 0), CHAN5G(64, 0), ++ CHAN5G(100, 0), CHAN5G(104, 0), ++ CHAN5G(108, 0), CHAN5G(112, 0), ++ CHAN5G(116, 0), CHAN5G(120, 0), ++ CHAN5G(124, 0), CHAN5G(128, 0), ++ CHAN5G(132, 0), CHAN5G(136, 0), ++ CHAN5G(140, 0), CHAN5G(149, 0), ++ CHAN5G(153, 0), CHAN5G(157, 0), ++ CHAN5G(161, 0), CHAN5G(165, 0), ++ CHAN5G(184, 0), CHAN5G(188, 0), ++ CHAN5G(192, 0), CHAN5G(196, 0), ++ CHAN5G(200, 0), CHAN5G(204, 0), ++ CHAN5G(208, 0), CHAN5G(212, 0), ++ CHAN5G(216, 0), ++}; ++#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ ++ ++static struct ieee80211_supported_band xradio_band_2ghz = { ++ .channels = xradio_2ghz_chantable, ++ .n_channels = ARRAY_SIZE(xradio_2ghz_chantable), ++ .bitrates = xradio_g_rates, ++ .n_bitrates = xradio_g_rates_size, ++ .ht_cap = { ++ .cap = IEEE80211_HT_CAP_GRN_FLD | ++ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), ++ .ht_supported = 1, ++ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K, ++ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, ++ .mcs = { ++ .rx_mask[0] = 0xFF, ++ .rx_highest = __cpu_to_le16(0x41), ++ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, ++ }, ++ }, ++}; ++ ++#ifdef CONFIG_XRADIO_5GHZ_SUPPORT ++static struct ieee80211_supported_band xradio_band_5ghz = { ++ .channels = xradio_5ghz_chantable, ++ .n_channels = ARRAY_SIZE(xradio_5ghz_chantable), ++ .bitrates = xradio_a_rates, ++ .n_bitrates = xradio_a_rates_size, ++ .ht_cap = { ++ .cap = IEEE80211_HT_CAP_GRN_FLD | ++ (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), ++ .ht_supported = 1, ++ .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, ++ .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, ++ .mcs = { ++ .rx_mask[0] = 0xFF, ++ .rx_highest = __cpu_to_le16(0x41), ++ .tx_params = IEEE80211_HT_MCS_TX_DEFINED, ++ }, ++ }, ++}; ++#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ ++ ++static const unsigned long xradio_ttl[] = { ++ 1 * HZ, /* VO */ ++ 2 * HZ, /* VI */ ++ 5 * HZ, /* BE */ ++ 10 * HZ /* BK */ ++}; ++ ++static const struct ieee80211_ops xradio_ops = { ++ .start = xradio_start, ++ .stop = xradio_stop, ++ .add_interface = xradio_add_interface, ++ .remove_interface = xradio_remove_interface, ++ .change_interface = xradio_change_interface, ++ .tx = xradio_tx, ++ .hw_scan = xradio_hw_scan, ++#ifdef ROAM_OFFLOAD ++ .sched_scan_start = xradio_hw_sched_scan_start, ++ .sched_scan_stop = xradio_hw_sched_scan_stop, ++#endif /*ROAM_OFFLOAD*/ ++ .set_tim = xradio_set_tim, ++ .sta_notify = xradio_sta_notify, ++ .sta_add = xradio_sta_add, ++ .sta_remove = xradio_sta_remove, ++ .set_key = xradio_set_key, ++ .set_rts_threshold = xradio_set_rts_threshold, ++ .config = xradio_config, ++ .bss_info_changed = xradio_bss_info_changed, ++ .prepare_multicast = xradio_prepare_multicast, ++ .configure_filter = xradio_configure_filter, ++ .conf_tx = xradio_conf_tx, ++ .get_stats = xradio_get_stats, ++ .ampdu_action = xradio_ampdu_action, ++ .flush = xradio_flush, ++#ifdef CONFIG_PM ++ .suspend = xradio_wow_suspend, ++ .resume = xradio_wow_resume, ++#endif /* CONFIG_PM */ ++ /* Intentionally not offloaded: */ ++ /*.channel_switch = xradio_channel_switch, */ ++ .remain_on_channel = xradio_remain_on_channel, ++ .cancel_remain_on_channel = xradio_cancel_remain_on_channel, ++#ifdef CONFIG_XRADIO_TESTMODE ++ .testmode_cmd = xradio_testmode_cmd, ++#endif /* CONFIG_XRADIO_TESTMODE */ ++}; ++ ++ ++/*************************************** functions ***************************************/ ++ ++static void xradio_set_ifce_comb(struct xradio_common *hw_priv, ++ struct ieee80211_hw *hw) ++{ ++#ifdef P2P_MULTIVIF ++ hw_priv->if_limits1[0].max = 2; ++#else ++ hw_priv->if_limits1[0].max = 1; ++#endif ++ ++ hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION); ++ hw_priv->if_limits1[1].max = 1; ++ hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP); ++ ++#ifdef P2P_MULTIVIF ++ hw_priv->if_limits2[0].max = 3; ++#else ++ hw_priv->if_limits2[0].max = 2; ++#endif ++ hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION); ++ ++#ifdef P2P_MULTIVIF ++ hw_priv->if_limits3[0].max = 2; ++#else ++ hw_priv->if_limits3[0].max = 1; ++#endif ++ ++ hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION); ++ hw_priv->if_limits3[1].max = 1; ++ hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | ++ BIT(NL80211_IFTYPE_P2P_GO); ++ ++ /* TODO:COMBO: mac80211 doesn't yet support more than 1 ++ * different channel */ ++ hw_priv->if_combs[0].num_different_channels = 1; ++#ifdef P2P_MULTIVIF ++ hw_priv->if_combs[0].max_interfaces = 3; ++#else ++ hw_priv->if_combs[0].max_interfaces = 2; ++#endif ++ hw_priv->if_combs[0].limits = hw_priv->if_limits1; ++ hw_priv->if_combs[0].n_limits = 2; ++ ++ hw_priv->if_combs[1].num_different_channels = 1; ++ ++#ifdef P2P_MULTIVIF ++ hw_priv->if_combs[1].max_interfaces = 3; ++#else ++ hw_priv->if_combs[1].max_interfaces = 2; ++#endif ++ hw_priv->if_combs[1].limits = hw_priv->if_limits2; ++ hw_priv->if_combs[1].n_limits = 1; ++ ++ hw_priv->if_combs[2].num_different_channels = 1; ++#ifdef P2P_MULTIVIF ++ hw_priv->if_combs[2].max_interfaces = 3; ++#else ++ hw_priv->if_combs[2].max_interfaces = 2; ++#endif ++ hw_priv->if_combs[2].limits = hw_priv->if_limits3; ++ hw_priv->if_combs[2].n_limits = 2; ++ ++ hw->wiphy->iface_combinations = &hw_priv->if_combs[0]; ++ hw->wiphy->n_iface_combinations = 3; ++} ++ ++struct ieee80211_hw *xradio_init_common(size_t hw_priv_data_len) ++{ ++ int i; ++ struct ieee80211_hw *hw; ++ struct xradio_common *hw_priv; ++ struct ieee80211_supported_band *sband; ++ int band; ++ ++ /* Alloc ieee_802.11 hw and xradio_common struct. */ ++ hw = ieee80211_alloc_hw(hw_priv_data_len, &xradio_ops); ++ if (!hw) ++ return NULL; ++ hw_priv = hw->priv; ++ memset(hw_priv, 0, sizeof(*hw_priv)); ++ ++ /* Initialize members of hw_priv. */ ++ hw_priv->hw = hw; ++ hw_priv->if_id_slot = 0; ++ hw_priv->roc_if_id = -1; ++ atomic_set(&hw_priv->num_vifs, 0); ++ /* initial rates and channels TODO: fetch from FW */ ++ hw_priv->rates = xradio_rates; ++ hw_priv->mcs_rates = xradio_n_rates; ++#ifdef ROAM_OFFLOAD ++ hw_priv->auto_scanning = 0; ++ hw_priv->frame_rcvd = 0; ++ hw_priv->num_scanchannels = 0; ++ hw_priv->num_2g_channels = 0; ++ hw_priv->num_5g_channels = 0; ++#endif /*ROAM_OFFLOAD*/ ++#ifdef AP_AGGREGATE_FW_FIX ++ /* Enable block ACK for 4 TID (BE,VI,VI,VO). */ ++ hw_priv->ba_tid_mask = 0xB1; /*due to HW limitations*/ ++#else ++ /* Enable block ACK for every TID but voice. */ ++ hw_priv->ba_tid_mask = 0x3F; ++#endif ++ hw_priv->noise = -94; ++ /* hw_priv->beacon_req_id = cpu_to_le32(0); */ ++ ++ /* Initialize members of ieee80211_hw, it works in UMAC. */ ++ hw->sta_data_size = sizeof(struct xradio_sta_priv); ++ hw->vif_data_size = sizeof(struct xradio_vif); ++ ++ ieee80211_hw_set(hw, SIGNAL_DBM); ++ ieee80211_hw_set(hw, SUPPORTS_PS); ++ ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ++ ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); ++ ieee80211_hw_set(hw, CONNECTION_MONITOR); ++ ++/* hw->flags = IEEE80211_HW_SIGNAL_DBM | ++ IEEE80211_HW_SUPPORTS_PS | ++ IEEE80211_HW_SUPPORTS_DYNAMIC_PS | ++ IEEE80211_HW_REPORTS_TX_ACK_STATUS | ++ IEEE80211_HW_CONNECTION_MONITOR;*/ ++ //IEEE80211_HW_SUPPORTS_CQM_RSSI | ++ /* Aggregation is fully controlled by firmware. ++ * Do not need any support from the mac80211 stack */ ++ /* IEEE80211_HW_AMPDU_AGGREGATION | */ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //IEEE80211_HW_SUPPORTS_P2P_PS | ++ //IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | ++ // IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ //IEEE80211_HW_BEACON_FILTER; ++ ++ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | ++ BIT(NL80211_IFTYPE_ADHOC) | ++ BIT(NL80211_IFTYPE_AP) | ++ BIT(NL80211_IFTYPE_MESH_POINT) | ++ BIT(NL80211_IFTYPE_P2P_CLIENT) | ++ BIT(NL80211_IFTYPE_P2P_GO); ++ ++ /* Support only for limited wowlan functionalities */ ++ /* TODO by Icenowy: RESTORE THIS */ ++/* hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT; ++ hw->wiphy->wowlan.n_patterns = 0;*/ ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ /* fix the problem that driver can not set pro-resp templet frame to fw */ ++ hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; ++ ++#if defined(CONFIG_XRADIO_DISABLE_BEACON_HINTS) ++ hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; ++#endif ++ hw->wiphy->n_addresses = XRWL_MAX_VIFS; ++ hw->wiphy->addresses = hw_priv->addresses; ++ hw->wiphy->max_remain_on_channel_duration = 500; ++ hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + ++ 8 /* TKIP IV */ + ++ 12 /* TKIP ICV and MIC */; ++ hw->wiphy->bands[NL80211_BAND_2GHZ] = &xradio_band_2ghz; ++#ifdef CONFIG_XRADIO_5GHZ_SUPPORT ++ hw->wiphy->bands[NL80211_BAND_5GHZ] = &xradio_band_5ghz; ++#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ ++ hw->queues = AC_QUEUE_NUM; ++ hw->max_rates = MAX_RATES_STAGE; ++ hw->max_rate_tries = MAX_RATES_RETRY; ++ /* Channel params have to be cleared before registering wiphy again */ ++ for (band = 0; band < NUM_NL80211_BANDS; band++) { ++ sband = hw->wiphy->bands[band]; ++ if (!sband) ++ continue; ++ for (i = 0; i < sband->n_channels; i++) { ++ sband->channels[i].flags = 0; ++ sband->channels[i].max_antenna_gain = 0; ++ sband->channels[i].max_power = 30; ++ } ++ } ++ /* hw_priv->channel init value is the local->oper_channel init value;when transplanting,take care */ ++ for (band = 0; band < NUM_NL80211_BANDS; band++) { ++ sband = hw->wiphy->bands[band]; ++ if (!sband) ++ continue; ++ if(!hw_priv->channel){ ++ hw_priv->channel = &sband->channels[2]; ++ } ++ } ++ hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS; ++ hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; ++ SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr); ++ ++ /* Initialize locks. */ ++ spin_lock_init(&hw_priv->vif_list_lock); ++ mutex_init(&hw_priv->wsm_cmd_mux); ++ mutex_init(&hw_priv->conf_mutex); ++ mutex_init(&hw_priv->wsm_oper_lock); ++ atomic_set(&hw_priv->tx_lock, 0); ++ sema_init(&hw_priv->tx_lock_sem, 1); ++ ++ hw_priv->workqueue = create_singlethread_workqueue(XRADIO_WORKQUEUE); ++ sema_init(&hw_priv->scan.lock, 1); ++ sema_init(&hw_priv->scan.status_lock,1); ++ INIT_WORK(&hw_priv->scan.work, xradio_scan_work); ++#ifdef ROAM_OFFLOAD ++ INIT_WORK(&hw_priv->scan.swork, xradio_sched_scan_work); ++#endif /*ROAM_OFFLOAD*/ ++ INIT_DELAYED_WORK(&hw_priv->scan.probe_work, xradio_probe_work); ++ INIT_DELAYED_WORK(&hw_priv->scan.timeout, xradio_scan_timeout); ++ INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, xradio_rem_chan_timeout); ++ INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); ++ atomic_set(&hw_priv->upload_count, 0); ++ memset(&hw_priv->connet_time, 0, sizeof(hw_priv->connet_time)); ++ ++ spin_lock_init(&hw_priv->event_queue_lock); ++ INIT_LIST_HEAD(&hw_priv->event_queue); ++ INIT_WORK(&hw_priv->event_handler, xradio_event_handler); ++ INIT_WORK(&hw_priv->ba_work, xradio_ba_work); ++ spin_lock_init(&hw_priv->ba_lock); ++ init_timer(&hw_priv->ba_timer); ++ hw_priv->ba_timer.data = (unsigned long)hw_priv; ++ hw_priv->ba_timer.function = xradio_ba_timer; ++ ++ if (unlikely(xradio_queue_stats_init(&hw_priv->tx_queue_stats, ++ WLAN_LINK_ID_MAX,xradio_skb_dtor, hw_priv))) { ++ ieee80211_free_hw(hw); ++ return NULL; ++ } ++ for (i = 0; i < AC_QUEUE_NUM; ++i) { ++ if (unlikely(xradio_queue_init(&hw_priv->tx_queue[i], ++ &hw_priv->tx_queue_stats, i, XRWL_MAX_QUEUE_SZ, xradio_ttl[i]))) { ++ for (; i > 0; i--) ++ xradio_queue_deinit(&hw_priv->tx_queue[i - 1]); ++ xradio_queue_stats_deinit(&hw_priv->tx_queue_stats); ++ ieee80211_free_hw(hw); ++ return NULL; ++ } ++ } ++ ++ init_waitqueue_head(&hw_priv->channel_switch_done); ++ init_waitqueue_head(&hw_priv->wsm_cmd_wq); ++ init_waitqueue_head(&hw_priv->wsm_startup_done); ++ init_waitqueue_head(&hw_priv->offchannel_wq); ++ hw_priv->wsm_caps.firmwareReady = 0; ++ hw_priv->driver_ready = 0; ++ hw_priv->offchannel_done = 0; ++ wsm_buf_init(&hw_priv->wsm_cmd_buf); ++ spin_lock_init(&hw_priv->wsm_cmd.lock); ++ tx_policy_init(hw_priv); ++ xradio_init_resv_skb(hw_priv); ++ /* add for setting short_frame_max_tx_count(mean wdev->retry_short) to drv,init the max_rate_tries */ ++ spin_lock_bh(&hw_priv->tx_policy_cache.lock); ++ hw_priv->long_frame_max_tx_count = hw->conf.long_frame_max_tx_count; ++ hw_priv->short_frame_max_tx_count = ++ (hw->conf.short_frame_max_tx_count< 0x0F) ? ++ hw->conf.short_frame_max_tx_count : 0x0F; ++ hw_priv->hw->max_rate_tries = hw->conf.short_frame_max_tx_count; ++ spin_unlock_bh(&hw_priv->tx_policy_cache.lock); ++ ++ for (i = 0; i < XRWL_MAX_VIFS; i++) ++ hw_priv->hw_bufs_used_vif[i] = 0; ++ ++#ifdef MCAST_FWDING ++ for (i = 0; i < WSM_MAX_BUF; i++) ++ wsm_init_release_buffer_request(hw_priv, i); ++ hw_priv->buf_released = 0; ++#endif ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; ++ ++#if defined(CONFIG_XRADIO_DEBUG) ++ hw_priv->wsm_enable_wsm_dumps = 0; ++ hw_priv->wsm_dump_max_size = WSM_DUMP_MAX_SIZE; ++#endif /* CONFIG_XRADIO_DEBUG */ ++ hw_priv->query_packetID = 0; ++ atomic_set(&hw_priv->query_cnt, 0); ++ INIT_WORK(&hw_priv->query_work, wsm_query_work); ++ ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++ atomic_set(&hw_priv->suspend_state, XRADIO_RESUME); ++#endif ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ hw_priv->test_frame.data = NULL; ++ hw_priv->test_frame.len = 0; ++ spin_lock_init(&hw_priv->tsm_lock); ++ INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout, ++ xradio_advance_scan_timeout); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++ xradio_set_ifce_comb(hw_priv, hw_priv->hw); ++ ++ return hw; ++} ++ ++void xradio_free_common(struct ieee80211_hw *dev) ++{ ++ int i; ++ struct xradio_common *hw_priv = dev->priv; ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ kfree(hw_priv->test_frame.data); ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++ cancel_work_sync(&hw_priv->query_work); ++ del_timer_sync(&hw_priv->ba_timer); ++ mutex_destroy(&hw_priv->wsm_oper_lock); ++ mutex_destroy(&hw_priv->conf_mutex); ++ mutex_destroy(&hw_priv->wsm_cmd_mux); ++ wsm_buf_deinit(&hw_priv->wsm_cmd_buf); ++ flush_workqueue(hw_priv->workqueue); ++ destroy_workqueue(hw_priv->workqueue); ++ hw_priv->workqueue = NULL; ++ ++ xradio_deinit_resv_skb(hw_priv); ++ if (hw_priv->skb_cache) { ++ dev_kfree_skb(hw_priv->skb_cache); ++ hw_priv->skb_cache = NULL; ++ } ++ ++ for (i = 0; i < 4; ++i) ++ xradio_queue_deinit(&hw_priv->tx_queue[i]); ++ xradio_queue_stats_deinit(&hw_priv->tx_queue_stats); ++ ++ for (i = 0; i < XRWL_MAX_VIFS; i++) { ++ kfree(hw_priv->vif_list[i]); ++ hw_priv->vif_list[i] = NULL; ++ } ++ ++//fixed memory leakage by yangfh ++#ifdef MCAST_FWDING ++ wsm_deinit_release_buffer(hw_priv); ++#endif ++ /* unsigned int i; */ ++ ieee80211_free_hw(dev); ++} ++ ++int xradio_register_common(struct ieee80211_hw *dev) ++{ ++ int err = 0; ++ struct xradio_common *hw_priv = dev->priv; ++ ++ SET_IEEE80211_DEV(dev, hw_priv->pdev); ++ err = ieee80211_register_hw(dev); ++ if (err) { ++ dev_dbg(hw_priv->pdev, "Cannot register device (%d).\n", err); ++ return err; ++ } ++ dev_dbg(hw_priv->pdev, "is registered as '%s'\n", ++ wiphy_name(dev->wiphy)); ++ ++ xradio_debug_init_common(hw_priv); ++ hw_priv->driver_ready = 1; ++ wake_up(&hw_priv->wsm_startup_done); ++ return 0; ++} ++ ++void xradio_unregister_common(struct ieee80211_hw *dev) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ ++ if (wiphy_dev(dev->wiphy)) { ++ ieee80211_unregister_hw(dev); ++ SET_IEEE80211_DEV(dev, NULL); ++ xradio_debug_release_common(hw_priv); ++ } ++ hw_priv->driver_ready = 0; ++} ++ ++int xradio_core_init(struct sdio_func* func) ++{ ++ int err = -ENOMEM; ++ u16 ctrl_reg; ++ int if_id; ++ struct ieee80211_hw *dev; ++ struct xradio_common *hw_priv; ++ struct wsm_operational_mode mode = { ++ .power_mode = wsm_power_mode_quiescent, ++ .disableMoreFlagUsage = true, ++ }; ++ unsigned char randomaddr[ETH_ALEN]; ++ const unsigned char *addr = NULL; ++ ++ //init xradio_common ++ dev = xradio_init_common(sizeof(struct xradio_common)); ++ if (!dev) { ++ xradio_dbg(XRADIO_DBG_ERROR,"xradio_init_common failed\n"); ++ return err; ++ } ++ hw_priv = dev->priv; ++ hw_priv->pdev = &func->dev; ++ hw_priv->sdio_func = func; ++ sdio_set_drvdata(func, hw_priv); ++ ++ // fill in mac addresses ++ if (hw_priv->pdev->of_node) { ++ addr = of_get_mac_address(hw_priv->pdev->of_node); ++ } ++ if (!addr) { ++ dev_warn(hw_priv->pdev, "no mac address provided, using random\n"); ++ eth_random_addr(randomaddr); ++ addr = randomaddr; ++ } ++ memcpy(hw_priv->addresses[0].addr, addr, ETH_ALEN); ++ memcpy(hw_priv->addresses[1].addr, addr, ETH_ALEN); ++ hw_priv->addresses[1].addr[5] += 0x01; ++#ifdef P2P_MULTIVIF ++ memcpy(hw_priv->addresses[2].addr, addr, ETH_ALEN); ++ hw_priv->addresses[2].addr[4] ^= 0x80; ++#endif ++ ++ /* WSM callbacks. */ ++ hw_priv->wsm_cbc.scan_complete = xradio_scan_complete_cb; ++ hw_priv->wsm_cbc.tx_confirm = xradio_tx_confirm_cb; ++ hw_priv->wsm_cbc.suspend_resume = xradio_suspend_resume; ++ /* hw_priv->wsm_cbc.set_pm_complete = xradio_set_pm_complete_cb; */ ++ hw_priv->wsm_cbc.channel_switch = xradio_channel_switch_cb; ++ ++ /*init pm and wakelock. */ ++#ifdef CONFIG_PM ++ err = xradio_pm_init(&hw_priv->pm_state, hw_priv); ++ if (err) { ++ xradio_dbg(XRADIO_DBG_ERROR, "xradio_pm_init failed(%d).\n", err); ++ goto err2; ++ } ++#endif ++ /* Register bh thread*/ ++ err = xradio_register_bh(hw_priv); ++ if (err) { ++ xradio_dbg(XRADIO_DBG_ERROR, "xradio_register_bh failed(%d).\n", ++ err); ++ goto err3; ++ } ++ ++ /* Load firmware and register Interrupt Handler */ ++ err = xradio_load_firmware(hw_priv); ++ if (err) { ++ xradio_dbg(XRADIO_DBG_ERROR, "xradio_load_firmware failed(%d).\n", ++ err); ++ goto err4; ++ } ++ ++ /* Set sdio blocksize. */ ++ sdio_lock(hw_priv); ++ SYS_WARN(sdio_set_blk_size(hw_priv, ++ SDIO_BLOCK_SIZE)); ++ sdio_unlock(hw_priv); ++ ++ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, ++ hw_priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { ++ ++ /* TODO: Needs to find how to reset device */ ++ /* in QUEUE mode properly. */ ++ xradio_dbg(XRADIO_DBG_ERROR, "Firmware Startup Timeout!\n"); ++ err = -ETIMEDOUT; ++ goto err5; ++ } ++ xradio_dbg(XRADIO_DBG_ALWY,"Firmware Startup Done.\n"); ++ ++ /* Keep device wake up. */ ++ SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT)); ++ if (xradio_reg_read_16(hw_priv,HIF_CONTROL_REG_ID, &ctrl_reg)) ++ SYS_WARN(xradio_reg_read_16(hw_priv,HIF_CONTROL_REG_ID, &ctrl_reg)); ++ SYS_WARN(!(ctrl_reg & HIF_CTRL_RDY_BIT)); ++ ++ /* Set device mode parameter. */ ++ for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); if_id++) { ++ /* Set low-power mode. */ ++ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, if_id)); ++ /* Enable multi-TX confirmation */ ++ SYS_WARN(wsm_use_multi_tx_conf(hw_priv, true, if_id)); ++ } ++ ++ /* Register wireless net device. */ ++ err = xradio_register_common(dev); ++ if (err) { ++ xradio_dbg(XRADIO_DBG_ERROR,"xradio_register_common failed(%d)!\n", err); ++ goto err5; ++ } ++ ++ return err; ++ ++err5: ++ xradio_dev_deinit(hw_priv); ++err4: ++ xradio_unregister_bh(hw_priv); ++err3: ++ xradio_pm_deinit(&hw_priv->pm_state); ++err2: ++err1: ++ xradio_free_common(dev); ++ return err; ++} ++ ++void xradio_core_deinit(struct sdio_func* func) ++{ ++ struct xradio_common* hw_priv = sdio_get_drvdata(func); ++ if (hw_priv) { ++ xradio_unregister_common(hw_priv->hw); ++ xradio_dev_deinit(hw_priv); ++ xradio_unregister_bh(hw_priv); ++ xradio_pm_deinit(&hw_priv->pm_state); ++ xradio_free_common(hw_priv->hw); ++ } ++ return; ++} +diff --git a/drivers/net/wireless/xradio/main.h b/drivers/net/wireless/xradio/main.h +new file mode 100644 +index 00000000..2238d751 +--- /dev/null ++++ b/drivers/net/wireless/xradio/main.h +@@ -0,0 +1,7 @@ ++#ifndef __XRADIO_MAIN_H ++#define __XRADIO_MAIN_H ++ ++int xradio_core_init(struct sdio_func* func); ++void xradio_core_deinit(struct sdio_func* func); ++ ++#endif +\ No newline at end of file +diff --git a/drivers/net/wireless/xradio/module.c b/drivers/net/wireless/xradio/module.c +new file mode 100644 +index 00000000..83d4d7db +--- /dev/null ++++ b/drivers/net/wireless/xradio/module.c +@@ -0,0 +1,29 @@ ++#include ++ ++#include "xradio.h" ++#include "debug.h" ++#include "sdio.h" ++ ++MODULE_AUTHOR("XRadioTech"); ++MODULE_DESCRIPTION("XRadioTech WLAN driver core"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("xradio_core"); ++ ++/* Init Module function -> Called by insmod */ ++static int __init xradio_core_entry(void) ++{ ++ int ret = 0; ++ ret = xradio_host_dbg_init(); ++ ret = xradio_sdio_register(); ++ return ret; ++} ++ ++/* Called at Driver Unloading */ ++static void __exit xradio_core_exit(void) ++{ ++ xradio_sdio_unregister(); ++ xradio_host_dbg_deinit(); ++} ++ ++module_init(xradio_core_entry); ++module_exit(xradio_core_exit); +diff --git a/drivers/net/wireless/xradio/pm.c b/drivers/net/wireless/xradio/pm.c +new file mode 100644 +index 00000000..49d171e2 +--- /dev/null ++++ b/drivers/net/wireless/xradio/pm.c +@@ -0,0 +1,812 @@ ++/* ++ * PM implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include "xradio.h" ++#include "pm.h" ++#include "sta.h" ++#include "bh.h" ++#include "sdio.h" ++ ++#define XRADIO_BEACON_SKIPPING_MULTIPLIER 3 ++ ++struct xradio_udp_port_filter { ++ struct wsm_udp_port_filter_hdr hdr; ++ struct wsm_udp_port_filter dhcp; ++ struct wsm_udp_port_filter upnp; ++} __packed; ++ ++struct xradio_ether_type_filter { ++ struct wsm_ether_type_filter_hdr hdr; ++ struct wsm_ether_type_filter ip; ++ struct wsm_ether_type_filter pae; ++ struct wsm_ether_type_filter wapi; ++} __packed; ++ ++static struct xradio_udp_port_filter xradio_udp_port_filter_on = { ++ .hdr.nrFilters = 2, ++ .dhcp = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_OUT, ++ .portType = WSM_FILTER_PORT_TYPE_DST, ++ .udpPort = __cpu_to_le16(67), ++ }, ++ .upnp = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_OUT, ++ .portType = WSM_FILTER_PORT_TYPE_DST, ++ .udpPort = __cpu_to_le16(1900), ++ }, ++ /* Please add other known ports to be filtered out here and ++ * update nrFilters field in the header. ++ * Up to 4 filters are allowed. */ ++}; ++ ++static struct wsm_udp_port_filter_hdr xradio_udp_port_filter_off = { ++ .nrFilters = 0, ++}; ++ ++#ifndef ETH_P_WAPI ++#define ETH_P_WAPI 0x88B4 ++#endif ++ ++#ifdef TES_P2P_000B_DISABLE_EAPOL_FILTER ++/* TES_P2P_000B WorkAround: ++ * when the link keep 10min more or less(i am not sure), ++ * wpa_s session maybe expired, and want to update group key. ++ * it will use eapol frame(802.1x,0x888E). ++ * if driver suspend, and discard eapol frame, then session end. ++ * i don't know why original code discards eapol frame in suspend. ++ * but now make this filter disable as WorkAround. wzw */ ++static struct xradio_ether_type_filter xradio_ether_type_filter_on = { ++ .hdr.nrFilters = 1, ++/* .ip = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_IN, ++ .etherType = __cpu_to_le16(ETH_P_IP), ++ },*/ ++/* .pae = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_IN, ++ .etherType = __cpu_to_le16(ETH_P_PAE), ++ },*/ ++ .wapi = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_IN, ++ .etherType = __cpu_to_le16(ETH_P_WAPI), ++ }, ++ /* Please add other known ether types to be filtered out here and ++ * update nrFilters field in the header. ++ * Up to 4 filters are allowed. */ ++}; ++#else ++static struct xradio_ether_type_filter xradio_ether_type_filter_on = { ++ .hdr.nrFilters = 2, ++/* .ip = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_IN, ++ .etherType = __cpu_to_le16(ETH_P_IP), ++ },*/ ++ .pae = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_IN, ++ .etherType = __cpu_to_le16(ETH_P_PAE), ++ }, ++ .wapi = { ++ .filterAction = WSM_FILTER_ACTION_FILTER_IN, ++ .etherType = __cpu_to_le16(ETH_P_WAPI), ++ }, ++ /* Please add other known ether types to be filtered out here and ++ * update nrFilters field in the header. ++ * Up to 4 filters are allowed. */ ++}; ++#endif ++ ++static struct wsm_ether_type_filter_hdr xradio_ether_type_filter_off = { ++ .nrFilters = 0, ++}; ++ ++static int xradio_suspend_late(struct device *dev); ++static void xradio_pm_release(struct device *dev); ++static int xradio_pm_probe(struct platform_device *pdev); ++static int __xradio_wow_suspend(struct xradio_vif *priv, ++ struct cfg80211_wowlan *wowlan); ++static int __xradio_wow_resume(struct xradio_vif *priv); ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++static int xradio_poweroff_suspend(struct xradio_common *hw_priv); ++static int xradio_poweroff_resume(struct xradio_common *hw_priv); ++#endif ++ ++ ++/* private */ ++struct xradio_suspend_state { ++ unsigned long bss_loss_tmo; ++ unsigned long connection_loss_tmo; ++ unsigned long join_tmo; ++ unsigned long direct_probe; ++ unsigned long link_id_gc; ++ bool beacon_skipping; ++}; ++ ++static const struct dev_pm_ops xradio_pm_ops = { ++ .suspend_noirq = xradio_suspend_late, ++}; ++ ++static struct platform_driver xradio_power_driver = { ++ .probe = xradio_pm_probe, ++ .driver = { ++ .name = XRADIO_PM_DEVICE, ++ .pm = &xradio_pm_ops, ++ }, ++}; ++ ++static int xradio_pm_init_common(struct xradio_pm_state *pm, ++ struct xradio_common *hw_priv) ++{ ++ int ret; ++ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ spin_lock_init(&pm->lock); ++ /* Register pm driver. */ ++ ret = platform_driver_register(&xradio_power_driver); ++ if (ret) { ++ pm_printk(XRADIO_DBG_ERROR, "%s:platform_driver_register failed(%d)!\n", ++ __FUNCTION__, ret); ++ return ret; ++ } ++ ++ /* Add pm device. */ ++ pm->pm_dev = platform_device_alloc(XRADIO_PM_DEVICE, 0); ++ if (!pm->pm_dev) { ++ pm_printk(XRADIO_DBG_ERROR, "%s:platform_device_alloc failed!\n", ++ __FUNCTION__); ++ platform_driver_unregister(&xradio_power_driver); ++ return -ENOMEM; ++ } ++ pm->pm_dev->dev.platform_data = hw_priv; ++ ret = platform_device_add(pm->pm_dev); ++ if (ret) { ++ pm_printk(XRADIO_DBG_ERROR, "%s:platform_device_add failed(%d)!\n", ++ __FUNCTION__, ret); ++ platform_driver_unregister(&xradio_power_driver); ++ kfree(pm->pm_dev); ++ pm->pm_dev = NULL; ++ } ++ ++ return ret; ++} ++ ++static void xradio_pm_deinit_common(struct xradio_pm_state *pm) ++{ ++ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ platform_driver_unregister(&xradio_power_driver); ++ if (pm->pm_dev) { ++ pm->pm_dev->dev.platform_data = NULL; ++ platform_device_unregister(pm->pm_dev); /* kfree is already do */ ++ pm->pm_dev = NULL; ++ } ++} ++ ++#ifdef CONFIG_WAKELOCK ++ ++int xradio_pm_init(struct xradio_pm_state *pm, ++ struct xradio_common *hw_priv) ++{ ++ int ret = 0; ++ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ ++ ret = xradio_pm_init_common(pm, hw_priv); ++ if (!ret) ++ wake_lock_init(&pm->wakelock, WAKE_LOCK_SUSPEND, XRADIO_WAKE_LOCK); ++ else ++ pm_printk(XRADIO_DBG_ERROR,"xradio_pm_init_common failed!\n"); ++ return ret; ++} ++ ++void xradio_pm_deinit(struct xradio_pm_state *pm) ++{ ++ pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); ++ if (wake_lock_active(&pm->wakelock)) ++ wake_unlock(&pm->wakelock); ++ wake_lock_destroy(&pm->wakelock); ++ xradio_pm_deinit_common(pm); ++} ++ ++void xradio_pm_stay_awake(struct xradio_pm_state *pm, ++ unsigned long tmo) ++{ ++ long cur_tmo; ++ pm_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); ++ ++ spin_lock_bh(&pm->lock); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) ++ cur_tmo = pm->wakelock.ws.timer.expires - jiffies; ++#else ++ cur_tmo = pm->wakelock.expires - jiffies; ++#endif ++ if (!wake_lock_active(&pm->wakelock) || cur_tmo < (long)tmo) ++ wake_lock_timeout(&pm->wakelock, tmo); ++ spin_unlock_bh(&pm->lock); ++} ++void xradio_pm_lock_awake(struct xradio_pm_state *pm) ++{ ++ ++ spin_lock_bh(&pm->lock); ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) ++ pm->expires_save = pm->wakelock.ws.timer.expires; ++#else ++ pm->expires_save = pm->wakelock.expires; ++#endif ++ wake_lock_timeout(&pm->wakelock, LONG_MAX); ++ spin_unlock_bh(&pm->lock); ++} ++void xradio_pm_unlock_awake(struct xradio_pm_state *pm) ++{ ++ ++ spin_lock_bh(&pm->lock); ++ pm->expires_save -= jiffies; ++ if (pm->expires_save) ++ wake_lock_timeout(&pm->wakelock, pm->expires_save); ++ else ++ wake_lock_timeout(&pm->wakelock, 1); ++ spin_unlock_bh(&pm->lock); ++} ++ ++#else /* CONFIG_WAKELOCK */ ++ ++static void xradio_pm_stay_awake_tmo(unsigned long arg) ++{ ++} ++ ++int xradio_pm_init(struct xradio_pm_state *pm, ++ struct xradio_common *hw_priv) ++{ ++ int ret = 0; ++ pm_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); ++ ++ ret = xradio_pm_init_common(pm, hw_priv); ++ if (!ret) { ++ init_timer(&pm->stay_awake); ++ pm->stay_awake.data = (unsigned long)pm; ++ pm->stay_awake.function = xradio_pm_stay_awake_tmo; ++ } else ++ pm_printk(XRADIO_DBG_ERROR,"xradio_pm_init_common failed!\n"); ++ return ret; ++} ++ ++void xradio_pm_deinit(struct xradio_pm_state *pm) ++{ ++ del_timer_sync(&pm->stay_awake); ++ xradio_pm_deinit_common(pm); ++} ++ ++void xradio_pm_stay_awake(struct xradio_pm_state *pm, ++ unsigned long tmo) ++{ ++ long cur_tmo; ++ ++ spin_lock_bh(&pm->lock); ++ cur_tmo = pm->stay_awake.expires - jiffies; ++ if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) ++ mod_timer(&pm->stay_awake, jiffies + tmo); ++ spin_unlock_bh(&pm->lock); ++} ++void xradio_pm_lock_awake(struct xradio_pm_state *pm) ++{ ++ ++ spin_lock_bh(&pm->lock); ++ pm->expires_save = pm->stay_awake.expires; ++ mod_timer(&pm->stay_awake, jiffies + LONG_MAX); ++ spin_unlock_bh(&pm->lock); ++} ++void xradio_pm_unlock_awake(struct xradio_pm_state *pm) ++{ ++ ++ spin_lock_bh(&pm->lock); ++ if (time_before(jiffies, pm->expires_save)) ++ mod_timer(&pm->stay_awake, pm->expires_save); ++ else ++ mod_timer(&pm->stay_awake, jiffies + 1); ++ spin_unlock_bh(&pm->lock); ++} ++#endif /* CONFIG_WAKELOCK */ ++ ++static long xradio_suspend_work(struct delayed_work *work) ++{ ++ int ret = cancel_delayed_work(work); ++ long tmo; ++ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); ++ ++ if (ret > 0) { ++ /* Timer is pending */ ++ tmo = work->timer.expires - jiffies; ++ if (tmo < 0) ++ tmo = 0; ++ } else { ++ tmo = -1; ++ } ++ return tmo; ++} ++ ++static int xradio_resume_work(struct xradio_common *hw_priv, ++ struct delayed_work *work, ++ unsigned long tmo) ++{ ++ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); ++ if ((long)tmo < 0) ++ return 1; ++ ++ return queue_delayed_work(hw_priv->workqueue, work, tmo); ++} ++ ++static int xradio_suspend_late(struct device *dev) ++{ ++ struct xradio_common *hw_priv = dev->platform_data; ++ ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++ if (XRADIO_POWEROFF_SUSP == atomic_read(&hw_priv->suspend_state)) { ++ return 0; /* we don't rx data when power down wifi.*/ ++ } ++#endif ++ ++ //if (atomic_read(&hw_priv->bh_rx)) { ++ // pm_printk(XRADIO_DBG_WARN, "%s: Suspend interrupted.\n", __func__); ++ // return -EAGAIN; ++ //} ++ return 0; ++} ++ ++static void xradio_pm_release(struct device *dev) ++{ ++ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); ++} ++ ++static int xradio_pm_probe(struct platform_device *pdev) ++{ ++ pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); ++ pdev->dev.release = xradio_pm_release; ++ return 0; ++} ++ ++int xradio_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv; ++ int i, ret = 0; ++ ++ ++ if(hw_priv->bh_error) return -EBUSY; ++ WARN_ON(!atomic_read(&hw_priv->num_vifs)); ++ ++ if (work_pending(&hw_priv->query_work)) ++ return -EBUSY; ++ ++#ifdef ROAM_OFFLOAD ++ xradio_for_each_vif(hw_priv, priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !priv) ++#else ++ if (!priv) ++#endif ++ continue; ++ if((priv->vif->type == NL80211_IFTYPE_STATION) ++ && (priv->join_status == XRADIO_JOIN_STATUS_STA)) { ++ down(&hw_priv->scan.lock); ++ hw_priv->scan.if_id = priv->if_id; ++ xradio_sched_scan_work(&hw_priv->scan.swork); ++ } ++ } ++#endif /*ROAM_OFFLOAD*/ ++ ++ /* Do not suspend when datapath is not idle */ ++ if (hw_priv->tx_queue_stats.num_queued[0] + ++ hw_priv->tx_queue_stats.num_queued[1]) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of tx_queue is not empty.\n"); ++ return -EBUSY; ++ } ++ ++ /* Make sure there is no configuration requests in progress. */ ++ if (!mutex_trylock(&hw_priv->conf_mutex)) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of configuration requests.\n"); ++ return -EBUSY; ++ } ++ ++ /* Make sure there is no wsm_oper_lock in progress. */ ++ if (!mutex_trylock(&hw_priv->wsm_oper_lock)) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of wsm_oper_lock.\n"); ++ mutex_unlock(&hw_priv->conf_mutex); ++ return -EBUSY; ++ } ++ ++ /* Do not suspend when scanning or ROC*/ ++ if (down_trylock(&hw_priv->scan.lock)) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of scan requests.\n"); ++ goto revert1; ++ } ++ ++ if (delayed_work_pending(&hw_priv->scan.probe_work)) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of probe frames tx in progress.\n"); ++ goto revert2; ++ } ++ ++ /* Lock TX. */ ++ wsm_lock_tx_async(hw_priv); ++ ++ /* Wait to avoid possible race with bh code. ++ * But do not wait too long... */ ++ if (wait_event_timeout(hw_priv->bh_evt_wq, ++ !hw_priv->hw_bufs_used, HZ / 10) <= 0) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of there are frames not confirm.\n"); ++ goto revert3; ++ } ++ ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++// if (STANDBY_WITH_POWER_OFF == standby_level) { ++ if (1) { ++ return xradio_poweroff_suspend(hw_priv); ++ } ++#endif ++ ++ xradio_for_each_vif(hw_priv, priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !priv) ++#else ++ if (!priv) ++#endif ++ continue; ++ ++ ret = __xradio_wow_suspend(priv, wowlan); ++ if (ret) { ++ for (; i >= 0; i--) { ++ if (!hw_priv->vif_list[i]) ++ continue; ++ priv = (struct xradio_vif *)hw_priv->vif_list[i]->drv_priv; ++ __xradio_wow_resume(priv); ++ } ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of __xradio_wow_suspend failed!\n"); ++ goto revert3; ++ } ++ } ++ ++ /* Stop serving thread */ ++ if (xradio_bh_suspend(hw_priv)) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ "because of xradio_bh_suspend failed!\n"); ++ xradio_wow_resume(hw); ++ return -EBUSY; ++ } ++ ++ /* Enable IRQ wake */ ++ ret = sdio_pm(hw_priv, true); ++ if (ret) { ++ pm_printk(XRADIO_DBG_WARN, "Don't suspend sbus pm failed\n"); ++ xradio_wow_resume(hw); ++ return -EBUSY; ++ } ++ ++ /* Force resume if event is coming from the device. */ ++ //if (atomic_read(&hw_priv->bh_rx)) { ++ // pm_printk(XRADIO_DBG_WARN, "Don't suspend " ++ // "because of recieved rx event!\n"); ++ // xradio_wow_resume(hw); ++ // return -EAGAIN; ++ //} ++ return 0; ++ ++revert3: ++ wsm_unlock_tx(hw_priv); ++revert2: ++ up(&hw_priv->scan.lock); ++revert1: ++ mutex_unlock(&hw_priv->conf_mutex); ++ mutex_unlock(&hw_priv->wsm_oper_lock); ++ return -EBUSY; ++} ++ ++static int __xradio_wow_suspend(struct xradio_vif *priv, ++ struct cfg80211_wowlan *wowlan) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct xradio_pm_state_vif *pm_state_vif = &priv->pm_state_vif; ++ struct xradio_suspend_state *state; ++ int ret; ++#ifdef MCAST_FWDING ++ struct wsm_forwarding_offload fwdoffload = { ++ .fwenable = 0x1, ++ .flags = 0x1, ++ }; ++#endif ++ ++ ++ /* Do not suspend when join work is scheduled */ ++ if (work_pending(&priv->join_work)) { ++ pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend " ++ "when join work is scheduled\n", __func__); ++ goto revert1; ++ } ++ ++ /* Set UDP filter */ ++ wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_on.hdr, ++ priv->if_id); ++ ++ /* Set ethernet frame type filter */ ++ wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_on.hdr, ++ priv->if_id); ++ ++ /* Set IP multicast filter */ ++ wsm_set_host_sleep(hw_priv, 1, priv->if_id); ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ WARN_ON(wsm_set_keepalive_filter(priv, true)); ++ ++#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE ++ /* Set Multicast Address Filter */ ++ if (priv->multicast_filter.numOfAddresses) { ++ priv->multicast_filter.enable = 1; ++ wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); ++ } ++ ++ /* Set Enable Broadcast Address Filter */ ++ priv->broadcast_filter.action_mode = 1; ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ priv->broadcast_filter.address_mode = 3; ++ ++ xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); ++#endif ++ ++#ifdef MCAST_FWDING ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); ++#endif ++ ++ /* Allocate state */ ++ state = kzalloc(sizeof(struct xradio_suspend_state), GFP_KERNEL); ++ if (!state) { ++ pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend " ++ "alloc xradio_suspend_state failed.\n", __func__); ++ goto revert2; ++ } ++ /* Store delayed work states. */ ++ state->bss_loss_tmo = xradio_suspend_work(&priv->bss_loss_work); ++ state->connection_loss_tmo = xradio_suspend_work(&priv->connection_loss_work); ++ state->join_tmo = xradio_suspend_work(&priv->join_timeout); ++ state->link_id_gc = xradio_suspend_work(&priv->link_id_gc_work); ++ ++ /* Enable beacon skipping */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA && ++ priv->join_dtim_period && !priv->has_multicast_subscription) { ++ state->beacon_skipping = true; ++ wsm_set_beacon_wakeup_period(hw_priv, priv->join_dtim_period, ++ XRADIO_BEACON_SKIPPING_MULTIPLIER * \ ++ priv->join_dtim_period, priv->if_id); ++ } ++ ++ ret = timer_pending(&priv->mcast_timeout); ++ if (ret) { ++ pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend " ++ "mcast timeout timer_pending failed.\n", __func__); ++ goto revert3; ++ } ++ ++ /* Store suspend state */ ++ pm_state_vif->suspend_state = state; ++ ++ return 0; ++ ++revert3: ++ xradio_resume_work(hw_priv, &priv->bss_loss_work, state->bss_loss_tmo); ++ xradio_resume_work(hw_priv, &priv->connection_loss_work, ++ state->connection_loss_tmo); ++ xradio_resume_work(hw_priv, &priv->join_timeout, state->join_tmo); ++ xradio_resume_work(hw_priv, &priv->link_id_gc_work, state->link_id_gc); ++ kfree(state); ++ ++revert2: ++ wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_off, priv->if_id); ++ wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_off, priv->if_id); ++ wsm_set_host_sleep(hw_priv, 0, priv->if_id); ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ WARN_ON(wsm_set_keepalive_filter(priv, false)); ++ ++#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE ++ /* Set Multicast Address Filter */ ++ if (priv->multicast_filter.numOfAddresses) { ++ priv->multicast_filter.enable = 0; ++ wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); ++ } ++ ++ /* Set Enable Broadcast Address Filter */ ++ priv->broadcast_filter.action_mode = 0; ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ priv->broadcast_filter.address_mode = 0; ++ xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); ++#endif ++ ++#ifdef MCAST_FWDING ++ fwdoffload.flags = 0x0; ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); ++#endif ++ ++revert1: ++ /* mutex_unlock(&hw_priv->conf_mutex); */ ++ return -EBUSY; ++} ++ ++int xradio_wow_resume(struct ieee80211_hw *hw) ++{ ++ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv; ++ int i, ret = 0; ++ ++ ++ WARN_ON(!atomic_read(&hw_priv->num_vifs)); ++ if(hw_priv->bh_error) return 0; ++ ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++ if (XRADIO_POWEROFF_SUSP == atomic_read(&hw_priv->suspend_state)) { ++ return xradio_poweroff_resume(hw_priv); ++ } ++#endif ++ ++ /* Disable IRQ wake */ ++ sdio_pm(hw_priv, false); ++ ++ up(&hw_priv->scan.lock); ++ ++ /* Resume BH thread */ ++ WARN_ON(xradio_bh_resume(hw_priv)); ++ ++ xradio_for_each_vif(hw_priv, priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !priv) ++#else ++ if (!priv) ++#endif ++ continue; ++ ret = __xradio_wow_resume(priv); ++ if (ret) { ++ pm_printk(XRADIO_DBG_ERROR, "%s:__xradio_wow_resume failed!\n", __func__); ++ break; ++ } ++ } ++ ++ wsm_unlock_tx(hw_priv); ++ ++ /* Unlock configuration mutex */ ++ mutex_unlock(&hw_priv->conf_mutex); ++ mutex_unlock(&hw_priv->wsm_oper_lock); ++ ++ return ret; ++} ++ ++static int __xradio_wow_resume(struct xradio_vif *priv) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct xradio_pm_state_vif *pm_state_vif = &priv->pm_state_vif; ++ struct xradio_suspend_state *state; ++#ifdef MCAST_FWDING ++ struct wsm_forwarding_offload fwdoffload = { ++ .fwenable = 0x1, ++ .flags = 0x0, ++ }; ++#endif ++ ++ ++ /* Restore suspend state */ ++ state = pm_state_vif->suspend_state; ++ pm_state_vif->suspend_state = NULL; ++ ++#ifdef ROAM_OFFLOAD ++ if((priv->vif->type == NL80211_IFTYPE_STATION) ++ && (priv->join_status == XRADIO_JOIN_STATUS_STA)) ++ xradio_hw_sched_scan_stop(hw_priv); ++#endif /*ROAM_OFFLOAD*/ ++ ++ if (state->beacon_skipping) { ++#ifdef XRADIO_USE_LONG_DTIM_PERIOD ++ int join_dtim_period_extend; ++ if (priv->join_dtim_period <= 3) { ++ join_dtim_period_extend = priv->join_dtim_period * 3; ++ } else if (priv->join_dtim_period <= 5) { ++ join_dtim_period_extend = priv->join_dtim_period * 2; ++ } else { ++ join_dtim_period_extend = priv->join_dtim_period; ++ } ++ wsm_set_beacon_wakeup_period(hw_priv, ++ ((priv->beacon_int * join_dtim_period_extend) > MAX_BEACON_SKIP_TIME_MS ? ++ 1 : join_dtim_period_extend) , 0, priv->if_id); ++#else ++ wsm_set_beacon_wakeup_period(hw_priv, priv->beacon_int * ++ (priv->join_dtim_period > MAX_BEACON_SKIP_TIME_MS ? 1 : priv->join_dtim_period), ++ 0, priv->if_id); ++#endif ++ state->beacon_skipping = false; ++ } ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ WARN_ON(wsm_set_keepalive_filter(priv, false)); ++ ++#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE ++ /* Set Multicast Address Filter */ ++ if (priv->multicast_filter.numOfAddresses) { ++ priv->multicast_filter.enable = 0; ++ wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); ++ } ++ /* Set Enable Broadcast Address Filter */ ++ priv->broadcast_filter.action_mode = 0; ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ priv->broadcast_filter.address_mode = 0; ++ xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); ++#endif ++ ++#ifdef MCAST_FWDING ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) ++ WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); ++#endif ++ ++ /* Resume delayed work */ ++ xradio_resume_work(hw_priv, &priv->bss_loss_work, state->bss_loss_tmo); ++ xradio_resume_work(hw_priv, &priv->connection_loss_work, ++ state->connection_loss_tmo); ++ xradio_resume_work(hw_priv, &priv->join_timeout, state->join_tmo); ++ xradio_resume_work(hw_priv, &priv->link_id_gc_work, state->link_id_gc); ++ ++ /* Remove UDP port filter */ ++ wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_off, priv->if_id); ++ ++ /* Remove ethernet frame type filter */ ++ wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_off, priv->if_id); ++ ++ /* Remove IP multicast filter */ ++ wsm_set_host_sleep(hw_priv, 0, priv->if_id); ++ /* Free memory */ ++ kfree(state); ++ ++ return 0; ++} ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++static int xradio_poweroff_suspend(struct xradio_common *hw_priv) ++{ ++ ++ //flush all work. ++ cancel_work_sync(&hw_priv->query_work); ++ flush_workqueue(hw_priv->workqueue); ++ /* Schedule hardware restart, ensure no cmds in progress.*/ ++ mutex_lock(&hw_priv->wsm_cmd_mux); ++ atomic_set(&hw_priv->suspend_state, XRADIO_POWEROFF_SUSP); ++ //hw_priv->hw_restart = true; ++ mutex_unlock(&hw_priv->wsm_cmd_mux); ++ /* Stop serving thread */ ++ if (xradio_bh_suspend(hw_priv)) { ++ pm_printk(XRADIO_DBG_WARN, "%s, xradio_bh_suspend failed!\n", __func__); ++ return -EBUSY; ++ } ++ ++ return 0; ++} ++ ++static int xradio_poweroff_resume(struct xradio_common *hw_priv) ++{ ++ ++ /* Revert locks */ ++ wsm_unlock_tx(hw_priv); ++ up(&hw_priv->scan.lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++ mutex_unlock(&hw_priv->wsm_oper_lock); ++ //if (schedule_work(&hw_priv->hw_restart_work) <= 0) ++ // pm_printk(XRADIO_DBG_ERROR, "%s restart_work failed!\n", __func__); ++ return 0; ++} ++#endif +diff --git a/drivers/net/wireless/xradio/pm.h b/drivers/net/wireless/xradio/pm.h +new file mode 100644 +index 00000000..6443cbc4 +--- /dev/null ++++ b/drivers/net/wireless/xradio/pm.h +@@ -0,0 +1,64 @@ ++/* ++ * power management interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++ ++#ifndef PM_H_INCLUDED ++#define PM_H_INCLUDED ++ ++#ifdef CONFIG_WAKELOCK ++#include ++#endif ++ ++/* ******************************************************************** */ ++/* mac80211 API */ ++ ++#ifdef CONFIG_PM ++ ++#define XRADIO_PM_DEVICE "xradio_pm" ++#define XRADIO_WAKE_LOCK "xradio_wlan" ++ ++/* extern */ struct xradio_common; ++ /* private */ struct xradio_suspend_state; ++ ++struct xradio_pm_state { ++#ifdef CONFIG_WAKELOCK ++ struct wake_lock wakelock; ++#else ++ struct timer_list stay_awake; ++#endif ++ struct platform_device *pm_dev; ++ spinlock_t lock; ++ unsigned long expires_save; ++}; ++ ++struct xradio_pm_state_vif { ++ struct xradio_suspend_state *suspend_state; ++}; ++ ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++enum suspend_state { ++ XRADIO_RESUME = 0, ++ XRADIO_CONNECT_SUSP, ++ XRADIO_DISCONNECT_SUSP, ++ XRADIO_POWEROFF_SUSP ++}; ++#endif ++int xradio_pm_init(struct xradio_pm_state *pm, struct xradio_common *priv); ++void xradio_pm_deinit(struct xradio_pm_state *pm); ++void xradio_pm_stay_awake(struct xradio_pm_state *pm, unsigned long tmo); ++void xradio_pm_lock_awake(struct xradio_pm_state *pm); ++void xradio_pm_unlock_awake(struct xradio_pm_state *pm); ++int xradio_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); ++int xradio_wow_resume(struct ieee80211_hw *hw); ++ ++#endif /* CONFIG_PM */ ++ ++#endif +diff --git a/drivers/net/wireless/xradio/queue.c b/drivers/net/wireless/xradio/queue.c +new file mode 100644 +index 00000000..1d8c824c +--- /dev/null ++++ b/drivers/net/wireless/xradio/queue.c +@@ -0,0 +1,898 @@ ++/* ++ * Queue implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include "xradio.h" ++#include "queue.h" ++#ifdef CONFIG_XRADIO_TESTMODE ++#include ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++/* private */ struct xradio_queue_item ++{ ++ struct list_head head; ++ struct sk_buff *skb; ++ u32 packetID; ++ unsigned long queue_timestamp; ++ unsigned long xmit_timestamp; ++#ifdef CONFIG_XRADIO_TESTMODE ++ unsigned long mdelay_timestamp; ++ unsigned long qdelay_timestamp; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ struct xradio_txpriv txpriv; ++ u8 generation; ++ u8 pack_stk_wr; ++}; ++ ++static inline void __xradio_queue_lock(struct xradio_queue *queue) ++{ ++ struct xradio_queue_stats *stats = queue->stats; ++ if (queue->tx_locked_cnt++ == 0) { ++ txrx_printk(XRADIO_DBG_MSG, "[TX] Queue %d is locked.\n", ++ queue->queue_id); ++ ieee80211_stop_queue(stats->hw_priv->hw, queue->queue_id); ++ } ++} ++ ++static inline void __xradio_queue_unlock(struct xradio_queue *queue) ++{ ++ struct xradio_queue_stats *stats = queue->stats; ++ SYS_BUG(!queue->tx_locked_cnt); ++ if (--queue->tx_locked_cnt == 0) { ++ txrx_printk(XRADIO_DBG_MSG, "[TX] Queue %d is unlocked.\n", ++ queue->queue_id); ++ ieee80211_wake_queue(stats->hw_priv->hw, queue->queue_id); ++ } ++} ++ ++static inline void xradio_queue_parse_id(u32 packetID, u8 *queue_generation, ++ u8 *queue_id, ++ u8 *item_generation, ++ u8 *item_id, ++ u8 *if_id, ++ u8 *link_id) ++{ ++ *item_id = (packetID >> 0) & 0xFF; ++ *item_generation = (packetID >> 8) & 0xFF; ++ *queue_id = (packetID >> 16) & 0xF; ++ *if_id = (packetID >> 20) & 0xF; ++ *link_id = (packetID >> 24) & 0xF; ++ *queue_generation = (packetID >> 28) & 0xF; ++} ++ ++static inline u32 xradio_queue_make_packet_id(u8 queue_generation, u8 queue_id, ++ u8 item_generation, u8 item_id, ++ u8 if_id, u8 link_id) ++{ ++ /*TODO:COMBO: Add interfaceID to the packetID */ ++ return ((u32)item_id << 0) | ++ ((u32)item_generation << 8) | ++ ((u32)queue_id << 16) | ++ ((u32)if_id << 20) | ++ ((u32)link_id << 24) | ++ ((u32)queue_generation << 28); ++} ++ ++static void xradio_queue_post_gc(struct xradio_queue_stats *stats, ++ struct list_head *gc_list) ++{ ++ struct xradio_queue_item *item; ++ ++ while (!list_empty(gc_list)) { ++ item = list_first_entry( ++ gc_list, struct xradio_queue_item, head); ++ list_del(&item->head); ++ stats->skb_dtor(stats->hw_priv, item->skb, &item->txpriv); ++ kfree(item); ++ } ++} ++ ++static void xradio_queue_register_post_gc(struct list_head *gc_list, ++ struct xradio_queue_item *item) ++{ ++ struct xradio_queue_item *gc_item; ++ gc_item = kmalloc(sizeof(struct xradio_queue_item), GFP_KERNEL); ++ SYS_BUG(!gc_item); ++ memcpy(gc_item, item, sizeof(struct xradio_queue_item)); ++ list_add_tail(&gc_item->head, gc_list); ++} ++ ++static void __xradio_queue_gc(struct xradio_queue *queue, ++ struct list_head *head, ++ bool unlock) ++{ ++ struct xradio_queue_stats *stats = queue->stats; ++ struct xradio_queue_item *item = NULL; ++ //struct xradio_vif *priv; ++ int if_id; ++ bool wakeup_stats = false; ++ ++ while (!list_empty(&queue->queue)) { ++ struct xradio_txpriv *txpriv; ++ item = list_first_entry( ++ &queue->queue, struct xradio_queue_item, head); ++ if (time_before(jiffies, item->queue_timestamp+queue->ttl)) ++ break; ++ ++ txpriv = &item->txpriv; ++ if_id = txpriv->if_id; ++ --queue->num_queued; ++ --queue->num_queued_vif[if_id]; ++ --queue->link_map_cache[if_id][txpriv->link_id]; ++ spin_lock_bh(&stats->lock); ++ --stats->num_queued[if_id]; ++ if (!--stats->link_map_cache[if_id][txpriv->link_id]) ++ wakeup_stats = true; ++ spin_unlock_bh(&stats->lock); ++ //priv = xrwl_hwpriv_to_vifpriv(stats->hw_priv, if_id); ++ //if (priv) { ++ // xradio_debug_tx_ttl(priv); ++ // spin_unlock(&priv->vif_lock); ++ //} ++ xradio_queue_register_post_gc(head, item); ++ item->skb = NULL; ++ list_move_tail(&item->head, &queue->free_pool); ++ } ++ ++ if (wakeup_stats) ++ wake_up(&stats->wait_link_id_empty); ++ ++ //modified by yangfh for test WFD ++ if (queue->overfull) { ++ if (queue->num_queued <= ((stats->hw_priv->vif0_throttle + ++ stats->hw_priv->vif1_throttle+2)>>1)) { ++ queue->overfull = false; ++ if (unlock) { ++ __xradio_queue_unlock(queue); ++ } ++ } else if (item) { ++ unsigned long tmo = item->queue_timestamp + queue->ttl; ++ mod_timer(&queue->gc, tmo); ++ xradio_pm_stay_awake(&stats->hw_priv->pm_state, ++ tmo - jiffies); ++ } ++ } ++} ++ ++static void xradio_queue_gc(unsigned long arg) ++{ ++ LIST_HEAD(list); ++ struct xradio_queue *queue = ++ (struct xradio_queue *)arg; ++ ++ spin_lock_bh(&queue->lock); ++ __xradio_queue_gc(queue, &list, true); ++ spin_unlock_bh(&queue->lock); ++ xradio_queue_post_gc(queue->stats, &list); ++} ++ ++int xradio_queue_stats_init(struct xradio_queue_stats *stats, ++ size_t map_capacity, ++ xradio_queue_skb_dtor_t skb_dtor, ++ struct xradio_common *hw_priv) ++{ ++ int i; ++ ++ memset(stats, 0, sizeof(*stats)); ++ stats->map_capacity = map_capacity; ++ stats->skb_dtor = skb_dtor; ++ stats->hw_priv = hw_priv; ++ spin_lock_init(&stats->lock); ++ init_waitqueue_head(&stats->wait_link_id_empty); ++ for (i = 0; i < XRWL_MAX_VIFS; i++) { ++ stats->link_map_cache[i] = kzalloc(sizeof(int[map_capacity]), GFP_KERNEL); ++ if (!stats->link_map_cache[i]) { ++ for (; i >= 0; i--) ++ kfree(stats->link_map_cache[i]); ++ return -ENOMEM; ++ } ++ } ++ ++ return 0; ++} ++ ++int xradio_queue_init(struct xradio_queue *queue, ++ struct xradio_queue_stats *stats, ++ u8 queue_id, ++ size_t capacity, ++ unsigned long ttl) ++{ ++ int i; ++ ++ memset(queue, 0, sizeof(*queue)); ++ queue->stats = stats; ++ queue->capacity = capacity; ++ queue->queue_id = queue_id; ++ queue->ttl = ttl; ++ INIT_LIST_HEAD(&queue->queue); ++ INIT_LIST_HEAD(&queue->pending); ++ INIT_LIST_HEAD(&queue->free_pool); ++ spin_lock_init(&queue->lock); ++ init_timer(&queue->gc); ++ queue->gc.data = (unsigned long)queue; ++ queue->gc.function = xradio_queue_gc; ++ ++ queue->pool = kzalloc(sizeof(struct xradio_queue_item) * capacity, ++ GFP_KERNEL); ++ if (!queue->pool) ++ return -ENOMEM; ++ ++ for (i = 0; i < XRWL_MAX_VIFS; i++) { ++ queue->link_map_cache[i] = ++ kzalloc(sizeof(int[stats->map_capacity]), GFP_KERNEL); ++ if (!queue->link_map_cache[i]) { ++ for (; i >= 0; i--) ++ kfree(queue->link_map_cache[i]); ++ kfree(queue->pool); ++ queue->pool = NULL; ++ return -ENOMEM; ++ } ++ } ++ ++ for (i = 0; i < capacity; ++i) ++ list_add_tail(&queue->pool[i].head, &queue->free_pool); ++ ++ return 0; ++} ++ ++/* TODO:COMBO: Flush only a particular interface specific parts */ ++int xradio_queue_clear(struct xradio_queue *queue, int if_id) ++{ ++ int i, cnt, iter; ++ struct xradio_queue_stats *stats = queue->stats; ++ LIST_HEAD(gc_list); ++ ++ cnt = 0; ++ spin_lock_bh(&queue->lock); ++ queue->generation++; ++ queue->generation &= 0xf; ++ list_splice_tail_init(&queue->queue, &queue->pending); ++ while (!list_empty(&queue->pending)) { ++ struct xradio_queue_item *item = list_first_entry( ++ &queue->pending, struct xradio_queue_item, head); ++ SYS_WARN(!item->skb); ++ if (XRWL_ALL_IFS == if_id || item->txpriv.if_id == if_id) { ++ xradio_queue_register_post_gc(&gc_list, item); ++ item->skb = NULL; ++ list_move_tail(&item->head, &queue->free_pool); ++ cnt++; ++ } ++ } ++ queue->num_queued -= cnt; ++ queue->num_pending = 0; ++ if (XRWL_ALL_IFS != if_id) { ++ queue->num_queued_vif[if_id] = 0; ++ queue->num_pending_vif[if_id] = 0; ++ } else { ++ for (iter = 0; iter < XRWL_MAX_VIFS; iter++) { ++ queue->num_queued_vif[iter] = 0; ++ queue->num_pending_vif[iter] = 0; ++ } ++ } ++ spin_lock_bh(&stats->lock); ++ if (XRWL_ALL_IFS != if_id) { ++ for (i = 0; i < stats->map_capacity; ++i) { ++ stats->num_queued[if_id] -= ++ queue->link_map_cache[if_id][i]; ++ stats->link_map_cache[if_id][i] -= ++ queue->link_map_cache[if_id][i]; ++ queue->link_map_cache[if_id][i] = 0; ++ } ++ } else { ++ for (iter = 0; iter < XRWL_MAX_VIFS; iter++) { ++ for (i = 0; i < stats->map_capacity; ++i) { ++ stats->num_queued[iter] -= ++ queue->link_map_cache[iter][i]; ++ stats->link_map_cache[iter][i] -= ++ queue->link_map_cache[iter][i]; ++ queue->link_map_cache[iter][i] = 0; ++ } ++ } ++ } ++ spin_unlock_bh(&stats->lock); ++ if (unlikely(queue->overfull)) { ++ queue->overfull = false; ++ __xradio_queue_unlock(queue); ++ } ++ spin_unlock_bh(&queue->lock); ++ wake_up(&stats->wait_link_id_empty); ++ xradio_queue_post_gc(stats, &gc_list); ++ return 0; ++} ++ ++void xradio_queue_stats_deinit(struct xradio_queue_stats *stats) ++{ ++ int i; ++ ++ for (i = 0; i < XRWL_MAX_VIFS ; i++) { ++ kfree(stats->link_map_cache[i]); ++ stats->link_map_cache[i] = NULL; ++ } ++} ++ ++void xradio_queue_deinit(struct xradio_queue *queue) ++{ ++ int i; ++ ++ xradio_queue_clear(queue, XRWL_ALL_IFS); ++ del_timer_sync(&queue->gc); ++ INIT_LIST_HEAD(&queue->free_pool); ++ kfree(queue->pool); ++ for (i = 0; i < XRWL_MAX_VIFS; i++) { ++ kfree(queue->link_map_cache[i]); ++ queue->link_map_cache[i] = NULL; ++ } ++ queue->pool = NULL; ++ queue->capacity = 0; ++} ++ ++size_t xradio_queue_get_num_queued(struct xradio_vif *priv, ++ struct xradio_queue *queue, ++ u32 link_id_map) ++{ ++ size_t ret; ++ int i, bit; ++ size_t map_capacity = queue->stats->map_capacity; ++ ++ if (!link_id_map) ++ return 0; ++ ++ spin_lock_bh(&queue->lock); ++ if (likely(link_id_map == (u32) -1)) { ++ ret = queue->num_queued_vif[priv->if_id] - ++ queue->num_pending_vif[priv->if_id]; ++ } else { ++ ret = 0; ++ for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { ++ if (link_id_map & bit) ++ ret += ++ queue->link_map_cache[priv->if_id][i]; ++ } ++ } ++ spin_unlock_bh(&queue->lock); ++ return ret; ++} ++ ++int xradio_queue_put(struct xradio_queue *queue, struct sk_buff *skb, ++ struct xradio_txpriv *txpriv) ++{ ++ int ret = 0; ++#ifdef CONFIG_XRADIO_TESTMODE ++ struct timeval tmval; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ LIST_HEAD(gc_list); ++ struct xradio_queue_stats *stats = queue->stats; ++ /* TODO:COMBO: Add interface ID info to queue item */ ++ ++ if (txpriv->link_id >= queue->stats->map_capacity) ++ return -EINVAL; ++ ++ spin_lock_bh(&queue->lock); ++ if (!SYS_WARN(list_empty(&queue->free_pool))) { ++ struct xradio_queue_item *item = list_first_entry( ++ &queue->free_pool, struct xradio_queue_item, head); ++ SYS_BUG(item->skb); ++ ++ list_move_tail(&item->head, &queue->queue); ++ item->skb = skb; ++ item->txpriv = *txpriv; ++ item->generation = 1; /* avoid packet ID is 0.*/ ++ item->pack_stk_wr = 0; ++ item->packetID = xradio_queue_make_packet_id( ++ queue->generation, queue->queue_id, ++ item->generation, item - queue->pool, ++ txpriv->if_id, txpriv->raw_link_id); ++ item->queue_timestamp = jiffies; ++#ifdef CONFIG_XRADIO_TESTMODE ++ do_gettimeofday(&tmval); ++ item->qdelay_timestamp = tmval.tv_usec; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++ if (TES_P2P_0002_state == TES_P2P_0002_STATE_SEND_RESP) { ++ TES_P2P_0002_packet_id = item->packetID; ++ TES_P2P_0002_state = TES_P2P_0002_STATE_GET_PKTID; ++ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_GET_PKTID]\n"); ++ } ++#endif ++ ++ ++queue->num_queued; ++ ++queue->num_queued_vif[txpriv->if_id]; ++ ++queue->link_map_cache[txpriv->if_id][txpriv->link_id]; ++ ++ spin_lock_bh(&stats->lock); ++ ++stats->num_queued[txpriv->if_id]; ++ ++stats->link_map_cache[txpriv->if_id][txpriv->link_id]; ++ spin_unlock_bh(&stats->lock); ++ ++ /* ++ * TX may happen in parallel sometimes. ++ * Leave extra queue slots so we don't overflow. ++ */ ++ if (queue->overfull == false && ++ queue->num_queued >= ++ ((stats->hw_priv->vif0_throttle +stats->hw_priv->vif1_throttle) ++ - (num_present_cpus() - 1))) { ++ queue->overfull = true; ++ __xradio_queue_lock(queue); ++ mod_timer(&queue->gc, jiffies); ++ txrx_printk(XRADIO_DBG_NIY,"!lock queue\n"); ++ } ++ } else { ++ ret = -ENOENT; ++ } ++#if 0 ++ txrx_printk(XRADIO_DBG_ERROR, "queue_put queue %d, %d, %d\n", ++ queue->num_queued, ++ queue->link_map_cache[txpriv->if_id][txpriv->link_id], ++ queue->num_pending); ++ txrx_printk(XRADIO_DBG_ERROR, "queue_put stats %d, %d\n", stats->num_queued, ++ stats->link_map_cache[txpriv->if_id][txpriv->link_id]); ++#endif ++ spin_unlock_bh(&queue->lock); ++ return ret; ++} ++ ++int xradio_queue_get(struct xradio_queue *queue, ++ int if_id, ++ u32 link_id_map, ++ struct wsm_tx **tx, ++ struct ieee80211_tx_info **tx_info, ++ struct xradio_txpriv **txpriv) ++{ ++ int ret = -ENOENT; ++ struct xradio_queue_item *item; ++ struct xradio_queue_stats *stats = queue->stats; ++ bool wakeup_stats = false; ++#ifdef CONFIG_XRADIO_TESTMODE ++ struct timeval tmval; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++ spin_lock_bh(&queue->lock); ++ list_for_each_entry(item, &queue->queue, head) { ++ if ((item->txpriv.if_id == if_id) && ++ (link_id_map & BIT(item->txpriv.link_id))) { ++ ret = 0; ++ break; ++ } ++ } ++ ++ if (!SYS_WARN(ret)) { ++ *tx = (struct wsm_tx *)item->skb->data; ++ *tx_info = IEEE80211_SKB_CB(item->skb); ++ *txpriv = &item->txpriv; ++ (*tx)->packetID = __cpu_to_le32(item->packetID); ++ list_move_tail(&item->head, &queue->pending); ++ ++queue->num_pending; ++ ++queue->num_pending_vif[item->txpriv.if_id]; ++ --queue->link_map_cache[item->txpriv.if_id] ++ [item->txpriv.link_id]; ++ item->xmit_timestamp = jiffies; ++#ifdef CONFIG_XRADIO_TESTMODE ++ do_gettimeofday(&tmval); ++ item->mdelay_timestamp = tmval.tv_usec; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++ spin_lock_bh(&stats->lock); ++ --stats->num_queued[item->txpriv.if_id]; ++ if (!--stats->link_map_cache[item->txpriv.if_id] ++ [item->txpriv.link_id]) ++ wakeup_stats = true; ++ ++ spin_unlock_bh(&stats->lock); ++#if 0 ++ txrx_printk(XRADIO_DBG_ERROR, "queue_get queue %d, %d, %d\n", ++ queue->num_queued, ++ queue->link_map_cache[item->txpriv.if_id][item->txpriv.link_id], ++ queue->num_pending); ++ txrx_printk(XRADIO_DBG_ERROR, "queue_get stats %d, %d\n", stats->num_queued, ++ stats->link_map_cache[item->txpriv.if_id] ++ [item->txpriv.link_id]); ++#endif ++ } ++ spin_unlock_bh(&queue->lock); ++ if (wakeup_stats) ++ wake_up(&stats->wait_link_id_empty); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++int xradio_queue_requeue(struct xradio_common *hw_priv, ++ struct xradio_queue *queue, u32 packetID, bool check) ++#else ++int xradio_queue_requeue(struct xradio_queue *queue, u32 packetID, bool check) ++#endif ++{ ++ int ret = 0; ++ u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; ++ struct xradio_queue_item *item; ++ struct xradio_queue_stats *stats = queue->stats; ++ ++ xradio_queue_parse_id(packetID, &queue_generation, &queue_id, ++ &item_generation, &item_id, &if_id, &link_id); ++ ++ item = &queue->pool[item_id]; ++#ifdef P2P_MULTIVIF ++ if (check && item->txpriv.if_id == XRWL_GENERIC_IF_ID) { ++#else ++ if (check && item->txpriv.offchannel_if_id == XRWL_GENERIC_IF_ID) { ++#endif ++ txrx_printk(XRADIO_DBG_MSG, "Requeued frame dropped for " ++ "generic interface id.\n"); ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_remove(hw_priv, queue, packetID); ++#else ++ xradio_queue_remove(queue, packetID); ++#endif ++ return 0; ++ } ++ ++#ifndef P2P_MULTIVIF ++ if (!check) ++ item->txpriv.offchannel_if_id = XRWL_GENERIC_IF_ID; ++#endif ++ ++ /*if_id = item->txpriv.if_id;*/ ++ ++ spin_lock_bh(&queue->lock); ++ SYS_BUG(queue_id != queue->queue_id); ++ if (unlikely(queue_generation != queue->generation)) { ++ ret = -ENOENT; ++ } else if (unlikely(item_id >= (unsigned) queue->capacity)) { ++ SYS_WARN(1); ++ ret = -EINVAL; ++ } else if (unlikely(item->generation != item_generation)) { ++ SYS_WARN(1); ++ ret = -ENOENT; ++ } else { ++ --queue->num_pending; ++ --queue->num_pending_vif[if_id]; ++ ++queue->link_map_cache[if_id][item->txpriv.link_id]; ++ ++ spin_lock_bh(&stats->lock); ++ ++stats->num_queued[item->txpriv.if_id]; ++ ++stats->link_map_cache[if_id][item->txpriv.link_id]; ++ spin_unlock_bh(&stats->lock); ++ ++ item->generation = ++item_generation; ++ item->packetID = xradio_queue_make_packet_id( ++ queue_generation, queue_id, item_generation, item_id, ++ if_id, link_id); ++ list_move(&item->head, &queue->queue); ++#if 0 ++ txrx_printk(XRADIO_DBG_ERROR, "queue_requeue queue %d, %d, %d\n", ++ queue->num_queued, ++ queue->link_map_cache[if_id][item->txpriv.link_id], ++ queue->num_pending); ++ txrx_printk(XRADIO_DBG_ERROR, "queue_requeue stats %d, %d\n", ++ stats->num_queued, ++ stats->link_map_cache[if_id][item->txpriv.link_id]); ++#endif ++ } ++ spin_unlock_bh(&queue->lock); ++ return ret; ++} ++ ++int xradio_queue_requeue_all(struct xradio_queue *queue) ++{ ++ struct xradio_queue_stats *stats = queue->stats; ++ spin_lock_bh(&queue->lock); ++ while (!list_empty(&queue->pending)) { ++ struct xradio_queue_item *item = list_entry( ++ queue->pending.prev, struct xradio_queue_item, head); ++ ++ --queue->num_pending; ++ --queue->num_pending_vif[item->txpriv.if_id]; ++ ++queue->link_map_cache[item->txpriv.if_id] ++ [item->txpriv.link_id]; ++ ++ spin_lock_bh(&stats->lock); ++ ++stats->num_queued[item->txpriv.if_id]; ++ ++stats->link_map_cache[item->txpriv.if_id] ++ [item->txpriv.link_id]; ++ spin_unlock_bh(&stats->lock); ++ ++ ++item->generation; ++ item->packetID = xradio_queue_make_packet_id( ++ queue->generation, queue->queue_id, ++ item->generation, item - queue->pool, ++ item->txpriv.if_id, item->txpriv.raw_link_id); ++ list_move(&item->head, &queue->queue); ++ } ++ spin_unlock_bh(&queue->lock); ++ ++ return 0; ++} ++#ifdef CONFIG_XRADIO_TESTMODE ++int xradio_queue_remove(struct xradio_common *hw_priv, ++ struct xradio_queue *queue, u32 packetID) ++#else ++int xradio_queue_remove(struct xradio_queue *queue, u32 packetID) ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++{ ++ int ret = 0; ++ u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; ++ struct xradio_queue_item *item; ++ struct xradio_queue_stats *stats = queue->stats; ++ struct sk_buff *gc_skb = NULL; ++ struct xradio_txpriv gc_txpriv; ++ ++ xradio_queue_parse_id(packetID, &queue_generation, &queue_id, ++ &item_generation, &item_id, &if_id, &link_id); ++ ++ item = &queue->pool[item_id]; ++ ++ spin_lock_bh(&queue->lock); ++ SYS_BUG(queue_id != queue->queue_id); ++ /*TODO:COMBO:Add check for interface ID also */ ++ if (unlikely(queue_generation != queue->generation)) { ++ ret = -ENOENT; ++ } else if (unlikely(item_id >= (unsigned) queue->capacity)) { ++ SYS_WARN(1); ++ ret = -EINVAL; ++ } else if (unlikely(item->generation != item_generation)) { ++ SYS_WARN(1); ++ ret = -ENOENT; ++ } else { ++ gc_txpriv = item->txpriv; ++ gc_skb = item->skb; ++ item->skb = NULL; ++ --queue->num_pending; ++ --queue->num_pending_vif[if_id]; ++ --queue->num_queued; ++ --queue->num_queued_vif[if_id]; ++ ++queue->num_sent; ++ ++item->generation; ++#ifdef CONFIG_XRADIO_TESTMODE ++ spin_lock_bh(&hw_priv->tsm_lock); ++ if (hw_priv->start_stop_tsm.start) { ++ if (queue_id == hw_priv->tsm_info.ac) { ++ struct timeval tmval; ++ unsigned long queue_delay; ++ unsigned long media_delay; ++ do_gettimeofday(&tmval); ++ ++ if (tmval.tv_usec > item->qdelay_timestamp) ++ queue_delay = tmval.tv_usec - ++ item->qdelay_timestamp; ++ else ++ queue_delay = tmval.tv_usec + ++ 1000000 - item->qdelay_timestamp; ++ ++ if (tmval.tv_usec > item->mdelay_timestamp) ++ media_delay = tmval.tv_usec - ++ item->mdelay_timestamp; ++ else ++ media_delay = tmval.tv_usec + ++ 1000000 - item->mdelay_timestamp; ++ hw_priv->tsm_info.sum_media_delay += ++ media_delay; ++ hw_priv->tsm_info.sum_pkt_q_delay += queue_delay; ++ if (queue_delay <= 10000) ++ hw_priv->tsm_stats.bin0++; ++ else if (queue_delay <= 20000) ++ hw_priv->tsm_stats.bin1++; ++ else if (queue_delay <= 40000) ++ hw_priv->tsm_stats.bin2++; ++ else ++ hw_priv->tsm_stats.bin3++; ++ } ++ } ++ spin_unlock_bh(&hw_priv->tsm_lock); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ /* Do not use list_move_tail here, but list_move: ++ * try to utilize cache row. ++ */ ++ list_move(&item->head, &queue->free_pool); ++ ++ if (unlikely(queue->overfull) && ++ (queue->num_queued <= ((stats->hw_priv->vif0_throttle + stats->hw_priv->vif1_throttle + 2)>>1))) { ++ queue->overfull = false; ++ __xradio_queue_unlock(queue); ++ } ++ } ++ spin_unlock_bh(&queue->lock); ++ ++#if 0 ++ txrx_printk(XRADIO_DBG_ERROR, "queue_drop queue %d, %d, %d\n", ++ queue->num_queued, queue->link_map_cache[if_id][0], ++ queue->num_pending); ++ txrx_printk(XRADIO_DBG_ERROR, "queue_drop stats %d, %d\n", stats->num_queued, ++ stats->link_map_cache[if_id][0]); ++#endif ++ if (gc_skb) ++ stats->skb_dtor(stats->hw_priv, gc_skb, &gc_txpriv); ++ ++ return ret; ++} ++ ++int xradio_queue_get_skb(struct xradio_queue *queue, u32 packetID, ++ struct sk_buff **skb, ++ const struct xradio_txpriv **txpriv) ++{ ++ int ret = 0; ++ u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; ++ struct xradio_queue_item *item; ++ ++ xradio_queue_parse_id(packetID, &queue_generation, &queue_id, ++ &item_generation, &item_id, &if_id, &link_id); ++ ++ item = &queue->pool[item_id]; ++ ++ spin_lock_bh(&queue->lock); ++ SYS_BUG(queue_id != queue->queue_id); ++ /* TODO:COMBO: Add check for interface ID here */ ++ if (unlikely(queue_generation != queue->generation)) { ++ ret = -ENOENT; ++ } else if (unlikely(item_id >= (unsigned) queue->capacity)) { ++ SYS_WARN(1); ++ ret = -EINVAL; ++ } else if (unlikely(item->generation != item_generation)) { ++ SYS_WARN(1); ++ ret = -ENOENT; ++ } else { ++ *skb = item->skb; ++ *txpriv = &item->txpriv; ++ } ++ spin_unlock_bh(&queue->lock); ++ return ret; ++} ++ ++void xradio_queue_lock(struct xradio_queue *queue) ++{ ++ spin_lock_bh(&queue->lock); ++ __xradio_queue_lock(queue); ++ spin_unlock_bh(&queue->lock); ++} ++ ++void xradio_queue_unlock(struct xradio_queue *queue) ++{ ++ spin_lock_bh(&queue->lock); ++ __xradio_queue_unlock(queue); ++ spin_unlock_bh(&queue->lock); ++} ++ ++bool xradio_queue_get_xmit_timestamp(struct xradio_queue *queue, ++ unsigned long *timestamp, int if_id, ++ u32 pending_frameID, u32 *Old_frame_ID) ++{ ++ struct xradio_queue_item *item; ++ bool ret; ++ ++ spin_lock_bh(&queue->lock); ++ ret = !list_empty(&queue->pending); ++ if (ret) { ++ list_for_each_entry(item, &queue->pending, head) { ++ if (((if_id == XRWL_GENERIC_IF_ID) || ++ (if_id == XRWL_ALL_IFS) || ++ (item->txpriv.if_id == if_id)) && ++ (item->packetID != pending_frameID)) { ++ if (time_before(item->xmit_timestamp, ++ *timestamp)) { ++ *timestamp = item->xmit_timestamp; ++ *Old_frame_ID = item->packetID; ++ } ++ } ++ } ++ } ++ spin_unlock_bh(&queue->lock); ++ return ret; ++} ++ ++bool xradio_queue_stats_is_empty(struct xradio_queue_stats *stats, ++ u32 link_id_map, int if_id) ++{ ++ bool empty = true; ++ ++ spin_lock_bh(&stats->lock); ++ if (link_id_map == (u32)-1) ++ empty = stats->num_queued[if_id] == 0; ++ else { ++ int i, if_id; ++ for (if_id = 0; if_id < XRWL_MAX_VIFS; if_id++) { ++ for (i = 0; i < stats->map_capacity; ++i) { ++ if (link_id_map & BIT(i)) { ++ if (stats->link_map_cache[if_id][i]) { ++ empty = false; ++ break; ++ } ++ } ++ } ++ } ++ } ++ spin_unlock_bh(&stats->lock); ++ ++ return empty; ++} ++ ++bool xradio_query_txpkt_timeout(struct xradio_common *hw_priv, int if_id, ++ u32 pending_pkt_id, long *timeout) ++{ ++ int i; ++ bool pending = false; ++ unsigned long timestamp = jiffies; ++ struct xradio_queue *queue = NULL; ++ struct xradio_queue_item *item = NULL; ++ struct xradio_queue *old_queue = NULL; ++ struct xradio_queue_item *old_item = NULL; ++ u8 pack_stk_wr = 0; ++ ++ /* Get oldest frame.*/ ++ for (i = 0; i < AC_QUEUE_NUM; ++i) { ++ queue = &hw_priv->tx_queue[i]; ++ spin_lock_bh(&queue->lock); ++ if (!list_empty(&queue->pending)) { ++ list_for_each_entry(item, &queue->pending, head) { ++ if (((if_id == XRWL_GENERIC_IF_ID) || ++ (if_id == XRWL_ALL_IFS) || ++ (item->txpriv.if_id == if_id)) && ++ (item->packetID != pending_pkt_id)) { ++ if (time_before(item->xmit_timestamp, timestamp)) { ++ timestamp = item->xmit_timestamp; ++ pack_stk_wr = item->pack_stk_wr; ++ old_queue = queue; ++ old_item = item; ++ } ++ } ++ } ++ pending = true; ++ } ++ spin_unlock_bh(&queue->lock); ++ } ++ if (!pending) ++ return false; ++ ++ /* Check if frame transmission is timed out. ++ * add (WSM_CMD_LAST_CHANCE_TIMEOUT>>1) for stuck workaround.*/ ++ *timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; ++ if (unlikely(*timeout < 0) && !pack_stk_wr) { ++ struct ieee80211_hdr *frame = NULL; ++ const struct xradio_txpriv *txpriv = NULL; ++ u16 fctl = 0x0; ++ u32 len = 0x0; ++ u8 if_id = 0, link_id = 0, tid = 0; ++ ++ /* query the timeout frame. */ ++ spin_lock_bh(&old_queue->lock); ++ if (likely(old_item->skb && !hw_priv->query_packetID)) { ++ hw_priv->query_packetID = old_item->packetID; ++ old_item->pack_stk_wr = 1; ++ atomic_add(1, &hw_priv->query_cnt); ++ ++ /* Info of stuck frames for debug.*/ ++ txpriv = &old_item->txpriv; ++ frame = (struct ieee80211_hdr *)(&old_item->skb->data[txpriv->offset]); ++ fctl = frame->frame_control; ++ len = old_item->skb->len; ++ if_id = txpriv->if_id; ++ link_id = txpriv->link_id; ++ tid = txpriv->tid; ++ } ++ spin_unlock_bh(&old_queue->lock); ++ /* Dump Info of stuck frames. */ ++ if (frame) { ++ txrx_printk(XRADIO_DBG_ERROR, "TX confirm timeout(%ds).\n", ++ WSM_CMD_LAST_CHANCE_TIMEOUT/HZ); ++ txrx_printk(XRADIO_DBG_ERROR, "if=%d, linkid=%d, tid=%d, " \ ++ "old_packetID=0x%08x, fctl=0x%04x, len=%d, wr=%d\n", ++ if_id, link_id, tid, hw_priv->query_packetID, fctl, len, ++ pack_stk_wr); ++ } ++ /* Return half of timeout for query packet. */ ++ *timeout = (WSM_CMD_LAST_CHANCE_TIMEOUT>>1); ++ } else if (unlikely(pack_stk_wr)){ ++ *timeout = *timeout + (WSM_CMD_LAST_CHANCE_TIMEOUT>>1); ++ txrx_printk(XRADIO_DBG_MSG,"%s, wr and timeout=%ld\n", __func__, *timeout); ++ } ++ return pending; ++} +diff --git a/drivers/net/wireless/xradio/queue.h b/drivers/net/wireless/xradio/queue.h +new file mode 100644 +index 00000000..db55c4ca +--- /dev/null ++++ b/drivers/net/wireless/xradio/queue.h +@@ -0,0 +1,153 @@ ++/* ++ * queue operations for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++ ++#ifndef XRADIO_QUEUE_H_INCLUDED ++#define XRADIO_QUEUE_H_INCLUDED ++ ++/* private */ struct xradio_queue_item; ++ ++/* extern */ struct sk_buff; ++/* extern */ struct wsm_tx; ++/* extern */ struct xradio_common; ++/* extern */ struct xradio_vif; ++/* extern */ struct ieee80211_tx_queue_stats; ++/* extern */ struct xradio_txpriv; ++ ++/* forward */ struct xradio_queue_stats; ++ ++typedef void (*xradio_queue_skb_dtor_t)(struct xradio_common *priv, ++ struct sk_buff *skb, ++ const struct xradio_txpriv *txpriv); ++ ++struct xradio_queue { ++ struct xradio_queue_stats *stats; ++ size_t capacity; ++ size_t num_queued; ++ size_t num_queued_vif[XRWL_MAX_VIFS]; ++ size_t num_pending; ++ size_t num_pending_vif[XRWL_MAX_VIFS]; ++ size_t num_sent; ++ struct xradio_queue_item *pool; ++ struct list_head queue; ++ struct list_head free_pool; ++ struct list_head pending; ++ int tx_locked_cnt; ++ int *link_map_cache[XRWL_MAX_VIFS]; ++ bool overfull; ++ spinlock_t lock; ++ u8 queue_id; ++ u8 generation; ++ struct timer_list gc; ++ unsigned long ttl; ++}; ++ ++struct xradio_queue_stats { ++ spinlock_t lock; ++ int *link_map_cache[XRWL_MAX_VIFS]; ++ int num_queued[XRWL_MAX_VIFS]; ++ size_t map_capacity; ++ wait_queue_head_t wait_link_id_empty; ++ xradio_queue_skb_dtor_t skb_dtor; ++ struct xradio_common *hw_priv; ++}; ++ ++struct xradio_txpriv { ++ u8 link_id; ++ u8 raw_link_id; ++ u8 tid; ++ u8 rate_id; ++ u8 offset; ++ u8 if_id; ++#ifndef P2P_MULTIVIF ++ u8 offchannel_if_id; ++#else ++ u8 raw_if_id; ++#endif ++ u8 use_bg_rate; ++}; ++ ++int xradio_queue_stats_init(struct xradio_queue_stats *stats, ++ size_t map_capacity, ++ xradio_queue_skb_dtor_t skb_dtor, ++ struct xradio_common *priv); ++int xradio_queue_init(struct xradio_queue *queue, ++ struct xradio_queue_stats *stats, ++ u8 queue_id, ++ size_t capacity, ++ unsigned long ttl); ++int xradio_queue_clear(struct xradio_queue *queue, int if_id); ++void xradio_queue_stats_deinit(struct xradio_queue_stats *stats); ++void xradio_queue_deinit(struct xradio_queue *queue); ++ ++size_t xradio_queue_get_num_queued(struct xradio_vif *priv, ++ struct xradio_queue *queue, ++ u32 link_id_map); ++int xradio_queue_put(struct xradio_queue *queue, ++ struct sk_buff *skb, struct xradio_txpriv *txpriv); ++int xradio_queue_get(struct xradio_queue *queue, ++ int if_id, u32 link_id_map, ++ struct wsm_tx **tx, ++ struct ieee80211_tx_info **tx_info, ++ struct xradio_txpriv **txpriv); ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++int xradio_queue_requeue(struct xradio_common *hw_priv, ++ struct xradio_queue *queue, ++ u32 packetID, bool check); ++#else ++int xradio_queue_requeue(struct xradio_queue *queue, u32 packetID, bool check); ++#endif ++int xradio_queue_requeue_all(struct xradio_queue *queue); ++#ifdef CONFIG_XRADIO_TESTMODE ++int xradio_queue_remove(struct xradio_common *hw_priv, ++ struct xradio_queue *queue, ++ u32 packetID); ++#else ++int xradio_queue_remove(struct xradio_queue *queue, ++ u32 packetID); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++int xradio_queue_get_skb(struct xradio_queue *queue, u32 packetID, ++ struct sk_buff **skb, ++ const struct xradio_txpriv **txpriv); ++void xradio_queue_lock(struct xradio_queue *queue); ++void xradio_queue_unlock(struct xradio_queue *queue); ++bool xradio_queue_get_xmit_timestamp(struct xradio_queue *queue, ++ unsigned long *timestamp, int if_id, ++ u32 pending_frameID, u32 *Old_frame_ID); ++bool xradio_query_txpkt_timeout(struct xradio_common *hw_priv, int if_id, ++ u32 pending_pkt_id, long *timeout); ++ ++ ++bool xradio_queue_stats_is_empty(struct xradio_queue_stats *stats, ++ u32 link_id_map, int if_id); ++ ++static inline u8 xradio_queue_get_queue_id(u32 packetID) ++{ ++ return (packetID >> 16) & 0xF; ++} ++ ++static inline u8 xradio_queue_get_if_id(u32 packetID) ++{ ++ return (packetID >> 20) & 0xF; ++} ++ ++static inline u8 xradio_queue_get_link_id(u32 packetID) ++{ ++ return (packetID >> 24) & 0xF; ++} ++ ++static inline u8 xradio_queue_get_generation(u32 packetID) ++{ ++ return (packetID >> 8) & 0xFF; ++} ++ ++#endif /* XRADIO_QUEUE_H_INCLUDED */ +diff --git a/drivers/net/wireless/xradio/scan.c b/drivers/net/wireless/xradio/scan.c +new file mode 100644 +index 00000000..aa181634 +--- /dev/null ++++ b/drivers/net/wireless/xradio/scan.c +@@ -0,0 +1,1142 @@ ++/* ++ * Scan implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include "xradio.h" ++#include "scan.h" ++#include "sta.h" ++#include "pm.h" ++ ++static void xradio_scan_restart_delayed(struct xradio_vif *priv); ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++static int xradio_advance_scan_start(struct xradio_common *hw_priv) ++{ ++ int tmo = 0; ++ ++ ++ tmo += hw_priv->advanceScanElems.duration; ++ xradio_pm_stay_awake(&hw_priv->pm_state, tmo * HZ / 1000); ++ /* Invoke Advance Scan Duration Timeout Handler */ ++ queue_delayed_work(hw_priv->workqueue, ++ &hw_priv->advance_scan_timeout, tmo * HZ / 1000); ++ return 0; ++} ++#endif ++ ++static void xradio_remove_wps_p2p_ie(struct wsm_template_frame *frame) ++{ ++ u8 *ies; ++ u32 ies_len; ++ u32 ie_len; ++ u32 p2p_ie_len = 0; ++ u32 wps_ie_len = 0; ++ ++ ++ ies = &frame->skb->data[sizeof(struct ieee80211_hdr_3addr)]; ++ ies_len = frame->skb->len - sizeof(struct ieee80211_hdr_3addr); ++ while (ies_len >= 6) { ++ ie_len = ies[1] + 2; ++ ies_len -= ie_len; ++ if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) && ++ (ies[2] == 0x00 && ies[3] == 0x50 && ++ ies[4] == 0xf2 && ies[5] == 0x04)) { ++ wps_ie_len = ie_len; ++ memmove(ies, ies + ie_len, ies_len); ++ } else if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) && ++ (ies[2] == 0x50 && ies[3] == 0x6f && ++ ies[4] == 0x9a && ies[5] == 0x09)) { ++ p2p_ie_len = ie_len; ++ memmove(ies, ies + ie_len, ies_len); ++ } else { ++ ies += ie_len; ++ } ++ } ++ ++ if (p2p_ie_len || wps_ie_len) { ++ skb_trim(frame->skb, frame->skb->len - (p2p_ie_len + wps_ie_len)); ++ } ++} ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++static int xradio_disable_filtering(struct xradio_vif *priv) ++{ ++ int ret = 0; ++ bool bssid_filtering = 0; ++ struct wsm_rx_filter rx_filter; ++ struct wsm_beacon_filter_control bf_control; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ /* RX Filter Disable */ ++ rx_filter.promiscuous = 0; ++ rx_filter.bssid = 0; ++ rx_filter.fcs = 0; ++ ret = wsm_set_rx_filter(hw_priv, &rx_filter, priv->if_id); ++ ++ /* Beacon Filter Disable */ ++ bf_control.enabled = 0; ++ bf_control.bcn_count = 1; ++ if (!ret) ++ ret = wsm_beacon_filter_control(hw_priv, &bf_control, priv->if_id); ++ ++ /* BSSID Filter Disable */ ++ if (!ret) ++ ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, priv->if_id); ++ ++ return ret; ++} ++#endif ++ ++static int xradio_scan_start(struct xradio_vif *priv, struct wsm_scan *scan) ++{ ++ int ret, i; ++#ifdef FPGA_SETUP ++ int tmo = 5000; ++#else ++ int tmo = 5000; ++#endif ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ for (i = 0; i < scan->numOfChannels; ++i) ++ tmo += scan->ch[i].maxChannelTime + 10; ++ ++ atomic_set(&hw_priv->scan.in_progress, 1); ++ atomic_set(&hw_priv->recent_scan, 1); ++ xradio_pm_stay_awake(&hw_priv->pm_state, tmo * HZ / 1000); ++#ifdef P2P_MULTIVIF ++ ret = wsm_scan(hw_priv, scan, priv->if_id ? 1 : 0); ++#else ++ ret = wsm_scan(hw_priv, scan, priv->if_id); ++#endif ++ if (unlikely(ret)) { ++ scan_printk(XRADIO_DBG_WARN, "%s,wsm_scan failed!\n", __func__); ++ atomic_set(&hw_priv->scan.in_progress, 0); ++ xradio_scan_restart_delayed(priv); ++ } else { ++ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, ++ tmo * HZ / 1000); ++ } ++ return ret; ++} ++ ++#ifdef ROAM_OFFLOAD ++static int xradio_sched_scan_start(struct xradio_vif *priv, struct wsm_scan *scan) ++{ ++ int ret; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ ret = wsm_scan(hw_priv, scan, priv->if_id); ++ if (unlikely(ret)) { ++ atomic_set(&hw_priv->scan.in_progress, 0); ++ scan_printk(XRADIO_DBG_WARN,"%s,wsm_scan failed!\n", __func__); ++ } ++ return ret; ++} ++#endif /*ROAM_OFFLOAD*/ ++ ++int xradio_hw_scan(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_scan_request *hw_req) ++{ ++ struct cfg80211_scan_request *req = &hw_req->req; ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, ++ }; ++ int i; ++#ifdef CONFIG_XRADIO_TESTMODE ++ int ret = 0; ++ u16 advance_scan_req_channel; ++#endif ++ ++ ++ /* Scan when P2P_GO corrupt firmware MiniAP mode */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) { ++ scan_printk(XRADIO_DBG_WARN,"%s, can't scan in AP mode!\n", __func__); ++ return -EOPNOTSUPP; ++ } ++ ++ if (hw_priv->bh_error) { ++ scan_printk(XRADIO_DBG_NIY, "Ignoring scan bh error occur!\n"); ++ return -EBUSY; ++ } ++ ++ if (work_pending(&priv->offchannel_work) || ++ (hw_priv->roc_if_id != -1)) { ++ scan_printk(XRADIO_DBG_WARN, "Offchannel work pending, " ++ "ignoring scan work %d\n", hw_priv->roc_if_id); ++ return -EBUSY; ++ } ++ ++ if (req->n_ssids == 1 && !req->ssids[0].ssid_len) ++ req->n_ssids = 0; ++ ++ scan_printk(XRADIO_DBG_NIY, "vif%d Scan request(%s-%dchs) for %d SSIDs.\n", ++ priv->if_id, (req->channels[0]->band==NL80211_BAND_2GHZ)?"2.4G":"5G", ++ req->n_channels, req->n_ssids); ++ ++ /*delay multiple ssids scan of vif0 for 3s when connnetting to a node*/ ++ if(hw_priv->connet_time[0] > 0 && req->n_ssids == 0 && priv->if_id == 0) { ++ long timeleft0 = hw_priv->connet_time[0] + SCAN_MAX_DELAY - jiffies; ++ if(jiffies >= hw_priv->connet_time[0] && timeleft0 > 0) { ++ scan_printk(XRADIO_DBG_NIY, "vif0 connetting, scan delay %ldms\n", ++ timeleft0*1000/HZ); ++ return -EBUSY; ++ } ++ hw_priv->connet_time[0] = 0; ++ } ++ ++ if (req->n_ssids > hw->wiphy->max_scan_ssids){ ++ scan_printk(XRADIO_DBG_ERROR, "%s: ssids is too much(%d)\n", ++ __func__, req->n_ssids); ++ return -EINVAL; ++ } ++ ++ /* TODO by Icenowy: so strange function call */ ++ frame.skb = ieee80211_probereq_get(hw, vif->addr, NULL, 0, 0); ++ if (!frame.skb) { ++ scan_printk(XRADIO_DBG_ERROR, "%s: ieee80211_probereq_get failed!\n", ++ __func__); ++ return -ENOMEM; ++ } ++ ++#ifdef ROAM_OFFLOAD ++ if (priv->join_status != XRADIO_JOIN_STATUS_STA) { ++ if (req->channels[0]->band == NL80211_BAND_2GHZ) ++ hw_priv->num_scanchannels = 0; ++ else ++ hw_priv->num_scanchannels = hw_priv->num_2g_channels; ++ ++ for (i=0; i < req->n_channels; i++) { ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number = \ ++ req->channels[i]->hw_value; ++ if (req->channels[i]->flags & IEEE80211_CHAN_NO_IR) { ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].minChannelTime = 50; ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].maxChannelTime = 110; ++ } ++ else { ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].minChannelTime = 10; ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].maxChannelTime = 40; ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \ ++ XRADIO_SCAN_TYPE_ACTIVE; ++ } ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].txPowerLevel = \ ++ req->channels[i]->max_power; ++ if (req->channels[0]->band == NL80211_BAND_5GHZ) ++ hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \ ++ XRADIO_SCAN_BAND_5G; ++ } ++ if (req->channels[0]->band == NL80211_BAND_2GHZ) ++ hw_priv->num_2g_channels = req->n_channels; ++ else ++ hw_priv->num_5g_channels = req->n_channels; ++ } ++ hw_priv->num_scanchannels = hw_priv->num_2g_channels + hw_priv->num_5g_channels; ++#endif /*ROAM_OFFLOAD*/ ++ ++ /* will be unlocked in xradio_scan_work() */ ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ /* Active Scan - Serving Channel Request Handling */ ++ advance_scan_req_channel = req->channels[0]->hw_value; ++ if (hw_priv->enable_advance_scan && ++ (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_ACTIVE) && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ (hw_priv->channel->hw_value == advance_scan_req_channel)) { ++ SYS_BUG(hw_priv->scan.req); ++ /* wsm_lock_tx(hw_priv); */ ++ wsm_vif_lock_tx(priv); ++ hw_priv->scan.if_id = priv->if_id; ++ /* Disable Power Save */ ++ if (priv->powersave_mode.pmMode & WSM_PSM_PS) { ++ struct wsm_set_pm pm = priv->powersave_mode; ++ pm.pmMode = WSM_PSM_ACTIVE; ++ wsm_set_pm(hw_priv, &pm, priv->if_id); ++ } ++ /* Disable Rx Beacon and Bssid filter */ ++ ret = xradio_disable_filtering(priv); ++ if (ret) ++ scan_printk(XRADIO_DBG_ERROR, "%s: Disable BSSID or Beacon filtering " ++ "failed: %d.\n", __func__, ret); ++ wsm_unlock_tx(hw_priv); ++ mutex_unlock(&hw_priv->conf_mutex); ++ /* Transmit Probe Request with Broadcast SSID */ ++ xradio_tx(hw, frame.skb); ++ /* Start Advance Scan Timer */ ++ xradio_advance_scan_start(hw_priv); ++ } else { ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++ if (frame.skb) { ++ int ret = 0; ++ if (priv->if_id == 0) ++ xradio_remove_wps_p2p_ie(&frame); ++#ifdef P2P_MULTIVIF ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id ? 1 : 0); ++#else ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); ++#endif ++ if (ret) { ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++ dev_kfree_skb(frame.skb); ++ scan_printk(XRADIO_DBG_ERROR, "%s: wsm_set_template_frame failed: %d.\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ wsm_vif_lock_tx(priv); ++ ++ SYS_BUG(hw_priv->scan.req); ++ hw_priv->scan.req = req; ++ hw_priv->scan.n_ssids = 0; ++ hw_priv->scan.status = 0; ++ hw_priv->scan.begin = &req->channels[0]; ++ hw_priv->scan.curr = hw_priv->scan.begin; ++ hw_priv->scan.end = &req->channels[req->n_channels]; ++ hw_priv->scan.output_power = hw_priv->output_power; ++ hw_priv->scan.if_id = priv->if_id; ++ /* TODO:COMBO: Populate BIT4 in scanflags to decide on which MAC ++ * address the SCAN request will be sent */ ++ ++ for (i = 0; i < req->n_ssids; ++i) { ++ struct wsm_ssid *dst = &hw_priv->scan.ssids[hw_priv->scan.n_ssids]; ++ SYS_BUG(req->ssids[i].ssid_len > sizeof(dst->ssid)); ++ memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); ++ dst->length = req->ssids[i].ssid_len; ++ ++hw_priv->scan.n_ssids; ++ } ++ ++ mutex_unlock(&hw_priv->conf_mutex); ++ ++ if (frame.skb) ++ dev_kfree_skb(frame.skb); ++ queue_work(hw_priv->workqueue, &hw_priv->scan.work); ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ } ++#endif ++ ++ return 0; ++} ++ ++#ifdef ROAM_OFFLOAD ++int xradio_hw_sched_scan_start(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct cfg80211_sched_scan_request *req, ++ struct ieee80211_sched_scan_ies *ies) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, ++ }; ++ int i; ++ ++ ++ scan_printk(XRADIO_DBG_WARN, "Scheduled scan request-->.\n"); ++ if (!priv->vif) ++ return -EINVAL; ++ ++ /* Scan when P2P_GO corrupt firmware MiniAP mode */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) { ++ scan_printk(XRADIO_DBG_WARN,"%s, can't scan in AP mode!\n", __func__); ++ return -EOPNOTSUPP; ++ } ++ ++ scan_printk(XRADIO_DBG_WARN, "Scheduled scan: n_ssids %d, ssid[0].len = %d\n", ++ req->n_ssids, req->ssids[0].ssid_len); ++ if (req->n_ssids == 1 && !req->ssids[0].ssid_len) ++ req->n_ssids = 0; ++ ++ scan_printk(XRADIO_DBG_NIY, "[SCAN] Scan request for %d SSIDs.\n", ++ req->n_ssids); ++ ++ if (req->n_ssids > hw->wiphy->max_scan_ssids) [ ++ scan_printk(XRADIO_DBG_ERROR, "%s: ssids is too much(%d)\n", ++ __func__, req->n_ssids); ++ return -EINVAL; ++ } ++ ++ frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, ++ ies->ie[0], ies->len[0]); ++ if (!frame.skb) { ++ scan_printk(XRADIO_DBG_ERROR, "%s: ieee80211_probereq_get failed!\n", ++ __func__); ++ return -ENOMEM; ++ } ++ ++ /* will be unlocked in xradio_scan_work() */ ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ if (frame.skb) { ++ int ret; ++ if (priv->if_id == 0) ++ xradio_remove_wps_p2p_ie(&frame); ++ ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); ++ if (0 == ret) { ++ /* Host want to be the probe responder. */ ++ ret = wsm_set_probe_responder(priv, true); ++ } ++ if (ret) { ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++ dev_kfree_skb(frame.skb); ++ scan_printk(XRADIO_DBG_ERROR, "%s: wsm_set_probe_responder failed: %d.\n", ++ __func__, ret); ++ return ret; ++ } ++ } ++ ++ wsm_lock_tx(hw_priv); ++ SYS_BUG(hw_priv->scan.req); ++ hw_priv->scan.sched_req = req; ++ hw_priv->scan.n_ssids = 0; ++ hw_priv->scan.status = 0; ++ hw_priv->scan.begin = &req->channels[0]; ++ hw_priv->scan.curr = hw_priv->scan.begin; ++ hw_priv->scan.end = &req->channels[req->n_channels]; ++ hw_priv->scan.output_power = hw_priv->output_power; ++ ++ for (i = 0; i < req->n_ssids; ++i) { ++ u8 j; ++ struct wsm_ssid *dst = &hw_priv->scan.ssids[hw_priv->scan.n_ssids]; ++ SYS_BUG(req->ssids[i].ssid_len > sizeof(dst->ssid)); ++ memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); ++ dst->length = req->ssids[i].ssid_len; ++ ++hw_priv->scan.n_ssids; ++ scan_printk(XRADIO_DBG_NIY, "SSID %d\n",i); ++ for(j=0; jssids[i].ssid_len; j++) ++ scan_printk(XRADIO_DBG_NIY, "0x%x\n", req->ssids[i].ssid[j]); ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ ++ if (frame.skb) ++ dev_kfree_skb(frame.skb); ++ queue_work(hw_priv->workqueue, &hw_priv->scan.swork); ++ scan_printk(XRADIO_DBG_NIY, "<-- Scheduled scan request.\n"); ++ return 0; ++} ++#endif /*ROAM_OFFLOAD*/ ++ ++void xradio_scan_work(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = container_of(work, ++ struct xradio_common, ++ scan.work); ++ struct xradio_vif *priv; ++ struct ieee80211_channel **it; ++ struct wsm_scan scan = { ++ .scanType = WSM_SCAN_TYPE_FOREGROUND, ++ .scanFlags = 0, /* TODO:COMBO */ ++ //.scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, /* TODO:COMBO */ ++ }; ++ bool first_run; ++ int i; ++ const u32 ProbeRequestTime = 2; ++ const u32 ChannelRemainTime = 15; ++ u32 maxChannelTime; ++#ifdef CONFIG_XRADIO_TESTMODE ++ int ret = 0; ++ u16 advance_scan_req_channel = hw_priv->scan.begin[0]->hw_value; ++#endif ++ struct cfg80211_scan_info scan_info; ++ ++ ++ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); ++ ++ /*TODO: COMBO: introduce locking so vif is not removed in meanwhile */ ++ if (!priv) { ++ scan_printk(XRADIO_DBG_WARN, "interface removed, " ++ "ignoring scan work\n"); ++ return; ++ } ++ ++ if (priv->if_id) ++ scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1; ++ else ++ scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1; ++ ++ /* No need to set WSM_SCAN_FLAG_FORCE_BACKGROUND in BSS_LOSS work. ++ * yangfh 2015-11-11 18:45:02 */ ++ //xradio_for_each_vif(hw_priv, vif, i) { ++ // if (!vif) ++ // continue; ++ // if (vif->bss_loss_status > XRADIO_BSS_LOSS_NONE) ++ // scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND; ++ //} ++ ++ first_run = (hw_priv->scan.begin == hw_priv->scan.curr && ++ hw_priv->scan.begin != hw_priv->scan.end); ++ if (first_run) { ++ /* Firmware gets crazy if scan request is sent ++ * when STA is joined but not yet associated. ++ * Force unjoin in this case. */ ++ if (cancel_delayed_work_sync(&priv->join_timeout) > 0) ++ xradio_join_timeout(&priv->join_timeout.work); ++ } ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ if (first_run) { ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ /* Passive Scan - Serving Channel Request Handling */ ++ if (hw_priv->enable_advance_scan && ++ (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_PASSIVE) && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ (hw_priv->channel->hw_value == advance_scan_req_channel)) { ++ /* If Advance Scan Request is for Serving Channel Device ++ * should be Active and Filtering Should be Disable */ ++ if (priv->powersave_mode.pmMode & WSM_PSM_PS) { ++ struct wsm_set_pm pm = priv->powersave_mode; ++ pm.pmMode = WSM_PSM_ACTIVE; ++ wsm_set_pm(hw_priv, &pm, priv->if_id); ++ } ++ /* Disable Rx Beacon and Bssid filter */ ++ ret = xradio_disable_filtering(priv); ++ if (ret) ++ scan_printk(XRADIO_DBG_ERROR, "%s: Disable BSSID or Beacon" ++ "filtering failed: %d.\n", __func__, ret); ++ } else if (hw_priv->enable_advance_scan && ++ (hw_priv->advanceScanElems.scanMode == ++ XRADIO_SCAN_MEASUREMENT_PASSIVE) && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA)) { ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA && ++ !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { ++ struct wsm_set_pm pm = priv->powersave_mode; ++ pm.pmMode = WSM_PSM_PS; ++ xradio_set_pm(priv, &pm); ++ } ++ } else { ++#endif ++ ++#if 0 ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA && ++ !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { ++ struct wsm_set_pm pm = priv->powersave_mode; ++ pm.pmMode = WSM_PSM_PS; ++ xradio_set_pm(priv, &pm); ++ } else ++#endif ++ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) { ++ /* FW bug: driver has to restart p2p-dev mode ++ * after scan */ ++ xradio_disable_listening(priv); ++ } ++#ifdef CONFIG_XRADIO_TESTMODE ++ } ++#endif ++ } ++ ++ if (!hw_priv->scan.req || (hw_priv->scan.curr == hw_priv->scan.end)) { ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ if (hw_priv->enable_advance_scan && ++ (hw_priv->advanceScanElems.scanMode == ++ XRADIO_SCAN_MEASUREMENT_PASSIVE) && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ (hw_priv->channel->hw_value == advance_scan_req_channel)) { ++ /* WSM Lock should be held here for WSM APIs */ ++ wsm_vif_lock_tx(priv); ++ ++ /* wsm_lock_tx(priv); */ ++ /* Once Duration is Over, enable filtering ++ * and Revert Back Power Save */ ++ if (priv->powersave_mode.pmMode & WSM_PSM_PS) ++ wsm_set_pm(hw_priv, &priv->powersave_mode, priv->if_id); ++ xradio_update_filtering(priv); ++ } else if (!hw_priv->enable_advance_scan) { ++#endif ++ if (hw_priv->scan.output_power != hw_priv->output_power) { ++ /* TODO:COMBO: Change when mac80211 implementation ++ * is available for output power also */ ++#ifdef P2P_MULTIVIF ++ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, ++ priv->if_id ? 1 : 0)); ++#else ++ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, ++ priv->if_id)); ++#endif ++ } ++#ifdef CONFIG_XRADIO_TESTMODE ++ } ++#endif ++ ++#if 0 ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA && ++ !(priv->powersave_mode.pmMode & WSM_PSM_PS)) ++ xradio_set_pm(priv, &priv->powersave_mode); ++#endif ++ ++ if (hw_priv->scan.status < 0) ++ scan_printk(XRADIO_DBG_ERROR, "Scan failed (%d).\n", hw_priv->scan.status); ++ else if (hw_priv->scan.req) ++ scan_printk(XRADIO_DBG_NIY, "Scan completed.\n"); ++ else ++ scan_printk(XRADIO_DBG_NIY, "Scan canceled.\n"); ++ ++ hw_priv->scan.req = NULL; ++ xradio_scan_restart_delayed(priv); ++#ifdef CONFIG_XRADIO_TESTMODE ++ hw_priv->enable_advance_scan = false; ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ wsm_unlock_tx(hw_priv); ++ mutex_unlock(&hw_priv->conf_mutex); ++ memset(&scan_info, 0, sizeof(scan_info)); ++ scan_info.aborted = hw_priv->scan.status ? 1 : 0; ++ ieee80211_scan_completed(hw_priv->hw, &scan_info); ++ up(&hw_priv->scan.lock); ++ return; ++ ++ } else { ++ struct ieee80211_channel *first = *hw_priv->scan.curr; ++ for (it = hw_priv->scan.curr + 1, i = 1; ++ it != hw_priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; ++ ++it, ++i) { ++ if ((*it)->band != first->band) ++ break; ++ if (((*it)->flags ^ first->flags) & IEEE80211_CHAN_NO_IR) ++ break; ++ if (!(first->flags & IEEE80211_CHAN_NO_IR) && ++ (*it)->max_power != first->max_power) ++ break; ++ } ++ scan.band = first->band; ++ ++ if (hw_priv->scan.req->no_cck) ++ scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; ++ else ++ scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ if (hw_priv->enable_advance_scan) { ++ if (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_PASSIVE) ++ scan.numOfProbeRequests = 0; ++ else ++ scan.numOfProbeRequests = 1; ++ } else { ++#endif ++ /* TODO: Is it optimal? */ ++ scan.numOfProbeRequests = (first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2; ++#ifdef CONFIG_XRADIO_TESTMODE ++ } ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++ scan.numOfSSIDs = hw_priv->scan.n_ssids; ++ scan.ssids = &hw_priv->scan.ssids[0]; ++ scan.numOfChannels = it - hw_priv->scan.curr; ++ /* TODO: Is it optimal? */ ++ scan.probeDelay = 100; ++ /* It is not stated in WSM specification, however ++ * FW team says that driver may not use FG scan ++ * when joined. */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ scan.scanType = WSM_SCAN_TYPE_BACKGROUND; ++ scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; ++ } ++ scan.ch = kzalloc(sizeof(struct wsm_scan_ch[it - hw_priv->scan.curr]), GFP_KERNEL); ++ if (!scan.ch) { ++ hw_priv->scan.status = -ENOMEM; ++ scan_printk(XRADIO_DBG_ERROR, "xr_kzalloc wsm_scan_ch failed.\n"); ++ goto fail; ++ } ++ maxChannelTime = (scan.numOfSSIDs * scan.numOfProbeRequests *ProbeRequestTime) + ++ ChannelRemainTime; ++ maxChannelTime = (maxChannelTime < 35) ? 35 : maxChannelTime; ++ for (i = 0; i < scan.numOfChannels; ++i) { ++ scan.ch[i].number = hw_priv->scan.curr[i]->hw_value; ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ if (hw_priv->enable_advance_scan) { ++ scan.ch[i].minChannelTime = hw_priv->advanceScanElems.duration; ++ scan.ch[i].maxChannelTime = hw_priv->advanceScanElems.duration; ++ } else { ++#endif ++ if (hw_priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) { ++ scan.ch[i].minChannelTime = 50; ++ scan.ch[i].maxChannelTime = 110; ++ } else { ++ scan.ch[i].minChannelTime = 15; ++ scan.ch[i].maxChannelTime = maxChannelTime; ++ } ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ } ++#endif ++ } ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ if (!hw_priv->enable_advance_scan) { ++#endif ++ if (!(first->flags & IEEE80211_CHAN_NO_IR) && ++ hw_priv->scan.output_power != first->max_power) { ++ hw_priv->scan.output_power = first->max_power; ++ /* TODO:COMBO: Change after mac80211 implementation ++ * complete */ ++#ifdef P2P_MULTIVIF ++ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->scan.output_power * 10, ++ priv->if_id ? 1 : 0)); ++#else ++ WARN_ON(wsm_set_output_power(hw_priv, hw_priv->scan.output_power * 10, ++ priv->if_id)); ++#endif ++ } ++#ifdef CONFIG_XRADIO_TESTMODE ++ } ++#endif ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ if (hw_priv->enable_advance_scan && ++ (hw_priv->advanceScanElems.scanMode == ++ XRADIO_SCAN_MEASUREMENT_PASSIVE) && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ (hw_priv->channel->hw_value == advance_scan_req_channel)) { ++ /* Start Advance Scan Timer */ ++ hw_priv->scan.status = xradio_advance_scan_start(hw_priv); ++ wsm_unlock_tx(hw_priv); ++ } else ++#endif ++ down(&hw_priv->scan.status_lock); ++ hw_priv->scan.status = xradio_scan_start(priv, &scan); ++ ++ kfree(scan.ch); ++ if (WARN_ON(hw_priv->scan.status)) { ++ scan_printk(XRADIO_DBG_ERROR, "scan failed, status=%d.\n", ++ hw_priv->scan.status); ++ up(&hw_priv->scan.status_lock); ++ goto fail; ++ } ++ up(&hw_priv->scan.status_lock); ++ hw_priv->scan.curr = it; ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ return; ++ ++fail: ++ hw_priv->scan.curr = hw_priv->scan.end; ++ mutex_unlock(&hw_priv->conf_mutex); ++ queue_work(hw_priv->workqueue, &hw_priv->scan.work); ++ return; ++} ++ ++#ifdef ROAM_OFFLOAD ++void xradio_sched_scan_work(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = container_of(work, struct xradio_common, ++ scan.swork); ++ struct wsm_scan scan; ++ struct wsm_ssid scan_ssid; ++ int i; ++ struct xradio_vif *priv = NULL; ++ ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); ++ if (unlikely(!priv)) { ++ WARN_ON(1); ++ return; ++ } ++ ++ spin_unlock(&priv->vif_lock); ++ /* Firmware gets crazy if scan request is sent ++ * when STA is joined but not yet associated. ++ * Force unjoin in this case. */ ++ if (cancel_delayed_work_sync(&priv->join_timeout) > 0) { ++ xradio_join_timeout(&priv->join_timeout.work); ++ } ++ mutex_lock(&hw_priv->conf_mutex); ++ hw_priv->auto_scanning = 1; ++ scan.band = 0; ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) ++ scan.scanType = 3; /* auto background */ ++ else ++ scan.scanType = 2; /* auto foreground */ ++ ++ scan.scanFlags = 0x01; /* bit 0 set => forced background scan */ ++ scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; ++ scan.autoScanInterval = (0xba << 24)|(30 * 1024); /* 30 seconds, -70 rssi */ ++ scan.numOfProbeRequests = 1; ++ //scan.numOfChannels = 11; ++ scan.numOfChannels = hw_priv->num_scanchannels; ++ scan.numOfSSIDs = 1; ++ scan.probeDelay = 100; ++ scan_ssid.length = priv->ssid_length; ++ memcpy(scan_ssid.ssid, priv->ssid, priv->ssid_length); ++ scan.ssids = &scan_ssid; ++ ++ scan.ch = xr_kzalloc(sizeof(struct wsm_scan_ch[scan.numOfChannels]), false); ++ if (!scan.ch) { ++ scan_printk(XRADIO_DBG_ERROR, "xr_kzalloc wsm_scan_ch failed.\n"); ++ hw_priv->scan.status = -ENOMEM; ++ goto fail; ++ } ++ ++ for (i = 0; i < scan.numOfChannels; i++) { ++ scan.ch[i].number = hw_priv->scan_channels[i].number; ++ scan.ch[i].minChannelTime = hw_priv->scan_channels[i].minChannelTime; ++ scan.ch[i].maxChannelTime = hw_priv->scan_channels[i].maxChannelTime; ++ scan.ch[i].txPowerLevel = hw_priv->scan_channels[i].txPowerLevel; ++ } ++ ++#if 0 ++ for (i = 1; i <= scan.numOfChannels; i++) { ++ scan.ch[i-1].number = i; ++ scan.ch[i-1].minChannelTime = 10; ++ scan.ch[i-1].maxChannelTime = 40; ++ } ++#endif ++ ++ hw_priv->scan.status = xradio_sched_scan_start(priv, &scan); ++ kfree(scan.ch); ++ if (hw_priv->scan.status) { ++ scan_printk(XRADIO_DBG_ERROR, "scan failed, status=%d.\n", ++ hw_priv->scan.status); ++ goto fail; ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ return; ++ ++fail: ++ mutex_unlock(&hw_priv->conf_mutex); ++ queue_work(hw_priv->workqueue, &hw_priv->scan.swork); ++ return; ++} ++ ++void xradio_hw_sched_scan_stop(struct xradio_common *hw_priv) ++{ ++ struct xradio_vif *priv = NULL; ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv,hw_priv->scan.if_id); ++ if (unlikely(!priv)) ++ return; ++ ++ spin_unlock(&priv->vif_lock); ++ wsm_stop_scan(hw_priv, priv->if_id); ++ ++ return; ++} ++#endif /*ROAM_OFFLOAD*/ ++ ++ ++static void xradio_scan_restart_delayed(struct xradio_vif *priv) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ if (priv->delayed_link_loss) { ++ int tmo = hw_priv->scan.direct_probe ? 0 : priv->cqm_beacon_loss_count; ++ ++ priv->delayed_link_loss = 0; ++ /* Restart beacon loss timer and requeue ++ BSS loss work. */ ++ scan_printk(XRADIO_DBG_WARN, "[CQM] Requeue BSS loss in %d " ++ "beacons.\n", tmo); ++ cancel_delayed_work_sync(&priv->bss_loss_work); ++ queue_delayed_work(hw_priv->workqueue, &priv->bss_loss_work, ++ tmo * HZ / 10); ++ ++ } ++ ++ /* FW bug: driver has to restart p2p-dev mode after scan. */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) { ++ /*xradio_enable_listening(priv);*/ ++ WARN_ON(1); ++ xradio_update_filtering(priv); ++ scan_printk(XRADIO_DBG_WARN, "driver has to restart " ++ "p2p-dev mode after scan"); ++ } ++ ++ if (atomic_xchg(&priv->delayed_unjoin, 0)) { ++ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ } ++} ++ ++static void xradio_scan_complete(struct xradio_common *hw_priv, int if_id) ++{ ++ struct xradio_vif *priv; ++ atomic_xchg(&hw_priv->recent_scan, 0); ++ ++ ++ if (hw_priv->scan.direct_probe) { ++ mutex_lock(&hw_priv->conf_mutex); ++ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, if_id); ++ if (priv) { ++ scan_printk(XRADIO_DBG_MSG, "Direct probe complete.\n"); ++ xradio_scan_restart_delayed(priv); ++ } else { ++ scan_printk(XRADIO_DBG_MSG, "Direct probe complete without interface!\n"); ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ hw_priv->scan.direct_probe = 0; ++ up(&hw_priv->scan.lock); ++ wsm_unlock_tx(hw_priv); ++ } else { ++ xradio_scan_work(&hw_priv->scan.work); ++ } ++} ++ ++void xradio_scan_complete_cb(struct xradio_common *hw_priv, ++ struct wsm_scan_complete *arg) ++{ ++ struct xradio_vif *priv = xrwl_hwpriv_to_vifpriv(hw_priv, ++ hw_priv->scan.if_id); ++ ++ ++ if (unlikely(!priv)) ++ return; ++ ++#ifdef ROAM_OFFLOAD ++ if (hw_priv->auto_scanning) ++ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0); ++#endif /*ROAM_OFFLOAD*/ ++ ++ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { ++ /* STA is stopped. */ ++ spin_unlock(&priv->vif_lock); ++ scan_printk(XRADIO_DBG_WARN, "%s: priv->mode UNSPECIFIED.\n", __func__); ++ return; ++ } ++ spin_unlock(&priv->vif_lock); ++ ++ /* ++ if(hw_priv->scan.status == -ETIMEDOUT) ++ scan_printk(XRADIO_DBG_WARN, "Scan timeout already occured. " ++ "Don't cancel work"); ++ if ((hw_priv->scan.status != -ETIMEDOUT) && ++ (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0)) { ++ hw_priv->scan.status = 1; ++ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0); ++ } ++ */ // should not involve status as a condition ++ ++ if (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0) { ++ down(&hw_priv->scan.status_lock); ++ hw_priv->scan.status = 1; ++ up(&hw_priv->scan.status_lock); ++ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0); ++ } ++} ++ ++void xradio_scan_timeout(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, scan.timeout.work); ++ ++ ++ if (likely(atomic_xchg(&hw_priv->scan.in_progress, 0))) { ++ if (hw_priv->scan.status > 0) ++ hw_priv->scan.status = 0; ++ else if (!hw_priv->scan.status) { ++ scan_printk(XRADIO_DBG_WARN, "Timeout waiting for scan " ++ "complete notification.\n"); ++ hw_priv->scan.status = -ETIMEDOUT; ++ hw_priv->scan.curr = hw_priv->scan.end; ++ WARN_ON(wsm_stop_scan(hw_priv, hw_priv->scan.if_id ? 1 : 0)); ++ } ++ xradio_scan_complete(hw_priv, hw_priv->scan.if_id); ++#ifdef ROAM_OFFLOAD ++ } else if (hw_priv->auto_scanning) { ++ hw_priv->auto_scanning = 0; ++ ieee80211_sched_scan_results(hw_priv->hw); ++#endif /*ROAM_OFFLOAD*/ ++ } ++} ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++void xradio_advance_scan_timeout(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, advance_scan_timeout.work); ++ ++ struct xradio_vif *priv = xrwl_hwpriv_to_vifpriv(hw_priv, ++ hw_priv->scan.if_id); ++ ++ ++ if (WARN_ON(!priv)) ++ return; ++ spin_unlock(&priv->vif_lock); ++ ++ hw_priv->scan.status = 0; ++ if (hw_priv->advanceScanElems.scanMode == ++ XRADIO_SCAN_MEASUREMENT_PASSIVE) { ++ /* Passive Scan on Serving Channel ++ * Timer Expire */ ++ xradio_scan_complete(hw_priv, hw_priv->scan.if_id); ++ } else { ++ /* Active Scan on Serving Channel ++ * Timer Expire */ ++ mutex_lock(&hw_priv->conf_mutex); ++ //wsm_lock_tx(priv); ++ wsm_vif_lock_tx(priv); ++ /* Once Duration is Over, enable filtering ++ * and Revert Back Power Save */ ++ if ((priv->powersave_mode.pmMode & WSM_PSM_PS)) ++ wsm_set_pm(hw_priv, &priv->powersave_mode, priv->if_id); ++ hw_priv->scan.req = NULL; ++ xradio_update_filtering(priv); ++ hw_priv->enable_advance_scan = false; ++ wsm_unlock_tx(hw_priv); ++ mutex_unlock(&hw_priv->conf_mutex); ++ ieee80211_scan_completed(hw_priv->hw, hw_priv->scan.status ? true : false); ++ up(&hw_priv->scan.lock); ++ } ++} ++#endif ++ ++void xradio_probe_work(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, scan.probe_work.work); ++ struct xradio_vif *priv; ++ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); ++ struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; ++ const struct xradio_txpriv *txpriv; ++ struct wsm_tx *wsm; ++ struct wsm_template_frame frame = { ++ .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, ++ }; ++ struct wsm_ssid ssids[1] = {{ ++ .length = 0, ++ } }; ++ struct wsm_scan_ch ch[1] = {{ ++ .minChannelTime = 0, ++ .maxChannelTime = 10, ++ } }; ++ struct wsm_scan scan = { ++ .scanType = WSM_SCAN_TYPE_FOREGROUND, ++ .numOfProbeRequests = 1, ++ .probeDelay = 0, ++ .numOfChannels = 1, ++ .ssids = ssids, ++ .ch = ch, ++ }; ++ u8 *ies; ++ size_t ies_len; ++ int ret = 1; ++ scan_printk(XRADIO_DBG_MSG, "%s:Direct probe.\n", __func__); ++ ++ SYS_BUG(queueId >= 4); ++ SYS_BUG(!hw_priv->channel); ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ if (unlikely(down_trylock(&hw_priv->scan.lock))) { ++ /* Scan is already in progress. Requeue self. */ ++ schedule(); ++ queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.probe_work, ++ HZ / 10); ++ mutex_unlock(&hw_priv->conf_mutex); ++ return; ++ } ++ ++ if (xradio_queue_get_skb(queue, hw_priv->pending_frame_id, &frame.skb, &txpriv)) { ++ up(&hw_priv->scan.lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++ wsm_unlock_tx(hw_priv); ++ scan_printk(XRADIO_DBG_ERROR, "%s:xradio_queue_get_skb error!\n", __func__); ++ return; ++ } ++ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); ++ if (!priv) { ++ up(&hw_priv->scan.lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++ scan_printk(XRADIO_DBG_ERROR, "%s:priv error!\n", __func__); ++ return; ++ } ++ wsm = (struct wsm_tx *)frame.skb->data; ++ scan.maxTransmitRate = wsm->maxTxRate; ++ scan.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ? ++ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ scan.scanType = WSM_SCAN_TYPE_BACKGROUND; ++ scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; ++ if (priv->if_id) ++ scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1; ++ else ++ scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1; ++ } ++ ++ /* No need to set WSM_SCAN_FLAG_FORCE_BACKGROUND in BSS_LOSS work. ++ * yangfh 2015-11-11 18:45:02 */ ++ //xradio_for_each_vif(hw_priv, vif, i) { ++ // if (!vif) ++ // continue; ++ // if (vif->bss_loss_status > XRADIO_BSS_LOSS_NONE) ++ // scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND; ++ //} ++ ++ ch[0].number = hw_priv->channel->hw_value; ++ skb_pull(frame.skb, txpriv->offset); ++ ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; ++ ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); ++ ++ if (ies_len) { ++ u8 *ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); ++ if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { ++ u8 *nextie = &ssidie[2 + ssidie[1]]; ++ /* Remove SSID from the IE list. It has to be provided ++ * as a separate argument in xradio_scan_start call */ ++ ++ /* Store SSID localy */ ++ ssids[0].length = ssidie[1]; ++ memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); ++ scan.numOfSSIDs = 1; ++ ++ /* Remove SSID from IE list */ ++ ssidie[1] = 0; ++ memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); ++ skb_trim(frame.skb, frame.skb->len - ssids[0].length); ++ } ++ } ++ ++ if (priv->if_id == 0) ++ xradio_remove_wps_p2p_ie(&frame); ++ ++ /* FW bug: driver has to restart p2p-dev mode after scan */ ++ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) { ++ WARN_ON(1); ++ /*xradio_disable_listening(priv);*/ ++ } ++ ret = WARN_ON(wsm_set_template_frame(hw_priv, &frame, ++ priv->if_id)); ++ ++ hw_priv->scan.direct_probe = 1; ++ hw_priv->scan.if_id = priv->if_id; ++ if (!ret) { ++ wsm_flush_tx(hw_priv); ++ ret = WARN_ON(xradio_scan_start(priv, &scan)); ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ ++ skb_push(frame.skb, txpriv->offset); ++ if (!ret) ++ IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; ++#ifdef CONFIG_XRADIO_TESTMODE ++ SYS_BUG(xradio_queue_remove(hw_priv, queue, hw_priv->pending_frame_id)); ++#else ++ SYS_BUG(xradio_queue_remove(queue, hw_priv->pending_frame_id)); ++#endif ++ ++ if (ret) { ++ hw_priv->scan.direct_probe = 0; ++ up(&hw_priv->scan.lock); ++ wsm_unlock_tx(hw_priv); ++ } ++ ++ return; ++} +diff --git a/drivers/net/wireless/xradio/scan.h b/drivers/net/wireless/xradio/scan.h +new file mode 100644 +index 00000000..3a1f11d8 +--- /dev/null ++++ b/drivers/net/wireless/xradio/scan.h +@@ -0,0 +1,75 @@ ++/* ++ * Scan interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++ ++#ifndef SCAN_H_INCLUDED ++#define SCAN_H_INCLUDED ++ ++#include ++#include "wsm.h" ++ ++/* external */ struct sk_buff; ++/* external */ struct cfg80211_scan_request; ++/* external */ struct ieee80211_channel; ++/* external */ struct ieee80211_hw; ++/* external */ struct work_struct; ++ ++#define SCAN_MAX_DELAY (3*HZ) //3s, add by yangfh for connect ++ ++struct xradio_scan { ++ struct semaphore lock; ++ struct work_struct work; ++#ifdef ROAM_OFFLOAD ++ struct work_struct swork; /* scheduled scan work */ ++ struct cfg80211_sched_scan_request *sched_req; ++#endif /*ROAM_OFFLOAD*/ ++ struct delayed_work timeout; ++ struct cfg80211_scan_request *req; ++ struct ieee80211_channel **begin; ++ struct ieee80211_channel **curr; ++ struct ieee80211_channel **end; ++ struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; ++ int output_power; ++ int n_ssids; ++ //add by liwei, for h64 ping WS550 BUG ++ struct semaphore status_lock; ++ int status; ++ atomic_t in_progress; ++ /* Direct probe requests workaround */ ++ struct delayed_work probe_work; ++ int direct_probe; ++ u8 if_id; ++}; ++ ++int xradio_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ++ struct ieee80211_scan_request *req); ++#ifdef ROAM_OFFLOAD ++int xradio_hw_sched_scan_start(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct cfg80211_sched_scan_request *req, ++ struct ieee80211_sched_scan_ies *ies); ++void xradio_hw_sched_scan_stop(struct xradio_common *priv); ++void xradio_sched_scan_work(struct work_struct *work); ++#endif /*ROAM_OFFLOAD*/ ++void xradio_scan_work(struct work_struct *work); ++void xradio_scan_timeout(struct work_struct *work); ++void xradio_scan_complete_cb(struct xradio_common *priv, ++ struct wsm_scan_complete *arg); ++ ++/* ******************************************************************** */ ++/* Raw probe requests TX workaround */ ++void xradio_probe_work(struct work_struct *work); ++#ifdef CONFIG_XRADIO_TESTMODE ++/* Advance Scan Timer */ ++void xradio_advance_scan_timeout(struct work_struct *work); ++#endif ++ ++#endif +diff --git a/drivers/net/wireless/xradio/sdio.c b/drivers/net/wireless/xradio/sdio.c +new file mode 100644 +index 00000000..70218423 +--- /dev/null ++++ b/drivers/net/wireless/xradio/sdio.c +@@ -0,0 +1,266 @@ ++/* ++ * SDIO driver for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "sdio.h" ++#include "main.h" ++ ++/* sdio vendor id and device id*/ ++#define SDIO_VENDOR_ID_XRADIO 0x0020 ++#define SDIO_DEVICE_ID_XRADIO 0x2281 ++static const struct sdio_device_id xradio_sdio_ids[] = { ++ { SDIO_DEVICE(SDIO_VENDOR_ID_XRADIO, SDIO_DEVICE_ID_XRADIO) }, ++ //{ SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, ++ { /* end: all zeroes */ }, ++}; ++ ++/* sbus_ops implemetation */ ++int sdio_data_read(struct xradio_common* self, unsigned int addr, ++ void *dst, int count) ++{ ++ int ret = sdio_memcpy_fromio(self->sdio_func, dst, addr, count); ++// printk("sdio_memcpy_fromio 0x%x:%d ret %d\n", addr, count, ret); ++// print_hex_dump_bytes("sdio read ", 0, dst, min(count,32)); ++ return ret; ++} ++ ++int sdio_data_write(struct xradio_common* self, unsigned int addr, ++ const void *src, int count) ++{ ++ int ret = sdio_memcpy_toio(self->sdio_func, addr, (void *)src, count); ++// printk("sdio_memcpy_toio 0x%x:%d ret %d\n", addr, count, ret); ++// print_hex_dump_bytes("sdio write", 0, src, min(count,32)); ++ return ret; ++} ++ ++void sdio_lock(struct xradio_common* self) ++{ ++ sdio_claim_host(self->sdio_func); ++} ++ ++void sdio_unlock(struct xradio_common *self) ++{ ++ sdio_release_host(self->sdio_func); ++} ++ ++size_t sdio_align_len(struct xradio_common *self, size_t size) ++{ ++ return sdio_align_size(self->sdio_func, size); ++} ++ ++int sdio_set_blk_size(struct xradio_common *self, size_t size) ++{ ++ return sdio_set_block_size(self->sdio_func, size); ++} ++ ++extern void xradio_irq_handler(struct xradio_common*); ++ ++static irqreturn_t sdio_irq_handler(int irq, void *dev_id) ++{ ++ struct sdio_func *func = (struct sdio_func*) dev_id; ++ struct xradio_common *self = sdio_get_drvdata(func); ++ if (self != NULL) ++ xradio_irq_handler(self); ++ return IRQ_HANDLED; ++} ++ ++static int sdio_enableint(struct sdio_func* func) ++{ ++ int ret = 0; ++ u8 cccr; ++ int func_num; ++ ++ sdio_claim_host(func); ++ ++ /* Hack to access Fuction-0 */ ++ func_num = func->num; ++ func->num = 0; ++ cccr = sdio_readb(func, SDIO_CCCR_IENx, &ret); ++ cccr |= BIT(0); /* Master interrupt enable ... */ ++ cccr |= BIT(func_num); /* ... for our function */ ++ sdio_writeb(func, cccr, SDIO_CCCR_IENx, &ret); ++ ++ /* Restore the WLAN function number */ ++ func->num = func_num; ++ ++ sdio_release_host(func); ++ ++ return ret; ++} ++ ++int sdio_pm(struct xradio_common *self, bool suspend) ++{ ++ int ret = 0; ++ if (suspend) { ++ /* Notify SDIO that XRADIO will remain powered during suspend */ ++ ret = sdio_set_host_pm_flags(self->sdio_func, MMC_PM_KEEP_POWER); ++ if (ret) ++ dev_dbg(&self->sdio_func->dev, "Error setting SDIO pm flags: %i\n", ret); ++ } ++ ++ return ret; ++} ++ ++//for sdio debug 2015-5-26 11:01:21 ++#if (defined(CONFIG_XRADIO_DEBUGFS)) ++u32 dbg_sdio_clk = 0; ++static int sdio_set_clk(struct sdio_func *func, u32 clk) ++{ ++ if (func) { ++ if (func->card->host->ops->set_ios && clk >= 1000000) { //set min to 1M ++ sdio_claim_host(func); ++ func->card->host->ios.clock = (clk < 50000000) ? clk : 50000000; ++ func->card->host->ops->set_ios(func->card->host, &func->card->host->ios); ++ sdio_release_host(func); ++ dev_dbg(&func->dev, "change mmc clk=%d\n", ++ func->card->host->ios.clock); ++ } else { ++ dev_dbg(&func->dev, "fail change mmc clk=%d\n", clk); ++ } ++ } ++ return 0; ++ ++} ++#endif ++ ++static const struct of_device_id xradio_sdio_of_match_table[] = { ++ { .compatible = "xradio,xr819" }, ++ { } ++}; ++ ++static int xradio_probe_of(struct sdio_func *func) ++{ ++ struct device *dev = &func->dev; ++ struct device_node *np = dev->of_node; ++ const struct of_device_id *of_id; ++ int irq; ++ ++ of_id = of_match_node(xradio_sdio_of_match_table, np); ++ if (!of_id) ++ return -ENODEV; ++ ++ //pdev_data->family = of_id->data; ++ ++ irq = irq_of_parse_and_map(np, 0); ++ if (!irq) { ++ dev_err(dev, "No irq in platform data\n"); ++ return -EINVAL; ++ } ++ ++ devm_request_irq(dev, irq, sdio_irq_handler, 0, "xradio", func); ++ ++ return 0; ++} ++ ++/* Probe Function to be called by SDIO stack when device is discovered */ ++static int sdio_probe(struct sdio_func *func, ++ const struct sdio_device_id *id) ++{ ++ dev_dbg(&func->dev, "XRadio Device:sdio clk=%d\n", ++ func->card->host->ios.clock); ++ dev_dbg(&func->dev, "sdio func->class=%x\n", func->class); ++ dev_dbg(&func->dev, "sdio_vendor: 0x%04x\n", func->vendor); ++ dev_dbg(&func->dev, "sdio_device: 0x%04x\n", func->device); ++ dev_dbg(&func->dev, "Function#: 0x%04x\n", func->num); ++ ++#if (defined(CONFIG_XRADIO_DEBUGFS)) ++ if (dbg_sdio_clk) ++ sdio_set_clk(func, dbg_sdio_clk); ++#endif ++ ++#if 0 //for odly and sdly debug. ++{ ++ u32 sdio_param = 0; ++ sdio_param = readl(__io_address(0x01c20088)); ++ sdio_param &= ~(0xf<<8); ++ sdio_param |= 3<<8; ++ sdio_param &= ~(0xf<<20); ++ sdio_param |= s_dly<<20; ++ writel(sdio_param, __io_address(0x01c20088)); ++ sbus_printk(XRADIO_DBG_ALWY, "%s: 0x01c20088=0x%08x\n", __func__, sdio_param); ++} ++#endif ++ ++ xradio_probe_of(func); ++ ++ func->card->quirks |= MMC_QUIRK_BROKEN_BYTE_MODE_512; ++ sdio_claim_host(func); ++ sdio_enable_func(func); ++ sdio_release_host(func); ++ ++ sdio_enableint(func); ++ ++ xradio_core_init(func); ++ ++ try_module_get(func->dev.driver->owner); ++ return 0; ++} ++/* Disconnect Function to be called by SDIO stack when ++ * device is disconnected */ ++static void sdio_remove(struct sdio_func *func) ++{ ++ sdio_claim_host(func); ++ sdio_disable_func(func); ++ sdio_release_host(func); ++ module_put(func->dev.driver->owner); ++} ++ ++static int sdio_suspend(struct device *dev) ++{ ++ int ret = 0; ++ /* ++ struct sdio_func *func = dev_to_sdio_func(dev); ++ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); ++ if (ret) ++ sbus_printk(XRADIO_DBG_ERROR, "set MMC_PM_KEEP_POWER error\n"); ++ */ ++ return ret; ++} ++ ++static int sdio_resume(struct device *dev) ++{ ++ return 0; ++} ++ ++static const struct dev_pm_ops sdio_pm_ops = { ++ .suspend = sdio_suspend, ++ .resume = sdio_resume, ++}; ++ ++static struct sdio_driver sdio_driver = { ++ .name = "xradio_wlan", ++ .id_table = xradio_sdio_ids, ++ .probe = sdio_probe, ++ .remove = sdio_remove, ++ .drv = { ++ .owner = THIS_MODULE, ++ .pm = &sdio_pm_ops, ++ } ++}; ++ ++ ++int xradio_sdio_register(){ ++ return sdio_register_driver(&sdio_driver); ++} ++ ++void xradio_sdio_unregister(){ ++ sdio_unregister_driver(&sdio_driver); ++} ++ ++MODULE_DEVICE_TABLE(sdio, xradio_sdio_ids); +diff --git a/drivers/net/wireless/xradio/sdio.h b/drivers/net/wireless/xradio/sdio.h +new file mode 100644 +index 00000000..ea3c45a0 +--- /dev/null ++++ b/drivers/net/wireless/xradio/sdio.h +@@ -0,0 +1,17 @@ ++#ifndef __XRADIO_SDIO_H ++#define __XRADIO_SDIO_H ++ ++size_t sdio_align_len(struct xradio_common *self, size_t size); ++void sdio_lock(struct xradio_common *self); ++void sdio_unlock(struct xradio_common *self); ++int sdio_set_blk_size(struct xradio_common *self, size_t size); ++int sdio_data_read(struct xradio_common *self, unsigned int addr, void *dst, ++ int count); ++int sdio_data_write(struct xradio_common *self, unsigned int addr, const void *src, ++ int count); ++int sdio_pm(struct xradio_common *self, bool suspend); ++ ++int xradio_sdio_register(void); ++void xradio_sdio_unregister(void); ++ ++#endif +diff --git a/drivers/net/wireless/xradio/sta.c b/drivers/net/wireless/xradio/sta.c +new file mode 100644 +index 00000000..e9820b8a +--- /dev/null ++++ b/drivers/net/wireless/xradio/sta.c +@@ -0,0 +1,2987 @@ ++/* ++ * STA APIs for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "sta.h" ++#include "ap.h" ++#include "keys.h" ++#include "fwio.h" ++#include "bh.h" ++#include "wsm.h" ++#ifdef ROAM_OFFLOAD ++#include ++#endif /*ROAM_OFFLOAD*/ ++#ifdef CONFIG_XRADIO_TESTMODE ++#include "nl80211_testmode_msg_copy.h" ++#include ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++#include "net/mac80211.h" ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++#include ++#endif ++ ++#define WEP_ENCRYPT_HDR_SIZE 4 ++#define WEP_ENCRYPT_TAIL_SIZE 4 ++#define WPA_ENCRYPT_HDR_SIZE 8 ++#define WPA_ENCRYPT_TAIL_SIZE 12 ++#define WPA2_ENCRYPT_HDR_SIZE 8 ++#define WPA2_ENCRYPT_TAIL_SIZE 8 ++#define WAPI_ENCRYPT_HDR_SIZE 18 ++#define WAPI_ENCRYPT_TAIL_SIZE 16 ++#define MAX_ARP_REPLY_TEMPLATE_SIZE 120 ++#ifdef CONFIG_XRADIO_TESTMODE ++const int xradio_1d_to_ac[8] = { ++ IEEE80211_AC_BE, ++ IEEE80211_AC_BK, ++ IEEE80211_AC_BK, ++ IEEE80211_AC_BE, ++ IEEE80211_AC_VI, ++ IEEE80211_AC_VI, ++ IEEE80211_AC_VO, ++ IEEE80211_AC_VO ++}; ++ ++/** ++ * enum xradio_ac_numbers - AC numbers as used in xradio ++ * @XRADIO_AC_VO: voice ++ * @XRADIO_AC_VI: video ++ * @XRADIO_AC_BE: best effort ++ * @XRADIO_AC_BK: background ++ */ ++enum xradio_ac_numbers { ++ XRADIO_AC_VO = 0, ++ XRADIO_AC_VI = 1, ++ XRADIO_AC_BE = 2, ++ XRADIO_AC_BK = 3, ++}; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++static inline void __xradio_free_event_queue(struct list_head *list) ++{ ++ while (!list_empty(list)) { ++ struct xradio_wsm_event *event = ++ list_first_entry(list, struct xradio_wsm_event,link); ++ list_del(&event->link); ++ kfree(event); ++ } ++} ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++/* User priority to WSM queue mapping */ ++const int xradio_priority_to_queueId[8] = { ++ WSM_QUEUE_BEST_EFFORT, ++ WSM_QUEUE_BACKGROUND, ++ WSM_QUEUE_BACKGROUND, ++ WSM_QUEUE_BEST_EFFORT, ++ WSM_QUEUE_VIDEO, ++ WSM_QUEUE_VIDEO, ++ WSM_QUEUE_VOICE, ++ WSM_QUEUE_VOICE ++}; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++static inline void __xradio_bf_configure(struct xradio_vif *priv) ++{ ++ priv->bf_table.numOfIEs = __cpu_to_le32(3); ++ priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC; ++ priv->bf_table.entry[0].actionFlags = ++ WSM_BEACON_FILTER_IE_HAS_CHANGED | ++ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | ++ WSM_BEACON_FILTER_IE_HAS_APPEARED; ++ ++ priv->bf_table.entry[0].oui[0] = 0x50; ++ priv->bf_table.entry[0].oui[1] = 0x6F; ++ priv->bf_table.entry[0].oui[2] = 0x9A; ++ ++ priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO; ++ priv->bf_table.entry[1].actionFlags = ++ WSM_BEACON_FILTER_IE_HAS_CHANGED | ++ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | ++ WSM_BEACON_FILTER_IE_HAS_APPEARED; ++ ++ priv->bf_table.entry[2].ieId = WLAN_EID_HT_OPERATION; ++ priv->bf_table.entry[2].actionFlags = ++ WSM_BEACON_FILTER_IE_HAS_CHANGED | ++ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | ++ WSM_BEACON_FILTER_IE_HAS_APPEARED; ++ ++ priv->bf_control.enabled = WSM_BEACON_FILTER_ENABLE; ++} ++ ++/* ******************************************************************** */ ++/* STA API */ ++ ++int xradio_start(struct ieee80211_hw *dev) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ int ret = 0; ++ ++ ++ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, ++ hw_priv->driver_ready, 3*HZ) <= 0) { ++ wiphy_err(dev->wiphy, "driver is not ready!\n"); ++ return -ETIMEDOUT; ++ } ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ spin_lock_bh(&hw_priv->tsm_lock); ++ memset(&hw_priv->tsm_stats, 0, sizeof(struct xr_tsm_stats)); ++ memset(&hw_priv->tsm_info, 0, sizeof(struct xradio_tsm_info)); ++ spin_unlock_bh(&hw_priv->tsm_lock); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ memcpy(hw_priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); ++ hw_priv->softled_state = 0; ++ ++ ret = xradio_setup_mac(hw_priv); ++ if (SYS_WARN(ret)) { ++ wiphy_err(dev->wiphy, "xradio_setup_mac failed(%d)\n", ret); ++ goto out; ++ } ++ ++out: ++ mutex_unlock(&hw_priv->conf_mutex); ++ return ret; ++} ++ ++void xradio_stop(struct ieee80211_hw *dev) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_vif *priv = NULL; ++ LIST_HEAD(list); ++ int i; ++ ++ wsm_lock_tx(hw_priv); ++ while (down_trylock(&hw_priv->scan.lock)) { ++ /* Scan is in progress. Force it to stop. */ ++ hw_priv->scan.req = NULL; ++ schedule(); ++ } ++ up(&hw_priv->scan.lock); ++ ++ cancel_delayed_work_sync(&hw_priv->scan.probe_work); ++ cancel_delayed_work_sync(&hw_priv->scan.timeout); ++#ifdef CONFIG_XRADIO_TESTMODE ++ cancel_delayed_work_sync(&hw_priv->advance_scan_timeout); ++#endif ++ flush_workqueue(hw_priv->workqueue); ++ del_timer_sync(&hw_priv->ba_timer); ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ hw_priv->softled_state = 0; ++ /* xradio_set_leds(hw_priv); */ ++ ++ spin_lock(&hw_priv->event_queue_lock); ++ list_splice_init(&hw_priv->event_queue, &list); ++ spin_unlock(&hw_priv->event_queue_lock); ++ __xradio_free_event_queue(&list); ++ ++ for (i = 0; i < 4; i++) ++ xradio_queue_clear(&hw_priv->tx_queue[i], XRWL_ALL_IFS); ++ ++ /* HACK! */ ++ if (atomic_xchg(&hw_priv->tx_lock, 1) != 1) ++ wiphy_debug(dev->wiphy, "TX is force-unlocked due to stop request.\n"); ++ ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (!priv) ++ continue; ++ priv->mode = NL80211_IFTYPE_UNSPECIFIED; ++ priv->listening = false; ++ priv->delayed_link_loss = 0; ++ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; ++ cancel_delayed_work_sync(&priv->join_timeout); ++ cancel_delayed_work_sync(&priv->bss_loss_work); ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ cancel_delayed_work_sync(&priv->link_id_gc_work); ++ del_timer_sync(&priv->mcast_timeout); ++ } ++ ++ wsm_unlock_tx(hw_priv); ++ ++ mutex_unlock(&hw_priv->conf_mutex); ++} ++ ++int xradio_add_interface(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif) ++{ ++ int ret; ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_vif *priv; ++ struct xradio_vif **drv_priv = (void *)vif->drv_priv; ++#ifndef P2P_MULTIVIF ++ int i; ++ if (atomic_read(&hw_priv->num_vifs) >= XRWL_MAX_VIFS) ++ return -EOPNOTSUPP; ++#endif ++ ++ if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, ++ hw_priv->driver_ready, 3*HZ) <= 0) { ++ wiphy_err(dev->wiphy, "driver is not ready!\n"); ++ return -ETIMEDOUT; ++ } ++ ++ /* fix the problem that when connected,then deauth */ ++ vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; ++ vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; ++ ++ priv = xrwl_get_vif_from_ieee80211(vif); ++ atomic_set(&priv->enabled, 0); ++ ++ *drv_priv = priv; ++ /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ priv->mode = vif->type; ++ ++ spin_lock(&hw_priv->vif_list_lock); ++ if (atomic_read(&hw_priv->num_vifs) < XRWL_MAX_VIFS) { ++#ifdef P2P_MULTIVIF ++ //~dgp in this mode it looks like virtual interface 2 shouldn't ++ // be used as other parts of the driver complain ++ // but when the second interface is added it gets id 2 here ++ if (!memcmp(vif->addr, hw_priv->addresses[0].addr, ETH_ALEN)) { ++ priv->if_id = 0; ++ } else if (!memcmp(vif->addr, hw_priv->addresses[1].addr, ++ ETH_ALEN)) { ++ priv->if_id = 1;//2; ++ } else if (!memcmp(vif->addr, hw_priv->addresses[2].addr, ++ ETH_ALEN)) { ++ priv->if_id = 2;//1; ++ } ++ wiphy_debug(dev->wiphy, "if_id %d mac %pM\n", ++ priv->if_id, vif->addr); ++#else ++ for (i = 0; i < XRWL_MAX_VIFS; i++) ++ if (!memcmp(vif->addr, hw_priv->addresses[i].addr, ETH_ALEN)) ++ break; ++ if (i == XRWL_MAX_VIFS) { ++ spin_unlock(&hw_priv->vif_list_lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++ return -EINVAL; ++ } ++ priv->if_id = i; ++#endif ++ hw_priv->if_id_slot |= BIT(priv->if_id); ++ priv->hw_priv = hw_priv; ++ priv->hw = dev; ++ priv->vif = vif; ++ hw_priv->vif_list[priv->if_id] = vif; ++ atomic_inc(&hw_priv->num_vifs); ++ } else { ++ spin_unlock(&hw_priv->vif_list_lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++ return -EOPNOTSUPP; ++ } ++ spin_unlock(&hw_priv->vif_list_lock); ++ /* TODO:COMBO :Check if MAC address matches the one expected by FW */ ++ memcpy(hw_priv->mac_addr, vif->addr, ETH_ALEN); ++ ++ /* Enable auto-calibration */ ++ /* Exception in subsequent channel switch; disabled. ++ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, ++ &auto_calibration_mode, sizeof(auto_calibration_mode))); ++ */ ++ wiphy_debug(dev->wiphy, "Interface ID:%d of type:%d added\n", priv->if_id, priv->mode); ++ mutex_unlock(&hw_priv->conf_mutex); ++ ++ xradio_vif_setup(priv); ++ ++ ret = SYS_WARN(xradio_setup_mac_pvif(priv)); ++ ++ return ret; ++} ++ ++void xradio_remove_interface(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct wsm_reset reset = { ++ .reset_statistics = true, ++ }; ++ int i; ++ bool is_htcapie = false; ++ struct xradio_vif *tmp_priv; ++ struct wsm_operational_mode mode = { ++ .power_mode = wsm_power_mode_quiescent, ++ .disableMoreFlagUsage = true, ++ }; ++ wiphy_warn(dev->wiphy, "!!! vif_id=%d\n", priv->if_id); ++ atomic_set(&priv->enabled, 0); ++ down(&hw_priv->scan.lock); ++ if(priv->join_status == XRADIO_JOIN_STATUS_STA){ ++ if (atomic_xchg(&priv->delayed_unjoin, 0)) { ++ wsm_unlock_tx(hw_priv); ++ wiphy_err(dev->wiphy, "delayed_unjoin exist!\n"); ++ } ++ cancel_work_sync(&priv->unjoin_work); ++ wsm_lock_tx(hw_priv); ++ xradio_unjoin_work(&priv->unjoin_work); ++ } ++ mutex_lock(&hw_priv->conf_mutex); ++ xradio_tx_queues_lock(hw_priv); ++ wsm_lock_tx(hw_priv); ++ switch (priv->join_status) { ++ case XRADIO_JOIN_STATUS_AP: ++ for (i = 0; priv->link_id_map; ++i) { ++ if (priv->link_id_map & BIT(i)) { ++ xrwl_unmap_link(priv, i); ++ priv->link_id_map &= ~BIT(i); ++ } ++ } ++ memset(priv->link_id_db, 0, ++ sizeof(priv->link_id_db)); ++ priv->sta_asleep_mask = 0; ++ priv->enable_beacon = false; ++ priv->tx_multicast = false; ++ priv->aid0_bit_set = false; ++ priv->buffered_multicasts = false; ++ priv->pspoll_mask = 0; ++ reset.link_id = 0; ++ wsm_reset(hw_priv, &reset, priv->if_id); ++ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); ++ xradio_for_each_vif(hw_priv, tmp_priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv) ++#else ++ if (!tmp_priv) ++#endif ++ continue; ++ if ((tmp_priv->join_status == XRADIO_JOIN_STATUS_STA) && tmp_priv->htcap) ++ is_htcapie = true; ++ } ++ ++ if (is_htcapie) { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE; ++ sta_printk(XRADIO_DBG_NIY, "AP REMOVE HTCAP 11N %d\n",hw_priv->vif0_throttle); ++ } else { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; ++ sta_printk(XRADIO_DBG_NIY, "AP REMOVE 11BG %d\n",hw_priv->vif0_throttle); ++ } ++ break; ++ case XRADIO_JOIN_STATUS_MONITOR: ++ xradio_disable_listening(priv); ++ break; ++ default: ++ break; ++ } ++ /* TODO:COMBO: Change Queue Module */ ++ __xradio_flush(hw_priv, false, priv->if_id); ++ ++ cancel_delayed_work_sync(&priv->bss_loss_work); ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ cancel_delayed_work_sync(&priv->link_id_gc_work); ++ cancel_delayed_work_sync(&priv->join_timeout); ++ cancel_delayed_work_sync(&priv->set_cts_work); ++ cancel_delayed_work_sync(&priv->pending_offchanneltx_work); ++ ++ del_timer_sync(&priv->mcast_timeout); ++ /* TODO:COMBO: May be reset of these variables "delayed_link_loss and ++ * join_status to default can be removed as dev_priv will be freed by ++ * mac80211 */ ++ priv->delayed_link_loss = 0; ++ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; ++ wsm_unlock_tx(hw_priv); ++ ++ if ((priv->if_id ==1) && (priv->mode == NL80211_IFTYPE_AP ++ || priv->mode == NL80211_IFTYPE_P2P_GO)) { ++ hw_priv->is_go_thru_go_neg = false; ++ } ++ spin_lock(&hw_priv->vif_list_lock); ++ spin_lock(&priv->vif_lock); ++ hw_priv->vif_list[priv->if_id] = NULL; ++ hw_priv->if_id_slot &= (~BIT(priv->if_id)); ++ atomic_dec(&hw_priv->num_vifs); ++ if (atomic_read(&hw_priv->num_vifs) == 0) { ++ xradio_free_keys(hw_priv); ++ memset(hw_priv->mac_addr, 0, ETH_ALEN); ++ } ++ spin_unlock(&priv->vif_lock); ++ spin_unlock(&hw_priv->vif_list_lock); ++ priv->listening = false; ++ ++ xradio_debug_release_priv(priv); ++ ++ xradio_tx_queues_unlock(hw_priv); ++ mutex_unlock(&hw_priv->conf_mutex); ++ ++ if (atomic_read(&hw_priv->num_vifs) == 0) ++ flush_workqueue(hw_priv->workqueue); ++ memset(priv, 0, sizeof(struct xradio_vif)); ++ up(&hw_priv->scan.lock); ++} ++ ++int xradio_change_interface(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif, ++ enum nl80211_iftype new_type, ++ bool p2p) ++{ ++ int ret = 0; ++ wiphy_debug(dev->wiphy, "changing interface type; new type=%d(%d), p2p=%d(%d)\n", ++ new_type, vif->type, p2p, vif->p2p); ++ if (new_type != vif->type || vif->p2p != p2p) { ++ xradio_remove_interface(dev, vif); ++ vif->type = new_type; ++ vif->p2p = p2p; ++ ret = xradio_add_interface(dev, vif); ++ } ++ ++ return ret; ++} ++ ++int xradio_config(struct ieee80211_hw *dev, u32 changed) ++{ ++ int ret = 0; ++ struct xradio_common *hw_priv = dev->priv; ++ struct ieee80211_conf *conf = &dev->conf; ++#ifdef CONFIG_XRADIO_TESTMODE ++ int max_power_level = 0; ++ int min_power_level = 0; ++#endif ++ /* TODO:COMBO: adjust to multi vif interface ++ * IEEE80211_CONF_CHANGE_IDLE is still handled per xradio_vif*/ ++ int if_id = 0; ++ struct xradio_vif *priv; ++ ++ ++ if (changed & ++ (IEEE80211_CONF_CHANGE_MONITOR|IEEE80211_CONF_CHANGE_IDLE)) { ++ /* TBD: It looks like it's transparent ++ * there's a monitor interface present -- use this ++ * to determine for example whether to calculate ++ * timestamps for packets or not, do not use instead ++ * of filter flags! */ ++ wiphy_debug(dev->wiphy, "ignore IEEE80211_CONF_CHANGE_MONITOR (%d)" ++ "IEEE80211_CONF_CHANGE_IDLE (%d)\n", ++ (changed & IEEE80211_CONF_CHANGE_MONITOR) ? 1 : 0, ++ (changed & IEEE80211_CONF_CHANGE_IDLE) ? 1 : 0); ++ return ret; ++ } ++ ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); ++ /* TODO: IEEE80211_CONF_CHANGE_QOS */ ++ /* TODO:COMBO:Change when support is available mac80211*/ ++ if (changed & IEEE80211_CONF_CHANGE_POWER) { ++ /*hw_priv->output_power = conf->power_level;*/ ++ hw_priv->output_power = 20; ++#ifdef CONFIG_XRADIO_TESTMODE ++ /* Testing if Power Level to set is out of device power range */ ++ if (conf->chan_conf->channel->band == NL80211_BAND_2GHZ) { ++ max_power_level = hw_priv->txPowerRange[0].max_power_level; ++ min_power_level = hw_priv->txPowerRange[0].min_power_level; ++ } else { ++ max_power_level = hw_priv->txPowerRange[1].max_power_level; ++ min_power_level = hw_priv->txPowerRange[1].min_power_level; ++ } ++ if (hw_priv->output_power > max_power_level) ++ hw_priv->output_power = max_power_level; ++ else if (hw_priv->output_power < min_power_level) ++ hw_priv->output_power = min_power_level; ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++ wiphy_debug(dev->wiphy, "Config Tx power=%d, but real=%d\n", ++ conf->power_level, hw_priv->output_power); ++ SYS_WARN(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, if_id)); ++ } ++ ++ if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && ++ (hw_priv->channel != conf->chandef.chan)) { ++ /* Switch Channel commented for CC Mode */ ++ struct ieee80211_channel *ch = conf->chandef.chan; ++ wiphy_debug(dev->wiphy, "Freq %d (wsm ch: %d).\n", ++ ch->center_freq, ch->hw_value); ++ /* Earlier there was a call to __xradio_flush(). ++ Removed as deemed unnecessary */ ++ hw_priv->channel = ch; ++ hw_priv->channel_changed = 1; ++ } ++ ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++ return ret; ++} ++ ++void xradio_update_filtering(struct xradio_vif *priv) ++{ ++ int ret; ++ bool bssid_filtering = !priv->rx_filter.bssid; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ static struct wsm_beacon_filter_control bf_disabled = { ++ .enabled = 0, ++ .bcn_count = 1, ++ }; ++ bool ap_mode = 0; ++ static struct wsm_beacon_filter_table bf_table_auto = { ++ .numOfIEs = __cpu_to_le32(2), ++ .entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC, ++ .entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | ++ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | ++ WSM_BEACON_FILTER_IE_HAS_APPEARED, ++ .entry[0].oui[0] = 0x50, ++ .entry[0].oui[1] = 0x6F, ++ .entry[0].oui[2] = 0x9A, ++ ++ .entry[1].ieId = WLAN_EID_HT_OPERATION, ++ .entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | ++ WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | ++ WSM_BEACON_FILTER_IE_HAS_APPEARED, ++ }; ++ static struct wsm_beacon_filter_control bf_auto = { ++ .enabled = WSM_BEACON_FILTER_ENABLE | ++ WSM_BEACON_FILTER_AUTO_ERP, ++ .bcn_count = 1, ++ }; ++ ++ ++ bf_auto.bcn_count = priv->bf_control.bcn_count; ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_PASSIVE) ++ return; ++ else if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) ++ bssid_filtering = false; ++ ++ if (priv->vif && (priv->vif->type == NL80211_IFTYPE_AP)) ++ ap_mode = true; ++ /* ++ * When acting as p2p client being connected to p2p GO, in order to ++ * receive frames from a different p2p device, turn off bssid filter. ++ * ++ * WARNING: FW dependency! ++ * This can only be used with FW WSM371 and its successors. ++ * In that FW version even with bssid filter turned off, ++ * device will block most of the unwanted frames. ++ */ ++ if (priv->vif && priv->vif->p2p) ++ bssid_filtering = false; ++ ++ ret = wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); ++ if (!ret && !ap_mode) { ++ if (priv->vif) { ++ if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type) ++ ret = wsm_set_beacon_filter_table(hw_priv, &priv->bf_table, priv->if_id); ++ else ++ ret = wsm_set_beacon_filter_table(hw_priv, &bf_table_auto, priv->if_id); ++ } else ++ SYS_WARN(1); ++ } ++ if (!ret && !ap_mode) { ++ if (priv->disable_beacon_filter) ++ ret = wsm_beacon_filter_control(hw_priv, &bf_disabled, priv->if_id); ++ else { ++ if (priv->vif) { ++ if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type) ++ ret = wsm_beacon_filter_control(hw_priv, &priv->bf_control, ++ priv->if_id); ++ else ++ ret = wsm_beacon_filter_control(hw_priv, &bf_auto, priv->if_id); ++ } else ++ SYS_WARN(1); ++ } ++ } ++ ++ if (!ret) ++ ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, priv->if_id); ++#if 0 ++ if (!ret) { ++ ret = wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); ++ } ++#endif ++ if (ret) ++ wiphy_debug(priv->hw_priv->hw->wiphy, "Update filtering failed: %d.\n", ret); ++ return; ++} ++ ++void xradio_update_filtering_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, ++ update_filtering_work); ++ ++ xradio_update_filtering(priv); ++} ++ ++void xradio_set_beacon_wakeup_period_work(struct work_struct *work) ++{ ++ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, set_beacon_wakeup_period_work); ++ ++ ++#ifdef XRADIO_USE_LONG_DTIM_PERIOD ++{ ++ int join_dtim_period_extend; ++ if (priv->join_dtim_period <= 3) { ++ join_dtim_period_extend = priv->join_dtim_period * 3; ++ } else if (priv->join_dtim_period <= 5) { ++ join_dtim_period_extend = priv->join_dtim_period * 2; ++ } else { ++ join_dtim_period_extend = priv->join_dtim_period; ++ } ++ SYS_WARN(wsm_set_beacon_wakeup_period(priv->hw_priv, ++ priv->beacon_int * join_dtim_period_extend > ++ MAX_BEACON_SKIP_TIME_MS ? 1 : join_dtim_period_extend, ++ 0, priv->if_id)); ++} ++#else ++ SYS_WARN(wsm_set_beacon_wakeup_period(priv->hw_priv, ++ priv->beacon_int * priv->join_dtim_period > ++ MAX_BEACON_SKIP_TIME_MS ? 1 :priv->join_dtim_period, ++ 0, priv->if_id)); ++#endif ++} ++ ++u64 xradio_prepare_multicast(struct ieee80211_hw *hw, ++ struct netdev_hw_addr_list *mc_list) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv = NULL; ++ static u8 broadcast_ipv6[ETH_ALEN] = { ++ 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 ++ }; ++ static u8 broadcast_ipv4[ETH_ALEN] = { ++ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 ++ }; ++ ++ int i= 0; ++ xradio_for_each_vif(hw_priv,priv,i) { ++ struct netdev_hw_addr *ha = NULL; ++ int count = 0; ++ if ((!priv)) ++ continue; ++#ifdef P2P_MULTIVIF ++ if (priv->if_id ==XRWL_GENERIC_IF_ID) ++ return netdev_hw_addr_list_count(mc_list); ++#endif ++ ++ /* Disable multicast filtering */ ++ priv->has_multicast_subscription = false; ++ memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); ++ if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) ++ return 0; ++ ++ /* Enable if requested */ ++ netdev_hw_addr_list_for_each(ha, mc_list) { ++ sta_printk(XRADIO_DBG_MSG, "multicast: %pM\n", ha->addr); ++ memcpy(&priv->multicast_filter.macAddress[count], ha->addr, ETH_ALEN); ++ if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && ++ memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) ++ priv->has_multicast_subscription = true; ++ count++; ++ } ++ if (count) { ++ priv->multicast_filter.enable = __cpu_to_le32(1); ++ priv->multicast_filter.numOfAddresses = __cpu_to_le32(count); ++ } ++ } ++ return netdev_hw_addr_list_count(mc_list); ++} ++ ++void xradio_configure_filter(struct ieee80211_hw *hw, ++ unsigned int changed_flags, ++ unsigned int *total_flags, ++ u64 multicast) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv = NULL; ++ int i = 0; ++ ++ /* delete umac warning */ ++ if (hw_priv->vif_list[0] == NULL && hw_priv->vif_list[1] == NULL ++#ifdef P2P_MULTIVIF ++ && hw_priv->vif_list[2] == NULL) ++#else ++ ) ++#endif ++ ++ *total_flags &= ~(1<<31); ++ ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if(NULL == priv) ++ continue; ++#ifdef P2P_MULTIVIF ++ if (priv->if_id == XRWL_GENERIC_IF_ID) { ++ *total_flags &= ~(1<<31); ++ continue; ++ } ++#endif ++ ++#if 0 ++ bool listening = !!(*total_flags & ++ (FIF_PROMISC_IN_BSS | ++ FIF_OTHER_BSS | ++ FIF_BCN_PRBRESP_PROMISC | ++ FIF_PROBE_REQ)); ++#endif ++ ++ *total_flags &= FIF_OTHER_BSS | ++ FIF_FCSFAIL | ++ FIF_BCN_PRBRESP_PROMISC | ++ FIF_PROBE_REQ; ++ ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ priv->rx_filter.promiscuous = 0; ++ priv->rx_filter.bssid = (*total_flags & ++ (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 1 : 0; ++ priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; ++ priv->bf_control.bcn_count = (*total_flags & ++ (FIF_BCN_PRBRESP_PROMISC | ++ FIF_PROBE_REQ)) ? 1 : 0; ++ ++ /*add for handle ap FIF_PROBE_REQ message,*/ ++ priv->rx_filter.promiscuous = 0; ++ priv->rx_filter.fcs = 0; ++ if(NL80211_IFTYPE_AP == priv->vif->type){ ++ priv->bf_control.bcn_count = 1; ++ priv->rx_filter.bssid = 1; ++ }else{ ++ priv->bf_control.bcn_count = 0; ++ priv->rx_filter.bssid = 0; ++ } ++#if 0 ++ if (priv->listening ^ listening) { ++ priv->listening = listening; ++ wsm_lock_tx(hw_priv); ++ xradio_update_listening(priv, listening); ++ wsm_unlock_tx(hw_priv); ++ } ++#endif ++ xradio_update_filtering(priv); ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++ } ++} ++ ++int xradio_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, ++ u16 queue, const struct ieee80211_tx_queue_params *params) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ int ret = 0; ++ /* To prevent re-applying PM request OID again and again*/ ++ bool old_uapsdFlags; ++ ++ wiphy_debug(dev->wiphy, "configuring tx for vif %d\n", priv->if_id); ++ ++ if (SYS_WARN(!priv)) ++ return -EOPNOTSUPP; ++ ++#ifdef P2P_MULTIVIF ++ if (priv->if_id == XRWL_GENERIC_IF_ID) ++ return 0; ++#endif ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ if (queue < dev->queues) { ++ old_uapsdFlags = priv->uapsd_info.uapsdFlags; ++ ++ WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); ++ ret = wsm_set_tx_queue_params(hw_priv, ++ &priv->tx_queue_params.params[queue], ++ queue, priv->if_id); ++ if (ret) { ++ wiphy_err(dev->wiphy, "wsm_set_tx_queue_params failed!\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ WSM_EDCA_SET(&priv->edca, queue, params->aifs, ++ params->cw_min, params->cw_max, ++ params->txop, 0xc8, params->uapsd); ++ /* sta role is not support the uapsd */ ++ if (priv->mode == NL80211_IFTYPE_STATION || ++ priv->mode == NL80211_IFTYPE_P2P_CLIENT) ++ priv->edca.params[queue].uapsdEnable = 0; ++ ++ ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); ++ if (ret) { ++ wiphy_err(dev->wiphy, "wsm_set_edca_params failed!\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ if (priv->mode == NL80211_IFTYPE_STATION) { ++ ret = xradio_set_uapsd_param(priv, &priv->edca); ++ if (!ret && priv->setbssparams_done && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ (old_uapsdFlags != priv->uapsd_info.uapsdFlags)) ++ xradio_set_pm(priv, &priv->powersave_mode); ++ } ++ } else { ++ wiphy_err(dev->wiphy, "queue is to large!\n"); ++ ret = -EINVAL; ++ } ++ ++out: ++ mutex_unlock(&hw_priv->conf_mutex); ++ return ret; ++} ++ ++int xradio_get_stats(struct ieee80211_hw *dev, ++ struct ieee80211_low_level_stats *stats) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ ++ memcpy(stats, &hw_priv->stats, sizeof(*stats)); ++ return 0; ++} ++ ++/* ++int xradio_get_tx_stats(struct ieee80211_hw *dev, ++ struct ieee80211_tx_queue_stats *stats) ++{ ++ int i; ++ struct xradio_common *priv = dev->priv; ++ ++ for (i = 0; i < dev->queues; ++i) ++ xradio_queue_get_stats(&priv->tx_queue[i], &stats[i]); ++ ++ return 0; ++} ++*/ ++ ++/* for ps debug */ ++#ifdef CONFIG_XRADIO_DEBUGFS ++u8 ps_disable = 0; ++u8 ps_idleperiod = 0; ++u8 ps_changeperiod = 0; ++#endif ++ ++int xradio_set_pm(struct xradio_vif *priv, const struct wsm_set_pm *arg) ++{ ++ struct wsm_set_pm pm = *arg; ++ ++ ++#ifdef CONFIG_XRADIO_DEBUGFS ++ if (ps_disable) ++ pm.pmMode = WSM_PSM_ACTIVE; ++ if (ps_idleperiod) { ++ pm.fastPsmIdlePeriod = ps_idleperiod << 1; ++ pm.apPsmChangePeriod = ps_changeperiod << 1; ++ } ++#endif ++ ++ if (priv->uapsd_info.uapsdFlags != 0) ++ pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG; ++ ++ if (memcmp(&pm, &priv->firmware_ps_mode, sizeof(struct wsm_set_pm))) { ++ priv->firmware_ps_mode = pm; ++ return wsm_set_pm(priv->hw_priv, &pm, priv->if_id); ++ } else { ++ return 0; ++ } ++} ++ ++void xradio_wep_key_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = container_of(work, struct xradio_vif , wep_key_work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); ++ struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; ++ __le32 wep_default_key_id = __cpu_to_le32(priv->wep_default_key_id); ++ ++ ++ SYS_BUG(queueId >= 4); ++ ++ sta_printk(XRADIO_DBG_MSG, "Setting default WEP key: %d\n", ++ priv->wep_default_key_id); ++ ++ wsm_flush_tx(hw_priv); ++ SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, ++ &wep_default_key_id, sizeof(wep_default_key_id), ++ priv->if_id)); ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_requeue(hw_priv, queue, hw_priv->pending_frame_id, true); ++#else ++ xradio_queue_requeue(queue, hw_priv->pending_frame_id, true); ++#endif ++ wsm_unlock_tx(hw_priv); ++} ++ ++int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ int ret = 0; ++ __le32 val32; ++ struct xradio_vif *priv = NULL; ++ int i =0; ++ int if_id; ++ ++ ++ xradio_for_each_vif(hw_priv,priv,i) { ++ if (!priv) ++ continue; ++ if_id = priv->if_id; ++#ifdef P2P_MULTIVIF ++ SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); ++#endif ++ ++ if (value != (u32) -1) ++ val32 = __cpu_to_le32(value); ++ else ++ val32 = 0; /* disabled */ ++ ++ /* mutex_lock(&priv->conf_mutex); */ ++ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, ++ &val32, sizeof(val32), if_id)); ++ /* mutex_unlock(&priv->conf_mutex); */ ++ } ++ return ret; ++} ++ ++/* TODO: COMBO: Flush only a particular interface specific parts */ ++int __xradio_flush(struct xradio_common *hw_priv, bool drop, int if_id) ++{ ++ int i, ret; ++ struct xradio_vif *priv = ++ __xrwl_hwpriv_to_vifpriv(hw_priv, if_id); ++ ++ ++ for (;;) { ++ /* TODO: correct flush handling is required when dev_stop. ++ * Temporary workaround: 2s ++ */ ++ if (drop) { ++ for (i = 0; i < 4; ++i) ++ xradio_queue_clear(&hw_priv->tx_queue[i],if_id); ++ } else if(!hw_priv->bh_error){ ++ ret = wait_event_timeout( ++ hw_priv->tx_queue_stats.wait_link_id_empty, ++ xradio_queue_stats_is_empty(&hw_priv->tx_queue_stats, -1, if_id), ++ 2 * HZ); ++ } else { //add by yangfh, don't wait when bh error ++ sta_printk(XRADIO_DBG_ERROR, " %s:bh_error occur.\n", __func__); ++ ret = -1; ++ break; ++ } ++ ++ if (!drop && unlikely(ret <= 0)) { ++ sta_printk(XRADIO_DBG_ERROR, " %s: timeout...\n", __func__); ++ ret = -ETIMEDOUT; ++ break; ++ } else { ++ ret = 0; ++ } ++ ++ wsm_vif_lock_tx(priv); ++ if (unlikely(!xradio_queue_stats_is_empty(&hw_priv->tx_queue_stats, ++ -1, if_id))) { ++ /* Highly unlekely: WSM requeued frames. */ ++ wsm_unlock_tx(hw_priv); ++ continue; ++ } ++ wsm_unlock_tx(hw_priv); ++ break; ++ } ++ return ret; ++} ++ ++void xradio_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop) ++{ ++ //struct xradio_vif *priv = NULL; ++ struct xradio_common *hw_priv = hw->priv; ++ int i = 0; ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ ++ /*TODO:COMBO: reenable this part of code when flush callback ++ * is implemented per vif */ ++ /*switch (hw_priv->mode) { ++ case NL80211_IFTYPE_MONITOR: ++ drop = true; ++ break; ++ case NL80211_IFTYPE_AP: ++ if (!hw_priv->enable_beacon) ++ drop = true; ++ break; ++ }*/ ++ ++ //if (!(hw_priv->if_id_slot & BIT(priv->if_id))) ++ // return; ++ ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if(NULL == priv) ++ continue; ++ if ((hw_priv->if_id_slot & BIT(priv->if_id))) ++ __xradio_flush(hw_priv, drop, priv->if_id); ++ } ++ return; ++} ++ ++int xradio_remain_on_channel(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_channel *chan, ++ int duration, enum ieee80211_roc_type type) ++{ ++ int ret = 0; ++ struct xradio_common *hw_priv = hw->priv; ++ struct xradio_vif *priv = NULL; ++ int i = 0; ++ int if_id; ++#ifdef TES_P2P_0002_ROC_RESTART ++ struct timeval TES_P2P_0002_tmval; ++#endif ++ ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++ do_gettimeofday(&TES_P2P_0002_tmval); ++ TES_P2P_0002_roc_dur = (s32)duration; ++ TES_P2P_0002_roc_sec = (s32)TES_P2P_0002_tmval.tv_sec; ++ TES_P2P_0002_roc_usec = (s32)TES_P2P_0002_tmval.tv_usec; ++#endif ++ ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if(NULL == priv) ++ continue; ++ if_id = priv->if_id; ++ ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_WARN, "ROC IN %d ch %d\n", ++ priv->if_id, chan->hw_value); ++#endif ++ /* default only p2p interface if_id can remain on */ ++ if((priv->if_id == 0) || (priv->if_id == 1)) ++ continue; ++ hw_priv->roc_if_id = priv->if_id; ++ ret = SYS_WARN(__xradio_flush(hw_priv, false, if_id)); ++ xradio_enable_listening(priv, chan); ++ ++ if (!ret) { ++ atomic_set(&hw_priv->remain_on_channel, 1); ++ queue_delayed_work(hw_priv->workqueue, &hw_priv->rem_chan_timeout, ++ duration * HZ / 1000); ++ priv->join_status = XRADIO_JOIN_STATUS_MONITOR; ++ ieee80211_ready_on_channel(hw); ++ } else { ++ hw_priv->roc_if_id = -1; ++ up(&hw_priv->scan.lock); ++ } ++ ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_WARN, "ROC OUT %d\n", priv->if_id); ++#endif ++ } ++ /* set the channel to supplied ieee80211_channel pointer, if it ++ is not set. This is to remove the crash while sending a probe res ++ in listen state. Later channel will updated on ++ IEEE80211_CONF_CHANGE_CHANNEL event*/ ++ if(!hw_priv->channel) { ++ hw_priv->channel = chan; ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ return ret; ++} ++ ++int xradio_cancel_remain_on_channel(struct ieee80211_hw *hw) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ ++ ++ sta_printk(XRADIO_DBG_NIY, "Cancel remain on channel\n"); ++#ifdef TES_P2P_0002_ROC_RESTART ++ if (TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) { ++ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; ++ sta_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE][Cancel ROC]\n"); ++ } ++#endif ++ ++ if (atomic_read(&hw_priv->remain_on_channel)) ++ cancel_delayed_work_sync(&hw_priv->rem_chan_timeout); ++ ++ if (atomic_read(&hw_priv->remain_on_channel)) ++ xradio_rem_chan_timeout(&hw_priv->rem_chan_timeout.work); ++ ++ return 0; ++} ++ ++/* ******************************************************************** */ ++/* WSM callbacks */ ++ ++void xradio_channel_switch_cb(struct xradio_common *hw_priv) ++{ ++ wsm_unlock_tx(hw_priv); ++} ++ ++void xradio_free_event_queue(struct xradio_common *hw_priv) ++{ ++ LIST_HEAD(list); ++ ++ ++ spin_lock(&hw_priv->event_queue_lock); ++ list_splice_init(&hw_priv->event_queue, &list); ++ spin_unlock(&hw_priv->event_queue_lock); ++ ++ __xradio_free_event_queue(&list); ++} ++ ++void xradio_event_handler(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, event_handler); ++ struct xradio_vif *priv; ++ struct xradio_wsm_event *event; ++ LIST_HEAD(list); ++ ++ ++ spin_lock(&hw_priv->event_queue_lock); ++ list_splice_init(&hw_priv->event_queue, &list); ++ spin_unlock(&hw_priv->event_queue_lock); ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ list_for_each_entry(event, &list, link) { ++ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, event->if_id); ++ if (!priv) { ++ sta_printk(XRADIO_DBG_WARN, "[CQM] Event for non existing " ++ "interface, ignoring.\n"); ++ continue; ++ } ++ switch (event->evt.eventId) { ++ case WSM_EVENT_ERROR: ++ /* I even don't know what is it about.. */ ++ //STUB(); ++ break; ++ case WSM_EVENT_BSS_LOST: ++ { ++ spin_lock(&priv->bss_loss_lock); ++ if (priv->bss_loss_status > XRADIO_BSS_LOSS_NONE) { ++ spin_unlock(&priv->bss_loss_lock); ++ break; ++ } ++ priv->bss_loss_status = XRADIO_BSS_LOSS_CHECKING; ++ spin_unlock(&priv->bss_loss_lock); ++ sta_printk(XRADIO_DBG_WARN, "[CQM] BSS lost, Beacon miss=%d, event=%x.\n", ++ (event->evt.eventData>>8)&0xff, event->evt.eventData&0xff); ++ ++ cancel_delayed_work_sync(&priv->bss_loss_work); ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ if (!down_trylock(&hw_priv->scan.lock)) { ++ up(&hw_priv->scan.lock); ++ priv->delayed_link_loss = 0; ++ queue_delayed_work(hw_priv->workqueue, ++ &priv->bss_loss_work, HZ/10); //100ms ++ } else { ++ /* Scan is in progress. Delay reporting. */ ++ /* Scan complete will trigger bss_loss_work */ ++ priv->delayed_link_loss = 1; ++ /* Also we're starting watchdog. */ ++ queue_delayed_work(hw_priv->workqueue, ++ &priv->bss_loss_work, 10 * HZ); ++ } ++ break; ++ } ++ case WSM_EVENT_BSS_REGAINED: ++ { ++ sta_printk(XRADIO_DBG_WARN, "[CQM] BSS regained.\n"); ++ priv->delayed_link_loss = 0; ++ spin_lock(&priv->bss_loss_lock); ++ priv->bss_loss_status = XRADIO_BSS_LOSS_NONE; ++ spin_unlock(&priv->bss_loss_lock); ++ cancel_delayed_work_sync(&priv->bss_loss_work); ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ break; ++ } ++ case WSM_EVENT_RADAR_DETECTED: ++ //STUB(); ++ break; ++ case WSM_EVENT_RCPI_RSSI: ++ { ++ /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 ++ * RSSI = RCPI / 2 - 110 */ ++ int rcpiRssi = (int)(event->evt.eventData & 0xFF); ++ int cqm_evt; ++ if (priv->cqm_use_rssi) ++ rcpiRssi = (s8)rcpiRssi; ++ else ++ rcpiRssi = rcpiRssi / 2 - 110; ++ ++ cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ? ++ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : ++ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; ++ sta_printk(XRADIO_DBG_NIY, "[CQM] RSSI event: %d", rcpiRssi); ++ ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, ++ GFP_KERNEL); ++ break; ++ } ++ case WSM_EVENT_BT_INACTIVE: ++ //STUB(); ++ break; ++ case WSM_EVENT_BT_ACTIVE: ++ //STUB(); ++ break; ++ case WSM_EVENT_INACTIVITY: ++ { ++ int link_id = ffs((u32)(event->evt.eventData)) - 1; ++ struct sk_buff *skb; ++ struct ieee80211_mgmt *deauth; ++ struct xradio_link_entry *entry = NULL; ++ ++ sta_printk(XRADIO_DBG_WARN, "Inactivity Event Recieved for " ++ "link_id %d\n", link_id); ++ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); ++ if (!skb) ++ break; ++ skb_reserve(skb, 64); ++ xrwl_unmap_link(priv, link_id); ++ deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); ++ SYS_WARN(!deauth); ++ entry = &priv->link_id_db[link_id - 1]; ++ deauth->duration = 0; ++ memcpy(deauth->da, priv->vif->addr, ETH_ALEN); ++ memcpy(deauth->sa, entry->mac/*priv->link_id_db[i].mac*/, ETH_ALEN); ++ memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN); ++ deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | ++ IEEE80211_STYPE_DEAUTH | ++ IEEE80211_FCTL_TODS); ++ deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; ++ deauth->seq_ctrl = 0; ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ sta_printk(XRADIO_DBG_WARN, " Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); ++ queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); ++ break; ++ } ++ case WSM_EVENT_PS_MODE_ERROR: ++ { ++ if (!priv->uapsd_info.uapsdFlags && ++ (priv->user_pm_mode != WSM_PSM_PS)) ++ { ++ struct wsm_set_pm pm = priv->powersave_mode; ++ int ret = 0; ++ ++ priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; ++ ret = xradio_set_pm (priv, &priv->powersave_mode); ++ if(ret) ++ priv->powersave_mode = pm; ++ } ++ break; ++ } ++ } ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ __xradio_free_event_queue(&list); ++} ++ ++void xradio_bss_loss_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, bss_loss_work.work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ int timeout; /* in beacons */ ++ ++ ++ timeout = priv->cqm_link_loss_count - priv->cqm_beacon_loss_count; ++ /* Skip the confimration procedure in P2P case */ ++ if (priv->vif->p2p) ++ goto report; ++ ++ spin_lock(&priv->bss_loss_lock); ++ if (priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING) { ++ //do loss report next time. ++ priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMED; ++ spin_unlock(&priv->bss_loss_lock); ++ //wait for more 1s to loss confirm. ++ queue_delayed_work(hw_priv->workqueue, &priv->bss_loss_work, 1 * HZ); ++ return; ++ } else if (priv->bss_loss_status == XRADIO_BSS_LOSS_NONE) { ++ spin_unlock(&priv->bss_loss_lock); ++ //link is alive. ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ return; ++ } else if (priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING) { ++ /* it mean no confirming packets, just report loss. */ ++ } ++ spin_unlock(&priv->bss_loss_lock); ++ ++report: ++ if (priv->cqm_beacon_loss_count) { ++ sta_printk(XRADIO_DBG_WARN, "[CQM] Beacon loss.\n"); ++ if (timeout <= 0) ++ timeout = 0; ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL); ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ } else { ++ timeout = 0; ++ } ++ ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ queue_delayed_work(hw_priv->workqueue, &priv->connection_loss_work, ++ timeout * HZ / 10); ++ ++ spin_lock(&priv->bss_loss_lock); ++ priv->bss_loss_status = XRADIO_BSS_LOSS_NONE; ++ spin_unlock(&priv->bss_loss_lock); ++} ++ ++void xradio_connection_loss_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, connection_loss_work.work); ++ sta_printk(XRADIO_DBG_ERROR, "[CQM] if%d Reporting connection loss.\n", ++ priv->if_id); ++ ieee80211_connection_loss(priv->vif); ++} ++ ++void xradio_tx_failure_work(struct work_struct *work) ++{ ++ //struct xradio_vif *priv = ++ // container_of(work, struct xradio_vif, tx_failure_work); ++ sta_printk(XRADIO_DBG_WARN, "[CQM] Reporting TX failure.\n"); ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ //ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL); ++#else /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ //(void)priv; ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++} ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++/** ++ * xradio_device_power_calc- Device power calculation ++ * from values fetch from SDD File. ++ * ++ * @priv: the private structure ++ * @Max_output_power: Power fetch from SDD ++ * @fe_cor: front-end loss correction ++ * @band: Either 2GHz or 5GHz ++ * ++ */ ++void xradio_device_power_calc(struct xradio_common *hw_priv, ++ s16 max_output_power, s16 fe_cor, u32 band) ++{ ++ s16 power_calc; ++ ++ power_calc = max_output_power - fe_cor; ++ if ((power_calc % 16) != 0) ++ power_calc += 16; ++ ++ hw_priv->txPowerRange[band].max_power_level = power_calc/16; ++ /* ++ * 12dBm is control range supported by firmware. ++ * This means absolute min power is ++ * max_power_level - 12. ++ */ ++ hw_priv->txPowerRange[band].min_power_level = ++ hw_priv->txPowerRange[band].max_power_level - 12; ++ hw_priv->txPowerRange[band].stepping = 1; ++ ++} ++#endif ++/* ******************************************************************** */ ++#ifdef CONFIG_XRADIO_TESTMODE ++#define SDD_MAX_OUTPUT_POWER_2G4_ELT_ID 0xE3 ++#define SDD_MAX_OUTPUT_POWER_5G_ELT_ID 0xE4 ++#define SDD_FE_COR_2G4_ELT_ID 0x30 ++#define SDD_FE_COR_5G_ELT_ID 0x31 ++#define MIN(x, y, z) (x < y ? (x < z ? x : z) : (y < z ? y : z)) ++static int xradio_test_pwrlevel(struct xradio_common *hw_priv) ++{ ++ int ret = -1; ++ int parsedLength = 0; ++ struct xradio_sdd *pElement = (struct xradio_sdd *)hw_priv->sdd->data; ++ ++ ++ ++ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); ++ pElement = FIND_NEXT_ELT(pElement); ++ ++ while (parsedLength <= hw_priv->sdd->size) { ++ switch (pElement->id) { ++ case SDD_MAX_OUTPUT_POWER_2G4_ELT_ID: ++ max_output_power_2G = *((s16 *)pElement->data); ++ break; ++ case SDD_FE_COR_2G4_ELT_ID: ++ fe_cor_2G = *((s16 *)pElement->data); ++ break; ++ case SDD_MAX_OUTPUT_POWER_5G_ELT_ID: ++ max_output_power_5G = *((s16 *)(pElement->data + 4)); ++ break; ++ case SDD_FE_COR_5G_ELT_ID: ++ fe_cor_5G = MIN(*((s16 *)pElement->data), ++ *((s16 *)(pElement->data + 2)), ++ *((s16 *)(pElement->data + 4))); ++ fe_cor_5G = MIN(fe_cor_5G, ++ *((s16 *)(pElement->data + 6)), ++ *((s16 *)(pElement->data + 8))); ++ break; ++ default: ++ break; ++ } ++ parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); ++ pElement = FIND_NEXT_ELT(pElement); ++ } ++ ++ /* Max/Min Power Calculation for 2.4G */ ++ xradio_device_power_calc(hw_priv, max_output_power_2G, fe_cor_2G, NL80211_BAND_2GHZ); ++ /* Max/Min Power Calculation for 5G */ ++ xradio_device_power_calc(hw_priv, max_output_power_5G, fe_cor_5G, NL80211_BAND_5GHZ); ++ for (i = 0; i < 2; ++i) { ++ sta_printk(XRADIO_DBG_MSG, "Power Values Read from SDD %s:" ++ "min_power_level[%d]: %d max_power_level[%d]:" ++ "%d stepping[%d]: %d\n", __func__, i, ++ hw_priv->txPowerRange[i].min_power_level, i, ++ hw_priv->txPowerRange[i].max_power_level, i, ++ hw_priv->txPowerRange[i].stepping); ++ } ++ return 0; ++} ++#endif ++ ++/* Internal API */ ++int xradio_setup_mac(struct xradio_common *hw_priv) ++{ ++ int ret = 0, if_id; ++ ++ ++ if (hw_priv->sdd) { ++ struct wsm_configuration cfg = { ++ .dot11StationId = &hw_priv->mac_addr[0], ++ .dpdData = hw_priv->sdd->data, ++ .dpdData_size = hw_priv->sdd->size, ++ }; ++ for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); ++ if_id++) { ++ /* Set low-power mode. */ ++ ret |= SYS_WARN(wsm_configuration(hw_priv, &cfg, ++ if_id)); ++ } ++#ifdef CONFIG_XRADIO_TESTMODE ++ /* Parse SDD file for power level test */ ++ xradio_test_pwrlevel(hw_priv); ++#endif ++ /* wsm_configuration only once, so release it */ ++ release_firmware(hw_priv->sdd); ++ hw_priv->sdd = NULL; ++ } ++ ++ /* BUG:TX output power is not set untill config_xradio is called. ++ * This would lead to 0 power set in fw and would effect scan & p2p-find ++ * Setting to default value here from sdd which would be overwritten when ++ * we make connection to AP.This value is used only during scan & p2p-ops ++ * untill AP connection is made */ ++ /*BUG:TX output power: Hardcoding to 20dbm if CCX is not enabled*/ ++ /*TODO: This might change*/ ++ if (!hw_priv->output_power) ++ hw_priv->output_power=20; ++ sta_printk(XRADIO_DBG_MSG, "%s output power %d\n",__func__,hw_priv->output_power); ++ ++ return ret; ++} ++ ++void xradio_pending_offchanneltx_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, pending_offchanneltx_work.work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ mutex_lock(&hw_priv->conf_mutex); ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_WARN, "OFFCHAN PEND IN\n"); ++#endif ++ xradio_disable_listening(priv); ++ hw_priv->roc_if_id = -1; ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_WARN, "OFFCHAN PEND OUT\n"); ++#endif ++ up(&hw_priv->scan.lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++} ++ ++void xradio_offchannel_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, offchannel_work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); ++ struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; ++ ++ ++ SYS_BUG(queueId >= 4); ++ SYS_BUG(!hw_priv->channel); ++ ++ if (unlikely(down_trylock(&hw_priv->scan.lock))) { ++ int ret; ++ sta_printk(XRADIO_DBG_ERROR, "xradio_offchannel_work***** drop frame\n"); ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_remove(hw_priv, queue, ++ hw_priv->pending_frame_id); ++#else ++ ret = xradio_queue_remove(queue, hw_priv->pending_frame_id); ++#endif ++ if (ret) ++ sta_printk(XRADIO_DBG_ERROR, "xradio_offchannel_work: " ++ "queue_remove failed %d\n", ret); ++ wsm_unlock_tx(hw_priv); ++ //workaround by yangfh ++ LOG_FILE(1, "xradio_offchannel_work error\n"); ++ up(&hw_priv->scan.lock); ++ ieee80211_connection_loss(priv->vif); ++ sta_printk(XRADIO_DBG_ERROR,"lock %d\n", hw_priv->scan.lock.count); ++ ++ return; ++ } ++ mutex_lock(&hw_priv->conf_mutex); ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_WARN, "OFFCHAN WORK IN %d\n", priv->if_id); ++#endif ++ hw_priv->roc_if_id = priv->if_id; ++ if (likely(!priv->join_status)) { ++ wsm_vif_flush_tx(priv); ++ xradio_enable_listening(priv, hw_priv->channel); ++ /* xradio_update_filtering(priv); */ ++ } ++ if (unlikely(!priv->join_status)) ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_remove(hw_priv, queue, ++ hw_priv->pending_frame_id); ++#else ++ xradio_queue_remove(queue, hw_priv->pending_frame_id); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ else ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_requeue(hw_priv, queue, ++ hw_priv->pending_frame_id, false); ++#else ++ xradio_queue_requeue(queue, hw_priv->pending_frame_id, false); ++#endif ++ ++ queue_delayed_work(hw_priv->workqueue, ++ &priv->pending_offchanneltx_work, 204 * HZ/1000); ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_WARN, "OFFCHAN WORK OUT %d\n", priv->if_id); ++#endif ++ mutex_unlock(&hw_priv->conf_mutex); ++ wsm_unlock_tx(hw_priv); ++} ++ ++void xradio_join_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, join_work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); ++ struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; ++ const struct xradio_txpriv *txpriv = NULL; ++ struct sk_buff *skb = NULL; ++ const struct wsm_tx *wsm; ++ const struct ieee80211_hdr *frame; ++ const u8 *bssid; ++ struct cfg80211_bss *bss; ++ const u8 *ssidie; ++ const u8 *dtimie; ++ const struct ieee80211_tim_ie *tim = NULL; ++ struct wsm_protected_mgmt_policy mgmt_policy; ++ struct wsm_operational_mode mode = { ++ .power_mode = wsm_power_mode_quiescent, ++ .disableMoreFlagUsage = true, ++ }; ++ //struct wsm_reset reset = { ++ // .reset_statistics = true, ++ //}; ++ ++ ++ ++ SYS_BUG(queueId >= 4); ++ if (xradio_queue_get_skb(queue, hw_priv->pending_frame_id, ++ &skb, &txpriv)) { ++ wsm_unlock_tx(hw_priv); ++ return; ++ } ++ wsm = (struct wsm_tx *)&skb->data[0]; ++ frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; ++ bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ ++ ++ SYS_BUG(!wsm); ++ SYS_BUG(!hw_priv->channel); ++ ++ if (unlikely(priv->join_status)) { ++ sta_printk(XRADIO_DBG_WARN, "%s, pre join_status=%d.\n", ++ __func__, priv->join_status); ++ wsm_lock_tx(hw_priv); ++ xradio_unjoin_work(&priv->unjoin_work); ++ } ++ ++ cancel_delayed_work_sync(&priv->join_timeout); ++ ++ bss = cfg80211_get_bss(hw_priv->hw->wiphy, hw_priv->channel, ++ bssid, NULL, 0, 0, 0); ++ if (!bss) { ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_remove(hw_priv, queue, hw_priv->pending_frame_id); ++#else ++ xradio_queue_remove(queue, hw_priv->pending_frame_id); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ wsm_unlock_tx(hw_priv); ++ return; ++ } ++ ssidie = cfg80211_find_ie(WLAN_EID_SSID, ++ bss->ies->data, ++ bss->ies->len); ++ dtimie = cfg80211_find_ie(WLAN_EID_TIM, ++ bss->ies->data, ++ bss->ies->len); ++ if (dtimie) ++ tim = (struct ieee80211_tim_ie *)&dtimie[2]; ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ { ++ struct wsm_join join = { ++ .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? ++ WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, ++ /* default changed to LONG, by HuangLu, fix 2/5.5/11m tx fail*/ ++ .preambleType = WSM_JOIN_PREAMBLE_LONG, ++ .probeForJoin = 1, ++ /* dtimPeriod will be updated after association */ ++ .dtimPeriod = 1, ++ .beaconInterval = bss->beacon_interval, ++ }; ++ ++ if (priv->if_id) ++ join.flags |= WSM_FLAG_MAC_INSTANCE_1; ++ else ++ join.flags &= ~WSM_FLAG_MAC_INSTANCE_1; ++ ++ /* BT Coex related changes */ ++ if (hw_priv->is_BT_Present) { ++ if (((hw_priv->conf_listen_interval * 100) % ++ bss->beacon_interval) == 0) ++ priv->listen_interval = ++ ((hw_priv->conf_listen_interval * 100) / ++ bss->beacon_interval); ++ else ++ priv->listen_interval = ++ ((hw_priv->conf_listen_interval * 100) / ++ bss->beacon_interval + 1); ++ } ++ ++ if (tim && tim->dtim_period > 1) { ++ join.dtimPeriod = tim->dtim_period; ++ priv->join_dtim_period = tim->dtim_period; ++ } ++ priv->beacon_int = bss->beacon_interval; ++ sta_printk(XRADIO_DBG_NIY, "Join DTIM: %d, interval: %d\n", ++ join.dtimPeriod, priv->beacon_int); ++ ++ hw_priv->is_go_thru_go_neg = false; ++ join.channelNumber = hw_priv->channel->hw_value; ++ ++ /* basicRateSet will be updated after association. ++ Currently these values are hardcoded */ ++ if (hw_priv->channel->band == NL80211_BAND_5GHZ) { ++ join.band = WSM_PHY_BAND_5G; ++ join.basicRateSet = 64; /*6 mbps*/ ++ }else{ ++ join.band = WSM_PHY_BAND_2_4G; ++ join.basicRateSet = 7; /*1, 2, 5.5 mbps*/ ++ } ++ memcpy(&join.bssid[0], bssid, sizeof(join.bssid)); ++ memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid)); ++ ++ if (ssidie) { ++ join.ssidLength = ssidie[1]; ++ if (SYS_WARN(join.ssidLength > sizeof(join.ssid))) ++ join.ssidLength = sizeof(join.ssid); ++ memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); ++ if(strstr(&join.ssid[0],"5.1.4")) ++ msleep(200); ++#ifdef ROAM_OFFLOAD ++ if((priv->vif->type == NL80211_IFTYPE_STATION)) { ++ priv->ssid_length = join.ssidLength; ++ memcpy(priv->ssid, &join.ssid[0], priv->ssid_length); ++ } ++#endif /*ROAM_OFFLOAD*/ ++ } ++ ++ if (priv->vif->p2p) { ++ join.flags |= WSM_JOIN_FLAGS_P2P_GO; ++#ifdef P2P_MULTIVIF ++ join.flags |= (1 << 6); ++#endif ++ join.basicRateSet = ++ xradio_rate_mask_to_wsm(hw_priv, 0xFF0); ++ } ++ ++ wsm_flush_tx(hw_priv); ++ ++ /* Queue unjoin if not associated in 3 sec. */ ++ queue_delayed_work(hw_priv->workqueue, ++ &priv->join_timeout, 3 * HZ); ++ /*Stay Awake for Join Timeout*/ ++ xradio_pm_stay_awake(&hw_priv->pm_state, 3 * HZ); ++ ++ xradio_disable_listening(priv); ++ ++ //SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id)); ++ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ 0, hw_priv->ba_tid_mask, priv->if_id)); ++ spin_lock_bh(&hw_priv->ba_lock); ++ hw_priv->ba_ena = false; ++ hw_priv->ba_cnt = 0; ++ hw_priv->ba_acc = 0; ++ hw_priv->ba_hist = 0; ++ hw_priv->ba_cnt_rx = 0; ++ hw_priv->ba_acc_rx = 0; ++ spin_unlock_bh(&hw_priv->ba_lock); ++ ++ mgmt_policy.protectedMgmtEnable = 0; ++ mgmt_policy.unprotectedMgmtFramesAllowed = 1; ++ mgmt_policy.encryptionForAuthFrame = 1; ++ wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, priv->if_id); ++ ++ if (wsm_join(hw_priv, &join, priv->if_id)) { ++ memset(&priv->join_bssid[0], ++ 0, sizeof(priv->join_bssid)); ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_remove(hw_priv, queue, ++ hw_priv->pending_frame_id); ++#else ++ xradio_queue_remove(queue, hw_priv->pending_frame_id); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ cancel_delayed_work_sync(&priv->join_timeout); ++ } else { ++ /* Upload keys */ ++#ifdef CONFIG_XRADIO_TESTMODE ++ xradio_queue_requeue(hw_priv, queue, ++ hw_priv->pending_frame_id, true); ++#else ++ xradio_queue_requeue(queue, hw_priv->pending_frame_id, ++ true); ++#endif ++ priv->join_status = XRADIO_JOIN_STATUS_STA; ++ ++ /* Due to beacon filtering it is possible that the ++ * AP's beacon is not known for the mac80211 stack. ++ * Disable filtering temporary to make sure the stack ++ * receives at least one */ ++ priv->disable_beacon_filter = true; ++ ++ } ++ xradio_update_filtering(priv); ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ cfg80211_put_bss(hw_priv->hw->wiphy,bss); ++ wsm_unlock_tx(hw_priv); ++} ++ ++void xradio_join_timeout(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, join_timeout.work); ++ sta_printk(XRADIO_DBG_WARN, "[WSM] Issue unjoin command (TMO).\n"); ++ wsm_lock_tx(priv->hw_priv); ++ xradio_unjoin_work(&priv->unjoin_work); ++} ++ ++void xradio_unjoin_work(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, unjoin_work); ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ struct wsm_reset reset = { ++ .reset_statistics = true, ++ }; ++ bool is_htcapie = false; ++ int i; ++ struct xradio_vif *tmp_priv; ++ struct wsm_operational_mode mode = { ++ .power_mode = wsm_power_mode_quiescent, ++ .disableMoreFlagUsage = true, ++ }; ++ ++ ++ //add by yangfh. ++ hw_priv->connet_time[priv->if_id] = 0; ++#ifdef AP_HT_COMPAT_FIX ++ priv->ht_compat_det &= ~1; ++ priv->ht_compat_cnt = 0; ++#endif ++ ++ del_timer_sync(&hw_priv->ba_timer); ++ mutex_lock(&hw_priv->conf_mutex); ++ if (unlikely(atomic_read(&hw_priv->scan.in_progress))) { ++ if (atomic_xchg(&priv->delayed_unjoin, 1)) { ++ sta_printk(XRADIO_DBG_NIY, ++ "%s: Delayed unjoin " ++ "is already scheduled.\n", ++ __func__); ++ wsm_unlock_tx(hw_priv); ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ return; ++ } ++ ++ if (priv->join_status && ++ priv->join_status > XRADIO_JOIN_STATUS_STA) { ++ sta_printk(XRADIO_DBG_ERROR, ++ "%s: Unexpected: join status: %d\n", ++ __func__, priv->join_status); ++ SYS_BUG(1); ++ } ++ if (priv->join_status) { ++ cancel_work_sync(&priv->update_filtering_work); ++ cancel_work_sync(&priv->set_beacon_wakeup_period_work); ++ memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); ++ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; ++ ++ /* Unjoin is a reset. */ ++ wsm_flush_tx(hw_priv); ++ SYS_WARN(wsm_keep_alive_period(hw_priv, 0, priv->if_id)); ++ SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id)); ++ SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); ++ SYS_WARN(wsm_set_output_power(hw_priv, ++ hw_priv->output_power * 10, priv->if_id)); ++ priv->join_dtim_period = 0; ++ priv->cipherType = 0; ++ SYS_WARN(xradio_setup_mac_pvif(priv)); ++ xradio_free_event_queue(hw_priv); ++ cancel_work_sync(&hw_priv->event_handler); ++ cancel_delayed_work_sync(&priv->connection_loss_work); ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ 0, hw_priv->ba_tid_mask, priv->if_id)); ++ priv->disable_beacon_filter = false; ++ xradio_update_filtering(priv); ++ priv->setbssparams_done = false; ++ memset(&priv->association_mode, 0, ++ sizeof(priv->association_mode)); ++ memset(&priv->bss_params, 0, sizeof(priv->bss_params)); ++ memset(&priv->firmware_ps_mode, 0, ++ sizeof(priv->firmware_ps_mode)); ++ priv->htcap = false; ++ xradio_for_each_vif(hw_priv, tmp_priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv) ++#else ++ if (!tmp_priv) ++#endif ++ continue; ++ if ((tmp_priv->join_status == XRADIO_JOIN_STATUS_STA) && tmp_priv->htcap) ++ is_htcapie = true; ++ } ++ ++ if (is_htcapie) { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE; ++ sta_printk(XRADIO_DBG_NIY, "UNJOIN HTCAP 11N %d\n",hw_priv->vif0_throttle); ++ } else { ++ hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; ++ hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; ++ sta_printk(XRADIO_DBG_NIY, "UNJOIN 11BG %d\n",hw_priv->vif0_throttle); ++ } ++ sta_printk(XRADIO_DBG_NIY, "Unjoin.\n"); ++ } ++ mutex_unlock(&hw_priv->conf_mutex); ++ wsm_unlock_tx(hw_priv); ++} ++ ++int xradio_enable_listening(struct xradio_vif *priv, ++ struct ieee80211_channel *chan) ++{ ++ /* TODO:COMBO: Channel is common to HW currently in mac80211. ++ Change the code below once channel is made per VIF */ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct wsm_start start = { ++#ifdef P2P_MULTIVIF ++ .mode = WSM_START_MODE_P2P_DEV | (priv->if_id ? (1 << 4) : 0), ++#else ++ .mode = WSM_START_MODE_P2P_DEV | (priv->if_id << 4), ++#endif ++ .band = (chan->band == NL80211_BAND_5GHZ) ? ++ WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, ++ .channelNumber = chan->hw_value, ++ .beaconInterval = 100, ++ .DTIMPeriod = 1, ++ .probeDelay = 0, ++ .basicRateSet = 0x0F, ++ }; ++ ++ ++ if(priv->if_id != 2) { ++ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); ++ return 0; ++ } ++ if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) ++ return 0; ++ if (priv->join_status == XRADIO_JOIN_STATUS_PASSIVE) ++ priv->join_status = XRADIO_JOIN_STATUS_MONITOR; ++ ++ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); ++ ++ return wsm_start(hw_priv, &start, XRWL_GENERIC_IF_ID); ++} ++ ++int xradio_disable_listening(struct xradio_vif *priv) ++{ ++ int ret; ++ struct wsm_reset reset = { ++ .reset_statistics = true, ++ }; ++ ++ ++ if(priv->if_id != 2) { ++ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); ++ return 0; ++ } ++ priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; ++ ++ SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); ++ ++ if (priv->hw_priv->roc_if_id == -1) ++ return 0; ++ ++ ret = wsm_reset(priv->hw_priv, &reset, XRWL_GENERIC_IF_ID); ++ return ret; ++} ++ ++/* TODO:COMBO:UAPSD will be supported only on one interface */ ++int xradio_set_uapsd_param(struct xradio_vif *priv, ++ const struct wsm_edca_params *arg) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ int ret; ++ u16 uapsdFlags = 0; ++ ++ ++ /* Here's the mapping AC [queue, bit] ++ VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/ ++ ++ if (arg->params[0].uapsdEnable) ++ uapsdFlags |= 1 << 3; ++ ++ if (arg->params[1].uapsdEnable) ++ uapsdFlags |= 1 << 2; ++ ++ if (arg->params[2].uapsdEnable) ++ uapsdFlags |= 1 << 1; ++ ++ if (arg->params[3].uapsdEnable) ++ uapsdFlags |= 1; ++ ++ /* Currently pseudo U-APSD operation is not supported, so setting ++ * MinAutoTriggerInterval, MaxAutoTriggerInterval and ++ * AutoTriggerStep to 0 */ ++ ++ priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags); ++ priv->uapsd_info.minAutoTriggerInterval = 0; ++ priv->uapsd_info.maxAutoTriggerInterval = 0; ++ priv->uapsd_info.autoTriggerStep = 0; ++ ++ ret = wsm_set_uapsd_info(hw_priv, &priv->uapsd_info, ++ priv->if_id); ++ return ret; ++} ++ ++void xradio_ba_work(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, ba_work); ++ u8 tx_ba_tid_mask; ++ ++ ++ /* TODO:COMBO: reenable this part of code */ ++/* if (priv->join_status != XRADIO_JOIN_STATUS_STA) ++ return; ++ if (!priv->setbssparams_done) ++ return;*/ ++ ++ sta_printk(XRADIO_DBG_WARN, "BA work****\n"); ++ spin_lock_bh(&hw_priv->ba_lock); ++// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0; ++ tx_ba_tid_mask = hw_priv->ba_tid_mask; ++ spin_unlock_bh(&hw_priv->ba_lock); ++ ++ wsm_lock_tx(hw_priv); ++ ++ SYS_WARN(wsm_set_block_ack_policy(hw_priv, ++ tx_ba_tid_mask, hw_priv->ba_tid_mask, -1)); /*TODO:COMBO*/ ++ ++ wsm_unlock_tx(hw_priv); ++} ++ ++void xradio_ba_timer(unsigned long arg) ++{ ++ bool ba_ena; ++ struct xradio_common *hw_priv = (struct xradio_common *)arg; ++ ++ ++ spin_lock_bh(&hw_priv->ba_lock); ++ xradio_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc, ++ hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx); ++ ++ if (atomic_read(&hw_priv->scan.in_progress)) { ++ hw_priv->ba_cnt = 0; ++ hw_priv->ba_acc = 0; ++ hw_priv->ba_cnt_rx = 0; ++ hw_priv->ba_acc_rx = 0; ++ goto skip_statistic_update; ++ } ++ ++ if (hw_priv->ba_cnt >= XRADIO_BLOCK_ACK_CNT && ++ (hw_priv->ba_acc / hw_priv->ba_cnt >= XRADIO_BLOCK_ACK_THLD || ++ (hw_priv->ba_cnt_rx >= XRADIO_BLOCK_ACK_CNT && ++ hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >= ++ XRADIO_BLOCK_ACK_THLD))) ++ ba_ena = true; ++ else ++ ba_ena = false; ++ ++ hw_priv->ba_cnt = 0; ++ hw_priv->ba_acc = 0; ++ hw_priv->ba_cnt_rx = 0; ++ hw_priv->ba_acc_rx = 0; ++ ++ if (ba_ena != hw_priv->ba_ena) { ++ if (ba_ena || ++hw_priv->ba_hist >= XRADIO_BLOCK_ACK_HIST) { ++ hw_priv->ba_ena = ba_ena; ++ hw_priv->ba_hist = 0; ++#if 0 ++ sta_printk(XRADIO_DBG_NIY, "%s block ACK:\n", ++ ba_ena ? "enable" : "disable"); ++ queue_work(hw_priv->workqueue, &hw_priv->ba_work); ++#endif ++ } ++ } else if (hw_priv->ba_hist) ++ --hw_priv->ba_hist; ++ ++skip_statistic_update: ++ spin_unlock_bh(&hw_priv->ba_lock); ++} ++ ++int xradio_vif_setup(struct xradio_vif *priv) ++{ ++ struct xradio_common *hw_priv = priv->hw_priv; ++ int ret = 0; ++ ++ ++ //reset channel change flag, yangfh 2015-5-15 17:12:14 ++ hw_priv->channel_changed = 0; ++ /* Setup per vif workitems and locks */ ++ spin_lock_init(&priv->vif_lock); ++ INIT_WORK(&priv->join_work, xradio_join_work); ++ INIT_DELAYED_WORK(&priv->join_timeout, xradio_join_timeout); ++ INIT_WORK(&priv->unjoin_work, xradio_unjoin_work); ++ INIT_WORK(&priv->wep_key_work, xradio_wep_key_work); ++ INIT_WORK(&priv->offchannel_work, xradio_offchannel_work); ++ INIT_DELAYED_WORK(&priv->bss_loss_work, xradio_bss_loss_work); ++ INIT_DELAYED_WORK(&priv->connection_loss_work, xradio_connection_loss_work); ++ priv->bss_loss_status = XRADIO_BSS_LOSS_NONE; ++ spin_lock_init(&priv->bss_loss_lock); ++ INIT_WORK(&priv->tx_failure_work, xradio_tx_failure_work); ++ spin_lock_init(&priv->ps_state_lock); ++ INIT_DELAYED_WORK(&priv->set_cts_work, xradio_set_cts_work); ++ INIT_WORK(&priv->set_tim_work, xradio_set_tim_work); ++ INIT_WORK(&priv->multicast_start_work, xradio_multicast_start_work); ++ INIT_WORK(&priv->multicast_stop_work, xradio_multicast_stop_work); ++ INIT_WORK(&priv->link_id_work, xradio_link_id_work); ++ INIT_DELAYED_WORK(&priv->link_id_gc_work, xradio_link_id_gc_work); ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ INIT_WORK(&priv->linkid_reset_work, xradio_link_id_reset); ++#endif ++ INIT_WORK(&priv->update_filtering_work, xradio_update_filtering_work); ++ INIT_DELAYED_WORK(&priv->pending_offchanneltx_work, ++ xradio_pending_offchanneltx_work); ++ INIT_WORK(&priv->set_beacon_wakeup_period_work, ++ xradio_set_beacon_wakeup_period_work); ++#ifdef AP_HT_CAP_UPDATE ++ INIT_WORK(&priv->ht_oper_update_work, xradio_ht_oper_update_work); ++#endif ++ init_timer(&priv->mcast_timeout); ++ priv->mcast_timeout.data = (unsigned long)priv; ++ priv->mcast_timeout.function = xradio_mcast_timeout; ++ priv->setbssparams_done = false; ++ priv->power_set_true = 0; ++ priv->user_power_set_true = 0; ++ priv->user_pm_mode = 0; ++ SYS_WARN(xradio_debug_init_priv(hw_priv, priv)); ++ ++ /* Initialising the broadcast filter */ ++ memset(priv->broadcast_filter.MacAddr, 0xFF, ETH_ALEN); ++ priv->broadcast_filter.nummacaddr = 1; ++ priv->broadcast_filter.address_mode = 1; ++ priv->broadcast_filter.filter_mode = 1; ++ priv->htcap = false; ++#ifdef AP_HT_COMPAT_FIX ++ priv->ht_compat_det = 0; ++ priv->ht_compat_cnt = 0; ++#endif ++ ++ sta_printk(XRADIO_DBG_ALWY, "!!!%s: id=%d, type=%d, p2p=%d\n", ++ __func__, priv->if_id, priv->vif->type, priv->vif->p2p); ++ ++ atomic_set(&priv->enabled, 1); ++ ++#ifdef P2P_MULTIVIF ++ if (priv->if_id < 2) { ++#endif ++ /* default EDCA */ ++ WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, ++ 47, 0xc8, false); ++ WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, ++ 94, 0xc8, false); ++ ++// if(priv->vif->p2p == true) { ++ WSM_EDCA_SET(&priv->edca, 2, 0x0002, 0x0003, 0x0007, ++ 0, 0xc8, false); ++ sta_printk(XRADIO_DBG_MSG, "EDCA params Best effort for sta/p2p is " \ ++ "aifs=%u, cw_min=%u, cw_max=%u \n", ++ priv->edca.params[2].aifns, priv->edca.params[2].cwMin, ++ priv->edca.params[2].cwMax); ++#if 0 ++ }else { ++ WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, ++ 0, 0xc8, false); ++ sta_printk(XRADIO_DBG_MSG, "EDCA params Best effort for sta is " \ ++ "aifs=%u, cw_min=%u, cw_max=%u \n", ++ priv->edca.params[2].aifns, priv->edca.params[2].cwMin, ++ priv->edca.params[2].cwMax); ++ } ++#endif ++ WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, ++ 0, 0xc8, false); ++ ++ ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); ++ if (SYS_WARN(ret)) ++ goto out; ++ ++ ret = xradio_set_uapsd_param(priv, &priv->edca); ++ if (SYS_WARN(ret)) ++ goto out; ++ ++ memset(priv->bssid, ~0, ETH_ALEN); ++ priv->wep_default_key_id = -1; ++ priv->cipherType = 0; ++ priv->cqm_link_loss_count = XRADIO_LINK_LOSS_THOLD_DEF; ++ priv->cqm_beacon_loss_count = XRADIO_BSS_LOSS_THOLD_DEF; ++ ++ /* Temporary configuration - beacon filter table */ ++ __xradio_bf_configure(priv); ++#ifdef P2P_MULTIVIF ++ } ++#endif ++out: ++ return ret; ++} ++ ++int xradio_setup_mac_pvif(struct xradio_vif *priv) ++{ ++ int ret = 0; ++ /* NOTE: There is a bug in FW: it reports signal ++ * as RSSI if RSSI subscription is enabled. ++ * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ ++ /* NOTE2: RSSI based reports have been switched to RCPI, since ++ * FW has a bug and RSSI reported values are not stable, ++ * what can leads to signal level oscilations in user-end applications */ ++ struct wsm_rcpi_rssi_threshold threshold = { ++ .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | ++ WSM_RCPI_RSSI_DONT_USE_UPPER | ++ WSM_RCPI_RSSI_DONT_USE_LOWER, ++ .rollingAverageCount = 16, ++ }; ++ ++ ++ /* Remember the decission here to make sure, we will handle ++ * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */ ++ if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) ++ priv->cqm_use_rssi = true; ++ ++ ++ /* Configure RSSI/SCPI reporting as RSSI. */ ++#ifdef P2P_MULTIVIF ++ ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, priv->if_id ? 1 : 0); ++#else ++ ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, priv->if_id); ++#endif ++ return ret; ++} ++ ++void xradio_rem_chan_timeout(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, rem_chan_timeout.work); ++ int ret, if_id; ++ struct xradio_vif *priv; ++ ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++ if(TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) { ++ sta_printk(XRADIO_DBG_WARN, "[Restart rem_chan_timeout:Timeout]\n"); ++ return; ++ } ++#endif ++ ++ if (atomic_read(&hw_priv->remain_on_channel) == 0) { ++ return; ++ } ++ ieee80211_remain_on_channel_expired(hw_priv->hw); ++ ++ mutex_lock(&hw_priv->conf_mutex); ++ if_id = hw_priv->roc_if_id; ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_ERROR, "ROC TO IN %d\n", if_id); ++#endif ++ priv = __xrwl_hwpriv_to_vifpriv(hw_priv, if_id); ++ ret = SYS_WARN(__xradio_flush(hw_priv, false, if_id)); ++ if (!ret) { ++ xradio_disable_listening(priv); ++ } ++ atomic_set(&hw_priv->remain_on_channel, 0); ++ hw_priv->roc_if_id = -1; ++ ++#ifdef ROC_DEBUG ++ sta_printk(XRADIO_DBG_ERROR, "ROC TO OUT %d\n", if_id); ++#endif ++ ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++} ++const u8 *xradio_get_ie(u8 *start, size_t len, u8 ie) ++{ ++ u8 *end, *pos; ++ ++ ++ pos = start; ++ if (pos == NULL) ++ return NULL; ++ end = pos + len; ++ ++ while (pos + 1 < end) { ++ if (pos + 2 + pos[1] > end) ++ break; ++ if (pos[0] == ie) ++ return pos; ++ pos += 2 + pos[1]; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * xradio_set_macaddrfilter -called when tesmode command ++ * is for setting mac address filter ++ * ++ * @hw: the hardware ++ * @data: incoming data ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_set_macaddrfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data) ++{ ++ struct wsm_mac_addr_filter *mac_addr_filter = NULL; ++ struct wsm_mac_addr_info *addr_info = NULL; ++ u8 action_mode = 0, no_of_mac_addr = 0, i = 0; ++ int ret = 0; ++ u16 macaddrfiltersize = 0; ++ ++ ++ /* Retrieving Action Mode */ ++ action_mode = data[0]; ++ /* Retrieving number of address entries */ ++ no_of_mac_addr = data[1]; ++ ++ addr_info = (struct wsm_mac_addr_info *)&data[2]; ++ ++ /* Computing sizeof Mac addr filter */ ++ macaddrfiltersize = sizeof(*mac_addr_filter) + \ ++ (no_of_mac_addr * sizeof(struct wsm_mac_addr_info)); ++ ++ mac_addr_filter = kzalloc(macaddrfiltersize, GFP_KERNEL); ++ if (!mac_addr_filter) { ++ ret = -ENOMEM; ++ goto exit_p; ++ } ++ mac_addr_filter->action_mode = action_mode; ++ mac_addr_filter->numfilter = no_of_mac_addr; ++ ++ for (i = 0; i < no_of_mac_addr; i++) { ++ mac_addr_filter->macaddrfilter[i].address_mode = \ ++ addr_info[i].address_mode; ++ memcpy(mac_addr_filter->macaddrfilter[i].MacAddr, \ ++ addr_info[i].MacAddr , ETH_ALEN); ++ mac_addr_filter->macaddrfilter[i].filter_mode = \ ++ addr_info[i].filter_mode; ++ } ++ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_MAC_ADDR_FILTER, \ ++ mac_addr_filter, macaddrfiltersize, priv->if_id)); ++ ++ kfree(mac_addr_filter); ++exit_p: ++ return ret; ++} ++ ++/** ++ * xradio_set_arpreply -called for creating and ++ * configuring arp response template frame ++ * ++ * @hw: the hardware ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif) ++{ ++ struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); ++ struct xradio_common *hw_priv = (struct xradio_common *)hw->priv; ++ u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0; ++ bool encrypt = false; ++ int ret = 0; ++ u8 *template_frame = NULL; ++ struct ieee80211_hdr_3addr *dot11hdr = NULL; ++ struct ieee80211_snap_hdr *snaphdr = NULL; ++ struct arphdr *arp_hdr = NULL; ++ ++ ++ template_frame = kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, GFP_KERNEL); ++ if (!template_frame) { ++ sta_printk(XRADIO_DBG_ERROR, "Template frame memory failed\n"); ++ ret = -ENOMEM; ++ goto exit_p; ++ } ++ dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4]; ++ ++ framehdrlen = sizeof(*dot11hdr); ++ if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) ++ priv->cipherType = WLAN_CIPHER_SUITE_CCMP; ++ switch (priv->cipherType) { ++ ++ case WLAN_CIPHER_SUITE_WEP40: ++ case WLAN_CIPHER_SUITE_WEP104: ++ sta_printk(XRADIO_DBG_NIY, "WEP\n"); ++ encrypthdr = WEP_ENCRYPT_HDR_SIZE; ++ encrypttailsize = WEP_ENCRYPT_TAIL_SIZE; ++ encrypt = 1; ++ break; ++ ++ ++ case WLAN_CIPHER_SUITE_TKIP: ++ sta_printk(XRADIO_DBG_NIY, "WPA\n"); ++ encrypthdr = WPA_ENCRYPT_HDR_SIZE; ++ encrypttailsize = WPA_ENCRYPT_TAIL_SIZE; ++ encrypt = 1; ++ break; ++ ++ case WLAN_CIPHER_SUITE_CCMP: ++ sta_printk(XRADIO_DBG_NIY, "WPA2\n"); ++ encrypthdr = WPA2_ENCRYPT_HDR_SIZE; ++ encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE; ++ encrypt = 1; ++ break; ++ ++ case WLAN_CIPHER_SUITE_SMS4: ++ sta_printk(XRADIO_DBG_NIY, "WAPI\n"); ++ encrypthdr = WAPI_ENCRYPT_HDR_SIZE; ++ encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE; ++ encrypt = 1; ++ break; ++ ++ default: ++ encrypthdr = 0; ++ encrypttailsize = 0; ++ encrypt = 0; ++ break; ++ } ++ ++ framehdrlen += encrypthdr; ++ /* Filling the 802.11 Hdr */ ++ dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); ++ if (priv->vif->type == NL80211_IFTYPE_STATION) ++ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS); ++ else ++ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS); ++ ++ if (encrypt) ++ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP); ++ ++ if (priv->vif->bss_conf.qos) { ++ sta_printk(XRADIO_DBG_NIY, "QOS Enabled\n"); ++ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP); ++ *(u16 *)(dot11hdr + 1) = 0x0; ++ framehdrlen += 2; ++ } else { ++ dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA); ++ } ++ ++ memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); ++ memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN); ++ memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); ++ ++ /* Filling the LLC/SNAP Hdr */ ++ snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen); ++ memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \ ++ sizeof(*snaphdr)); ++ *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_ARP); ++ /* Updating the framebdylen with snaphdr and LLC hdr size */ ++ framebdylen = sizeof(*snaphdr) + 2; ++ ++ /* Filling the ARP Reply Payload */ ++ arp_hdr = (struct arphdr *)((u8 *)dot11hdr + framehdrlen + framebdylen); ++ arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); ++ arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); ++ arp_hdr->ar_hln = ETH_ALEN; ++ arp_hdr->ar_pln = 4; ++ arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); ++ ++ /* Updating the frmbdylen with Arp Reply Hdr and Arp payload size(20) */ ++ framebdylen += sizeof(*arp_hdr) + 20; ++ ++ /* Updating the framebdylen with Encryption Tail Size */ ++ framebdylen += encrypttailsize; ++ ++ /* Filling the Template Frame Hdr */ ++ template_frame[0] = WSM_FRAME_TYPE_ARP_REPLY; /* Template frame type */ ++ template_frame[1] = 0xFF; /* Rate to be fixed */ ++ ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen; ++ ++ ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \ ++ template_frame, (framehdrlen+framebdylen+4), ++ priv->if_id)); ++ kfree(template_frame); ++exit_p: ++ return ret; ++} ++ ++#ifdef ROAM_OFFLOAD ++/** ++ * xradio_testmode_event -send asynchronous event ++ * to userspace ++ * ++ * @wiphy: the wiphy ++ * @msg_id: XR msg ID ++ * @data: data to be sent ++ * @len: data length ++ * @gfp: allocation flag ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_testmode_event(struct wiphy *wiphy, const u32 msg_id, ++ const void *data, int len, gfp_t gfp) ++{ ++ struct sk_buff *skb = NULL; ++ ++ ++ skb = cfg80211_testmode_alloc_event_skb(wiphy, ++ nla_total_size(len+sizeof(msg_id)), gfp); ++ ++ if (!skb) ++ return -ENOMEM; ++ ++ cfg80211_testmode_event(skb, gfp); ++ return 0; ++} ++#endif /*ROAM_OFFLOAD*/ ++ ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++/** ++ * xradio_set_snap_frame -Set SNAP frame format ++ * ++ * @hw: the hardware ++ * @data: data frame ++ * @len: data length ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++static int xradio_set_snap_frame(struct ieee80211_hw *hw, ++ u8 *data, int len) ++{ ++ struct xr_msg_set_snap_frame *snap_frame = ++ (struct xr_msg_set_snap_frame *) data; ++ struct xradio_common *priv = (struct xradio_common *) hw->priv; ++ u8 frame_len = snap_frame->len; ++ u8 *frame = &snap_frame->frame[0]; ++ ++ ++ /* ++ * Check length of incoming frame format: ++ * SNAP + SNAP_LEN (u8) ++ */ ++ if (frame_len + sizeof(snap_frame->len) != len) ++ return -EINVAL; ++ ++ if (frame_len > 0) { ++ priv->test_frame.data = (u8 *) xr_krealloc(priv->test_frame.data, ++ sizeof(u8) * frame_len, false); ++ if (priv->test_frame.data == NULL) { ++ sta_printk(XRADIO_DBG_ERROR, "xradio_set_snap_frame memory" \ ++ "allocation failed"); ++ priv->test_frame.len = 0; ++ return -EINVAL; ++ } ++ memcpy(priv->test_frame.data, frame, frame_len); ++ } else { ++ kfree(priv->test_frame.data); ++ priv->test_frame.data = NULL; ++ } ++ priv->test_frame.len = frame_len; ++ return 0; ++} ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++/** ++ * xradio_set_txqueue_params -Set txqueue params after successful TSPEC negotiation ++ * ++ * @hw: the hardware ++ * @data: data frame ++ * @len: data length ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++static int xradio_set_txqueue_params(struct ieee80211_hw *hw, ++ u8 *data, int len) ++{ ++ struct xr_msg_set_txqueue_params *txqueue_params = ++ (struct xr_msg_set_txqueue_params *) data; ++ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; ++ struct xradio_vif *priv; ++ /* Interface ID is hard coded here, as interface is not ++ * passed in testmode command. ++ * Also it is assumed here that STA will be on interface ++ * 0 always. ++ */ ++ ++ int if_id = 0; ++ u16 queueId = xradio_priority_to_queueId[txqueue_params->user_priority]; ++ ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, if_id); ++ ++ if (unlikely(!priv)) { ++ sta_printk(XRADIO_DBG_ERROR, "%s: Warning Priv is Null\n", ++ __func__); ++ return 0; ++ } ++ spin_unlock(&priv->vif_lock); ++ ++ /* Default Ack policy is WSM_ACK_POLICY_NORMAL */ ++ WSM_TX_QUEUE_SET(&priv->tx_queue_params, ++ queueId, ++ WSM_ACK_POLICY_NORMAL, ++ txqueue_params->medium_time, ++ txqueue_params->expiry_time); ++ return SYS_WARN(wsm_set_tx_queue_params(hw_priv, ++ &priv->tx_queue_params.params[queueId], queueId, ++ priv->if_id)); ++} ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++/** ++ * xradio_tesmode_reply -called inside a testmode command ++ * handler to send a response to user space ++ * ++ * @wiphy: the wiphy ++ * @data: data to be send to user space ++ * @len: data length ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++static int xradio_tesmode_reply(struct wiphy *wiphy, ++ const void *data, int len) ++{ ++ int ret = 0; ++ struct sk_buff *skb = NULL; ++ ++ ++ skb = cfg80211_testmode_alloc_reply_skb(wiphy, nla_total_size(len)); ++ ++ if (!skb) ++ return -ENOMEM; ++ ++ ret = nla_put(skb, XR_TM_MSG_DATA, len, data); ++ if (ret) { ++ kfree_skb(skb); ++ return ret; ++ } ++ ++ return cfg80211_testmode_reply(skb); ++} ++ ++/** ++ * xradio_tesmode_event -send asynchronous event ++ * to userspace ++ * ++ * @wiphy: the wiphy ++ * @msg_id: XR msg ID ++ * @data: data to be sent ++ * @len: data length ++ * @gfp: allocation flag ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_tesmode_event(struct wiphy *wiphy, const u32 msg_id, ++ const void *data, int len, gfp_t gfp) ++{ ++ struct sk_buff *skb = NULL; ++ ++ ++ skb = cfg80211_testmode_alloc_event_skb(wiphy, ++ nla_total_size(len+sizeof(msg_id)), gfp); ++ if (!skb) ++ return -ENOMEM; ++ ++ NLA_PUT_U32(skb, XR_TM_MSG_ID, msg_id); ++ if (data) ++ NLA_PUT(skb, XR_TM_MSG_DATA, len, data); ++ ++ cfg80211_testmode_event(skb, gfp); ++ return 0; ++nla_put_failure: ++ kfree_skb(skb); ++ return -ENOBUFS; ++} ++ ++/** ++ * example function for test purposes ++ * sends both: synchronous reply and asynchronous event ++ */ ++static int xradio_test(struct ieee80211_hw *hw, ++ void *data, int len) ++{ ++ struct xr_msg_test_t *test_p; ++ struct xr_reply_test_t reply; ++ struct xr_event_test_t event; ++ ++ ++ if (sizeof(struct xr_msg_test_t) != len) ++ return -EINVAL; ++ ++ test_p = (struct xr_msg_test_t *) data; ++ ++ reply.dummy = test_p->dummy + 10; ++ ++ event.dummy = test_p->dummy + 20; ++ ++ if (xradio_tesmode_event(hw->wiphy, XR_MSG_EVENT_TEST, ++ &event, sizeof(event), GFP_KERNEL)) ++ return -1; ++ ++ return xradio_tesmode_reply(hw->wiphy, &reply, sizeof(reply)); ++} ++ ++/** ++ * xradio_get_tx_power_level - send tx power level ++ * to userspace ++ * ++ * @hw: the hardware ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_get_tx_power_level(struct ieee80211_hw *hw) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ int get_power = 0; ++ ++ ++ get_power = hw_priv->output_power; ++ sta_printk(XRADIO_DBG_MSG, "%s: Power set on Device : %d", ++ __func__, get_power); ++ return xradio_tesmode_reply(hw->wiphy, &get_power, sizeof(get_power)); ++} ++ ++/** ++ * xradio_get_tx_power_range- send tx power range ++ * to userspace for each band ++ * ++ * @hw: the hardware ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_get_tx_power_range(struct ieee80211_hw *hw) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct wsm_tx_power_range txPowerRange[2]; ++ ++ ++ size_t len = sizeof(txPowerRange); ++ memcpy(txPowerRange, hw_priv->txPowerRange, len); ++ return xradio_tesmode_reply(hw->wiphy, txPowerRange, len); ++} ++ ++/** ++ * xradio_set_advance_scan_elems -Set Advcance Scan ++ * elements ++ * @hw: the hardware ++ * @data: data frame ++ * @len: data length ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++static int xradio_set_advance_scan_elems(struct ieee80211_hw *hw, ++ u8 *data, int len) ++{ ++ struct advance_scan_elems *scan_elems = ++ (struct advance_scan_elems *) data; ++ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; ++ size_t elems_len = sizeof(struct advance_scan_elems); ++ ++ ++ if (elems_len != len) ++ return -EINVAL; ++ ++ scan_elems = (struct advance_scan_elems *) data; ++ ++ /* Locks required to prevent simultaneous scan */ ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ hw_priv->advanceScanElems.scanMode = scan_elems->scanMode; ++ hw_priv->advanceScanElems.duration = scan_elems->duration; ++ hw_priv->enable_advance_scan = true; ++ ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++ ++ return 0; ++} ++ ++/** ++ * xradio_set_power_save -Set Power Save ++ * elements ++ * @hw: the hardware ++ * @data: data frame ++ * @len: data length ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++static int xradio_set_power_save(struct ieee80211_hw *hw, ++ u8 *data, int len) ++{ ++ struct power_save_elems *ps_elems = ++ (struct power_save_elems *) data; ++ struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; ++ size_t elems_len = sizeof(struct power_save_elems); ++ struct xradio_vif *priv; ++ int if_id = 0; ++ ++ ++ /* Interface ID is hard coded here, as interface is not ++ * passed in testmode command. ++ * Also it is assumed here that STA will be on interface ++ * 0 always. */ ++ ++ if (elems_len != len) ++ return -EINVAL; ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, if_id); ++ ++ if (unlikely(!priv)) { ++ sta_printk(XRADIO_DBG_ERROR, "%s: Warning Priv is Null\n", ++ __func__); ++ return 0; ++ } ++ ++ spin_unlock(&priv->vif_lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ ++ ps_elems = (struct power_save_elems *) data; ++ ++ if (ps_elems->powerSave == 1) ++ priv->user_pm_mode = WSM_PSM_PS; ++ else ++ priv->user_pm_mode = WSM_PSM_FAST_PS; ++ ++ sta_printk(XRADIO_DBG_MSG, "Aid: %d, Joined: %s, Powersave: %s\n", ++ priv->bss_params.aid, ++ priv->join_status == XRADIO_JOIN_STATUS_STA ? "yes" : "no", ++ priv->user_pm_mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : ++ priv->user_pm_mode == WSM_PSM_PS ? "WSM_PSM_PS" : ++ priv->user_pm_mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : "UNKNOWN"); ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA && ++ priv->bss_params.aid && ++ priv->setbssparams_done && ++ priv->filter4.enable) { ++ priv->powersave_mode.pmMode = priv->user_pm_mode; ++ xradio_set_pm(priv, &priv->powersave_mode); ++ } ++ else ++ priv->user_power_set_true = ps_elems->powerSave; ++ mutex_unlock(&hw_priv->conf_mutex); ++ return 0; ++} ++/** ++ * xradio_start_stop_tsm - starts/stops collecting TSM ++ * ++ * @hw: the hardware ++ * @data: data frame ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_start_stop_tsm(struct ieee80211_hw *hw, void *data) ++{ ++ struct xr_msg_start_stop_tsm *start_stop_tsm = ++ (struct xr_msg_start_stop_tsm *) data; ++ struct xradio_common *hw_priv = hw->priv; ++ ++ ++ hw_priv->start_stop_tsm.start = start_stop_tsm->start; ++ hw_priv->start_stop_tsm.up = start_stop_tsm->up; ++ hw_priv->start_stop_tsm.packetization_delay = ++ start_stop_tsm->packetization_delay; ++ sta_printk(XRADIO_DBG_MSG, "%s: start : %u: up : %u", ++ __func__, hw_priv->start_stop_tsm.start, ++ hw_priv->start_stop_tsm.up); ++ hw_priv->tsm_info.ac = xradio_1d_to_ac[start_stop_tsm->up]; ++ ++ if (!hw_priv->start_stop_tsm.start) { ++ spin_lock_bh(&hw_priv->tsm_lock); ++ memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats)); ++ memset(&hw_priv->tsm_info, 0, sizeof(hw_priv->tsm_info)); ++ spin_unlock_bh(&hw_priv->tsm_lock); ++ } ++ return 0; ++} ++ ++/** ++ * xradio_get_tsm_params - Retrieves TSM parameters ++ * ++ * @hw: the hardware ++ * ++ * Returns: TSM parameters collected ++ */ ++int xradio_get_tsm_params(struct ieee80211_hw *hw) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ struct xr_tsm_stats tsm_stats; ++ u32 pkt_count; ++ ++ ++ spin_lock_bh(&hw_priv->tsm_lock); ++ pkt_count = hw_priv->tsm_stats.txed_msdu_count - ++ hw_priv->tsm_stats.msdu_discarded_count; ++ if (pkt_count) { ++ hw_priv->tsm_stats.avg_q_delay = ++ hw_priv->tsm_info.sum_pkt_q_delay/(pkt_count * 1000); ++ hw_priv->tsm_stats.avg_transmit_delay = ++ hw_priv->tsm_info.sum_media_delay/pkt_count; ++ } else { ++ hw_priv->tsm_stats.avg_q_delay = 0; ++ hw_priv->tsm_stats.avg_transmit_delay = 0; ++ } ++ sta_printk(XRADIO_DBG_MSG, "%s: Txed MSDU count : %u", ++ __func__, hw_priv->tsm_stats.txed_msdu_count); ++ sta_printk(XRADIO_DBG_MSG, "%s: Average queue delay : %u", ++ __func__, hw_priv->tsm_stats.avg_q_delay); ++ sta_printk(XRADIO_DBG_MSG, "%s: Average transmit delay : %u", ++ __func__, hw_priv->tsm_stats.avg_transmit_delay); ++ memcpy(&tsm_stats, &hw_priv->tsm_stats, sizeof(hw_priv->tsm_stats)); ++ /* Reset the TSM statistics */ ++ memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats)); ++ hw_priv->tsm_info.sum_pkt_q_delay = 0; ++ hw_priv->tsm_info.sum_media_delay = 0; ++ spin_unlock_bh(&hw_priv->tsm_lock); ++ return xradio_tesmode_reply(hw->wiphy, &tsm_stats, ++ sizeof(hw_priv->tsm_stats)); ++} ++ ++/** ++ * xradio_get_roam_delay - Retrieves roam delay ++ * ++ * @hw: the hardware ++ * ++ * Returns: Returns the last measured roam delay ++ */ ++int xradio_get_roam_delay(struct ieee80211_hw *hw) ++{ ++ struct xradio_common *hw_priv = hw->priv; ++ u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000; ++ sta_printk(XRADIO_DBG_MSG, "%s: Roam delay : %u", ++ __func__, roam_delay); ++ ++ spin_lock_bh(&hw_priv->tsm_lock); ++ hw_priv->tsm_info.roam_delay = 0; ++ hw_priv->tsm_info.use_rx_roaming = 0; ++ spin_unlock_bh(&hw_priv->tsm_lock); ++ return xradio_tesmode_reply(hw->wiphy, &roam_delay, sizeof(u16)); ++} ++ ++/** ++ * xradio_testmode_cmd -called when tesmode command ++ * reaches xradio ++ * ++ * @hw: the hardware ++ * @data: incoming data ++ * @len: incoming data length ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++int xradio_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) ++{ ++ int ret = 0; ++ struct nlattr *type_p = nla_find(data, len, XR_TM_MSG_ID); ++ struct nlattr *data_p = nla_find(data, len, XR_TM_MSG_DATA); ++ ++ ++ if (!type_p || !data_p) ++ return -EINVAL; ++ ++ sta_printk(XRADIO_DBG_MSG, "%s: type: %i", ++ __func__, nla_get_u32(type_p)); ++ ++ switch (nla_get_u32(type_p)) { ++ case XR_MSG_TEST: ++ ret = xradio_test(hw, ++ nla_data(data_p), nla_len(data_p)); ++ break; ++ case XR_MSG_SET_SNAP_FRAME: ++ ret = xradio_set_snap_frame(hw, (u8 *) nla_data(data_p), ++ nla_len(data_p)); ++ break; ++ case XR_MSG_GET_TX_POWER_LEVEL: ++ ret = xradio_get_tx_power_level(hw); ++ break; ++ case XR_MSG_GET_TX_POWER_RANGE: ++ ret = xradio_get_tx_power_range(hw); ++ break; ++ case XR_MSG_SET_ADVANCE_SCAN_ELEMS: ++ ret = xradio_set_advance_scan_elems(hw, (u8 *) nla_data(data_p), ++ nla_len(data_p)); ++ break; ++ case XR_MSG_SET_TX_QUEUE_PARAMS: ++ ret = xradio_set_txqueue_params(hw, (u8 *) nla_data(data_p), ++ nla_len(data_p)); ++ break; ++ case XR_MSG_GET_TSM_PARAMS: ++ ret = xradio_get_tsm_params(hw); ++ break; ++ case XR_MSG_START_STOP_TSM: ++ ret = xradio_start_stop_tsm(hw, (u8 *) nla_data(data_p)); ++ break; ++ case XR_MSG_GET_ROAM_DELAY: ++ ret = xradio_get_roam_delay(hw); ++ break; ++ case XR_MSG_SET_POWER_SAVE: ++ ret = xradio_set_power_save(hw, (u8 *) nla_data(data_p), ++ nla_len(data_p)); ++ break; ++ default: ++ break; ++ } ++ return ret; ++} ++#endif /* CONFIG_XRADIO_TESTMODE */ +diff --git a/drivers/net/wireless/xradio/sta.h b/drivers/net/wireless/xradio/sta.h +new file mode 100644 +index 00000000..f663b63a +--- /dev/null ++++ b/drivers/net/wireless/xradio/sta.h +@@ -0,0 +1,131 @@ ++/* ++ * sta interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++#ifndef STA_H_INCLUDED ++#define STA_H_INCLUDED ++ ++ ++#ifdef XRADIO_USE_LONG_KEEP_ALIVE_PERIOD ++#define XRADIO_KEEP_ALIVE_PERIOD (28) ++#else ++/*For Samsung, it is defined as 4*/ ++#define XRADIO_KEEP_ALIVE_PERIOD (4) ++#endif ++ ++#ifdef XRADIO_USE_LONG_DTIM_PERIOD ++#define XRADIO_BSS_LOSS_THOLD_DEF 30 ++#define XRADIO_LINK_LOSS_THOLD_DEF 50 ++#else ++#define XRADIO_BSS_LOSS_THOLD_DEF 20 ++#define XRADIO_LINK_LOSS_THOLD_DEF 40 ++#endif ++ ++/* ******************************************************************** */ ++/* mac80211 API */ ++ ++int xradio_start(struct ieee80211_hw *dev); ++void xradio_stop(struct ieee80211_hw *dev); ++int xradio_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif); ++void xradio_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif); ++int xradio_change_interface(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif, ++ enum nl80211_iftype new_type, ++ bool p2p); ++int xradio_config(struct ieee80211_hw *dev, u32 changed); ++int xradio_change_interface(struct ieee80211_hw *dev, ++ struct ieee80211_vif *vif, ++ enum nl80211_iftype new_type, ++ bool p2p); ++void xradio_configure_filter(struct ieee80211_hw *dev, ++ unsigned int changed_flags, ++ unsigned int *total_flags, ++ u64 multicast); ++int xradio_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, ++ u16 queue, const struct ieee80211_tx_queue_params *params); ++int xradio_get_stats(struct ieee80211_hw *dev, ++ struct ieee80211_low_level_stats *stats); ++/* Not more a part of interface? ++int xradio_get_tx_stats(struct ieee80211_hw *dev, ++ struct ieee80211_tx_queue_stats *stats); ++*/ ++int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value); ++ ++void xradio_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u32 queues, bool drop); ++ ++ ++int xradio_remain_on_channel(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ struct ieee80211_channel *chan, ++ int duration, enum ieee80211_roc_type type); ++int xradio_cancel_remain_on_channel(struct ieee80211_hw *hw); ++int xradio_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif); ++u64 xradio_prepare_multicast(struct ieee80211_hw *hw, ++ struct netdev_hw_addr_list *mc_list); ++int xradio_set_pm(struct xradio_vif *priv, const struct wsm_set_pm *arg); ++void xradio_set_data_filter(struct ieee80211_hw *hw, ++ struct ieee80211_vif *vif, ++ void *data, ++ int len); ++ ++/* ******************************************************************** */ ++/* WSM callbacks */ ++ ++/* void xradio_set_pm_complete_cb(struct xradio_common *hw_priv, ++ struct wsm_set_pm_complete *arg); */ ++void xradio_channel_switch_cb(struct xradio_common *hw_priv); ++ ++/* ******************************************************************** */ ++/* WSM events */ ++ ++void xradio_free_event_queue(struct xradio_common *hw_priv); ++void xradio_event_handler(struct work_struct *work); ++void xradio_bss_loss_work(struct work_struct *work); ++void xradio_connection_loss_work(struct work_struct *work); ++void xradio_keep_alive_work(struct work_struct *work); ++void xradio_tx_failure_work(struct work_struct *work); ++ ++/* ******************************************************************** */ ++/* Internal API */ ++ ++int xradio_setup_mac(struct xradio_common *hw_priv); ++void xradio_join_work(struct work_struct *work); ++void xradio_join_timeout(struct work_struct *work); ++void xradio_unjoin_work(struct work_struct *work); ++void xradio_offchannel_work(struct work_struct *work); ++void xradio_wep_key_work(struct work_struct *work); ++void xradio_update_filtering(struct xradio_vif *priv); ++void xradio_update_filtering_work(struct work_struct *work); ++int __xradio_flush(struct xradio_common *hw_priv, bool drop, int if_id); ++void xradio_set_beacon_wakeup_period_work(struct work_struct *work); ++int xradio_enable_listening(struct xradio_vif *priv, struct ieee80211_channel *chan); ++int xradio_disable_listening(struct xradio_vif *priv); ++int xradio_set_uapsd_param(struct xradio_vif *priv, const struct wsm_edca_params *arg); ++void xradio_ba_work(struct work_struct *work); ++void xradio_ba_timer(unsigned long arg); ++const u8 *xradio_get_ie(u8 *start, size_t len, u8 ie); ++int xradio_vif_setup(struct xradio_vif *priv); ++int xradio_setup_mac_pvif(struct xradio_vif *priv); ++void xradio_iterate_vifs(void *data, u8 *mac, struct ieee80211_vif *vif); ++void xradio_rem_chan_timeout(struct work_struct *work); ++int xradio_set_macaddrfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data); ++#ifdef ROAM_OFFLOAD ++int xradio_testmode_event(struct wiphy *wiphy, const u32 msg_id, ++ const void *data, int len, gfp_t gfp); ++#endif /*ROAM_OFFLOAD*/ ++#ifdef CONFIG_XRADIO_TESTMODE ++void xradio_device_power_calc(struct xradio_common *priv, ++ s16 max_output_power, s16 fe_cor, u32 band); ++int xradio_testmode_cmd(struct ieee80211_hw *hw, void *data, int len); ++int xradio_tesmode_event(struct wiphy *wiphy, const u32 msg_id, ++ const void *data, int len, gfp_t gfp); ++int xradio_get_tx_power_range(struct ieee80211_hw *hw); ++int xradio_get_tx_power_level(struct ieee80211_hw *hw); ++#endif /* CONFIG_XRADIO_TESTMODE */ ++#endif +diff --git a/drivers/net/wireless/xradio/txrx.c b/drivers/net/wireless/xradio/txrx.c +new file mode 100644 +index 00000000..1b3014a7 +--- /dev/null ++++ b/drivers/net/wireless/xradio/txrx.c +@@ -0,0 +1,2210 @@ ++/* ++ * Datapath implementation for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "wsm.h" ++#include "bh.h" ++#include "ap.h" ++#include "sta.h" ++#include "sdio.h" ++#include "common.h" ++ ++#define B_RATE_INDEX 0 //11b rate for important short frames in 2.4G. ++#define AG_RATE_INDEX 6 //11a/g rate for important short frames in 5G. ++#define XRADIO_INVALID_RATE_ID (0xFF) ++ ++/* rate should fall quickly to avoid dropping frames by aps. ++ * Add by yangfh 2014-9-22 13:39:57 ++ */ ++#define HIGH_RATE_MAX_RETRY 7 ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++#include "nl80211_testmode_msg_copy.h" ++#endif /* CONFIG_XRADIO_TESTMODE */ ++#ifdef TES_P2P_0002_ROC_RESTART ++#include ++#endif ++static const struct ieee80211_rate * ++xradio_get_tx_rate(const struct xradio_common *hw_priv, ++ const struct ieee80211_tx_rate *rate); ++ ++u32 TxedRateIdx_Map[24] = {0}; ++u32 RxedRateIdx_Map[24] = {0}; ++ ++#ifdef CONFIG_XRADIO_DEBUGFS ++//add by yangfh for tx rates debug. ++extern u8 rates_dbg_en; ++extern u32 rates_debug[3]; ++extern u8 maxRate_dbg; ++extern u8 retry_dbg; ++#endif ++/* ******************************************************************** */ ++/* TX policy cache implementation */ ++ ++static void tx_policy_dump(struct tx_policy *policy) ++{ ++ txrx_printk(XRADIO_DBG_MSG, "[TX policy] " ++ "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" ++ "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" ++ "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", ++ policy->raw[0] & 0x0F, policy->raw[0] >> 4, ++ policy->raw[1] & 0x0F, policy->raw[1] >> 4, ++ policy->raw[2] & 0x0F, policy->raw[2] >> 4, ++ policy->raw[3] & 0x0F, policy->raw[3] >> 4, ++ policy->raw[4] & 0x0F, policy->raw[4] >> 4, ++ policy->raw[5] & 0x0F, policy->raw[5] >> 4, ++ policy->raw[6] & 0x0F, policy->raw[6] >> 4, ++ policy->raw[7] & 0x0F, policy->raw[7] >> 4, ++ policy->raw[8] & 0x0F, policy->raw[8] >> 4, ++ policy->raw[9] & 0x0F, policy->raw[9] >> 4, ++ policy->raw[10] & 0x0F, policy->raw[10] >> 4, ++ policy->raw[11] & 0x0F, policy->raw[11] >> 4, ++ policy->defined); ++} ++ ++static void xradio_check_go_neg_conf_success(struct xradio_common *hw_priv, ++ u8 *action) ++{ ++ ++ if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A && ++ action[5] == 0x09 && action[6] == 0x02) { ++ if(action[17] == 0) { ++ hw_priv->is_go_thru_go_neg = true; ++ } ++ else { ++ hw_priv->is_go_thru_go_neg = false; ++ } ++ } ++} ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++///w, TES_P2P_0002 WorkAround: ++///w, P2P GO Neg Process and P2P FIND may be collision. ++///w, When P2P Device is waiting for GO NEG CFM in 30ms, ++///w, P2P FIND may end with p2p listen, and then goes to p2p search. ++///w, Then xradio scan will occupy phy on other channel in 3+ seconds. ++///w, P2P Device will not be able to receive the GO NEG CFM. ++///w, We extend the roc period to remaind phy to receive GO NEG CFM as WorkAround. ++ ++s32 TES_P2P_0002_roc_dur; ++s32 TES_P2P_0002_roc_sec; ++s32 TES_P2P_0002_roc_usec; ++u32 TES_P2P_0002_packet_id; ++u32 TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; ++ ++static void xradio_frame_monitor(struct xradio_common *hw_priv, struct sk_buff *skb, bool tx) { ++ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; ++ ++ u8 *action = (u8*)&mgmt->u.action.category; ++ u8 *category_code = &(action[0]); ++ u8 *action_code = &(action[1]); ++ u8 *oui = &(action[2]); ++ u8 *subtype = &(action[5]); ++ u8 *oui_subtype = &(action[6]); ++ ++ ++ if(ieee80211_is_action(frame->frame_control)) { ++ if( *category_code == WLAN_CATEGORY_PUBLIC) { ++ if (*action_code == 0x09) { ++ if((oui[0] == 0x50) && (oui[1] == 0x6F) && ++ (oui[2] == 0x9A) && (*subtype == 0x09)) { ++ if ( *oui_subtype == 0x01 ) { ///w, GO Negotiation Response ++ if((TES_P2P_0002_state == TES_P2P_0002_STATE_IDLE) && ++ (tx == true)) { ///w, p2p atturbute:status,id=0 ++ u8 *go_neg_resp_res = &(action[17]); ++ if (*go_neg_resp_res == 0x0) { ++ TES_P2P_0002_state = TES_P2P_0002_STATE_SEND_RESP; ++ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_SEND_RESP]\n"); ++ } ++ } ++ } else if ( *oui_subtype == 0x02 ) { ///w, GO Negotiation Confirmation ++ if( tx == false ) { ++ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; ++ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE]" ++ "[GO Negotiation Confirmation]\n"); ++ } ++ } else if ( *oui_subtype == 0x08 ) { ///w, Provision Discovery Response ++ if(tx == false) { ++ TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; ++ txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE]" ++ "[Provision Discovery Response]\n"); ++ } ++ } ++ } ++ } ++ } ++ } ++} ++#endif ++ ++static void xradio_check_prov_desc_req(struct xradio_common *hw_priv, ++ u8 *action) ++{ ++ ++ if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A && ++ action[5] == 0x09 && action[6] == 0x07) { ++ hw_priv->is_go_thru_go_neg = false; ++ } ++} ++ ++#ifdef AP_HT_COMPAT_FIX ++#define AP_COMPAT_THRESHOLD 2000 ++#define AP_COMPAT_MIN_CNT 200 ++u8 ap_compat_bssid[ETH_ALEN] = {0}; ++static int xradio_apcompat_detect(struct xradio_vif *priv, u8 rx_rate) ++{ ++ if (rx_rate < AG_RATE_INDEX) { ++ priv->ht_compat_cnt++; ++ txrx_printk(XRADIO_DBG_MSG,"%s:rate=%d.\n", __func__, rx_rate); ++ } else { ++ priv->ht_compat_det |= 1; ++ priv->ht_compat_cnt = 0; ++ txrx_printk(XRADIO_DBG_NIY,"%s:HT compat detect\n", __func__); ++ return 0; ++ } ++ ++ /* Enhance compatibility with some illegal APs.*/ ++ if (priv->ht_compat_cnt > AP_COMPAT_THRESHOLD || ++ (priv->ht_compat_cnt > AP_COMPAT_MIN_CNT && ++ priv->bssid[0] == 0xC8 && ++ priv->bssid[1] == 0x3A && ++ priv->bssid[2] == 0x35)) { ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ memcpy(ap_compat_bssid, priv->bssid, ETH_ALEN); ++ wms_send_disassoc_to_self(hw_priv, priv); ++ txrx_printk(XRADIO_DBG_WARN, "%s:SSID=%s, BSSID=" \ ++ "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, priv->ssid, ++ ap_compat_bssid[0], ap_compat_bssid[1], ++ ap_compat_bssid[2], ap_compat_bssid[3], ++ ap_compat_bssid[4], ap_compat_bssid[5]); ++ return 1; ++ } ++ return 0; ++} ++ ++static void xradio_remove_ht_ie(struct xradio_vif *priv, struct sk_buff *skb) ++{ ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; ++ u8 *ies = NULL; ++ size_t ies_len = 0; ++ u8 *ht_ie = NULL; ++ ++ if (!mgmt || memcmp(ap_compat_bssid, mgmt->bssid, ETH_ALEN)) ++ return; ++ ++ if (ieee80211_is_probe_resp(mgmt->frame_control)) ++ ies = mgmt->u.probe_resp.variable; ++ else if (ieee80211_is_beacon(mgmt->frame_control)) ++ ies = mgmt->u.beacon.variable; ++ else if (ieee80211_is_assoc_resp(mgmt->frame_control)) ++ ies = mgmt->u.assoc_resp.variable; ++ else if (ieee80211_is_assoc_req(mgmt->frame_control)) ++ ies = mgmt->u.assoc_req.variable; ++ else ++ return; ++ ++ ies_len = skb->len - (ies - (u8 *)(skb->data)); ++ ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY); ++ if (ht_ie) { ++ u8 ht_len = *(ht_ie + 1) + 2; ++ u8 move_len = (ies + ies_len) - (ht_ie + ht_len); ++ memmove(ht_ie, (ht_ie + ht_len), move_len); ++ skb_trim(skb, skb->len - ht_len); ++ ies_len = skb->len - (ies - (u8 *)(skb->data)); ++ ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_OPERATION); ++ if (ht_ie) { ++ ht_len = *(ht_ie + 1) + 2; ++ move_len = (ies + ies_len) - (ht_ie + ht_len); ++ memmove(ht_ie, (ht_ie + ht_len), move_len); ++ skb_trim(skb, skb->len - ht_len); ++ } ++ } ++ txrx_printk(XRADIO_DBG_WARN, "%s: BSSID=%02x:%02x:%02x:%02x:%02x:%02x\n", ++ __func__, ++ mgmt->bssid[0], mgmt->bssid[1], ++ mgmt->bssid[2], mgmt->bssid[3], ++ mgmt->bssid[4], mgmt->bssid[5]); ++} ++#endif //AP_HT_COMPAT_FIX ++ ++//modified by yangfh ++static void tx_policy_build(const struct xradio_common *hw_priv, ++ /* [out] */ struct tx_policy *policy, ++ struct ieee80211_tx_rate *rates, size_t count) ++{ ++ int i, j; ++ struct ieee80211_rate * tmp_rate = NULL; ++ unsigned limit = hw_priv->short_frame_max_tx_count; ++ unsigned max_rates_cnt = count; ++ unsigned total = 0; ++ SYS_BUG(rates[0].idx < 0); ++ memset(policy, 0, sizeof(*policy)); ++ ++ ++ txrx_printk(XRADIO_DBG_NIY,"============================"); ++#if 0 ++ //debug yangfh ++ for (i = 0; i < count; ++i) { ++ if(rates[i].idx>=0) { ++ tmp_rate = xradio_get_tx_rate(hw_priv, &rates[i]); ++ txrx_printk(XRADIO_DBG_NIY,"[TX policy] Org %d.%dMps=%d", ++ tmp_rate->bitrate/10, tmp_rate->bitrate%10, rates[i].count); ++ } ++ } ++ txrx_printk(XRADIO_DBG_NIY,"----------------------------"); ++#endif ++ ++ /* minstrel is buggy a little bit, so distille ++ * incoming rates first. ++ */ ++ /* Sort rates in descending order. */ ++ total = rates[0].count; ++ for (i = 1; i < count; ++i) { ++ if (rates[i].idx > rates[i-1].idx) { ++ rates[i].idx = rates[i-1].idx>0?(rates[i-1].idx-1):-1; ++ } ++ if (rates[i].idx < 0 || i>=limit) { ++ count = i; ++ break; ++ } else { ++ total += rates[i].count; ++ } ++ } ++ ++ /* Add lowest rate to the end when 11a/n. ++ * Don't apply in 11b/g because p2p unsupport 1Mbps. ++ * TODO: it's better to do this in rate control of mac80211. ++ */ ++ if (((rates[0].flags & IEEE80211_TX_RC_MCS) || ++ hw_priv->channel->band == NL80211_BAND_5GHZ) && ++ count < max_rates_cnt && rates[count-1].idx != 0) { ++ rates[count].idx = 0; ++ rates[count].count = rates[0].count; ++ rates[count].flags = rates[0].flags; ++ total += rates[count].count; ++ count++; ++ } ++ ++ /* adjust tx count to limit, rates should fall quickly ++ * and lower rates should be more retry, because reorder ++ * buffer of reciever will be timeout and clear probably. ++ */ ++ if (count < 2) { ++ rates[0].count = limit; ++ total = limit; ++ } else { ++ u8 end_retry = 0; //the retry should be add to last rate. ++ if (limit > HIGH_RATE_MAX_RETRY) { ++ end_retry = limit - HIGH_RATE_MAX_RETRY; ++ limit = HIGH_RATE_MAX_RETRY; ++ } ++ for (i = 0; (limit != total) && (i < 100); ++i) { //i<100 to avoid dead loop ++ j = i % count; ++ if(limit < total) { ++ total += (rates[j].count > 1? -1 : 0); ++ rates[j].count += (rates[j].count > 1? -1 : 0); ++ } else { ++ j = count - 1 - j; ++ if (rates[j].count > 0) { ++ total++; ++ rates[j].count++; ++ } ++ } ++ } ++ if (end_retry) { ++ rates[count-1].count += end_retry; ++ limit += end_retry; ++ } ++ } ++ ++ /* Eliminate duplicates. */ ++ total = rates[0].count; ++ for (i = 0, j = 1; j < count; ++j) { ++ if (rates[j].idx == rates[i].idx) { ++ rates[i].count += rates[j].count; ++ } else if (rates[j].idx > rates[i].idx) { ++ break; ++ } else { ++ ++i; ++ if (i != j) ++ rates[i] = rates[j]; ++ } ++ total += rates[j].count; ++ } ++ count = i + 1; ++ ++ /* Re-fill policy trying to keep every requested rate and with ++ * respect to the global max tx retransmission count. ++ */ ++ if (limit < count) ++ limit = count; ++ if (total > limit) { ++ for (i = 0; i < count; ++i) { ++ int left = count - i - 1; ++ if (rates[i].count > limit - left) ++ rates[i].count = limit - left; ++ limit -= rates[i].count; ++ } ++ } ++ ++ /* HACK!!! Device has problems (at least) switching from ++ * 54Mbps CTS to 1Mbps. This switch takes enormous amount ++ * of time (100-200 ms), leading to valuable throughput drop. ++ * As a workaround, additional g-rates are injected to the ++ * policy. ++ */ ++ if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && ++ rates[0].idx > 4 && rates[0].count > 2 && ++ rates[1].idx < 2) { ++ /* ">> 1" is an equivalent of "/ 2", but faster */ ++ int mid_rate = (rates[0].idx + 4) >> 1; ++ ++ /* Decrease number of retries for the initial rate */ ++ rates[0].count -= 2; ++ ++ if (mid_rate != 4) { ++ /* Keep fallback rate at 1Mbps. */ ++ rates[3] = rates[1]; ++ ++ /* Inject 1 transmission on lowest g-rate */ ++ rates[2].idx = 4; ++ rates[2].count = 1; ++ rates[2].flags = rates[1].flags; ++ ++ /* Inject 1 transmission on mid-rate */ ++ rates[1].idx = mid_rate; ++ rates[1].count = 1; ++ ++ /* Fallback to 1 Mbps is a really bad thing, ++ * so let's try to increase probability of ++ * successful transmission on the lowest g rate ++ * even more */ ++ if (rates[0].count >= 3) { ++ --rates[0].count; ++ ++rates[2].count; ++ } ++ ++ /* Adjust amount of rates defined */ ++ count += 2; ++ } else { ++ /* Keep fallback rate at 1Mbps. */ ++ rates[2] = rates[1]; ++ ++ /* Inject 2 transmissions on lowest g-rate */ ++ rates[1].idx = 4; ++ rates[1].count = 2; ++ ++ /* Adjust amount of rates defined */ ++ count += 1; ++ } ++ } ++ ++ tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[0]); ++ if(tmp_rate) ++ policy->defined = tmp_rate->hw_value + 1; ++ ++ for (i = 0; i < count; ++i) { ++ register unsigned rateid, off, shift, retries; ++ ++ tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[i]); ++ if(tmp_rate) { ++ rateid = tmp_rate->hw_value; ++ } else { ++ break; ++ } ++ off = rateid >> 3; /* eq. rateid / 8 */ ++ shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ ++ ++ retries = rates[i].count; ++ if (unlikely(retries > 0x0F)) ++ rates[i].count = retries = 0x0F; ++ policy->tbl[off] |= __cpu_to_le32(retries << shift); ++ policy->retry_count += retries; ++ txrx_printk(XRADIO_DBG_NIY,"[TX policy] %d.%dMps=%d", ++ tmp_rate->bitrate/10, tmp_rate->bitrate%10, retries); ++ } ++ ++ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Dst Policy (%d): " \ ++ "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", ++ count, ++ rates[0].idx, rates[0].count, ++ rates[1].idx, rates[1].count, ++ rates[2].idx, rates[2].count, ++ rates[3].idx, rates[3].count, ++ rates[4].idx, rates[4].count); ++} ++ ++static inline bool tx_policy_is_equal(const struct tx_policy *wanted, ++ const struct tx_policy *cached) ++{ ++ size_t count = wanted->defined >> 1; ++ ++ if (wanted->defined > cached->defined) ++ return false; ++ if (count) { ++ if (memcmp(wanted->raw, cached->raw, count)) ++ return false; ++ } ++ if (wanted->defined & 1) { ++ if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) ++ return false; ++ } ++ return true; ++} ++ ++static int tx_policy_find(struct tx_policy_cache *cache, ++ const struct tx_policy *wanted) ++{ ++ /* O(n) complexity. Not so good, but there's only 8 entries in ++ * the cache. ++ * Also lru helps to reduce search time. */ ++ struct tx_policy_cache_entry *it; ++ /* Search for policy in "used" list */ ++ list_for_each_entry(it, &cache->used, link) { ++ if (tx_policy_is_equal(wanted, &it->policy)) ++ return it - cache->cache; ++ } ++ /* Then - in "free list" */ ++ list_for_each_entry(it, &cache->free, link) { ++ if (tx_policy_is_equal(wanted, &it->policy)) ++ return it - cache->cache; ++ } ++ return -1; ++} ++ ++static inline void tx_policy_use(struct tx_policy_cache *cache, ++ struct tx_policy_cache_entry *entry) ++{ ++ ++entry->policy.usage_count; ++ list_move(&entry->link, &cache->used); ++} ++ ++static inline int tx_policy_release(struct tx_policy_cache *cache, ++ struct tx_policy_cache_entry *entry) ++{ ++ int ret = --entry->policy.usage_count; ++ if (!ret) ++ list_move(&entry->link, &cache->free); ++ return ret; ++} ++ ++/* ******************************************************************** */ ++/* External TX policy cache API */ ++ ++void tx_policy_init(struct xradio_common *hw_priv) ++{ ++ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; ++ int i; ++ ++ ++ memset(cache, 0, sizeof(*cache)); ++ ++ spin_lock_init(&cache->lock); ++ INIT_LIST_HEAD(&cache->used); ++ INIT_LIST_HEAD(&cache->free); ++ ++ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) ++ list_add(&cache->cache[i].link, &cache->free); ++} ++ ++static int tx_policy_get(struct xradio_common *hw_priv, ++ struct ieee80211_tx_rate *rates, ++ u8 use_bg_rate, bool *renew) ++{ ++ int idx; ++ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; ++ struct tx_policy wanted; ++ ++ ++ if(use_bg_rate) { ++ u8 rate = (u8)(use_bg_rate & 0x3f); ++ u8 shitf = ((rate&0x7)<<2); ++ u8 off = (rate>>3); ++ memset(&wanted, 0, sizeof(wanted)); ++ wanted.defined = rate + 1; ++ wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf); ++ wanted.tbl[off] = wanted.retry_count<short_frame_max_tx_count&0xf); ++ memcpy(&wanted.tbl[0], &rates_debug[0], sizeof(wanted.tbl)); ++ } ++#endif ++ ++ spin_lock_bh(&cache->lock); ++ idx = tx_policy_find(cache, &wanted); ++ if (idx >= 0) { ++ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Used TX policy: %d\n", ++ idx); ++ *renew = false; ++ } else { ++ struct tx_policy_cache_entry *entry; ++ if (WARN_ON_ONCE(list_empty(&cache->free))) { ++ spin_unlock_bh(&cache->lock); ++ txrx_printk(XRADIO_DBG_ERROR, "[TX policy] no policy cache\n"); ++ return XRADIO_INVALID_RATE_ID; ++ } ++ /* If policy is not found create a new one ++ * using the oldest entry in "free" list */ ++ *renew = true; ++ entry = list_entry(cache->free.prev, ++ struct tx_policy_cache_entry, link); ++ entry->policy = wanted; ++ idx = entry - cache->cache; ++ txrx_printk(XRADIO_DBG_MSG, "[TX policy] New TX policy: %d\n", ++ idx); ++ tx_policy_dump(&entry->policy); ++ } ++ tx_policy_use(cache, &cache->cache[idx]); ++ if (unlikely(list_empty(&cache->free))) { ++ /* Lock TX queues. */ ++ txrx_printk(XRADIO_DBG_WARN, "[TX policy] policy cache used up\n"); ++ xradio_tx_queues_lock(hw_priv); ++ } ++ spin_unlock_bh(&cache->lock); ++ ++ /*force to upload retry limit when using debug rate policy */ ++#ifdef CONFIG_XRADIO_DEBUGFS ++ if (retry_dbg & 0x2) { ++ retry_dbg &= ~0x2; ++ //retry dgb need to be applied to policy. ++ *renew = true; ++ cache->cache[idx].policy.uploaded = 0; ++ } ++#endif ++ ++ return idx; ++} ++ ++static void tx_policy_put(struct xradio_common *hw_priv, int idx) ++{ ++ int usage, locked; ++ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; ++ ++ ++ spin_lock_bh(&cache->lock); ++ locked = list_empty(&cache->free); ++ usage = tx_policy_release(cache, &cache->cache[idx]); ++ if (unlikely(locked) && !usage) { ++ /* Unlock TX queues. */ ++ xradio_tx_queues_unlock(hw_priv); ++ } ++ spin_unlock_bh(&cache->lock); ++} ++ ++/* ++bool tx_policy_cache_full(struct xradio_common *hw_priv) ++{ ++ bool ret; ++ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; ++ spin_lock_bh(&cache->lock); ++ ret = list_empty(&cache->free); ++ spin_unlock_bh(&cache->lock); ++ return ret; ++} ++*/ ++extern u32 policy_upload; ++extern u32 policy_num; ++static int tx_policy_upload(struct xradio_common *hw_priv) ++{ ++ struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; ++ int i; ++ struct wsm_set_tx_rate_retry_policy arg = { ++ .hdr = { ++ .numTxRatePolicies = 0, ++ } ++ }; ++ int if_id = 0; ++ ++ ++ spin_lock_bh(&cache->lock); ++ /* Upload only modified entries. */ ++ for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { ++ struct tx_policy *src = &cache->cache[i].policy; ++ if (src->retry_count && !src->uploaded) { ++ struct wsm_set_tx_rate_retry_policy_policy *dst = ++ &arg.tbl[arg.hdr.numTxRatePolicies]; ++ dst->policyIndex = i; ++ dst->shortRetryCount = hw_priv->short_frame_max_tx_count-1; ++ //only RTS need use longRetryCount, should be short_frame. ++ dst->longRetryCount = hw_priv->short_frame_max_tx_count-1; ++ ++ /* BIT(2) - Terminate retries when Tx rate retry policy ++ * finishes. ++ * BIT(3) - Count initial frame transmission as part of ++ * rate retry counting but not as a retry ++ * attempt */ ++ dst->policyFlags = BIT(2) | BIT(3); ++ memcpy(dst->rateCountIndices, src->tbl, ++ sizeof(dst->rateCountIndices)); ++ src->uploaded = 1; ++ ++arg.hdr.numTxRatePolicies; ++ } ++ } ++ spin_unlock_bh(&cache->lock); ++ atomic_set(&hw_priv->upload_count, 0); ++ ++ xradio_debug_tx_cache_miss(hw_priv); ++ txrx_printk(XRADIO_DBG_MSG, "[TX policy] Upload %d policies\n", ++ arg.hdr.numTxRatePolicies); ++#ifdef CONFIG_XRADIO_DEBUGFS ++ //test yangfh ++ if(arg.tbl[0].policyIndex == 7) ++ txrx_printk(XRADIO_DBG_MSG, "rate:0x%08x, 0x%08x, 0x%08x\n", ++ arg.tbl[0].rateCountIndices[2], ++ arg.tbl[0].rateCountIndices[1], ++ arg.tbl[0].rateCountIndices[0]); ++ policy_upload++; ++ policy_num += arg.hdr.numTxRatePolicies; ++#endif ++ /*TODO: COMBO*/ ++ return wsm_set_tx_rate_retry_policy(hw_priv, &arg, if_id); ++} ++ ++void tx_policy_upload_work(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, tx_policy_upload_work); ++ ++ ++ SYS_WARN(tx_policy_upload(hw_priv)); ++ wsm_unlock_tx(hw_priv); ++} ++ ++/* ******************************************************************** */ ++/* xradio TX implementation */ ++ ++struct xradio_txinfo { ++ struct sk_buff *skb; ++ unsigned queue; ++ struct ieee80211_tx_info *tx_info; ++ const struct ieee80211_rate *rate; ++ struct ieee80211_hdr *hdr; ++ size_t hdrlen; ++ const u8 *da; ++ struct xradio_sta_priv *sta_priv; ++ struct xradio_txpriv txpriv; ++}; ++ ++u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv, u32 rates) ++{ ++ u32 ret = 0; ++ int i; ++ u32 n_bitrates = ++ hw_priv->hw->wiphy->bands[hw_priv->channel->band]->n_bitrates; ++ struct ieee80211_rate * bitrates = ++ hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates; ++ ++ for (i = 0; i < n_bitrates; ++i) { ++ if (rates & BIT(i)) ++ ret |= BIT(bitrates[i].hw_value); ++ } ++ return ret; ++} ++ ++static const struct ieee80211_rate * ++xradio_get_tx_rate(const struct xradio_common *hw_priv, ++ const struct ieee80211_tx_rate *rate) ++{ ++ if (rate->idx < 0) ++ return NULL; ++ if (rate->flags & IEEE80211_TX_RC_MCS) ++ return &hw_priv->mcs_rates[rate->idx]; ++ return &hw_priv->hw->wiphy->bands[hw_priv->channel->band]-> ++ bitrates[rate->idx]; ++} ++ ++inline static s8 ++xradio_get_rate_idx(const struct xradio_common *hw_priv, u8 flag, u16 hw_value) ++{ ++ s16 ret = (s16)hw_value; ++ if(flag & IEEE80211_TX_RC_MCS) { //11n ++ if(hw_value <= hw_priv->mcs_rates[7].hw_value && ++ hw_value >= hw_priv->mcs_rates[0].hw_value) ++ ret -= hw_priv->mcs_rates[0].hw_value; ++ else ++ ret = -1; ++ } else { //11b/g ++ if(hw_value>5 && hw_valuemcs_rates[0].hw_value) { ++ ret -= hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value; ++ if(hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value<5) //11a ++ ret -= 2; ++ } else if(hw_value<4) { ++ ret -= hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value; ++ } else { ++ ret = -1; ++ } ++ } ++ return (s8)ret; ++} ++ ++static int ++xradio_tx_h_calc_link_ids(struct xradio_vif *priv, ++ struct ieee80211_tx_control *control, ++ struct xradio_txinfo *t) ++{ ++#ifndef P2P_MULTIVIF ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++#endif ++ ++ ++#ifndef P2P_MULTIVIF ++ if ((t->tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || ++ (hw_priv->roc_if_id == priv->if_id)) ++ t->txpriv.offchannel_if_id = 2; ++ else ++ t->txpriv.offchannel_if_id = 0; ++#endif ++ ++ if (likely(control->sta && t->sta_priv->link_id)) ++ t->txpriv.raw_link_id = ++ t->txpriv.link_id = ++ t->sta_priv->link_id; ++ else if (priv->mode != NL80211_IFTYPE_AP) ++ t->txpriv.raw_link_id = ++ t->txpriv.link_id = 0; ++ else if (is_multicast_ether_addr(t->da)) { ++ if (priv->enable_beacon) { ++ t->txpriv.raw_link_id = 0; ++ t->txpriv.link_id = priv->link_id_after_dtim; ++ } else { ++ t->txpriv.raw_link_id = 0; ++ t->txpriv.link_id = 0; ++ } ++ } else { ++ t->txpriv.link_id = ++ xradio_find_link_id(priv, t->da); ++ /* Do not assign valid link id for deauth/disassoc frame being ++ transmitted to an unassociated STA */ ++ if (!(t->txpriv.link_id) && ++ (ieee80211_is_deauth(t->hdr->frame_control) || ++ ieee80211_is_disassoc(t->hdr->frame_control))) { ++ t->txpriv.link_id = 0; ++ } else { ++ if (!t->txpriv.link_id) ++ t->txpriv.link_id = xradio_alloc_link_id(priv, t->da); ++ if (!t->txpriv.link_id) { ++ txrx_printk(XRADIO_DBG_ERROR, ++ "%s: No more link IDs available.\n", __func__); ++ return -ENOENT; ++ } ++ } ++ t->txpriv.raw_link_id = t->txpriv.link_id; ++ } ++ if (t->txpriv.raw_link_id) ++ priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = ++ jiffies; ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ if (control->sta && ++ (control->sta->uapsd_queues & BIT(t->queue))) ++ t->txpriv.link_id = priv->link_id_uapsd; ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ return 0; ++} ++ ++static void ++xradio_tx_h_pm(struct xradio_vif *priv, ++ struct xradio_txinfo *t) ++{ ++ ++ ++ if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) { ++ u32 mask = ~BIT(t->txpriv.raw_link_id); ++ spin_lock_bh(&priv->ps_state_lock); ++ priv->sta_asleep_mask &= mask; ++ priv->pspoll_mask &= mask; ++ spin_unlock_bh(&priv->ps_state_lock); ++ } ++} ++ ++static void ++xradio_tx_h_calc_tid(struct xradio_vif *priv, ++ struct xradio_txinfo *t) ++{ ++ if (ieee80211_is_data_qos(t->hdr->frame_control)) { ++ u8 *qos = ieee80211_get_qos_ctl(t->hdr); ++ t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; ++ } else if (ieee80211_is_data(t->hdr->frame_control)) { ++ t->txpriv.tid = 0; ++ } ++} ++ ++/* IV/ICV injection. */ ++/* TODO: Quite unoptimal. It's better co modify mac80211 ++ * to reserve space for IV */ ++static int ++xradio_tx_h_crypt(struct xradio_vif *priv, ++ struct xradio_txinfo *t) ++{ ++ size_t iv_len; ++ size_t icv_len; ++ u8 *icv; ++ ++ ++ if (!t->tx_info->control.hw_key || ++ !(t->hdr->frame_control & ++ __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) ++ return 0; ++ ++ iv_len = t->tx_info->control.hw_key->iv_len; ++ icv_len = t->tx_info->control.hw_key->icv_len; ++ ++ if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) ++ icv_len += 8; /* MIC */ ++ ++ if ((skb_headroom(t->skb) + skb_tailroom(t->skb) < ++ iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || ++ (skb_headroom(t->skb) < ++ iv_len + WSM_TX_EXTRA_HEADROOM)) { ++ txrx_printk(XRADIO_DBG_ERROR, ++ "Bug: no space allocated for crypto headers.\n" ++ "headroom: %d, tailroom: %d, " ++ "req_headroom: %d, req_tailroom: %d\n" ++ "Please fix it in xradio_get_skb().\n", ++ skb_headroom(t->skb), skb_tailroom(t->skb), ++ iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); ++ return -ENOMEM; ++ } else if (skb_tailroom(t->skb) < icv_len) { ++ size_t offset = icv_len - skb_tailroom(t->skb); ++ u8 *p; ++ txrx_printk(XRADIO_DBG_ERROR, ++ "Slowpath: tailroom is not big enough. " ++ "Req: %d, got: %d.\n", ++ icv_len, skb_tailroom(t->skb)); ++ ++ p = skb_push(t->skb, offset); ++ memmove(p, &p[offset], t->skb->len - offset); ++ skb_trim(t->skb, t->skb->len - offset); ++ } ++ /* ccmp pkt from umac to driver,it has iv room,,so ccmp pkt do not add iv room */ ++ if (t->tx_info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP){ ++ u8 *newhdr; ++ newhdr = skb_push(t->skb, iv_len); ++ memmove(newhdr, newhdr + iv_len, t->hdrlen); ++ t->hdr = (struct ieee80211_hdr *) newhdr; ++ } ++ t->hdrlen += iv_len; ++ icv = skb_put(t->skb, icv_len); ++ ++ return 0; ++} ++ ++static int ++xradio_tx_h_align(struct xradio_vif *priv, struct xradio_txinfo *t, ++ u8 *flags) ++{ ++ size_t offset = (size_t)t->skb->data & 3; ++ u8 *newhdr;//add by dingxh ++ ++ ++ if (!offset) ++ return 0; ++ ++ if (skb_headroom(t->skb) < offset) { ++ txrx_printk(XRADIO_DBG_ERROR, ++ "Bug: no space allocated " ++ "for DMA alignment.\n" ++ "headroom: %d\n", ++ skb_headroom(t->skb)); ++ return -ENOMEM; ++ } ++ //offset = 1or3 process add by dingxh ++ if (offset & 1) { ++ newhdr = skb_push(t->skb, offset); ++ memmove(newhdr, newhdr + offset, t->skb->len-offset); ++ skb_trim(t->skb, t->skb->len-offset); ++ t->hdr = (struct ieee80211_hdr *) newhdr; ++ xradio_debug_tx_align(priv); ++ return 0; ++ } ++ //add by dingxh ++ //offset=2 process ++ skb_push(t->skb, offset); ++ t->hdrlen += offset; ++ t->txpriv.offset += offset; ++ *flags |= WSM_TX_2BYTES_SHIFT; ++ xradio_debug_tx_align(priv); ++ return 0; ++} ++ ++static int ++xradio_tx_h_action(struct xradio_vif *priv, struct xradio_txinfo *t) ++{ ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)t->hdr; ++ ++ ++ if (ieee80211_is_action(t->hdr->frame_control) && ++ mgmt->u.action.category == WLAN_CATEGORY_BACK) ++ return 1; ++ else ++ return 0; ++} ++ ++/* Add WSM header */ ++static struct wsm_tx * ++xradio_tx_h_wsm(struct xradio_vif *priv, struct xradio_txinfo *t) ++{ ++ struct wsm_tx *wsm; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { ++ txrx_printk(XRADIO_DBG_ERROR, ++ "Bug: no space allocated " ++ "for WSM header.\n" ++ "headroom: %d\n", ++ skb_headroom(t->skb)); ++ return NULL; ++ } ++ ++ wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); ++ t->txpriv.offset += sizeof(struct wsm_tx); ++ memset(wsm, 0, sizeof(*wsm)); ++ wsm->hdr.len = __cpu_to_le16(t->skb->len); ++ wsm->hdr.id = __cpu_to_le16(0x0004); ++ wsm->queueId = (t->txpriv.raw_link_id << 2) | wsm_queue_id_to_wsm(t->queue); ++ if (wsm->hdr.len > hw_priv->wsm_caps.sizeInpChBuf) { ++ txrx_printk(XRADIO_DBG_ERROR,"%s,msg length too big=%d\n", ++ __func__, wsm->hdr.len); ++ wsm = NULL; ++ } ++ ++ return wsm; ++} ++ ++/* BT Coex specific handling */ ++static void ++xradio_tx_h_bt(struct xradio_vif *priv, struct xradio_txinfo *t, struct wsm_tx *wsm) ++{ ++ u8 priority = 0; ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ ++ if (!hw_priv->is_BT_Present) ++ return; ++ ++ if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control))) ++ priority = WSM_EPTA_PRIORITY_MGT; ++ else if (ieee80211_is_data(t->hdr->frame_control)) { ++ /* Skip LLC SNAP header (+6) */ ++ u8 *payload = &t->skb->data[t->hdrlen]; ++ u16 *ethertype = (u16 *) &payload[6]; ++ if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE))) ++ priority = WSM_EPTA_PRIORITY_EAPOL; ++ } else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || ++ ieee80211_is_reassoc_req(t->hdr->frame_control))) { ++ struct ieee80211_mgmt *mgt_frame = ++ (struct ieee80211_mgmt *)t->hdr; ++ ++ if (mgt_frame->u.assoc_req.listen_interval < ++ priv->listen_interval) { ++ txrx_printk(XRADIO_DBG_MSG, ++ "Modified Listen Interval to %d from %d\n", ++ priv->listen_interval, ++ mgt_frame->u.assoc_req.listen_interval); ++ /* Replace listen interval derieved from ++ * the one read from SDD */ ++ mgt_frame->u.assoc_req.listen_interval = ++ priv->listen_interval; ++ } ++ } ++ ++ if (likely(!priority)) { ++ if (ieee80211_is_action(t->hdr->frame_control)) ++ priority = WSM_EPTA_PRIORITY_ACTION; ++ else if (ieee80211_is_mgmt(t->hdr->frame_control)) ++ priority = WSM_EPTA_PRIORITY_MGT; ++ else if ((wsm->queueId == WSM_QUEUE_VOICE)) ++ priority = WSM_EPTA_PRIORITY_VOICE; ++ else if ((wsm->queueId == WSM_QUEUE_VIDEO)) ++ priority = WSM_EPTA_PRIORITY_VIDEO; ++ else ++ priority = WSM_EPTA_PRIORITY_DATA; ++ } ++ ++ txrx_printk(XRADIO_DBG_MSG, "[TX] EPTA priority %d.\n", ++ priority); ++ ++ wsm->flags |= priority << 1; ++} ++ ++static int ++xradio_tx_h_rate_policy(struct xradio_common *hw_priv, struct xradio_txinfo *t, ++ struct wsm_tx *wsm) ++{ ++ bool tx_policy_renew = false; ++ struct xradio_vif *priv = ++ xrwl_get_vif_from_ieee80211(t->tx_info->control.vif); ++ ++ ++ /*use debug policy for data frames only*/ ++#ifdef CONFIG_XRADIO_DEBUGFS ++ if((rates_dbg_en & 0x1) && ieee80211_is_data(t->hdr->frame_control)) { ++ rates_dbg_en |= 0x02; ++ } ++#endif ++ ++ t->txpriv.rate_id = tx_policy_get(hw_priv, ++ t->tx_info->control.rates, t->txpriv.use_bg_rate, ++ &tx_policy_renew); ++ if (t->txpriv.rate_id == XRADIO_INVALID_RATE_ID) ++ return -EFAULT; ++ ++ wsm->flags |= t->txpriv.rate_id << 4; ++ t->rate = xradio_get_tx_rate(hw_priv, &t->tx_info->control.rates[0]); ++ if (t->txpriv.use_bg_rate) ++ wsm->maxTxRate = (u8)(t->txpriv.use_bg_rate & 0x3f); ++ else ++ wsm->maxTxRate = t->rate->hw_value; ++ ++ /*set the maxTxRate and clear the dataframe flag of rates_dbg_en */ ++#ifdef CONFIG_XRADIO_DEBUGFS ++ if(rates_dbg_en & 0x02) { ++ wsm->maxTxRate = maxRate_dbg; ++ rates_dbg_en &= ~0x2; ++ } ++#endif ++ ++ if (t->rate->flags & IEEE80211_TX_RC_MCS) { ++ if (priv->association_mode.greenfieldMode) ++ wsm->htTxParameters |= ++ __cpu_to_le32(WSM_HT_TX_GREENFIELD); ++ else ++ wsm->htTxParameters |= ++ __cpu_to_le32(WSM_HT_TX_MIXED); ++ } ++ ++ if (tx_policy_renew) { ++ txrx_printk(XRADIO_DBG_MSG, "[TX] TX policy renew.\n"); ++ /* It's not so optimal to stop TX queues every now and then. ++ * Maybe it's better to reimplement task scheduling with ++ * a counter. */ ++ /* xradio_tx_queues_lock(priv); */ ++ /* Definetly better. TODO. */ ++ if (atomic_add_return(1, &hw_priv->upload_count) == 1) { ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, ++ &hw_priv->tx_policy_upload_work) <= 0) { ++ atomic_set(&hw_priv->upload_count, 0); ++ wsm_unlock_tx(hw_priv); ++ } ++ } ++ } ++ return 0; ++} ++ ++static bool ++xradio_tx_h_pm_state(struct xradio_vif *priv, struct xradio_txinfo *t) ++{ ++ int was_buffered = 1; ++ ++ ++ if (t->txpriv.link_id == priv->link_id_after_dtim && ++ !priv->buffered_multicasts) { ++ priv->buffered_multicasts = true; ++ if (priv->sta_asleep_mask) ++ queue_work(priv->hw_priv->workqueue, ++ &priv->multicast_start_work); ++ } ++ ++ if (t->txpriv.raw_link_id && t->txpriv.tid < XRADIO_MAX_TID) ++ was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1] ++ .buffered[t->txpriv.tid]++; ++ ++ return !was_buffered; ++} ++ ++static void ++xradio_tx_h_ba_stat(struct xradio_vif *priv, ++ struct xradio_txinfo *t) ++{ ++ struct xradio_common *hw_priv = priv->hw_priv; ++ ++ ++ if (priv->join_status != XRADIO_JOIN_STATUS_STA) ++ return; ++ if (!xradio_is_ht(&hw_priv->ht_oper)) ++ return; ++ if (!priv->setbssparams_done) ++ return; ++ if (!ieee80211_is_data(t->hdr->frame_control)) ++ return; ++ ++ spin_lock_bh(&hw_priv->ba_lock); ++ hw_priv->ba_acc += t->skb->len - t->hdrlen; ++ if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { ++ mod_timer(&hw_priv->ba_timer, ++ jiffies + XRADIO_BLOCK_ACK_INTERVAL); ++ } ++ hw_priv->ba_cnt++; ++ spin_unlock_bh(&hw_priv->ba_lock); ++} ++ ++static int ++xradio_tx_h_skb_pad(struct xradio_common *priv, ++ struct wsm_tx *wsm, ++ struct sk_buff *skb) ++{ ++ size_t len = __le16_to_cpu(wsm->hdr.len); ++ size_t padded_len = sdio_align_len(priv, len); ++ ++ ++ if (SYS_WARN(skb_padto(skb, padded_len) != 0)) { ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/* ******************************************************************** */ ++//#if (defined(CONFIG_XRADIO_DEBUG)) ++u16 txparse_flags = 0;//PF_DHCP|PF_8021X|PF_MGMT; ++u16 rxparse_flags = 0;//PF_DHCP|PF_8021X|PF_MGMT; ++//#endif ++ ++void xradio_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb) ++{ ++ struct xradio_common *hw_priv = dev->priv; ++ struct xradio_txinfo t = { ++ .skb = skb, ++ .queue = skb_get_queue_mapping(skb), ++ .tx_info = IEEE80211_SKB_CB(skb), ++ .hdr = (struct ieee80211_hdr *)skb->data, ++ .txpriv.tid = XRADIO_MAX_TID, ++ .txpriv.rate_id = XRADIO_INVALID_RATE_ID, ++#ifdef P2P_MULTIVIF ++ .txpriv.raw_if_id = 0, ++#endif ++ .txpriv.use_bg_rate = 0, ++ }; ++ struct ieee80211_sta *sta; ++ struct wsm_tx *wsm; ++ bool tid_update = 0; ++ u8 flags = 0; ++ int ret = 0; ++ struct xradio_vif *priv; ++ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; ++ ++ if (!skb->data) ++ SYS_BUG(1); ++ ++ if (!(t.tx_info->control.vif)) { ++ ret = __LINE__; ++ goto drop; ++ } ++ priv = xrwl_get_vif_from_ieee80211(t.tx_info->control.vif); ++ if (!priv) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ++ if (atomic_read(&priv->enabled) == 0) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ++#if (defined(CONFIG_XRADIO_DEBUG)) ++ /* parse frame for debug. */ ++ if (txparse_flags){ ++ u8 temp_iv_len ; ++ if(t.tx_info->control.hw_key && ++ (t.hdr->frame_control & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && ++ (t.tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP)) ++ temp_iv_len = t.tx_info->control.hw_key->iv_len; ++ else ++ temp_iv_len =0; ++ xradio_parse_frame(skb->data, temp_iv_len, txparse_flags, priv->if_id); ++ } ++#endif ++ ++ //dhcp and 80211 frames are important, use b/g rate and delay scan. ++ //it can make sense, such as accelerate connect. ++ if (ieee80211_is_auth(frame->frame_control)) { ++ hw_priv->connet_time[priv->if_id] = jiffies; ++ } else if (ieee80211_is_data_present(frame->frame_control)) { ++ /* since Umac had already alloc IV space in ccmp skb, so we need to add this iv_len as the new offset to LLC */ ++ u8* llc = NULL; ++ if(t.tx_info->control.hw_key && ++ (t.hdr->frame_control & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && ++ (t.tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP)) ++ llc = skb->data+ieee80211_hdrlen(frame->frame_control) + t.tx_info->control.hw_key->iv_len; ++ else ++ llc = skb->data+ieee80211_hdrlen(frame->frame_control); ++ if (is_dhcp(llc) || is_8021x(llc)) { ++ t.txpriv.use_bg_rate = ++ hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value; ++ if (priv->vif->p2p) ++ t.txpriv.use_bg_rate = AG_RATE_INDEX; ++ t.txpriv.use_bg_rate |= 0x80; ++ } ++ if (t.txpriv.use_bg_rate){ ++ hw_priv->connet_time[priv->if_id] = jiffies; ++ } ++ } else if (ieee80211_is_deauth(frame->frame_control) || ++ ieee80211_is_disassoc(frame->frame_control)) { ++ hw_priv->connet_time[priv->if_id] = 0; ++ } ++ ++#ifdef AP_HT_COMPAT_FIX ++ if (ieee80211_is_assoc_req(frame->frame_control) && ++ priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) { ++ xradio_remove_ht_ie(priv, skb); ++ } ++#endif ++ ++#ifdef CONFIG_XRADIO_TESTMODE ++ spin_lock_bh(&hw_priv->tsm_lock); ++ if (hw_priv->start_stop_tsm.start) { ++ if (hw_priv->tsm_info.ac == t.queue) ++ hw_priv->tsm_stats.txed_msdu_count++; ++ } ++ spin_unlock_bh(&hw_priv->tsm_lock); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++ xradio_frame_monitor(hw_priv,skb,true); ++#endif ++ ++ if (ieee80211_is_action(frame->frame_control) && ++ mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { ++ u8 *action = (u8*)&mgmt->u.action.category; ++ xradio_check_go_neg_conf_success(hw_priv, action); ++ xradio_check_prov_desc_req(hw_priv, action); ++ } ++ ++ t.txpriv.if_id = priv->if_id; ++ t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); ++ t.da = ieee80211_get_DA(t.hdr); ++ t.sta_priv = ++ (struct xradio_sta_priv *)&control->sta->drv_priv; ++ ++ if (SYS_WARN(t.queue >= 4)) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ++ //spin_lock_bh(&hw_priv->tx_queue[t.queue].lock); ++ //if ((priv->if_id == 0) && ++ // (hw_priv->tx_queue[t.queue].num_queued_vif[0] >= ++ // hw_priv->vif0_throttle)) { ++ // spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); ++ // ++ // ret = __LINE__; ++ // goto drop; ++ //} else if ((priv->if_id == 1) && ++ // (hw_priv->tx_queue[t.queue].num_queued_vif[1] >= ++ // hw_priv->vif1_throttle)) { ++ // spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); ++ // ++ // ret = __LINE__; ++ // goto drop; ++ //} ++ //spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); ++ ++ ret = xradio_tx_h_calc_link_ids(priv, control, &t); ++ if (ret) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ++ dev_dbg(hw_priv->pdev, "vif %d: tx, %d bytes queue %d, link_id %d(%d).\n", ++ priv->if_id, skb->len, t.queue, t.txpriv.link_id, t.txpriv.raw_link_id); ++ if(ieee80211_is_assoc_resp(frame->frame_control)){ ++ dev_dbg(hw_priv->pdev, "vif %d: association response\n", priv->if_id); ++ } ++ ++ xradio_tx_h_pm(priv, &t); ++ xradio_tx_h_calc_tid(priv, &t); ++ ret = xradio_tx_h_crypt(priv, &t); ++ if (ret) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ret = xradio_tx_h_align(priv, &t, &flags); ++ if (ret) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ret = xradio_tx_h_action(priv, &t); ++ if (ret) { ++ ret = __LINE__; ++ goto drop; ++ } ++ wsm = xradio_tx_h_wsm(priv, &t); ++ if (!wsm) { ++ ret = __LINE__; ++ goto drop; ++ } ++#ifdef CONFIG_XRADIO_TESTMODE ++ flags |= WSM_TX_FLAG_EXPIRY_TIME; ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ wsm->flags |= flags; ++ xradio_tx_h_bt(priv, &t, wsm); ++ ret = xradio_tx_h_rate_policy(hw_priv, &t, wsm); ++ if (ret) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ++ ret = xradio_tx_h_skb_pad(hw_priv, wsm, skb); ++ if (ret) { ++ ret = __LINE__; ++ goto drop; ++ } ++ ++ rcu_read_lock(); ++ sta = rcu_dereference(control->sta); ++ ++ xradio_tx_h_ba_stat(priv, &t); ++ spin_lock_bh(&priv->ps_state_lock); ++ { ++ tid_update = xradio_tx_h_pm_state(priv, &t); ++ SYS_BUG(xradio_queue_put(&hw_priv->tx_queue[t.queue], ++ t.skb, &t.txpriv)); ++#ifdef ROC_DEBUG ++ txrx_printk(XRADIO_DBG_ERROR, "QPUT %x, %pM, if_id - %d\n", ++ t.hdr->frame_control, t.da, priv->if_id); ++#endif ++ } ++ spin_unlock_bh(&priv->ps_state_lock); ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ if (tid_update && sta) ++ ieee80211_sta_set_buffered(sta, ++ t.txpriv.tid, true); ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++ ++ rcu_read_unlock(); ++ ++ xradio_bh_wakeup(hw_priv); ++ ++ return; ++ ++drop: ++ dev_dbg(hw_priv->pdev, "dropped tx at line %d, fctl=0x%04x.\n", ret, frame->frame_control); ++ xradio_skb_dtor(hw_priv, skb, &t.txpriv); ++ return; ++} ++ ++/* ******************************************************************** */ ++ ++static int xradio_handle_pspoll(struct xradio_vif *priv, ++ struct sk_buff *skb) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct ieee80211_sta *sta; ++ struct ieee80211_pspoll *pspoll = ++ (struct ieee80211_pspoll *) skb->data; ++ int link_id = 0; ++ u32 pspoll_mask = 0; ++ int drop = 1; ++ int i; ++ ++ ++ if (priv->join_status != XRADIO_JOIN_STATUS_AP) ++ goto done; ++ if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) ++ goto done; ++ ++ rcu_read_lock(); ++ sta = ieee80211_find_sta(priv->vif, pspoll->ta); ++ if (sta) { ++ struct xradio_sta_priv *sta_priv; ++ sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; ++ link_id = sta_priv->link_id; ++ pspoll_mask = BIT(sta_priv->link_id); ++ } ++ rcu_read_unlock(); ++ if (!link_id) ++ goto done; ++ ++ priv->pspoll_mask |= pspoll_mask; ++ drop = 0; ++ ++ /* Do not report pspols if data for given link id is ++ * queued already. */ ++ for (i = 0; i < 4; ++i) { ++ if (xradio_queue_get_num_queued(priv, ++ &hw_priv->tx_queue[i], ++ pspoll_mask)) { ++ xradio_bh_wakeup(hw_priv); ++ drop = 1; ++ break; ++ } ++ } ++ txrx_printk(XRADIO_DBG_NIY, "[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); ++done: ++ return drop; ++} ++ ++/* ******************************************************************** */ ++//for test yangfh ++extern u32 tx_retrylimit; ++extern u32 tx_over_limit; ++extern u32 tx_lower_limit; ++extern int retry_mis; ++ ++void xradio_tx_confirm_cb(struct xradio_common *hw_priv, ++ struct wsm_tx_confirm *arg) ++{ ++ u8 queue_id = xradio_queue_get_queue_id(arg->packetID); ++ struct xradio_queue *queue = &hw_priv->tx_queue[queue_id]; ++ struct sk_buff *skb; ++ const struct xradio_txpriv *txpriv; ++ struct xradio_vif *priv; ++ u32 feedback_retry = 0; ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg->if_id); ++ if (unlikely(!priv)) ++ return; ++ ++ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { ++ /* STA is stopped. */ ++ spin_unlock(&priv->vif_lock); ++ return; ++ } ++ ++ if (SYS_WARN(queue_id >= 4)) { ++ spin_unlock(&priv->vif_lock); ++ return; ++ } ++ ++ dev_dbg(hw_priv->pdev, "vif %d: tx confirm status=%d, retry=%d, lastRate=%d\n", ++ priv->if_id, arg->status, arg->ackFailures, arg->txedRate); ++ ++ if ((arg->status == WSM_REQUEUE) && ++ (arg->flags & WSM_TX_STATUS_REQUEUE)) { ++ /* "Requeue" means "implicit suspend" */ ++ struct wsm_suspend_resume suspend = { ++ .link_id = arg->link_id, ++ .stop = 1, ++ .multicast = !arg->link_id, ++ .if_id = arg->if_id, ++ }; ++ xradio_suspend_resume(priv, &suspend); ++ txrx_printk(XRADIO_DBG_WARN, "Requeue for link_id %d (try %d)." ++ " STAs asleep: 0x%.8X\n", ++ arg->link_id, ++ xradio_queue_get_generation(arg->packetID) + 1, ++ priv->sta_asleep_mask); ++ ++ SYS_WARN(xradio_queue_requeue(queue, ++ arg->packetID, true)); ++ ++ spin_lock_bh(&priv->ps_state_lock); ++ if (!arg->link_id) { ++ priv->buffered_multicasts = true; ++ if (priv->sta_asleep_mask) { ++ queue_work(hw_priv->workqueue, ++ &priv->multicast_start_work); ++ } ++ } ++ spin_unlock_bh(&priv->ps_state_lock); ++ spin_unlock(&priv->vif_lock); ++ } else if (!SYS_WARN(xradio_queue_get_skb( ++ queue, arg->packetID, &skb, &txpriv))) { ++ struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); ++ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; ++ int tx_count = arg->ackFailures; ++ u8 ht_flags = 0; ++ int i; ++ ++ //yangfh add to reset if_0 in firmware when STA-unjoined, ++ //fix the errors when switch APs in combo mode. ++ if (unlikely(ieee80211_is_disassoc(frame->frame_control) || ++ ieee80211_is_deauth(frame->frame_control))) { ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ wms_send_deauth_to_self(hw_priv, priv); ++ /* Shedule unjoin work */ ++ txrx_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX) by self.\n"); ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ } ++ } ++ ++ if (priv->association_mode.greenfieldMode) ++ ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; ++ ++ //bss loss confirm. ++ if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING && ++ priv->bss_loss_confirm_id == arg->packetID)) { ++ spin_lock(&priv->bss_loss_lock); ++ priv->bss_loss_status = arg->status? ++ XRADIO_BSS_LOSS_CONFIRMED : XRADIO_BSS_LOSS_NONE; ++ spin_unlock(&priv->bss_loss_lock); ++ } ++ ++ if (likely(!arg->status)) { ++ tx->flags |= IEEE80211_TX_STAT_ACK; ++ priv->cqm_tx_failure_count = 0; ++ ++tx_count; ++ if (arg->txedRate<24) ++ TxedRateIdx_Map[arg->txedRate]++; ++ else ++ SYS_WARN(1); ++ xradio_debug_txed(priv); ++ if (arg->flags & WSM_TX_STATUS_AGGREGATION) { ++ /* Do not report aggregation to mac80211: ++ * it confuses minstrel a lot. */ ++ /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ ++ xradio_debug_txed_agg(priv); ++ } ++ } else { ++ /* TODO: Update TX failure counters */ ++ if (unlikely(priv->cqm_tx_failure_thold && ++ (++priv->cqm_tx_failure_count > ++ priv->cqm_tx_failure_thold))) { ++ priv->cqm_tx_failure_thold = 0; ++ queue_work(hw_priv->workqueue, ++ &priv->tx_failure_work); ++ } ++ if (tx_count) ++ ++tx_count; ++ } ++ spin_unlock(&priv->vif_lock); ++ ++ tx->status.ampdu_len = 1; ++ tx->status.ampdu_ack_len = 1; ++ ++ txrx_printk(XRADIO_DBG_NIY,"feedback:%08x, %08x, %08x.\n", ++ arg->rate_try[2], arg->rate_try[1], arg->rate_try[0]); ++ if(txpriv->use_bg_rate) { //bg rates ++ tx->status.rates[0].count = arg->ackFailures+1; ++ tx->status.rates[0].idx = 0; ++ tx->status.rates[1].idx = -1; ++ tx->status.rates[2].idx = -1; ++ tx->status.rates[3].idx = -1; ++ } else { ++ int j; ++ s8 txed_idx; ++ register u8 rate_num=0, shift=0, retries=0; ++ u8 flag = tx->status.rates[0].flags; ++ ++ //get retry rate idx. ++ for(i=2; i>=0;i--) { ++ if(arg->rate_try[i]) { ++ for(j=7; j>=0;j--) { ++ shift = j<<2; ++ retries = (arg->rate_try[i]>>shift)&0xf; ++ if(retries) { ++ feedback_retry += retries; ++ txed_idx = xradio_get_rate_idx(hw_priv,flag,((i<<3)+j)); ++ txrx_printk(XRADIO_DBG_NIY, "rate_num=%d, hw=%d, idx=%d, " ++ "retries=%d, flag=%d", rate_num, ((i<<3)+j), ++ txed_idx, retries, flag); ++ if(likely(txed_idx>=0)) { ++ tx->status.rates[rate_num].idx = txed_idx; ++ tx->status.rates[rate_num].count = retries; ++ if (tx->status.rates[rate_num].flags & IEEE80211_TX_RC_MCS) ++ tx->status.rates[rate_num].flags |= ht_flags; ++ rate_num++; ++ if(rate_num>=IEEE80211_TX_MAX_RATES) { ++ i = -1; ++ break; ++ } ++ } ++ } ++ } ++ } ++ } ++ //clear other rate. ++ for (i=rate_num; i < IEEE80211_TX_MAX_RATES; ++i) { ++ tx->status.rates[i].count = 0; ++ tx->status.rates[i].idx = -1; ++ } ++ //get successful rate idx. ++ if(!arg->status) { ++ txed_idx = xradio_get_rate_idx(hw_priv, flag, arg->txedRate); ++ if(rate_num == 0) { ++ tx->status.rates[0].idx = txed_idx; ++ tx->status.rates[0].count = 1; ++ } else if(rate_num <= IEEE80211_TX_MAX_RATES){ ++ --rate_num; ++ if(txed_idx == tx->status.rates[rate_num].idx) { ++ tx->status.rates[rate_num].count += 1; ++ } else if(rate_num<(IEEE80211_TX_MAX_RATES-1)){ ++ ++rate_num; ++ tx->status.rates[rate_num].idx = txed_idx; ++ tx->status.rates[rate_num].count = 1; ++ } else if(txed_idx >=0) { ++ tx->status.rates[rate_num].idx = txed_idx; ++ tx->status.rates[rate_num].count = 1; ++ } ++ } ++ } ++ } ++ ++#ifdef CONFIG_XRADIO_DEBUGFS ++ if (arg->status == WSM_STATUS_RETRY_EXCEEDED) { ++ tx_retrylimit++; ++ retry_mis += ((s32)hw_priv->short_frame_max_tx_count-arg->ackFailures-1); ++ if(arg->ackFailures != (hw_priv->short_frame_max_tx_count-1)) { ++ if(arg->ackFailures < (hw_priv->short_frame_max_tx_count-1)) ++ tx_lower_limit++; ++ else ++ tx_over_limit++; ++ txrx_printk(XRADIO_DBG_WARN, "retry_err, ackFailures=%d, feedbk_retry=%d.\n", ++ arg->ackFailures, feedback_retry); ++ } ++ } else if (feedback_retry > hw_priv->short_frame_max_tx_count-1) { ++ txrx_printk(XRADIO_DBG_WARN, "status=%d, ackFailures=%d, feedbk_retry=%d.\n", ++ arg->status, arg->ackFailures, feedback_retry); ++ } ++#endif ++ ++ dev_dbg(hw_priv->pdev, "[TX policy] Ack: " \ ++ "%d:%d, %d:%d, %d:%d, %d:%d\n", ++ tx->status.rates[0].idx, tx->status.rates[0].count, ++ tx->status.rates[1].idx, tx->status.rates[1].count, ++ tx->status.rates[2].idx, tx->status.rates[2].count, ++ tx->status.rates[3].idx, tx->status.rates[3].count); ++ ++ ++ xradio_queue_remove(queue, arg->packetID); ++ } ++} ++ ++static void xradio_notify_buffered_tx(struct xradio_vif *priv, ++ struct sk_buff *skb, int link_id, int tid) ++{ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ struct ieee80211_sta *sta; ++ struct ieee80211_hdr *hdr; ++ u8 *buffered; ++ u8 still_buffered = 0; ++ ++ ++ if (link_id && tid < XRADIO_MAX_TID) { ++ buffered = priv->link_id_db ++ [link_id - 1].buffered; ++ ++ spin_lock_bh(&priv->ps_state_lock); ++ if (!SYS_WARN(!buffered[tid])) ++ still_buffered = --buffered[tid]; ++ spin_unlock_bh(&priv->ps_state_lock); ++ ++ if (!still_buffered && tid < XRADIO_MAX_TID) { ++ hdr = (struct ieee80211_hdr *) skb->data; ++ rcu_read_lock(); ++ sta = ieee80211_find_sta(priv->vif, hdr->addr1); ++ if (sta) ++ ieee80211_sta_set_buffered(sta, tid, false); ++ rcu_read_unlock(); ++ } ++ } ++#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ ++} ++ ++void xradio_skb_dtor(struct xradio_common *hw_priv, ++ struct sk_buff *skb, ++ const struct xradio_txpriv *txpriv) ++{ ++ struct xradio_vif *priv = ++ __xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); ++ ++ ++ skb_pull(skb, txpriv->offset); ++ if (priv && txpriv->rate_id != XRADIO_INVALID_RATE_ID) { ++ xradio_notify_buffered_tx(priv, skb, ++ txpriv->raw_link_id, txpriv->tid); ++ tx_policy_put(hw_priv, txpriv->rate_id); ++ } ++ ieee80211_tx_status(hw_priv->hw, skb); ++} ++#ifdef CONFIG_XRADIO_TESTMODE ++/* TODO It should be removed before official delivery */ ++static void frame_hexdump(char *prefix, u8 *data, int len) ++{ ++ int i; ++ ++ txrx_printk(XRADIO_DBG_MSG, "%s hexdump:\n", prefix); ++ for (i = 0; i < len; i++) { ++ if (i + 10 < len) { ++ txrx_printk(XRADIO_DBG_MSG, "%.1X %.1X %.1X %.1X" \ ++ "%.1X %.1X %.1X %.1X %.1X %.1X", ++ data[i], data[i+1], data[i+2], ++ data[i+3], data[i+4], data[i+5], ++ data[i+6], data[i+7], data[i+8], ++ data[i+9]); ++ i += 9; ++ } else { ++ txrx_printk(XRADIO_DBG_MSG, "%.1X ", data[i]); ++ } ++ } ++} ++/** ++ * c1200_tunnel_send_testmode_data - Send test frame to the driver ++ * ++ * @priv: pointer to xradio private structure ++ * @skb: skb with frame ++ * ++ * Returns: 0 on success or non zero value on failure ++ */ ++static int xradio_tunnel_send_testmode_data(struct xradio_common *hw_priv, ++ struct sk_buff *skb) ++{ ++ ++ if (xradio_tesmode_event(hw_priv->hw->wiphy, AW_MSG_EVENT_FRAME_DATA, ++ skb->data, skb->len, GFP_ATOMIC)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/** ++ * xradio_frame_test_detection - Detection frame_test ++ * ++ * @priv: pointer to xradio vif structure ++ * @frame: ieee80211 header ++ * @skb: skb with frame ++ * ++ * Returns: 1 - frame test detected, 0 - not detected ++ */ ++static int xradio_frame_test_detection(struct xradio_vif *priv, ++ struct ieee80211_hdr *frame, ++ struct sk_buff *skb) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ int hdrlen = ieee80211_hdrlen(frame->frame_control); ++ int detected = 0; ++ int ret; ++ ++ ++ if (hdrlen + hw_priv->test_frame.len <= skb->len && ++ memcmp(skb->data + hdrlen, hw_priv->test_frame.data, ++ hw_priv->test_frame.len) == 0) { ++ detected = 1; ++ txrx_printk(XRADIO_DBG_MSG, "TEST FRAME detected"); ++ frame_hexdump("TEST FRAME original:", skb->data, skb->len); ++ ret = ieee80211_data_to_8023(skb, hw_priv->mac_addr, ++ priv->mode); ++ if (!ret) { ++ frame_hexdump("FRAME 802.3:", skb->data, skb->len); ++ ret = xradio_tunnel_send_testmode_data(hw_priv, skb); ++ } ++ if (ret) ++ txrx_printk(XRADIO_DBG_ERROR, "Send TESTFRAME failed(%d)", ret); ++ } ++ return detected; ++} ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++ ++static void ++xradio_rx_h_ba_stat(struct xradio_vif *priv, ++ size_t hdrlen, size_t skb_len ) ++{ ++ struct xradio_common *hw_priv = priv->hw_priv; ++ ++ ++ if (priv->join_status != XRADIO_JOIN_STATUS_STA) ++ return; ++ if (!xradio_is_ht(&hw_priv->ht_oper)) ++ return; ++ if (!priv->setbssparams_done) ++ return; ++ ++ spin_lock_bh(&hw_priv->ba_lock); ++ hw_priv->ba_acc_rx += skb_len - hdrlen; ++ if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { ++ mod_timer(&hw_priv->ba_timer, ++ jiffies + XRADIO_BLOCK_ACK_INTERVAL); ++ } ++ hw_priv->ba_cnt_rx++; ++ spin_unlock_bh(&hw_priv->ba_lock); ++} ++ ++#if 0 ++u8 nettest_bssid[] = {0x00,0x02,0x03,0x04,0x05,0x06}; ++u8 save_rate_ie; ++#endif ++ ++void xradio_rx_cb(struct xradio_vif *priv, ++ struct wsm_rx *arg, ++ struct sk_buff **skb_p) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ struct sk_buff *skb = *skb_p; ++ struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); ++ struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; ++#endif ++ struct xradio_link_entry *entry = NULL; ++ unsigned long grace_period; ++ bool early_data = false; ++ size_t hdrlen = 0; ++ u8 parse_iv_len = 0; ++ ++ dev_dbg(hw_priv->pdev, "vif %d: rx, status %u flags 0x%.8x", ++ priv->if_id, arg->status, arg->flags); ++ if(ieee80211_is_deauth(frame->frame_control)) ++ dev_dbg(hw_priv->pdev, "vif %d: deauth\n", priv->if_id); ++ ++ hdr->flag = 0; ++ ++ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { ++ /* STA is stopped. */ ++ goto drop; ++ } ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++ xradio_frame_monitor(hw_priv,skb,false); ++#endif ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ if ((ieee80211_is_action(frame->frame_control)) ++ && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { ++ u8 *action = (u8*)&mgmt->u.action.category; ++ xradio_check_go_neg_conf_success(hw_priv, action); ++ } ++#endif ++ ++ if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED) ++ && (arg->link_id <= XRADIO_MAX_STA_IN_AP_MODE)) { ++ entry = &priv->link_id_db[arg->link_id - 1]; ++ if (entry->status == XRADIO_LINK_SOFT && ++ ieee80211_is_data(frame->frame_control)) ++ early_data = true; ++ entry->timestamp = jiffies; ++ } ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ else if ((arg->link_id == XRADIO_LINK_ID_UNMAPPED) ++ && (priv->vif->p2p == WSM_START_MODE_P2P_GO) ++ && ieee80211_is_action(frame->frame_control) ++ && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { ++ txrx_printk(XRADIO_DBG_NIY, "[RX] Going to MAP&RESET link ID\n"); ++ ++ if (work_pending(&priv->linkid_reset_work)) ++ SYS_WARN(1); ++ ++ memcpy(&priv->action_frame_sa[0], ++ ieee80211_get_SA(frame), ETH_ALEN); ++ priv->action_linkid = 0; ++ schedule_work(&priv->linkid_reset_work); ++ } ++ ++ if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED) ++ && (priv->vif->p2p == WSM_START_MODE_P2P_GO) ++ && ieee80211_is_action(frame->frame_control) ++ && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { ++ /* Link ID already exists for the ACTION frame. ++ * Reset and Remap */ ++ if (work_pending(&priv->linkid_reset_work)) ++ SYS_WARN(1); ++ memcpy(&priv->action_frame_sa[0], ++ ieee80211_get_SA(frame), ETH_ALEN); ++ priv->action_linkid = arg->link_id; ++ schedule_work(&priv->linkid_reset_work); ++ } ++#endif ++ if (unlikely(arg->status)) { ++ if (arg->status == WSM_STATUS_MICFAILURE) { ++ dev_err(priv->hw_priv->pdev, "[RX] IF=%d, MIC failure.\n", ++ priv->if_id); ++ hdr->flag |= RX_FLAG_MMIC_ERROR; ++ } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { ++ dev_warn(priv->hw_priv->pdev, "received frame has no key status\n"); ++ //goto drop; ++ } else { ++ dev_err(priv->hw_priv->pdev, "[RX] IF=%d, Receive failure: %d.\n", ++ priv->if_id, arg->status); ++ goto drop; ++ } ++ } ++ ++ if (skb->len < sizeof(struct ieee80211_pspoll)) { ++ dev_err(priv->hw_priv->pdev, "Malformed SDU rx'ed. " ++ "Size is lesser than IEEE header.\n"); ++ goto drop; ++ } ++ ++ if (unlikely(ieee80211_is_pspoll(frame->frame_control))) ++ if (xradio_handle_pspoll(priv, skb)) ++ goto drop; ++ ++ hdr->mactime = 0; /* Not supported by WSM */ ++ hdr->band = (arg->channelNumber > 14) ? ++ NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; ++ hdr->freq = ieee80211_channel_to_frequency( ++ arg->channelNumber, ++ hdr->band); ++ ++#ifdef AP_HT_COMPAT_FIX ++ if (!priv->ht_compat_det && priv->htcap && ++ ieee80211_is_data_qos(frame->frame_control)) { ++ if(xradio_apcompat_detect(priv, arg->rxedRate)) ++ goto drop; ++ } ++#endif ++ ++ if (arg->rxedRate<24) ++ RxedRateIdx_Map[arg->rxedRate]++; ++ else ++ SYS_WARN(1); ++ ++ if (arg->rxedRate >= 14) { ++ hdr->flag |= RX_FLAG_HT; ++ hdr->rate_idx = arg->rxedRate - 14; ++ } else if (arg->rxedRate >= 4) { ++ if (hdr->band == NL80211_BAND_5GHZ) ++ hdr->rate_idx = arg->rxedRate - 6; ++ else ++ hdr->rate_idx = arg->rxedRate - 2; ++ } else { ++ hdr->rate_idx = arg->rxedRate; ++ } ++ ++ hdr->signal = (s8)arg->rcpiRssi; ++ hdr->antenna = 0; ++ ++ hdrlen = ieee80211_hdrlen(frame->frame_control); ++ ++ if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { ++ size_t iv_len = 0, icv_len = 0; ++ ++ hdr->flag |= RX_FLAG_DECRYPTED; ++ ++ /* Oops... There is no fast way to ask mac80211 about ++ * IV/ICV lengths. Even defines are not exposed.*/ ++ switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { ++ case WSM_RX_STATUS_WEP: ++ iv_len = 4 /* WEP_IV_LEN */; ++ icv_len = 4 /* WEP_ICV_LEN */; ++ break; ++ case WSM_RX_STATUS_TKIP: ++ iv_len = 8 /* TKIP_IV_LEN */; ++ icv_len = 4 /* TKIP_ICV_LEN */ ++ + 8 /*MICHAEL_MIC_LEN*/; ++ break; ++ case WSM_RX_STATUS_AES: ++ iv_len = 8 /* CCMP_HDR_LEN */; ++ icv_len = 8 /* CCMP_MIC_LEN */; ++ break; ++ case WSM_RX_STATUS_WAPI: ++ iv_len = 18 /* WAPI_HDR_LEN */; ++ icv_len = 16 /* WAPI_MIC_LEN */; ++ hdr->flag |= RX_FLAG_IV_STRIPPED; ++ break; ++ default: ++ SYS_WARN("Unknown encryption type"); ++ goto drop; ++ } ++ ++ /* Firmware strips ICV in case of MIC failure. */ ++ if (arg->status == WSM_STATUS_MICFAILURE) { ++ icv_len = 0; ++ hdr->flag |= RX_FLAG_IV_STRIPPED; ++ } ++ ++ if (skb->len < hdrlen + iv_len + icv_len) { ++ dev_err(priv->hw_priv->pdev, "Mailformed SDU rx'ed. " ++ "Size is lesser than crypto headers.\n"); ++ goto drop; ++ } ++ ++ if (WSM_RX_STATUS_ENCRYPTION(arg->flags) == ++ WSM_RX_STATUS_TKIP) { ++ /* Remove TKIP MIC 8 bytes*/ ++ memmove(skb->data + skb->len-icv_len, ++ skb->data + skb->len-icv_len+8, 4); ++ skb_trim(skb, skb->len - 8); ++ hdr->flag |= RX_FLAG_MMIC_STRIPPED; ++ } else if (unlikely(WSM_RX_STATUS_ENCRYPTION(arg->flags) == ++ WSM_RX_STATUS_WAPI)) { ++ /* Protocols not defined in mac80211 should be ++ stripped/crypted in driver/firmware */ ++ /* Remove IV, ICV and MIC */ ++ skb_trim(skb, skb->len - icv_len); ++ memmove(skb->data + iv_len, skb->data, hdrlen); ++ skb_pull(skb, iv_len); ++ } ++ parse_iv_len = iv_len; ++ } ++ ++ xradio_debug_rxed(priv); ++ if (arg->flags & WSM_RX_STATUS_AGGREGATE) ++ xradio_debug_rxed_agg(priv); ++ ++ if (ieee80211_is_beacon(frame->frame_control) && ++ !arg->status && ++ !memcmp(ieee80211_get_SA(frame), priv->join_bssid,ETH_ALEN)) { ++ const u8 *tim_ie; ++ u8 *ies; ++ size_t ies_len; ++ priv->disable_beacon_filter = false; ++ queue_work(hw_priv->workqueue, &priv->update_filtering_work); ++ ies = ((struct ieee80211_mgmt *) ++ (skb->data))->u.beacon.variable; ++ ies_len = skb->len - (ies - (u8 *)(skb->data)); ++ ++ tim_ie = xradio_get_ie(ies, ies_len, WLAN_EID_TIM); ++ if (tim_ie) { ++ struct ieee80211_tim_ie *tim = ++ (struct ieee80211_tim_ie *)&tim_ie[2]; ++ ++ if (priv->join_dtim_period != tim->dtim_period) { ++ priv->join_dtim_period = tim->dtim_period; ++ queue_work(hw_priv->workqueue, ++ &priv->set_beacon_wakeup_period_work); ++ } ++ } ++ if (unlikely(priv->disable_beacon_filter)) { ++ priv->disable_beacon_filter = false; ++ queue_work(hw_priv->workqueue, ++ &priv->update_filtering_work); ++ } ++ } ++#ifdef AP_HT_CAP_UPDATE ++ if (priv->mode == NL80211_IFTYPE_AP && ++ ieee80211_is_beacon(frame->frame_control) && ++ ((priv->ht_oper&HT_INFO_MASK) != 0x0011) && ++ !arg->status){ ++ u8 *ies; ++ size_t ies_len; ++ const u8 *ht_cap; ++ ies = ((struct ieee80211_mgmt *)(skb->data))->u.beacon.variable; ++ ies_len = skb->len - (ies - (u8 *)(skb->data)); ++ ht_cap = xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY); ++ if(!ht_cap) { ++ priv->ht_oper |= 0x0011; ++ queue_work(hw_priv->workqueue, &priv->ht_oper_update_work); ++ } ++ } ++#endif ++ ++#ifdef AP_HT_COMPAT_FIX ++ if (ieee80211_is_mgmt(frame->frame_control) && ++ priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) { ++ xradio_remove_ht_ie(priv, skb); ++ } ++#endif ++ ++#ifdef ROAM_OFFLOAD ++ if ((ieee80211_is_beacon(frame->frame_control)||ieee80211_is_probe_resp(frame->frame_control)) && ++ !arg->status ) { ++ if (hw_priv->auto_scanning && !atomic_read(&hw_priv->scan.in_progress)) ++ hw_priv->frame_rcvd = 1; ++ ++ if (!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) { ++ if (hw_priv->beacon) ++ dev_kfree_skb(hw_priv->beacon); ++ hw_priv->beacon = skb_copy(skb, GFP_ATOMIC); ++ if (!hw_priv->beacon) ++ txrx_printk(XRADIO_DBG_ERROR, "sched_scan: own beacon storing failed\n"); ++ } ++ } ++#endif /*ROAM_OFFLOAD*/ ++ ++ //don't delay scan before next connect, yangfh. ++ if (ieee80211_is_deauth(frame->frame_control) || ++ ieee80211_is_disassoc(frame->frame_control)) ++ hw_priv->connet_time[priv->if_id] = 0; ++ ++ /* Stay awake for 1sec. after frame is received to give ++ * userspace chance to react and acquire appropriate ++ * wakelock. */ ++ if (ieee80211_is_auth(frame->frame_control)) ++ grace_period = 5 * HZ; ++ else if (ieee80211_is_deauth(frame->frame_control)) ++ grace_period = 5 * HZ; ++ else ++ grace_period = HZ; ++ ++ if (ieee80211_is_data(frame->frame_control)) ++ xradio_rx_h_ba_stat(priv, hdrlen, skb->len); ++ ++ xradio_pm_stay_awake(&hw_priv->pm_state, grace_period); ++ ++ if(xradio_realloc_resv_skb(hw_priv, *skb_p)) { ++ *skb_p = NULL; ++ return; ++ } ++ /* Try to a packet for the case dev_alloc_skb failed in bh.*/ ++ if (unlikely(early_data)) { ++ spin_lock_bh(&priv->ps_state_lock); ++ /* Double-check status with lock held */ ++ if (entry->status == XRADIO_LINK_SOFT) { ++ skb_queue_tail(&entry->rx_queue, skb); ++ dev_warn(priv->hw_priv->pdev, "***skb_queue_tail\n"); ++ } else ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ spin_unlock_bh(&priv->ps_state_lock); ++ } else { ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ } ++ *skb_p = NULL; ++ ++ return; ++ ++drop: ++ dev_warn(priv->hw_priv->pdev, "dropped received frame\n"); ++ return; ++} ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++/* Workaround for WFD test case 6.1.10 */ ++void xradio_link_id_reset(struct work_struct *work) ++{ ++ struct xradio_vif *priv = ++ container_of(work, struct xradio_vif, linkid_reset_work); ++ struct xradio_common *hw_priv = priv->hw_priv; ++ int temp_linkid; ++ ++ ++ if (!priv->action_linkid) { ++ /* In GO mode we can receive ACTION frames without a linkID */ ++ temp_linkid = xradio_alloc_link_id(priv, ++ &priv->action_frame_sa[0]); ++ SYS_WARN(!temp_linkid); ++ if (temp_linkid) { ++ /* Make sure we execute the WQ */ ++ flush_workqueue(hw_priv->workqueue); ++ /* Release the link ID */ ++ spin_lock_bh(&priv->ps_state_lock); ++ priv->link_id_db[temp_linkid - 1].prev_status = ++ priv->link_id_db[temp_linkid - 1].status; ++ priv->link_id_db[temp_linkid - 1].status = ++ XRADIO_LINK_RESET; ++ spin_unlock_bh(&priv->ps_state_lock); ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, ++ &priv->link_id_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ } ++ } else { ++ spin_lock_bh(&priv->ps_state_lock); ++ priv->link_id_db[priv->action_linkid - 1].prev_status = ++ priv->link_id_db[priv->action_linkid - 1].status; ++ priv->link_id_db[priv->action_linkid - 1].status = ++ XRADIO_LINK_RESET_REMAP; ++ spin_unlock_bh(&priv->ps_state_lock); ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ flush_workqueue(hw_priv->workqueue); ++ } ++} ++#endif +diff --git a/drivers/net/wireless/xradio/txrx.h b/drivers/net/wireless/xradio/txrx.h +new file mode 100644 +index 00000000..f21bfb86 +--- /dev/null ++++ b/drivers/net/wireless/xradio/txrx.h +@@ -0,0 +1,89 @@ ++/* ++ * txrx interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef XRADIO_TXRX_H ++#define XRADIO_TXRX_H ++ ++#include ++ ++/* extern */ struct ieee80211_hw; ++/* extern */ struct sk_buff; ++/* extern */ struct wsm_tx; ++/* extern */ struct wsm_rx; ++/* extern */ struct wsm_tx_confirm; ++/* extern */ struct xradio_txpriv; ++/* extern */ struct xradio_vif; ++ ++struct tx_policy { ++ union { ++ __le32 tbl[3]; ++ u8 raw[12]; ++ }; ++ u8 defined; /* TODO: u32 or u8, profile and select best */ ++ u8 usage_count; /* --// -- */ ++ u8 retry_count; /* --// -- */ ++ u8 uploaded; ++}; ++ ++struct tx_policy_cache_entry { ++ struct tx_policy policy; ++ struct list_head link; ++}; ++ ++#define TX_POLICY_CACHE_SIZE (8) ++struct tx_policy_cache { ++ struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; ++ struct list_head used; ++ struct list_head free; ++ spinlock_t lock; ++}; ++ ++/* ******************************************************************** */ ++/* TX policy cache */ ++/* Intention of TX policy cache is an overcomplicated WSM API. ++ * Device does not accept per-PDU tx retry sequence. ++ * It uses "tx retry policy id" instead, so driver code has to sync ++ * linux tx retry sequences with a retry policy table in the device. ++ */ ++void tx_policy_init(struct xradio_common *hw_priv); ++void tx_policy_upload_work(struct work_struct *work); ++ ++/* ******************************************************************** */ ++/* TX implementation */ ++ ++u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv, ++ u32 rates); ++void xradio_tx(struct ieee80211_hw *dev, struct ieee80211_tx_control *control, struct sk_buff *skb); ++void xradio_skb_dtor(struct xradio_common *hw_priv, ++ struct sk_buff *skb, ++ const struct xradio_txpriv *txpriv); ++ ++/* ******************************************************************** */ ++/* WSM callbacks */ ++ ++void xradio_tx_confirm_cb(struct xradio_common *hw_priv, ++ struct wsm_tx_confirm *arg); ++void xradio_rx_cb(struct xradio_vif *priv, ++ struct wsm_rx *arg, ++ struct sk_buff **skb_p); ++ ++/* ******************************************************************** */ ++/* Timeout */ ++ ++void xradio_tx_timeout(struct work_struct *work); ++ ++/* ******************************************************************** */ ++/* Workaround for WFD test case 6.1.10 */ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++void xradio_link_id_reset(struct work_struct *work); ++#endif ++ ++#endif /* XRADIO_TXRX_H */ +diff --git a/drivers/net/wireless/xradio/wsm.c b/drivers/net/wireless/xradio/wsm.c +new file mode 100644 +index 00000000..7b67349b +--- /dev/null ++++ b/drivers/net/wireless/xradio/wsm.c +@@ -0,0 +1,3252 @@ ++/* ++ * WSM host interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "xradio.h" ++#include "wsm.h" ++#include "bh.h" ++#ifdef ROAM_OFFLOAD ++#include "sta.h" ++#endif /*ROAM_OFFLOAD*/ ++ ++ ++#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ ++#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ ++#define WSM_CMD_START_TIMEOUT (7 * HZ) ++#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ ++#define WSM_CMD_DEFAULT_TIMEOUT (3 * HZ) ++#define WSM_SKIP(buf, size) \ ++ do { \ ++ if (unlikely((buf)->data + size > (buf)->end)) \ ++ goto underflow; \ ++ (buf)->data += size; \ ++ } while (0) ++ ++#define WSM_GET(buf, ptr, size) \ ++ do { \ ++ if (unlikely((buf)->data + size > (buf)->end)) \ ++ goto underflow; \ ++ memcpy(ptr, (buf)->data, size); \ ++ (buf)->data += size; \ ++ } while (0) ++ ++#define __WSM_GET(buf, type, cvt) \ ++ ({ \ ++ type val; \ ++ if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ ++ goto underflow; \ ++ val = cvt(*(type *)(buf)->data); \ ++ (buf)->data += sizeof(type); \ ++ val; \ ++ }) ++ ++#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) ++#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) ++#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) ++ ++#define WSM_PUT(buf, ptr, size) \ ++ do { \ ++ if (unlikely((buf)->data + size > (buf)->end)) \ ++ if (unlikely(wsm_buf_reserve((buf), size))) \ ++ goto nomem; \ ++ memcpy((buf)->data, ptr, size); \ ++ (buf)->data += size; \ ++ } while (0) ++ ++#define __WSM_PUT(buf, val, type, cvt) \ ++ do { \ ++ if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ ++ if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \ ++ goto nomem; \ ++ *(type *)(buf)->data = cvt(val); \ ++ (buf)->data += sizeof(type); \ ++ } while (0) ++ ++#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) ++#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) ++#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) ++ ++static void wsm_buf_reset(struct wsm_buf *buf); ++static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); ++static int get_interface_id_scanning(struct xradio_common *hw_priv); ++ ++static int wsm_cmd_send(struct xradio_common *hw_priv, ++ struct wsm_buf *buf, ++ void *arg, u16 cmd, long tmo, int if_id); ++ ++static struct xradio_vif ++ *wsm_get_interface_for_tx(struct xradio_common *hw_priv); ++ ++static inline void wsm_cmd_lock(struct xradio_common *hw_priv) ++{ ++ mutex_lock(&hw_priv->wsm_cmd_mux); ++} ++ ++static inline void wsm_cmd_unlock(struct xradio_common *hw_priv) ++{ ++ mutex_unlock(&hw_priv->wsm_cmd_mux); ++} ++ ++static inline void wsm_oper_lock(struct xradio_common *hw_priv) ++{ ++ mutex_lock(&hw_priv->wsm_oper_lock); ++} ++ ++static inline void wsm_oper_unlock(struct xradio_common *hw_priv) ++{ ++ mutex_unlock(&hw_priv->wsm_oper_lock); ++} ++ ++/* ******************************************************************** */ ++/* WSM API implementation */ ++ ++static int wsm_generic_confirm(struct xradio_common *hw_priv, ++ void *arg, ++ struct wsm_buf *buf) ++{ ++ u32 status = WSM_GET32(buf); ++ if (status != WSM_STATUS_SUCCESS) ++ return -EINVAL; ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++#if defined(DGB_XRADIO_HWT) ++int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg, size_t arg_size) ++{ ++ int ret = 0; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ WSM_PUT(buf, arg, arg_size); ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0024, WSM_CMD_TIMEOUT, -1); ++ wsm_cmd_unlock(hw_priv); ++ ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++#endif ++ ++#ifdef XR_RRM//RadioResourceMeasurement ++static int wsm_start_measure_requset(struct xradio_common *hw_priv, ++ MEASUREMENT_PARAMETERS *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT(buf, arg, sizeof(*arg)); ++ ret = wsm_cmd_send(hw_priv, buf, arg, 0x000E, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++ nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++ ++} ++ ++int wsm_11k_measure_requset(struct xradio_common *hw_priv, ++ u8 measure_type, ++ u16 ChannelNum, ++ u16 Duration) ++{ ++ int ret; ++ u8 type, sub_type; ++ MEASUREMENT_PARAMETERS rrm_paras; ++ LMAC_MEAS_REQUEST *rrm_req = &rrm_paras.MeasurementRequest; ++// LMAC_MEAS_CHANNEL_LOAD_PARAMS *rrm_req = &rrm_paras.MeasurementRequest; ++ rrm_paras.TxPowerLevel = 0x11; ++ rrm_paras.DurationMandatory = 0x22; ++ rrm_paras.MeasurementRequestLength = 0x33; ++ ++ type = (measure_type&0xf0)>>4; ++ sub_type = measure_type&0xf; ++ rrm_paras.MeasurementType = type; ++// if (measure_type == ChannelLoadMeasurement) { ++ if (type == ChannelLoadMeasurement) { ++ rrm_req->ChannelLoadParams.Reserved = 0; ++ rrm_req->ChannelLoadParams.ChannelLoadCCA = sub_type; ++ rrm_req->ChannelLoadParams.ChannelNum = ChannelNum; ++ //valid when channelload measure, interval bettween request&start ++ rrm_req->ChannelLoadParams.RandomInterval = 0; ++ //unit:1TU=1024us ++ rrm_req->ChannelLoadParams.MeasurementDuration = Duration; ++ rrm_req->ChannelLoadParams.MeasurementStartTimel = 0; ++ rrm_req->ChannelLoadParams.MeasurementStartTimeh = 0; ++ } else if (type == NoiseHistrogramMeasurement) { ++ rrm_req->NoisHistogramParams.Reserved = 0; ++ rrm_req->NoisHistogramParams.IpiRpi = sub_type; ++ rrm_req->NoisHistogramParams.ChannelNum = ChannelNum; ++ rrm_req->NoisHistogramParams.RandomInterval = 0; ++ rrm_req->NoisHistogramParams.MeasurementDuration = Duration; ++ rrm_req->NoisHistogramParams.MeasurementStartTimel = 0; ++ rrm_req->NoisHistogramParams.MeasurementStartTimeh = 0; ++ } ++ ret = wsm_start_measure_requset(hw_priv, &rrm_paras, 0); ++ ++ return ret; ++} ++ ++ ++#endif//RadioResourceMeasurement ++int wsm_configuration(struct xradio_common *hw_priv, ++ struct wsm_configuration *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); ++ WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); ++ WSM_PUT32(buf, arg->dot11RtsThreshold); ++ ++ /* DPD block. */ ++ WSM_PUT16(buf, arg->dpdData_size + 12); ++ WSM_PUT16(buf, 1); /* DPD version */ ++ WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); ++ WSM_PUT16(buf, 5); /* DPD flags */ ++ WSM_PUT(buf, arg->dpdData, arg->dpdData_size); ++ ++ ret = wsm_cmd_send(hw_priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++static int wsm_configuration_confirm(struct xradio_common *hw_priv, ++ struct wsm_configuration *arg, ++ struct wsm_buf *buf) ++{ ++ int i; ++ int status; ++ ++ status = WSM_GET32(buf); ++ if (SYS_WARN(status != WSM_STATUS_SUCCESS)) ++ return -EINVAL; ++ ++ WSM_GET(buf, arg->dot11StationId, ETH_ALEN); ++ arg->dot11FrequencyBandsSupported = WSM_GET8(buf); ++ WSM_SKIP(buf, 1); ++ arg->supportedRateMask = WSM_GET32(buf); ++ for (i = 0; i < 2; ++i) { ++ arg->txPowerRange[i].min_power_level = WSM_GET32(buf); ++ arg->txPowerRange[i].max_power_level = WSM_GET32(buf); ++ arg->txPowerRange[i].stepping = WSM_GET32(buf); ++ } ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++/* ******************************************************************** */ ++/*forcing upper layer to restart wifi.*/ ++void wsm_upper_restart(struct xradio_common *hw_priv) ++{ ++ int i = 0; ++ struct xradio_vif *priv = NULL; ++ ++#ifdef CONFIG_PM ++ xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ); ++#endif ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) && 0 ++ spin_lock(&hw_priv->vif_list_lock); ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (!priv) ++ continue; ++ ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); ++ wsm_printk(XRADIO_DBG_WARN, "%s driver_hang_notify\n", __func__); ++ } ++ spin_unlock(&hw_priv->vif_list_lock); ++#elif defined(HW_RESTART) ++ if (!work_pending(&hw_priv->hw_restart_work)) { ++ bool reset = false; ++ spin_lock(&hw_priv->vif_list_lock); ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (priv) { ++ reset = true; ++ break; ++ } ++ } ++ spin_unlock(&hw_priv->vif_list_lock); ++ if (reset) { ++ wsm_cmd_lock(hw_priv); ++ hw_priv->hw_restart = true; ++ wsm_cmd_unlock(hw_priv); ++ /* wait for scan complete.*/ ++ wsm_printk(XRADIO_DBG_WARN, "Wait for scan complete!\n"); ++ down(&hw_priv->scan.lock); ++ mutex_lock(&hw_priv->conf_mutex); ++ /* Unlock wsm_oper_lock since no confirms of wsm_oper_locks.*/ ++ if (!mutex_trylock(&hw_priv->wsm_oper_lock)) ++ wsm_printk(XRADIO_DBG_WARN, "oper_lock may be locked!\n"); ++ mutex_unlock(&hw_priv->wsm_oper_lock); ++ mutex_unlock(&hw_priv->conf_mutex); ++ up(&hw_priv->scan.lock); ++ msleep(200); ++ schedule_work(&hw_priv->hw_restart_work); ++ wsm_printk(XRADIO_DBG_WARN, "%s schedule restart_work!\n", __func__); ++ } ++ } ++#endif ++} ++ ++void wsm_query_work(struct work_struct *work) ++{ ++ struct xradio_common *hw_priv = ++ container_of(work, struct xradio_common, query_work); ++ u8 ret[100] = {0}; ++ ++ ++ *(u32*)&ret[0] = hw_priv->query_packetID; ++ wsm_read_mib(hw_priv, WSM_MIB_ID_REQ_PKT_STATUS, (void*)&ret[0], sizeof(ret), 4); ++ if(!ret[4]) { ++ wsm_printk(XRADIO_DBG_ERROR,"QuerypktID=0x%08x, status=0x%x, retry=%d, flags=0x%x, PktDebug=0x%x\n" \ ++ "pktqueue=0x%x, ext1=%d, ext2=%d, ext3=%d, ext4=0x%x, ext5=0x%x\n", ++ *(u32*)&ret[0], ret[6], ret[7], *(u32*)&ret[8], *(u32*)&ret[12], ++ ret[44], ret[45], ret[46], ret[47], ret[48], ret[49]); ++ wsm_printk(XRADIO_DBG_ERROR,"interdebug=0x%x, 0x%x, 0x%x, Soure=0x%x, 0x%x, 0x%x\n" \ ++ "interuse=%d, external=%d, TxOutstanding=%d, QueueStatus=0x%x, BA0=0x%x, BA1=0x%x\n" \ ++ "ScanStatus=0x%x, scanNULL=0x%x, wr_state=0x%x,0x%x,0x%x,0x%x," \ ++ "wr_cnt=%d, %d, %d, %d\n", ++ *(u32*)&ret[16], *(u32*)&ret[20], *(u32*)&ret[24], ret[28], ret[29], ret[30], ++ ret[32], ret[33], ret[34], ret[35], *(u32*)&ret[36], *(u32*)&ret[40], ++ ret[50], ret[51], ret[52], ret[53], ret[54], ret[55], ++ *(u16*)&ret[56], *(u16*)&ret[58], *(u16*)&ret[60], *(u16*)&ret[62]); ++ } else { ++ ret[5] = 0; ++ wsm_printk(XRADIO_DBG_ERROR,"No req packid=0x%08x!\n", *(u32*)&ret[0]); ++ } ++ //hardware error occurs, try to restart wifi. ++ if(ret[5] & 0x4) { ++ wsm_printk(XRADIO_DBG_ERROR,"Hardware need to reset 0x%x.\n", ret[5]); ++ hw_priv->bh_error = 1; ++#ifdef BH_USE_SEMAPHORE ++ up(&hw_priv->bh_sem); ++#else ++ wake_up(&hw_priv->bh_wq); ++#endif ++ } ++ hw_priv->query_packetID = 0; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id); ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); ++ ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT, ++ if_id); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++struct wsm_mib { ++ u16 mibId; ++ void *buf; ++ size_t buf_size; ++}; ++ ++int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf, ++ size_t buf_size, size_t arg_size) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ struct wsm_mib mib_buf = { ++ .mibId = mibId, ++ .buf = _buf, ++ .buf_size = buf_size, ++ }; ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT16(buf, mibId); ++ WSM_PUT16(buf, arg_size); ++ WSM_PUT(buf, _buf, arg_size); ++ ++ ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT, -1); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++static int wsm_read_mib_confirm(struct xradio_common *hw_priv, ++ struct wsm_mib *arg, ++ struct wsm_buf *buf) ++{ ++ u16 size; ++ if (SYS_WARN(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) ++ return -EINVAL; ++ ++ if (SYS_WARN(WSM_GET16(buf) != arg->mibId)) ++ return -EINVAL; ++ ++ size = WSM_GET16(buf); ++ if (size > arg->buf_size) ++ size = arg->buf_size; ++ ++ WSM_GET(buf, arg->buf, size); ++ arg->buf_size = size; ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf, ++ size_t buf_size, int if_id) ++{ ++ int ret = 0; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ struct wsm_mib mib_buf = { ++ .mibId = mibId, ++ .buf = _buf, ++ .buf_size = buf_size, ++ }; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT16(buf, mibId); ++ WSM_PUT16(buf, buf_size); ++ WSM_PUT(buf, _buf, buf_size); ++ ++ ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT, ++ if_id); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++static int wsm_write_mib_confirm(struct xradio_common *hw_priv, ++ struct wsm_mib *arg, ++ struct wsm_buf *buf, ++ int interface_link_id) ++{ ++ int ret; ++ int i; ++ struct xradio_vif *priv; ++ ret = wsm_generic_confirm(hw_priv, arg, buf); ++ if (ret) ++ return ret; ++ ++ /*wsm_set_operational_mode confirm.*/ ++ if (arg->mibId == 0x1006) { ++ const char *p = arg->buf; ++ bool powersave_enabled = (p[0] & 0x0F) ? true : false; ++ ++ /* update vif PM status. */ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); ++ if (priv) { ++ xradio_enable_powersave(priv, powersave_enabled); ++ spin_unlock(&priv->vif_lock); ++ } ++ ++ /* HW powersave base on vif except for generic vif. */ ++ spin_lock(&hw_priv->vif_list_lock); ++ xradio_for_each_vif(hw_priv, priv, i) { ++#ifdef P2P_MULTIVIF ++ if ((i == (XRWL_MAX_VIFS - 1)) || !priv) ++#else ++ if (!priv) ++#endif ++ continue; ++ powersave_enabled &= !!priv->powersave_enabled; ++ } ++ hw_priv->powersave_enabled = powersave_enabled; ++ spin_unlock(&hw_priv->vif_list_lock); ++ ++ } ++ return 0; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg, ++ int if_id) ++{ ++ int i; ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ if (unlikely(arg->numOfChannels > 48)) ++ return -EINVAL; ++ ++ if (unlikely(arg->numOfSSIDs > WSM_SCAN_MAX_NUM_OF_SSIDS)) ++ return -EINVAL; ++ ++ if (unlikely(arg->band > 1)) ++ return -EINVAL; ++ ++ wsm_oper_lock(hw_priv); ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, arg->band); ++ WSM_PUT8(buf, arg->scanType); ++ WSM_PUT8(buf, arg->scanFlags); ++ WSM_PUT8(buf, arg->maxTransmitRate); ++ WSM_PUT32(buf, arg->autoScanInterval); ++ WSM_PUT8(buf, arg->numOfProbeRequests); ++ WSM_PUT8(buf, arg->numOfChannels); ++ WSM_PUT8(buf, arg->numOfSSIDs); ++ WSM_PUT8(buf, arg->probeDelay); ++ ++ for (i = 0; i < arg->numOfChannels; ++i) { ++ WSM_PUT16(buf, arg->ch[i].number); ++ WSM_PUT16(buf, 0); ++ WSM_PUT32(buf, arg->ch[i].minChannelTime); ++ WSM_PUT32(buf, arg->ch[i].maxChannelTime); ++ WSM_PUT32(buf, 0); ++ } ++ ++ for (i = 0; i < arg->numOfSSIDs; ++i) { ++ WSM_PUT32(buf, arg->ssids[i].length); ++ WSM_PUT(buf, &arg->ssids[i].ssid[0], ++ sizeof(arg->ssids[i].ssid)); ++ } ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT, ++ if_id); ++ wsm_cmd_unlock(hw_priv); ++ if (ret) ++ wsm_oper_unlock(hw_priv); ++ ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ wsm_oper_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_stop_scan(struct xradio_common *hw_priv, int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ wsm_cmd_lock(hw_priv); ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT, ++ if_id); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++} ++ ++ ++static int wsm_tx_confirm(struct xradio_common *hw_priv, ++ struct wsm_buf *buf, ++ int interface_link_id) ++{ ++ struct wsm_tx_confirm tx_confirm; ++ ++ tx_confirm.packetID = WSM_GET32(buf); ++ tx_confirm.status = WSM_GET32(buf); ++ tx_confirm.txedRate = WSM_GET8(buf); ++ tx_confirm.ackFailures = WSM_GET8(buf); ++ tx_confirm.flags = WSM_GET16(buf); ++ tx_confirm.rate_try[0] = WSM_GET32(buf); ++ tx_confirm.rate_try[1] = WSM_GET32(buf); ++ tx_confirm.rate_try[2] = WSM_GET32(buf); ++ tx_confirm.mediaDelay = WSM_GET32(buf); ++ tx_confirm.txQueueDelay = WSM_GET32(buf); ++ ++ if (is_hardware_xradio(hw_priv)) { ++ /* TODO:COMBO:linkID will be stored in packetID*/ ++ /* TODO:COMBO: Extract traffic resumption map */ ++ tx_confirm.if_id = xradio_queue_get_if_id(tx_confirm.packetID); ++ tx_confirm.link_id = xradio_queue_get_link_id( ++ tx_confirm.packetID); ++ } else { ++ tx_confirm.link_id = interface_link_id; ++ tx_confirm.if_id = 0; ++ } ++ ++ wsm_release_vif_tx_buffer(hw_priv, tx_confirm.if_id, 1); ++ ++ if (hw_priv->wsm_cbc.tx_confirm) ++ hw_priv->wsm_cbc.tx_confirm(hw_priv, &tx_confirm); ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++static int wsm_multi_tx_confirm(struct xradio_common *hw_priv, ++ struct wsm_buf *buf, int interface_link_id) ++{ ++ struct xradio_vif *priv; ++ int ret; ++ int count; ++ int i; ++ ++ count = WSM_GET32(buf); ++ if (SYS_WARN(count <= 0)) ++ return -EINVAL; ++ else if (count > 1) { ++ ret = wsm_release_tx_buffer(hw_priv, count - 1); ++ if (ret < 0) ++ return ret; ++ else if (ret > 0) ++ xradio_bh_wakeup(hw_priv); ++ } ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); ++ if (priv) { ++ xradio_debug_txed_multi(priv, count); ++ spin_unlock(&priv->vif_lock); ++ } ++ for (i = 0; i < count; ++i) { ++ ret = wsm_tx_confirm(hw_priv, buf, interface_link_id); ++ if (ret) ++ return ret; ++ } ++ return ret; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++/* ******************************************************************** */ ++ ++static int wsm_join_confirm(struct xradio_common *hw_priv, ++ struct wsm_join *arg, ++ struct wsm_buf *buf) ++{ ++ if (WSM_GET32(buf) != WSM_STATUS_SUCCESS) ++ return -EINVAL; ++ arg->minPowerLevel = WSM_GET32(buf); ++ arg->maxPowerLevel = WSM_GET32(buf); ++ ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg, ++ int if_id) ++/*TODO: combo: make it work per vif.*/ ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_oper_lock(hw_priv); ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, arg->mode); ++ WSM_PUT8(buf, arg->band); ++ WSM_PUT16(buf, arg->channelNumber); ++ WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); ++ WSM_PUT16(buf, arg->atimWindow); ++ WSM_PUT8(buf, arg->preambleType); ++ WSM_PUT8(buf, arg->probeForJoin); ++ WSM_PUT8(buf, arg->dtimPeriod); ++ WSM_PUT8(buf, arg->flags); ++ WSM_PUT32(buf, arg->ssidLength); ++ WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); ++ WSM_PUT32(buf, arg->beaconInterval); ++ WSM_PUT32(buf, arg->basicRateSet); ++ ++ hw_priv->tx_burst_idx = -1; ++ ret = wsm_cmd_send(hw_priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT, ++ if_id); ++ wsm_cmd_unlock(hw_priv); ++ wsm_oper_unlock(hw_priv); /*confirm, not indcation.*/ ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ wsm_oper_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_set_bss_params(struct xradio_common *hw_priv, ++ const struct wsm_set_bss_params *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, 0); ++ WSM_PUT8(buf, arg->beaconLostCount); ++ WSM_PUT16(buf, arg->aid); ++ WSM_PUT32(buf, arg->operationalRateSet); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT, ++ if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT(buf, arg, sizeof(*arg)); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT, ++ if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_remove_key(struct xradio_common *hw_priv, ++ const struct wsm_remove_key *arg, int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, arg->entryIndex); ++ WSM_PUT8(buf, 0); ++ WSM_PUT16(buf, 0); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT, ++ if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_set_tx_queue_params(struct xradio_common *hw_priv, ++ const struct wsm_set_tx_queue_params *arg, ++ u8 id, int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, queue_id_to_wmm_aci[id]); ++ WSM_PUT8(buf, 0); ++ WSM_PUT8(buf, arg->ackPolicy); ++ WSM_PUT8(buf, 0); ++ WSM_PUT32(buf, arg->maxTransmitLifetime); ++ WSM_PUT16(buf, arg->allowedMediumTime); ++ WSM_PUT16(buf, 0); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_set_edca_params(struct xradio_common *hw_priv, ++ const struct wsm_edca_params *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ /* Implemented according to specification. */ ++ ++ WSM_PUT16(buf, arg->params[3].cwMin); ++ WSM_PUT16(buf, arg->params[2].cwMin); ++ WSM_PUT16(buf, arg->params[1].cwMin); ++ WSM_PUT16(buf, arg->params[0].cwMin); ++ ++ WSM_PUT16(buf, arg->params[3].cwMax); ++ WSM_PUT16(buf, arg->params[2].cwMax); ++ WSM_PUT16(buf, arg->params[1].cwMax); ++ WSM_PUT16(buf, arg->params[0].cwMax); ++ ++ WSM_PUT8(buf, arg->params[3].aifns); ++ WSM_PUT8(buf, arg->params[2].aifns); ++ WSM_PUT8(buf, arg->params[1].aifns); ++ WSM_PUT8(buf, arg->params[0].aifns); ++ ++ WSM_PUT16(buf, arg->params[3].txOpLimit); ++ WSM_PUT16(buf, arg->params[2].txOpLimit); ++ WSM_PUT16(buf, arg->params[1].txOpLimit); ++ WSM_PUT16(buf, arg->params[0].txOpLimit); ++ ++ WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); ++ WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); ++ WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); ++ WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT, if_id); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_switch_channel(struct xradio_common *hw_priv, ++ const struct wsm_switch_channel *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_lock_tx(hw_priv); ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, arg->channelMode); ++ WSM_PUT8(buf, arg->channelSwitchCount); ++ WSM_PUT16(buf, arg->newChannelNumber); ++ ++ hw_priv->channel_switch_in_progress = 1; ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT, if_id); ++ wsm_cmd_unlock(hw_priv); ++ if (ret) { ++ wsm_unlock_tx(hw_priv); ++ hw_priv->channel_switch_in_progress = 0; ++ } ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ wsm_unlock_tx(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_oper_lock(hw_priv); ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, arg->pmMode); ++ WSM_PUT8(buf, arg->fastPsmIdlePeriod); ++ WSM_PUT8(buf, arg->apPsmChangePeriod); ++ WSM_PUT8(buf, arg->minAutoPsPollPeriod); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ if (ret) ++ wsm_oper_unlock(hw_priv); ++ ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ wsm_oper_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT8(buf, arg->mode); ++ WSM_PUT8(buf, arg->band); ++ WSM_PUT16(buf, arg->channelNumber); ++ WSM_PUT32(buf, arg->CTWindow); ++ WSM_PUT32(buf, arg->beaconInterval); ++ WSM_PUT8(buf, arg->DTIMPeriod); ++ WSM_PUT8(buf, arg->preambleType); ++ WSM_PUT8(buf, arg->probeDelay); ++ WSM_PUT8(buf, arg->ssidLength); ++ WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); ++ WSM_PUT32(buf, arg->basicRateSet); ++ ++ hw_priv->tx_burst_idx = -1; ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT, ++ if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++#if 0 ++/* This API is no longer present in WSC */ ++/* ******************************************************************** */ ++ ++int wsm_beacon_transmit(struct xradio_common *hw_priv, ++ const struct wsm_beacon_transmit *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++#endif ++ ++/* ******************************************************************** */ ++ ++int wsm_start_find(struct xradio_common *hw_priv, int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT, if_id); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_stop_find(struct xradio_common *hw_priv, int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT, if_id); ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg, ++ int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ u16 cmd = 0x001C; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); ++ ++ if (is_hardware_xradio(hw_priv)) { ++ WSM_PUT8(buf, arg->unmap); ++ WSM_PUT8(buf, arg->link_id); ++ } else { ++ cmd |= WSM_TX_LINK_ID(arg->link_id); ++ WSM_PUT16(buf, 0); ++ } ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++} ++ ++/* ******************************************************************** */ ++ ++int wsm_update_ie(struct xradio_common *hw_priv, ++ const struct wsm_update_ie *arg, int if_id) ++{ ++ int ret; ++ struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(hw_priv); ++ ++ WSM_PUT16(buf, arg->what); ++ WSM_PUT16(buf, arg->count); ++ WSM_PUT(buf, arg->ies, arg->length); ++ ++ ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT, if_id); ++ ++ wsm_cmd_unlock(hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(hw_priv); ++ return -ENOMEM; ++ ++} ++/* ******************************************************************** */ ++#ifdef MCAST_FWDING ++/* 3.66 */ ++static int wsm_give_buffer_confirm(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ wsm_printk(XRADIO_DBG_MSG, "HW Buf count %d\n", hw_priv->hw_bufs_used); ++ if (!hw_priv->hw_bufs_used) ++ wake_up(&hw_priv->bh_evt_wq); ++ ++ return 0; ++} ++ ++/* 3.65 */ ++int wsm_init_release_buffer_request(struct xradio_common *hw_priv, u8 index) ++{ ++ struct wsm_buf *buf = &hw_priv->wsm_release_buf[index]; ++ u16 cmd = 0x0022; /* Buffer Request */ ++ u8 flags; ++ size_t buf_len; ++ ++ wsm_buf_init(buf); ++ ++ flags = index ? 0: 0x1; ++ ++ WSM_PUT8(buf, flags); ++ WSM_PUT8(buf, 0); ++ WSM_PUT16(buf, 0); ++ ++ buf_len = buf->data - buf->begin; ++ ++ /* Fill HI message header */ ++ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); ++ ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); ++ ++ return 0; ++nomem: ++ return -ENOMEM; ++} ++ ++/* 3.65 fixed memory leakage by yangfh*/ ++int wsm_deinit_release_buffer(struct xradio_common *hw_priv) ++{ ++ struct wsm_buf *buf = NULL; ++ int i, err = 0; ++ ++ for (i = 0; i < WSM_MAX_BUF; i++) { ++ buf = &hw_priv->wsm_release_buf[i]; ++ if(likely(buf)) { ++ if(likely(buf->begin)) ++ kfree(buf->begin); ++ buf->begin = buf->data = buf->end = NULL; ++ } else { ++ err++; ++ } ++ } ++ if(err) wsm_printk(XRADIO_DBG_ERROR, "%s, NULL buf=%d!\n", __func__, err); ++ return 0; ++} ++ ++/* 3.68 */ ++static int wsm_request_buffer_confirm(struct xradio_vif *priv, ++ u8 *arg, ++ struct wsm_buf *buf) ++{ ++ u8 count; ++ u32 sta_asleep_mask = 0; ++ int i; ++ u32 mask = 0; ++ u32 change_mask = 0; ++ struct xradio_common *hw_priv = priv->hw_priv; ++ ++ /* There is no status field in this message */ ++ sta_asleep_mask = WSM_GET32(buf); ++ count = WSM_GET8(buf); ++ count -= 1; /* Current workaround for FW issue */ ++ ++ spin_lock_bh(&priv->ps_state_lock); ++ change_mask = (priv->sta_asleep_mask ^ sta_asleep_mask); ++ wsm_printk(XRADIO_DBG_MSG, "CM %x, HM %x, FWM %x\n", change_mask,priv->sta_asleep_mask, sta_asleep_mask); ++ spin_unlock_bh(&priv->ps_state_lock); ++ ++ if (change_mask) { ++ struct ieee80211_sta *sta; ++ int ret = 0; ++ ++ ++ for (i = 0; i < MAX_STA_IN_AP_MODE ; ++i) { ++ ++ if(XRADIO_LINK_HARD != priv->link_id_db[i].status) ++ continue; ++ ++ mask = BIT(i + 1); ++ ++ /* If FW state and host state for this link are different then notify OMAC */ ++ if(change_mask & mask) { ++ wsm_printk(XRADIO_DBG_MSG, "PS State Changed %d for sta %pM\n", (sta_asleep_mask & mask) ? 1:0, priv->link_id_db[i].mac); ++ rcu_read_lock(); ++ sta = ieee80211_find_sta(priv->vif, priv->link_id_db[i].mac); ++ if (!sta) { ++ wsm_printk(XRADIO_DBG_MSG, "WRBC - could not find sta %pM\n", ++ priv->link_id_db[i].mac); ++ } else { ++ ret = ieee80211_sta_ps_transition_ni(sta, (sta_asleep_mask & mask) ? true: false); ++ wsm_printk(XRADIO_DBG_MSG, "PS State NOTIFIED %d\n", ret); ++ SYS_WARN(ret); ++ } ++ rcu_read_unlock(); ++ } ++ } ++ /* Replace STA mask with one reported by FW */ ++ spin_lock_bh(&priv->ps_state_lock); ++ priv->sta_asleep_mask = sta_asleep_mask; ++ spin_unlock_bh(&priv->ps_state_lock); ++ } ++ ++ wsm_printk(XRADIO_DBG_MSG, "WRBC - HW Buf count %d SleepMask %d\n", ++ hw_priv->hw_bufs_used, sta_asleep_mask); ++ hw_priv->buf_released = 0; ++ SYS_WARN(count != (hw_priv->wsm_caps.numInpChBufs - 1)); ++ ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++/* 3.67 */ ++int wsm_request_buffer_request(struct xradio_vif *priv, ++ u8 *arg) ++{ ++ int ret; ++ struct wsm_buf *buf = &priv->hw_priv->wsm_cmd_buf; ++ ++ wsm_cmd_lock(priv->hw_priv); ++ ++ WSM_PUT8(buf, (*arg)); ++ WSM_PUT8(buf, 0); ++ WSM_PUT16(buf, 0); ++ ++ ret = wsm_cmd_send(priv->hw_priv, buf, arg, 0x0023, WSM_CMD_JOIN_TIMEOUT,priv->if_id); ++ ++ wsm_cmd_unlock(priv->hw_priv); ++ return ret; ++ ++nomem: ++ wsm_cmd_unlock(priv->hw_priv); ++ return -ENOMEM; ++} ++ ++#endif ++ ++int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ priv->rx_filter.keepalive = enable; ++ return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); ++} ++ ++int wsm_set_probe_responder(struct xradio_vif *priv, bool enable) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ ++ priv->rx_filter.probeResponder = enable; ++ return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); ++} ++/* ******************************************************************** */ ++/* WSM indication events implementation */ ++ ++static int wsm_startup_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ u16 status; ++#ifdef CONFIG_XRADIO_DEBUG ++ static const char * const fw_types[] = { ++ "ETF", ++ "WFM", ++ "WSM", ++ "HI test", ++ "Platform test" ++ }; ++#endif ++ ++ hw_priv->wsm_caps.numInpChBufs = WSM_GET16(buf); ++ hw_priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf); ++ hw_priv->wsm_caps.hardwareId = WSM_GET16(buf); ++ hw_priv->wsm_caps.hardwareSubId = WSM_GET16(buf); ++ status = WSM_GET16(buf); ++ hw_priv->wsm_caps.firmwareCap = WSM_GET16(buf); ++ hw_priv->wsm_caps.firmwareType = WSM_GET16(buf); ++ hw_priv->wsm_caps.firmwareApiVer = WSM_GET16(buf); ++ hw_priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf); ++ hw_priv->wsm_caps.firmwareVersion = WSM_GET16(buf); ++ WSM_GET(buf, &hw_priv->wsm_caps.fw_label[0], WSM_FW_LABEL); ++ hw_priv->wsm_caps.fw_label[WSM_FW_LABEL+1] = 0; /* Do not trust FW too much. */ ++ ++ if (SYS_WARN(status)) ++ return -EINVAL; ++ ++ if (SYS_WARN(hw_priv->wsm_caps.firmwareType > 4)) ++ return -EINVAL; ++ ++ wsm_printk(XRADIO_DBG_NIY, "%s\n" ++ " Input buffers: %d x %d bytes\n" ++ " Hardware: %d.%d\n" ++ " %s firmware ver: %d, build: %d," ++ " api: %d, cap: 0x%.4X\n", ++ __func__, ++ hw_priv->wsm_caps.numInpChBufs, ++ hw_priv->wsm_caps.sizeInpChBuf, ++ hw_priv->wsm_caps.hardwareId, ++ hw_priv->wsm_caps.hardwareSubId, ++ fw_types[hw_priv->wsm_caps.firmwareType], ++ hw_priv->wsm_caps.firmwareVersion, ++ hw_priv->wsm_caps.firmwareBuildNumber, ++ hw_priv->wsm_caps.firmwareApiVer, ++ hw_priv->wsm_caps.firmwareCap); ++ ++ wsm_printk(XRADIO_DBG_ALWY, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]); ++ ++ hw_priv->wsm_caps.firmwareReady = 1; ++ ++ wake_up(&hw_priv->wsm_startup_done); ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++//add by yangfh 2014-10-31 16:58:53 ++void wms_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv) ++{ ++ struct sk_buff *skb = NULL; ++ struct ieee80211_mgmt *deauth = NULL; ++ ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) { ++ int i = 0; ++ wsm_printk(XRADIO_DBG_WARN, "AP mode, send_deauth_to_self\n"); ++ for (i = 0; ilink_id_db[i].status == XRADIO_LINK_HARD) { ++ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); ++ if (!skb) ++ return; ++ skb_reserve(skb, 64); ++ deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); ++ if(!deauth) { ++ SYS_WARN(1); ++ return; ++ } ++ deauth->frame_control = ++ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); ++ deauth->duration = 0; ++ memcpy(deauth->da, priv->vif->addr, ETH_ALEN); ++ memcpy(deauth->sa, priv->link_id_db[i].mac, ETH_ALEN); ++ memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN); ++ deauth->seq_ctrl = 0; ++ deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ } ++ } ++ } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ wsm_printk(XRADIO_DBG_WARN, "STA mode, send_deauth_to_self\n"); ++ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); ++ if (!skb) ++ return; ++ skb_reserve(skb, 64); ++ deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); ++ if(!deauth) { ++ SYS_WARN(1); ++ return; ++ } ++ deauth->frame_control = ++ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); ++ deauth->duration = 0; ++ memcpy(deauth->da, priv->vif->addr, ETH_ALEN); ++ memcpy(deauth->sa, priv->join_bssid, ETH_ALEN); ++ memcpy(deauth->bssid, priv->join_bssid, ETH_ALEN); ++ deauth->seq_ctrl = 0; ++ deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ } ++} ++ ++void wms_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv) ++{ ++ struct sk_buff *skb = NULL; ++ struct ieee80211_mgmt *disassoc = NULL; ++ if (priv->join_status == XRADIO_JOIN_STATUS_AP) { ++ int i = 0; ++ wsm_printk(XRADIO_DBG_WARN, "AP mode, wms_send_disassoc_to_self\n"); ++ for (i = 0; ilink_id_db[i].status == XRADIO_LINK_HARD) { ++ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); ++ if (!skb) ++ return; ++ skb_reserve(skb, 64); ++ disassoc = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); ++ if(!disassoc) { ++ SYS_WARN(1); ++ return; ++ } ++ disassoc->frame_control = ++ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); ++ disassoc->duration = 0; ++ memcpy(disassoc->da, priv->vif->addr, ETH_ALEN); ++ memcpy(disassoc->sa, priv->link_id_db[i].mac, ETH_ALEN); ++ memcpy(disassoc->bssid, priv->vif->addr, ETH_ALEN); ++ disassoc->seq_ctrl = 0; ++ disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_STA_HAS_LEFT; ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ } ++ } ++ } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ wsm_printk(XRADIO_DBG_WARN, "STA mode, wms_send_disassoc_to_self\n"); ++ skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); ++ if (!skb) ++ return; ++ skb_reserve(skb, 64); ++ disassoc = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); ++ if(!disassoc) { ++ SYS_WARN(1); ++ return; ++ } ++ disassoc->frame_control = ++ cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); ++ disassoc->duration = 0; ++ memcpy(disassoc->da, priv->vif->addr, ETH_ALEN); ++ memcpy(disassoc->sa, priv->join_bssid, ETH_ALEN); ++ memcpy(disassoc->bssid, priv->join_bssid, ETH_ALEN); ++ disassoc->seq_ctrl = 0; ++ disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY; ++ ieee80211_rx_irqsafe(priv->hw, skb); ++ } ++} ++ ++static int wsm_receive_indication(struct xradio_common *hw_priv, ++ int interface_link_id, ++ struct wsm_buf *buf, ++ struct sk_buff **skb_p) ++{ ++ struct xradio_vif *priv; ++ ++ hw_priv->rx_timestamp = jiffies; ++ ++ struct wsm_rx rx; ++ struct ieee80211_hdr *hdr; ++ size_t hdr_len; ++ ++ rx.status = WSM_GET32(buf); ++ rx.channelNumber = WSM_GET16(buf); ++ rx.rxedRate = WSM_GET8(buf); ++ rx.rcpiRssi = WSM_GET8(buf); ++ rx.flags = WSM_GET32(buf); ++ ++ /* TODO:COMBO: Frames received from scanning are received ++ * with interface ID == 2 */ ++ if (is_hardware_xradio(hw_priv)) { ++ if (interface_link_id == XRWL_GENERIC_IF_ID) { ++ /* Frames received in response to SCAN ++ * Request */ ++ interface_link_id = ++ get_interface_id_scanning(hw_priv); ++ if (interface_link_id == -1) { ++ interface_link_id = hw_priv->roc_if_id; ++ } ++#ifdef ROAM_OFFLOAD ++ if (hw_priv->auto_scanning) { ++ interface_link_id = hw_priv->scan.if_id; ++ } ++#endif/*ROAM_OFFLOAD*/ ++ } ++ /* linkid (peer sta id is encoded in bit 25-28 of ++ flags field */ ++ rx.link_id = ((rx.flags & (0xf << 25)) >> 25); ++ rx.if_id = interface_link_id; ++ } else { ++ rx.link_id = interface_link_id; ++ rx.if_id = 0; ++ } ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, rx.if_id); ++ if (!priv) { ++ dev_dbg(hw_priv->pdev, "got frame on a vif we don't have, dropped\n"); ++ return 0; ++ } ++ //remove wsm hdr of skb ++ hdr_len = buf->data - buf->begin; ++ skb_pull(*skb_p, hdr_len); ++ ++ /* FW Workaround: Drop probe resp or ++ beacon when RSSI is 0 */ ++ hdr = (struct ieee80211_hdr *) (*skb_p)->data; ++ ++ if (!rx.rcpiRssi && ++ (ieee80211_is_probe_resp(hdr->frame_control) || ++ ieee80211_is_beacon(hdr->frame_control))) { ++ spin_unlock(&priv->vif_lock); ++ return 0; ++ } ++ ++ /* If no RSSI subscription has been made, ++ * convert RCPI to RSSI here */ ++ if (!priv->cqm_use_rssi) ++ rx.rcpiRssi = rx.rcpiRssi / 2 - 110; ++ ++ if (!rx.status && unlikely(ieee80211_is_deauth(hdr->frame_control))) { ++ if (priv->join_status == XRADIO_JOIN_STATUS_STA) { ++ /* Shedule unjoin work */ ++ dev_dbg(hw_priv->pdev, ++ "Issue unjoin command (RX).\n"); ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, ++ &priv->unjoin_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ } ++ } ++ xradio_rx_cb(priv, &rx, skb_p); ++ if (*skb_p) ++ skb_push(*skb_p, hdr_len); ++ spin_unlock(&priv->vif_lock); ++ ++ return 0; ++ ++underflow: ++ return -EINVAL; ++} ++ ++static int wsm_event_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf, ++ int interface_link_id) ++{ ++ int first; ++ struct xradio_wsm_event *event = NULL; ++ struct xradio_vif *priv; ++ ++ if (!is_hardware_xradio(hw_priv)) ++ interface_link_id = 0; ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); ++ ++ if (unlikely(!priv)) { ++ dev_warn(hw_priv->pdev, "Event: %d(%d) for removed " ++ "interface, ignoring\n", __le32_to_cpu(WSM_GET32(buf)), ++ __le32_to_cpu(WSM_GET32(buf))); ++ return 0; ++ } ++ ++ if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { ++ /* STA is stopped. */ ++ return 0; ++ } ++ spin_unlock(&priv->vif_lock); ++ ++ event = kzalloc(sizeof(struct xradio_wsm_event), GFP_KERNEL); ++ if (event == NULL) { ++ dev_err(hw_priv->pdev, "xr_kzalloc failed!"); ++ return -EINVAL; ++ } ++ ++ event->evt.eventId = __le32_to_cpu(WSM_GET32(buf)); ++ event->evt.eventData = __le32_to_cpu(WSM_GET32(buf)); ++ event->if_id = interface_link_id; ++ ++ dev_dbg(hw_priv->pdev, "Event: %d(%d)\n", ++ event->evt.eventId, event->evt.eventData); ++ ++ spin_lock(&hw_priv->event_queue_lock); ++ first = list_empty(&hw_priv->event_queue); ++ list_add_tail(&event->link, &hw_priv->event_queue); ++ spin_unlock(&hw_priv->event_queue_lock); ++ ++ if (first) ++ queue_work(hw_priv->workqueue, &hw_priv->event_handler); ++ ++ return 0; ++ ++underflow: ++ kfree(event); ++ return -EINVAL; ++} ++ ++#define PRINT_11K_MEASRURE 1 ++static int wsm_measure_cmpl_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ MEASUREMENT_COMPLETE measure_cmpl; ++ u8 cca_chanload; ++ u32 buf_len = 0; ++ u32 *data; ++ ++ LMAC_MEAS_CHANNEL_LOAD_RESULTS *chanload_res; ++ LMAC_MEAS_NOISE_HISTOGRAM_RESULTS *noise_res; ++ WSM_GET(buf, &measure_cmpl, 12); ++ ++ switch (measure_cmpl.MeasurementType) { ++ case ChannelLoadMeasurement: ++ buf_len = sizeof(LMAC_MEAS_CHANNEL_LOAD_RESULTS); ++ break; ++ case NoiseHistrogramMeasurement: ++ buf_len = sizeof(LMAC_MEAS_NOISE_HISTOGRAM_RESULTS); ++ break; ++ case BeaconReport: ++ buf_len = sizeof(LMAC_MEAS_BEACON_RESULTS); ++ break; ++ case STAstatisticsReport: ++ buf_len = sizeof(LMAC_MEAS_STA_STATS_RESULTS); ++ break; ++ case LinkMeasurement: ++ buf_len = sizeof(LMAC_MEAS_LINK_MEASUREMENT_RESULTS); ++ break; ++ } ++ wsm_printk(XRADIO_DBG_ERROR, "[11K]buf_len = %d\n", buf_len); ++ WSM_GET(buf, &measure_cmpl.MeasurementReport, buf_len); ++ ++ data = (u32 *)(&measure_cmpl); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[0]=%08x\n", data[0]); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[1]=%08x\n", data[1]); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[2]=%08x\n", data[2]); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[3]=%08x\n", data[3]); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[4]=%08x\n", data[4]); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[5]=%08x\n", data[5]); ++// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[6]=%08x\n", data[6]); ++ wsm_printk(XRADIO_DBG_ERROR, "[***HL***]MeasurementType=%0d\n", measure_cmpl.MeasurementType); ++ ++ if (measure_cmpl.Status == WSM_STATUS_SUCCESS){ ++ switch (measure_cmpl.MeasurementType) { ++ case ChannelLoadMeasurement: ++ chanload_res = &measure_cmpl.MeasurementReport.ChannelLoadResults; ++ cca_chanload = (chanload_res->ChannelLoadCCA == MEAS_CCA) ? ++ chanload_res->CCAbusyFraction : ++ chanload_res->ChannelLoad; ++ #ifdef PRINT_11K_MEASRURE ++ wsm_printk(XRADIO_DBG_ERROR, "[11K] ChannelLoadMeasurement Result:\n"\ ++ "ChannelLoadCCA = %d\n"\ ++ "ChannelNum = %d\n"\ ++ "Duration = %d\n"\ ++ "Fraction = %d\n", \ ++ chanload_res->ChannelLoadCCA,\ ++ chanload_res->ChannelNum,\ ++ chanload_res->MeasurementDuration,\ ++ cca_chanload ++ ); ++ #endif ++ break; ++ case NoiseHistrogramMeasurement: ++ noise_res = &measure_cmpl.MeasurementReport.NoiseHistogramResults; ++// IpiRpi = (noise_res->IpiRpi == MEAS_RPI) ? ++// chanload_res->CCAbusyFraction : ++// chanload_res->ChannelLoad; ++ #ifdef PRINT_11K_MEASRURE ++ wsm_printk(XRADIO_DBG_ERROR, "[11K] NoiseHistogramResults:\n"\ ++ "IpiRpi = %d\n"\ ++ "ChannelNum = %d\n"\ ++ "PI_0__Density = %d\n"\ ++ "PI_1__Density = %d\n"\ ++ "PI_2__Density = %d\n"\ ++ "PI_3__Density = %d\n"\ ++ "PI_4__Density = %d\n"\ ++ "PI_5__Density = %d\n"\ ++ "PI_6__Density = %d\n"\ ++ "PI_7__Density = %d\n"\ ++ "PI_8__Density = %d\n"\ ++ "PI_9__Density = %d\n"\ ++ "PI_10_Density = %d\n", \ ++ noise_res->IpiRpi,\ ++ noise_res->ChannelNum,\ ++ noise_res->PI_0_Density,\ ++ noise_res->PI_1_Density,\ ++ noise_res->PI_2_Density,\ ++ noise_res->PI_3_Density,\ ++ noise_res->PI_4_Density,\ ++ noise_res->PI_5_Density,\ ++ noise_res->PI_6_Density,\ ++ noise_res->PI_7_Density,\ ++ noise_res->PI_8_Density,\ ++ noise_res->PI_9_Density,\ ++ noise_res->PI_10_Density ++ ); ++ #endif ++ break; ++ case BeaconReport: ++ break; ++ case STAstatisticsReport: ++ break; ++ case LinkMeasurement: ++ break; ++ } ++ } else { ++ wsm_printk(XRADIO_DBG_ERROR, "11K Measure(type=%d) Fail\n", measure_cmpl.MeasurementType); ++ } ++ ++ return 0; ++ ++underflow: ++ return -EINVAL; ++} ++/* TODO:COMBO:Make this perVIFF once mac80211 support is available */ ++static int wsm_channel_switch_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ wsm_unlock_tx(hw_priv); /* Re-enable datapath */ ++ SYS_WARN(WSM_GET32(buf)); ++ ++ hw_priv->channel_switch_in_progress = 0; ++ wake_up(&hw_priv->channel_switch_done); ++ ++ if (hw_priv->wsm_cbc.channel_switch) ++ hw_priv->wsm_cbc.channel_switch(hw_priv); ++ return 0; ++ ++underflow: ++ return -EINVAL; ++} ++ ++static int wsm_set_pm_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ wsm_oper_unlock(hw_priv); ++ return 0; ++} ++ ++static int wsm_scan_complete_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++#ifdef ROAM_OFFLOAD ++ if(hw_priv->auto_scanning == 0) ++ wsm_oper_unlock(hw_priv); ++#else ++ wsm_oper_unlock(hw_priv); ++#endif /*ROAM_OFFLOAD*/ ++ ++ if (hw_priv->wsm_cbc.scan_complete) { ++ struct wsm_scan_complete arg; ++ arg.status = WSM_GET32(buf); ++ arg.psm = WSM_GET8(buf); ++ arg.numChannels = WSM_GET8(buf); ++ hw_priv->wsm_cbc.scan_complete(hw_priv, &arg); ++ } ++ return 0; ++ ++underflow: ++ return -EINVAL; ++} ++ ++static int wsm_find_complete_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ /* TODO: Implement me. */ ++ //STUB(); ++ return 0; ++} ++ ++static int wsm_suspend_resume_indication(struct xradio_common *hw_priv, ++ int interface_link_id, ++ struct wsm_buf *buf) ++{ ++ if (hw_priv->wsm_cbc.suspend_resume) { ++ u32 flags; ++ struct wsm_suspend_resume arg; ++ struct xradio_vif *priv; ++ ++ if (is_hardware_xradio(hw_priv)) { ++ int i; ++ arg.if_id = interface_link_id; ++ /* TODO:COMBO: Extract bitmap from suspend-resume ++ * TX indication */ ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (!priv) ++ continue; ++ if (priv->join_status == ++ XRADIO_JOIN_STATUS_AP) { ++ arg.if_id = priv->if_id; ++ break; ++ } ++ arg.link_id = 0; ++ } ++ } else { ++ arg.if_id = 0; ++ arg.link_id = interface_link_id; ++ } ++ ++ flags = WSM_GET32(buf); ++ arg.stop = !(flags & 1); ++ arg.multicast = !!(flags & 8); ++ arg.queue = (flags >> 1) & 3; ++ ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg.if_id); ++ if (unlikely(!priv)) { ++ wsm_printk(XRADIO_DBG_MSG, "suspend-resume indication" ++ " for removed interface!\n"); ++ return 0; ++ } ++ hw_priv->wsm_cbc.suspend_resume(priv, &arg); ++ spin_unlock(&priv->vif_lock); ++ } ++ return 0; ++ ++underflow: ++ return -EINVAL; ++} ++ ++ ++/* ******************************************************************** */ ++/* WSM TX */ ++ ++int wsm_cmd_send(struct xradio_common *hw_priv, ++ struct wsm_buf *buf, ++ void *arg, u16 cmd, long tmo, int if_id) ++{ ++ size_t buf_len = buf->data - buf->begin; ++ int ret; ++ ++ if (cmd == 0x0006 || cmd == 0x0005) /* Write/Read MIB */ ++ wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X [MIB: 0x%.4X] (%d)\n", ++ cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), ++ buf_len); ++ else ++ wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%d)\n", cmd, buf_len); ++ ++ if (unlikely(hw_priv->bh_error)) { ++ wsm_buf_reset(buf); ++ wsm_printk(XRADIO_DBG_ERROR, "bh error!>>> 0x%.4X (%d)\n", cmd, buf_len); ++ return -ETIMEDOUT; ++ } ++ ++ /* Fill HI message header */ ++ /* BH will add sequence number */ ++ ++ /* TODO:COMBO: Add if_id from to the WSM header */ ++ /* if_id == -1 indicates that command is HW specific, ++ * eg. wsm_configuration which is called during driver initialzation ++ * (mac80211 .start callback called when first ifce is created. )*/ ++ ++ /* send hw specific commands on if 0 */ ++ if (if_id == -1) ++ if_id = 0; ++ ++ ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); ++ ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd | ++ ((is_hardware_xradio(hw_priv)) ? (if_id << 6) : 0)); ++ ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ SYS_BUG(hw_priv->wsm_cmd.ptr); ++ hw_priv->wsm_cmd.done = 0; ++ hw_priv->wsm_cmd.ptr = buf->begin; ++ hw_priv->wsm_cmd.len = buf_len; ++ hw_priv->wsm_cmd.arg = arg; ++ hw_priv->wsm_cmd.cmd = cmd; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ ++ xradio_bh_wakeup(hw_priv); ++ ++ if (unlikely(hw_priv->bh_error)) { ++ /* Do not wait for timeout if BH is dead. Exit immediately. */ ++ ret = 0; ++ } else { ++ unsigned long wsm_cmd_max_tmo; ++ ++ /* Give start cmd a little more time */ ++ if (unlikely(tmo == WSM_CMD_START_TIMEOUT)) ++ wsm_cmd_max_tmo = WSM_CMD_START_TIMEOUT; ++ else ++ wsm_cmd_max_tmo = WSM_CMD_DEFAULT_TIMEOUT; ++ ++ /*Set max timeout.*/ ++ wsm_cmd_max_tmo = jiffies + wsm_cmd_max_tmo; ++ ++ /* Firmware prioritizes data traffic over control confirm. ++ * Loop below checks if data was RXed and increases timeout ++ * accordingly. */ ++ do { ++ /* It's safe to use unprotected access to wsm_cmd.done here */ ++ ret = wait_event_timeout(hw_priv->wsm_cmd_wq, hw_priv->wsm_cmd.done, tmo); ++ ++ /* check time since last rxed and max timeout.*/ ++ } while (!ret && ++ time_before_eq(jiffies, hw_priv->rx_timestamp+tmo) && ++ time_before(jiffies, wsm_cmd_max_tmo)); ++ ++ } ++ ++ if (unlikely(ret == 0)) { ++ u16 raceCheck; ++ ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ raceCheck = hw_priv->wsm_cmd.cmd; ++ hw_priv->wsm_cmd.arg = NULL; ++ hw_priv->wsm_cmd.ptr = NULL; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ ++ dev_err(hw_priv->pdev, "***CMD timeout!>>> 0x%.4X (%d), buf_use=%d, bh_state=%d\n", ++ cmd, buf_len, hw_priv->hw_bufs_used, hw_priv->bh_error); ++ /* Race condition check to make sure _confirm is not called ++ * after exit of _send */ ++ if (raceCheck == 0xFFFF) { ++ /* If wsm_handle_rx got stuck in _confirm we will hang ++ * system there. It's better than silently currupt ++ * stack or heap, isn't it? */ ++ SYS_BUG(wait_event_timeout( ++ hw_priv->wsm_cmd_wq, ++ hw_priv->wsm_cmd.done, ++ WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); ++ } ++ ++ /* Kill BH thread to report the error to the top layer. */ ++ hw_priv->bh_error = 1; ++#ifdef BH_USE_SEMAPHORE ++ up(&hw_priv->bh_sem); ++#else ++ wake_up(&hw_priv->bh_wq); ++#endif ++ ret = -ETIMEDOUT; ++ } else { ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ SYS_BUG(!hw_priv->wsm_cmd.done); ++ ret = hw_priv->wsm_cmd.ret; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ } ++ wsm_buf_reset(buf); ++ return ret; ++} ++ ++/* ******************************************************************** */ ++/* WSM TX port control */ ++ ++void wsm_lock_tx(struct xradio_common *hw_priv) ++{ ++ down(&hw_priv->tx_lock_sem); ++ atomic_add(1, &hw_priv->tx_lock); ++ /* always check event if wsm_vif_lock_tx.*/ ++ if (wsm_flush_tx(hw_priv)) ++ wsm_printk(XRADIO_DBG_MSG, "TX is locked.\n"); ++ up(&hw_priv->tx_lock_sem); ++} ++ ++void wsm_vif_lock_tx(struct xradio_vif *priv) ++{ ++ struct xradio_common *hw_priv = priv->hw_priv; ++ down(&hw_priv->tx_lock_sem); ++ if (atomic_add_return(1, &hw_priv->tx_lock) == 1) { ++ if (wsm_vif_flush_tx(priv)) ++ wsm_printk(XRADIO_DBG_MSG, "TX is locked for" ++ " if_id %d.\n", priv->if_id); ++ } ++ up(&hw_priv->tx_lock_sem); ++} ++ ++void wsm_lock_tx_async(struct xradio_common *hw_priv) ++{ ++ if (atomic_add_return(1, &hw_priv->tx_lock) == 1) ++ wsm_printk(XRADIO_DBG_MSG, "TX is locked (async).\n"); ++} ++ ++bool wsm_flush_tx(struct xradio_common *hw_priv) ++{ ++ long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT; ++ ++ /* Flush must be called with TX lock held. */ ++ SYS_BUG(!atomic_read(&hw_priv->tx_lock)); ++ ++ /* First check if we really need to do something. ++ * It is safe to use unprotected access, as hw_bufs_used ++ * can only decrements. */ ++ if (!hw_priv->hw_bufs_used) ++ return true; ++ ++ if (hw_priv->bh_error) { ++ /* In case of failure do not wait for magic. */ ++ wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, " ++ "will not flush TX.\n"); ++ return false; ++ } else { ++ /* Get "oldest" frame, if any frames stuck in firmware, ++ query all of them until max timeout. */ ++ int num = hw_priv->hw_bufs_used + 1; ++ while (xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS, ++ 0xffffffff, &timeout)) { ++ if (timeout < 0 || !num) { ++ /* Hmmm... Not good. Frame had stuck in firmware. */ ++ wsm_printk(XRADIO_DBG_ERROR, ++ "%s:hw_bufs_used=%d, num=%d, timeout=%ld\n", ++ __func__, hw_priv->hw_bufs_used, num, timeout); ++ hw_priv->bh_error = 1; ++#ifdef BH_USE_SEMAPHORE ++ up(&hw_priv->bh_sem); ++#else ++ wake_up(&hw_priv->bh_wq); ++#endif ++ return false; ++ } else if (wait_event_timeout(hw_priv->bh_evt_wq, ++ !hw_priv->hw_bufs_used, timeout) > 0) { ++ return true; ++ } ++ --num; ++ } ++ if (hw_priv->hw_bufs_used) ++ wsm_printk(XRADIO_DBG_ERROR, "%s:No pengding, but hw_bufs_used=%d\n", ++ __func__, hw_priv->hw_bufs_used); ++ /* Ok, everything is flushed. */ ++ return true; ++ } ++} ++ ++bool wsm_vif_flush_tx(struct xradio_vif *priv) ++{ ++ struct xradio_common *hw_priv = priv->hw_priv; ++ long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT; ++ int if_id = priv->if_id; ++ ++ /* Flush must be called with TX lock held. */ ++ SYS_BUG(!atomic_read(&hw_priv->tx_lock)); ++ ++ /* First check if we really need to do something. ++ * It is safe to use unprotected access, as hw_bufs_used ++ * can only decrements. */ ++ if (!hw_priv->hw_bufs_used_vif[if_id]) ++ return true; ++ ++ if (hw_priv->bh_error) { ++ /* In case of failure do not wait for magic. */ ++ wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, " ++ "will not flush TX.\n"); ++ return false; ++ } else { ++ /* Get "oldest" frame, if any frames stuck in firmware, ++ query all of them until max timeout. */ ++ int num = hw_priv->hw_bufs_used_vif[if_id] + 1; ++ while (xradio_query_txpkt_timeout(hw_priv, if_id, 0xffffffff, &timeout)) { ++ if (timeout < 0 || !num) { ++ /* Hmmm... Not good. Frame had stuck in firmware. */ ++ wsm_printk(XRADIO_DBG_ERROR, "%s: if_id=%d, hw_bufs_used_vif=%d, num=%d\n", ++ __func__, if_id, hw_priv->hw_bufs_used_vif[priv->if_id], ++ num); ++ hw_priv->bh_error = 1; ++ #ifdef BH_USE_SEMAPHORE ++ up(&hw_priv->bh_sem); ++ #else ++ wake_up(&hw_priv->bh_wq); ++ #endif ++ return false; ++ } else if (wait_event_timeout(hw_priv->bh_evt_wq, ++ !hw_priv->hw_bufs_used_vif[if_id], timeout) > 0) { ++ return true; ++ } ++ --num; ++ } ++ if (hw_priv->hw_bufs_used_vif[if_id]) ++ wsm_printk(XRADIO_DBG_ERROR, "%s:No pengding, but hw_bufs_used_vif=%d\n", ++ __func__, hw_priv->hw_bufs_used_vif[priv->if_id]); ++ /* Ok, everything is flushed. */ ++ return true; ++ } ++} ++ ++ ++void wsm_unlock_tx(struct xradio_common *hw_priv) ++{ ++ int tx_lock; ++ if (hw_priv->bh_error) ++ wsm_printk(XRADIO_DBG_ERROR, "bh_error=%d, wsm_unlock_tx is unsafe\n", ++ hw_priv->bh_error); ++ else { ++ tx_lock = atomic_sub_return(1, &hw_priv->tx_lock); ++ if (tx_lock < 0) { ++ SYS_BUG(1); ++ } else if (tx_lock == 0) { ++ xradio_bh_wakeup(hw_priv); ++ wsm_printk(XRADIO_DBG_MSG, "TX is unlocked.\n"); ++ } ++ } ++} ++ ++/* ******************************************************************** */ ++/* WSM RX */ ++ ++int wsm_handle_exception(struct xradio_common *hw_priv, u8 *data, size_t len) ++{ ++ struct wsm_buf buf; ++ u32 reason; ++ u32 reg[18]; ++ char fname[48]; ++ int i = 0; ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ struct xradio_vif *priv = NULL; ++#endif ++ ++ static const char * const reason_str[] = { ++ "undefined instruction", ++ "prefetch abort", ++ "data abort", ++ "unknown error", ++ }; ++ ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ /* Send the event upwards on the FW exception */ ++ xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ); ++ ++ spin_lock(&hw_priv->vif_list_lock); ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (!priv) ++ continue; ++ //ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); ++ } ++ spin_unlock(&hw_priv->vif_list_lock); ++#endif ++ ++ buf.begin = buf.data = data; ++ buf.end = &buf.begin[len]; ++ ++ reason = WSM_GET32(&buf); ++ for (i = 0; i < ARRAY_SIZE(reg); ++i) ++ reg[i] = WSM_GET32(&buf); ++ WSM_GET(&buf, fname, sizeof(fname)); ++ ++ if (reason < 4) { ++ dev_err(hw_priv->pdev, "Firmware exception: %s.\n", ++ reason_str[reason]); ++ } else { ++ dev_err(hw_priv->pdev, "Firmware assert at %.*s, line %d, reason=0x%x\n", ++ sizeof(fname), fname, reg[1], reg[2]); ++ } ++ ++ for (i = 0; i < 12; i += 4) { ++ dev_err(hw_priv->pdev, "Firmware:" \ ++ "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", ++ i + 0, reg[i + 0], i + 1, reg[i + 1], ++ i + 2, reg[i + 2], i + 3, reg[i + 3]); ++ } ++ dev_err(hw_priv->pdev, "Firmware:" \ ++ "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", ++ reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); ++ i += 4; ++ dev_err(hw_priv->pdev, "Firmware:CPSR: 0x%.8X, SPSR: 0x%.8X\n", ++ reg[i + 0], reg[i + 1]); ++ ++ return 0; ++ ++underflow: ++ dev_err(hw_priv->pdev, "Firmware exception.\n"); ++ print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, data, len); ++ return -EINVAL; ++} ++ ++static int wsm_debug_indication(struct xradio_common *hw_priv, ++ struct wsm_buf *buf) ++{ ++ //for only one debug item. ++ u32 dbg_id, buf_data=0; ++ u16 dbg_buf_len; ++ u8 dbg_len; ++ u8 *dbg_buf; ++ dbg_id = WSM_GET32(buf); ++ ++ dbg_buf_len = buf->end - buf->data; ++ ++ if (dbg_id == 5) { ++ do { ++ dbg_buf_len = buf->end - buf->data; ++ dbg_len = WSM_GET8(buf); ++ if (dbg_len > dbg_buf_len - sizeof(dbg_len)) { ++ wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_len = %d\n", dbg_len); ++ wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_buf_len = %d\n", dbg_buf_len); ++ wsm_printk(XRADIO_DBG_ERROR, "[FW]debug ind err\n"); ++ //ret = -EINVAL; ++ //return ret; ++ ++ ++ break; ++ } ++ ++ dbg_buf = buf->data; ++ //print it; ++ wsm_printk(XRADIO_DBG_ALWY, "[FW-LOG] %s", dbg_buf); ++ // ++ buf->data += dbg_len; ++ ++ } while (buf->data < buf->end); ++ } else { ++ if (dbg_buf_len >= 4) { ++ buf_data = WSM_GET32(buf); ++ wsm_printk(XRADIO_DBG_ALWY, "[FW-DEBUG] DbgId = %d, data = %d", dbg_id, buf_data); ++ } else { ++ wsm_printk(XRADIO_DBG_ALWY, "[FW-DEBUG] DbgId = %d", dbg_id); ++ } ++ } ++ ++ return 0; ++ ++underflow: ++ SYS_WARN(1); ++ return -EINVAL; ++} ++ ++#if defined(DGB_XRADIO_HWT) ++extern u8 hwt_testing; ++extern u16 hwt_tx_len; ++extern u16 hwt_tx_num; ++extern int sent_num; ++extern struct timeval hwt_start_time; ++extern struct timeval hwt_end_time; ++int wsm_hwt_tx_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf) ++{ ++ u32 *packetID = (u32 *)(buf->data+8); ++ u8 num = *(buf->data + 6); ++ ++ wsm_printk(XRADIO_DBG_NIY, "%s, num=%d, hw_bufs_used=%d\n", ++ __func__, num, hw_priv->hw_bufs_used); ++ ++ wsm_release_vif_tx_buffer(hw_priv, 0, num); ++ wsm_release_tx_buffer(hw_priv, num-1); //one release is in bh. ++ ++ //confirm of last packet, so report the test results. ++ if (*(buf->data+7) & 0x01) { //last packet ++ u32 time_int = 0; ++ u32 total = hwt_tx_num*hwt_tx_len*8; ++ do_gettimeofday(&hwt_end_time); ++ time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \ ++ (hwt_end_time.tv_usec-hwt_start_time.tv_usec); ++ wsm_printk(XRADIO_DBG_ALWY, "%s, HIF TX: time=%dms, throughput=%d.%dMbps, SW time=%dus\n", ++ __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int, ++ (*(u32 *)(buf->data+8))); ++ hwt_tx_len = 0; ++ hwt_tx_num = 0; ++ sent_num = 0; //reset the sent_num ++ hwt_testing = 0; ++ } ++ return 0; ++} ++ ++u16 recv_num = 0; ++extern u8 hwt_rx_en; ++extern u16 hwt_rx_len; ++extern u16 hwt_rx_num; ++int wsm_hwt_rx_frames(struct xradio_common *hw_priv, struct wsm_buf *buf) ++{ ++ ++ wsm_printk(XRADIO_DBG_NIY, "%s, status=%d, len=%d\n", __func__, ++ *(u16 *)(buf->data+2), *(u16 *)(buf->data+4)); ++ recv_num++; ++ if (recv_num >= hwt_rx_num) { //last packet ++ u32 time_int = 0; ++ u32 total = recv_num*hwt_rx_len*8; ++ do_gettimeofday(&hwt_end_time); ++ time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \ ++ (hwt_end_time.tv_usec-hwt_start_time.tv_usec); ++ wsm_printk(XRADIO_DBG_ALWY, "%s, HIF RX: time=%dms, throughput=%d.%dMbps\n", ++ __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int); ++ hwt_rx_en = 0; ++ hwt_rx_num = 0; ++ recv_num = 0; //reset the recv_num ++ hwt_testing = 0; ++ } ++ ++ return 0; ++} ++ ++int wsm_hwt_enc_results(struct xradio_common *hw_priv, struct wsm_buf *buf) ++{ ++ wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, enc throughput=%d.%02dMbps\n", __func__, ++ *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12)); ++ hwt_testing = 0; ++ return 0; ++} ++ ++int wsm_hwt_mic_results(struct xradio_common *hw_priv, struct wsm_buf *buf) ++{ ++ wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, mic throughput=%d.%02dMbps\n", __func__, ++ *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12)); ++ hwt_testing = 0; ++ return 0; ++} ++#endif //DGB_XRADIO_HWT ++ ++ ++int wsm_handle_rx(struct xradio_common *hw_priv, int id, ++ struct wsm_hdr *wsm, struct sk_buff **skb_p) ++{ ++ int ret = 0; ++ struct wsm_buf wsm_buf; ++ struct xradio_vif *priv = NULL; ++ int i = 0; ++ int interface_link_id = (id >> 6) & 0x0F; ++#ifdef ROAM_OFFLOAD ++#if 0 ++ struct xradio_vif *priv; ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); ++ if (unlikely(!priv)) { ++ SYS_WARN(1); ++ return 0; ++ } ++ spin_unlock(&priv->vif_lock); ++#endif ++#endif/*ROAM_OFFLOAD*/ ++ ++ /* Strip link id. */ ++ id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); ++ ++ wsm_buf.begin = (u8 *)&wsm[0]; ++ wsm_buf.data = (u8 *)&wsm[1]; ++ wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; ++ ++ wsm_printk(XRADIO_DBG_MSG, "<<< 0x%.4X (%d)\n", id, ++ wsm_buf.end - wsm_buf.begin); ++ ++#if defined(DGB_XRADIO_HWT) ++/***************************for HWT ********************************/ ++ if (id == 0x0424) { ++ u16 TestID = *(u16 *)(wsm_buf.data); ++ if (TestID == 1) //test frame confirm. ++ wsm_hwt_tx_confirm(hw_priv, &wsm_buf); ++ else { ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ hw_priv->wsm_cmd.ret = *((u16 *)(wsm_buf.data) + 1); ++ hw_priv->wsm_cmd.done = 1; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ wake_up(&hw_priv->wsm_cmd_wq); ++ wsm_printk(XRADIO_DBG_ALWY, "HWT TestID=0x%x Confirm ret=%d\n", ++ *(u16 *)(wsm_buf.data), hw_priv->wsm_cmd.ret); ++ } ++ return 0; ++ } else if (id == 0x0824) { ++ u16 TestID = *(u16 *)(wsm_buf.data); ++ switch (TestID) { ++ case 2: //recieve a test frame. ++ wsm_hwt_rx_frames(hw_priv, &wsm_buf); ++ break; ++ case 3: //enc test result. ++ wsm_hwt_enc_results(hw_priv, &wsm_buf); ++ break; ++ case 4: //mic test result. ++ wsm_hwt_mic_results(hw_priv, &wsm_buf); ++ break; ++ default: ++ wsm_printk(XRADIO_DBG_ERROR, "HWT ERROR Indication TestID=0x%x\n", TestID); ++ break; ++ } ++ return 0; ++ } ++/***************************for HWT ********************************/ ++#endif //DGB_XRADIO_HWT ++ ++ if (id == 0x404) { ++ ret = wsm_tx_confirm(hw_priv, &wsm_buf, interface_link_id); ++#ifdef MCAST_FWDING ++#if 1 ++ } else if (id == 0x422) { ++ ret = wsm_give_buffer_confirm(hw_priv, &wsm_buf); ++#endif ++#endif ++ ++ } else if (id == 0x41E) { ++ ret = wsm_multi_tx_confirm(hw_priv, &wsm_buf, ++ interface_link_id); ++ } else if (id & 0x0400) { ++ void *wsm_arg; ++ u16 wsm_cmd; ++ ++ /* Do not trust FW too much. Protection against repeated ++ * response and race condition removal (see above). */ ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ wsm_arg = hw_priv->wsm_cmd.arg; ++ wsm_cmd = hw_priv->wsm_cmd.cmd & ++ ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); ++ hw_priv->wsm_cmd.cmd = 0xFFFF; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ ++ if (SYS_WARN((id & ~0x0400) != wsm_cmd)) { ++ /* Note that any non-zero is a fatal retcode. */ ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ switch (id) { ++ case 0x0409: ++ /* Note that wsm_arg can be NULL in case of timeout in ++ * wsm_cmd_send(). */ ++ if (likely(wsm_arg)) ++ ret = wsm_configuration_confirm(hw_priv, ++ wsm_arg, ++ &wsm_buf); ++ break; ++ case 0x0405: ++ if (likely(wsm_arg)) ++ ret = wsm_read_mib_confirm(hw_priv, wsm_arg, ++ &wsm_buf); ++ break; ++ case 0x0406: ++ if (likely(wsm_arg)) ++ ret = wsm_write_mib_confirm(hw_priv, wsm_arg, ++ &wsm_buf, ++ interface_link_id); ++ break; ++ case 0x040B: ++ if (likely(wsm_arg)) ++ ret = wsm_join_confirm(hw_priv, wsm_arg, &wsm_buf); ++ if (ret) ++ wsm_printk(XRADIO_DBG_WARN, "Join confirm Failed!\n"); ++ break; ++ case 0x040E: /* 11K measure*/ ++ if (likely(wsm_arg)) ++ ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); ++ if (ret) ++ wsm_printk(XRADIO_DBG_ERROR, "[***HL***]11K Confirm Error\n"); ++ ++ break; ++ ++#ifdef MCAST_FWDING ++ case 0x0423: /* req buffer cfm*/ ++ if (likely(wsm_arg)){ ++ xradio_for_each_vif(hw_priv, priv, i) { ++ if (priv && (priv->join_status == XRADIO_JOIN_STATUS_AP)) ++ ret = wsm_request_buffer_confirm(priv, ++ wsm_arg, &wsm_buf); ++ } ++ } ++ break; ++#endif ++ case 0x0407: /* start-scan */ ++#ifdef ROAM_OFFLOAD ++ if (hw_priv->auto_scanning) { ++ if (atomic_read(&hw_priv->scan.in_progress)) { ++ hw_priv->auto_scanning = 0; ++ } ++ else { ++ wsm_oper_unlock(hw_priv); ++ up(&hw_priv->scan.lock); ++ } ++ } ++#endif /*ROAM_OFFLOAD*/ ++ case 0x0408: /* stop-scan */ ++ case 0x040A: /* wsm_reset */ ++ case 0x040C: /* add_key */ ++ case 0x040D: /* remove_key */ ++ case 0x0410: /* wsm_set_pm */ ++ case 0x0411: /* set_bss_params */ ++ case 0x0412: /* set_tx_queue_params */ ++ case 0x0413: /* set_edca_params */ ++ case 0x0416: /* switch_channel */ ++ case 0x0417: /* start */ ++ case 0x0418: /* beacon_transmit */ ++ case 0x0419: /* start_find */ ++ case 0x041A: /* stop_find */ ++ case 0x041B: /* update_ie */ ++ case 0x041C: /* map_link */ ++ SYS_WARN(wsm_arg != NULL); ++ ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); ++ if (ret) ++ wsm_printk(XRADIO_DBG_ERROR, ++ "wsm_generic_confirm " ++ "failed for request 0x%.4X.\n", ++ id & ~0x0400); ++ break; ++ default: ++ SYS_BUG(1); ++ } ++ ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ hw_priv->wsm_cmd.ret = ret; ++ hw_priv->wsm_cmd.done = 1; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ ret = 0; /* Error response from device should ne stop BH. */ ++ ++ wake_up(&hw_priv->wsm_cmd_wq); ++ } else if (id & 0x0800) { ++ switch (id) { ++ case 0x0801: ++ ret = wsm_startup_indication(hw_priv, &wsm_buf); ++ break; ++ case 0x0804: ++ ret = wsm_receive_indication(hw_priv, interface_link_id, ++ &wsm_buf, skb_p); ++ break; ++ case 0x0805: ++ ret = wsm_event_indication(hw_priv, &wsm_buf, ++ interface_link_id); ++ break; ++ case 0x0807: ++ wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm_measure_cmpl_indication\n"); ++// wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm->len = %d\n",wsm->len); ++ ++ ret = wsm_measure_cmpl_indication(hw_priv, &wsm_buf); ++ break; ++ case 0x080A: ++ ret = wsm_channel_switch_indication(hw_priv, &wsm_buf); ++ break; ++ case 0x0809: ++ ret = wsm_set_pm_indication(hw_priv, &wsm_buf); ++ break; ++ case 0x0806: ++#ifdef ROAM_OFFLOAD ++ if(hw_priv->auto_scanning && hw_priv->frame_rcvd) { ++ struct xradio_vif *priv; ++ hw_priv->frame_rcvd = 0; ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); ++ if (unlikely(!priv)) { ++ SYS_WARN(1); ++ return 0; ++ } ++ spin_unlock(&priv->vif_lock); ++ if (hw_priv->beacon) { ++ struct wsm_scan_complete *scan_cmpl = \ ++ (struct wsm_scan_complete *) \ ++ ((u8 *)wsm + sizeof(struct wsm_hdr)); ++ struct ieee80211_rx_status *rhdr = \ ++ IEEE80211_SKB_RXCB(hw_priv->beacon); ++ rhdr->signal = (s8)scan_cmpl->reserved; ++ if (!priv->cqm_use_rssi) { ++ rhdr->signal = rhdr->signal / 2 - 110; ++ } ++ if (!hw_priv->beacon_bkp) ++ hw_priv->beacon_bkp = \ ++ skb_copy(hw_priv->beacon, GFP_ATOMIC); ++ ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); ++ hw_priv->beacon = hw_priv->beacon_bkp; ++ ++ hw_priv->beacon_bkp = NULL; ++ } ++ wsm_printk(XRADIO_DBG_MSG, \ ++ "Send Testmode Event.\n"); ++ xradio_testmode_event(priv->hw->wiphy, ++ NL80211_CMD_NEW_SCAN_RESULTS, 0, ++ 0, GFP_KERNEL); ++ ++ } ++#endif /*ROAM_OFFLOAD*/ ++ ret = wsm_scan_complete_indication(hw_priv, &wsm_buf); ++ break; ++ case 0x080B: ++ ret = wsm_find_complete_indication(hw_priv, &wsm_buf); ++ break; ++ case 0x080C: ++ ret = wsm_suspend_resume_indication(hw_priv, ++ interface_link_id, &wsm_buf); ++ break; ++ case 0x080E: ++ wsm_printk(XRADIO_DBG_MSG, "wsm_debug_indication"); ++ ret = wsm_debug_indication(hw_priv, &wsm_buf); ++ break; ++ ++ default: ++ wsm_printk(XRADIO_DBG_ERROR, "unknown Indmsg ID=0x%04x,len=%d\n", ++ wsm->id, wsm->len); ++ break; ++ } ++ } else { ++ SYS_WARN(1); ++ ret = -EINVAL; ++ } ++out: ++ return ret; ++} ++ ++static bool wsm_handle_tx_data(struct xradio_vif *priv, ++ const struct wsm_tx *wsm, ++ const struct ieee80211_tx_info *tx_info, ++ struct xradio_txpriv *txpriv, ++ struct xradio_queue *queue) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++#ifdef P2P_MULTIVIF ++ struct xradio_vif *p2p_if_vif = NULL; ++#endif ++ bool handled = false; ++ const struct ieee80211_hdr *frame = ++ (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; ++ __le16 fctl = frame->frame_control; ++ enum { ++ doProbe, ++ doDrop, ++ doJoin, ++ doOffchannel, ++ doWep, ++ doTx, ++ } action = doTx; ++ ++ hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++#ifdef P2P_MULTIVIF ++ if (priv->if_id == XRWL_GENERIC_IF_ID) ++ p2p_if_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1); ++#endif ++ frame = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; ++ fctl = frame->frame_control; ++ ++ switch (priv->mode) { ++ case NL80211_IFTYPE_STATION: ++ if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING && ++ priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ ieee80211_is_data(fctl)) { ++ spin_lock(&priv->bss_loss_lock); ++ priv->bss_loss_confirm_id = wsm->packetID; ++ priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMING; ++ spin_unlock(&priv->bss_loss_lock); ++ } else if (unlikely((priv->join_status <= XRADIO_JOIN_STATUS_MONITOR) || ++ memcmp(frame->addr1, priv->join_bssid,sizeof(priv->join_bssid)))) { ++#ifdef P2P_MULTIVIF ++ if (p2p_if_vif && (p2p_if_vif->join_status > XRADIO_JOIN_STATUS_MONITOR) && ++ (priv->join_status < XRADIO_JOIN_STATUS_MONITOR)) { ++ ++ /* Post group formation, frame transmission on p2p0 ++ * interafce should not use offchannel/generic channel. ++ * Instead, the frame should be transmitted on interafce ++ * 1. This is needed by wsc fw. ++ */ ++ action = doTx; ++ txpriv->raw_if_id = 1; ++ } else ++#endif ++ if (ieee80211_is_auth(fctl)) ++ action = doJoin; ++ else if ((ieee80211_is_deauth(fctl) || ieee80211_is_disassoc(fctl))&& ++ priv->join_status < XRADIO_JOIN_STATUS_MONITOR) ++ action = doDrop; //no need to send deauth when STA-unjoined, yangfh 2014-10-31 16:32:16. ++ else if (ieee80211_is_probe_req(fctl)) ++ action = doTx; ++ else if (memcmp(frame->addr1, priv->join_bssid, ++ sizeof(priv->join_bssid)) && ++ (priv->join_status == ++ XRADIO_JOIN_STATUS_STA) && ++ (ieee80211_is_data(fctl))) { ++ action = doDrop; ++ } ++ else if (priv->join_status >= ++ XRADIO_JOIN_STATUS_MONITOR) ++ action = doTx; ++ else if (get_interface_id_scanning(hw_priv) != -1) { ++ wsm_printk(XRADIO_DBG_WARN, "Scan ONGOING dropping" ++ " offchannel eligible frame.\n"); ++ action = doDrop; ++ } else { ++ if (ieee80211_is_probe_resp(fctl)) ++ action = doDrop; ++ else ++ action = doOffchannel; ++ wsm_printk(XRADIO_DBG_WARN, "Offchannel fctl=0x%04x", fctl); ++ } ++ } ++ break; ++ case NL80211_IFTYPE_AP: ++ if (unlikely(!priv->join_status)) ++ action = doDrop; ++ else if (unlikely(!(BIT(txpriv->raw_link_id) & ++ (BIT(0) | priv->link_id_map)))) { ++ wsm_printk(XRADIO_DBG_WARN, ++ "A frame with expired link id " ++ "is dropped.\n"); ++ action = doDrop; ++ } ++ if (xradio_queue_get_generation(wsm->packetID) > ++ XRADIO_MAX_REQUEUE_ATTEMPTS) { ++ /* HACK!!! WSM324 firmware has tendency to requeue ++ * multicast frames in a loop, causing performance ++ * drop and high power consumption of the driver. ++ * In this situation it is better just to drop ++ * the problematic frame. */ ++ wsm_printk(XRADIO_DBG_WARN, ++ "Too many attempts " ++ "to requeue a frame. " ++ "Frame is dropped, fctl=0x%04x.\n", fctl); ++ action = doDrop; ++ } ++ break; ++ case NL80211_IFTYPE_ADHOC: ++ case NL80211_IFTYPE_MESH_POINT: ++ //STUB(); ++ case NL80211_IFTYPE_MONITOR: ++ default: ++ action = doDrop; ++ break; ++ } ++ ++ if (action == doTx) { ++ if (unlikely(ieee80211_is_probe_req(fctl))) { ++#ifdef CONFIG_XRADIO_TESTMODE ++ if (hw_priv->enable_advance_scan && ++ (priv->join_status == XRADIO_JOIN_STATUS_STA) && ++ (hw_priv->advanceScanElems.scanMode == ++ XRADIO_SCAN_MEASUREMENT_ACTIVE)) ++ /* If Advance Scan is Requested on Active Scan ++ * then transmit the Probe Request */ ++ action = doTx; ++ else ++#endif ++ action = doProbe; ++ } else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && ++ tx_info->control.hw_key && ++ unlikely(tx_info->control.hw_key->keyidx != ++ priv->wep_default_key_id) && ++ (tx_info->control.hw_key->cipher == ++ WLAN_CIPHER_SUITE_WEP40 || ++ tx_info->control.hw_key->cipher == ++ WLAN_CIPHER_SUITE_WEP104)) { ++ action = doWep; ++ } ++ } ++ ++ switch (action) { ++ case doProbe: ++ { ++ /* An interesting FW "feature". Device filters ++ * probe responses. ++ * The easiest way to get it back is to convert ++ * probe request into WSM start_scan command. */ ++ wsm_printk(XRADIO_DBG_MSG, \ ++ "Convert probe request to scan.\n"); ++ wsm_lock_tx_async(hw_priv); ++ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); ++ queue_delayed_work(hw_priv->workqueue, ++ &hw_priv->scan.probe_work, 0); ++ handled = true; ++ } ++ break; ++ case doDrop: ++ { ++ /* See detailed description of "join" below. ++ * We are dropping everything except AUTH in non-joined mode. */ ++ wsm_printk(XRADIO_DBG_MSG, "Drop frame (0x%.4X).\n", fctl); ++#ifdef CONFIG_XRADIO_TESTMODE ++ SYS_BUG(xradio_queue_remove(hw_priv, queue, ++ __le32_to_cpu(wsm->packetID))); ++#else ++ SYS_BUG(xradio_queue_remove(queue, ++ __le32_to_cpu(wsm->packetID))); ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ handled = true; ++ } ++ break; ++ case doJoin: ++ { ++ /* p2p should disconnect when sta try to join a different channel AP, ++ * because no good performance in this case. ++ */ ++ struct xradio_vif *p2p_tmp_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1); ++ if (priv->if_id == 0 && p2p_tmp_vif) { ++ if (p2p_tmp_vif->join_status >= XRADIO_JOIN_STATUS_STA && ++ hw_priv->channel_changed) { ++ wsm_printk(XRADIO_DBG_WARN, "combo with different channels, p2p disconnect.\n"); ++ wms_send_disassoc_to_self(hw_priv, p2p_tmp_vif); ++ } ++ } ++ ++ /* There is one more interesting "feature" ++ * in FW: it can't do RX/TX before "join". ++ * "Join" here is not an association, ++ * but just a syncronization between AP and STA. ++ * priv->join_status is used only in bh thread and does ++ * not require protection */ ++ wsm_printk(XRADIO_DBG_NIY, "Issue join command.\n"); ++ wsm_lock_tx_async(hw_priv); ++ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); ++ if (queue_work(hw_priv->workqueue, &priv->join_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ handled = true; ++ } ++ break; ++ case doOffchannel: ++ { ++ wsm_printk(XRADIO_DBG_MSG, "Offchannel TX request.\n"); ++ wsm_lock_tx_async(hw_priv); ++ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); ++ if (queue_work(hw_priv->workqueue, &priv->offchannel_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ handled = true; ++ } ++ break; ++ case doWep: ++ { ++ wsm_printk(XRADIO_DBG_MSG, "Issue set_default_wep_key.\n"); ++ wsm_lock_tx_async(hw_priv); ++ priv->wep_default_key_id = tx_info->control.hw_key->keyidx; ++ hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); ++ if (queue_work(hw_priv->workqueue, &priv->wep_key_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ handled = true; ++ } ++ break; ++ case doTx: ++ { ++#if 0 ++ /* Kept for history. If you want to implement wsm->more, ++ * make sure you are able to send a frame after that. */ ++ wsm->more = (count > 1) ? 1 : 0; ++ if (wsm->more) { ++ /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ++ * It's undocumented in WSM spec, but XRADIO hangs ++ * if 'more' is set and no TX is performed due to TX ++ * buffers limitation. */ ++ if (priv->hw_bufs_used + 1 == ++ priv->wsm_caps.numInpChBufs) ++ wsm->more = 0; ++ } ++ ++ /* BUG!!! FIXME: we can't use 'more' at all: we don't know ++ * future. It could be a request from upper layer with TX lock ++ * requirements (scan, for example). If "more" is set device ++ * will not send data and wsm_tx_lock() will fail... ++ * It's not obvious how to fix this deadlock. Any ideas? ++ * As a workaround more is set to 0. */ ++ wsm->more = 0; ++#endif /* 0 */ ++ ++ if (ieee80211_is_deauth(fctl) && ++ priv->mode != NL80211_IFTYPE_AP) { ++ /* Shedule unjoin work */ ++ wsm_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX).\n"); ++#if 0 ++ wsm->more = 0; ++#endif /* 0 */ ++ wsm_lock_tx_async(hw_priv); ++ if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) ++ wsm_unlock_tx(hw_priv); ++ } ++ } ++ break; ++ } ++ return handled; ++} ++ ++static int xradio_get_prio_queue(struct xradio_vif *priv, ++ u32 link_id_map, int *total) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ static u32 urgent; ++ struct wsm_edca_queue_params *edca; ++ unsigned score, best = -1; ++ int winner = -1; ++ int queued; ++ int i; ++ urgent = BIT(priv->link_id_after_dtim) | BIT(priv->link_id_uapsd); ++ ++ /* search for a winner using edca params */ ++ for (i = 0; i < 4; ++i) { ++ queued = xradio_queue_get_num_queued(priv, ++ &hw_priv->tx_queue[i], ++ link_id_map); ++ if (!queued) ++ continue; ++ *total += queued; ++ edca = &priv->edca.params[i]; ++ score = ((edca->aifns + edca->cwMin) << 16) + ++ (edca->cwMax - edca->cwMin) * ++ (prandom_u32() & 0xFFFF); ++ if (score < best && (winner < 0 || i != 3)) { ++ best = score; ++ winner = i; ++ } ++ } ++ ++ /* override winner if bursting */ ++ if (winner >= 0 && hw_priv->tx_burst_idx >= 0 && ++ winner != hw_priv->tx_burst_idx && ++ !xradio_queue_get_num_queued(priv, ++ &hw_priv->tx_queue[winner], ++ link_id_map & urgent) && ++ xradio_queue_get_num_queued(priv, ++ &hw_priv->tx_queue[hw_priv->tx_burst_idx], ++ link_id_map)) ++ winner = hw_priv->tx_burst_idx; ++ ++ return winner; ++} ++ ++static int wsm_get_tx_queue_and_mask(struct xradio_vif *priv, ++ struct xradio_queue **queue_p, ++ u32 *tx_allowed_mask_p, ++ bool *more) ++{ ++ struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); ++ int idx; ++ u32 tx_allowed_mask; ++ int total = 0; ++ ++ /* Search for a queue with multicast frames buffered */ ++ if (priv->tx_multicast) { ++ tx_allowed_mask = BIT(priv->link_id_after_dtim); ++ idx = xradio_get_prio_queue(priv, ++ tx_allowed_mask, &total); ++ if (idx >= 0) { ++ *more = total > 1; ++ goto found; ++ } ++ } ++ ++ /* Search for unicast traffic */ ++ tx_allowed_mask = ~priv->sta_asleep_mask; ++ tx_allowed_mask |= BIT(priv->link_id_uapsd); ++ if (priv->sta_asleep_mask) { ++ tx_allowed_mask |= priv->pspoll_mask; ++ tx_allowed_mask &= ~BIT(priv->link_id_after_dtim); ++ } else { ++ tx_allowed_mask |= BIT(priv->link_id_after_dtim); ++ } ++ idx = xradio_get_prio_queue(priv, ++ tx_allowed_mask, &total); ++ if (idx < 0) ++ return -ENOENT; ++ ++found: ++ *queue_p = &hw_priv->tx_queue[idx]; ++ *tx_allowed_mask_p = tx_allowed_mask; ++ return 0; ++} ++ ++int wsm_get_tx(struct xradio_common *hw_priv, u8 **data, ++ size_t *tx_len, int *burst, int *vif_selected) ++{ ++ struct wsm_tx *wsm = NULL; ++ struct ieee80211_tx_info *tx_info; ++ struct xradio_queue *queue = NULL; ++ int queue_num; ++ u32 tx_allowed_mask = 0; ++ struct xradio_txpriv *txpriv = NULL; ++#ifdef P2P_MULTIVIF ++ int first = 1; ++ int tmp_if_id = -1; ++#endif ++ /* ++ * Count was intended as an input for wsm->more flag. ++ * During implementation it was found that wsm->more ++ * is not usable, see details above. It is kept just ++ * in case you would like to try to implement it again. ++ */ ++ int count = 0; ++#ifdef P2P_MULTIVIF ++ int if_pending = XRWL_MAX_VIFS - 1; ++#else ++ int if_pending = 1; ++#endif ++ ++ /* More is used only for broadcasts. */ ++ bool more = false; ++ ++ if (hw_priv->wsm_cmd.ptr) { ++ ++count; ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ SYS_BUG(!hw_priv->wsm_cmd.ptr); ++ *data = hw_priv->wsm_cmd.ptr; ++ *tx_len = hw_priv->wsm_cmd.len; ++ *burst = 1; ++ *vif_selected = -1; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ } else { ++ for (;;) { ++ int ret; ++ struct xradio_vif *priv; ++#if 0 ++ int num_pending_vif0, num_pending_vif1; ++#endif ++ if (atomic_add_return(0, &hw_priv->tx_lock)) ++ break; ++ /* Keep one buffer reserved for commands. Note ++ that, hw_bufs_used has already been incremented ++ before reaching here. */ ++ if (hw_priv->hw_bufs_used >= ++ hw_priv->wsm_caps.numInpChBufs) ++ break; ++#ifdef P2P_MULTIVIF ++ if (first) { ++ tmp_if_id = hw_priv->if_id_selected; ++ hw_priv->if_id_selected = 2; ++ } ++#endif ++ priv = wsm_get_interface_for_tx(hw_priv); ++ /* go to next interface ID to select next packet */ ++#ifdef P2P_MULTIVIF ++ if (first) { ++ hw_priv->if_id_selected = tmp_if_id; ++ first = 0; ++ } else ++#endif ++ hw_priv->if_id_selected ^= 1; ++ ++ /* There might be no interface before add_interface ++ * call */ ++ if (!priv) { ++ if (if_pending) { ++#ifdef P2P_MULTIVIF ++ if_pending--; ++#else ++ if_pending = 0; ++#endif ++ continue; ++ } ++ break; ++ } ++ ++#if 0 ++ if (((priv->if_id == 0) && ++ (hw_priv->hw_bufs_used_vif[0] >= ++ XRWL_FW_VIF0_THROTTLE)) || ++ ((priv->if_id == 1) && ++ (hw_priv->hw_bufs_used_vif[1] >= ++ XRWL_FW_VIF1_THROTTLE))) { ++ spin_unlock(&priv->vif_lock); ++ if (if_pending) { ++ if_pending = 0; ++ continue; ++ } ++ break; ++ } ++#endif ++ ++ /* This can be removed probably: xradio_vif will not ++ * be in hw_priv->vif_list (as returned from ++ * wsm_get_interface_for_tx) until it's fully ++ * enabled, so statement above will take case of that*/ ++ if (!atomic_read(&priv->enabled)) { ++ spin_unlock(&priv->vif_lock); ++ break; ++ } ++ ++ /* TODO:COMBO: Find the next interface for which ++ * packet needs to be found */ ++ spin_lock_bh(&priv->ps_state_lock); ++ ret = wsm_get_tx_queue_and_mask(priv, &queue, ++ &tx_allowed_mask, &more); ++ queue_num = queue - hw_priv->tx_queue; ++ ++ if (priv->buffered_multicasts && ++ (ret || !more) && ++ (priv->tx_multicast || ++ !priv->sta_asleep_mask)) { ++ priv->buffered_multicasts = false; ++ if (priv->tx_multicast) { ++ priv->tx_multicast = false; ++ queue_work(hw_priv->workqueue, ++ &priv->multicast_stop_work); ++ } ++ } ++ ++ spin_unlock_bh(&priv->ps_state_lock); ++ ++ if (ret) { ++ spin_unlock(&priv->vif_lock); ++#ifdef P2P_MULTIVIF ++ if (if_pending) { ++#else ++ if (if_pending == 1) { ++#endif ++#ifdef P2P_MULTIVIF ++ if_pending--; ++#else ++ if_pending = 0; ++#endif ++ continue; ++ } ++ break; ++ } ++ ++ if (xradio_queue_get(queue, ++ priv->if_id, ++ tx_allowed_mask, ++ &wsm, &tx_info, &txpriv)) { ++ spin_unlock(&priv->vif_lock); ++ if_pending = 0; ++ continue; ++ } ++ ++#ifdef ROC_DEBUG ++#ifndef P2P_MULTIVIF ++ { ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) ++ &((u8 *)wsm)[txpriv->offset]; ++ ++ wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d," ++ " if_id %d\n", ++ hdr->frame_control, ++ txpriv->offchannel_if_id, ++ priv->if_id); ++ } ++#else ++ { ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) ++ &((u8 *)wsm)[txpriv->offset]; ++ ++ wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d," ++ " if_id %d\n", ++ hdr->frame_control, ++ txpriv->raw_if_id, ++ priv->if_id); ++ } ++#endif ++#endif ++ ++ if (wsm_handle_tx_data(priv, wsm, ++ tx_info, txpriv, queue)) { ++ spin_unlock(&priv->vif_lock); ++ if_pending = 0; ++ continue; /* Handled by WSM */ ++ } ++ ++ wsm->hdr.id &= __cpu_to_le16( ++ ~WSM_TX_IF_ID(WSM_TX_IF_ID_MAX)); ++#ifdef P2P_MULTIVIF ++ if (txpriv->raw_if_id) ++ wsm->hdr.id |= cpu_to_le16( ++ WSM_TX_IF_ID(txpriv->raw_if_id)); ++#else ++ if (txpriv->offchannel_if_id) ++ wsm->hdr.id |= cpu_to_le16( ++ WSM_TX_IF_ID(txpriv->offchannel_if_id)); ++#endif ++ else ++ wsm->hdr.id |= cpu_to_le16( ++ WSM_TX_IF_ID(priv->if_id)); ++ ++ *vif_selected = priv->if_id; ++#ifdef ROC_DEBUG ++/* remand the roc debug. */ ++#ifndef P2P_MULTIVIF ++ ++ { ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) ++ &((u8 *)wsm)[txpriv->offset]; ++ ++ wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d," ++ " if_id %d\n", ++ hdr->frame_control, ++ txpriv->offchannel_if_id, ++ priv->if_id); ++ } ++#else ++ { ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) ++ &((u8 *)wsm)[txpriv->offset]; ++ ++ wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d," ++ " if_id %d\n", ++ hdr->frame_control, ++ txpriv->raw_if_id, ++ priv->if_id); ++ } ++#endif ++#endif ++ ++ priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); ++ ++ *data = (u8 *)wsm; ++ *tx_len = __le16_to_cpu(wsm->hdr.len); ++ ++ /* allow bursting if txop is set */ ++ if (priv->edca.params[queue_num].txOpLimit) ++ *burst = min(*burst, ++ (int)xradio_queue_get_num_queued(priv, ++ queue, tx_allowed_mask) + 1); ++ else ++ *burst = 1; ++ ++ /* store index of bursting queue */ ++ if (*burst > 1) ++ hw_priv->tx_burst_idx = queue_num; ++ else ++ hw_priv->tx_burst_idx = -1; ++ ++ if (more) { ++ struct ieee80211_hdr *hdr = ++ (struct ieee80211_hdr *) ++ &((u8 *)wsm)[txpriv->offset]; ++ if(strstr(&priv->ssid[0], "6.1.12")) { ++ if(hdr->addr1[0] & 0x01 ) { ++ hdr->frame_control |= ++ cpu_to_le16(IEEE80211_FCTL_MOREDATA); ++ } ++ } ++ else { ++ /* more buffered multicast/broadcast frames ++ * ==> set MoreData flag in IEEE 802.11 header ++ * to inform PS STAs */ ++ hdr->frame_control |= ++ cpu_to_le16(IEEE80211_FCTL_MOREDATA); ++ } ++ } ++ wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%d) %p %c\n", ++ 0x0004, *tx_len, *data, ++ wsm->more ? 'M' : ' '); ++ ++count; ++ spin_unlock(&priv->vif_lock); ++ break; ++ } ++ } ++ ++ return count; ++} ++ ++void wsm_txed(struct xradio_common *hw_priv, u8 *data) ++{ ++ if (data == hw_priv->wsm_cmd.ptr) { ++ spin_lock(&hw_priv->wsm_cmd.lock); ++ hw_priv->wsm_cmd.ptr = NULL; ++ spin_unlock(&hw_priv->wsm_cmd.lock); ++ } ++} ++ ++/* ******************************************************************** */ ++/* WSM buffer */ ++ ++void wsm_buf_init(struct wsm_buf *buf) ++{ ++ int size = (SDIO_BLOCK_SIZE<<1); //for sdd file big than SDIO_BLOCK_SIZE ++ SYS_BUG(buf->begin); ++ buf->begin = kmalloc(size, GFP_KERNEL); ++ buf->end = buf->begin ? &buf->begin[size] : buf->begin; ++ wsm_buf_reset(buf); ++} ++ ++void wsm_buf_deinit(struct wsm_buf *buf) ++{ ++ if(buf->begin) ++ kfree(buf->begin); ++ buf->begin = buf->data = buf->end = NULL; ++} ++ ++static void wsm_buf_reset(struct wsm_buf *buf) ++{ ++ if (buf->begin) { ++ buf->data = &buf->begin[4]; ++ *(u32 *)buf->begin = 0; ++ } else ++ buf->data = buf->begin; ++} ++ ++static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) ++{ ++ size_t pos = buf->data - buf->begin; ++ size_t size = pos + extra_size; ++ ++ ++ if (size & (SDIO_BLOCK_SIZE - 1)) { ++ size &= SDIO_BLOCK_SIZE; ++ size += SDIO_BLOCK_SIZE; ++ } ++ ++ buf->begin = krealloc(buf->begin, size, GFP_KERNEL); ++ if (buf->begin) { ++ buf->data = &buf->begin[pos]; ++ buf->end = &buf->begin[size]; ++ return 0; ++ } else { ++ buf->end = buf->data = buf->begin; ++ return -ENOMEM; ++ } ++} ++ ++static struct xradio_vif ++ *wsm_get_interface_for_tx(struct xradio_common *hw_priv) ++{ ++ struct xradio_vif *priv = NULL, *i_priv; ++ int i = hw_priv->if_id_selected; ++ ++ if ( 1 /*TODO:COMBO*/) { ++ spin_lock(&hw_priv->vif_list_lock); ++ i_priv = hw_priv->vif_list[i] ? ++ xrwl_get_vif_from_ieee80211(hw_priv->vif_list[i]) : NULL; ++ if (i_priv) { ++ priv = i_priv; ++ spin_lock(&priv->vif_lock); ++ } ++ /* TODO:COMBO: ++ * Find next interface based on TX bitmap announced by the FW ++ * Find next interface based on load balancing */ ++ spin_unlock(&hw_priv->vif_list_lock); ++ } else { ++ priv = xrwl_hwpriv_to_vifpriv(hw_priv, 0); ++ } ++ ++ return priv; ++} ++ ++static inline int get_interface_id_scanning(struct xradio_common *hw_priv) ++{ ++ if (hw_priv->scan.req || hw_priv->scan.direct_probe) ++ return hw_priv->scan.if_id; ++ else ++ return -1; ++} +diff --git a/drivers/net/wireless/xradio/wsm.h b/drivers/net/wireless/xradio/wsm.h +new file mode 100644 +index 00000000..e88314ee +--- /dev/null ++++ b/drivers/net/wireless/xradio/wsm.h +@@ -0,0 +1,2396 @@ ++/* ++ * wsm interfaces for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef XRADIO_WSM_H_INCLUDED ++#define XRADIO_WSM_H_INCLUDED ++ ++#include ++ ++struct xradio_common; ++ ++/* Bands */ ++/* Radio band 2.412 -2.484 GHz. */ ++#define WSM_PHY_BAND_2_4G (0) ++ ++/* Radio band 4.9375-5.8250 GHz. */ ++#define WSM_PHY_BAND_5G (1) ++ ++/* Transmit rates */ ++/* 1 Mbps ERP-DSSS */ ++#define WSM_TRANSMIT_RATE_1 (0) ++ ++/* 2 Mbps ERP-DSSS */ ++#define WSM_TRANSMIT_RATE_2 (1) ++ ++/* 5.5 Mbps ERP-CCK, ERP-PBCC (Not supported) */ ++/* #define WSM_TRANSMIT_RATE_5 (2) */ ++ ++/* 11 Mbps ERP-CCK, ERP-PBCC (Not supported) */ ++/* #define WSM_TRANSMIT_RATE_11 (3) */ ++ ++/* 22 Mbps ERP-PBCC (Not supported) */ ++/* #define WSM_TRANSMIT_RATE_22 (4) */ ++ ++/* 33 Mbps ERP-PBCC (Not supported) */ ++/* #define WSM_TRANSMIT_RATE_33 (5) */ ++ ++/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_6 (6) ++ ++/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_9 (7) ++ ++/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_12 (8) ++ ++/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_18 (9) ++ ++/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_24 (10) ++ ++/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_36 (11) ++ ++/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_48 (12) ++ ++/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_54 (13) ++ ++/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_HT_6 (14) ++ ++/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_HT_13 (15) ++ ++/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_HT_19 (16) ++ ++/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ ++#define WSM_TRANSMIT_RATE_HT_26 (17) ++ ++/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_HT_39 (18) ++ ++/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ ++#define WSM_TRANSMIT_RATE_HT_52 (19) ++ ++/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ ++#define WSM_TRANSMIT_RATE_HT_58 (20) ++ ++/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ ++#define WSM_TRANSMIT_RATE_HT_65 (21) ++ ++/* Scan types */ ++/* Foreground scan */ ++#define WSM_SCAN_TYPE_FOREGROUND (0) ++ ++/* Background scan */ ++#define WSM_SCAN_TYPE_BACKGROUND (1) ++ ++/* Auto scan */ ++#define WSM_SCAN_TYPE_AUTO (2) ++ ++/* Scan flags */ ++/* Forced background scan means if the station cannot */ ++/* enter the power-save mode, it shall force to perform a */ ++/* background scan. Only valid when ScanType is */ ++/* background scan. */ ++#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) ++ ++/* The WLAN device scans one channel at a time so */ ++/* that disturbance to the data traffic is minimized. */ ++#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) ++ ++/* Preamble Type. Long if not set. */ ++#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) ++ ++/* 11n Tx Mode. Mixed if not set. */ ++#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) ++ ++#define WSM_FLAG_MAC_INSTANCE_1 (BIT(4)) ++ ++#define WSM_FLAG_MAC_INSTANCE_0 (~(BIT(4))) ++ ++/* Scan constraints */ ++/* Maximum number of channels to be scanned. */ ++#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) ++ ++/* The maximum number of SSIDs that the device can scan for. */ ++#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) ++#ifdef CONFIG_XRADIO_TESTMODE ++/* Transmit flags */ ++/* Start Expiry time from the receipt of tx request */ ++#define WSM_TX_FLAG_EXPIRY_TIME (BIT(0)) ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++/* Power management modes */ ++/* 802.11 Active mode */ ++#define WSM_PSM_ACTIVE (0) ++ ++/* 802.11 PS mode */ ++#define WSM_PSM_PS BIT(0) ++ ++/* Fast Power Save bit */ ++#define WSM_PSM_FAST_PS_FLAG BIT(7) ++ ++/* Dynamic aka Fast power save */ ++#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) ++ ++/* Undetermined */ ++/* Note : Undetermined status is reported when the */ ++/* NULL data frame used to advertise the PM mode to */ ++/* the AP at Pre or Post Background Scan is not Acknowledged */ ++#define WSM_PSM_UNKNOWN BIT(1) ++ ++/* Queue IDs */ ++/* best effort/legacy */ ++#define WSM_QUEUE_BEST_EFFORT (0) ++ ++/* background */ ++#define WSM_QUEUE_BACKGROUND (1) ++ ++/* video */ ++#define WSM_QUEUE_VIDEO (2) ++ ++/* voice */ ++#define WSM_QUEUE_VOICE (3) ++ ++/* HT TX parameters */ ++/* Non-HT */ ++#define WSM_HT_TX_NON_HT (0) ++ ++/* Mixed format */ ++#define WSM_HT_TX_MIXED (1) ++ ++/* Greenfield format */ ++#define WSM_HT_TX_GREENFIELD (2) ++ ++/* STBC allowed */ ++#define WSM_HT_TX_STBC (BIT(7)) ++ ++/* EPTA prioirty flags for BT Coex */ ++/* default epta priority */ ++#define WSM_EPTA_PRIORITY_DEFAULT 4 ++/* use for normal data */ ++#define WSM_EPTA_PRIORITY_DATA 4 ++/* use for connect/disconnect/roaming*/ ++#define WSM_EPTA_PRIORITY_MGT 5 ++/* use for action frames */ ++#define WSM_EPTA_PRIORITY_ACTION 5 ++/* use for AC_VI data */ ++#define WSM_EPTA_PRIORITY_VIDEO 5 ++/* use for AC_VO data */ ++#define WSM_EPTA_PRIORITY_VOICE 6 ++/* use for EAPOL exchange */ ++#define WSM_EPTA_PRIORITY_EAPOL 7 ++ ++/* TX status */ ++/* Frame was sent aggregated */ ++/* Only valid for WSM_SUCCESS status. */ ++#define WSM_TX_STATUS_AGGREGATION (BIT(0)) ++ ++/* Host should requeue this frame later. */ ++/* Valid only when status is WSM_REQUEUE. */ ++#define WSM_TX_STATUS_REQUEUE (BIT(1)) ++ ++/* Normal Ack */ ++#define WSM_TX_STATUS_NORMAL_ACK (0<<2) ++ ++/* No Ack */ ++#define WSM_TX_STATUS_NO_ACK (1<<2) ++ ++/* No explicit acknowledgement */ ++#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) ++ ++/* Block Ack */ ++/* Only valid for WSM_SUCCESS status. */ ++#define WSM_TX_STATUS_BLOCK_ACK (3<<2) ++ ++/* RX status */ ++/* Unencrypted */ ++#define WSM_RX_STATUS_UNENCRYPTED (0<<0) ++ ++/* WEP */ ++#define WSM_RX_STATUS_WEP (1<<0) ++ ++/* TKIP */ ++#define WSM_RX_STATUS_TKIP (2<<0) ++ ++/* AES */ ++#define WSM_RX_STATUS_AES (3<<0) ++ ++/* WAPI */ ++#define WSM_RX_STATUS_WAPI (4<<0) ++ ++/* Macro to fetch encryption subfield. */ ++#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) ++ ++/* Frame was part of an aggregation */ ++#define WSM_RX_STATUS_AGGREGATE (BIT(3)) ++ ++/* Frame was first in the aggregation */ ++#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) ++ ++/* Frame was last in the aggregation */ ++#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) ++ ++/* Indicates a defragmented frame */ ++#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) ++ ++/* Indicates a Beacon frame */ ++#define WSM_RX_STATUS_BEACON (BIT(7)) ++ ++/* Indicates STA bit beacon TIM field */ ++#define WSM_RX_STATUS_TIM (BIT(8)) ++ ++/* Indicates Beacon frame's virtual bitmap contains multicast bit */ ++#define WSM_RX_STATUS_MULTICAST (BIT(9)) ++ ++/* Indicates frame contains a matching SSID */ ++#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) ++ ++/* Indicates frame contains a matching BSSI */ ++#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) ++ ++/* Indicates More bit set in Framectl field */ ++#define WSM_RX_STATUS_MORE_DATA (BIT(12)) ++ ++/* Indicates frame received during a measurement process */ ++#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) ++ ++/* Indicates frame received as an HT packet */ ++#define WSM_RX_STATUS_HT (BIT(14)) ++ ++/* Indicates frame received with STBC */ ++#define WSM_RX_STATUS_STBC (BIT(15)) ++ ++/* Indicates Address 1 field matches dot11StationId */ ++#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) ++ ++/* Indicates Group address present in the Address 1 field */ ++#define WSM_RX_STATUS_GROUP (BIT(17)) ++ ++/* Indicates Broadcast address present in the Address 1 field */ ++#define WSM_RX_STATUS_BROADCAST (BIT(18)) ++ ++/* Indicates group key used with encrypted frames */ ++#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) ++ ++/* Macro to fetch encryption key index. */ ++#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) ++ ++/* Frame Control field starts at Frame offset + 2 */ ++#define WSM_TX_2BYTES_SHIFT (BIT(7)) ++ ++/* Join mode */ ++/* IBSS */ ++#define WSM_JOIN_MODE_IBSS (0) ++ ++/* BSS */ ++#define WSM_JOIN_MODE_BSS (1) ++ ++/* PLCP preamble type */ ++/* For long preamble */ ++#define WSM_JOIN_PREAMBLE_LONG (0) ++ ++/* For short preamble (Long for 1Mbps) */ ++#define WSM_JOIN_PREAMBLE_SHORT (1) ++ ++/* For short preamble (Long for 1 and 2Mbps) */ ++#define WSM_JOIN_PREAMBLE_SHORT_2 (2) ++ ++/* Join flags */ ++/* Unsynchronized */ ++#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) ++/* The BSS owner is a P2P GO */ ++#define WSM_JOIN_FLAGS_P2P_GO BIT(1) ++/* Force to join BSS with the BSSID and the ++ * SSID specified without waiting for beacons. The ++ * ProbeForJoin parameter is ignored. */ ++#define WSM_JOIN_FLAGS_FORCE BIT(2) ++/* Give probe request/response higher ++ * priority over the BT traffic */ ++#define WSM_JOIN_FLAGS_PRIO BIT(3) ++ ++/* Key types */ ++#define WSM_KEY_TYPE_WEP_DEFAULT (0) ++#define WSM_KEY_TYPE_WEP_PAIRWISE (1) ++#define WSM_KEY_TYPE_TKIP_GROUP (2) ++#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) ++#define WSM_KEY_TYPE_AES_GROUP (4) ++#define WSM_KEY_TYPE_AES_PAIRWISE (5) ++#define WSM_KEY_TYPE_WAPI_GROUP (6) ++#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) ++ ++/* Key indexes */ ++#define WSM_KEY_MAX_INDEX (10) ++ ++/* ACK policy */ ++#define WSM_ACK_POLICY_NORMAL (0) ++#define WSM_ACK_POLICY_NO_ACK (1) ++ ++/* Start modes */ ++#define WSM_START_MODE_AP (0) /* Mini AP */ ++#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ ++#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ ++ ++/* SetAssociationMode MIB flags */ ++#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) ++#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) ++#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) ++#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) ++#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) ++ ++/* RcpiRssiThreshold MIB flags */ ++#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) ++#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) ++#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) ++#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) ++ ++/* Update-ie constants */ ++#define WSM_UPDATE_IE_BEACON (BIT(0)) ++#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) ++#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) ++ ++/* WSM events */ ++/* Error */ ++#define WSM_EVENT_ERROR (0) ++ ++/* BSS lost */ ++#define WSM_EVENT_BSS_LOST (1) ++ ++/* BSS regained */ ++#define WSM_EVENT_BSS_REGAINED (2) ++ ++/* Radar detected */ ++#define WSM_EVENT_RADAR_DETECTED (3) ++ ++/* RCPI or RSSI threshold triggered */ ++#define WSM_EVENT_RCPI_RSSI (4) ++ ++/* BT inactive */ ++#define WSM_EVENT_BT_INACTIVE (5) ++ ++/* BT active */ ++#define WSM_EVENT_BT_ACTIVE (6) ++ ++#define WSM_EVENT_PS_MODE_ERROR (7) ++ ++#define WSM_EVENT_INACTIVITY (9) ++ ++/* MAC Addr Filter */ ++#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030 ++ ++/* MIB IDs */ ++/* 4.1 dot11StationId */ ++#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 ++ ++/* 4.2 dot11MaxtransmitMsduLifeTime */ ++#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 ++ ++/* 4.3 dot11MaxReceiveLifeTime */ ++#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 ++ ++/* 4.4 dot11SlotTime */ ++#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 ++ ++/* 4.5 dot11GroupAddressesTable */ ++#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 ++#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 ++ ++/* 4.6 dot11WepDefaultKeyId */ ++#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 ++ ++/* 4.7 dot11CurrentTxPowerLevel */ ++#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 ++ ++/* 4.8 dot11RTSThreshold */ ++#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 ++ ++/* Huanglu add for firmware debug control */ ++#define WSM_MIB_ID_FW_DEBUG_CONTROL 0x0008 ++ ++/* yangfh add for read/write registers from firmware*/ ++#define WSM_MIB_ID_RW_FW_REG 0x0009 ++ ++/* yangfh add for Set max number of mpdus in a-mpdu*/ ++#define WSM_MIB_ID_SET_AMPDU_NUM 0x000a ++ ++/* Huanglu add for tx-ampdu-len-adaption */ ++#define WSM_MIB_ID_SET_TALA_PARA 0x000b ++ ++/* yangfh add for set TPA param */ ++#define WSM_MIB_ID_SET_TPA_PARAM 0x000c ++ ++/* 4.9 NonErpProtection */ ++#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 ++ ++/* 4.10 ArpIpAddressesTable */ ++#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 ++#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 ++ ++/* 4.11 TemplateFrame */ ++#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 ++ ++/* 4.12 RxFilter */ ++#define WSM_MIB_ID_RX_FILTER 0x1003 ++ ++/* 4.13 BeaconFilterTable */ ++#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 ++ ++/* 4.14 BeaconFilterEnable */ ++#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 ++ ++/* 4.15 OperationalPowerMode */ ++#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 ++ ++/* 4.16 BeaconWakeUpPeriod */ ++#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 ++ ++/* 4.17 RcpiRssiThreshold */ ++#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 ++ ++/* 4.18 StatisticsTable */ ++#define WSM_MIB_ID_STATISTICS_TABLE 0x100A ++ ++/* 4.19 IbssPsConfig */ ++#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B ++ ++/* 4.20 CountersTable */ ++#define WSM_MIB_ID_COUNTERS_TABLE 0x100C ++#define WSM_MIB_ID_AMPDUCOUNTERS_TABLE 0x1036 ++#define WSM_MIB_ID_TXPIPE_TABLE 0x1037 ++#define WSM_MIB_ID_BACKOFF_DBG 0x1038 ++#define WSM_MIB_ID_BACKOFF_CTRL 0x1039 ++ ++//add yangfh for requery packet status ++#define WSM_MIB_ID_REQ_PKT_STATUS 0x1040 ++ ++//add yangfh for TPA debug informations ++#define WSM_MIB_ID_TPA_DEBUG_INFO 0x1041 ++ ++//add yangfh for tx power informations ++#define WSM_MIB_ID_TX_POWER_INFO 0x1042 ++ ++//add yangfh for some hardware information ++#define WSM_MIB_ID_HW_INFO 0x1043 ++ ++/* 4.21 BlockAckPolicy */ ++#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E ++ ++/* 4.22 OverrideInternalTxRate */ ++#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F ++ ++/* 4.23 SetAssociationMode */ ++#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 ++ ++/* 4.24 UpdateEptaConfigData */ ++#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 ++ ++/* 4.25 SelectCcaMethod */ ++#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 ++ ++/* 4.26 SetUpasdInformation */ ++#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 ++ ++/* 4.27 SetAutoCalibrationMode WBF00004073 */ ++#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 ++ ++/* 4.28 SetTxRateRetryPolicy */ ++#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 ++ ++/* 4.29 SetHostMessageTypeFilter */ ++#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 ++ ++/* 4.30 P2PFindInfo */ ++#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 ++ ++/* 4.31 P2PPsModeInfo */ ++#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 ++ ++/* 4.32 SetEtherTypeDataFrameFilter */ ++#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A ++ ++/* 4.33 SetUDPPortDataFrameFilter */ ++#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B ++ ++/* 4.34 SetMagicDataFrameFilter */ ++#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C ++#define WSM_MIB_ID_SET_HOST_SLEEP 0x1050 ++ ++/* This is the end of specification. */ ++ ++/* 4.35 P2PDeviceInfo */ ++#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D ++ ++/* 4.36 SetWCDMABand */ ++#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E ++ ++/* 4.37 GroupTxSequenceCounter */ ++#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F ++ ++/* 4.38 ProtectedMgmtPolicy */ ++#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 ++ ++/* 4.39 SetHtProtection */ ++#define WSM_MID_ID_SET_HT_PROTECTION 0x1021 ++ ++/* 4.40 GPIO Command */ ++#define WSM_MIB_ID_GPIO_COMMAND 0x1022 ++ ++/* 4.41 TSF Counter Value */ ++#define WSM_MIB_ID_TSF_COUNTER 0x1023 ++ ++/* Test Purposes Only */ ++#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D ++ ++/* 4.42 UseMultiTxConfMessage */ ++#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 ++ ++/* 4.43 Keep-alive period */ ++#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 ++ ++/* 4.44 Disable BSSID filter */ ++#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 ++ ++/* Inactivity */ ++#define WSM_MIB_ID_SET_INACTIVITY 0x1035 ++ ++/* MAC Addr Filter */ ++#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030 ++ ++#ifdef MCAST_FWDING ++/* 4.51 Set Forwarding Offload */ ++#define WSM_MIB_ID_FORWARDING_OFFLOAD 0x1033 ++#endif ++ ++/* Frame template types */ ++#define WSM_FRAME_TYPE_PROBE_REQUEST (0) ++#define WSM_FRAME_TYPE_BEACON (1) ++#define WSM_FRAME_TYPE_NULL (2) ++#define WSM_FRAME_TYPE_QOS_NULL (3) ++#define WSM_FRAME_TYPE_PS_POLL (4) ++#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) ++#define WSM_FRAME_TYPE_ARP_REPLY (6) ++ ++#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ ++ ++/* Status */ ++/* The WSM firmware has completed a request */ ++/* successfully. */ ++#define WSM_STATUS_SUCCESS (0) ++ ++/* This is a generic failure code if other error codes do */ ++/* not apply. */ ++#define WSM_STATUS_FAILURE (1) ++ ++/* A request contains one or more invalid parameters. */ ++#define WSM_INVALID_PARAMETER (2) ++ ++/* The request cannot perform because the device is in */ ++/* an inappropriate mode. */ ++#define WSM_ACCESS_DENIED (3) ++ ++/* The frame received includes a decryption error. */ ++#define WSM_STATUS_DECRYPTFAILURE (4) ++ ++/* A MIC failure is detected in the received packets. */ ++#define WSM_STATUS_MICFAILURE (5) ++ ++/* The transmit request failed due to retry limit being */ ++/* exceeded. */ ++#define WSM_STATUS_RETRY_EXCEEDED (6) ++ ++/* The transmit request failed due to MSDU life time */ ++/* being exceeded. */ ++#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) ++ ++/* The link to the AP is lost. */ ++#define WSM_STATUS_LINK_LOST (8) ++ ++/* No key was found for the encrypted frame */ ++#define WSM_STATUS_NO_KEY_FOUND (9) ++ ++/* Jammer was detected when transmitting this frame */ ++#define WSM_STATUS_JAMMER_DETECTED (10) ++ ++/* The message should be requeued later. */ ++/* This is applicable only to Transmit */ ++#define WSM_REQUEUE (11) ++ ++/* Advanced filtering options */ ++#define WSM_MAX_FILTER_ELEMENTS (4) ++ ++#define WSM_FILTER_ACTION_IGNORE (0) ++#define WSM_FILTER_ACTION_FILTER_IN (1) ++#define WSM_FILTER_ACTION_FILTER_OUT (2) ++ ++#define WSM_FILTER_PORT_TYPE_DST (0) ++#define WSM_FILTER_PORT_TYPE_SRC (1) ++ ++ ++ ++struct wsm_hdr { ++ __le16 len; ++ __le16 id; ++}; ++ ++#define WSM_TX_SEQ_MAX (7) ++#define WSM_TX_SEQ(seq) \ ++ ((seq & WSM_TX_SEQ_MAX) << 13) ++#define WSM_TX_LINK_ID_MAX (0x0F) ++#define WSM_TX_LINK_ID(link_id) \ ++ ((link_id & WSM_TX_LINK_ID_MAX) << 6) ++ ++#define WSM_TX_IF_ID_MAX (0x0F) ++#define WSM_TX_IF_ID(if_id) \ ++ ((if_id & WSM_TX_IF_ID_MAX) << 6) ++ ++#define MAX_BEACON_SKIP_TIME_MS 1000 ++ ++#ifdef FPGA_SETUP ++#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 9 / 2) ++#else ++#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 20 / 2) ++#endif ++#define WSM_CMD_EXTENDED_TIMEOUT (HZ * 20 / 2) ++ ++#define WSM_RI_GET_PEER_ID_FROM_FLAGS(_f) (((_f)&(0xF<<25)>>25)) ++ ++ ++/* ******************************************************************** */ ++/* WSM capcbility */ ++#define WSM_FW_LABEL 128 ++struct wsm_caps { ++ u16 numInpChBufs; ++ u16 sizeInpChBuf; ++ u16 hardwareId; ++ u16 hardwareSubId; ++ u16 firmwareCap; ++ u16 firmwareType; ++ u16 firmwareApiVer; ++ u16 firmwareBuildNumber; ++ u16 firmwareVersion; ++ char fw_label[WSM_FW_LABEL+2]; ++ int firmwareReady; ++}; ++ ++/* ******************************************************************** */ ++/* WSM commands */ ++ ++struct wsm_tx_power_range { ++ int min_power_level; ++ int max_power_level; ++ u32 stepping; ++}; ++ ++/* 3.1 */ ++struct wsm_configuration { ++ /* [in] */ u32 dot11MaxTransmitMsduLifeTime; ++ /* [in] */ u32 dot11MaxReceiveLifeTime; ++ /* [in] */ u32 dot11RtsThreshold; ++ /* [in, out] */ u8 *dot11StationId; ++ /* [in] */ const void *dpdData; ++ /* [in] */ size_t dpdData_size; ++ /* [out] */ u8 dot11FrequencyBandsSupported; ++ /* [out] */ u32 supportedRateMask; ++ /* [out] */ struct wsm_tx_power_range txPowerRange[2]; ++}; ++ ++int wsm_configuration(struct xradio_common *hw_priv, ++ struct wsm_configuration *arg, ++ int if_id); ++ ++/* 3.3 */ ++struct wsm_reset { ++ /* [in] */ int link_id; ++ /* [in] */ bool reset_statistics; ++}; ++ ++int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg, ++ int if_id); ++ ++//add by yangfh ++void wsm_upper_restart(struct xradio_common *hw_priv); ++ ++//add by yangfh ++void wsm_query_work(struct work_struct *work); ++ ++/* 3.5 */ ++int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *buf, ++ size_t buf_size, size_t arg_size); ++ ++/* 3.7 */ ++int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *buf, ++ size_t buf_size, int if_id); ++ ++/* 3.9 */ ++struct wsm_ssid { ++ u8 ssid[32]; ++ u32 length; ++}; ++ ++struct wsm_scan_ch { ++ u16 number; ++ u32 minChannelTime; ++ u32 maxChannelTime; ++ u32 txPowerLevel; ++}; ++ ++/* 3.13 */ ++struct wsm_scan_complete { ++ /* WSM_STATUS_... */ ++ u32 status; ++ ++ /* WSM_PSM_... */ ++ u8 psm; ++ ++ /* Number of channels that the scan operation completed. */ ++ u8 numChannels; ++#ifdef ROAM_OFFLOAD ++ u16 reserved; ++#endif /*ROAM_OFFLOAD*/ ++}; ++ ++typedef void (*wsm_scan_complete_cb) (struct xradio_common *hw_priv, ++ struct wsm_scan_complete *arg); ++ ++/* 3.9 */ ++struct wsm_scan { ++ /* WSM_PHY_BAND_... */ ++ /* [in] */ u8 band; ++ ++ /* WSM_SCAN_TYPE_... */ ++ /* [in] */ u8 scanType; ++ ++ /* WSM_SCAN_FLAG_... */ ++ /* [in] */ u8 scanFlags; ++ ++ /* WSM_TRANSMIT_RATE_... */ ++ /* [in] */ u8 maxTransmitRate; ++ ++ /* Interval period in TUs that the device shall the re- */ ++ /* execute the requested scan. Max value supported by the device */ ++ /* is 256s. */ ++ /* [in] */ u32 autoScanInterval; ++ ++ /* Number of probe requests (per SSID) sent to one (1) */ ++ /* channel. Zero (0) means that none is send, which */ ++ /* means that a passive scan is to be done. Value */ ++ /* greater than zero (0) means that an active scan is to */ ++ /* be done. */ ++ /* [in] */ u32 numOfProbeRequests; ++ ++ /* Number of channels to be scanned. */ ++ /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ ++ /* [in] */ u8 numOfChannels; ++ ++ /* Number of SSID provided in the scan command (this */ ++ /* is zero (0) in broadcast scan) */ ++ /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ ++ /* [in] */ u8 numOfSSIDs; ++ ++ /* The delay time (in microseconds) period */ ++ /* before sending a probe-request. */ ++ /* [in] */ u8 probeDelay; ++ ++ /* SSIDs to be scanned [numOfSSIDs]; */ ++ /* [in] */ struct wsm_ssid *ssids; ++ ++ /* Channels to be scanned [numOfChannels]; */ ++ /* [in] */ struct wsm_scan_ch *ch; ++}; ++ ++int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg, ++ int if_id); ++ ++/* 3.11 */ ++int wsm_stop_scan(struct xradio_common *hw_priv, int if_id); ++ ++/* 3.14 */ ++struct wsm_tx_confirm { ++ /* Packet identifier used in wsm_tx. */ ++ /* [out] */ u32 packetID; ++ ++ /* WSM_STATUS_... */ ++ /* [out] */ u32 status; ++ ++ /* WSM_TRANSMIT_RATE_... */ ++ /* [out] */ u8 txedRate; ++ ++ /* The number of times the frame was transmitted */ ++ /* without receiving an acknowledgement. */ ++ /* [out] */ u8 ackFailures; ++ ++ /* WSM_TX_STATUS_... */ ++ /* [out] */ u16 flags; ++ ++ //rate feed back, add by yangfh ++ /* [out] */ u32 rate_try[3]; ++ ++ /* The total time in microseconds that the frame spent in */ ++ /* the WLAN device before transmission as completed. */ ++ /* [out] */ u32 mediaDelay; ++ ++ /* The total time in microseconds that the frame spent in */ ++ /* the WLAN device before transmission was started. */ ++ /* [out] */ u32 txQueueDelay; ++ ++ /* [out]*/ u32 link_id; ++ ++ /*[out]*/ int if_id; ++}; ++ ++/* 3.15 */ ++typedef void (*wsm_tx_confirm_cb) (struct xradio_common *hw_priv, ++ struct wsm_tx_confirm *arg); ++ ++/* Note that ideology of wsm_tx struct is different against the rest of ++ * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input ++ * argument for WSM call, but a prepared bytestream to be sent to firmware. ++ * It is filled partly in xradio_tx, partly in low-level WSM code. ++ * Please pay attention once again: ideology is different. ++ * ++ * Legend: ++ * - [in]: xradio_tx must fill this field. ++ * - [wsm]: the field is filled by low-level WSM. ++ */ ++struct wsm_tx { ++ /* common WSM header */ ++ /* [in/wsm] */ struct wsm_hdr hdr; ++ ++ /* Packet identifier that meant to be used in completion. */ ++ /* [in] */ __le32 packetID; ++ ++ /* WSM_TRANSMIT_RATE_... */ ++ /* [in] */ u8 maxTxRate; ++ ++ /* WSM_QUEUE_... */ ++ /* [in] */ u8 queueId; ++ ++ /* True: another packet is pending on the host for transmission. */ ++ /* [wsm] */ u8 more; ++ ++ /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ ++ /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ ++ /* Bits 3:1 - PTA Priority */ ++ /* Bits 6:4 - Tx Rate Retry Policy */ ++ /* Bit 7 - Reserved */ ++ /* [in] */ u8 flags; ++ ++ /* Should be 0. */ ++ /* [in] */ __le32 reserved; ++ ++ /* The elapsed time in TUs, after the initial transmission */ ++ /* of an MSDU, after which further attempts to transmit */ ++ /* the MSDU shall be terminated. Overrides the global */ ++ /* dot11MaxTransmitMsduLifeTime setting [optional] */ ++ /* Device will set the default value if this is 0. */ ++ /* [wsm] */ __le32 expireTime; ++ ++ /* WSM_HT_TX_... */ ++ /* [in] */ __le32 htTxParameters; ++}; ++ ++/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ ++#define WSM_TX_EXTRA_HEADROOM (28) ++ ++/* 3.16 */ ++struct wsm_rx { ++ /* WSM_STATUS_... */ ++ /* [out] */ u32 status; ++ ++ /* Specifies the channel of the received packet. */ ++ /* [out] */ u16 channelNumber; ++ ++ /* WSM_TRANSMIT_RATE_... */ ++ /* [out] */ u8 rxedRate; ++ ++ /* This value is expressed in signed Q8.0 format for */ ++ /* RSSI and unsigned Q7.1 format for RCPI. */ ++ /* [out] */ u8 rcpiRssi; ++ ++ /* WSM_RX_STATUS_... */ ++ /* [out] */ u32 flags; ++ ++ /* An 802.11 frame. */ ++ /* [out] */ void *frame; ++ ++ /* Size of the frame */ ++ /* [out] */ size_t frame_size; ++ ++ /* Link ID */ ++ /* [out] */ int link_id; ++ /* [out] */ int if_id; ++}; ++ ++/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ ++#define WSM_RX_EXTRA_HEADROOM (16) ++ ++typedef void (*wsm_rx_cb) (struct xradio_vif *priv, struct wsm_rx *arg, ++ struct sk_buff **skb_p); ++ ++/* 3.17 */ ++struct wsm_event { ++ /* WSM_STATUS_... */ ++ /* [out] */ u32 eventId; ++ ++ /* Indication parameters. */ ++ /* For error indication, this shall be a 32-bit WSM status. */ ++ /* For RCPI or RSSI indication, this should be an 8-bit */ ++ /* RCPI or RSSI value. */ ++ /* [out] */ u32 eventData; ++}; ++ ++struct xradio_wsm_event { ++ struct list_head link; ++ struct wsm_event evt; ++ u8 if_id; ++}; ++ ++/* 3.18 - 3.22 */ ++/* Measurement. Skipped for now. Irrelevent. */ ++ ++typedef void (*wsm_event_cb) (struct xradio_common *hw_priv, ++ struct wsm_event *arg); ++ ++/* 3.23 */ ++struct wsm_join { ++ /* WSM_JOIN_MODE_... */ ++ /* [in] */ u8 mode; ++ ++ /* WSM_PHY_BAND_... */ ++ /* [in] */ u8 band; ++ ++ /* Specifies the channel number to join. The channel */ ++ /* number will be mapped to an actual frequency */ ++ /* according to the band */ ++ /* [in] */ u16 channelNumber; ++ ++ /* Specifies the BSSID of the BSS or IBSS to be joined */ ++ /* or the IBSS to be started. */ ++ /* [in] */ u8 bssid[6]; ++ ++ /* ATIM window of IBSS */ ++ /* When ATIM window is zero the initiated IBSS does */ ++ /* not support power saving. */ ++ /* [in] */ u16 atimWindow; ++ ++ /* WSM_JOIN_PREAMBLE_... */ ++ /* [in] */ u8 preambleType; ++ ++ /* Specifies if a probe request should be send with the */ ++ /* specified SSID when joining to the network. */ ++ /* [in] */ u8 probeForJoin; ++ ++ /* DTIM Period (In multiples of beacon interval) */ ++ /* [in] */ u8 dtimPeriod; ++ ++ /* WSM_JOIN_FLAGS_... */ ++ /* [in] */ u8 flags; ++ ++ /* Length of the SSID */ ++ /* [in] */ u32 ssidLength; ++ ++ /* Specifies the SSID of the IBSS to join or start */ ++ /* [in] */ u8 ssid[32]; ++ ++ /* Specifies the time between TBTTs in TUs */ ++ /* [in] */ u32 beaconInterval; ++ ++ /* A bit mask that defines the BSS basic rate set. */ ++ /* [in] */ u32 basicRateSet; ++ ++ /* Minimum transmission power level in units of 0.1dBm */ ++ /* [out] */ int minPowerLevel; ++ ++ /* Maximum transmission power level in units of 0.1dBm */ ++ /* [out] */ int maxPowerLevel; ++}; ++ ++int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg, int if_id); ++ ++/* 3.25 */ ++struct wsm_set_pm { ++ /* WSM_PSM_... */ ++ /* [in] */ u8 pmMode; ++ ++ /* in unit of 500us; 0 to use default */ ++ /* [in] */ u8 fastPsmIdlePeriod; ++ ++ /* in unit of 500us; 0 to use default */ ++ /* [in] */ u8 apPsmChangePeriod; ++ ++ /* in unit of 500us; 0 to disable auto-pspoll */ ++ /* [in] */ u8 minAutoPsPollPeriod; ++}; ++ ++int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg, ++ int if_id); ++ ++/* 3.27 */ ++struct wsm_set_pm_complete { ++ u8 psm; /* WSM_PSM_... */ ++}; ++ ++typedef void (*wsm_set_pm_complete_cb) (struct xradio_common *hw_priv, ++ struct wsm_set_pm_complete *arg); ++ ++/* 3.28 */ ++struct wsm_set_bss_params { ++ /* The number of lost consecutive beacons after which */ ++ /* the WLAN device should indicate the BSS-Lost event */ ++ /* to the WLAN host driver. */ ++ u8 beaconLostCount; ++ ++ /* The AID received during the association process. */ ++ u16 aid; ++ ++ /* The operational rate set mask */ ++ u32 operationalRateSet; ++}; ++ ++int wsm_set_bss_params(struct xradio_common *hw_priv, ++ const struct wsm_set_bss_params *arg, int if_id); ++ ++/* 3.30 */ ++struct wsm_add_key { ++ u8 type; /* WSM_KEY_TYPE_... */ ++ u8 entryIndex; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ ++ u16 reserved; ++ union { ++ struct { ++ u8 peerAddress[6]; /* MAC address of the ++ * peer station */ ++ u8 reserved; ++ u8 keyLength; /* Key length in bytes */ ++ u8 keyData[16]; /* Key data */ ++ } __packed wepPairwiseKey; ++ struct { ++ u8 keyId; /* Unique per key identifier ++ * (0..3) */ ++ u8 keyLength; /* Key length in bytes */ ++ u16 reserved; ++ u8 keyData[16]; /* Key data */ ++ } __packed wepGroupKey; ++ struct { ++ u8 peerAddress[6]; /* MAC address of the ++ * peer station */ ++ u8 reserved[2]; ++ u8 tkipKeyData[16]; /* TKIP key data */ ++ u8 rxMicKey[8]; /* Rx MIC key */ ++ u8 txMicKey[8]; /* Tx MIC key */ ++ } __packed tkipPairwiseKey; ++ struct { ++ u8 tkipKeyData[16]; /* TKIP key data */ ++ u8 rxMicKey[8]; /* Rx MIC key */ ++ u8 keyId; /* Key ID */ ++ u8 reserved[3]; ++ u8 rxSeqCounter[8]; /* Receive Sequence Counter */ ++ } __packed tkipGroupKey; ++ struct { ++ u8 peerAddress[6]; /* MAC address of the ++ * peer station */ ++ u16 reserved; ++ u8 aesKeyData[16]; /* AES key data */ ++ } __packed aesPairwiseKey; ++ struct { ++ u8 aesKeyData[16]; /* AES key data */ ++ u8 keyId; /* Key ID */ ++ u8 reserved[3]; ++ u8 rxSeqCounter[8]; /* Receive Sequence Counter */ ++ } __packed aesGroupKey; ++ struct { ++ u8 peerAddress[6]; /* MAC address of the ++ * peer station */ ++ u8 keyId; /* Key ID */ ++ u8 reserved; ++ u8 wapiKeyData[16]; /* WAPI key data */ ++ u8 micKeyData[16]; /* MIC key data */ ++ } __packed wapiPairwiseKey; ++ struct { ++ u8 wapiKeyData[16]; /* WAPI key data */ ++ u8 micKeyData[16]; /* MIC key data */ ++ u8 keyId; /* Key ID */ ++ u8 reserved[3]; ++ } __packed wapiGroupKey; ++ } __packed; ++} __packed; ++ ++int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg, ++ int if_id); ++ ++/* 3.32 */ ++struct wsm_remove_key { ++ /* Key entry index : 0-10 */ ++ u8 entryIndex; ++}; ++ ++int wsm_remove_key(struct xradio_common *hw_priv, ++ const struct wsm_remove_key *arg, int if_id); ++ ++/* 3.34 */ ++struct wsm_set_tx_queue_params { ++ /* WSM_ACK_POLICY_... */ ++ u8 ackPolicy; ++ ++ /* Medium Time of TSPEC (in 32us units) allowed per */ ++ /* One Second Averaging Period for this queue. */ ++ u16 allowedMediumTime; ++ ++ /* dot11MaxTransmitMsduLifetime to be used for the */ ++ /* specified queue. */ ++ u32 maxTransmitLifetime; ++}; ++ ++struct wsm_tx_queue_params { ++ /* NOTE: index is a linux queue id. */ ++ struct wsm_set_tx_queue_params params[4]; ++}; ++ ++#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time, \ ++ max_life_time) \ ++do { \ ++ struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ ++ p->ackPolicy = (ack_policy); \ ++ p->allowedMediumTime = (allowed_time); \ ++ p->maxTransmitLifetime = (max_life_time); \ ++} while (0) ++ ++int wsm_set_tx_queue_params(struct xradio_common *hw_priv, ++ const struct wsm_set_tx_queue_params *arg, ++ u8 id, int if_id); ++ ++/* 3.36 */ ++struct wsm_edca_queue_params { ++ /* CWmin (in slots) for the access class. */ ++ /* [in] */ u16 cwMin; ++ ++ /* CWmax (in slots) for the access class. */ ++ /* [in] */ u16 cwMax; ++ ++ /* AIFS (in slots) for the access class. */ ++ /* [in] */ u8 aifns; ++ ++ /* TX OP Limit (in microseconds) for the access class. */ ++ /* [in] */ u16 txOpLimit; ++ ++ /* dot11MaxReceiveLifetime to be used for the specified */ ++ /* the access class. Overrides the global */ ++ /* dot11MaxReceiveLifetime value */ ++ /* [in] */ u32 maxReceiveLifetime; ++ ++ /* UAPSD trigger support for the access class. */ ++ /* [in] */ bool uapsdEnable; ++}; ++ ++struct wsm_edca_params { ++ /* NOTE: index is a linux queue id. */ ++ struct wsm_edca_queue_params params[4]; ++}; ++ ++#define TXOP_UNIT 32 ++#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, life_time,\ ++ uapsd) \ ++ do { \ ++ struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ ++ p->cwMin = (cw_min); \ ++ p->cwMax = (cw_max); \ ++ p->aifns = (aifs); \ ++ p->txOpLimit = ((txop) * TXOP_UNIT); \ ++ p->maxReceiveLifetime = (life_time); \ ++ p->uapsdEnable = (uapsd); \ ++ } while (0) ++ ++int wsm_set_edca_params(struct xradio_common *hw_priv, ++ const struct wsm_edca_params *arg, int if_id); ++ ++int wsm_set_uapsd_param(struct xradio_common *hw_priv, ++ const struct wsm_edca_params *arg); ++ ++/* 3.38 */ ++/* Set-System info. Skipped for now. Irrelevent. */ ++ ++/* 3.40 */ ++struct wsm_switch_channel { ++ /* 1 - means the STA shall not transmit any further */ ++ /* frames until the channel switch has completed */ ++ /* [in] */ u8 channelMode; ++ ++ /* Number of TBTTs until channel switch occurs. */ ++ /* 0 - indicates switch shall occur at any time */ ++ /* 1 - occurs immediately before the next TBTT */ ++ /* [in] */ u8 channelSwitchCount; ++ ++ /* The new channel number to switch to. */ ++ /* Note this is defined as per section 2.7. */ ++ /* [in] */ u16 newChannelNumber; ++}; ++ ++int wsm_switch_channel(struct xradio_common *hw_priv, ++ const struct wsm_switch_channel *arg, int if_id); ++ ++typedef void (*wsm_channel_switch_cb) (struct xradio_common *hw_priv); ++ ++struct wsm_start { ++ /* WSM_START_MODE_... */ ++ /* [in] */ u8 mode; ++ ++ /* WSM_PHY_BAND_... */ ++ /* [in] */ u8 band; ++ ++ /* Channel number */ ++ /* [in] */ u16 channelNumber; ++ ++ /* Client Traffic window in units of TU */ ++ /* Valid only when mode == ..._P2P */ ++ /* [in] */ u32 CTWindow; ++ ++ /* Interval between two consecutive */ ++ /* beacon transmissions in TU. */ ++ /* [in] */ u32 beaconInterval; ++ ++ /* DTIM period in terms of beacon intervals */ ++ /* [in] */ u8 DTIMPeriod; ++ ++ /* WSM_JOIN_PREAMBLE_... */ ++ /* [in] */ u8 preambleType; ++ ++ /* The delay time (in microseconds) period */ ++ /* before sending a probe-request. */ ++ /* [in] */ u8 probeDelay; ++ ++ /* Length of the SSID */ ++ /* [in] */ u8 ssidLength; ++ ++ /* SSID of the BSS or P2P_GO to be started now. */ ++ /* [in] */ u8 ssid[32]; ++ ++ /* The basic supported rates for the MiniAP. */ ++ /* [in] */ u32 basicRateSet; ++}; ++ ++int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg, ++ int if_id); ++ ++#if 0 ++struct wsm_beacon_transmit { ++ /* 1: enable; 0: disable */ ++ /* [in] */ u8 enableBeaconing; ++}; ++ ++int wsm_beacon_transmit(struct xradio_common *hw_priv, ++ const struct wsm_beacon_transmit *arg, ++ int if_id); ++#endif ++ ++int wsm_start_find(struct xradio_common *hw_priv, int if_id); ++ ++int wsm_stop_find(struct xradio_common *hw_priv, int if_id); ++ ++typedef void (*wsm_find_complete_cb) (struct xradio_common *hw_priv, ++ u32 status); ++ ++struct wsm_suspend_resume { ++ /* See 3.52 */ ++ /* Link ID */ ++ /* [out] */ int link_id; ++ /* Stop sending further Tx requests down to device for this link */ ++ /* [out] */ bool stop; ++ /* Transmit multicast Frames */ ++ /* [out] */ bool multicast; ++ /* The AC on which Tx to be suspended /resumed. */ ++ /* This is applicable only for U-APSD */ ++ /* WSM_QUEUE_... */ ++ /* [out] */ int queue; ++ /* [out] */ int if_id; ++}; ++ ++typedef void (*wsm_suspend_resume_cb) (struct xradio_vif *priv, ++ struct wsm_suspend_resume *arg); ++ ++/* 3.54 Update-IE request. */ ++struct wsm_update_ie { ++ /* WSM_UPDATE_IE_... */ ++ /* [in] */ u16 what; ++ /* [in] */ u16 count; ++ /* [in] */ u8 *ies; ++ /* [in] */ size_t length; ++}; ++ ++int wsm_update_ie(struct xradio_common *hw_priv, ++ const struct wsm_update_ie *arg, int if_id); ++ ++/* 3.56 */ ++struct wsm_map_link { ++ /* MAC address of the remote device */ ++ /* [in] */ u8 mac_addr[6]; ++ /* [in] */ u8 unmap; ++ /* [in] */ u8 link_id; ++}; ++ ++int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg, ++ int if_id); ++ ++struct wsm_cbc { ++ wsm_scan_complete_cb scan_complete; ++ wsm_tx_confirm_cb tx_confirm; ++ wsm_event_cb event; ++ wsm_set_pm_complete_cb set_pm_complete; ++ wsm_channel_switch_cb channel_switch; ++ wsm_find_complete_cb find_complete; ++ wsm_suspend_resume_cb suspend_resume; ++}; ++#ifdef MCAST_FWDING ++ ++/* 3.65 Give Buffer Request */ ++int wsm_init_release_buffer_request(struct xradio_common *priv, u8 index); ++ ++/* 3.65 fixed memory leakage by yangfh*/ ++int wsm_deinit_release_buffer(struct xradio_common *hw_priv); ++ ++/* 3.67 Request Buffer Request */ ++int wsm_request_buffer_request(struct xradio_vif *priv, ++ u8 *arg); ++#endif ++/* ******************************************************************** */ ++/* MIB shortcats */ ++#define XR_RRM 1 ++#ifdef XR_RRM//RadioResourceMeasurement ++/* RadioResourceMeasurement Request*/ ++#define MEAS_CCA 0 ++#define MEAS_CHANNELLOAD 1 ++typedef struct LMAC_MEAS_CHANNEL_LOAD_PARAMS_S ++{ ++ u8 Reserved; ++ u8 ChannelLoadCCA; ++ u16 ChannelNum; ++ u16 RandomInterval; ++ u16 MeasurementDuration; ++ u32 MeasurementStartTimel; ++ u32 MeasurementStartTimeh; ++}LMAC_MEAS_CHANNEL_LOAD_PARAMS; ++ ++#define MEAS_RPI 0 ++#define MEAS_IPI 1 ++ ++typedef struct LMAC_MEAS_NOISE_HISTOGRAM_PARAMS_S ++{ ++ u8 Reserved; ++ u8 IpiRpi; ++ u16 ChannelNum; ++ u16 RandomInterval; ++ u16 MeasurementDuration; ++ u32 MeasurementStartTimel; ++ u32 MeasurementStartTimeh; ++}LMAC_MEAS_NOISE_HISTOGRAM_PARAMS; ++ ++#define LMAC_MAX_SSIDS 16 ++#define LMAC_MAX_SSID_LENGTH 32 ++typedef struct LMAC_CHANNELS_S ++{ ++ u32 ChannelNum; ++ u32 MinChannelTime; ++ u32 MaxChannelTime; ++ s32 TxPowerLevel; ++}LMAC_CHANNELS; ++ ++typedef struct LMAC_SSIDS_S ++{ ++ u32 SSIDLength; ++ u8 SSID[LMAC_MAX_SSID_LENGTH]; ++}LMAC_SSIDS; ++ ++typedef struct LMAC_MEAS_BEACON_PARAMS_S ++{ ++ //u8 RegulatoryClass; ++ //u8 MeasurementMode; ++ //u16 ChannelNum; ++ u16 RandomInterval; ++ //u16 MeasurementDuration; ++ //u8 Bssid[6]; ++ u16 Reserved; ++ //SCAN_PARAMETERS ScanParameters; ++ u8 Band; ++ u8 ScanType; ++ u8 ScanFlags; ++ u8 MaxTransmitRate; ++ u32 AutoScanInterval; ++ u8 NumOfProbeRequests; ++ u8 NumOfChannels; ++ u8 NumOfSSIDs; ++ u8 ProbeDelay; ++ LMAC_CHANNELS Channels; ++ LMAC_SSIDS Ssids; // here for SCAN_PARAMETER sizing purposes ++}LMAC_MEAS_BEACON_PARAMS; ++ ++typedef struct LMAC_MEAS_STA_STATS_PARAMS_S ++{ ++ u8 PeerMacAddress[6]; ++ u16 RandomInterval; ++ u16 MeasurementDuration; ++ u8 GroupId; ++ u8 Reserved; ++}LMAC_MEAS_STA_STATS_PARAMS; ++ ++typedef struct LMAC_MEAS_LINK_MEASUREMENT_PARAMS_S ++{ ++ u8 Reserved[4]; ++}LMAC_MEAS_LINK_MEASUREMENT_PARAMS; ++ ++typedef union LMAC_MEAS_REQUEST_U ++{ ++ LMAC_MEAS_CHANNEL_LOAD_PARAMS ChannelLoadParams; ++ LMAC_MEAS_NOISE_HISTOGRAM_PARAMS NoisHistogramParams; ++ LMAC_MEAS_BEACON_PARAMS BeaconParams; ++ LMAC_MEAS_STA_STATS_PARAMS StaStatsParams; ++ LMAC_MEAS_LINK_MEASUREMENT_PARAMS LinkMeasurementParams; ++} LMAC_MEAS_REQUEST; ++ ++// This struct is a copy of WSM_HI_START_MEASUREMENT_REQ, except that MsgLen and MsgId is not included ++typedef struct MEASUREMENT_PARAMETERS_S ++{ ++// u16 MsgLen; ++// u16 MsgId; ++ s32 TxPowerLevel; ++ u8 DurationMandatory; ++ u8 MeasurementType; ++ u8 MeasurementRequestLength; ++ u8 Reserved[5]; ++ LMAC_MEAS_REQUEST MeasurementRequest; ++}MEASUREMENT_PARAMETERS; ++ ++/* RadioResourceMeasurement Result*/ ++ typedef struct LMAC_MEAS_CHANNEL_LOAD_RESULTS_S ++{ ++ u8 Reserved; ++ u8 ChannelLoadCCA; ++ u16 ChannelNum; ++ u32 ActualMeasurementStartTimel; ++ u32 ActualMeasurementStartTimeh; ++ u16 MeasurementDuration; ++ u8 CCAbusyFraction; ++ u8 ChannelLoad; ++}LMAC_MEAS_CHANNEL_LOAD_RESULTS; ++ ++typedef struct LMAC_MEAS_NOISE_HISTOGRAM_RESULTS_S ++{ ++ u16 Reserved; ++ u16 ChannelNum; ++ u32 ActualMeasurementStartTimel; ++ u32 ActualMeasurementStartTimeh; ++ u16 MeasurementDuration; ++ u8 AntennaID; ++ u8 IpiRpi; ++ u8 PI_0_Density; ++ u8 PI_1_Density; ++ u8 PI_2_Density; ++ u8 PI_3_Density; ++ u8 PI_4_Density; ++ u8 PI_5_Density; ++ u8 PI_6_Density; ++ u8 PI_7_Density; ++ u8 PI_8_Density; ++ u8 PI_9_Density; ++ u8 PI_10_Density; ++ u8 Reserved2; ++}LMAC_MEAS_NOISE_HISTOGRAM_RESULTS; ++ ++typedef struct LMAC_MEAS_BEACON_RESULTS_S ++{ ++ u16 MeasurementDuration; ++ u16 Reserved; ++ u32 StartTsfl; ++ u32 StartTsfh; ++ u32 Durationl; ++ u32 Durationh; ++ //SCAN_PARAMETERS ScanParameters; ++ u8 Band; ++ u8 ScanType; ++ u8 ScanFlags; ++ u8 MaxTransmitRate; ++ u32 AutoScanInterval; ++ u8 NumOfProbeRequests; ++ u8 NumOfChannels; ++ u8 NumOfSSIDs; ++ u8 ProbeDelay; ++ LMAC_CHANNELS Channels; ++ LMAC_SSIDS Ssids; ++}LMAC_MEAS_BEACON_RESULTS; ++ ++typedef struct LMAC_MEAS_STA_STATS_RESULTS_S ++{ ++ u16 MeasurementDuration; ++ u8 GroupId; ++ u8 StatisticsGroupDataLength; ++ u8 StatisticsGroupData[52]; ++}LMAC_MEAS_STA_STATS_RESULTS; ++ ++typedef struct LMAC_MEAS_LINK_MEASUREMENT_RESULTS_S ++{ ++ s16 TransmitPower; ++ u8 RxAntennaID; ++ u8 TxAntennaID; ++ s32 NoiseLeveldBm; ++ s8 LatestRssi; ++ u8 Reserved1; ++ u8 Reserved2; ++ u8 Reserved3; ++}LMAC_MEAS_LINK_MEASUREMENT_RESULTS; ++ ++typedef union LMAC_MEAS_REPORT_U ++{ ++ LMAC_MEAS_CHANNEL_LOAD_RESULTS ChannelLoadResults; ++ LMAC_MEAS_NOISE_HISTOGRAM_RESULTS NoiseHistogramResults; ++ LMAC_MEAS_BEACON_RESULTS BeaconResults; ++ LMAC_MEAS_STA_STATS_RESULTS StaStatsResults; ++ LMAC_MEAS_LINK_MEASUREMENT_RESULTS LinkMeasurementResults; ++}LMAC_MEAS_REPORT; ++ ++// Note: eMeasurementTypes MUST match the #define WSM_MEASURE_TYPE_XXX from wsm_api.h ++typedef enum { ++ ChannelLoadMeasurement=0, ++ NoiseHistrogramMeasurement, ++ BeaconReport, ++ STAstatisticsReport, ++ LinkMeasurement ++}eMeasurementTypes; ++ ++typedef struct MEASUREMENT_COMPLETE_S ++{ ++// u16 RandomInterval; ++// u16 Reserved0; ++ u8 Dot11PowerMgmtMode; // From here WSM_HI_MEASURE_CMPL_IND and MEASUREMENT_COMPLETE_S must be identical ++ u8 MeasurementType; ++ u16 MoreInd; // Set to 1 if more indications are to follow for this measurement, otherwise 0; ++ u32 Status; ++ u8 MeasurementReportLength; ++ u8 Reserved2[3]; ++ LMAC_MEAS_REPORT MeasurementReport; ++}MEASUREMENT_COMPLETE; // Note: must be 32 bit aligned ++ ++#endif ++int wsm_11k_measure_requset(struct xradio_common *hw_priv, ++ u8 measure_type, ++ u16 ChannelNum, ++ u16 Duration); ++ ++ ++static inline int wsm_set_fw_debug_control(struct xradio_common *hw_priv, ++ int debug_control, int if_id) ++{ ++ __le32 val = __cpu_to_le32(debug_control); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_FW_DEBUG_CONTROL, ++ &val, sizeof(val), if_id); ++} ++ ++static inline int wsm_set_host_sleep(struct xradio_common *hw_priv, ++ u8 host_sleep, int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_HOST_SLEEP, ++ &host_sleep, sizeof(host_sleep), if_id); ++} ++ ++static inline int wsm_set_output_power(struct xradio_common *hw_priv, ++ int power_level, int if_id) ++{ ++ __le32 val = __cpu_to_le32(power_level); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, ++ &val, sizeof(val), if_id); ++} ++ ++static inline int wsm_set_beacon_wakeup_period(struct xradio_common *hw_priv, ++ unsigned dtim_interval, ++ unsigned listen_interval, ++ int if_id) ++{ ++ struct { ++ u8 numBeaconPeriods; ++ u8 reserved; ++ __le16 listenInterval; ++ } val = { ++ dtim_interval, 0, __cpu_to_le16(listen_interval)}; ++ if (dtim_interval > 0xFF || listen_interval > 0xFFFF) ++ return -EINVAL; ++ else ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, ++ &val, sizeof(val), if_id); ++} ++ ++struct wsm_rcpi_rssi_threshold { ++ u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ ++ u8 lowerThreshold; ++ u8 upperThreshold; ++ u8 rollingAverageCount; ++}; ++ ++static inline int wsm_set_rcpi_rssi_threshold(struct xradio_common *hw_priv, ++ struct wsm_rcpi_rssi_threshold *arg, ++ int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, ++ sizeof(*arg), if_id); ++} ++ ++struct wsm_counters_table { ++ __le32 countPlcpErrors; ++ __le32 countFcsErrors; ++ __le32 countTxPackets; ++ __le32 countRxPackets; ++ __le32 countRxPacketErrors; ++ __le32 countRtsSuccess; ++ __le32 countRtsFailures; ++ __le32 countRxFramesSuccess; ++ __le32 countRxDecryptionFailures; ++ __le32 countRxMicFailures; ++ __le32 countRxNoKeyFailures; ++ __le32 countTxMulticastFrames; ++ __le32 countTxFramesSuccess; ++ __le32 countTxFrameFailures; ++ __le32 countTxFramesRetried; ++ __le32 countTxFramesMultiRetried; ++ __le32 countRxFrameDuplicates; ++ __le32 countAckFailures; ++ __le32 countRxMulticastFrames; ++ __le32 countRxCMACICVErrors; ++ __le32 countRxCMACReplays; ++ __le32 countRxMgmtCCMPReplays; ++ __le32 countRxBIPMICErrors; ++}; ++ ++ ++struct wsm_ampducounters_table { ++ u32 countTxAMPDUs; ++ u32 countTxMPDUsInAMPDUs; ++ u32 countTxOctetsInAMPDUs_l32; ++ u32 countTxOctetsInAMPDUs_h32; ++ u32 countRxAMPDUs; ++ u32 countRxMPDUsInAMPDUs; ++ u32 countRxOctetsInAMPDUs_l32; ++ u32 countRxOctetsInAMPDUs_h32; ++ u32 countRxDelimeterCRCErrorCount; ++ u32 countImplictBARFailures; ++ u32 countExplictBARFailures; ++}; ++ ++struct wsm_txpipe_counter { ++ u32 count1; ++ u32 count2; ++ u32 count3; ++ u32 count4; ++ u32 count5; ++ u32 count6; ++ u32 count7; ++ u32 count8; ++ u32 count9; ++ u32 counta; ++}; ++ ++struct wsm_backoff_counter { ++ u32 count0; ++ u32 count1; ++ u32 count2; ++ u32 count3; ++ u32 count4; ++ u32 count5; ++ u32 count6; ++ u32 count7; ++ u32 count8; ++ u32 count9; ++}; ++//add by yangfh for read/write fw registers ++#define WSM_REG_RW_F BIT(0) //0:read, 1:write ++#define WSM_REG_RET_F BIT(1) //results is valid. ++#define WSM_REG_BK_F BIT(4) //operate in block mode. ++ ++struct reg_data { ++ u32 reg_addr; ++ u32 reg_val; ++}; ++ ++typedef struct tag_wsm_reg_w { ++ u16 flag; ++ u16 data_size; ++ struct reg_data arg[16]; ++} WSM_REG_W; ++ ++typedef struct tag_wsm_reg_r { ++ u16 flag; ++ u16 data_size; ++ u32 arg[16]; ++} WSM_REG_R; ++ ++struct wsm_backoff_ctrl { ++ u32 enable; ++ u32 min; ++ u32 max; ++}; ++struct wsm_tala_para { ++ u32 para; ++ u32 thresh; ++}; ++static inline int wsm_get_counters_table(struct xradio_common *hw_priv, ++ struct wsm_counters_table *arg) ++{ ++ return wsm_read_mib(hw_priv, WSM_MIB_ID_COUNTERS_TABLE, ++ arg, sizeof(*arg), 0); ++} ++ ++static inline int wsm_get_ampducounters_table(struct xradio_common *hw_priv, ++ struct wsm_ampducounters_table *arg) ++{ ++ return wsm_read_mib(hw_priv, WSM_MIB_ID_AMPDUCOUNTERS_TABLE, ++ arg, sizeof(*arg), 0); ++} ++ ++static inline int wsm_get_txpipe_table(struct xradio_common *hw_priv, ++ struct wsm_txpipe_counter *arg) ++{ ++ return wsm_read_mib(hw_priv, WSM_MIB_ID_TXPIPE_TABLE, ++ arg, sizeof(*arg), 0); ++} ++ ++static inline int wsm_get_backoff_dbg(struct xradio_common *hw_priv, ++ struct wsm_backoff_counter *arg) ++{ ++ return wsm_read_mib(hw_priv, WSM_MIB_ID_BACKOFF_DBG, ++ arg, sizeof(*arg), 0); ++} ++ ++static inline int wsm_set_backoff_ctrl(struct xradio_common *hw_priv, ++ struct wsm_backoff_ctrl *arg) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_BACKOFF_CTRL, ++ arg, sizeof(*arg), 0); ++} ++ ++static inline int wsm_set_tala(struct xradio_common *hw_priv, ++ struct wsm_tala_para *arg) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TALA_PARA, ++ arg, sizeof(*arg), 0); ++} ++static inline int wsm_get_station_id(struct xradio_common *hw_priv, u8 *mac) ++{ ++ return wsm_read_mib(hw_priv, WSM_MIB_ID_DOT11_STATION_ID, mac, ++ ETH_ALEN, 0); ++} ++ ++struct wsm_rx_filter { ++ bool promiscuous; ++ bool bssid; ++ bool fcs; ++ bool probeResponder; ++ bool keepalive; ++}; ++ ++static inline int wsm_set_rx_filter(struct xradio_common *hw_priv, ++ const struct wsm_rx_filter *arg, ++ int if_id) ++{ ++ __le32 val = 0; ++ if (arg->promiscuous) ++ val |= __cpu_to_le32(BIT(0)); ++ if (arg->bssid) ++ val |= __cpu_to_le32(BIT(1)); ++ if (arg->fcs) ++ val |= __cpu_to_le32(BIT(2)); ++ if (arg->probeResponder) ++ val |= __cpu_to_le32(BIT(3)); ++ if (arg->keepalive) ++ val |= __cpu_to_le32(BIT(4)); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val), ++ if_id); ++} ++ ++int wsm_set_probe_responder(struct xradio_vif *priv, bool enable); ++int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable); ++ ++#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) ++#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) ++#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) ++ ++struct wsm_beacon_filter_table_entry { ++ u8 ieId; ++ u8 actionFlags; ++ u8 oui[3]; ++ u8 matchData[3]; ++} __packed; ++ ++struct wsm_beacon_filter_table { ++ __le32 numOfIEs; ++ struct wsm_beacon_filter_table_entry entry[10]; ++} __packed; ++ ++static inline int wsm_set_beacon_filter_table(struct xradio_common *hw_priv, ++ struct wsm_beacon_filter_table *ft, ++ int if_id) ++{ ++ size_t size = __le32_to_cpu(ft->numOfIEs) * ++ sizeof(struct wsm_beacon_filter_table_entry) + ++ sizeof(__le32); ++ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size, ++ if_id); ++} ++ ++#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ ++#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ ++ ++struct wsm_beacon_filter_control { ++ int enabled; ++ int bcn_count; ++}; ++ ++static inline int wsm_beacon_filter_control(struct xradio_common *hw_priv, ++ struct wsm_beacon_filter_control *arg, ++ int if_id) ++{ ++ struct { ++ __le32 enabled; ++ __le32 bcn_count; ++ } val; ++ val.enabled = __cpu_to_le32(arg->enabled); ++ val.bcn_count = __cpu_to_le32(arg->bcn_count); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, ++ sizeof(val), if_id); ++} ++ ++enum wsm_power_mode { ++ wsm_power_mode_active = 0, ++ wsm_power_mode_doze = 1, ++ wsm_power_mode_quiescent = 2, ++}; ++ ++struct wsm_operational_mode { ++ enum wsm_power_mode power_mode; ++ int disableMoreFlagUsage; ++ int performAntDiversity; ++}; ++ ++#ifdef CONFIG_XRADIO_DEBUGFS ++extern u8 low_pwr_disable; ++#endif ++ ++static inline int wsm_set_operational_mode(struct xradio_common *hw_priv, ++ const struct wsm_operational_mode *arg, ++ int if_id) ++{ ++ u32 val = arg->power_mode; ++ ++#ifdef CONFIG_XRADIO_DEBUGFS //add by yangfh for disable low_power mode. ++ if(low_pwr_disable) ++ val = wsm_power_mode_active; ++#endif ++ ++ if (arg->disableMoreFlagUsage) ++ val |= BIT(4); ++ if (arg->performAntDiversity) ++ val |= BIT(5); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, ++ sizeof(val), if_id); ++} ++ ++struct wsm_inactivity { ++ u8 max_inactivity; ++ u8 min_inactivity; ++}; ++ ++static inline int wsm_set_inactivity(struct xradio_common *hw_priv, ++ const struct wsm_inactivity *arg, ++ int if_id) ++{ ++ struct { ++ u8 min_inactive; ++ u8 max_inactive; ++ u16 reserved; ++ } val; ++ ++ val.max_inactive = arg->max_inactivity; ++ val.min_inactive = arg->min_inactivity; ++ val.reserved = 0; ++ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_INACTIVITY, &val, ++ sizeof(val), if_id); ++} ++ ++struct wsm_template_frame { ++ u8 frame_type; ++ u8 rate; ++ bool disable; ++ struct sk_buff *skb; ++}; ++ ++static inline int wsm_set_template_frame(struct xradio_common *hw_priv, ++ struct wsm_template_frame *arg, ++ int if_id) ++{ ++ int ret; ++ u8 *p = skb_push(arg->skb, 4); ++ p[0] = arg->frame_type; ++ p[1] = arg->rate; ++ if (arg->disable) ++ ((u16 *) p)[1] = 0; ++ else ++ ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); ++ ret = wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, p, ++ arg->skb->len, if_id); ++ skb_pull(arg->skb, 4); ++ return ret; ++} ++ ++ ++struct wsm_protected_mgmt_policy { ++ bool protectedMgmtEnable; ++ bool unprotectedMgmtFramesAllowed; ++ bool encryptionForAuthFrame; ++}; ++ ++static inline int ++wsm_set_protected_mgmt_policy(struct xradio_common *hw_priv, ++ struct wsm_protected_mgmt_policy *arg, ++ int if_id) ++{ ++ __le32 val = 0; ++ int ret; ++ if (arg->protectedMgmtEnable) ++ val |= __cpu_to_le32(BIT(0)); ++ if (arg->unprotectedMgmtFramesAllowed) ++ val |= __cpu_to_le32(BIT(1)); ++ if (arg->encryptionForAuthFrame) ++ val |= __cpu_to_le32(BIT(2)); ++ ret = wsm_write_mib(hw_priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, &val, ++ sizeof(val), if_id); ++ return ret; ++} ++ ++static inline int wsm_set_block_ack_policy(struct xradio_common *hw_priv, ++ u8 blockAckTxTidPolicy, ++ u8 blockAckRxTidPolicy, ++ int if_id) ++{ ++ struct { ++ u8 blockAckTxTidPolicy; ++ u8 reserved1; ++ u8 blockAckRxTidPolicy; ++ u8 reserved2; ++ } val = { ++ .blockAckTxTidPolicy = blockAckTxTidPolicy, ++ .blockAckRxTidPolicy = blockAckRxTidPolicy, ++ }; ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, ++ sizeof(val), if_id); ++} ++ ++struct wsm_association_mode { ++ u8 flags; /* WSM_ASSOCIATION_MODE_... */ ++ u8 preambleType; /* WSM_JOIN_PREAMBLE_... */ ++ u8 greenfieldMode; /* 1 for greenfield */ ++ u8 mpduStartSpacing; ++ __le32 basicRateSet; ++}; ++ ++static inline int wsm_set_association_mode(struct xradio_common *hw_priv, ++ struct wsm_association_mode *arg, ++ int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, ++ sizeof(*arg), if_id); ++} ++ ++struct wsm_set_tx_rate_retry_policy_header { ++ u8 numTxRatePolicies; ++ u8 reserved[3]; ++} __packed; ++ ++struct wsm_set_tx_rate_retry_policy_policy { ++ u8 policyIndex; ++ u8 shortRetryCount; ++ u8 longRetryCount; ++ u8 policyFlags; ++ u8 rateRecoveryCount; ++ u8 reserved[3]; ++ __le32 rateCountIndices[3]; ++} __packed; ++ ++struct wsm_set_tx_rate_retry_policy { ++ struct wsm_set_tx_rate_retry_policy_header hdr; ++ struct wsm_set_tx_rate_retry_policy_policy tbl[8]; ++} __packed; ++ ++static inline int wsm_set_tx_rate_retry_policy(struct xradio_common *hw_priv, ++ struct wsm_set_tx_rate_retry_policy *arg, ++ int if_id) ++{ ++ size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) + ++ arg->hdr.numTxRatePolicies * ++ sizeof(struct wsm_set_tx_rate_retry_policy_policy); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, ++ size, if_id); ++} ++ ++/* 4.32 SetEtherTypeDataFrameFilter */ ++struct wsm_ether_type_filter_hdr { ++ u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ ++ u8 reserved[3]; ++} __packed; ++ ++struct wsm_ether_type_filter { ++ u8 filterAction; /* WSM_FILTER_ACTION_XXX */ ++ u8 reserved; ++ __le16 etherType; /* Type of ethernet frame */ ++} __packed; ++ ++static inline int wsm_set_ether_type_filter(struct xradio_common *hw_priv, ++ struct wsm_ether_type_filter_hdr *arg, ++ int if_id) ++{ ++ size_t size = sizeof(struct wsm_ether_type_filter_hdr) + ++ arg->nrFilters * sizeof(struct wsm_ether_type_filter); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, ++ arg, size, if_id); ++} ++ ++ ++/* 4.33 SetUDPPortDataFrameFilter */ ++struct wsm_udp_port_filter_hdr { ++ u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ ++ u8 reserved[3]; ++} __packed; ++ ++struct wsm_udp_port_filter { ++ u8 filterAction; /* WSM_FILTER_ACTION_XXX */ ++ u8 portType; /* WSM_FILTER_PORT_TYPE_XXX */ ++ __le16 udpPort; /* Port number */ ++} __packed; ++ ++static inline int wsm_set_udp_port_filter(struct xradio_common *hw_priv, ++ struct wsm_udp_port_filter_hdr *arg, ++ int if_id) ++{ ++ size_t size = sizeof(struct wsm_udp_port_filter_hdr) + ++ arg->nrFilters * sizeof(struct wsm_udp_port_filter); ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, ++ arg, size, if_id); ++} ++ ++/* Undocumented MIBs: */ ++/* 4.35 P2PDeviceInfo */ ++#define D11_MAX_SSID_LEN (32) ++ ++struct wsm_p2p_device_type { ++ __le16 categoryId; ++ u8 oui[4]; ++ __le16 subCategoryId; ++} __packed; ++ ++struct wsm_p2p_device_info { ++ struct wsm_p2p_device_type primaryDevice; ++ u8 reserved1[3]; ++ u8 devNameSize; ++ u8 localDevName[D11_MAX_SSID_LEN]; ++ u8 reserved2[3]; ++ u8 numSecDevSupported; ++ struct wsm_p2p_device_type secondaryDevices[0]; ++} __packed; ++ ++/* 4.36 SetWCDMABand - WO */ ++struct wsm_cdma_band { ++ u8 WCDMA_Band; ++ u8 reserved[3]; ++} __packed; ++ ++/* 4.37 GroupTxSequenceCounter - RO */ ++struct wsm_group_tx_seq { ++ __le32 bits_47_16; ++ __le16 bits_15_00; ++ __le16 reserved; ++} __packed; ++ ++/* 4.39 SetHtProtection - WO */ ++#define WSM_DUAL_CTS_PROT_ENB (1 << 0) ++#define WSM_NON_GREENFIELD_STA PRESENT(1 << 1) ++#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) ++#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) ++#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) ++#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) ++#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) ++#define WSM_LARGE_L_LENGTH_PROT (1 << 5) ++ ++struct wsm_ht_protection { ++ __le32 flags; ++} __packed; ++ ++/* 4.40 GPIO Command - R/W */ ++#define WSM_GPIO_COMMAND_SETUP 0 ++#define WSM_GPIO_COMMAND_READ 1 ++#define WSM_GPIO_COMMAND_WRITE 2 ++#define WSM_GPIO_COMMAND_RESET 3 ++#define WSM_GPIO_ALL_PINS 0xFF ++ ++struct wsm_gpio_command { ++ u8 GPIO_Command; ++ u8 pin; ++ __le16 config; ++} __packed; ++ ++/* 4.41 TSFCounter - RO */ ++struct wsm_tsf_counter { ++ __le64 TSF_Counter; ++} __packed; ++ ++/* 4.43 Keep alive period */ ++struct wsm_keep_alive_period { ++ __le16 keepAlivePeriod; ++ u8 reserved[2]; ++} __packed; ++ ++static inline int wsm_keep_alive_period(struct xradio_common *hw_priv, ++ int period, int if_id) ++{ ++ struct wsm_keep_alive_period arg = { ++ .keepAlivePeriod = __cpu_to_le16(period), ++ }; ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, ++ &arg, sizeof(arg), if_id); ++}; ++ ++/* BSSID filtering */ ++struct wsm_set_bssid_filtering { ++ u8 filter; ++ u8 reserved[3]; ++} __packed; ++ ++static inline int wsm_set_bssid_filtering(struct xradio_common *hw_priv, ++ bool enabled, int if_id) ++{ ++ struct wsm_set_bssid_filtering arg = { ++ .filter = !enabled, ++ }; ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, ++ &arg, sizeof(arg), if_id); ++} ++ ++/* Multicat filtering - 4.5 */ ++struct wsm_multicast_filter { ++ __le32 enable; ++ __le32 numOfAddresses; ++ u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; ++} __packed; ++ ++/* Mac Addr Filter Info */ ++struct wsm_mac_addr_info { ++ u8 filter_mode; ++ u8 address_mode; ++ u8 MacAddr[6]; ++} __packed; ++ ++/* Mac Addr Filter */ ++struct wsm_mac_addr_filter { ++ u8 numfilter; ++ u8 action_mode; ++ u8 Reserved[2]; ++ struct wsm_mac_addr_info macaddrfilter[0]; ++} __packed; ++ ++/* Broadcast Addr Filter */ ++struct wsm_broadcast_addr_filter { ++ u8 action_mode; ++ u8 nummacaddr; ++ u8 filter_mode; ++ u8 address_mode; ++ u8 MacAddr[6]; ++} __packed; ++ ++static inline int wsm_set_multicast_filter(struct xradio_common *hw_priv, ++ struct wsm_multicast_filter *fp, ++ int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, ++ fp, sizeof(*fp), if_id); ++} ++ ++/* ARP IPv4 filtering - 4.10 */ ++struct wsm_arp_ipv4_filter { ++ __le32 enable; ++ __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; ++} __packed; ++ ++ ++static inline int wsm_set_arp_ipv4_filter(struct xradio_common *hw_priv, ++ struct wsm_arp_ipv4_filter *fp, ++ int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, ++ fp, sizeof(*fp), if_id); ++} ++ ++/* P2P Power Save Mode Info - 4.31 */ ++struct wsm_p2p_ps_modeinfo { ++ u8 oppPsCTWindow; ++ u8 count; ++ u8 reserved; ++ u8 dtimCount; ++ __le32 duration; ++ __le32 interval; ++ __le32 startTime; ++} __packed; ++ ++static inline int wsm_set_p2p_ps_modeinfo(struct xradio_common *hw_priv, ++ struct wsm_p2p_ps_modeinfo *mi, ++ int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO, ++ mi, sizeof(*mi), if_id); ++} ++ ++static inline int wsm_get_p2p_ps_modeinfo(struct xradio_common *hw_priv, ++ struct wsm_p2p_ps_modeinfo *mi) ++{ ++ return wsm_read_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO, ++ mi, sizeof(*mi), 0); ++} ++ ++/* UseMultiTxConfMessage */ ++ ++static inline int wsm_use_multi_tx_conf(struct xradio_common *hw_priv, ++ bool enabled, int if_id) ++{ ++ __le32 arg = enabled ? __cpu_to_le32(1) : 0; ++ ++ return wsm_write_mib(hw_priv, WSM_MIB_USE_MULTI_TX_CONF, ++ &arg, sizeof(arg), if_id); ++} ++ ++ ++/* 4.26 SetUpasdInformation */ ++struct wsm_uapsd_info { ++ __le16 uapsdFlags; ++ __le16 minAutoTriggerInterval; ++ __le16 maxAutoTriggerInterval; ++ __le16 autoTriggerStep; ++}; ++ ++static inline int wsm_set_uapsd_info(struct xradio_common *hw_priv, ++ struct wsm_uapsd_info *arg, ++ int if_id) ++{ ++ /* TODO:COMBO:UAPSD will be supported only on one interface */ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, ++ arg, sizeof(*arg), if_id); ++} ++ ++/* 4.22 OverrideInternalTxRate */ ++struct wsm_override_internal_txrate { ++ u8 internalTxRate; ++ u8 nonErpInternalTxRate; ++ u8 reserved[2]; ++} __packed; ++ ++static inline int ++wsm_set_override_internal_txrate(struct xradio_common *hw_priv, ++ struct wsm_override_internal_txrate *arg, ++ int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, ++ arg, sizeof(*arg), if_id); ++} ++#ifdef MCAST_FWDING ++/* 4.51 SetForwardingOffload */ ++struct wsm_forwarding_offload { ++ u8 fwenable; ++ u8 flags; ++ u8 reserved[2]; ++} __packed; ++ ++static inline int wsm_set_forwarding_offlad(struct xradio_common *hw_priv, ++ struct wsm_forwarding_offload *arg,int if_id) ++{ ++ return wsm_write_mib(hw_priv, WSM_MIB_ID_FORWARDING_OFFLOAD, ++ arg, sizeof(*arg),if_id); ++} ++ ++#endif ++/* ******************************************************************** */ ++/* WSM TX port control */ ++ ++void wsm_lock_tx(struct xradio_common *hw_priv); ++void wsm_vif_lock_tx(struct xradio_vif *priv); ++void wsm_lock_tx_async(struct xradio_common *hw_priv); ++bool wsm_flush_tx(struct xradio_common *hw_priv); ++bool wsm_vif_flush_tx(struct xradio_vif *priv); ++void wsm_unlock_tx(struct xradio_common *hw_priv); ++ ++/* ******************************************************************** */ ++/* WSM / BH API */ ++ ++int wsm_handle_exception(struct xradio_common *hw_priv, u8 * data, size_t len); ++int wsm_handle_rx(struct xradio_common *hw_priv, int id, struct wsm_hdr *wsm, ++ struct sk_buff **skb_p); ++void wms_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv); ++void wms_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv); ++ ++/* ******************************************************************** */ ++/* wsm_buf API */ ++ ++struct wsm_buf { ++ u8 *begin; ++ u8 *data; ++ u8 *end; ++}; ++ ++void wsm_buf_init(struct wsm_buf *buf); ++void wsm_buf_deinit(struct wsm_buf *buf); ++ ++/* ******************************************************************** */ ++/* wsm_cmd API */ ++ ++struct wsm_cmd { ++ spinlock_t lock; ++ int done; ++ u8 *ptr; ++ size_t len; ++ void *arg; ++ int ret; ++ u16 cmd; ++}; ++ ++/* ******************************************************************** */ ++/* WSM TX buffer access */ ++ ++int wsm_get_tx(struct xradio_common *hw_priv, u8 **data, ++ size_t *tx_len, int *burst, int *vif_selected); ++void wsm_txed(struct xradio_common *hw_priv, u8 *data); ++ ++/* ******************************************************************** */ ++/* Queue mapping: WSM <---> linux */ ++/* Linux: VO VI BE BK */ ++/* WSM: BE BK VI VO */ ++ ++static inline u8 wsm_queue_id_to_linux(u8 queueId) ++{ ++ static const u8 queue_mapping[] = { ++ 2, 3, 1, 0 ++ }; ++ return queue_mapping[queueId]; ++} ++ ++static inline u8 wsm_queue_id_to_wsm(u8 queueId) ++{ ++ static const u8 queue_mapping[] = { ++ 3, 2, 0, 1 ++ }; ++ return queue_mapping[queueId]; ++} ++ ++#endif /* XRADIO_HWIO_H_INCLUDED */ +diff --git a/drivers/net/wireless/xradio/xradio.h b/drivers/net/wireless/xradio/xradio.h +new file mode 100644 +index 00000000..45393582 +--- /dev/null ++++ b/drivers/net/wireless/xradio/xradio.h +@@ -0,0 +1,655 @@ ++/* ++ * Common define of private data for XRadio drivers ++ * ++ * Copyright (c) 2013, XRadio ++ * Author: XRadio ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef XRADIO_H ++#define XRADIO_H ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++//Macroses for Driver parameters. ++#define XRWL_MAX_QUEUE_SZ (128) ++#define AC_QUEUE_NUM 4 ++ ++#ifdef P2P_MULTIVIF ++#define XRWL_MAX_VIFS (3) ++#else ++#define XRWL_MAX_VIFS (2) ++#endif ++#define XRWL_GENERIC_IF_ID (2) ++#define XRWL_HOST_VIF0_11N_THROTTLE (58) //(XRWL_MAX_QUEUE_SZ/(XRWL_MAX_VIFS-1))*0.9 ++#define XRWL_HOST_VIF1_11N_THROTTLE (58) //(XRWL_MAX_QUEUE_SZ/(XRWL_MAX_VIFS-1))*0.9 ++#define XRWL_HOST_VIF0_11BG_THROTTLE (35) //XRWL_HOST_VIF0_11N_THROTTLE*0.6 = 35 ++#define XRWL_HOST_VIF1_11BG_THROTTLE (35) //XRWL_HOST_VIF0_11N_THROTTLE*0.6 = 35 ++#if 0 ++#define XRWL_FW_VIF0_THROTTLE (15) ++#define XRWL_FW_VIF1_THROTTLE (15) ++#endif ++ ++#define IEEE80211_FCTL_WEP 0x4000 ++#define IEEE80211_QOS_DATAGRP 0x0080 ++#define WSM_KEY_MAX_IDX 20 ++ ++#include "queue.h" ++#include "wsm.h" ++#include "scan.h" ++#include "txrx.h" ++#include "ht.h" ++#include "pm.h" ++#include "fwio.h" ++#ifdef CONFIG_XRADIO_TESTMODE ++#include "nl80211_testmode_msg_copy.h" ++#endif /*CONFIG_XRADIO_TESTMODE*/ ++ ++/* #define ROC_DEBUG */ ++/* hidden ssid is only supported when separate probe resp IE ++ configuration is supported */ ++#ifdef PROBE_RESP_EXTRA_IE ++#define HIDDEN_SSID 1 ++#endif ++ ++#define XRADIO_MAX_CTRL_FRAME_LEN (0x1000) ++ ++#define MAX_STA_IN_AP_MODE (14) ++#define WLAN_LINK_ID_MAX (MAX_STA_IN_AP_MODE + 3) ++ ++#define XRADIO_MAX_STA_IN_AP_MODE (5) ++#define XRADIO_MAX_REQUEUE_ATTEMPTS (5) ++#define XRADIO_LINK_ID_UNMAPPED (15) ++#define XRADIO_MAX_TID (8) ++ ++#define XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F) ++#define XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F) ++#define XRADIO_RX_BLOCK_ACK_ENABLED_FOR_BE_TID \ ++ (XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID & 0x01) ++#define XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0) ++#define XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0) ++ ++#define XRADIO_BLOCK_ACK_CNT (30) ++#define XRADIO_BLOCK_ACK_THLD (800) ++#define XRADIO_BLOCK_ACK_HIST (3) ++#define XRADIO_BLOCK_ACK_INTERVAL (1 * HZ / XRADIO_BLOCK_ACK_HIST) ++#define XRWL_ALL_IFS (-1) ++ ++#ifdef ROAM_OFFLOAD ++#define XRADIO_SCAN_TYPE_ACTIVE 0x1000 ++#define XRADIO_SCAN_BAND_5G 0x2000 ++#endif /*ROAM_OFFLOAD*/ ++ ++#define IEEE80211_FCTL_WEP 0x4000 ++#define IEEE80211_QOS_DATAGRP 0x0080 ++#ifdef CONFIG_XRADIO_TESTMODE ++#define XRADIO_SCAN_MEASUREMENT_PASSIVE (0) ++#define XRADIO_SCAN_MEASUREMENT_ACTIVE (1) ++#endif ++ ++#ifdef MCAST_FWDING ++#define WSM_MAX_BUF 30 ++#endif ++ ++#define MAX_RATES_STAGE 8 // ++#define MAX_RATES_RETRY 15 ++ ++#define XRADIO_WORKQUEUE "xradio_wq" ++#define WIFI_CONF_PATH "/data/xr_wifi.conf" ++ ++/* extern */ struct task_struct; ++/* extern */ struct xradio_debug_priv; ++/* extern */ struct xradio_debug_common; ++/* extern */ struct firmware; ++ ++/* Please keep order */ ++enum xradio_join_status { ++ XRADIO_JOIN_STATUS_PASSIVE = 0, ++ XRADIO_JOIN_STATUS_MONITOR, ++ XRADIO_JOIN_STATUS_STA, ++ XRADIO_JOIN_STATUS_AP, ++}; ++ ++enum xradio_link_status { ++ XRADIO_LINK_OFF, ++ XRADIO_LINK_RESERVE, ++ XRADIO_LINK_SOFT, ++ XRADIO_LINK_HARD, ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ XRADIO_LINK_RESET, ++ XRADIO_LINK_RESET_REMAP, ++#endif ++}; ++ ++enum xradio_bss_loss_status { ++ XRADIO_BSS_LOSS_NONE, ++ XRADIO_BSS_LOSS_CHECKING, ++ XRADIO_BSS_LOSS_CONFIRMING, ++ XRADIO_BSS_LOSS_CONFIRMED, ++}; ++ ++struct xradio_link_entry { ++ unsigned long timestamp; ++ enum xradio_link_status status; ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ enum xradio_link_status prev_status; ++#endif ++ u8 mac[ETH_ALEN]; ++ u8 buffered[XRADIO_MAX_TID]; ++ struct sk_buff_head rx_queue; ++}; ++ ++#if defined(ROAM_OFFLOAD) || defined(CONFIG_XRADIO_TESTMODE) ++struct xradio_testframe { ++ u8 len; ++ u8 *data; ++}; ++#endif ++#ifdef CONFIG_XRADIO_TESTMODE ++struct advance_scan_elems { ++ u8 scanMode; ++ u16 duration; ++}; ++/** ++ * xradio_tsm_info - Keeps information about ongoing TSM collection ++ * @ac: Access category for which metrics to be collected ++ * @use_rx_roaming: Use received voice packets to compute roam delay ++ * @sta_associated: Set to 1 after association ++ * @sta_roamed: Set to 1 after successful roaming ++ * @roam_delay: Roam delay ++ * @rx_timestamp_vo: Timestamp of received voice packet ++ * @txconf_timestamp_vo: Timestamp of received tx confirmation for ++ * successfully transmitted VO packet ++ * @sum_pkt_q_delay: Sum of packet queue delay ++ * @sum_media_delay: Sum of media delay ++ * ++ */ ++struct xradio_tsm_info { ++ u8 ac; ++ u8 use_rx_roaming; ++ u8 sta_associated; ++ u8 sta_roamed; ++ u16 roam_delay; ++ u32 rx_timestamp_vo; ++ u32 txconf_timestamp_vo; ++ u32 sum_pkt_q_delay; ++ u32 sum_media_delay; ++}; ++ ++/** ++ * xradio_start_stop_tsm - To start or stop collecting TSM metrics in ++ * xradio driver ++ * @start: To start or stop collecting TSM metrics ++ * @up: up for which metrics to be collected ++ * @packetization_delay: Packetization delay for this TID ++ * ++ */ ++struct xradio_start_stop_tsm { ++ u8 start; /*1: To start, 0: To stop*/ ++ u8 up; ++ u16 packetization_delay; ++}; ++ ++#endif /* CONFIG_XRADIO_TESTMODE */ ++struct xradio_common { ++ struct xradio_debug_common *debug; ++ struct xradio_queue tx_queue[AC_QUEUE_NUM]; ++ struct xradio_queue_stats tx_queue_stats; ++ ++ struct ieee80211_hw *hw; ++ struct mac_address addresses[XRWL_MAX_VIFS]; ++ ++ /*Will be a pointer to a list of VIFs - Dynamically allocated */ ++ struct ieee80211_vif *vif_list[XRWL_MAX_VIFS]; ++ atomic_t num_vifs; ++ spinlock_t vif_list_lock; ++ u32 if_id_slot; ++ struct device *pdev; ++ struct workqueue_struct *workqueue; ++ ++ struct mutex conf_mutex; ++ ++ struct sdio_func *sdio_func; ++ int driver_ready; ++ ++ /* HW/FW type (HIF_...) */ ++ int hw_type; ++ int hw_revision; ++ int fw_revision; ++ ++ /* firmware/hardware info */ ++ unsigned int tx_hdr_len; ++ ++ /* Radio data */ ++ int output_power; ++ int noise; ++ ++ /* calibration, output power limit and rssi<->dBm conversation data */ ++ ++ /* BBP/MAC state */ ++ const struct firmware *sdd; ++ struct ieee80211_rate *rates; ++ struct ieee80211_rate *mcs_rates; ++ u8 mac_addr[ETH_ALEN]; ++ /*TODO:COMBO: To be made per VIFF after mac80211 support */ ++ struct ieee80211_channel *channel; ++ int channel_switch_in_progress; ++ wait_queue_head_t channel_switch_done; ++ u8 channel_changed; //add by yangfh 2015-5-15 16:57:38. ++ u8 long_frame_max_tx_count; ++ u8 short_frame_max_tx_count; ++ /* TODO:COMBO: According to Hong aggregation will happen per VIFF. ++ * Keeping in common structure for the time being. Will be moved to VIFF ++ * after the mechanism is clear */ ++ u8 ba_tid_mask; ++ int ba_acc; /*TODO: Same as above */ ++ int ba_cnt; /*TODO: Same as above */ ++ int ba_cnt_rx; /*TODO: Same as above */ ++ int ba_acc_rx; /*TODO: Same as above */ ++ int ba_hist; /*TODO: Same as above */ ++ struct timer_list ba_timer;/*TODO: Same as above */ ++ spinlock_t ba_lock; /*TODO: Same as above */ ++ bool ba_ena; /*TODO: Same as above */ ++ struct work_struct ba_work; /*TODO: Same as above */ ++ struct xradio_pm_state pm_state; ++ bool is_BT_Present; ++ bool is_go_thru_go_neg; ++ u8 conf_listen_interval; ++ ++ /* BH */ ++ atomic_t bh_tx; ++ atomic_t bh_term; ++ atomic_t bh_suspend; ++ struct task_struct *bh_thread; ++ int bh_error; ++#ifdef BH_USE_SEMAPHORE ++ struct semaphore bh_sem; ++ atomic_t bh_wk; ++#else ++ wait_queue_head_t bh_wq; ++#endif ++ wait_queue_head_t bh_evt_wq; ++ ++ ++ int buf_id_tx; /* byte */ ++ int buf_id_rx; /* byte */ ++ int wsm_rx_seq; /* byte */ ++ int wsm_tx_seq; /* byte */ ++ int hw_bufs_used; ++ int hw_bufs_used_vif[XRWL_MAX_VIFS]; ++ struct sk_buff *skb_cache; ++ struct sk_buff *skb_reserved; ++ int skb_resv_len; ++ bool powersave_enabled; ++ bool device_can_sleep; ++ /* Keep xradio awake (WUP = 1) 1 second after each scan to avoid ++ * FW issue with sleeping/waking up. */ ++ atomic_t recent_scan; ++ long connet_time[XRWL_MAX_VIFS]; ++#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF ++ atomic_t suspend_state; ++#endif ++ ++ /* WSM */ ++ struct wsm_caps wsm_caps; ++ struct mutex wsm_cmd_mux; ++ struct wsm_buf wsm_cmd_buf; ++ struct wsm_cmd wsm_cmd; ++ wait_queue_head_t wsm_cmd_wq; ++ wait_queue_head_t wsm_startup_done; ++ struct wsm_cbc wsm_cbc; ++ struct semaphore tx_lock_sem; ++ atomic_t tx_lock; ++ u32 pending_frame_id; ++#ifdef CONFIG_XRADIO_TESTMODE ++ /* Device Power Range */ ++ struct wsm_tx_power_range txPowerRange[2]; ++ /* Advance Scan */ ++ struct advance_scan_elems advanceScanElems; ++ bool enable_advance_scan; ++ struct delayed_work advance_scan_timeout; ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ ++ /* WSM debug */ ++ int wsm_enable_wsm_dumps; ++ u32 wsm_dump_max_size; ++ u32 query_packetID; ++ atomic_t query_cnt; ++ struct work_struct query_work; /* for query packet */ ++ ++ /* Scan status */ ++ struct xradio_scan scan; ++ ++ /* TX/RX */ ++ unsigned long rx_timestamp; ++ ++ /* WSM events */ ++ spinlock_t event_queue_lock; ++ struct list_head event_queue; ++ struct work_struct event_handler; ++ ++ /* TX rate policy cache */ ++ struct tx_policy_cache tx_policy_cache; ++ struct work_struct tx_policy_upload_work; ++ atomic_t upload_count; ++ ++ /* cryptographic engine information */ ++ ++ /* bit field of glowing LEDs */ ++ u16 softled_state; ++ ++ /* statistics */ ++ struct ieee80211_low_level_stats stats; ++ ++ struct xradio_ht_oper ht_oper; ++ int tx_burst_idx; ++ ++ struct ieee80211_iface_limit if_limits1[2]; ++ struct ieee80211_iface_limit if_limits2[2]; ++ struct ieee80211_iface_limit if_limits3[2]; ++ struct ieee80211_iface_combination if_combs[3]; ++ ++ struct mutex wsm_oper_lock; ++ struct delayed_work rem_chan_timeout; ++ atomic_t remain_on_channel; ++ int roc_if_id; ++ u64 roc_cookie; ++ wait_queue_head_t offchannel_wq; ++ u16 offchannel_done; ++ u16 prev_channel; ++ int if_id_selected; ++ u32 key_map; ++ struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; ++#ifdef MCAST_FWDING ++ struct wsm_buf wsm_release_buf[WSM_MAX_BUF]; ++ u8 buf_released; ++#endif ++#ifdef ROAM_OFFLOAD ++ u8 auto_scanning; ++ u8 frame_rcvd; ++ u8 num_scanchannels; ++ u8 num_2g_channels; ++ u8 num_5g_channels; ++ struct wsm_scan_ch scan_channels[48]; ++ struct sk_buff *beacon; ++ struct sk_buff *beacon_bkp; ++ struct xradio_testframe testframe; ++#endif /*ROAM_OFFLOAD*/ ++#ifdef CONFIG_XRADIO_TESTMODE ++ struct xradio_testframe test_frame; ++ struct xr_tsm_stats tsm_stats; ++ struct xradio_tsm_info tsm_info; ++ spinlock_t tsm_lock; ++ struct xradio_start_stop_tsm start_stop_tsm; ++#endif /* CONFIG_XRADIO_TESTMODE */ ++ u8 connected_sta_cnt; ++ u16 vif0_throttle; ++ u16 vif1_throttle; ++}; ++ ++/* Virtual Interface State. One copy per VIF */ ++struct xradio_vif { ++ atomic_t enabled; ++ spinlock_t vif_lock; ++ int if_id; ++ /*TODO: Split into Common and VIF parts */ ++ struct xradio_debug_priv *debug; ++ /* BBP/MAC state */ ++ u8 bssid[ETH_ALEN]; ++ struct wsm_edca_params edca; ++ struct wsm_tx_queue_params tx_queue_params; ++ struct wsm_association_mode association_mode; ++ struct wsm_set_bss_params bss_params; ++ struct wsm_set_pm powersave_mode; ++ struct wsm_set_pm firmware_ps_mode; ++ int power_set_true; ++ int user_power_set_true; ++ u8 user_pm_mode; ++ int cqm_rssi_thold; ++ unsigned cqm_rssi_hyst; ++ unsigned cqm_tx_failure_thold; ++ unsigned cqm_tx_failure_count; ++ bool cqm_use_rssi; ++ int cqm_link_loss_count; ++ int cqm_beacon_loss_count; ++ int mode; ++ bool enable_beacon; ++ int beacon_int; ++ size_t ssid_length; ++ u8 ssid[IEEE80211_MAX_SSID_LEN]; ++#ifdef HIDDEN_SSID ++ bool hidden_ssid; ++#endif ++ bool listening; ++ struct wsm_rx_filter rx_filter; ++ struct wsm_beacon_filter_table bf_table; ++ struct wsm_beacon_filter_control bf_control; ++ struct wsm_multicast_filter multicast_filter; ++ bool has_multicast_subscription; ++ struct wsm_broadcast_addr_filter broadcast_filter; ++ bool disable_beacon_filter; ++ struct wsm_arp_ipv4_filter filter4; ++ struct work_struct update_filtering_work; ++ struct work_struct set_beacon_wakeup_period_work; ++ struct xradio_pm_state_vif pm_state_vif; ++ /*TODO: Add support in mac80211 for psmode info per VIF */ ++ struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; ++ struct wsm_uapsd_info uapsd_info; ++ bool setbssparams_done; ++ u32 listen_interval; ++ u32 erp_info; ++ bool powersave_enabled; ++ ++ /* WSM Join */ ++ enum xradio_join_status join_status; ++ u8 join_bssid[ETH_ALEN]; ++ struct work_struct join_work; ++ struct delayed_work join_timeout; ++ struct work_struct unjoin_work; ++ struct work_struct offchannel_work; ++ int join_dtim_period; ++ atomic_t delayed_unjoin; ++ ++ /* Security */ ++ s8 wep_default_key_id; ++ struct work_struct wep_key_work; ++ unsigned long rx_timestamp; ++ u32 cipherType; ++ ++ ++ /* AP powersave */ ++ u32 link_id_map; ++ u32 max_sta_ap_mode; ++ u32 link_id_after_dtim; ++ u32 link_id_uapsd; ++ u32 link_id_max; ++ u32 wsm_key_max_idx; ++ struct xradio_link_entry link_id_db[MAX_STA_IN_AP_MODE]; ++ struct work_struct link_id_work; ++ struct delayed_work link_id_gc_work; ++ u32 sta_asleep_mask; ++ u32 pspoll_mask; ++ bool aid0_bit_set; ++ spinlock_t ps_state_lock; ++ bool buffered_multicasts; ++ bool tx_multicast; ++ u8 last_tim[8]; //for softap dtim, add by yangfh ++ struct work_struct set_tim_work; ++ struct delayed_work set_cts_work; ++ struct work_struct multicast_start_work; ++ struct work_struct multicast_stop_work; ++ struct timer_list mcast_timeout; ++ ++ /* CQM Implementation */ ++ struct delayed_work bss_loss_work; ++ struct delayed_work connection_loss_work; ++ struct work_struct tx_failure_work; ++ int delayed_link_loss; ++ spinlock_t bss_loss_lock; ++ int bss_loss_status; ++ int bss_loss_confirm_id; ++ ++ struct ieee80211_vif *vif; ++ struct xradio_common *hw_priv; ++ struct ieee80211_hw *hw; ++ ++ /* ROC implementation */ ++ struct delayed_work pending_offchanneltx_work; ++#if defined(CONFIG_XRADIO_USE_EXTENSIONS) ++ /* Workaround for WFD testcase 6.1.10*/ ++ struct work_struct linkid_reset_work; ++ u8 action_frame_sa[ETH_ALEN]; ++ u8 action_linkid; ++#endif ++ bool htcap; ++#ifdef AP_HT_CAP_UPDATE ++ u16 ht_oper; ++ struct work_struct ht_oper_update_work; ++#endif ++ ++#ifdef AP_HT_COMPAT_FIX ++ u16 ht_compat_cnt; ++ u16 ht_compat_det; ++#endif ++}; ++struct xradio_sta_priv { ++ int link_id; ++ struct xradio_vif *priv; ++}; ++enum xradio_data_filterid { ++ IPV4ADDR_FILTER_ID = 0, ++}; ++ ++/* Datastructure for LLC-SNAP HDR */ ++#define P80211_OUI_LEN 3 ++struct ieee80211_snap_hdr { ++ u8 dsap; /* always 0xAA */ ++ u8 ssap; /* always 0xAA */ ++ u8 ctrl; /* always 0x03 */ ++ u8 oui[P80211_OUI_LEN]; /* organizational universal id */ ++} __packed; ++ ++ ++#ifdef TES_P2P_0002_ROC_RESTART ++extern s32 TES_P2P_0002_roc_dur; ++extern s32 TES_P2P_0002_roc_sec; ++extern s32 TES_P2P_0002_roc_usec; ++extern u32 TES_P2P_0002_packet_id; ++extern u32 TES_P2P_0002_state; ++ ++#define TES_P2P_0002_STATE_IDLE 0x00 ++#define TES_P2P_0002_STATE_SEND_RESP 0x01 ++#define TES_P2P_0002_STATE_GET_PKTID 0x02 ++#endif ++ ++/* debug.h must be here because refer to struct xradio_vif and ++ struct xradio_common.*/ ++#include "debug.h" ++ ++/******************************************************* ++ interfaces for operations of vif. ++********************************************************/ ++static inline ++struct xradio_common *xrwl_vifpriv_to_hwpriv(struct xradio_vif *priv) ++{ ++ return priv->hw_priv; ++} ++static inline ++struct xradio_vif *xrwl_get_vif_from_ieee80211(struct ieee80211_vif *vif) ++{ ++ return (struct xradio_vif *)vif->drv_priv; ++} ++ ++static inline ++struct xradio_vif *xrwl_hwpriv_to_vifpriv(struct xradio_common *hw_priv, ++ int if_id) ++{ ++ struct xradio_vif *vif; ++ ++ if (SYS_WARN((-1 == if_id) || (if_id > XRWL_MAX_VIFS))) ++ return NULL; ++ /* TODO:COMBO: During scanning frames can be received ++ * on interface ID 3 */ ++ spin_lock(&hw_priv->vif_list_lock); ++ if (!hw_priv->vif_list[if_id]) { ++ spin_unlock(&hw_priv->vif_list_lock); ++ return NULL; ++ } ++ ++ vif = xrwl_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); ++ SYS_WARN(!vif); ++ if (vif) ++ spin_lock(&vif->vif_lock); ++ spin_unlock(&hw_priv->vif_list_lock); ++ return vif; ++} ++ ++static inline ++struct xradio_vif *__xrwl_hwpriv_to_vifpriv(struct xradio_common *hw_priv, ++ int if_id) ++{ ++ SYS_WARN((-1 == if_id) || (if_id > XRWL_MAX_VIFS)); ++ /* TODO:COMBO: During scanning frames can be received ++ * on interface ID 3 */ ++ if (!hw_priv->vif_list[if_id]) { ++ return NULL; ++ } ++ ++ return xrwl_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); ++} ++ ++static inline ++struct xradio_vif *xrwl_get_activevif(struct xradio_common *hw_priv) ++{ ++ return xrwl_hwpriv_to_vifpriv(hw_priv, ffs(hw_priv->if_id_slot)-1); ++} ++ ++static inline bool is_hardware_xradio(struct xradio_common *hw_priv) ++{ ++ return (hw_priv->hw_revision == XR819_HW_REV0); ++} ++ ++static inline int xrwl_get_nr_hw_ifaces(struct xradio_common *hw_priv) ++{ ++ switch(hw_priv->hw_revision) { ++ case XR819_HW_REV0: ++ default: ++ return 1; ++ } ++} ++ ++#define xradio_for_each_vif(_hw_priv, _priv, _i) \ ++ for( \ ++ _i = 0; \ ++ (_i < XRWL_MAX_VIFS) \ ++ && ((_priv = _hw_priv->vif_list[_i] ? \ ++ xrwl_get_vif_from_ieee80211(_hw_priv->vif_list[_i]) : NULL),1); \ ++ _i++ \ ++ ) ++ ++/******************************************************* ++ interfaces for operations of queue. ++********************************************************/ ++static inline void xradio_tx_queues_lock(struct xradio_common *hw_priv) ++{ ++ int i; ++ for (i = 0; i < 4; ++i) ++ xradio_queue_lock(&hw_priv->tx_queue[i]); ++} ++ ++static inline void xradio_tx_queues_unlock(struct xradio_common *hw_priv) ++{ ++ int i; ++ for (i = 0; i < 4; ++i) ++ xradio_queue_unlock(&hw_priv->tx_queue[i]); ++} ++ ++#endif /* XRADIO_H */