diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 65aa7b1..67d7b86 100755 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -299,2 +299,3 @@ source "drivers/net/wireless/wl12xx/Kconfig" source "drivers/net/wireless/rtl8723bs/Kconfig" source "drivers/net/wireless/rtl8703as/Kconfig" +source "drivers/net/wireless/xradio/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 8a0704a..d263d7b 100755 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -65,6 +64,6 @@ obj-$(CONFIG_IWM) += iwmc3200wifi/ obj-$(CONFIG_MWIFIEX) += mwifiex/ obj-$(CONFIG_BCMDHD) += bcmdhd/ - +obj-$(CONFIG_XRADIO) += xradio/ obj-$(CONFIG_BRCMFMAC) += brcm80211/ obj-$(CONFIG_BRCMSMAC) += brcm80211/ diff --git a/drivers/net/wireless/xradio/Kconfig b/drivers/net/wireless/xradio/Kconfig new file mode 100644 index 0000000..67b11c4 --- /dev/null +++ b/drivers/net/wireless/xradio/Kconfig @@ -0,0 +1,62 @@ +config XRADIO + tristate "XRADIO WLAN support" + depends on MAC80211 + default n + help + + This is an experimental driver for the XRADIO chip-set. + Enabling this option enables the generic driver without + any platform support. + Please select the appropriate platform below. + +if XRADIO + +config XRADIO_SDIO + bool "SDIO bus wlan module" + depends on XRADIO + default y + ---help--- + Say N here if module don't use sdio bus. + If unsure, say Y. + +config XRADIO_NON_POWER_OF_TWO_BLOCKSIZES + bool "Platform supports non-power-of-two SDIO transfer" + depends on XRADIO + default y + ---help--- + Say N here only if you are running the driver on a platform + which does not have support for non-power-of-two SDIO transfer. + If unsure, say Y. + +config XRADIO_USE_GPIO_IRQ + bool "Use GPIO interrupt" + depends on XRADIO + default y + ---help--- + Say Y here if you want to include GPIO IRQ support instead of SDIO IRQ. + If unsure, say N. + +config XRADIO_5GHZ_SUPPORT + bool "5GHz band support" + depends on XRADIO + default n + ---help--- + Say Y if your device supports 5GHz band. If unsure, say N. + +config XRADIO_WAPI_SUPPORT + bool "WAPI support" + depends on XRADIO + default n + ---help--- + Say Y if your compat-wireless support WAPI. + If unsure, say N. + +config XRADIO_USE_EXTENSIONS + bool "Extensions for WFD and PS mode" + depends on XRADIO + default y + ---help--- + Say Y if you want to include XR extensions + If unsure, say Y. + +endif diff --git a/drivers/net/wireless/xradio/Makefile b/drivers/net/wireless/xradio/Makefile new file mode 100644 index 0000000..235d117 --- /dev/null +++ b/drivers/net/wireless/xradio/Makefile @@ -0,0 +1,62 @@ +xradio_wlan-y := \ + common.o \ + fwio.o \ + txrx.o \ + main.o \ + queue.o \ + hwio.o \ + bh.o \ + wsm.o \ + sta.o \ + ap.o \ + scan.o \ + platform.o \ + debug.o + +xradio_wlan-$(CONFIG_PM) += pm.o +xradio_wlan-$(CONFIG_XRADIO_SDIO) += sdio.o +xradio_wlan-$(CONFIG_XRADIO_ITP) += itp.o + +ccflags-y += -DP2P_MULTIVIF +ccflags-y += -DMCAST_FWDING +ccflags-y += -DXRADIO_SUSPEND_RESUME_FILTER_ENABLE +ccflags-y += -DAP_AGGREGATE_FW_FIX +ccflags-y += -DAP_HT_CAP_UPDATE +ccflags-y += -DHW_RESTART +ccflags-y += -DHW_ERROR_WIFI_RESET +ccflags-y += -DAP_HT_COMPAT_FIX +ccflags-y += -DCONFIG_XRADIO_DEBUG +ccflags-y += -DCONFIG_XRADIO_DUMP_ON_ERROR +ccflags-y += -DCONFIG_XRADIO_DEBUGFS + +ccflags-y += -DCONFIG_XRADIO_SUSPEND_POWER_OFF +# Use vfs for firmware load when request_firmware +# can't work on other platform. +# ccflags-y += -DUSE_VFS_FIRMWARE + +# Extra IE for probe response from upper layer is needed in P2P GO +# For offloading probe response to FW, the extra IE must be included +# in the probe response template +ccflags-y += -DPROBE_RESP_EXTRA_IE +ccflags-y += -DIPV6_FILTERING + +# Modified by wzw +ccflags-y += -DTES_P2P_0002_ROC_RESTART +ccflags-y += -DTES_P2P_000B_EXTEND_INACTIVITY_CNT +ccflags-y += -DTES_P2P_000B_DISABLE_EAPOL_FILTER +ccflags-y += -DXRADIO_USE_LONG_DTIM_PERIOD +ccflags-y += -DXRADIO_USE_LONG_KEEP_ALIVE_PERIOD + +# Mac addr config, disable hex for default. +#ccflags-y += -DXRADIO_MACPARAM_HEX + +# Mac addr generate from chipid. +ccflags-y += -DXRADIO_MACADDR_FROM_CHIPID + +# Use semaphore to sync bh txrx. +#ccflags-y += -DBH_USE_SEMAPHORE + +ldflags-y += --strip-debug + +obj-$(CONFIG_XRADIO) += xradio_wlan.o + diff --git a/drivers/net/wireless/xradio/ap.c b/drivers/net/wireless/xradio/ap.c new file mode 100644 index 0000000..3a6cd12 --- /dev/null +++ b/drivers/net/wireless/xradio/ap.c @@ -0,0 +1,1786 @@ +/* + * STA and AP APIs for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include "xradio.h" +#include "sta.h" +#include "ap.h" +#include "bh.h" +#include "net/mac80211.h" + +#define XRADIO_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ)) +#define XRADIO_ENABLE_ARP_FILTER_OFFLOAD 3 + +#ifndef ERP_INFO_BYTE_OFFSET +#define ERP_INFO_BYTE_OFFSET 2 +#endif + +#ifdef IPV6_FILTERING +#define XRADIO_ENABLE_NDP_FILTER_OFFLOAD 3 +#endif /*IPV6_FILTERING*/ + +static int xradio_upload_beacon(struct xradio_vif *priv); +#ifdef PROBE_RESP_EXTRA_IE +static int xradio_upload_proberesp(struct xradio_vif *priv); +#endif +static int xradio_upload_pspoll(struct xradio_vif *priv); +static int xradio_upload_null(struct xradio_vif *priv); +static int xradio_upload_qosnull(struct xradio_vif *priv); +static int xradio_start_ap(struct xradio_vif *priv); +static int xradio_update_beaconing(struct xradio_vif *priv); +/* +static int xradio_enable_beaconing(struct xradio_vif *priv, + bool enable); +*/ +static void __xradio_sta_notify(struct xradio_vif *priv, + enum sta_notify_cmd notify_cmd, + int link_id); + +/* ******************************************************************** */ +/* AP API */ +int xradio_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct xradio_link_entry *entry; + struct sk_buff *skb; +#ifdef AP_AGGREGATE_FW_FIX + struct xradio_common *hw_priv = hw->priv; +#endif + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef P2P_MULTIVIF + SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); +#endif + + if (priv->mode != NL80211_IFTYPE_AP) { + return 0; + } + + sta_priv->priv = priv; + sta_priv->link_id = xradio_find_link_id(priv, sta->addr); + if (SYS_WARN(!sta_priv->link_id)) { + /* Impossible error */ + ap_printk(XRADIO_DBG_ERROR,"No more link IDs available.\n"); + return -ENOENT; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + if ((sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) == + IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) { + priv->sta_asleep_mask |= BIT(sta_priv->link_id); + } + entry->status = XRADIO_LINK_HARD; + while ((skb = skb_dequeue(&entry->rx_queue))) + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + +#ifdef AP_AGGREGATE_FW_FIX + hw_priv->connected_sta_cnt++; + if(hw_priv->connected_sta_cnt>1) { + wsm_lock_tx(hw_priv); + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + priv->if_id)); + wsm_unlock_tx(hw_priv); + } +#endif + + return 0; +} + +int xradio_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct xradio_common *hw_priv = hw->priv; + struct xradio_sta_priv *sta_priv = + (struct xradio_sta_priv *)&sta->drv_priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct xradio_link_entry *entry; + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef P2P_MULTIVIF + SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); +#endif + + if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id) { + ap_printk(XRADIO_DBG_NIY, "no station to remove\n"); + return 0; + } + + entry = &priv->link_id_db[sta_priv->link_id - 1]; + spin_lock_bh(&priv->ps_state_lock); + entry->status = XRADIO_LINK_RESERVE; + entry->timestamp = jiffies; + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + spin_unlock_bh(&priv->ps_state_lock); + flush_workqueue(hw_priv->workqueue); + +#ifdef AP_AGGREGATE_FW_FIX + hw_priv->connected_sta_cnt--; + if(hw_priv->connected_sta_cnt <= 1) { + if ((priv->if_id != 1) || + ((priv->if_id == 1) && hw_priv->is_go_thru_go_neg)) { + wsm_lock_tx(hw_priv); + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + priv->if_id)); + wsm_unlock_tx(hw_priv); + } + } +#endif + + return 0; +} + +static void __xradio_sta_notify(struct xradio_vif *priv, + enum sta_notify_cmd notify_cmd, + int link_id) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + u32 bit, prev; + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + /* Zero link id means "for all link IDs" */ + if (link_id) + bit = BIT(link_id); + else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE)) + bit = 0; + else + bit = priv->link_id_map; + prev = priv->sta_asleep_mask & bit; + + switch (notify_cmd) { + case STA_NOTIFY_SLEEP: + if (!prev) { + if (priv->buffered_multicasts && + !priv->sta_asleep_mask) + queue_work(hw_priv->workqueue, + &priv->multicast_start_work); + priv->sta_asleep_mask |= bit; + } + break; + case STA_NOTIFY_AWAKE: + if (prev) { + priv->sta_asleep_mask &= ~bit; + priv->pspoll_mask &= ~bit; + if (priv->tx_multicast && link_id && + !priv->sta_asleep_mask) + queue_work(hw_priv->workqueue, + &priv->multicast_stop_work); + xradio_bh_wakeup(hw_priv); + } + break; + } +} + +void xradio_sta_notify(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta) +{ + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef P2P_MULTIVIF + SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); +#endif + spin_lock_bh(&priv->ps_state_lock); + __xradio_sta_notify(priv, notify_cmd, sta_priv->link_id); + spin_unlock_bh(&priv->ps_state_lock); +} + +static void xradio_ps_notify(struct xradio_vif *priv, + int link_id, bool ps) +{ + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (link_id > MAX_STA_IN_AP_MODE) { + ap_printk(XRADIO_DBG_WARN,"link_id is invalid=%d\n", link_id); + return; + } + + ap_printk(XRADIO_DBG_NIY, "%s for LinkId: %d. STAs asleep: %.8X\n", + ps ? "Stop" : "Start", link_id, priv->sta_asleep_mask); + + /* TODO:COMBO: __xradio_sta_notify changed. */ + __xradio_sta_notify(priv, ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id); +} + +static int xradio_set_tim_impl(struct xradio_vif *priv, bool aid0_bit_set) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct sk_buff *skb; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + u16 tim_offset, tim_length; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + ap_printk(XRADIO_DBG_MSG, "%s mcast: %s.\n", __func__, + aid0_bit_set ? "ena" : "dis"); + + skb = ieee80211_beacon_get_tim(priv->hw, priv->vif, &tim_offset, &tim_length); + if (!skb) { + __xradio_flush(hw_priv, true, priv->if_id); + return -ENOENT; + } + + if (tim_offset && tim_length >= 6) { + /* Ignore DTIM count from mac80211: + * firmware handles DTIM internally. */ + skb->data[tim_offset + 2] = 0; + + /* Set/reset aid0 bit */ + if (aid0_bit_set) + skb->data[tim_offset + 4] |= 1; + else + skb->data[tim_offset + 4] &= ~1; + } + + update_ie.ies = &skb->data[tim_offset]; + update_ie.length = tim_length; + //filter same tim info, yangfh + if(memcmp(priv->last_tim, update_ie.ies, tim_length)) { + SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); + memcpy(priv->last_tim, update_ie.ies, tim_length); + ap_printk(XRADIO_DBG_MSG,"%02x %02x %02x %02x %02x %02x\n", + update_ie.ies[0], update_ie.ies[1], update_ie.ies[2], + update_ie.ies[3], update_ie.ies[4], update_ie.ies[5]); + } + + dev_kfree_skb(skb); + + return 0; +} + +void xradio_set_tim_work(struct work_struct *work) +{ + struct xradio_vif *priv = container_of(work, struct xradio_vif, set_tim_work); + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + xradio_set_tim_impl(priv, priv->aid0_bit_set); +} + +int xradio_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set) +{ + struct xradio_sta_priv *sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; + struct xradio_vif *priv = sta_priv->priv; + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef P2P_MULTIVIF + SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); +#endif + SYS_WARN(priv->mode != NL80211_IFTYPE_AP); + queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); + return 0; +} + +void xradio_set_cts_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, set_cts_work.work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + u8 erp_ie[3] = {WLAN_EID_ERP_INFO, 0x1, 0}; + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + .ies = erp_ie, + .length = 3, + }; + u32 erp_info; + __le32 use_cts_prot; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + mutex_lock(&hw_priv->conf_mutex); + erp_info = priv->erp_info; + mutex_unlock(&hw_priv->conf_mutex); + use_cts_prot = (erp_info & WLAN_ERP_USE_PROTECTION)? __cpu_to_le32(1) : 0; + + erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info; + + ap_printk(XRADIO_DBG_MSG, "ERP information 0x%x\n", erp_info); + + /* TODO:COMBO: If 2 interfaces are on the same channel they share + the same ERP values */ + SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION, + &use_cts_prot, sizeof(use_cts_prot), priv->if_id)); + /* If STA Mode update_ie is not required */ + if (priv->mode != NL80211_IFTYPE_STATION) { + SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); + } + + return; +} + +static int xradio_set_btcoexinfo(struct xradio_vif *priv) +{ + struct wsm_override_internal_txrate arg; + int ret = 0; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->mode == NL80211_IFTYPE_STATION) { + /* Plumb PSPOLL and NULL template */ + SYS_WARN(xradio_upload_pspoll(priv)); + SYS_WARN(xradio_upload_null(priv)); + } else { + return 0; + } + + memset(&arg, 0, sizeof(struct wsm_override_internal_txrate)); + + if (!priv->vif->p2p) { + /* STATION mode */ + if (priv->bss_params.operationalRateSet & ~0xF) { + ap_printk(XRADIO_DBG_NIY, "STA has ERP rates\n"); + /* G or BG mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } else { + ap_printk(XRADIO_DBG_NIY, "STA has non ERP rates\n"); + /* B only mode */ + arg.internalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } + arg.nonErpInternalTxRate = (__ffs( + priv->association_mode.basicRateSet)); + } else { + /* P2P mode */ + arg.internalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + arg.nonErpInternalTxRate = (__ffs( + priv->bss_params.operationalRateSet & ~0xF)); + } + + ap_printk(XRADIO_DBG_NIY, "BTCOEX_INFO" "MODE %d, internalTxRate : %x," + "nonErpInternalTxRate: %x\n", priv->mode, arg.internalTxRate, + arg.nonErpInternalTxRate); + + ret = SYS_WARN(wsm_write_mib(xrwl_vifpriv_to_hwpriv(priv), + WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &arg, sizeof(arg), priv->if_id)); + + return ret; +} + +void xradio_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed) +{ + struct xradio_common *hw_priv = dev->priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef P2P_MULTIVIF + if (priv->if_id == XRWL_GENERIC_IF_ID) + return; +#endif + mutex_lock(&hw_priv->conf_mutex); + if (changed & BSS_CHANGED_BSSID) { +#ifdef CONFIG_XRADIO_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->tsm_info.sta_associated) { + unsigned now = jiffies; + hw_priv->tsm_info.sta_roamed = 1; + if ((now - hw_priv->tsm_info.txconf_timestamp_vo) > + (now - hw_priv->tsm_info.rx_timestamp_vo)) + hw_priv->tsm_info.use_rx_roaming = 1; + } else { + hw_priv->tsm_info.sta_associated = 1; + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_XRADIO_TESTMODE*/ + memcpy(priv->bssid, info->bssid, ETH_ALEN); + xradio_setup_mac_pvif(priv); + } + + /* TODO: BSS_CHANGED_IBSS */ + if (changed & BSS_CHANGED_ARP_FILTER) { + struct wsm_arp_ipv4_filter filter = {0}; + int i; + ap_printk(XRADIO_DBG_MSG, "[STA] BSS_CHANGED_ARP_FILTER enabled: %d, cnt: %d\n", + info->arp_filter_enabled, info->arp_addr_cnt); + + if (info->arp_filter_enabled){ + if (vif->type == NL80211_IFTYPE_STATION) + filter.enable = (u32)XRADIO_ENABLE_ARP_FILTER_OFFLOAD; + else if (priv->join_status == XRADIO_JOIN_STATUS_AP) + filter.enable = (u32)(1<<1); + else + filter.enable = 0; + } + + /* Currently only one IP address is supported by firmware. + * In case of more IPs arp filtering will be disabled. */ + if (info->arp_addr_cnt > 0 && + info->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->arp_addr_cnt; i++) { + filter.ipv4Address[i] = info->arp_addr_list[i]; + ap_printk(XRADIO_DBG_NIY, "[STA]addr[%d]: 0x%X\n", i, filter.ipv4Address[i]); + } + } else + filter.enable = 0; + + if (filter.enable) + xradio_set_arpreply(dev, vif); + + priv->filter4.enable = filter.enable; + ap_printk(XRADIO_DBG_NIY, "[STA]arp ip filter enable: %d\n", __le32_to_cpu(filter.enable)); + + if (wsm_set_arp_ipv4_filter(hw_priv, &filter, priv->if_id)) + SYS_WARN(1); + + if (filter.enable && + (priv->join_status == XRADIO_JOIN_STATUS_STA)) { + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. */ + //if (info->dynamic_ps_timeout >= 0x80) + // priv->powersave_mode.fastPsmIdlePeriod = 0xFF; + //else + // priv->powersave_mode.fastPsmIdlePeriod = info->dynamic_ps_timeout << 1; + + priv->powersave_mode.fastPsmIdlePeriod = 200;//when connected,the dev->conf.dynamic_ps_timeout value is 0 + priv->powersave_mode.apPsmChangePeriod = 200; //100ms, add by yangfh + ap_printk(XRADIO_DBG_NIY, "[STA]fastPsmIdle=%d, apPsmChange=%d\n", + priv->powersave_mode.fastPsmIdlePeriod, + priv->powersave_mode.apPsmChangePeriod); + + if (priv->setbssparams_done) { + int ret = 0; + struct wsm_set_pm pm = priv->powersave_mode; + if (priv->user_power_set_true) + priv->powersave_mode.pmMode = priv->user_pm_mode; + else if ((priv->power_set_true && + ((priv->powersave_mode.pmMode == WSM_PSM_ACTIVE) || + (priv->powersave_mode.pmMode == WSM_PSM_PS))) || + !priv->power_set_true) + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + + ret = xradio_set_pm (priv, &priv->powersave_mode); + if(ret) + priv->powersave_mode = pm; + } else { + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + } + priv->power_set_true = 0; + priv->user_power_set_true = 0; + } + } + +#ifdef IPV6_FILTERING + /*in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have BSS_CHANGED_NDP_FILTER enum value*/ +#if 0 + if (changed & BSS_CHANGED_NDP_FILTER) { + int i; + struct wsm_ndp_ipv6_filter filter = {0}; + u16 *ipv6addr = NULL; + + ap_printk(XRADIO_DBG_MSG, "[STA] BSS_CHANGED_NDP_FILTER enabled: %d, cnt: %d\n", + info->ndp_filter_enabled, info->ndp_addr_cnt); + + if (info->ndp_filter_enabled) { + if (vif->type == NL80211_IFTYPE_STATION) + filter.enable = (u32)XRADIO_ENABLE_NDP_FILTER_OFFLOAD; + else if ((vif->type == NL80211_IFTYPE_AP)) + filter.enable = (u32)(1<<1); + else + filter.enable = 0; + } + + /* Currently only one IP address is supported by firmware. + * In case of more IPs ndp filtering will be disabled. */ + if (info->ndp_addr_cnt > 0 && + info->ndp_addr_cnt <= WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES) { + for (i = 0; i < info->ndp_addr_cnt; i++) { + priv->filter6.ipv6Address[i] = filter.ipv6Address[i] = info->ndp_addr_list[i]; + ipv6addr = (u16 *)(&filter.ipv6Address[i]); + ap_printk(XRADIO_DBG_MSG, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \ + i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \ + cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \ + cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \ + cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7))); + } + } else { + filter.enable = 0; + for (i = 0; i < info->ndp_addr_cnt; i++) { + ipv6addr = (u16 *)(&info->ndp_addr_list[i]); + ap_printk(XRADIO_DBG_MSG, "[STA] ipv6 addr[%d]: %x:%x:%x:%x:%x:%x:%x:%x\n", \ + i, cpu_to_be16(*(ipv6addr + 0)), cpu_to_be16(*(ipv6addr + 1)), \ + cpu_to_be16(*(ipv6addr + 2)), cpu_to_be16(*(ipv6addr + 3)), \ + cpu_to_be16(*(ipv6addr + 4)), cpu_to_be16(*(ipv6addr + 5)), \ + cpu_to_be16(*(ipv6addr + 6)), cpu_to_be16(*(ipv6addr + 7))); + } + } + + ap_printk(XRADIO_DBG_NIY, "[STA] ndp ip filter enable: %d\n", + __le32_to_cpu(filter.enable)); + + if (filter.enable) + xradio_set_na(dev, vif); + + priv->filter6.enable = filter.enable; + + if (wsm_set_ndp_ipv6_filter(hw_priv, &filter, priv->if_id)) + SYS_WARN(1); +#if 0 /*Commented out to disable Power Save in IPv6*/ + if (filter.enable && (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (priv->vif->p2p) && !(priv->firmware_ps_mode.pmMode & WSM_PSM_FAST_PS)) { + if(priv->setbssparams_done) { + int ret = 0; + struct wsm_set_pm pm = priv->powersave_mode; + + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + ret = xradio_set_pm(priv, &priv->powersave_mode); + if(ret) { + priv->powersave_mode = pm; + } + } else { + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + } + } +#endif + } +#endif +#endif /*IPV6_FILTERING*/ + + if (changed & BSS_CHANGED_BEACON) { + ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_BEACON\n"); +#ifdef HIDDEN_SSID + if(priv->join_status != XRADIO_JOIN_STATUS_AP) { + priv->hidden_ssid = info->hidden_ssid; + priv->ssid_length = info->ssid_len; + memcpy(priv->ssid, info->ssid, info->ssid_len); + } else + ap_printk(XRADIO_DBG_NIY, "priv->join_status=%d\n", priv->join_status); +#endif + SYS_WARN(xradio_upload_beacon(priv)); + SYS_WARN(xradio_update_beaconing(priv)); + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) { + ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_BEACON_ENABLED dummy\n"); + priv->enable_beacon = info->enable_beacon; + } + + if (changed & BSS_CHANGED_BEACON_INT) { + ap_printk(XRADIO_DBG_NIY, "CHANGED_BEACON_INT\n"); + /* Restart AP only when connected */ + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + SYS_WARN(xradio_update_beaconing(priv)); + } + + + if (changed & BSS_CHANGED_ASSOC) { + wsm_lock_tx(hw_priv); + priv->wep_default_key_id = -1; + wsm_unlock_tx(hw_priv); + + if (!info->assoc /* && !info->ibss_joined */) { + priv->cqm_link_loss_count = XRADIO_LINK_LOSS_THOLD_DEF; + priv->cqm_beacon_loss_count = XRADIO_BSS_LOSS_THOLD_DEF; + priv->cqm_tx_failure_thold = 0; + } + priv->cqm_tx_failure_count = 0; + } + + if (changed & + (BSS_CHANGED_ASSOC | + BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_ERP_PREAMBLE | + BSS_CHANGED_HT | + BSS_CHANGED_ERP_SLOT)) { + int is_combo = 0; + int i; + struct xradio_vif *tmp_priv; + ap_printk(XRADIO_DBG_NIY, "BSS_CHANGED_ASSOC.\n"); + if (info->assoc) { /* TODO: ibss_joined */ + struct ieee80211_sta *sta = NULL; + if (info->dtim_period) + priv->join_dtim_period = info->dtim_period; + priv->beacon_int = info->beacon_int; + + /* Associated: kill join timeout */ + cancel_delayed_work_sync(&priv->join_timeout); + + rcu_read_lock(); + if (info->bssid) + sta = ieee80211_find_sta(vif, info->bssid); + if (sta) { + /* TODO:COMBO:Change this once + * mac80211 changes are available */ + SYS_BUG(!hw_priv->channel); + hw_priv->ht_info.ht_cap = sta->ht_cap; + priv->bss_params.operationalRateSet =__cpu_to_le32( + xradio_rate_mask_to_wsm(hw_priv, sta->supp_rates[hw_priv->channel->band])); + hw_priv->ht_info.channel_type = info->channel_type; + hw_priv->ht_info.operation_mode = info->ht_operation_mode; + } else { + memset(&hw_priv->ht_info, 0, sizeof(hw_priv->ht_info)); + priv->bss_params.operationalRateSet = -1; + } + rcu_read_unlock(); + priv->htcap = (sta && xradio_is_ht(&hw_priv->ht_info)); + xradio_for_each_vif(hw_priv, tmp_priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv) +#else + if (!tmp_priv) +#endif + continue; + if (tmp_priv->join_status >= XRADIO_JOIN_STATUS_STA) + is_combo++; + } + + if (is_combo > 1) { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; + ap_printk(XRADIO_DBG_WARN, "%sASSOC is_combo %d\n", + (priv->join_status == XRADIO_JOIN_STATUS_STA)?"[STA] ":"", + hw_priv->vif0_throttle); + } else if ((priv->join_status == XRADIO_JOIN_STATUS_STA) && priv->htcap) { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE; + ap_printk(XRADIO_DBG_WARN, "[STA] ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle); + } else { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; + ap_printk(XRADIO_DBG_WARN, "ASSOC not_combo 11BG %d\n",hw_priv->vif0_throttle); + } + + if (sta) { + __le32 val = 0; + if (hw_priv->ht_info.operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT) { + ap_printk(XRADIO_DBG_NIY,"[STA] Non-GF STA present\n"); + /* Non Green field capable STA */ + val = __cpu_to_le32(BIT(1)); + } + SYS_WARN(wsm_write_mib(hw_priv, WSM_MID_ID_SET_HT_PROTECTION, + &val, sizeof(val), priv->if_id)); + } + + priv->association_mode.greenfieldMode = xradio_ht_greenfield(&hw_priv->ht_info); + priv->association_mode.flags = + WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES | + WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE | + WSM_ASSOCIATION_MODE_USE_HT_MODE | + WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET | + WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING; + + priv->association_mode.preambleType = + (info->use_short_preamble ? WSM_JOIN_PREAMBLE_SHORT : WSM_JOIN_PREAMBLE_LONG); + priv->association_mode.basicRateSet = __cpu_to_le32( + xradio_rate_mask_to_wsm(hw_priv,info->basic_rates)); + priv->association_mode.mpduStartSpacing = + xradio_ht_ampdu_density(&hw_priv->ht_info); + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold; + //priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; + //priv->cqm_tx_failure_count = 0; + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + + priv->bss_params.beaconLostCount = (priv->cqm_beacon_loss_count ? + priv->cqm_beacon_loss_count : priv->cqm_link_loss_count); + + priv->bss_params.aid = info->aid; + + if (priv->join_dtim_period < 1) + priv->join_dtim_period = 1; + + ap_printk(XRADIO_DBG_MSG, "[STA] DTIM %d, interval: %d\n", + priv->join_dtim_period, priv->beacon_int); + ap_printk(XRADIO_DBG_MSG, "[STA] Preamble: %d, " \ + "Greenfield: %d, Aid: %d, Rates: 0x%.8X, Basic: 0x%.8X\n", + priv->association_mode.preambleType, + priv->association_mode.greenfieldMode, + priv->bss_params.aid, + priv->bss_params.operationalRateSet, + priv->association_mode.basicRateSet); + SYS_WARN(wsm_set_association_mode(hw_priv, &priv->association_mode, priv->if_id)); + SYS_WARN(wsm_keep_alive_period(hw_priv, XRADIO_KEEP_ALIVE_PERIOD /* sec */, + priv->if_id)); + SYS_WARN(wsm_set_bss_params(hw_priv, &priv->bss_params, priv->if_id)); + priv->setbssparams_done = true; +#ifdef XRADIO_USE_LONG_DTIM_PERIOD +{ + int join_dtim_period_extend; + if (priv->join_dtim_period <= 3) { + join_dtim_period_extend = priv->join_dtim_period * 3; + } else if (priv->join_dtim_period <= 5) { + join_dtim_period_extend = priv->join_dtim_period * 2; + } else { + join_dtim_period_extend = priv->join_dtim_period; + } + SYS_WARN(wsm_set_beacon_wakeup_period(hw_priv, + ((priv->beacon_int * join_dtim_period_extend) > MAX_BEACON_SKIP_TIME_MS + ? 1 : join_dtim_period_extend) , 0, priv->if_id)); +} +#else + SYS_WARN(wsm_set_beacon_wakeup_period(hw_priv, + ((priv->beacon_int * priv->join_dtim_period) > MAX_BEACON_SKIP_TIME_MS + ? 1 : priv->join_dtim_period) , 0, priv->if_id)); +#endif + if (priv->htcap) { + wsm_lock_tx(hw_priv); + /* Statically enabling block ack for TX/RX */ + SYS_WARN(wsm_set_block_ack_policy(hw_priv, hw_priv->ba_tid_mask, + hw_priv->ba_tid_mask, priv->if_id)); + wsm_unlock_tx(hw_priv); + } + /*set ps active,avoid that when connecting process,the device sleeps,then can't receive pkts.*/ + if (changed & BSS_CHANGED_ASSOC) + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + xradio_set_pm(priv, &priv->powersave_mode); + if (priv->vif->p2p) { + ap_printk(XRADIO_DBG_NIY, "[STA] Setting p2p powersave configuration.\n"); + SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, &priv->p2p_ps_modeinfo, priv->if_id)); +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //xradio_notify_noa(priv, XRADIO_NOA_NOTIFICATION_DELAY); +#endif + } + + if (priv->mode == NL80211_IFTYPE_STATION) + SYS_WARN(xradio_upload_qosnull(priv)); + + if (hw_priv->is_BT_Present) + SYS_WARN(xradio_set_btcoexinfo(priv)); +#if 0 + /* It's better to override internal TX rete; otherwise + * device sends RTS at too high rate. However device + * can't receive CTS at 1 and 2 Mbps. Well, 5.5 is a + * good choice for RTS/CTS, but that means PS poll + * will be sent at the same rate - impact on link + * budget. Not sure what is better.. */ + + /* Update: internal rate selection algorythm is not + * bad: if device is not receiving CTS at high rate, + * it drops RTS rate. + * So, conclusion: if-0 the code. Keep code just for + * information: + * Do not touch WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE! */ + + /* ~3 is a bug in device: RTS/CTS is not working at + * low rates */ + __le32 internal_tx_rate = __cpu_to_le32( + __ffs(priv->association_mode.basicRateSet & ~3)); + SYS_WARN(wsm_write_mib(priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + &internal_tx_rate,sizeof(internal_tx_rate))); +#endif + } else { + memset(&priv->association_mode, 0, sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + } + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT)) { + u32 prev_erp_info = priv->erp_info; + if (priv->join_status == XRADIO_JOIN_STATUS_AP) { + if (info->use_cts_prot) + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + else if (!(prev_erp_info & WLAN_ERP_NON_ERP_PRESENT)) + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + + if (prev_erp_info != priv->erp_info) + queue_delayed_work(hw_priv->workqueue, &priv->set_cts_work, 0*HZ); + } + } + + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_SLOT)) { + __le32 slot_time = info->use_short_slot ? __cpu_to_le32(9) : __cpu_to_le32(20); + ap_printk(XRADIO_DBG_MSG, "[STA] Slot time :%d us.\n", __le32_to_cpu(slot_time)); + SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_SLOT_TIME, &slot_time, + sizeof(slot_time), priv->if_id)); + } + if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) { + struct wsm_rcpi_rssi_threshold threshold = { + .rollingAverageCount = 8, + }; + +#if 0 + /* For verification purposes */ + info->cqm_rssi_thold = -50; + info->cqm_rssi_hyst = 4; +#endif /* 0 */ + + ap_printk(XRADIO_DBG_NIY, "[CQM] RSSI threshold subscribe: %d(+-%d)\n", + info->cqm_rssi_thold, info->cqm_rssi_hyst); + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + priv->cqm_rssi_thold = info->cqm_rssi_thold; + priv->cqm_rssi_hyst = info->cqm_rssi_hyst; +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + + if (info->cqm_rssi_thold || info->cqm_rssi_hyst) { + /* RSSI subscription enabled */ + /* TODO: It's not a correct way of setting threshold. + * Upper and lower must be set equal here and adjusted + * in callback. However current implementation is much + * more relaible and stable. */ + if (priv->cqm_use_rssi) { + threshold.upperThreshold = info->cqm_rssi_thold + info->cqm_rssi_hyst; + threshold.lowerThreshold = info->cqm_rssi_thold; + } else { + /* convert RSSI to RCPI, RCPI = (RSSI + 110) * 2 */ + threshold.upperThreshold = (info->cqm_rssi_thold + info->cqm_rssi_hyst + 110)<<1; + threshold.lowerThreshold = (info->cqm_rssi_thold + 110)<<1; + } + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE; + } else { + /* There is a bug in FW, see sta.c. We have to enable + * dummy subscription to get correct RSSI values. */ + threshold.rssiRcpiMode |= WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER; + } + SYS_WARN(wsm_set_rcpi_rssi_threshold(hw_priv, &threshold, priv->if_id)); + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //priv->cqm_tx_failure_thold = info->cqm_tx_fail_thold; + //priv->cqm_tx_failure_count = 0; + + //if (priv->cqm_beacon_loss_count != info->cqm_beacon_miss_thold) { + // priv->cqm_beacon_loss_count = info->cqm_beacon_miss_thold; + // priv->bss_params.beaconLostCount = (priv->cqm_beacon_loss_count? + // priv->cqm_beacon_loss_count : priv->cqm_link_loss_count); + /* Make sure we are associated before sending + * set_bss_params to firmware */ + if (priv->bss_params.aid) { + SYS_WARN(wsm_set_bss_params(hw_priv, &priv->bss_params, priv->if_id)); + priv->setbssparams_done = true; + } + //} +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + } + /* + * in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have + * BSS_CHANGED_PS and BSS_CHANGED_RETRY_LIMITS enum value. + */ +#if 0 + if (changed & BSS_CHANGED_PS) { + if (info->ps_enabled == false) + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + else if (info->dynamic_ps_timeout <= 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_FAST_PS; + + ap_printk(XRADIO_DBG_MSG, "[STA] Aid: %d, Joined: %s, Powersave: %s\n", + priv->bss_params.aid, + priv->join_status == XRADIO_JOIN_STATUS_STA ? "yes" : "no", + (priv->powersave_mode.pmMode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : + priv->powersave_mode.pmMode == WSM_PSM_PS ? "WSM_PSM_PS" : + priv->powersave_mode.pmMode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : + "UNKNOWN")); + + /* Firmware requires that value for this 1-byte field must + * be specified in units of 500us. Values above the 128ms + * threshold are not supported. */ + if (info->dynamic_ps_timeout >= 0x80) + priv->powersave_mode.fastPsmIdlePeriod = 0xFF; + else + priv->powersave_mode.fastPsmIdlePeriod = info->dynamic_ps_timeout << 1; + ap_printk(XRADIO_DBG_NIY, "[STA]CHANGED_PS fastPsmIdle=%d, apPsmChange=%d\n", + priv->powersave_mode.fastPsmIdlePeriod, + priv->powersave_mode.apPsmChangePeriod); + + if (priv->join_status == XRADIO_JOIN_STATUS_STA && priv->bss_params.aid && + priv->setbssparams_done && priv->filter4.enable) + xradio_set_pm(priv, &priv->powersave_mode); + else + priv->power_set_true = 1; + } + + if (changed & BSS_CHANGED_RETRY_LIMITS) { + ap_printk(XRADIO_DBG_NIY, "Retry limits: %d (long), %d (short).\n", + info->retry_long, info->retry_short); + spin_lock_bh(&hw_priv->tx_policy_cache.lock); + /*TODO:COMBO: for now it's still handled per hw and kept + * in xradio_common */ + hw_priv->long_frame_max_tx_count = info->retry_long; + hw_priv->short_frame_max_tx_count = + (info->retry_short < 0x0F ? info->retry_short : 0x0F); + hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count; + spin_unlock_bh(&hw_priv->tx_policy_cache.lock); + /* TBD: I think we don't need tx_policy_force_upload(). + * Outdated policies will leave cache in a normal way. */ + /* SYS_WARN(tx_policy_force_upload(priv)); */ + } +#endif + /*in linux3.4 mac,the enum ieee80211_bss_change variable doesn't have BSS_CHANGED_P2P_PS enum value*/ +#if 0 +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + if (changed & BSS_CHANGED_P2P_PS) { + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + ap_printk(XRADIO_DBG_NIY, "[AP] BSS_CHANGED_P2P_PS\n"); + ap_printk(XRADIO_DBG_NIY, "[AP] Legacy PS: %d for AID %d in %d mode.\n", + info->p2p_ps.legacy_ps, priv->bss_params.aid, priv->join_status); + + if (info->p2p_ps.legacy_ps >= 0) { + if (info->p2p_ps.legacy_ps > 0) + priv->powersave_mode.pmMode = WSM_PSM_PS; + else + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + + if(info->p2p_ps.ctwindow && info->p2p_ps.opp_ps) + priv->powersave_mode.pmMode = WSM_PSM_PS; + if (priv->join_status == XRADIO_JOIN_STATUS_STA) + xradio_set_pm(priv, &priv->powersave_mode); + } + + ap_printk(XRADIO_DBG_MSG, "[AP] CTWindow: %d\n", info->p2p_ps.ctwindow); + if (info->p2p_ps.ctwindow >= 128) + modeinfo->oppPsCTWindow = 127; + else if (info->p2p_ps.ctwindow >= 0) + modeinfo->oppPsCTWindow = info->p2p_ps.ctwindow; + + ap_printk(XRADIO_DBG_MSG, "[AP] Opportunistic: %d\n", info->p2p_ps.opp_ps); + switch (info->p2p_ps.opp_ps) { + case 0: + modeinfo->oppPsCTWindow &= ~(BIT(7)); + break; + case 1: + modeinfo->oppPsCTWindow |= BIT(7); + break; + default: + break; + } + + ap_printk(XRADIO_DBG_MSG, "[AP] NOA: %d, %d, %d, %d\n", + info->p2p_ps.count, info->p2p_ps.start, + info->p2p_ps.duration, info->p2p_ps.interval); + /* Notice of Absence */ + modeinfo->count = info->p2p_ps.count; + + if (info->p2p_ps.count) { + /* In case P2P_GO we need some extra time to be sure + * we will update beacon/probe_resp IEs correctly */ +#define NOA_DELAY_START_MS 300 + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + modeinfo->startTime = __cpu_to_le32(info->p2p_ps.start + NOA_DELAY_START_MS); + else + modeinfo->startTime = __cpu_to_le32(info->p2p_ps.start); + modeinfo->duration = __cpu_to_le32(info->p2p_ps.duration); + modeinfo->interval = __cpu_to_le32(info->p2p_ps.interval); + modeinfo->dtimCount = 1; + modeinfo->reserved = 0; + } else { + modeinfo->dtimCount = 0; + modeinfo->startTime = 0; + modeinfo->reserved = 0; + modeinfo->duration = 0; + modeinfo->interval = 0; + } + +#if defined(CONFIG_XRADIO_DEBUG) + print_hex_dump_bytes("p2p_set_ps_modeinfo: ", DUMP_PREFIX_NONE, + (u8 *)modeinfo, sizeof(*modeinfo)); +#endif /* CONFIG_XRADIO_DEBUG */ + + if (priv->join_status == XRADIO_JOIN_STATUS_STA || + priv->join_status == XRADIO_JOIN_STATUS_AP) { + SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, modeinfo, priv->if_id)); + } +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + /* Temporary solution while firmware don't support NOA change + * notification yet */ + xradio_notify_noa(priv, 10); +#endif + } +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ +#endif + mutex_unlock(&hw_priv->conf_mutex); +} + +void xradio_multicast_start_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, multicast_start_work); + long tmo = priv->join_dtim_period * (priv->beacon_int + 20) * HZ / 1024; + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + cancel_work_sync(&priv->multicast_stop_work); + if (!priv->aid0_bit_set) { + wsm_lock_tx(priv->hw_priv); + xradio_set_tim_impl(priv, true); + priv->aid0_bit_set = true; + mod_timer(&priv->mcast_timeout, jiffies + tmo); + wsm_unlock_tx(priv->hw_priv); + } +} + +void xradio_multicast_stop_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, multicast_stop_work); + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->aid0_bit_set) { + del_timer_sync(&priv->mcast_timeout); + wsm_lock_tx(priv->hw_priv); + priv->aid0_bit_set = false; + xradio_set_tim_impl(priv, false); + wsm_unlock_tx(priv->hw_priv); + } +} + +void xradio_mcast_timeout(unsigned long arg) +{ + struct xradio_vif *priv = (struct xradio_vif *)arg; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + ap_printk(XRADIO_DBG_WARN, "Multicast delivery timeout.\n"); + spin_lock_bh(&priv->ps_state_lock); + priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; + if (priv->tx_multicast) + xradio_bh_wakeup(xrwl_vifpriv_to_hwpriv(priv)); + spin_unlock_bh(&priv->ps_state_lock); +} + +int xradio_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, + u16 tid, u16 *ssn, u8 buf_size) +{ + /* Aggregation is implemented fully in firmware, + * including block ack negotiation. + * In case of AMPDU aggregation in RX direction + * re-ordering of packets takes place on host. mac80211 + * needs the ADDBA Request to setup reodering.mac80211 also + * sends ADDBA Response which is discarded in the driver as + * FW generates the ADDBA Response on its own.*/ + int ret; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + /* Just return OK to mac80211 */ + ret = 0; + break; + default: + ret = -ENOTSUPP; + } + return ret; +} + +/* ******************************************************************** */ +/* WSM callback */ +void xradio_suspend_resume(struct xradio_vif *priv, struct wsm_suspend_resume *arg) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#if 0 + ap_printk(XRADIO_DBG_MSG, "[AP] %s: %s\n", + arg->stop ? "stop" : "start", + arg->multicast ? "broadcast" : "unicast"); +#endif + if (arg->multicast) { + bool cancel_tmo = false; + spin_lock_bh(&priv->ps_state_lock); + if (arg->stop) { + priv->tx_multicast = false; + } else { + /* Firmware sends this indication every DTIM if there + * is a STA in powersave connected. There is no reason + * to suspend, following wakeup will consume much more + * power than it could be saved. */ + xradio_pm_stay_awake(&hw_priv->pm_state, (priv->join_dtim_period * + (priv->beacon_int + 20) * HZ / 1024)); + priv->tx_multicast = priv->aid0_bit_set && priv->buffered_multicasts; + if (priv->tx_multicast) { + cancel_tmo = true; + xradio_bh_wakeup(hw_priv); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (cancel_tmo) + del_timer_sync(&priv->mcast_timeout); + } else { + spin_lock_bh(&priv->ps_state_lock); + xradio_ps_notify(priv, arg->link_id, arg->stop); + spin_unlock_bh(&priv->ps_state_lock); + if (!arg->stop) + xradio_bh_wakeup(hw_priv); + } + return; +} + +/* ******************************************************************** */ +/* AP privates */ + +static int xradio_upload_beacon(struct xradio_vif *priv) +{ + int ret = 0; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_BEACON, + }; + struct ieee80211_mgmt *mgmt; + u8 *erp_inf, *ies, *ht_info; + u32 ies_len; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->vif->p2p || hw_priv->channel->band == IEEE80211_BAND_5GHZ) + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (SYS_WARN(!frame.skb)) + return -ENOMEM; + + mgmt = (void *)frame.skb->data; + ies = mgmt->u.beacon.variable; + ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt); + + ht_info = (u8 *)cfg80211_find_ie( WLAN_EID_HT_INFORMATION, ies, ies_len); + if (ht_info) { + /* Enable RIFS*/ + ht_info[3] |= 8; + } + + erp_inf = (u8 *)cfg80211_find_ie(WLAN_EID_ERP_INFO, ies, ies_len); + if (erp_inf) { + if (erp_inf[ERP_INFO_BYTE_OFFSET] + & WLAN_ERP_BARKER_PREAMBLE) + priv->erp_info |= WLAN_ERP_BARKER_PREAMBLE; + else + priv->erp_info &= ~WLAN_ERP_BARKER_PREAMBLE; + + if (erp_inf[ERP_INFO_BYTE_OFFSET] + & WLAN_ERP_NON_ERP_PRESENT) { + priv->erp_info |= WLAN_ERP_USE_PROTECTION; + priv->erp_info |= WLAN_ERP_NON_ERP_PRESENT; + } else { + priv->erp_info &= ~WLAN_ERP_USE_PROTECTION; + priv->erp_info &= ~WLAN_ERP_NON_ERP_PRESENT; + } + } + +#ifdef HIDDEN_SSID + if (priv->hidden_ssid) { + u8 *ssid_ie; + u8 ssid_len; + + ap_printk(XRADIO_DBG_NIY, "%s: hidden_ssid set\n", __func__); + ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + SYS_WARN(!ssid_ie); + ssid_len = ssid_ie[1]; + if (ssid_len) { + ap_printk(XRADIO_DBG_MSG, "hidden_ssid with zero content ssid\n"); + ssid_ie[1] = 0; + memmove(ssid_ie + 2, ssid_ie + 2 + ssid_len, + (ies + ies_len - + (ssid_ie + 2 + ssid_len))); + frame.skb->len -= ssid_len; + } else { + ap_printk(XRADIO_DBG_WARN, "hidden ssid with ssid len 0 not supported"); + dev_kfree_skb(frame.skb); + return -1; + } + } +#endif + + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + if (!ret) { +#ifdef PROBE_RESP_EXTRA_IE + ret = xradio_upload_proberesp(priv); +#else + /* TODO: Distille probe resp; remove TIM + * and other beacon-specific IEs */ + *(__le16 *)frame.skb->data = __cpu_to_le16( + IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_PROBE_RESP); + frame.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE; + /* TODO: Ideally probe response template should separately + configured by supplicant through openmac. This is a + temporary work-around known to fail p2p group info + attribute related tests + */ + if (0 /* priv->vif->p2p */) + ret = wsm_set_probe_responder(priv, true); + else { + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + SYS_WARN(wsm_set_probe_responder(priv, false)); + } +#endif + } + dev_kfree_skb(frame.skb); + + return ret; +} + +#ifdef PROBE_RESP_EXTRA_IE +static int xradio_upload_proberesp(struct xradio_vif *priv) +{ + int ret = 0; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE, + }; +#ifdef HIDDEN_SSID + u8 *ssid_ie; +#endif + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->vif->p2p || hw_priv->channel->band == IEEE80211_BAND_5GHZ) + frame.rate = WSM_TRANSMIT_RATE_6; + + frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif); + if (SYS_WARN(!frame.skb)) + return -ENOMEM; + +#ifdef HIDDEN_SSID + if (priv->hidden_ssid) { + int offset; + u8 ssid_len; + /* we are assuming beacon from upper layer will always contain + zero filled ssid for hidden ap. The beacon shall never have + ssid len = 0. + */ + + offset = offsetof(struct ieee80211_mgmt, u.probe_resp.variable); + ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, frame.skb->data + offset, + frame.skb->len - offset); + ssid_len = ssid_ie[1]; + if (ssid_len && (ssid_len == priv->ssid_length)) { + memcpy(ssid_ie + 2, priv->ssid, ssid_len); + } else { + ap_printk(XRADIO_DBG_ERROR, "%s: hidden ssid with mismatched ssid_len %d\n", + __func__, ssid_len); + dev_kfree_skb(frame.skb); + return -1; + } + } +#endif + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + SYS_WARN(wsm_set_probe_responder(priv, false)); + + dev_kfree_skb(frame.skb); + + return ret; +} +#endif + +static int xradio_upload_pspoll(struct xradio_vif *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PS_POLL, + .rate = 0xFF, + }; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif); + if (SYS_WARN(!frame.skb)) + return -ENOMEM; + ret = wsm_set_template_frame(xrwl_vifpriv_to_hwpriv(priv), &frame, priv->if_id); + dev_kfree_skb(frame.skb); + return ret; +} + +static int xradio_upload_null(struct xradio_vif *priv) +{ + int ret = 0; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_NULL, + .rate = 0xFF, + }; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif); + if (SYS_WARN(!frame.skb)) + return -ENOMEM; + + ret = wsm_set_template_frame(xrwl_vifpriv_to_hwpriv(priv), &frame, priv->if_id); + dev_kfree_skb(frame.skb); + return ret; +} + +static int xradio_upload_qosnull(struct xradio_vif *priv) +{ + struct ieee80211_qos_hdr* qos_null_template; + struct sk_buff* skb; + int ret = 0; + struct xradio_common *hw_priv =xrwl_vifpriv_to_hwpriv(priv); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_QOS_NULL, + .rate = 0xFF, + }; + if (!hw_priv) + ap_printk(XRADIO_DBG_ERROR,"%s: Cannot find xradio_common pointer!\n",__FUNCTION__); + /*set qos template*/ + skb = dev_alloc_skb(hw_priv->hw->extra_tx_headroom + sizeof(struct ieee80211_qos_hdr)); + if (!skb) { + ap_printk(XRADIO_DBG_ERROR,"%s: failed to allocate buffer for qos nullfunc template!\n",__FUNCTION__); + return -1; + } + skb_reserve(skb, hw_priv->hw->extra_tx_headroom); + qos_null_template = (struct ieee80211_qos_hdr *)skb_put(skb,sizeof(struct ieee80211_qos_hdr)); + memset(qos_null_template, 0, sizeof(struct ieee80211_qos_hdr)); + memcpy(qos_null_template->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); + memcpy(qos_null_template->addr2, priv->vif->addr, ETH_ALEN); + memcpy(qos_null_template->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); + qos_null_template->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_TODS); + frame.skb = skb; + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + dev_kfree_skb(frame.skb); + return ret; +} + +/* This API is nolonegr present in WSC */ +#if 0 +static int xradio_enable_beaconing(struct xradio_vif *priv, + bool enable) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_beacon_transmit transmit = { + .enableBeaconing = enable, + }; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + return wsm_beacon_transmit(hw_priv, &transmit, priv->if_id); +} +#endif + +static int xradio_start_ap(struct xradio_vif *priv) +{ + int ret; +#ifndef HIDDEN_SSID + const u8 *ssidie; + struct sk_buff *skb; + int offset; +#endif + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_start start = { + .mode = priv->vif->p2p ? WSM_START_MODE_P2P_GO : WSM_START_MODE_AP, + /* TODO:COMBO:Change once mac80211 support is available */ + .band = (hw_priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = hw_priv->channel->hw_value, + .beaconInterval = conf->beacon_int, + .DTIMPeriod = conf->dtim_period, + .preambleType = conf->use_short_preamble ? + WSM_JOIN_PREAMBLE_SHORT :WSM_JOIN_PREAMBLE_LONG, + .probeDelay = 100, + .basicRateSet = xradio_rate_mask_to_wsm(hw_priv, conf->basic_rates), +#ifdef P2P_MULTIVIF + .CTWindow = priv->vif->p2p ? 0xFFFFFFFF : 0, +#endif + }; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + +#ifdef TES_P2P_000B_EXTEND_INACTIVITY_CNT + ///w, TES_P2P_000B WorkAround: + ///w, when inactivity count of a peer device is zero, + ///w, which will reset while receiving a peer device frame, + ///w, firmware will disconnect with it. + ///w, due to some reason, such as scan/phy error, we miss these frame. + ///w, then we can't keep connection with peer device. + ///w, we set the min_inactivity value to large as WorkAround. + //min_inactivity be modified to 20, yangfh. + struct wsm_inactivity inactivity = { + .min_inactivity = 20, + .max_inactivity = 10, + }; +#else + struct wsm_inactivity inactivity = { + .min_inactivity = 9, + .max_inactivity = 1, + }; +#endif + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->if_id) + start.mode |= WSM_FLAG_MAC_INSTANCE_1; + else + start.mode &= ~WSM_FLAG_MAC_INSTANCE_1; + + hw_priv->connected_sta_cnt = 0; + +#ifndef HIDDEN_SSID + /* Get SSID */ + skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (SYS_WARN(!skb)) { + ap_printk(XRADIO_DBG_ERROR,"%s, ieee80211_beacon_get failed\n", __func__); + return -ENOMEM; + } + + offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + ssidie = cfg80211_find_ie(WLAN_EID_SSID, skb->data + offset, skb->len - offset); + + memset(priv->ssid, 0, sizeof(priv->ssid)); + if (ssidie) { + priv->ssid_length = ssidie[1]; + if (SYS_WARN(priv->ssid_length > sizeof(priv->ssid))) + priv->ssid_length = sizeof(priv->ssid); + memcpy(priv->ssid, &ssidie[2], priv->ssid_length); + } else { + priv->ssid_length = 0; + } + dev_kfree_skb(skb); +#endif + + priv->beacon_int = conf->beacon_int; + priv->join_dtim_period = conf->dtim_period; + memset(&priv->last_tim[0], 0, sizeof(priv->last_tim)); //yangfh + + start.ssidLength = priv->ssid_length; + memcpy(&start.ssid[0], priv->ssid, start.ssidLength); + + memset(&priv->link_id_db, 0, sizeof(priv->link_id_db)); + + ap_printk(XRADIO_DBG_NIY, "[AP] ch: %d(%d), bcn: %d(%d)," + "bss_rate: 0x%.8X, ssid: %.*s.\n", + start.channelNumber, start.band, + start.beaconInterval, start.DTIMPeriod, + start.basicRateSet, start.ssidLength, start.ssid); + ret = SYS_WARN(wsm_start(hw_priv, &start, priv->if_id)); + + if (!ret && priv->vif->p2p) { + ap_printk(XRADIO_DBG_NIY,"[AP] Setting p2p powersave configuration.\n"); + SYS_WARN(wsm_set_p2p_ps_modeinfo(hw_priv, + &priv->p2p_ps_modeinfo, priv->if_id)); +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //xradio_notify_noa(priv, XRADIO_NOA_NOTIFICATION_DELAY); +#endif + } + + /*Set Inactivity time*/ + if(!(strstr(&start.ssid[0], "6.1.12"))) { + wsm_set_inactivity(hw_priv, &inactivity, priv->if_id); + } + if (!ret) { +#ifndef AP_AGGREGATE_FW_FIX + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID, + XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, priv->if_id)); +#else + if ((priv->if_id ==1) && !hw_priv->is_go_thru_go_neg) + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, //modified for WFD by yangfh + XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, priv->if_id)); + else + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID, + XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID, priv->if_id)); +#endif + priv->join_status = XRADIO_JOIN_STATUS_AP; + /* xradio_update_filtering(priv); */ + } + SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; + ap_printk(XRADIO_DBG_WARN, "vif%d, AP/GO mode THROTTLE=%d\n", priv->if_id, + priv->if_id==0?hw_priv->vif0_throttle:hw_priv->vif1_throttle); + return ret; +} + +static int xradio_update_beaconing(struct xradio_vif *priv) +{ + struct ieee80211_bss_conf *conf = &priv->vif->bss_conf; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_reset reset = { + .link_id = 0, + .reset_statistics = true, + }; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->mode == NL80211_IFTYPE_AP) { + /* TODO: check if changed channel, band */ + if (priv->join_status != XRADIO_JOIN_STATUS_AP || + priv->beacon_int != conf->beacon_int) { + ap_printk(XRADIO_DBG_WARN, "ap restarting!\n"); + wsm_lock_tx(hw_priv); + if (priv->join_status != XRADIO_JOIN_STATUS_PASSIVE) + SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id)); + priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; + SYS_WARN(xradio_start_ap(priv)); + wsm_unlock_tx(hw_priv); + } else + ap_printk(XRADIO_DBG_NIY, "ap started join_status: %d\n", priv->join_status); + } + return 0; +} + +int xradio_find_link_id(struct xradio_vif *priv, const u8 *mac) +{ + int i, ret = 0; + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { + if (!memcmp(mac, priv->link_id_db[i].mac, ETH_ALEN) && + priv->link_id_db[i].status) { + priv->link_id_db[i].timestamp = jiffies; + ret = i + 1; + break; + } + } + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +int xradio_alloc_link_id(struct xradio_vif *priv, const u8 *mac) +{ + int i, ret = 0; + unsigned long max_inactivity = 0; + unsigned long now = jiffies; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { + if (!priv->link_id_db[i].status) { + ret = i + 1; + break; + } else if (priv->link_id_db[i].status != XRADIO_LINK_HARD && + !hw_priv->tx_queue_stats.link_map_cache[i + 1]) { + unsigned long inactivity = now - priv->link_id_db[i].timestamp; + if (inactivity < max_inactivity) + continue; + max_inactivity = inactivity; + ret = i + 1; + } + } + if (ret) { + struct xradio_link_entry *entry = &priv->link_id_db[ret - 1]; + ap_printk(XRADIO_DBG_NIY, "STA added, link_id: %d\n", ret); + entry->status = XRADIO_LINK_RESERVE; + memcpy(&entry->mac, mac, ETH_ALEN); + memset(&entry->buffered, 0, XRADIO_MAX_TID); + skb_queue_head_init(&entry->rx_queue); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + } else { + ap_printk(XRADIO_DBG_WARN, "Early: no more link IDs available.\n"); + } + + spin_unlock_bh(&priv->ps_state_lock); + return ret; +} + +void xradio_link_id_work(struct work_struct *work) +{ + struct xradio_vif *priv = container_of(work, struct xradio_vif, link_id_work); + struct xradio_common *hw_priv = priv->hw_priv; + + wsm_flush_tx(hw_priv); + xradio_link_id_gc_work(&priv->link_id_gc_work.work); + wsm_unlock_tx(hw_priv); +} + +void xradio_link_id_gc_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, link_id_gc_work.work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_map_link map_link = { + .link_id = 0, + }; + unsigned long now = jiffies; + unsigned long next_gc = -1; + long ttl; + bool need_reset; + u32 mask; + int i; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->join_status != XRADIO_JOIN_STATUS_AP) + return; + + wsm_lock_tx(hw_priv); + spin_lock_bh(&priv->ps_state_lock); + for (i = 0; i < MAX_STA_IN_AP_MODE; ++i) { + need_reset = false; + mask = BIT(i + 1); + if (priv->link_id_db[i].status == XRADIO_LINK_RESERVE || + (priv->link_id_db[i].status == XRADIO_LINK_HARD && + !(priv->link_id_map & mask))) { + if (priv->link_id_map & mask) { + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + need_reset = true; + } + priv->link_id_map |= mask; + if (priv->link_id_db[i].status != XRADIO_LINK_HARD) + priv->link_id_db[i].status = XRADIO_LINK_SOFT; + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + if (need_reset) { + SYS_WARN(xrwl_unmap_link(priv, i + 1)); + } + map_link.link_id = i + 1; + SYS_WARN(wsm_map_link(hw_priv, &map_link, priv->if_id)); + next_gc = min(next_gc, XRADIO_LINK_ID_GC_TIMEOUT); + spin_lock_bh(&priv->ps_state_lock); + } else if (priv->link_id_db[i].status == XRADIO_LINK_SOFT) { + ttl = priv->link_id_db[i].timestamp - now + XRADIO_LINK_ID_GC_TIMEOUT; + if (ttl <= 0) { + need_reset = true; + priv->link_id_db[i].status = XRADIO_LINK_OFF; + priv->link_id_map &= ~mask; + priv->sta_asleep_mask &= ~mask; + priv->pspoll_mask &= ~mask; + memset(map_link.mac_addr, 0, ETH_ALEN); + spin_unlock_bh(&priv->ps_state_lock); + SYS_WARN(xrwl_unmap_link(priv, i + 1)); + spin_lock_bh(&priv->ps_state_lock); + } else { + next_gc = min_t(unsigned long, next_gc, ttl); + } +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + } else if (priv->link_id_db[i].status == XRADIO_LINK_RESET || + priv->link_id_db[i].status == XRADIO_LINK_RESET_REMAP) { + int status = priv->link_id_db[i].status; + priv->link_id_db[i].status = XRADIO_LINK_OFF; + priv->link_id_db[i].timestamp = now; + spin_unlock_bh(&priv->ps_state_lock); + SYS_WARN(xrwl_unmap_link(priv, i + 1)); + if (status == XRADIO_LINK_RESET_REMAP) { + memcpy(map_link.mac_addr, priv->link_id_db[i].mac, ETH_ALEN); + map_link.link_id = i + 1; + SYS_WARN(wsm_map_link(hw_priv, &map_link, priv->if_id)); + next_gc = min(next_gc, XRADIO_LINK_ID_GC_TIMEOUT); + priv->link_id_db[i].status = priv->link_id_db[i].prev_status; + } + spin_lock_bh(&priv->ps_state_lock); +#endif + } + if (need_reset) { + skb_queue_purge(&priv->link_id_db[i].rx_queue); + ap_printk(XRADIO_DBG_NIY, "STA removed, link_id: %d\n", i + 1); + } + } + spin_unlock_bh(&priv->ps_state_lock); + if (next_gc != -1) + queue_delayed_work(hw_priv->workqueue, &priv->link_id_gc_work, next_gc); + wsm_unlock_tx(hw_priv); +} + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) +#if 0 +void xradio_notify_noa(struct xradio_vif *priv, int delay) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct cfg80211_p2p_ps p2p_ps = {0}; + struct wsm_p2p_ps_modeinfo *modeinfo; + modeinfo = &priv->p2p_ps_modeinfo; + + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (priv->join_status != XRADIO_JOIN_STATUS_AP) + return; + + if (delay) + msleep(delay); + + if (!SYS_WARN(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo))) { +#if defined(CONFIG_XRADIO_DEBUG) + print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ", DUMP_PREFIX_NONE, + (u8 *)modeinfo, sizeof(*modeinfo)); +#endif /* CONFIG_XRADIO_DEBUG */ + p2p_ps.opp_ps = !!(modeinfo->oppPsCTWindow & BIT(7)); + p2p_ps.ctwindow = modeinfo->oppPsCTWindow & (~BIT(7)); + p2p_ps.count = modeinfo->count; + p2p_ps.start = __le32_to_cpu(modeinfo->startTime); + p2p_ps.duration = __le32_to_cpu(modeinfo->duration); + p2p_ps.interval = __le32_to_cpu(modeinfo->interval); + p2p_ps.index = modeinfo->reserved; + + ieee80211_p2p_noa_notify(priv->vif, &p2p_ps, GFP_KERNEL); + } +} +#endif +#endif +int xrwl_unmap_link(struct xradio_vif *priv, int link_id) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + int ret = 0; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (is_hardware_xradio(hw_priv)) { + struct wsm_map_link maplink = { + .link_id = link_id, + .unmap = true, + }; + if (link_id) + memcpy(&maplink.mac_addr[0], priv->link_id_db[link_id - 1].mac, ETH_ALEN); + return wsm_map_link(hw_priv, &maplink, priv->if_id); + } else { + struct wsm_reset reset = { + .link_id = link_id, + .reset_statistics = true, + }; + ret = wsm_reset(hw_priv, &reset, priv->if_id); + SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); + return ret; + } +} +#ifdef AP_HT_CAP_UPDATE +void xradio_ht_info_update_work(struct work_struct *work) +{ + struct sk_buff *skb; + struct ieee80211_mgmt *mgmt; + u8 *ht_info, *ies; + u32 ies_len; + struct xradio_vif *priv = + container_of(work, struct xradio_vif, ht_info_update_work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_update_ie update_ie = { + .what = WSM_UPDATE_IE_BEACON, + .count = 1, + }; + ap_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + skb = ieee80211_beacon_get(priv->hw, priv->vif); + if (SYS_WARN(!skb)) + return; + + mgmt = (void *)skb->data; + ies = mgmt->u.beacon.variable; + ies_len = skb->len - (u32)(ies - (u8 *)mgmt); + ht_info= (u8 *)cfg80211_find_ie( WLAN_EID_HT_INFORMATION, ies, ies_len); + if(ht_info && priv->ht_info == HT_INFO_MASK) { + ht_info[HT_INFO_OFFSET] |= 0x11; + update_ie.ies = ht_info; + update_ie.length = HT_INFO_IE_LEN; + SYS_WARN(wsm_update_ie(hw_priv, &update_ie, priv->if_id)); + } + dev_kfree_skb(skb); +} +#endif diff --git a/drivers/net/wireless/xradio/ap.h b/drivers/net/wireless/xradio/ap.h new file mode 100644 index 0000000..be46c00 --- /dev/null +++ b/drivers/net/wireless/xradio/ap.h @@ -0,0 +1,62 @@ +/* + * STA and AP APIs for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef AP_H_INCLUDED +#define AP_H_INCLUDED + +#define XRADIO_NOA_NOTIFICATION_DELAY 10 + +#ifdef AP_HT_CAP_UPDATE +#define HT_INFO_OFFSET 4 +#define HT_INFO_MASK 0x0011 +#define HT_INFO_IE_LEN 22 +#endif + +int xradio_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta, + bool set); +int xradio_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +int xradio_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void xradio_sta_notify(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + enum sta_notify_cmd notify_cmd, + struct ieee80211_sta *sta); +void xradio_bss_info_changed(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, + u32 changed); +int xradio_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); + +void xradio_suspend_resume(struct xradio_vif *priv, + struct wsm_suspend_resume *arg); +void xradio_set_tim_work(struct work_struct *work); +void xradio_set_cts_work(struct work_struct *work); +void xradio_multicast_start_work(struct work_struct *work); +void xradio_multicast_stop_work(struct work_struct *work); +void xradio_mcast_timeout(unsigned long arg); +int xradio_find_link_id(struct xradio_vif *priv, const u8 *mac); +int xradio_alloc_link_id(struct xradio_vif *priv, const u8 *mac); +void xradio_link_id_work(struct work_struct *work); +void xradio_link_id_gc_work(struct work_struct *work); +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) +/*in linux3.4 mac,it does't have the noa pass*/ +//void xradio_notify_noa(struct xradio_vif *priv, int delay); +#endif +int xrwl_unmap_link(struct xradio_vif *priv, int link_id); +#ifdef AP_HT_CAP_UPDATE +void xradio_ht_info_update_work(struct work_struct *work); +#endif + +#endif diff --git a/drivers/net/wireless/xradio/bh.c b/drivers/net/wireless/xradio/bh.c new file mode 100644 index 0000000..abb3610 --- /dev/null +++ b/drivers/net/wireless/xradio/bh.c @@ -0,0 +1,962 @@ +/* + * Data Transmission thread implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include + +#include "xradio.h" +#include "bh.h" +#include "hwio.h" +#include "wsm.h" +#include "sbus.h" + +/* TODO: Verify these numbers with WSM specification. */ +#define DOWNLOAD_BLOCK_SIZE_WR (0x1000 - 4) +/* an SPI message cannot be bigger than (2"12-1)*2 bytes + * "*2" to cvt to bytes */ +#define MAX_SZ_RD_WR_BUFFERS (DOWNLOAD_BLOCK_SIZE_WR*2) +#define PIGGYBACK_CTRL_REG (2) +#define EFFECTIVE_BUF_SIZE (MAX_SZ_RD_WR_BUFFERS - PIGGYBACK_CTRL_REG) + +/* Suspend state privates */ +enum xradio_bh_pm_state { + XRADIO_BH_RESUMED = 0, + XRADIO_BH_SUSPEND, + XRADIO_BH_SUSPENDED, + XRADIO_BH_RESUME, +}; +typedef int (*xradio_wsm_handler)(struct xradio_common *hw_priv, u8 *data, size_t size); + +#ifdef MCAST_FWDING +int wsm_release_buffer_to_fw(struct xradio_vif *priv, int count); +#endif +static int xradio_bh(void *arg); + +int xradio_register_bh(struct xradio_common *hw_priv) +{ + int err = 0; + struct sched_param param = { .sched_priority = 1 }; + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + SYS_BUG(hw_priv->bh_thread); + atomic_set(&hw_priv->bh_rx, 0); + atomic_set(&hw_priv->bh_tx, 0); + atomic_set(&hw_priv->bh_term, 0); + atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED); + hw_priv->buf_id_tx = 0; + hw_priv->buf_id_rx = 0; +#ifdef BH_USE_SEMAPHORE + sema_init(&hw_priv->bh_sem, 0); + atomic_set(&hw_priv->bh_wk, 0); +#else + init_waitqueue_head(&hw_priv->bh_wq); +#endif + init_waitqueue_head(&hw_priv->bh_evt_wq); + + hw_priv->bh_thread = kthread_create(&xradio_bh, hw_priv, XRADIO_BH_THREAD); + if (IS_ERR(hw_priv->bh_thread)) { + err = PTR_ERR(hw_priv->bh_thread); + hw_priv->bh_thread = NULL; + } else { + SYS_WARN(sched_setscheduler(hw_priv->bh_thread, SCHED_FIFO, ¶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; + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (SYS_WARN(!thread)) + return; + + hw_priv->bh_thread = NULL; + kthread_stop(thread); +#ifdef HAS_PUT_TASK_STRUCT + put_task_struct(thread); +#endif + bh_printk(XRADIO_DBG_NIY, "Unregister success.\n"); +} + +void xradio_irq_handler(struct xradio_common *hw_priv) +{ + + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + DBG_BH_IRQ_ADD; + if (/* SYS_WARN */(hw_priv->bh_error)) + return; +#ifdef BH_USE_SEMAPHORE + atomic_add(1, &hw_priv->bh_rx); + if (atomic_add_return(1, &hw_priv->bh_wk) == 1) { + up(&hw_priv->bh_sem); + } +#else + if (atomic_add_return(1, &hw_priv->bh_rx) == 1) { + wake_up(&hw_priv->bh_wq); + } +#endif + +} + +void xradio_bh_wakeup(struct xradio_common *hw_priv) +{ + bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + if (SYS_WARN(hw_priv->bh_error)) + return; +#ifdef BH_USE_SEMAPHORE + atomic_add(1, &hw_priv->bh_tx); + if (atomic_add_return(1, &hw_priv->bh_wk) == 1) { + up(&hw_priv->bh_sem); + } +#else + if (atomic_add_return(1, &hw_priv->bh_tx) == 1) { + wake_up(&hw_priv->bh_wq); + } +#endif +} + +int xradio_bh_suspend(struct xradio_common *hw_priv) +{ + +#ifdef MCAST_FWDING + int i =0; + struct xradio_vif *priv = NULL; +#endif + + bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + if (hw_priv->bh_error) { + return -EINVAL; + } + +#ifdef MCAST_FWDING + xradio_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if ( (priv->multicast_filter.enable) + && (priv->join_status == XRADIO_JOIN_STATUS_AP) ) { + wsm_release_buffer_to_fw(priv, + (hw_priv->wsm_caps.numInpChBufs - 1)); + break; + } + } +#endif + + atomic_set(&hw_priv->bh_suspend, XRADIO_BH_SUSPEND); +#ifdef BH_USE_SEMAPHORE + up(&hw_priv->bh_sem); +#else + wake_up(&hw_priv->bh_wq); +#endif + return wait_event_timeout(hw_priv->bh_evt_wq, (hw_priv->bh_error || + XRADIO_BH_SUSPENDED == atomic_read(&hw_priv->bh_suspend)), + 1 * HZ)? 0 : -ETIMEDOUT; +} + +int xradio_bh_resume(struct xradio_common *hw_priv) +{ + +#ifdef MCAST_FWDING + int ret; + int i =0; + struct xradio_vif *priv =NULL; +#endif + + bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + if (hw_priv->bh_error) { + return -EINVAL; + } + + atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUME); +#ifdef BH_USE_SEMAPHORE + up(&hw_priv->bh_sem); +#else + wake_up(&hw_priv->bh_wq); +#endif + +#ifdef MCAST_FWDING + ret = wait_event_timeout(hw_priv->bh_evt_wq, (hw_priv->bh_error || + XRADIO_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)) + ,1 * HZ)? 0 : -ETIMEDOUT; + + xradio_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if ((priv->join_status == XRADIO_JOIN_STATUS_AP) && + (priv->multicast_filter.enable)) { + u8 count = 0; + SYS_WARN(wsm_request_buffer_request(priv, &count)); + bh_printk(XRADIO_DBG_NIY, "Reclaim Buff %d \n",count); + break; + } + } + + return ret; +#else + return wait_event_timeout(hw_priv->bh_evt_wq,hw_priv->bh_error || + (XRADIO_BH_RESUMED == atomic_read(&hw_priv->bh_suspend)), + 1 * HZ) ? 0 : -ETIMEDOUT; +#endif + +} + +static inline void wsm_alloc_tx_buffer(struct xradio_common *hw_priv) +{ + ++hw_priv->hw_bufs_used; +} + +int wsm_release_tx_buffer(struct xradio_common *hw_priv, int count) +{ + int ret = 0; + int hw_bufs_used = hw_priv->hw_bufs_used; + bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + + hw_priv->hw_bufs_used -= count; + if (SYS_WARN(hw_priv->hw_bufs_used < 0)) { + /* Tx data patch stops when all but one hw buffers are used. + So, re-start tx path in case we find hw_bufs_used equals + numInputChBufs - 1. + */ + bh_printk(XRADIO_DBG_ERROR,"%s, hw_bufs_used=%d, count=%d.\n", + __func__, hw_priv->hw_bufs_used, count); + ret = -1; + } else if (hw_bufs_used >= (hw_priv->wsm_caps.numInpChBufs - 1)) + ret = 1; + if (!hw_priv->hw_bufs_used) + wake_up(&hw_priv->bh_evt_wq); + return ret; +} + +int wsm_release_vif_tx_buffer(struct xradio_common *hw_priv, int if_id, int count) +{ + int ret = 0; + bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + + hw_priv->hw_bufs_used_vif[if_id] -= count; + if (!hw_priv->hw_bufs_used_vif[if_id]) + wake_up(&hw_priv->bh_evt_wq); + + if (SYS_WARN(hw_priv->hw_bufs_used_vif[if_id] < 0)) + ret = -1; + return ret; +} +#ifdef MCAST_FWDING +int wsm_release_buffer_to_fw(struct xradio_vif *priv, int count) +{ + int i; + u8 flags; + struct wsm_buf *buf; + size_t buf_len; + struct wsm_hdr *wsm; + struct xradio_common *hw_priv = priv->hw_priv; + bh_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + + if (priv->join_status != XRADIO_JOIN_STATUS_AP) { + return 0; + } + bh_printk(XRADIO_DBG_NIY, "Rel buffer to FW %d, %d\n", count, hw_priv->hw_bufs_used); + + for (i = 0; i < count; i++) { + if ((hw_priv->hw_bufs_used + 1) < hw_priv->wsm_caps.numInpChBufs) { + flags = i ? 0: 0x1; + + wsm_alloc_tx_buffer(hw_priv); + buf = &hw_priv->wsm_release_buf[i]; + buf_len = buf->data - buf->begin; + + /* Add sequence number */ + wsm = (struct wsm_hdr *)buf->begin; + SYS_BUG(buf_len < sizeof(*wsm)); + + wsm->id &= __cpu_to_le32(~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= cpu_to_le32(WSM_TX_SEQ(hw_priv->wsm_tx_seq)); + + bh_printk(XRADIO_DBG_NIY, "REL %d\n", hw_priv->wsm_tx_seq); + if (SYS_WARN(xradio_data_write(hw_priv, buf->begin, buf_len))) { + break; + } + hw_priv->buf_released = 1; + hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; + } else + break; + } + + if (i == count) { + return 0; + } + + /* Should not be here */ + bh_printk(XRADIO_DBG_ERROR,"Error, Less HW buf %d,%d.\n", + hw_priv->hw_bufs_used, hw_priv->wsm_caps.numInpChBufs); + SYS_WARN(1); + return -1; +} +#endif + +/* reserve a packet for the case dev_alloc_skb failed in bh.*/ +int xradio_init_resv_skb(struct xradio_common *hw_priv) +{ + int len = (SDIO_BLOCK_SIZE<<2) + WSM_TX_EXTRA_HEADROOM + \ + 8 + 12; /* TKIP IV + ICV and MIC */ + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + hw_priv->skb_reserved = xr_alloc_skb(len); + if (hw_priv->skb_reserved) { + hw_priv->skb_resv_len = len; + } else { + bh_printk(XRADIO_DBG_WARN,"%s xr_alloc_skb failed(%d)\n", + __func__, len); + } + return 0; +} + +void xradio_deinit_resv_skb(struct xradio_common *hw_priv) +{ + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + if (hw_priv->skb_reserved) { + dev_kfree_skb(hw_priv->skb_reserved); + hw_priv->skb_reserved = NULL; + hw_priv->skb_resv_len = 0; + } +} + +int xradio_realloc_resv_skb(struct xradio_common *hw_priv, + struct sk_buff *skb) +{ + if (!hw_priv->skb_reserved && hw_priv->skb_resv_len) { + hw_priv->skb_reserved = xr_alloc_skb(hw_priv->skb_resv_len); + if (!hw_priv->skb_reserved) { + hw_priv->skb_reserved = skb; + bh_printk(XRADIO_DBG_WARN, "%s xr_alloc_skb failed(%d)\n", + __FUNCTION__, hw_priv->skb_resv_len); + return -1; + } + } + return 0; /* realloc sbk success, deliver to upper.*/ +} + +static inline struct sk_buff *xradio_get_resv_skb(struct xradio_common *hw_priv, + size_t len) +{ struct sk_buff *skb = NULL; + if (hw_priv->skb_reserved && len <= hw_priv->skb_resv_len) { + skb = hw_priv->skb_reserved; + hw_priv->skb_reserved = NULL; + } + return skb; +} + +static inline int xradio_put_resv_skb(struct xradio_common *hw_priv, + struct sk_buff *skb) +{ + if (!hw_priv->skb_reserved && hw_priv->skb_resv_len) { + hw_priv->skb_reserved = skb; + return 0; + } + return 1; /* sbk not put to reserve*/ +} + +static struct sk_buff *xradio_get_skb(struct xradio_common *hw_priv, size_t len) +{ + struct sk_buff *skb = NULL; + size_t alloc_len = (len > SDIO_BLOCK_SIZE) ? len : SDIO_BLOCK_SIZE; + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + /* TKIP IV + TKIP ICV and MIC - Piggyback.*/ + alloc_len += WSM_TX_EXTRA_HEADROOM + 8 + 12- 2; + if (len > SDIO_BLOCK_SIZE || !hw_priv->skb_cache) { + skb = xr_alloc_skb(alloc_len); + /* In AP mode RXed SKB can be looped back as a broadcast. + * Here we reserve enough space for headers. */ + if (skb) { + skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */ + - WSM_RX_EXTRA_HEADROOM); + } else { + skb = xradio_get_resv_skb(hw_priv, alloc_len); + if (skb) { + bh_printk(XRADIO_DBG_WARN,"%s get skb_reserved(%d)!\n", + __FUNCTION__, alloc_len); + skb_reserve(skb, WSM_TX_EXTRA_HEADROOM + 8 /* TKIP IV */ + - WSM_RX_EXTRA_HEADROOM); + } else { + bh_printk(XRADIO_DBG_ERROR,"%s xr_alloc_skb failed(%d)!\n", + __FUNCTION__, alloc_len); + } + } + } else { + skb = hw_priv->skb_cache; + hw_priv->skb_cache = NULL; + } + return skb; +} + +static void xradio_put_skb(struct xradio_common *hw_priv, struct sk_buff *skb) +{ + bh_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + if (hw_priv->skb_cache) + dev_kfree_skb(skb); + else + hw_priv->skb_cache = skb; +} + +static int xradio_bh_read_ctrl_reg(struct xradio_common *hw_priv, + u16 *ctrl_reg) +{ + int ret; + ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, ctrl_reg); + if (ret) { + ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, ctrl_reg); + if (ret) { + hw_priv->bh_error = 1; + bh_printk(XRADIO_DBG_ERROR, "Failed to read control register.\n"); + } + } + + return ret; +} + +static int xradio_device_wakeup(struct xradio_common *hw_priv) +{ + u16 ctrl_reg; + int ret, i=0; + + bh_printk(XRADIO_DBG_MSG, "%s\n", __FUNCTION__); + + /* To force the device to be always-on, the host sets WLAN_UP to 1 */ + ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT); + if (SYS_WARN(ret)) + return ret; + + ret = xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if (SYS_WARN(ret)) + return ret; + + /* If the device returns WLAN_RDY as 1, the device is active and will + * remain active. */ + while (!(ctrl_reg & HIF_CTRL_RDY_BIT) && i < 500) { + ret = xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + msleep(1); + i++; + } + if (unlikely(i >= 500)) { + bh_printk(XRADIO_DBG_ERROR, "Device cannot wakeup.\n"); + return -1; + } else if (unlikely(i >= 50)) + bh_printk(XRADIO_DBG_WARN, "Device wakeup time=%dms.\n", i); + bh_printk(XRADIO_DBG_NIY, "Device awake, t=%dms.\n", i); + return 1; +} + +/* Must be called from BH thraed. */ +void xradio_enable_powersave(struct xradio_vif *priv, + bool enable) +{ + priv->powersave_enabled = enable; + bh_printk(XRADIO_DBG_NIY, "Powerave is %s.\n", enable ? "enabled" : "disabled"); +} + + +static int xradio_bh(void *arg) +{ + struct xradio_common *hw_priv = arg; + struct sk_buff *skb_rx = NULL; + size_t read_len = 0; + int rx = 0, tx = 0, term, suspend; + struct wsm_hdr *wsm; + size_t wsm_len; + int wsm_id; + u8 wsm_seq; + int rx_resync = 1; + u16 ctrl_reg = 0; + int tx_allowed; + int pending_tx = 0; + int tx_burst; + int rx_burst = 0; + long status; + u32 dummy; + int vif_selected; + + bh_printk(XRADIO_DBG_MSG, "%s\n", __FUNCTION__); + + for (;;) { + /* Check if devices can sleep, and set time to wait for interrupt. */ + if (!hw_priv->hw_bufs_used && !pending_tx && + hw_priv->powersave_enabled && !hw_priv->device_can_sleep && + !atomic_read(&hw_priv->recent_scan) && + atomic_read(&hw_priv->bh_rx) == 0 && + atomic_read(&hw_priv->bh_tx) == 0) { + bh_printk(XRADIO_DBG_MSG, "Device idle, can sleep.\n"); + SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0)); + hw_priv->device_can_sleep = true; + status = HZ/8; //125ms + } else if (hw_priv->hw_bufs_used) { + /* don't wait too long if some frames to confirm + * and miss interrupt.*/ + status = HZ/20; //50ms. + } else { + status = HZ/8; //125ms + } + + /* Dummy Read for SDIO retry mechanism*/ + if (atomic_read(&hw_priv->bh_rx) == 0 && + atomic_read(&hw_priv->bh_tx) == 0) { + xradio_reg_read(hw_priv, HIF_CONFIG_REG_ID, &dummy, sizeof(dummy)); + } + /* If a packet has already been txed to the device then read the + * control register for a probable interrupt miss before going + * further to wait for interrupt; if the read length is non-zero + * then it means there is some data to be received */ + if (hw_priv->hw_bufs_used) { + xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) { + DBG_BH_FIX_RX_ADD; + rx = 1; + goto data_proc; + } + } + + /* Wait for Events in HZ/8 */ +#ifdef BH_USE_SEMAPHORE + rx = atomic_xchg(&hw_priv->bh_rx, 0); + tx = atomic_xchg(&hw_priv->bh_tx, 0); + suspend = pending_tx ? 0 : atomic_read(&hw_priv->bh_suspend); + term = kthread_should_stop(); + if (!(rx || tx || term || suspend || hw_priv->bh_error)) { + atomic_set(&hw_priv->bh_wk, 0); + status = (long)(down_timeout(&hw_priv->bh_sem, status) != -ETIME); + //if (status && !atomic_read(&hw_priv->bh_rx) && !atomic_read(&hw_priv->bh_tx)) + // bh_printk(XRADIO_DBG_ALWY, "bh+\n"); + } +#else + status = wait_event_interruptible_timeout(hw_priv->bh_wq, ({ + rx = atomic_xchg(&hw_priv->bh_rx, 0); + tx = atomic_xchg(&hw_priv->bh_tx, 0); + term = kthread_should_stop(); + suspend = pending_tx ? 0 : atomic_read(&hw_priv->bh_suspend); + (rx || tx || term || suspend || hw_priv->bh_error);}), + status); +#endif + + /* 0--bh is going to be shut down */ + if(term) { + bh_printk(XRADIO_DBG_MSG, "xradio_bh exit!\n"); + break; + } + /* 1--An fatal error occurs */ + if (status < 0 || hw_priv->bh_error) { + bh_printk(XRADIO_DBG_ERROR, "bh_error=%d, status=%ld\n", + hw_priv->bh_error, status); + hw_priv->bh_error = __LINE__; + break; + } + + /* 2--Wait for interrupt time out */ + if (!status) { + /* Check if miss interrupt. */ + xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) { + bh_printk(XRADIO_DBG_WARN, "miss interrupt!\n" ); + DBG_BH_MISS_ADD; + rx = 1; + goto data_proc; + } + + /* There are some frames to be confirmed. */ + if (hw_priv->hw_bufs_used) { + long timeout = 0; + bool pending = 0; + bh_printk(XRADIO_DBG_NIY, "Need confirm:%d!\n", hw_priv->hw_bufs_used); + /* Check if frame transmission is timed out. */ + pending = xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS, + hw_priv->pending_frame_id, &timeout); + /* There are some frames confirm time out. */ + if (pending && timeout < 0) { + bh_printk(XRADIO_DBG_ERROR, "query_txpkt_timeout:%ld!\n", timeout); + hw_priv->bh_error = __LINE__; + break; + } + rx = 1; /* Go to check rx again. */ + } else if (!pending_tx){ + if (hw_priv->powersave_enabled && !hw_priv->device_can_sleep && !atomic_read(&hw_priv->recent_scan)) { + /* Device is idle, we can go to sleep. */ + bh_printk(XRADIO_DBG_MSG, "Device idle(timeout), can sleep.\n"); + SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0)); + hw_priv->device_can_sleep = true; + } + continue; + } + + /* 3--Host suspend request. */ + } else if (suspend) { + bh_printk(XRADIO_DBG_NIY, "Host suspend request.\n"); + /* Check powersave setting again. */ + if (hw_priv->powersave_enabled) { + bh_printk(XRADIO_DBG_MSG, + "Device idle(host suspend), can sleep.\n"); + SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, 0)); + hw_priv->device_can_sleep = true; + } + + /* bh thread go to suspend. */ + atomic_set(&hw_priv->bh_suspend, XRADIO_BH_SUSPENDED); + wake_up(&hw_priv->bh_evt_wq); +#ifdef BH_USE_SEMAPHORE + do { + status = down_interruptible(&hw_priv->bh_sem); + } while (!status && XRADIO_BH_RESUME != atomic_read(&hw_priv->bh_suspend)); + if (XRADIO_BH_RESUME != atomic_read(&hw_priv->bh_suspend)) + status = -1; + else + status = 0; +#else + status = wait_event_interruptible(hw_priv->bh_wq, + XRADIO_BH_RESUME == atomic_read(&hw_priv->bh_suspend)); +#endif + if (status < 0) { + bh_printk(XRADIO_DBG_ERROR,"ERR: Failed to wait for resume: %ld.\n", status); + hw_priv->bh_error = __LINE__; + break; + } + bh_printk(XRADIO_DBG_NIY, "Host resume.\n"); + atomic_set(&hw_priv->bh_suspend, XRADIO_BH_RESUMED); + wake_up(&hw_priv->bh_evt_wq); + atomic_add(1, &hw_priv->bh_rx); + continue; + } + /* query stuck frames in firmware. */ + if (atomic_xchg(&hw_priv->query_cnt, 0)){ + if(schedule_work(&hw_priv->query_work) <= 0) + atomic_add(1, &hw_priv->query_cnt); + } + + /* 4--Rx & Tx process. */ +data_proc: + tx += pending_tx; + pending_tx = 0; + + if (rx) { + size_t alloc_len; + u8 *data; + /* Check ctrl_reg again. */ + if(!(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK)) + if (SYS_WARN(xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg))) { + hw_priv->bh_error = __LINE__; + break; + } +rx: + read_len = (ctrl_reg & HIF_CTRL_NEXT_LEN_MASK)<<1; //read_len=ctrl_reg*2. + if (!read_len) { + rx_burst = 0; + goto tx; + } + if (SYS_WARN((read_len < sizeof(struct wsm_hdr)) || + (read_len > EFFECTIVE_BUF_SIZE))) { + bh_printk(XRADIO_DBG_ERROR, "ERR: Invalid read len: %d", read_len); + hw_priv->bh_error = __LINE__; + break; + } + + /* Add SIZE of PIGGYBACK reg (CONTROL Reg) + * to the NEXT Message length + 2 Bytes for SKB */ + read_len = read_len + 2; + +#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES) + alloc_len = hw_priv->sbus_ops->align_size(hw_priv->sbus_priv, read_len); +#else + /* Platform's SDIO workaround */ + alloc_len = read_len & ~(SDIO_BLOCK_SIZE - 1); + if (read_len & (SDIO_BLOCK_SIZE - 1)) + alloc_len += SDIO_BLOCK_SIZE; +#endif /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */ + /* Check if not exceeding XRADIO capabilities */ + if (WARN_ON_ONCE(alloc_len > EFFECTIVE_BUF_SIZE)) { + bh_printk(XRADIO_DBG_MSG, "ERR: Read aligned len: %d\n", alloc_len); + } + + /* Get skb buffer. */ + skb_rx = xradio_get_skb(hw_priv, alloc_len); + if (SYS_WARN(!skb_rx)) { + bh_printk(XRADIO_DBG_ERROR, "ERR: xradio_get_skb failed.\n"); + hw_priv->bh_error = __LINE__; + break; + } + skb_trim(skb_rx, 0); + skb_put(skb_rx, read_len); + data = skb_rx->data; + if (SYS_WARN(!data)) { + bh_printk(XRADIO_DBG_ERROR, "ERR: skb data is NULL.\n"); + hw_priv->bh_error = __LINE__; + break; + } + + /* Read data from device. */ + if (SYS_WARN(xradio_data_read(hw_priv, data, alloc_len))) { + hw_priv->bh_error = __LINE__; + break; + } + DBG_BH_RX_TOTAL_ADD; + + /* Piggyback */ + ctrl_reg = __le16_to_cpu(((__le16 *)data)[(alloc_len >> 1) - 1]); + + /* check wsm length. */ + wsm = (struct wsm_hdr *)data; + wsm_len = __le32_to_cpu(wsm->len); + if (SYS_WARN(wsm_len > read_len)) { + bh_printk(XRADIO_DBG_ERROR, "wsm_len=%d.\n", wsm_len); + hw_priv->bh_error = __LINE__; + break; + } + + /* dump rx data. */ +#if defined(CONFIG_XRADIO_DEBUG) + if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { + u16 msgid, ifid; + u16 *p = (u16 *)data; + msgid = (*(p + 1)) & 0xC3F; + ifid = (*(p + 1)) >> 6; + ifid &= 0xF; + bh_printk(XRADIO_DBG_ALWY, "[DUMP] msgid 0x%.4X ifid %d len %d\n", + msgid, ifid, *p); + print_hex_dump_bytes("<-- ", DUMP_PREFIX_NONE, + data, min(wsm_len, hw_priv->wsm_dump_max_size)); + } +#endif /* CONFIG_XRADIO_DEBUG */ + + /* extract wsm id and seq. */ + wsm_id = __le32_to_cpu(wsm->id) & 0xFFF; + wsm_seq = (__le32_to_cpu(wsm->id) >> 13) & 7; + skb_trim(skb_rx, wsm_len); + + /* process exceptions. */ + if (unlikely(wsm_id == 0x0800)) { + bh_printk(XRADIO_DBG_ERROR, "firmware exception!\n"); + wsm_handle_exception(hw_priv, &data[sizeof(*wsm)], wsm_len-sizeof(*wsm)); + hw_priv->bh_error = __LINE__; + break; + } else if (unlikely(!rx_resync)) { + if (SYS_WARN(wsm_seq != hw_priv->wsm_rx_seq)) { + bh_printk(XRADIO_DBG_ERROR, "wsm_seq=%d.\n", wsm_seq); + hw_priv->bh_error = __LINE__; + break; + } + } + hw_priv->wsm_rx_seq = (wsm_seq + 1) & 7; + rx_resync = 0; +#if defined(DGB_XRADIO_HWT) + rx_resync = 1; //0 -> 1, HWT test, should not check this. +#endif + + /* Process tx frames confirm. */ + if (wsm_id & 0x0400) { + int rc = wsm_release_tx_buffer(hw_priv, 1); + if (SYS_WARN(rc < 0)) { + bh_printk(XRADIO_DBG_ERROR, "tx buffer < 0.\n"); + hw_priv->bh_error = __LINE__; + break; + } else if (rc > 0) + tx = 1; + } + + /* WSM processing frames. */ + if (SYS_WARN(wsm_handle_rx(hw_priv, wsm_id, wsm, &skb_rx))) { + bh_printk(XRADIO_DBG_ERROR, "wsm_handle_rx failed.\n"); + hw_priv->bh_error = __LINE__; + break; + } + + /* Reclaim the SKB buffer */ + if (skb_rx) { + if(xradio_put_resv_skb(hw_priv, skb_rx)) + xradio_put_skb(hw_priv, skb_rx); + skb_rx = NULL; + } + read_len = 0; + + /* Check if rx burst */ + if (rx_burst) { + xradio_debug_rx_burst(hw_priv); + --rx_burst; + goto rx; + } + } + +tx: + SYS_BUG(hw_priv->hw_bufs_used > hw_priv->wsm_caps.numInpChBufs); + tx_burst = hw_priv->wsm_caps.numInpChBufs - hw_priv->hw_bufs_used; + tx_allowed = tx_burst > 0; + if (tx && tx_allowed) { + int ret; + u8 *data; + size_t tx_len; + + /* Wake up the devices */ + if (hw_priv->device_can_sleep) { + ret = xradio_device_wakeup(hw_priv); + if (SYS_WARN(ret < 0)) { + hw_priv->bh_error = __LINE__; + break; + } else if (ret) { + hw_priv->device_can_sleep = false; + } else { /* Wait for "awake" interrupt */ + pending_tx = tx; + continue; + } + } + /* Increase Tx buffer*/ + wsm_alloc_tx_buffer(hw_priv); + +#if defined(DGB_XRADIO_HWT) + //hardware test. + ret = get_hwt_hif_tx(hw_priv, &data, &tx_len, &tx_burst, &vif_selected); + if (ret <= 0) +#endif //DGB_XRADIO_HWT + /* Get data to send and send it. */ + ret = wsm_get_tx(hw_priv, &data, &tx_len, &tx_burst, &vif_selected); + if (ret <= 0) { + wsm_release_tx_buffer(hw_priv, 1); + if (SYS_WARN(ret < 0)) { + bh_printk(XRADIO_DBG_ERROR, "wsm_get_tx=%d.\n", ret); + hw_priv->bh_error = __LINE__; + break; + } + } else { + wsm = (struct wsm_hdr *)data; + SYS_BUG(tx_len < sizeof(*wsm)); + SYS_BUG(__le32_to_cpu(wsm->len) != tx_len); + + /* Continue to send next data if have any. */ + atomic_add(1, &hw_priv->bh_tx); + + /* Align tx length and check it. */ +#if defined(CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES) + if (tx_len <= 8) + tx_len = 16; + tx_len = hw_priv->sbus_ops->align_size(hw_priv->sbus_priv, tx_len); +#else /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */ + /* HACK!!! Platform limitation. + * It is also supported by upper layer: + * there is always enough space at the end of the buffer. */ + if (tx_len & (SDIO_BLOCK_SIZE - 1)) { + tx_len &= ~(SDIO_BLOCK_SIZE - 1); + tx_len += SDIO_BLOCK_SIZE; + } +#endif /* CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES */ + /* Check if not exceeding XRADIO capabilities */ + if (tx_len > EFFECTIVE_BUF_SIZE) { + bh_printk(XRADIO_DBG_WARN, "Write aligned len: %d\n", tx_len); + } + + /* Make sequence number. */ + wsm->id &= __cpu_to_le32(~WSM_TX_SEQ(WSM_TX_SEQ_MAX)); + wsm->id |= cpu_to_le32(WSM_TX_SEQ(hw_priv->wsm_tx_seq)); + + /* Send the data to devices. */ + if (SYS_WARN(xradio_data_write(hw_priv, data, tx_len))) { + wsm_release_tx_buffer(hw_priv, 1); + bh_printk(XRADIO_DBG_ERROR, "xradio_data_write failed\n"); + hw_priv->bh_error = __LINE__; + break; + } + DBG_BH_TX_TOTAL_ADD; + +#if defined(CONFIG_XRADIO_DEBUG) + if (unlikely(hw_priv->wsm_enable_wsm_dumps)) { + u16 msgid, ifid; + u16 *p = (u16 *)data; + msgid = (*(p + 1)) & 0x3F; + ifid = (*(p + 1)) >> 6; + ifid &= 0xF; + if (msgid == 0x0006) { + bh_printk(XRADIO_DBG_ALWY, "[DUMP] >>> msgid 0x%.4X ifid %d" + "len %d MIB 0x%.4X\n", msgid, ifid,*p, *(p + 2)); + } else { + bh_printk(XRADIO_DBG_ALWY, "[DUMP] >>> msgid 0x%.4X ifid %d " + "len %d\n", msgid, ifid, *p); + } + print_hex_dump_bytes("--> ", DUMP_PREFIX_NONE, data, + min(__le32_to_cpu(wsm->len), + hw_priv->wsm_dump_max_size)); + } +#endif /* CONFIG_XRADIO_DEBUG */ + + /* Process after data have sent. */ + if (vif_selected != -1) { + hw_priv->hw_bufs_used_vif[vif_selected]++; + } + wsm_txed(hw_priv, data); + hw_priv->wsm_tx_seq = (hw_priv->wsm_tx_seq + 1) & WSM_TX_SEQ_MAX; + + /* Check for burst. */ + if (tx_burst > 1) { + xradio_debug_tx_burst(hw_priv); + ++rx_burst; + goto tx; + } + } + } else { + pending_tx = tx; //if not allow to tx, pending it. + } + + /* Check if there are frames to be read. */ + if (ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) { + DBG_BH_NEXT_RX_ADD; + goto rx; + } else { + xradio_bh_read_ctrl_reg(hw_priv, &ctrl_reg); + if(ctrl_reg & HIF_CTRL_NEXT_LEN_MASK) { + DBG_BH_FIX_RX_ADD; + goto rx; + } + } + } /* for (;;)*/ + + /* Reclaim the SKB buffer when exit. */ + if (skb_rx) { + if(xradio_put_resv_skb(hw_priv, skb_rx)) + xradio_put_skb(hw_priv, skb_rx); + skb_rx = NULL; + } + + /* If BH Error, handle it. */ + if (!term) { + bh_printk(XRADIO_DBG_ERROR, "Fatal error, exitting code=%d.\n", + hw_priv->bh_error); + +#ifdef HW_ERROR_WIFI_RESET + /* notify upper layer to restart wifi. + * don't do it in debug version. */ + wsm_upper_restart(hw_priv); +#endif + /* TODO: schedule_work(recovery) */ +#ifndef HAS_PUT_TASK_STRUCT + /* The only reason of having this stupid code here is + * that __put_task_struct is not exported by kernel. */ + for (;;) { +#ifdef BH_USE_SEMAPHORE + status = down_interruptible(&hw_priv->bh_sem); + term = kthread_should_stop(); +#else + int status = wait_event_interruptible(hw_priv->bh_wq, ({ + term = kthread_should_stop(); + (term);})); +#endif + if (status || term) + break; + } +#endif + } + atomic_add(1, &hw_priv->bh_term); //debug info, show bh status. + return 0; +} diff --git a/drivers/net/wireless/xradio/bh.h b/drivers/net/wireless/xradio/bh.h new file mode 100644 index 0000000..ca9187e --- /dev/null +++ b/drivers/net/wireless/xradio/bh.h @@ -0,0 +1,36 @@ +/* + * Data Transmission thread for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_BH_H +#define XRADIO_BH_H + +#define XRADIO_BH_THREAD "xradio_bh" + +/* extern */ struct xradio_common; + +#define SDIO_BLOCK_SIZE (528) + +int xradio_register_bh(struct xradio_common *hw_priv); +void xradio_unregister_bh(struct xradio_common *hw_priv); +void xradio_irq_handler(struct xradio_common *hw_priv); +void xradio_bh_wakeup(struct xradio_common *hw_priv); +int xradio_bh_suspend(struct xradio_common *hw_priv); +int xradio_bh_resume(struct xradio_common *hw_priv); +/* Must be called from BH thread. */ +void xradio_enable_powersave(struct xradio_vif *priv, bool enable); +int wsm_release_tx_buffer(struct xradio_common *hw_priv, int count); +int wsm_release_vif_tx_buffer(struct xradio_common *hw_priv, int if_id, + int count); +int xradio_init_resv_skb(struct xradio_common *hw_priv); +void xradio_deinit_resv_skb(struct xradio_common *hw_priv); +int xradio_realloc_resv_skb(struct xradio_common *hw_priv, + struct sk_buff *skb); +#endif /* XRADIO_BH_H */ diff --git a/drivers/net/wireless/xradio/common.c b/drivers/net/wireless/xradio/common.c new file mode 100644 index 0000000..ce0039e --- /dev/null +++ b/drivers/net/wireless/xradio/common.c @@ -0,0 +1,166 @@ +/* + * Common interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_COMMON_C +#define XRADIO_COMMON_C +#include +#include + +#include "xradio.h" + +struct xr_file *xr_fileopen(const char *path, int open_mode, umode_t mode) +{ + struct xr_file *xr_fp = NULL; + if (!path) return NULL; + + xr_fp = xr_kmalloc(sizeof(struct xr_file), false); + if (!xr_fp) { + xradio_dbg(XRADIO_DBG_ERROR, "%s:xr_kmalloc failed,size=%d!\n", + __func__, sizeof(struct xr_file)); + return NULL; + } + memset(xr_fp, 0, sizeof(struct xr_file)); + + //open file. + xr_fp->fp = filp_open(path, open_mode, mode); + if (IS_ERR(xr_fp->fp)) { + xradio_dbg(XRADIO_DBG_ERROR, "filp_open failed(%d)\n", (int)xr_fp->fp); + kfree(xr_fp); + return NULL; + } + + //get file size. + if(xr_fp->fp->f_op->llseek != NULL) { + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + xr_fp->size = vfs_llseek(xr_fp->fp, 0, SEEK_END); + vfs_llseek(xr_fp->fp, 0, SEEK_SET); + set_fs(old_fs); + } else { + xr_fp->size = 0; + } + return xr_fp; +} + +int xr_fileclose(const struct xr_file *xr_fp) +{ + if (xr_fp) { + if (!IS_ERR(xr_fp->fp)) + filp_close(xr_fp->fp, NULL); + if (xr_fp->data) + kfree(xr_fp->data); + kfree(xr_fp); + return 0; + } + return -1; +} + +int xr_fileread(const struct xr_file *xr_fp, char *buffer, int size) +{ + int ret = -1; + if(xr_fp && buffer && xr_fp->fp->f_op->read != NULL) { + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = vfs_read(xr_fp->fp, buffer, size, &xr_fp->fp->f_pos); + set_fs(old_fs); + } + return ret; +} + +int xr_filewrite(const struct xr_file *xr_fp, char *buffer, int size) +{ + int ret = -1; + if(xr_fp && buffer && xr_fp->fp->f_op->write != NULL) { + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = vfs_write(xr_fp->fp, buffer, size, &xr_fp->fp->f_pos); + set_fs(old_fs); + } + return ret; +} + +struct xr_file *xr_request_file(const char *path) +{ + int ret = -1; + struct xr_file *xr_fp = xr_fileopen(path, O_RDONLY, 0); + if (!xr_fp) { + xradio_dbg(XRADIO_DBG_ERROR, "%s:xr_fileopen failed!\n", __func__); + return NULL; + } + + //get file data. + if (xr_fp->size) { + xr_fp->data = xr_kmalloc(xr_fp->size, false); + if(xr_fp->data && xr_fp->fp->f_op->read != NULL) { + mm_segment_t old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = vfs_read(xr_fp->fp, xr_fp->data, xr_fp->size, &xr_fp->fp->f_pos); + set_fs(old_fs); + } + } + if (ret < 0) { + xr_fileclose(xr_fp); + xr_fp = NULL; + } + return xr_fp; +} + +/* To use macaddr and ps mode of customers */ +int access_file(char *path, char *buffer, int size, int isRead) +{ + int ret=0; + struct file *fp; + mm_segment_t old_fs = get_fs(); + + if(isRead) + fp = filp_open(path, O_RDONLY, S_IRUSR); + else + fp = filp_open(path, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); + + if (IS_ERR(fp)) { + if ((int)fp != -ENOENT) + xradio_dbg(XRADIO_DBG_ERROR, "can't open %s, failed(%d)\n", path, (int)fp); + return -1; + } + + if(isRead) + { + if(fp->f_op->read == NULL) { + xradio_dbg(XRADIO_DBG_ERROR, "read %s Not allow.\n", path); + return -1; + } + else { + fp->f_pos = 0; + set_fs(KERNEL_DS); + ret = vfs_read(fp, buffer, size, &fp->f_pos); + set_fs(old_fs); + } + } + else + { + if(fp->f_op->write == NULL) { + xradio_dbg(XRADIO_DBG_ERROR, "write %s Not allow.\n", path); + return -1; + } + else { + fp->f_pos = 0; + set_fs(KERNEL_DS); + ret = vfs_write(fp, buffer, size, &fp->f_pos); + set_fs(old_fs); + } + } + filp_close(fp,NULL); + + xradio_dbg(XRADIO_DBG_NIY, "Access_file %s(%d)\n", path, ret); + return ret; +} + +#endif //XRADIO_COMMON_C \ No newline at end of file diff --git a/drivers/net/wireless/xradio/common.h b/drivers/net/wireless/xradio/common.h new file mode 100644 index 0000000..10a265b --- /dev/null +++ b/drivers/net/wireless/xradio/common.h @@ -0,0 +1,162 @@ +/* + * Common interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_COMMON_H +#define XRADIO_COMMON_H + +#include + +/******************************************************* + interfaces for file operation. +********************************************************/ +struct xr_file { + struct file *fp; + char *data; + size_t size; +}; +struct xr_file *xr_fileopen(const char *path, int open_mode, umode_t mode); +struct xr_file *xr_request_file(const char *path); +int xr_fileclose(const struct xr_file *fp); +int xr_fileread(const struct xr_file *fp, char *buffer, int size); +int xr_filewrite(const struct xr_file *fp, char *buffer, int size); +int access_file(char *path, char *buffer, int size, int isRead); + +/******************************************************* + interfaces for parse frame protocol info. +********************************************************/ +#define LLC_LEN 8 +#define LLC_TYPE_OFF 6 //Ether type offset +#define IP_PROTO_OFF 9 //protocol offset +#define IP_S_ADD_OFF 12 +#define IP_D_ADD_OFF 16 +#define UDP_LEN 8 +//DHCP +#define DHCP_BOOTP_C 68 +#define DHCP_BOOTP_S 67 +#define UDP_BOOTP_LEN 236 //exclude "Options:64" +#define BOOTP_OPS_LEN 64 +#define DHCP_MAGIC 0x63825363 +#define DHCP_DISCOVER 0x01 +#define DHCP_OFFER 0x02 +#define DHCP_REQUEST 0x03 +#define DHCP_DECLINE 0x04 +#define DHCP_ACK 0x05 +#define DHCP_NACK 0x06 +#define DHCP_RELEASE 0x07 + +//LLC layer. +static inline bool is_SNAP(u8* llc_data) +{ + return (bool)(*(u16*)(llc_data) == 0xAAAA && llc_data[2] == 0x03); //0xAA, 0xAA, 0x03. +} + +static inline bool is_STP(u8* llc_data) +{ + return (bool)(*(u16*)(llc_data) == 0xAAAA && llc_data[2] == 0x03); //0x42, 0x42, 0x03. +} + +//IP/IPV6/ARP layer... +static inline bool is_ip(u8* llc_data) +{ + return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IP)); //0x0800 +} +static inline bool is_ipv6(u8* llc_data) +{ + return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_IPV6)); //0x08dd +} +static inline bool is_arp(u8* llc_data) +{ + return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_ARP)); //0x0806 +} +static inline bool is_8021x(u8* llc_data) +{ + return (bool)(*(u16*)(llc_data+LLC_TYPE_OFF) == cpu_to_be16(ETH_P_PAE)); //0x888E +} + +//TCP/UDP layer... +static inline bool is_tcp(u8* llc_data) +{ + return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_TCP); // +} + +static inline bool is_udp(u8* llc_data) +{ + return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_UDP); // +} + +static inline bool is_icmp(u8* llc_data) +{ + return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_ICMP); // +} + +static inline bool is_igmp(u8* llc_data) +{ + return (bool)(llc_data[LLC_LEN+IP_PROTO_OFF] == IPPROTO_IGMP); // +} + +static inline bool is_dhcp(u8* llc_data) +{ + u8* ip_hdr = llc_data+LLC_LEN; + if(!is_ip(llc_data)) + return (bool)0; + if(ip_hdr[IP_PROTO_OFF] == IPPROTO_UDP) { + u8* udp_hdr = ip_hdr+((ip_hdr[0]&0xf)<<2); //ihl:words + return (bool)((((udp_hdr[0]<<8)|udp_hdr[1]) == DHCP_BOOTP_C) || //DHCP client + (((udp_hdr[0]<<8)|udp_hdr[1]) == DHCP_BOOTP_S)); //DHCP server + } + return (bool)0; +} + +static inline struct sk_buff *xr_alloc_skb(unsigned int len) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; + return __dev_alloc_skb(len, flags); +#else + return dev_alloc_skb(len); +#endif +} + +static inline void *xr_kmalloc(unsigned int len, bool isDMA) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; +#else + gfp_t flags = GFP_ATOMIC; +#endif + flags = isDMA ? (flags | GFP_DMA) : flags; + return kmalloc(len, flags); +} + +static inline void *xr_kzalloc(unsigned int len, bool isDMA) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; +#else + gfp_t flags = GFP_ATOMIC; +#endif + flags = isDMA ? (flags | GFP_DMA) : flags; + return kzalloc(len, flags); +} + + +static inline void *xr_krealloc(void *buf, unsigned int len, bool isDMA) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) + gfp_t flags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL; +#else + gfp_t flags = GFP_ATOMIC; +#endif + flags = isDMA ? (flags | GFP_DMA) : flags; + return krealloc(buf, len, flags); +} + +#endif //XRADIO_COMMON_H diff --git a/drivers/net/wireless/xradio/debug.c b/drivers/net/wireless/xradio/debug.c new file mode 100644 index 0000000..395c574 --- /dev/null +++ b/drivers/net/wireless/xradio/debug.c @@ -0,0 +1,3273 @@ +/* + * Debug code for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/*Linux version 3.4.0 compilation*/ +//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +#include +//#endif +#include +#include +#include +#include + +#include "xradio.h" +#include "hwio.h" +#include "debug.h" + +/*added by yangfh, for host debuglevel*/ +u8 dbg_common = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_sbus = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_bh = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_txrx = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR; +u8 dbg_wsm = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_sta = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_scan = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_ap = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_pm = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_itp = XRADIO_DBG_ALWY|XRADIO_DBG_ERROR|XRADIO_DBG_WARN; +u8 dbg_logfile = XRADIO_DBG_ERROR; + +#ifdef CONFIG_XRADIO_DEBUGFS +/* join_status */ +static const char * const xradio_debug_join_status[] = { + "passive", + "monitor", + "station", + "access point", +}; + +/* WSM_JOIN_PREAMBLE_... */ +static const char * const xradio_debug_preamble[] = { + "long", + "short", + "long on 1 and 2 Mbps", +}; + +static const char * const xradio_debug_fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test", +}; + +static const char * const xradio_debug_link_id[] = { + "OFF", + "REQ", + "SOFT", + "HARD", +}; + +static const char *xradio_debug_mode(int mode) +{ + switch (mode) { + case NL80211_IFTYPE_UNSPECIFIED: + return "unspecified"; + case NL80211_IFTYPE_MONITOR: + return "monitor"; + case NL80211_IFTYPE_STATION: + return "station"; + case NL80211_IFTYPE_ADHOC: + return "ad-hok"; + case NL80211_IFTYPE_MESH_POINT: + return "mesh point"; + case NL80211_IFTYPE_AP: + return "access point"; + case NL80211_IFTYPE_P2P_CLIENT: + return "p2p client"; + case NL80211_IFTYPE_P2P_GO: + return "p2p go"; + default: + return "unsupported"; + } +} + +static void xradio_queue_status_show(struct seq_file *seq, + struct xradio_queue *q) +{ + int i, if_id; + seq_printf(seq, "Queue %d:\n", q->queue_id); + seq_printf(seq, " capacity: %d\n", q->capacity); + seq_printf(seq, " queued: %d\n", q->num_queued); + seq_printf(seq, " pending: %d\n", q->num_pending); + seq_printf(seq, " sent: %d\n", q->num_sent); + seq_printf(seq, " locked: %s\n", q->tx_locked_cnt ? "yes" : "no"); + seq_printf(seq, " overfull: %s\n", q->overfull ? "yes" : "no"); + seq_puts(seq, " link map: 0-> "); + for (if_id = 0; if_id < XRWL_MAX_VIFS; if_id++) { + for (i = 0; i < q->stats->map_capacity; ++i) + seq_printf(seq, "%.2d ", q->link_map_cache[if_id][i]); + seq_printf(seq, "<-%d\n", q->stats->map_capacity); + } +} + +static void xradio_debug_print_map(struct seq_file *seq, + struct xradio_vif *priv, + const char *label, + u32 map) +{ + int i; + seq_printf(seq, "%s0-> ", label); + for (i = 0; i < priv->hw_priv->tx_queue_stats.map_capacity; ++i) + seq_printf(seq, "%s ", (map & BIT(i)) ? "**" : ".."); + seq_printf(seq, "<-%d\n", + priv->hw_priv->tx_queue_stats.map_capacity - 1); +} + +static int xradio_version_show(struct seq_file *seq, void *v) +{ + struct xradio_common *hw_priv = seq->private; + seq_printf(seq, "Driver Label:%s %s\n", DRV_VERSION, DRV_BUILDTIME); + seq_printf(seq, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]); + return 0; +} + +static int xradio_version_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_version_show, inode->i_private); +} + +static const struct file_operations fops_version = { + .open = xradio_version_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +#if defined(DGB_XRADIO_QC) +static int xradio_hwinfo_show(struct seq_file *seq, void *v) +{ + int ret; + struct xradio_common *hw_priv = seq->private; + u32 hw_arry[8] = { 0 }; + + // + wsm_read_mib(hw_priv, WSM_MIB_ID_HW_INFO, (void*)&hw_arry, sizeof(hw_arry), 4); + + //get_random_bytes((u8 *)&hw_arry[0], 8*sizeof(u32)); + //hw_arry[0] = 0x0B140D4; + //hw_arry[1] &= ~0xF803FFFF; + //hw_arry[2] &= ~0xC0001FFF; + //hw_arry[5] &= ~0xFFFFF000; + //hw_arry[7] &= ~0xFFFC07C0; + + seq_printf(seq, "0x%08x,0x%08x,0x%08x,0x%08x," + "0x%08x,0x%08x,0x%08x,0x%08x\n", + hw_arry[0], hw_arry[1], hw_arry[2], hw_arry[3], + hw_arry[4], hw_arry[5], hw_arry[6], hw_arry[7]); + return 0; +} + +static int xradio_hwinfo_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_hwinfo_show, inode->i_private); +} + +static const struct file_operations fops_hwinfo = { + .open = xradio_hwinfo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; +#endif + +static int xradio_status_show_common(struct seq_file *seq, void *v) +{ + int i; + struct list_head *item; + struct xradio_common *hw_priv = seq->private; + struct xradio_debug_common *d = hw_priv->debug; + int ba_cnt, ba_acc, ba_cnt_rx, ba_acc_rx, ba_avg = 0, ba_avg_rx = 0; + bool ba_ena; + + spin_lock_bh(&hw_priv->ba_lock); + ba_cnt = hw_priv->debug->ba_cnt; + ba_acc = hw_priv->debug->ba_acc; + ba_cnt_rx = hw_priv->debug->ba_cnt_rx; + ba_acc_rx = hw_priv->debug->ba_acc_rx; + ba_ena = hw_priv->ba_ena; + if (ba_cnt) + ba_avg = ba_acc / ba_cnt; + if (ba_cnt_rx) + ba_avg_rx = ba_acc_rx / ba_cnt_rx; + spin_unlock_bh(&hw_priv->ba_lock); + + seq_puts(seq, "XRADIO Wireless LAN driver status\n"); + seq_printf(seq, "Hardware: %d.%d\n", + hw_priv->wsm_caps.hardwareId, + hw_priv->wsm_caps.hardwareSubId); + seq_printf(seq, "Firmware: %s %d.%d\n", + xradio_debug_fw_types[hw_priv->wsm_caps.firmwareType], + hw_priv->wsm_caps.firmwareVersion, + hw_priv->wsm_caps.firmwareBuildNumber); + seq_printf(seq, "FW API: %d\n", + hw_priv->wsm_caps.firmwareApiVer); + seq_printf(seq, "FW caps: 0x%.4X\n", + hw_priv->wsm_caps.firmwareCap); + if (hw_priv->channel) + seq_printf(seq, "Channel: %d%s\n", + hw_priv->channel->hw_value, + hw_priv->channel_switch_in_progress ? + " (switching)" : ""); + seq_printf(seq, "HT: %s\n", + xradio_is_ht(&hw_priv->ht_info) ? "on" : "off"); + if (xradio_is_ht(&hw_priv->ht_info)) { + seq_printf(seq, "Greenfield: %s\n", + xradio_ht_greenfield(&hw_priv->ht_info) ? "yes" : "no"); + seq_printf(seq, "AMPDU dens: %d\n", + xradio_ht_ampdu_density(&hw_priv->ht_info)); + } + spin_lock_bh(&hw_priv->tx_policy_cache.lock); + i = 0; + list_for_each(item, &hw_priv->tx_policy_cache.used) + ++i; + spin_unlock_bh(&hw_priv->tx_policy_cache.lock); + seq_printf(seq, "RC in use: %d\n", i); + seq_printf(seq, "BA stat: %d, %d (%d)\n", + ba_cnt, ba_acc, ba_avg); + seq_printf(seq, "BA RX stat: %d, %d (%d)\n", + ba_cnt_rx, ba_acc_rx, ba_avg_rx); + seq_printf(seq, "Block ACK: %s\n", ba_ena ? "on" : "off"); + + seq_puts(seq, "\n"); + for (i = 0; i < 4; ++i) { + xradio_queue_status_show(seq, &hw_priv->tx_queue[i]); + seq_puts(seq, "\n"); + } + seq_printf(seq, "TX burst: %d\n", + d->tx_burst); + seq_printf(seq, "RX burst: %d\n", + d->rx_burst); + seq_printf(seq, "TX miss: %d\n", + d->tx_cache_miss); + seq_printf(seq, "Long retr: %d\n", + hw_priv->long_frame_max_tx_count); + seq_printf(seq, "Short retr: %d\n", + hw_priv->short_frame_max_tx_count); + + seq_printf(seq, "BH status: %s, errcode=%d\n", + atomic_read(&hw_priv->bh_term) ? "terminated" : "alive", + hw_priv->bh_error); + seq_printf(seq, "Pending RX: %d\n", + atomic_read(&hw_priv->bh_rx)); + seq_printf(seq, "Pending TX: %d\n", + atomic_read(&hw_priv->bh_tx)); + + seq_printf(seq, "TX bufs: %d x %d bytes\n", + hw_priv->wsm_caps.numInpChBufs, + hw_priv->wsm_caps.sizeInpChBuf); + seq_printf(seq, "Used bufs: %d\n", + hw_priv->hw_bufs_used); + seq_printf(seq, "Powersavemode:%s\n", + hw_priv->powersave_enabled ? "enable" : "disable"); + seq_printf(seq, "Device: %s\n", + hw_priv->device_can_sleep ? "alseep" : "awake"); + + spin_lock(&hw_priv->wsm_cmd.lock); + seq_printf(seq, "WSM status: %s\n", + hw_priv->wsm_cmd.done ? "idle" : "active"); + seq_printf(seq, "WSM cmd: 0x%.4X (%d bytes)\n", + hw_priv->wsm_cmd.cmd, hw_priv->wsm_cmd.len); + seq_printf(seq, "WSM retval: %d\n", + hw_priv->wsm_cmd.ret); + spin_unlock(&hw_priv->wsm_cmd.lock); + + seq_printf(seq, "Datapath: %s\n", + atomic_read(&hw_priv->tx_lock) ? "locked" : "unlocked"); + if (atomic_read(&hw_priv->tx_lock)) + seq_printf(seq, "TXlock cnt: %d\n", + atomic_read(&hw_priv->tx_lock)); + + seq_printf(seq, "Scan: %s\n", + atomic_read(&hw_priv->scan.in_progress) ? "active" : "idle"); + seq_printf(seq, "Led state: 0x%.2X\n", + hw_priv->softled_state); + + return 0; +} + +static int xradio_status_open_common(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_status_show_common, + inode->i_private); +} + +static const struct file_operations fops_status_common = { + .open = xradio_status_open_common, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int xradio_counters_show(struct seq_file *seq, void *v) +{ + int ret; + struct xradio_common *hw_priv = seq->private; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(hw_priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.CAT_STR(count, name))) + + PUT_COUNTER("\t\t\t\t", PlcpErrors); + PUT_COUNTER("\t\t\t\t", FcsErrors); + PUT_COUNTER("\t\t\t\t", TxPackets); + PUT_COUNTER("\t\t\t\t", RxPackets); + PUT_COUNTER("\t\t\t", RxPacketErrors); + PUT_COUNTER("\t\t\t\t", RtsSuccess); + PUT_COUNTER("\t\t\t", RtsFailures); + PUT_COUNTER("\t\t", RxFramesSuccess); + PUT_COUNTER("\t", RxDecryptionFailures); + PUT_COUNTER("\t\t\t", RxMicFailures); + PUT_COUNTER("\t\t", RxNoKeyFailures); + PUT_COUNTER("\t\t", TxMulticastFrames); + PUT_COUNTER("\t\t", TxFramesSuccess); + PUT_COUNTER("\t\t", TxFrameFailures); + PUT_COUNTER("\t\t", TxFramesRetried); + PUT_COUNTER("\t", TxFramesMultiRetried); + PUT_COUNTER("\t\t", RxFrameDuplicates); + PUT_COUNTER("\t\t\t", AckFailures); + PUT_COUNTER("\t\t", RxMulticastFrames); + PUT_COUNTER("\t\t", RxCMACICVErrors); + PUT_COUNTER("\t\t\t", RxCMACReplays); + PUT_COUNTER("\t\t", RxMgmtCCMPReplays); + PUT_COUNTER("\t\t\t", RxBIPMICErrors); + + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int xradio_counters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_counters_show, + inode->i_private); +} + +static const struct file_operations fops_counters = { + .open = xradio_counters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int xradio_backoff_show(struct seq_file *seq, void *v) +{ + int ret; + struct xradio_common *hw_priv = seq->private; + struct wsm_backoff_counter counters; + + ret = wsm_get_backoff_dbg(hw_priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, tab"%s:\t%d\n", #name, \ + (__le32_to_cpu(counters.CAT_STR(count, name))&0xffff)) + + PUT_COUNTER("backoff_max ", 0); + PUT_COUNTER("[0,7] ", 1); + PUT_COUNTER("[~,15] ", 2); + PUT_COUNTER("[~,31] ", 3); + PUT_COUNTER("[~,63] ", 4); + PUT_COUNTER("[~,127] ", 5); + PUT_COUNTER("[~,255] ", 6); + PUT_COUNTER("[~,511] ", 7); + PUT_COUNTER("[~,1023] ", 8); + + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int xradio_backoff_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_backoff_show, + inode->i_private); +} + +static const struct file_operations fops_backoff = { + .open = xradio_backoff_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +extern u32 TxedRateIdx_Map[24]; +extern u32 RxedRateIdx_Map[24]; + +static int xradio_ratemap_show(struct seq_file *seq, void *v) +{ + //int ret; + //struct xradio_common *hw_priv = seq->private; + + seq_printf(seq, "\nRateMap for Tx & RX:\n"); +#define PUT_RATE_COUNT(name,idx) \ + seq_printf(seq, "%s\t" "%d, %d\n", #name, \ + __le32_to_cpu(TxedRateIdx_Map[idx]),\ + __le32_to_cpu(RxedRateIdx_Map[idx]));\ + TxedRateIdx_Map[idx]=0;\ + RxedRateIdx_Map[idx]=0; + + PUT_RATE_COUNT("65 Mbps:", 21); + PUT_RATE_COUNT("58.5 Mbps:", 20); + PUT_RATE_COUNT("52 Mbps:", 19); + PUT_RATE_COUNT("39 Mbps:", 18); + PUT_RATE_COUNT("26 Mbps:", 17); + PUT_RATE_COUNT("19.5 Mbps:", 16); + PUT_RATE_COUNT("13 Mbps:",15); + PUT_RATE_COUNT("6.5 Mbps:", 14); + + PUT_RATE_COUNT("54 Mbps:", 13); + PUT_RATE_COUNT("48 Mbps:", 12); + PUT_RATE_COUNT("36 Mbps:", 11); + PUT_RATE_COUNT("24 Mbps:", 10); + PUT_RATE_COUNT("18 Mbps:", 9); + PUT_RATE_COUNT("12 Mbps:", 8); + PUT_RATE_COUNT("9 Mbps:", 7); + PUT_RATE_COUNT("6 Mbps:", 6); + + PUT_RATE_COUNT("11 Mbps:", 3); + PUT_RATE_COUNT("5.5 Mbps:", 2); + PUT_RATE_COUNT("2 Mbps:", 1); + PUT_RATE_COUNT("1 Mbps:", 0); + +#undef PUT_RATE_COUNT + + return 0; +} + +static int xradio_ratemap_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_ratemap_show, + inode->i_private); +} + +static const struct file_operations fops_ratemap= { + .open = xradio_ratemap_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int xradio_ampducounters_show(struct seq_file *seq, void *v) +{ + int ret; + struct xradio_common *hw_priv = seq->private; + struct wsm_ampducounters_table counters; + + ret = wsm_get_ampducounters_table(hw_priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, "%s:" tab "%d\n", #name, \ + __le32_to_cpu(counters.CAT_STR(count, name))) + + PUT_COUNTER("\t\t\t\t\t", TxAMPDUs); + PUT_COUNTER("\t\t\t", TxMPDUsInAMPDUs); + PUT_COUNTER("\t\t", TxOctetsInAMPDUs_l32); + PUT_COUNTER("\t\t", TxOctetsInAMPDUs_h32); + PUT_COUNTER("\t\t\t\t\t", RxAMPDUs); + PUT_COUNTER("\t\t\t", RxMPDUsInAMPDUs); + PUT_COUNTER("\t\t", RxOctetsInAMPDUs_l32); + PUT_COUNTER("\t\t", RxOctetsInAMPDUs_h32); + PUT_COUNTER("\t", RxDelimeterCRCErrorCount); + PUT_COUNTER("\t\t\t", ImplictBARFailures); + PUT_COUNTER("\t\t\t", ExplictBARFailures); + + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int xradio_ampducounters_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_ampducounters_show, + inode->i_private); +} + +static const struct file_operations fops_ampducounters = { + .open = xradio_ampducounters_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int xradio_txpipe_show(struct seq_file *seq, void *v) +{ + int ret; + struct xradio_common *hw_priv = seq->private; + struct wsm_txpipe_counter counters; + + ret = wsm_get_txpipe_table(hw_priv, &counters); + if (ret) + return ret; + +#define CAT_STR(x, y) x ## y +#define PUT_COUNTER(tab, name) \ + seq_printf(seq, tab":\t%d\n", \ + __le32_to_cpu(counters.CAT_STR(count, name))) + + PUT_COUNTER("tx-aggr ", 1); + PUT_COUNTER("retx-aggr ", 2); + PUT_COUNTER("retry_type1 ", 3); + PUT_COUNTER("retry_type2 ", 4); + PUT_COUNTER("retry_type3 ", 5); + PUT_COUNTER("rx-aggr-event ", 6); + PUT_COUNTER("rx-aggr-end ", 7); + PUT_COUNTER("rx-ba ", 8); + PUT_COUNTER("tx_ampdu_len ", 9); + PUT_COUNTER("fail_by_rts ", a); + + +#undef PUT_COUNTER +#undef CAT_STR + + return 0; +} + +static int xradio_txpipe_open(struct inode *inode, struct file *file) +{ + return single_open(file, &xradio_txpipe_show, + inode->i_private); +} + +static const struct file_operations fops_txpipe = { + .open = xradio_txpipe_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; +static int count_idx=0; +static int xradio_dbgstats_show(struct seq_file *seq, void *v) +{ + int ret; + int avg_ampdu_len=0; + int FrameFail_ratio=0; + int FailByRts_ratio=0; + int FrameRetry_ratio=0; + int AMPDURetry_ratio=0; + int Retry1_ratio=0; + int Retry2_ratio=0; + int Retry3_ratio=0; + struct xradio_common *hw_priv = seq->private; + struct wsm_counters_table counters; + struct wsm_ampducounters_table ampdu_counters; + struct wsm_txpipe_counter txpipe; + + ret = wsm_get_counters_table(hw_priv, &counters); + if (ret) + return ret; + + ret = wsm_get_txpipe_table(hw_priv, &txpipe); + if (ret) + return ret; + + ret = wsm_get_ampducounters_table(hw_priv, &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[IEEE80211_BAND_2GHZ]; + return simple_read_from_buffer(user_buf, count, ppos, + band->ht_cap.ht_supported ? "1\n" : "0\n", 2); +} + +static ssize_t xradio_11n_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct xradio_common *hw_priv = file->private_data; + struct ieee80211_supported_band *band[2] = { + hw_priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ], + hw_priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ], + }; + char buf[1]; + int ena = 0; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, 1)) + return -EFAULT; + if (buf[0] == 1) + ena = 1; + + band[0]->ht_cap.ht_supported = ena; +#ifdef CONFIG_XRADIO_5GHZ_SUPPORT + band[1]->ht_cap.ht_supported = ena; +#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ + + return count; +} + +static const struct file_operations fops_11n = { + .open = xradio_generic_open, + .read = xradio_11n_read, + .write = xradio_11n_write, + .llseek = default_llseek, +}; + + +static u32 fwdbg_ctrl; + +static ssize_t xradio_fwdbg_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct xradio_common *hw_priv = file->private_data; + char buf[12] = {0}; + char* endptr = NULL; + + count = (count > 11 ? 11 : count); + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + fwdbg_ctrl = simple_strtoul(buf, &endptr, 16); + xradio_dbg(XRADIO_DBG_ALWY,"fwdbg_ctrl = %d\n", fwdbg_ctrl); + SYS_WARN(wsm_set_fw_debug_control(hw_priv, fwdbg_ctrl, 0)); + + return count; +} + +static ssize_t xradio_fwdbg_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + //struct xradio_common *hw_priv = file->private_data; + char buf[50]; + size_t size = 0; + + sprintf(buf, "fwdbg_ctrl = %u\n", fwdbg_ctrl); + size = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, size); +} +static const struct file_operations fops_fwdbg = { + .open = xradio_generic_open, + .write = xradio_fwdbg_write, + .read = xradio_fwdbg_read, + .llseek = default_llseek, +}; + +//add by yangfh for read/write fw registers +static ssize_t xradio_fwreg_rw(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct xradio_common *hw_priv = file->private_data; + char buf[256] = {0}; + u16 buf_size = (count > 255 ? 255 : count); + char* startptr = &buf[0]; + char* endptr = NULL; + u16 flag = 0; + int i, end = 16; + + if (!count) + return -EINVAL; + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + flag = simple_strtoul(startptr, &endptr, 16); + startptr = endptr+1; + if(flag & WSM_REG_RW_F) { //write + WSM_REG_W reg_w; + reg_w.flag = flag; + reg_w.data_size = 0; + if(flag&WSM_REG_BK_F) end = 2; + + for(i=0; (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 = xr_kzalloc(sizeof(struct xradio_debug_priv), false); + priv->debug = d; + if (SYS_WARN(!d)) + return ret; + + memset(name, 0, VIF_DEBUGFS_NAME_S); + ret = snprintf(name, VIF_DEBUGFS_NAME_S, "vif_%d", priv->if_id); + if (SYS_WARN(ret < 0)) + goto err; + + d->debugfs_phy = debugfs_create_dir(name, + hw_priv->debug->debugfs_phy); + if (SYS_WARN(!d->debugfs_phy)) + goto err; + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + if (SYS_WARN(!debugfs_create_file("hang", S_IWUSR, d->debugfs_phy, + priv, &fops_hang))) + goto err; +#endif + +#if defined(AP_HT_COMPAT_FIX) + if (SYS_WARN(!debugfs_create_file("htcompat_disable", S_IWUSR, d->debugfs_phy, + priv, &fops_ht_compat_dis))) + goto err; +#endif + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + priv, &fops_status_priv)) + goto err; + + return 0; +err: + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; + +} + +void xradio_debug_release_priv(struct xradio_vif *priv) +{ + struct xradio_debug_priv *d = priv->debug; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + if (d) { + priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + } +} + +int xradio_print_fw_version(struct xradio_common *hw_priv, u8* buf, size_t len) +{ + return snprintf(buf, len, "%s %d.%d", + xradio_debug_fw_types[hw_priv->wsm_caps.firmwareType], + hw_priv->wsm_caps.firmwareVersion, + hw_priv->wsm_caps.firmwareBuildNumber); +} + +/*added by yangfh, for host debuglevel*/ +struct dentry *debugfs_host = NULL; +#if defined(DGB_XRADIO_QC) +struct dentry *debugfs_hwinfo = NULL; +#endif +int xradio_host_dbg_init(void) +{ + int line = 0; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if(!debugfs_initialized()) { + xradio_dbg(XRADIO_DBG_ERROR, "debugfs isnot initialized\n"); + return 0; + } + +#define ERR_LINE do { line = __LINE__; goto err;} while(0) + + debugfs_host = debugfs_create_dir("xradio_host_dbg", NULL); + if (!debugfs_host) + ERR_LINE; + + if (!debugfs_create_x8("dbg_common", S_IRUSR | S_IWUSR, debugfs_host, &dbg_common)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_sbus", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sbus)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_ap", S_IRUSR | S_IWUSR, debugfs_host, &dbg_ap)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_sta", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sta)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_scan", S_IRUSR | S_IWUSR, debugfs_host, &dbg_scan)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_bh", S_IRUSR | S_IWUSR, debugfs_host, &dbg_bh)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_txrx", S_IRUSR | S_IWUSR, debugfs_host, &dbg_txrx)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_wsm", S_IRUSR | S_IWUSR, debugfs_host, &dbg_wsm)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_pm", S_IRUSR | S_IWUSR, debugfs_host, &dbg_pm)) + ERR_LINE; + +#ifdef CONFIG_XRADIO_ITP + if (!debugfs_create_x8("dbg_itp", S_IRUSR | S_IWUSR, debugfs_host, &dbg_itp)) + ERR_LINE; +#endif + + if (!debugfs_create_x8("dbg_logfile", S_IRUSR | S_IWUSR, debugfs_host, &dbg_logfile)) + ERR_LINE; + + if (!debugfs_create_x8("dbg_tpa_node", S_IRUSR | S_IWUSR, debugfs_host, &tpa_node_dbg)) + ERR_LINE; + + if (!debugfs_create_x32("set_sdio_clk", S_IRUSR | S_IWUSR, debugfs_host, &dbg_sdio_clk)) + ERR_LINE; + + return 0; + +#undef ERR_LINE +err: + xradio_dbg(XRADIO_DBG_ERROR, "xradio_host_dbg_init failed=%d\n", line); + if (debugfs_host) + debugfs_remove_recursive(debugfs_host); +#if defined(DGB_XRADIO_QC) + debugfs_hwinfo = NULL; +#endif + debugfs_host = NULL; + return 0; +} + +void xradio_host_dbg_deinit(void) +{ + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + if(debugfs_host) + debugfs_remove_recursive(debugfs_host); +#if defined(DGB_XRADIO_QC) + debugfs_hwinfo = NULL; +#endif + debugfs_host = NULL; +} + +int xradio_debug_init_common(struct xradio_common *hw_priv) +{ + int ret = -ENOMEM; + int line = 0; + struct xradio_debug_common *d = NULL; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + //init some debug variables here. + retry_dbg = 0; + tpa_node_dbg = 0; + +#define ERR_LINE do { line = __LINE__; goto err;} while(0) + + d = xr_kzalloc(sizeof(struct xradio_debug_common), false); + hw_priv->debug = d; + if (!d) { + xradio_dbg(XRADIO_DBG_ERROR,"%s, xr_kzalloc failed!\n", __FUNCTION__); + return ret; + } + + d->debugfs_phy = debugfs_create_dir("xradio", hw_priv->hw->wiphy->debugfsdir); + if (!d->debugfs_phy) + ERR_LINE; + + if (!debugfs_create_file("version", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_version)) + ERR_LINE; + + if (!debugfs_create_file("status", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_status_common)) + ERR_LINE; + + if (!debugfs_create_file("counters", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_counters)) + ERR_LINE; + + if (!debugfs_create_file("backoff", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_backoff)) + ERR_LINE; + + if (!debugfs_create_file("txpipe", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_txpipe)) + ERR_LINE; + + if (!debugfs_create_file("ampdu", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_ampducounters)) + ERR_LINE; + + if (!debugfs_create_file("ratemap", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_ratemap)) + ERR_LINE; + + if (!debugfs_create_file("dbgstats", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_dbgstats)) + ERR_LINE; + + if (!debugfs_create_file("11n", S_IRUSR | S_IWUSR, + d->debugfs_phy, hw_priv, &fops_11n)) + ERR_LINE; + + if (!debugfs_create_file("wsm_dumps", S_IWUSR, d->debugfs_phy, + hw_priv, &fops_wsm_dumps)) + ERR_LINE; + + if (!debugfs_create_file("set_fwdbg", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_fwdbg)) + ERR_LINE; + + if (!debugfs_create_file("rw_fwreg", S_IWUSR, d->debugfs_phy, hw_priv, + &fops_rw_fwreg)) + ERR_LINE; + + if (!debugfs_create_file("rw_fwreg_direct", S_IWUSR, d->debugfs_phy, hw_priv, + &fops_rw_fwreg_direct)) + ERR_LINE; + + if (!debugfs_create_file("set_ampdu_len", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_ampdu_len)) + ERR_LINE; + + if (!debugfs_create_file("set_rts_threshold", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_rts_threshold)) + ERR_LINE; + + if (!debugfs_create_file("low_pwr_disable", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_low_pwr)) + ERR_LINE; + + if (!debugfs_create_file("ps_disable", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_ps_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("retry_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_retry_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("rates_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_rates_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("backoff_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_backoff_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("tala_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_tala_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("tx_pwr_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_tx_pwr_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("tpa_ctrl", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_tpa_ctrl)) + ERR_LINE; + + if (!debugfs_create_file("tpa_debug", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_tpa_debug)) + ERR_LINE; + + if (!debugfs_create_file("policy_info", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_policy_info)) + ERR_LINE; + + if (!debugfs_create_file("bh_stat", S_IRUSR, d->debugfs_phy, + hw_priv, &fops_bh_stat)) + ERR_LINE; + + if (!debugfs_create_file("parse_flags", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_parse_flags)) + ERR_LINE; + + if (!debugfs_create_file("set_measure_type", S_IRUSR | S_IWUSR, d->debugfs_phy, + hw_priv, &fops_11k)) + ERR_LINE; + + if (!debugfs_create_file("wsm_dump_size", S_IRUSR | S_IWUSR,d->debugfs_phy, + hw_priv, &fops_short_dump)) + ERR_LINE; + +#if defined(DGB_XRADIO_HWT) + //hardware test + if (!debugfs_create_file("hwt_hif_tx", S_IWUSR, d->debugfs_phy, + hw_priv, &fops_hwt_hif_tx)) + ERR_LINE; + + if (!debugfs_create_file("hwt_hif_rx", S_IWUSR, d->debugfs_phy, + hw_priv, &fops_hwt_hif_rx)) + ERR_LINE; + + if (!debugfs_create_file("hwt_enc", S_IWUSR, d->debugfs_phy, + hw_priv, &fops_hwt_enc)) + ERR_LINE; + + if (!debugfs_create_file("hwt_mic", S_IWUSR, d->debugfs_phy, + hw_priv, &fops_hwt_mic)) + ERR_LINE; +#endif //DGB_XRADIO_HWT + +#if defined(DGB_XRADIO_QC) + //for QC apk read. + if (debugfs_host && !debugfs_hwinfo) { + debugfs_hwinfo = debugfs_create_file("hwinfo", 0666, debugfs_host, + hw_priv, &fops_hwinfo); + if (!debugfs_hwinfo) + ERR_LINE; + } +#endif + + ret = xradio_itp_init(hw_priv); + if (ret) + ERR_LINE; + + return 0; + +#undef ERR_LINE + +err: + xradio_dbg(XRADIO_DBG_ERROR, "xradio_debug_init_common failed=%d\n", line); + hw_priv->debug = NULL; + debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + return ret; +} + +void xradio_debug_release_common(struct xradio_common *hw_priv) +{ + struct xradio_debug_common *d = hw_priv->debug; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#if defined(DGB_XRADIO_QC) + if (debugfs_hwinfo) { + debugfs_remove(debugfs_hwinfo); + debugfs_hwinfo = NULL; + } +#endif + if (d) { + xradio_itp_release(hw_priv); + hw_priv->debug = NULL; + //removed by mac80211, don't remove it again, fixed wifi on/off by yangfh + //debugfs_remove_recursive(d->debugfs_phy); + kfree(d); + } +} +#endif //CONFIG_XRADIO_DEBUGFS + +#define FRAME_TYPE(xx) ieee80211_is_ ## xx(fctl) +#define FT_MSG_PUT(f, ...) do{ \ + if(flags&f) frame_msg += sprintf(frame_msg, __VA_ARGS__); \ + }while(0) + +#define PT_MSG_PUT(f, ...) do{ \ + if(flags&f) proto_msg += sprintf(proto_msg, __VA_ARGS__); \ + }while(0) + +#define FRAME_PARSE(f, name) do{ \ + if(FRAME_TYPE(name)) { FT_MSG_PUT(f, "%s", #name); goto outprint;} \ + } while(0) + +#define IS_FRAME_PRINT (frame_msg != (char *)&framebuf[0]) +#define IS_PROTO_PRINT (proto_msg != (char *)&protobuf[0]) + + +char framebuf[512] = {0}; +char protobuf[512] = {0}; + +char * p2p_frame_type[] = { + "GO Negotiation Request", + "GO Negotiation Response", + "GO Negotiation Confirmation", + "P2P Invitation Request", + "P2P Invitation Response", + "Device Discoverability Request", + "Device Discoverability Response", + "Provision Discovery Request", + "Provision Discovery Response", + "Reserved" +}; +#if (defined(CONFIG_XRADIO_DEBUG)) +void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id) +{ + char * frame_msg = &framebuf[0]; + char * proto_msg = &protobuf[0]; + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)mac_data; + u16 fctl = frame->frame_control; + + memset(frame_msg, 0, sizeof(frame_msg)); + memset(proto_msg, 0, sizeof(proto_msg)); + + if(ieee80211_is_data(fctl)) { + u8 machdrlen = ieee80211_hdrlen(fctl); + u8* llc_data = mac_data+machdrlen+iv_len; + + if(ieee80211_is_qos_nullfunc(fctl)){ + FT_MSG_PUT(PF_DATA, "QoS-NULL"); + goto outprint; + } + if(ieee80211_is_data_qos(fctl)) + FT_MSG_PUT(PF_DATA, "QoS"); + if(ieee80211_is_nullfunc(fctl)) { + FT_MSG_PUT(PF_DATA, "NULL(ps=%d)", !!(fctl&IEEE80211_FCTL_PM)); + goto outprint; + } + FT_MSG_PUT(PF_DATA, "data(TDFD=%d%d,R=%d,P=%d)", + !!(fctl&IEEE80211_FCTL_TODS), !!(fctl&IEEE80211_FCTL_FROMDS), + !!(fctl&IEEE80211_FCTL_RETRY),!!(fctl&IEEE80211_FCTL_PROTECTED)); + + if (is_SNAP(llc_data)) { + if(is_ip(llc_data)) { + u8 * ip_hdr = llc_data+LLC_LEN; + u8 * ipaddr_s = ip_hdr+IP_S_ADD_OFF; + u8 * ipaddr_d = ip_hdr+IP_D_ADD_OFF; + u8 * proto_hdr = ip_hdr+((ip_hdr[0]&0xf)<<2); //ihl:words + + if(is_tcp(llc_data)) { + PT_MSG_PUT(PF_TCP, "TCP%s%s, src=%d, dest=%d, seq=0x%08x, ack=0x%08x", + (proto_hdr[13]&0x01)?"(S)":"", (proto_hdr[13]&0x02)?"(F)":"", + (proto_hdr[0]<<8)|proto_hdr[1], (proto_hdr[2]<<8)|proto_hdr[3], + (proto_hdr[4]<<24)|(proto_hdr[5]<<16)|(proto_hdr[6]<<8)|proto_hdr[7], + (proto_hdr[8]<<24)|(proto_hdr[9]<<16)|(proto_hdr[10]<<8)|proto_hdr[11]); + + } else if(is_udp(llc_data)) { + if(is_dhcp(llc_data)) { + u8 Options_len = BOOTP_OPS_LEN; + u32 dhcp_magic = cpu_to_be32(DHCP_MAGIC); + u8 * dhcphdr = proto_hdr+UDP_LEN+UDP_BOOTP_LEN; + while(Options_len) { + if(*(u32*)dhcphdr == dhcp_magic) + break; + dhcphdr++; + Options_len--; + } + PT_MSG_PUT(PF_DHCP, "DHCP, Opt=%d, MsgType=%d", *(dhcphdr+4), *(dhcphdr+6)); + } else { + PT_MSG_PUT(PF_UDP, "UDP, source=%d, dest=%d", + (proto_hdr[0]<<8)|proto_hdr[1], (proto_hdr[2]<<8)|proto_hdr[3]); + } + } else if(is_icmp(llc_data)) { + PT_MSG_PUT(PF_ICMP,"ICMP%s%s, Seq=%d", (8 == proto_hdr[0])?"(ping)":"", + (0 == proto_hdr[0])?"(reply)":"", (proto_hdr[6]<<8)|proto_hdr[7]); + } else if(is_igmp(llc_data)) { + PT_MSG_PUT(PF_UNKNWN, "IGMP, type=0x%x", proto_hdr[0]); + } else { + PT_MSG_PUT(PF_UNKNWN, "unknown IP type=%d", *(ip_hdr+IP_PROTO_OFF)); + } + if (IS_PROTO_PRINT) { + PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(s)", \ + ipaddr_s[0], ipaddr_s[1], ipaddr_s[2], ipaddr_s[3]); + PT_MSG_PUT(PF_IPADDR, "-%d.%d.%d.%d(d)", \ + ipaddr_d[0], ipaddr_d[1], ipaddr_d[2], ipaddr_d[3]); + } + + } else if (is_8021x(llc_data)) { + PT_MSG_PUT(PF_8021X, "8021X"); + } else { //other protol, no detail. + switch(cpu_to_be16(*(u16*)(llc_data+LLC_TYPE_OFF))) { + case ETH_P_IPV6: //0x08dd + PT_MSG_PUT(PF_UNKNWN, "IPv6"); + break; + case ETH_P_ARP: //0x0806 + PT_MSG_PUT(PF_UNKNWN, "ARP"); + break; + case ETH_P_RARP: //0x8035 + PT_MSG_PUT(PF_UNKNWN, "RARP"); + break; + case ETH_P_DNA_RC: //0x6002 + PT_MSG_PUT(PF_UNKNWN, "DNA Remote Console"); + break; + case ETH_P_DNA_RT: //0x6003 + PT_MSG_PUT(PF_UNKNWN, "DNA Routing"); + break; + case ETH_P_8021Q: //0x8100 + PT_MSG_PUT(PF_UNKNWN, "802.1Q VLAN"); + break; + case ETH_P_LINK_CTL: //0x886c + PT_MSG_PUT(PF_UNKNWN, "wlan link local tunnel(HPNA)"); + break; + case ETH_P_PPP_DISC: //0x8863 + PT_MSG_PUT(PF_UNKNWN, "PPPoE discovery"); + break; + case ETH_P_PPP_SES: //0x8864 + PT_MSG_PUT(PF_UNKNWN, "PPPoE session"); + break; + case ETH_P_MPLS_UC: //0x8847 + PT_MSG_PUT(PF_UNKNWN, "MPLS Unicast"); + break; + case ETH_P_MPLS_MC: //0x8848 + PT_MSG_PUT(PF_UNKNWN, "MPLS Multicast"); + break; + default: + PT_MSG_PUT(PF_UNKNWN, "unknown Ethernet type=0x%04x", cpu_to_be16(*(u16*)(llc_data+LLC_TYPE_OFF))); + break; + } + } + } else if (is_STP(llc_data)){ + //spanning tree proto. + PT_MSG_PUT(PF_UNKNWN, "spanning tree"); + } else { + PT_MSG_PUT(PF_UNKNWN, "unknown LLC type=0x%08x,0x%08x", *(u32*)(llc_data), *((u32*)(llc_data)+1)); + } + + } else if(ieee80211_is_mgmt(fctl) && (PF_MGMT&flags)) { + + FRAME_PARSE(PF_MGMT, auth ); + FRAME_PARSE(PF_MGMT, deauth ); + FRAME_PARSE(PF_MGMT, assoc_req ); + FRAME_PARSE(PF_MGMT, assoc_resp); + FRAME_PARSE(PF_MGMT, disassoc ); + FRAME_PARSE(PF_MGMT, atim ); + + //for more information about action frames. + if (FRAME_TYPE(action)) { + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame; + FT_MSG_PUT(PF_MGMT, "%s", "action"); + + if (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { + u8 *action = (u8*)&mgmt->u.action.category; + u32 oui = *(u32 *)&action[2]; + u8 oui_subtype = action[6] > 8? 9 : action[6]; + if (action[1] == 0x09 && oui == 0x099A6F50) + FT_MSG_PUT(PF_MGMT, "(%s)", p2p_frame_type[oui_subtype]); + } else if (mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) { + FT_MSG_PUT(PF_MGMT, "(ADDBA_REQ-%d)", mgmt->u.action.u.addba_req.start_seq_num); + } else if (mgmt->u.action.category == WLAN_CATEGORY_BACK && + mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_RESP) { + FT_MSG_PUT(PF_MGMT, "(ADDBA_RESP-%d)", mgmt->u.action.u.addba_resp.status); + } else { + FT_MSG_PUT(PF_MGMT, "(%d)", mgmt->u.action.category); + } + goto outprint; + } + + //too much scan results, don't print if no need. + FRAME_PARSE(PF_SCAN, probe_req ); + FRAME_PARSE(PF_SCAN, probe_resp ); + FRAME_PARSE(PF_SCAN, beacon ); + //must be last. + FT_MSG_PUT(PF_UNKNWN, "unknown mgmt"); + + } else if (ieee80211_is_ctl(fctl) && (PF_CTRL&flags)){ + + flags &= (~PF_MAC_SN); //no seq ctrl in ctrl frames. + FRAME_PARSE(PF_CTRL, back ); + FRAME_PARSE(PF_CTRL, back_req); + FRAME_PARSE(PF_CTRL, ack ); + FRAME_PARSE(PF_CTRL, rts ); + FRAME_PARSE(PF_CTRL, cts ); + FRAME_PARSE(PF_CTRL, pspoll ); + //must be last. + FT_MSG_PUT(PF_UNKNWN, "unknown ctrl"); + } else { + FT_MSG_PUT(PF_UNKNWN, "unknown mac frame, fctl=0x%04x\n", fctl); + } + +outprint: + + FT_MSG_PUT(PF_MAC_SN, "-SN=%d(%d)", (frame->seq_ctrl>>4), (frame->seq_ctrl&0xf)); + + //output all msg. + if(IS_FRAME_PRINT || IS_PROTO_PRINT) { + u8 *related = NULL; + u8 *own = NULL; + char *r_type = NULL; + char *o_type = NULL; + u8 machdrlen = ieee80211_hdrlen(fctl); + u8 *sa = ieee80211_get_SA(frame); + u8 *da = ieee80211_get_DA(frame); + + if (flags&PF_RX) { + related = frame->addr2; + own = frame->addr1; + r_type = "TA"; + o_type = "RA"; + } else { + related = frame->addr1; + own = frame->addr2; + r_type = "RA"; + o_type = "TA"; + } + + if (machdrlen >= 16) { //if ACK or BA, don't print. + FT_MSG_PUT(PF_MACADDR, "-%02x:%02x:%02x:%02x:%02x:%02x(%s)", + related[0], related[1], related[2], + related[3], related[4], related[5], + r_type); + FT_MSG_PUT(PF_OWNMAC, "-%02x:%02x:%02x:%02x:%02x:%02x(%s)", + own[0],own[1],own[2],own[3],own[4],own[5], + o_type); + FT_MSG_PUT(PF_SA_DA, "-%02x:%02x:%02x:%02x:%02x:%02x(DA)", + da[0], da[1], da[2], da[3], da[4], da[5]); + FT_MSG_PUT(PF_SA_DA, "-%02x:%02x:%02x:%02x:%02x:%02x(SA)", + sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); + } + + xradio_dbg(XRADIO_DBG_ALWY, "if%d-%s%s--%s\n", if_id, + (PF_RX&flags)?"RX-":"TX-",framebuf, protobuf); + } +} +#endif +#undef FT_MSG_PUT +#undef PT_MSG_PUT +#undef FRAME_PARSE +#undef FRAME_TYPE + +#ifdef DGB_LOG_FILE +u8 log_buffer[DGB_LOG_BUF_LEN]; +u16 log_pos = 0; +struct file *fp_log = NULL; +atomic_t file_ref = {0}; +#define T_LABEL_LEN 32 +char last_time_label[T_LABEL_LEN] = {0}; + +int xradio_logfile(char *buffer, int buf_len, u8 b_time) +{ + int ret=-1; + int size = buf_len; + mm_segment_t old_fs = get_fs(); + + if (!buffer) + return ret; + + if (buf_len < 0) + size = strlen(buffer); + if (!size) + return ret; + + if (atomic_add_return(1, &file_ref) == 1) { + fp_log = filp_open(DGB_LOG_PATH0, O_CREAT|O_WRONLY, 0666); + if (IS_ERR(fp_log)) { + printk(KERN_ERR "[XRADIO] ERR, can't open %s(%d).\n", + DGB_LOG_PATH0, (int)fp_log); + goto exit; + } + } + //printk(KERN_ERR "[XRADIO] file_ref=%d\n", atomic_read(&file_ref)); + + if(fp_log->f_op->write == NULL) { + printk(KERN_ERR "[XRADIO] ERR, %s:File is not allow to write!\n", + __FUNCTION__); + goto exit; + } else { + set_fs(KERNEL_DS); + if (fp_log->f_op->llseek != NULL) { + vfs_llseek(fp_log, 0, SEEK_END); + } else { + fp_log->f_pos = 0; + } + if (b_time) { + struct timeval time_now = {0}; + struct rtc_time tm; + int hour = 0; + char time_label[T_LABEL_LEN] = {0}; + do_gettimeofday(&time_now); + rtc_time_to_tm(time_now.tv_sec, &tm); + hour = tm.tm_hour-sys_tz.tz_minuteswest/60; + sprintf(time_label,"\n%d-%02d-%02d_%02d-%02d-%02d\n", + tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday+hour/24, + hour%24, tm.tm_min-sys_tz.tz_minuteswest%60, tm.tm_sec); + if (memcmp(last_time_label, time_label, T_LABEL_LEN)) { + memcpy(last_time_label, time_label, T_LABEL_LEN); + ret = vfs_write(fp_log, time_label, strlen(time_label), &fp_log->f_pos); + } + } + ret = vfs_write(fp_log, buffer, size, &fp_log->f_pos); + set_fs(old_fs); + } + +exit: + if (atomic_read(&file_ref) == 1) { + if (!IS_ERR(fp_log)) { + filp_close(fp_log, NULL); + fp_log = (struct file *)-ENOENT; + } + } + atomic_sub(1, &file_ref); + return ret; +} +#endif + +#if defined(DGB_XRADIO_HWT) +/***************************for HWT, yangfh********************************/ +struct sk_buff *hwt_skb = NULL; +int sent_num = 0; +int get_hwt_hif_tx(struct xradio_common *hw_priv, u8 **data, + size_t *tx_len, int *burst, int *vif_selected) +{ + + HWT_PARAMETERS *hwt_tx_hdr = NULL; + if (!hwt_tx_en || !hwt_tx_len || !hwt_tx_num || + sent_num >= hwt_tx_num) { + if (hwt_skb) { + dev_kfree_skb(hwt_skb); + hwt_skb = NULL; + } + return 0; + } + + if (!hwt_skb) { + hwt_skb = xr_alloc_skb(1504); + if (!hwt_skb) { + xradio_dbg(XRADIO_DBG_ERROR, "%s:skb is NULL!\n", __func__); + return 0; + } + if ((u32)hwt_skb->data & 3) { + u8 align = 4-((u32)hwt_skb->data & 3); + skb_reserve(hwt_skb, align); + } + skb_put(hwt_skb, 1500); + } + + //fill the header info + if (hwt_tx_len < sizeof(HWT_PARAMETERS)) + hwt_tx_len = sizeof(HWT_PARAMETERS); + if (hwt_tx_len > 1500) + hwt_tx_len = 1500; + hwt_tx_hdr = hwt_skb->data; + hwt_tx_hdr->MsgID = 0x0024; + hwt_tx_hdr->Msglen = hwt_tx_len; + hwt_tx_hdr->TestID = 0x0001; + hwt_tx_hdr->Data = 0x1234; + + //send the packet + *data = hwt_skb->data; + *tx_len = hwt_tx_hdr->Msglen; + *vif_selected = 0; + *burst = 2; //burst > 1 for continuous tx. + sent_num++; + + //first packet. + if (sent_num == 1) { + do_gettimeofday(&hwt_start_time); + } + + //set confirm + hwt_tx_hdr->Params = 0; + if (sent_num >= hwt_tx_num) { + hwt_tx_hdr->Params = 0x101; //last packet + hwt_tx_en = 0; //disable hwt_tx_en + xradio_dbg(XRADIO_DBG_ALWY, "%s:sent last packet!\n", __func__); + } else if (hwt_tx_cfm) { + hwt_tx_hdr->Params = !(sent_num%hwt_tx_cfm); + } + + return 1; +} +#endif diff --git a/drivers/net/wireless/xradio/debug.h b/drivers/net/wireless/xradio/debug.h new file mode 100644 index 0000000..e90708e --- /dev/null +++ b/drivers/net/wireless/xradio/debug.h @@ -0,0 +1,525 @@ +/* + * DebugFS code for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef XRADIO_DEBUG_H_INCLUDED +#define XRADIO_DEBUG_H_INCLUDED + +#include "itp.h" + +#define XRADIO_DBG_ALWY 0x01 /* Message always need to be present even in release version. */ +#define XRADIO_DBG_ERROR 0x02 /* Error message to report an error, it can hardly works. */ +#define XRADIO_DBG_WARN 0x04 /* Warning message to inform us of something unnormal or + * something very important, but it still work. */ +#define XRADIO_DBG_NIY 0x08 /* Important message we need to know in unstable version. */ +#define XRADIO_DBG_MSG 0x10 /* Normal message just for debug in developing stage. */ +#define XRADIO_DBG_TRC 0x20 /* Trace of functions, for sequence of functions called. Normally, + * don't set this level because there are too more print. */ +#define XRADIO_DBG_LEVEL 0xFF + +/*added by yangfh, for host debuglevel*/ +extern u8 dbg_common ; +extern u8 dbg_sbus ; +extern u8 dbg_bh ; +extern u8 dbg_txrx ; +extern u8 dbg_wsm ; +extern u8 dbg_sta ; +extern u8 dbg_scan ; +extern u8 dbg_ap ; +extern u8 dbg_pm ; +extern u8 dbg_itp ; +extern u8 dbg_logfile; + +/*for sdio clk debug*/ +extern u32 dbg_sdio_clk; + +/* for ps debug */ +extern u8 ps_disable; +extern u8 ps_idleperiod; +extern u8 ps_changeperiod; +//info of bh thread +extern u32 irq_count; +extern u32 int_miss_cnt; +extern u32 fix_miss_cnt; +extern u32 next_rx_cnt; +extern u32 rx_total_cnt; +extern u32 tx_total_cnt; + +#define WSM_DUMP_MAX_SIZE 20 + +#if defined(CONFIG_XRADIO_DEBUG) +//#define DGB_LOG_FILE //Don't log more than 500byte once. +//#define DGB_XRADIO_QC //Enable this only in QC test. +//#define DGB_XRADIO_HWT //Enable this only in hardware testmode using test fw. +#endif + +#ifdef DGB_LOG_FILE +#define DGB_LOG_BUF_LEN 1500 +#define DGB_LOG_PATH0 "/tmp/xradio_err.log" + +extern u8 log_buffer[DGB_LOG_BUF_LEN]; +extern u16 log_pos; +int xradio_logfile(char *buffer, int buf_len, u8 b_time); + +#define LOG_FILE(b_time, msg) xradio_logfile(msg, -1, b_time) +#define LOG_FILE_VARS(b_time, ...) do { \ + if (!log_pos) \ + memset(log_buffer, 0, DGB_LOG_BUF_LEN); \ + if (log_pos <= 1000) \ + log_pos += sprintf((char *)(log_buffer+log_pos), __VA_ARGS__); \ + if (xradio_logfile(log_buffer, log_pos, b_time) >= 0) \ + log_pos = 0; \ +} while (0) + +#else //DGB_LOG_FILE disable + +#define LOG_FILE(b_time, msg) +#define LOG_FILE_VARS(b_time, ...) +#endif + + +#if (defined(CONFIG_XRADIO_DEBUG)) +/****************************** debug version *******************************/ +#if (defined(CONFIG_XRADIO_DUMP_ON_ERROR)) +#define SYS_BUG(c) BUG_ON(c) +#define SYS_WARN(c) WARN_ON(c) +#else +#define SYS_BUG(c) WARN_ON(c) +#define SYS_WARN(c) WARN_ON(c) +#endif + +#define xradio_dbg(level, ...) \ + do { \ + if ((level) & dbg_common & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[XRADIO_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_common & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[XRADIO_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_common) \ + printk(KERN_ERR "[XRADIO] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[XRADIO_ERR] " __VA_ARGS__); \ + } while (0) + +#define sbus_printk(level, ...) \ + do { \ + if ((level) & dbg_sbus & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[SBUS_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_sbus & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[SBUS_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_sbus) \ + printk(KERN_ERR "[SBUS] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[SBUS_ERR] " __VA_ARGS__); \ + } while (0) + +#define txrx_printk(level, ...) \ + do { \ + if ((level) & dbg_txrx & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[TXRX_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_txrx & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[TXRX_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_txrx) \ + printk(KERN_ERR "[TXRX] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[TXRX_ERR] " __VA_ARGS__); \ + } while (0) + +#define bh_printk(level, ...) \ + do { \ + if ((level) & dbg_bh & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[BH_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_bh & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[BH_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_bh) \ + printk(KERN_ERR "[BH] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[BH_ERR] " __VA_ARGS__); \ + } while (0) + +#define wsm_printk(level, ...) \ + do { \ + if ((level) & dbg_wsm & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[WSM_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_wsm & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[WSM_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_wsm) \ + printk(KERN_ERR "[WSM] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[WSM_ERR] " __VA_ARGS__); \ + } while (0) + +#define sta_printk(level, ...) \ + do { \ + if ((level) & dbg_sta & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[STA_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_sta & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[STA_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_sta) \ + printk(KERN_ERR "[STA] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[STA_ERR] " __VA_ARGS__); \ + } while (0) + +#define scan_printk(level, ...) \ + do { \ + if ((level) & dbg_scan & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[SCAN_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_scan & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[SCAN_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_scan) \ + printk(KERN_ERR "[SCAN] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[SCAN_ERR] " __VA_ARGS__); \ + } while (0) + +#define ap_printk(level, ...) \ + do { \ + if ((level) & dbg_ap & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[AP_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_ap & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[AP_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_ap) \ + printk(KERN_ERR "[AP] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[AP_ERR] " __VA_ARGS__); \ + } while (0) + +#define pm_printk(level, ...) \ + do { \ + if ((level) & dbg_pm & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[PM_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_pm & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[PM_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_pm) \ + printk(KERN_ERR "[PM] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[PM_ERR] " __VA_ARGS__); \ + } while (0) + +#define itp_printk(level, ...) \ + do { \ + if ((level) & dbg_itp & XRADIO_DBG_ERROR) \ + printk(KERN_ERR "[ITP_ERR] " __VA_ARGS__); \ + else if ((level) & dbg_itp & XRADIO_DBG_WARN) \ + printk(KERN_ERR "[ITP_WRN] " __VA_ARGS__); \ + else if ((level) & dbg_itp) \ + printk(KERN_ERR "[ITP] " __VA_ARGS__); \ + if ((level) & dbg_logfile) \ + LOG_FILE_VARS(((level)&XRADIO_DBG_ERROR), \ + "[ITP_ERR] " __VA_ARGS__); \ + } while (0) + + +#define DBG_FUN_LINE printk(KERN_ERR "%s,line=%d", __FUNCTION__, __LINE__) +#define PARAM_CHECK_FALSE(p) \ + do { \ + if (!p) DBG_FUN_LINE; \ + } while (0) + +#define PARAM_CHECK_TRUE(p) \ + do { \ + if (p) DBG_FUN_LINE; \ + } while (0) + + +//interfaces to debug packet's information. +//for802.11 +#define PF_CTRL 0x0001 +#define PF_MGMT 0x0002 +#define PF_DATA 0x0004 +#define PF_SCAN 0x0008 + +//for ip data +#define PF_TCP 0x0010 +#define PF_UDP 0x0020 +#define PF_DHCP 0x0040 +#define PF_ICMP 0x0080 + +//for special frames or info. +#define PF_8021X 0x0100 //action frames +#define PF_MAC_SN 0x0200 //mac seq +#define PF_OWNMAC 0x0400 //TA in Tx, RA in Rx. +#define PF_SA_DA 0x0800 //SA, DA for Ethernet. + +#define PF_MACADDR 0x1000 //RA in Tx, TA in Rx. +#define PF_IPADDR 0x2000 //ip address of ip packets. +#define PF_UNKNWN 0x4000 //print unknown type frames of 802.11 flag you set. +#define PF_RX 0x8000 //0:TX, 1:RX. So, need to add PF_RX in Rx path. +void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id); + +#if defined(DGB_XRADIO_HWT) //hardware test +typedef struct HWT_PARAMETERS_S { + u16 Msglen; + u16 MsgID; + u16 TestID; + u16 Params; + u16 Datalen; + u16 Data; +} HWT_PARAMETERS; +int get_hwt_hif_tx(struct xradio_common *hw_priv, u8 **data, + size_t *tx_len, int *burst, int *vif_selected); +#endif //DGB_XRADIO_HWT + +#else +/****************************** release version *******************************/ +#define SYS_BUG(c) BUG_ON(c) +#define SYS_WARN(c) WARN_ON(c) + +#define xradio_dbg(level, ...) +#define sbus_printk(level, ...) +#define txrx_printk(level, ...) +#define bh_printk(level, ...) +#define wsm_printk(level, ...) +#define sta_printk(level, ...) +#define scan_printk(level, ...) +#define ap_printk(level, ...) +#define pm_printk(level, ...) +#define itp_printk(level, ...) + +#define DBG_FUN_LINE +#define PARAM_CHECK_FALSE +#define PARAM_CHECK_TRUE + +static inline void xradio_parse_frame(u8* mac_data, u8 iv_len, u16 flags, u8 if_id) +{ +} +#endif //CONFIG_XRADIO_DEBUG + +#ifdef CONFIG_XRADIO_DEBUGFS +/****************************** debugfs version *******************************/ +struct xradio_debug_common { + struct dentry *debugfs_phy; + int tx_cache_miss; + int tx_burst; + int rx_burst; + int ba_cnt; + int ba_acc; + int ba_cnt_rx; + int ba_acc_rx; +#ifdef CONFIG_XRADIO_ITP + struct xradio_itp itp; +#endif /* CONFIG_XRADIO_ITP */ +}; + +struct xradio_debug_priv { + struct dentry *debugfs_phy; + int tx; + int tx_agg; + int rx; + int rx_agg; + int tx_multi; + int tx_multi_frames; + int tx_align; + int tx_ttl; +}; + + +#define DBG_BH_IRQ_ADD irq_count++ +#define DBG_BH_MISS_ADD int_miss_cnt++ +#define DBG_BH_FIX_RX_ADD fix_miss_cnt++ +#define DBG_BH_NEXT_RX_ADD next_rx_cnt++ +#define DBG_BH_RX_TOTAL_ADD rx_total_cnt++ +#define DBG_BH_TX_TOTAL_ADD tx_total_cnt++ + +int xradio_debug_init_common(struct xradio_common *hw_priv); +int xradio_debug_init_priv(struct xradio_common *hw_priv, + struct xradio_vif *priv); +void xradio_debug_release_common(struct xradio_common *hw_priv); +void xradio_debug_release_priv(struct xradio_vif *priv); + +static inline void xradio_debug_txed(struct xradio_vif *priv) +{ + if (!priv->debug) + return; + ++priv->debug->tx; +} + +static inline void xradio_debug_txed_agg(struct xradio_vif *priv) +{ + if (!priv->debug) + return; + ++priv->debug->tx_agg; +} + +static inline void xradio_debug_txed_multi(struct xradio_vif *priv, + int count) +{ + if (!priv->debug) + return; + ++priv->debug->tx_multi; + priv->debug->tx_multi_frames += count; +} + +static inline void xradio_debug_rxed(struct xradio_vif *priv) +{ + if (!priv->debug) + return; + ++priv->debug->rx; +} + +static inline void xradio_debug_rxed_agg(struct xradio_vif *priv) +{ + if (!priv->debug) + return; + ++priv->debug->rx_agg; +} + +static inline void xradio_debug_tx_cache_miss(struct xradio_common *hw_priv) +{ + if (!hw_priv->debug) + return; + ++hw_priv->debug->tx_cache_miss; +} + +static inline void xradio_debug_tx_align(struct xradio_vif *priv) +{ + if (!priv->debug) + return; + ++priv->debug->tx_align; +} + +static inline void xradio_debug_tx_ttl(struct xradio_vif *priv) +{ + if (!priv->debug) + return; + ++priv->debug->tx_ttl; +} + +static inline void xradio_debug_tx_burst(struct xradio_common *hw_priv) +{ + if (!hw_priv->debug) + return; + ++hw_priv->debug->tx_burst; +} + +static inline void xradio_debug_rx_burst(struct xradio_common *hw_priv) +{ + if (!hw_priv->debug) + return; + ++hw_priv->debug->rx_burst; +} + +static inline void xradio_debug_ba(struct xradio_common *hw_priv, + int ba_cnt, int ba_acc, int ba_cnt_rx, + int ba_acc_rx) +{ + if (!hw_priv->debug) + return; + hw_priv->debug->ba_cnt = ba_cnt; + hw_priv->debug->ba_acc = ba_acc; + hw_priv->debug->ba_cnt_rx = ba_cnt_rx; + hw_priv->debug->ba_acc_rx = ba_acc_rx; +} + +int xradio_print_fw_version(struct xradio_common *hw_priv, u8* buf, size_t len); + +int xradio_host_dbg_init(void); +void xradio_host_dbg_deinit(void); + +#else /* CONFIG_XRADIO_DEBUGFS */ +/****************************** no debugfs version *******************************/ +#define DBG_BH_IRQ_ADD +#define DBG_BH_MISS_ADD +#define DBG_BH_FIX_RX_ADD +#define DBG_BH_NEXT_RX_ADD +#define DBG_BH_RX_TOTAL_ADD +#define DBG_BH_TX_TOTAL_ADD + +static inline int xradio_debug_init_common(struct xradio_common *hw_priv) +{ + return 0; +} + +static inline int xradio_debug_init_priv(struct xradio_common *hw_priv, + struct xradio_vif *priv) +{ + return 0; +} + +static inline void xradio_debug_release_common(struct xradio_common *hw_priv) +{ +} + +static inline void xradio_debug_release_priv(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_txed(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_txed_agg(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_txed_multi(struct xradio_vif *priv, + int count) +{ +} + +static inline void xradio_debug_rxed(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_rxed_agg(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_tx_cache_miss(struct xradio_common *common) +{ +} + +static inline void xradio_debug_tx_align(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_tx_ttl(struct xradio_vif *priv) +{ +} + +static inline void xradio_debug_tx_burst(struct xradio_common *hw_priv) +{ +} + +static inline void xradio_debug_rx_burst(struct xradio_common *hw_priv) +{ +} + +static inline void xradio_debug_ba(struct xradio_common *hw_priv, + int ba_cnt, int ba_acc, int ba_cnt_rx, + int ba_acc_rx) +{ +} + +static inline int xradio_print_fw_version(struct xradio_vif *priv, + u8* buf, size_t len) +{ + return 0; +} + +static inline int xradio_host_dbg_init(void) +{ + return 0; +} + +static inline void xradio_host_dbg_deinit(void) +{ +} +#endif /* CONFIG_XRADIO_DEBUGFS */ + +#endif /* XRADIO_DEBUG_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/fwio.c b/drivers/net/wireless/xradio/fwio.c new file mode 100644 index 0000000..3eb58ca --- /dev/null +++ b/drivers/net/wireless/xradio/fwio.c @@ -0,0 +1,650 @@ +/* + * Firmware I/O implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "xradio.h" +#include "fwio.h" +#include "hwio.h" +#include "sbus.h" +#include "bh.h" + +/* Macroses are local. */ +#define APB_WRITE(reg, val) \ + do { \ + ret = xradio_apb_write_32(hw_priv, APB_ADDR(reg), (val)); \ + if (ret < 0) { \ + xradio_dbg(XRADIO_DBG_ERROR, \ + "%s: can't write %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define APB_READ(reg, val) \ + do { \ + ret = xradio_apb_read_32(hw_priv, APB_ADDR(reg), &(val)); \ + if (ret < 0) { \ + xradio_dbg(XRADIO_DBG_ERROR, \ + "%s: can't read %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define REG_WRITE(reg, val) \ + do { \ + ret = xradio_reg_write_32(hw_priv, (reg), (val)); \ + if (ret < 0) { \ + xradio_dbg(XRADIO_DBG_ERROR, \ + "%s: can't write %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) +#define REG_READ(reg, val) \ + do { \ + ret = xradio_reg_read_32(hw_priv, (reg), &(val)); \ + if (ret < 0) { \ + xradio_dbg(XRADIO_DBG_ERROR, \ + "%s: can't read %s at line %d.\n", \ + __func__, #reg, __LINE__); \ + goto error; \ + } \ + } while (0) + + +static int xradio_get_hw_type(u32 config_reg_val, int *major_revision) +{ + int hw_type = -1; + u32 hif_type = (config_reg_val >> 24) & 0x4; + //u32 hif_vers = (config_reg_val >> 31) & 0x1; + + /* Check if we have XRADIO*/ + if (hif_type == 0x4) { + *major_revision = 0x4; + hw_type = HIF_HW_TYPE_XRADIO; + } else { + //hw type unknown. + *major_revision = 0x0; + } + return hw_type; +} + +/* + * This function is called to Parse the SDD file + * to extract some informations + */ +static int xradio_parse_sdd(struct xradio_common *hw_priv, u32 *dpll) +{ + int ret = 0; + const char *sdd_path = NULL; + struct xradio_sdd *pElement = NULL; + int parsedLength = 0; + + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + SYS_BUG(hw_priv->sdd != NULL); + + /* select and load sdd file depend on hardware version. */ + switch (hw_priv->hw_revision) { + case XR819_HW_REV0: + sdd_path = XR819_SDD_FILE; + break; + default: + xradio_dbg(XRADIO_DBG_ERROR, "%s: unknown hardware version.\n", __func__); + return ret; + } + +#ifdef USE_VFS_FIRMWARE + hw_priv->sdd = xr_request_file(sdd_path); + if (unlikely(!hw_priv->sdd)) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; + } +#else + ret = request_firmware(&hw_priv->sdd, sdd_path, hw_priv->pdev); + if (unlikely(ret)) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load sdd file %s.\n", + __func__, sdd_path); + return ret; + } +#endif + + //parse SDD config. + hw_priv->is_BT_Present = false; + pElement = (struct xradio_sdd *)hw_priv->sdd->data; + parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); + pElement = FIND_NEXT_ELT(pElement); + + while (parsedLength < hw_priv->sdd->size) { + switch (pElement->id) { + case SDD_PTA_CFG_ELT_ID: + hw_priv->conf_listen_interval = (*((u16 *)pElement->data+1) >> 7) & 0x1F; + hw_priv->is_BT_Present = true; + xradio_dbg(XRADIO_DBG_NIY, "PTA element found.Listen Interval %d\n", + hw_priv->conf_listen_interval); + break; + case SDD_REFERENCE_FREQUENCY_ELT_ID: + switch(*((uint16_t*)pElement->data)) { + case 0x32C8: + *dpll = 0x1D89D241; + break; + case 0x3E80: + *dpll = 0x1E1; + break; + case 0x41A0: + *dpll = 0x124931C1; + break; + case 0x4B00: + *dpll = 0x191; + break; + case 0x5DC0: + *dpll = 0x141; + break; + case 0x6590: + *dpll = 0x0EC4F121; + break; + case 0x8340: + *dpll = 0x92490E1; + break; + case 0x9600: + *dpll = 0x100010C1; + break; + case 0x9C40: + *dpll = 0xC1; + break; + case 0xBB80: + *dpll = 0xA1; + break; + case 0xCB20: + *dpll = 0x7627091; + break; + default: + *dpll = DPLL_INIT_VAL_XRADIO; + xradio_dbg(XRADIO_DBG_WARN, "Unknown Reference clock frequency." + "Use default DPLL value=0x%08x.", DPLL_INIT_VAL_XRADIO); + break; + } + default: + break; + } + parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); + pElement = FIND_NEXT_ELT(pElement); + } + + xradio_dbg(XRADIO_DBG_MSG, "sdd size=%d parse len=%d.\n", + hw_priv->sdd->size, parsedLength); + + // + if (hw_priv->is_BT_Present == false) { + hw_priv->conf_listen_interval = 0; + xradio_dbg(XRADIO_DBG_NIY, "PTA element NOT found.\n"); + } + return ret; +} + +static int xradio_firmware(struct xradio_common *hw_priv) +{ + int ret, block, num_blocks; + unsigned i; + u32 val32; + u32 put = 0, get = 0; + u8 *buf = NULL; + const char *fw_path; +#ifdef USE_VFS_FIRMWARE + const struct xr_file *firmware = NULL; +#else + const struct firmware *firmware = NULL; +#endif + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + switch (hw_priv->hw_revision) { + case XR819_HW_REV0: + fw_path = XR819_FIRMWARE; + break; + default: + xradio_dbg(XRADIO_DBG_ERROR, "%s: invalid silicon revision %d.\n", + __func__, hw_priv->hw_revision); + return -EINVAL; + } + /* Initialize common registers */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, DOWNLOAD_ARE_YOU_HERE); + APB_WRITE(DOWNLOAD_PUT_REG, 0); + APB_WRITE(DOWNLOAD_GET_REG, 0); + APB_WRITE(DOWNLOAD_STATUS_REG, DOWNLOAD_PENDING); + APB_WRITE(DOWNLOAD_FLAGS_REG, 0); + + /* Release CPU from RESET */ + REG_READ(HIF_CONFIG_REG_ID, val32); + val32 &= ~HIF_CONFIG_CPU_RESET_BIT; + REG_WRITE(HIF_CONFIG_REG_ID, val32); + + /* Enable Clock */ + val32 &= ~HIF_CONFIG_CPU_CLK_DIS_BIT; + REG_WRITE(HIF_CONFIG_REG_ID, val32); + + /* Load a firmware file */ +#ifdef USE_VFS_FIRMWARE + firmware = xr_fileopen(fw_path, O_RDONLY, 0); + if (!firmware) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load firmware file %s.\n", + __func__, fw_path); + ret = -1; + goto error; + } +#else + ret = request_firmware(&firmware, fw_path, hw_priv->pdev); + if (ret) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load firmware file %s.\n", + __func__, fw_path); + goto error; + } + SYS_BUG(!firmware->data); +#endif + + buf = xr_kmalloc(DOWNLOAD_BLOCK_SIZE, true); + if (!buf) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't allocate firmware buffer.\n", __func__); + ret = -ENOMEM; + goto error; + } + + /* Check if the bootloader is ready */ + for (i = 0; i < 100; i++/*= 1 + i / 2*/) { + APB_READ(DOWNLOAD_IMAGE_SIZE_REG, val32); + if (val32 == DOWNLOAD_I_AM_HERE) + break; + mdelay(10); + } /* End of for loop */ + if (val32 != DOWNLOAD_I_AM_HERE) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: bootloader is not ready.\n", __func__); + ret = -ETIMEDOUT; + goto error; + } + + /* Calculcate number of download blocks */ + num_blocks = (firmware->size - 1) / DOWNLOAD_BLOCK_SIZE + 1; + + /* Updating the length in Download Ctrl Area */ + val32 = firmware->size; /* Explicit cast from size_t to u32 */ + APB_WRITE(DOWNLOAD_IMAGE_SIZE_REG, val32); + + /* Firmware downloading loop */ + for (block = 0; block < num_blocks ; block++) { + size_t tx_size; + size_t block_size; + + /* check the download status */ + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: bootloader reported error %d.\n", + __func__, val32); + ret = -EIO; + goto error; + } + + /* loop until put - get <= 24K */ + for (i = 0; i < 100; i++) { + APB_READ(DOWNLOAD_GET_REG, get); + if ((put - get) <= (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) + break; + mdelay(i); + } + + if ((put - get) > (DOWNLOAD_FIFO_SIZE - DOWNLOAD_BLOCK_SIZE)) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: Timeout waiting for FIFO.\n", __func__); + ret = -ETIMEDOUT; + goto error; + } + + /* calculate the block size */ + tx_size = block_size = min((size_t)(firmware->size - put), (size_t)DOWNLOAD_BLOCK_SIZE); +#ifdef USE_VFS_FIRMWARE + ret = xr_fileread(firmware, buf, block_size); + if (ret < block_size) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: xr_fileread error %d.\n", __func__, ret); + goto error; + } +#else + memcpy(buf, &firmware->data[put], block_size); +#endif + if (block_size < DOWNLOAD_BLOCK_SIZE) { + memset(&buf[block_size], 0, DOWNLOAD_BLOCK_SIZE - block_size); + tx_size = DOWNLOAD_BLOCK_SIZE; + } + + /* send the block to sram */ + ret = xradio_apb_write(hw_priv, APB_ADDR(DOWNLOAD_FIFO_OFFSET + (put & (DOWNLOAD_FIFO_SIZE - 1))), + buf, tx_size); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't write block at line %d.\n", __func__, __LINE__); + goto error; + } + + /* update the put register */ + put += block_size; + APB_WRITE(DOWNLOAD_PUT_REG, put); + } /* End of firmware download loop */ + + /* Wait for the download completion */ + for (i = 0; i < 300; i += 1 + i / 2) { + APB_READ(DOWNLOAD_STATUS_REG, val32); + if (val32 != DOWNLOAD_PENDING) + break; + mdelay(i); + } + if (val32 != DOWNLOAD_SUCCESS) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: wait for download completion failed. " \ + "Read: 0x%.8X\n", __func__, val32); + ret = -ETIMEDOUT; + goto error; + } else { + xradio_dbg(XRADIO_DBG_ALWY, "Firmware completed.\n"); + ret = 0; + } + +error: + if(buf) + kfree(buf); + if (firmware) { +#ifdef USE_VFS_FIRMWARE + xr_fileclose(firmware); +#else + release_firmware(firmware); +#endif + } + return ret; +} + +static int xradio_bootloader(struct xradio_common *hw_priv) +{ + int ret = -1; + u32 i = 0; + const char *bl_path = XR819_BOOTLOADER; + u32 addr = AHB_MEMORY_ADDRESS; + u32 *data = NULL; +#ifdef USE_VFS_FIRMWARE + const struct xr_file *bootloader = NULL; +#else + const struct firmware *bootloader = NULL; +#endif + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef USE_VFS_FIRMWARE + bootloader = xr_request_file(bl_path); + if (!bootloader) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load bootloader file %s.\n", + __func__, bl_path); + goto error; + } +#else + /* Load a bootloader file */ + ret = request_firmware(&bootloader, bl_path, hw_priv->pdev); + if (ret) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't load bootloader file %s.\n", + __func__, bl_path); + goto error; + } +#endif + + xradio_dbg(XRADIO_DBG_NIY, "%s: bootloader size = %d, loopcount = %d\n", + __func__,bootloader->size, (bootloader->size)/4); + + /* Down bootloader. */ + data = (u32 *)bootloader->data; + for(i = 0; i < (bootloader->size)/4; i++) { + REG_WRITE(HIF_SRAM_BASE_ADDR_REG_ID, addr); + REG_WRITE(HIF_AHB_DPORT_REG_ID,data[i]); + if(i == 100 || i == 200 || i == 300 || i == 400 || i == 500 || i == 600 ) + xradio_dbg(XRADIO_DBG_NIY, "%s: addr = 0x%x,data = 0x%x\n",__func__,addr, data[i]); + addr += 4; + } + xradio_dbg(XRADIO_DBG_ALWY, "Bootloader complete\n"); + +error: + if(bootloader) { +#ifdef USE_VFS_FIRMWARE + xr_fileclose(bootloader); +#else + release_firmware(bootloader); +#endif + } + return ret; +} + +bool test_retry = false; +int xradio_load_firmware(struct xradio_common *hw_priv) +{ + int ret; + int i; + u32 val32; + u16 val16; + u32 dpll = 0; + int major_revision; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + SYS_BUG(!hw_priv); + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't read config register, err=%d.\n", + __func__, ret); + return ret; + } + + //check hardware type and revision. + hw_priv->hw_type = xradio_get_hw_type(val32, &major_revision); + switch (hw_priv->hw_type) { + case HIF_HW_TYPE_XRADIO: + xradio_dbg(XRADIO_DBG_NIY, "%s: HW_TYPE_XRADIO detected.\n", __func__); + break; + default: + xradio_dbg(XRADIO_DBG_ERROR, "%s: Unknown hardware: %d.\n", + __func__, hw_priv->hw_type); + return -ENOTSUPP; + } + if (major_revision == 4) { + hw_priv->hw_revision = XR819_HW_REV0; + xradio_dbg(XRADIO_DBG_ALWY, "XRADIO_HW_REV 1.0 detected.\n"); + } else { + xradio_dbg(XRADIO_DBG_ERROR, "%s: Unsupported major revision %d.\n", + __func__, major_revision); + return -ENOTSUPP; + } + + //load sdd file, and get config from it. + ret = xradio_parse_sdd(hw_priv, &dpll); + if (ret < 0) { + return ret; + } + + //set dpll initial value and check. + ret = xradio_reg_write_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, dpll); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't write DPLL register.\n", __func__); + goto out; + } + msleep(5); + ret = xradio_reg_read_32(hw_priv, HIF_TSET_GEN_R_W_REG_ID, &val32); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't read DPLL register.\n", __func__); + goto out; + } + if (val32 != dpll) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: unable to initialise " \ + "DPLL register. Wrote 0x%.8X, read 0x%.8X.\n", + __func__, dpll, val32); + ret = -EIO; + goto out; + } + + /* Set wakeup bit in device */ + ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &val16); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: set_wakeup: can't read control register.\n", + __func__); + goto out; + } + ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, val16 | HIF_CTRL_WUP_BIT); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: set_wakeup: can't write control register.\n", + __func__); + goto out; + } + + /* Wait for wakeup */ + for (i = 0 ; i < 300 ; i += 1 + i / 2) { + ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &val16); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: Wait_for_wakeup: " + "can't read control register.\n", __func__); + goto out; + } + if (val16 & HIF_CTRL_RDY_BIT) { + break; + } + msleep(i); + } + if ((val16 & HIF_CTRL_RDY_BIT) == 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: Wait for wakeup:" + "device is not responding.\n", __func__); + ret = -ETIMEDOUT; + goto out; + } else { + xradio_dbg(XRADIO_DBG_NIY, "WLAN device is ready.\n"); + } + + /* Checking for access mode and download firmware. */ + ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: check_access_mode: " + "can't read config register.\n", __func__); + goto out; + } + if (val32 & HIF_CONFIG_ACCESS_MODE_BIT) { + /* Down bootloader. */ + ret = xradio_bootloader(hw_priv); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't download bootloader.\n", __func__); + goto out; + } + /* Down firmware. */ + ret = xradio_firmware(hw_priv); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't download firmware.\n", __func__); + goto out; + } + } else { + xradio_dbg(XRADIO_DBG_WARN, "%s: check_access_mode: " + "device is already in QUEUE mode.\n", __func__); + /* TODO: verify this branch. Do we need something to do? */ + } + + /* Register Interrupt Handler */ + ret = hw_priv->sbus_ops->irq_subscribe(hw_priv->sbus_priv, + (sbus_irq_handler)xradio_irq_handler, + hw_priv); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: can't register IRQ handler.\n", __func__); + goto out; + } + + if (HIF_HW_TYPE_XRADIO == hw_priv->hw_type) { + /* If device is XRADIO the IRQ enable/disable bits + * are in CONFIG register */ + ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't read " \ + "config register.\n", __func__); + goto unsubscribe; + } + ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID, + val32 | HIF_CONF_IRQ_RDY_ENABLE); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't write " \ + "config register.\n", __func__); + goto unsubscribe; + } + } else { + /* If device is XRADIO the IRQ enable/disable bits + * are in CONTROL register */ + /* Enable device interrupts - Both DATA_RDY and WLAN_RDY */ + ret = xradio_reg_read_16(hw_priv, HIF_CONFIG_REG_ID, &val16); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't read " \ + "control register.\n", __func__); + goto unsubscribe; + } + ret = xradio_reg_write_16(hw_priv, HIF_CONFIG_REG_ID, + val16 | HIF_CTRL_IRQ_RDY_ENABLE); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: enable_irq: can't write " \ + "control register.\n", __func__); + goto unsubscribe; + } + + } + + /* Configure device for MESSSAGE MODE */ + ret = xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: set_mode: can't read config register.\n", + __func__); + goto unsubscribe; + } + ret = xradio_reg_write_32(hw_priv, HIF_CONFIG_REG_ID, + val32 & ~HIF_CONFIG_ACCESS_MODE_BIT); + if (ret < 0) { + xradio_dbg(XRADIO_DBG_ERROR, "%s: set_mode: can't write config register.\n", + __func__); + goto unsubscribe; + } + + /* Unless we read the CONFIG Register we are + * not able to get an interrupt */ + mdelay(10); + xradio_reg_read_32(hw_priv, HIF_CONFIG_REG_ID, &val32); + return 0; + +unsubscribe: + hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv); +out: + if (hw_priv->sdd) { +#ifdef USE_VFS_FIRMWARE + xr_fileclose(hw_priv->sdd); +#else + release_firmware(hw_priv->sdd); +#endif + hw_priv->sdd = NULL; + } + return ret; +} + +int xradio_dev_deinit(struct xradio_common *hw_priv) +{ + hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv); + if (hw_priv->sdd) { + #ifdef USE_VFS_FIRMWARE + xr_fileclose(hw_priv->sdd); + #else + release_firmware(hw_priv->sdd); + #endif + hw_priv->sdd = NULL; + } + return 0; +} +#undef APB_WRITE +#undef APB_READ +#undef REG_WRITE +#undef REG_READ \ No newline at end of file diff --git a/drivers/net/wireless/xradio/fwio.h b/drivers/net/wireless/xradio/fwio.h new file mode 100644 index 0000000..4816322 --- /dev/null +++ b/drivers/net/wireless/xradio/fwio.h @@ -0,0 +1,39 @@ +/* + * Firmware APIs for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef FWIO_H_INCLUDED +#define FWIO_H_INCLUDED + +#define XR819_HW_REV0 (8190) +#ifdef USE_VFS_FIRMWARE +#define XR819_BOOTLOADER ("/lib/firmware/xr819/boot_xr819.bin") +#define XR819_FIRMWARE ("/lib/firmware/xr819/fw_xr819.bin") +#define XR819_SDD_FILE ("/lib/firmware/xr819/sdd_xr819.bin") +#else +#define XR819_BOOTLOADER ("xr819/boot_xr819.bin") +#define XR819_FIRMWARE ("xr819/fw_xr819.bin") +#define XR819_SDD_FILE ("xr819/sdd_xr819.bin") +#endif + +#define SDD_PTA_CFG_ELT_ID 0xEB +#define SDD_REFERENCE_FREQUENCY_ELT_ID 0xC5 +#define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0) +#define FIND_NEXT_ELT(e) (struct xradio_sdd *)((u8 *)&e->data + e->length) +struct xradio_sdd { + u8 id; + u8 length; + u8 data[]; +}; + +struct xradio_common; +int xradio_load_firmware(struct xradio_common *hw_priv); +int xradio_dev_deinit(struct xradio_common *hw_priv); + +#endif diff --git a/drivers/net/wireless/xradio/ht.h b/drivers/net/wireless/xradio/ht.h new file mode 100644 index 0000000..827ce94 --- /dev/null +++ b/drivers/net/wireless/xradio/ht.h @@ -0,0 +1,41 @@ +/* + * HT-related code for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef XRADIO_HT_H_INCLUDED +#define XRADIO_HT_H_INCLUDED + +#include + +struct xradio_ht_info { + struct ieee80211_sta_ht_cap ht_cap; + enum nl80211_channel_type channel_type; + u16 operation_mode; +}; + +static inline int xradio_is_ht(const struct xradio_ht_info *ht_info) +{ + return ht_info->channel_type != NL80211_CHAN_NO_HT; +} + +static inline int xradio_ht_greenfield(const struct xradio_ht_info *ht_info) +{ + return (xradio_is_ht(ht_info) && + (ht_info->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ht_info->operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT)); +} + +static inline int xradio_ht_ampdu_density(const struct xradio_ht_info *ht_info) +{ + if (!xradio_is_ht(ht_info)) + return 0; + return ht_info->ht_cap.ampdu_density; +} + +#endif /* XRADIO_HT_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/hwio.c b/drivers/net/wireless/xradio/hwio.c new file mode 100644 index 0000000..9090f40 --- /dev/null +++ b/drivers/net/wireless/xradio/hwio.c @@ -0,0 +1,286 @@ +/* + * Hardware I/O implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include "xradio.h" +#include "hwio.h" +#include "sbus.h" + +#define CHECK_ADDR_LEN 1 + + /* Sdio addr is 4*spi_addr */ +#define SPI_REG_ADDR_TO_SDIO(spi_reg_addr) ((spi_reg_addr) << 2) +#define SDIO_ADDR17BIT(buf_id, mpf, rfu, reg_id_ofs) \ + ((((buf_id) & 0x1F) << 7) \ + | (((mpf) & 1) << 6) \ + | (((rfu) & 1) << 5) \ + | (((reg_id_ofs) & 0x1F) << 0)) +#define MAX_RETRY 3 + + +static int __xradio_read(struct xradio_common *hw_priv, u16 addr, + void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + +#if (CHECK_ADDR_LEN) + /* Check if buffer is aligned to 4 byte boundary */ + if (SYS_WARN(((unsigned long)buf & 3) && (buf_len > 4))) { + sbus_printk(XRADIO_DBG_ERROR, "%s: buffer is not aligned.\n", __func__); + return -EINVAL; + } +#endif + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + SYS_BUG(!hw_priv->sbus_ops); + return hw_priv->sbus_ops->sbus_data_read(hw_priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static int __xradio_write(struct xradio_common *hw_priv, u16 addr, + const void *buf, size_t buf_len, int buf_id) +{ + u16 addr_sdio; + u32 sdio_reg_addr_17bit ; + +#if (CHECK_ADDR_LEN) + /* Check if buffer is aligned to 4 byte boundary */ + if (SYS_WARN(((unsigned long)buf & 3) && (buf_len > 4))) { + sbus_printk(XRADIO_DBG_ERROR, "%s: buffer is not aligned.\n", __func__); + return -EINVAL; + } +#endif + + /* Convert to SDIO Register Address */ + addr_sdio = SPI_REG_ADDR_TO_SDIO(addr); + sdio_reg_addr_17bit = SDIO_ADDR17BIT(buf_id, 0, 0, addr_sdio); + + SYS_BUG(!hw_priv->sbus_ops); + return hw_priv->sbus_ops->sbus_data_write(hw_priv->sbus_priv, + sdio_reg_addr_17bit, + buf, buf_len); +} + +static inline int __xradio_read_reg32(struct xradio_common *hw_priv, + u16 addr, u32 *val) +{ + return __xradio_read(hw_priv, addr, val, sizeof(val), 0); +} + +static inline int __xradio_write_reg32(struct xradio_common *hw_priv, + u16 addr, u32 val) +{ + return __xradio_write(hw_priv, addr, &val, sizeof(val), 0); +} + +int xradio_reg_read(struct xradio_common *hw_priv, u16 addr, + void *buf, size_t buf_len) +{ + int ret; + SYS_BUG(!hw_priv->sbus_ops); + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + ret = __xradio_read(hw_priv, addr, buf, buf_len, 0); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} + +int xradio_reg_write(struct xradio_common *hw_priv, u16 addr, + const void *buf, size_t buf_len) +{ + int ret; + SYS_BUG(!hw_priv->sbus_ops); + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + ret = __xradio_write(hw_priv, addr, buf, buf_len, 0); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} + +int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len) +{ + int ret, retry = 1; + SYS_BUG(!hw_priv->sbus_ops); + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + { + int buf_id_rx = hw_priv->buf_id_rx; + while (retry <= MAX_RETRY) { + ret = __xradio_read(hw_priv, HIF_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_rx + 1); + if (!ret) { + buf_id_rx = (buf_id_rx + 1) & 3; + hw_priv->buf_id_rx = buf_id_rx; + break; + } else { + retry++; + mdelay(1); + sbus_printk(XRADIO_DBG_ERROR, "%s, error :[%d]\n", __func__, ret); + } + } + } + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} + +int xradio_data_write(struct xradio_common *hw_priv, const void *buf, + size_t buf_len) +{ + int ret, retry = 1; + SYS_BUG(!hw_priv->sbus_ops); + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + { + int buf_id_tx = hw_priv->buf_id_tx; + while (retry <= MAX_RETRY) { + ret = __xradio_write(hw_priv, HIF_IN_OUT_QUEUE_REG_ID, buf, + buf_len, buf_id_tx); + if (!ret) { + buf_id_tx = (buf_id_tx + 1) & 31; + hw_priv->buf_id_tx = buf_id_tx; + break; + } else { + retry++; + mdelay(1); + sbus_printk(XRADIO_DBG_ERROR, "%s,error :[%d]\n", __func__, ret); + } + } + } + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} + +int xradio_indirect_read(struct xradio_common *hw_priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr) +{ + u32 val32 = 0; + int i, ret; + + if ((buf_len / 2) >= 0x1000) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't read more than 0xfff words.\n", + __func__); + return -EINVAL; + goto out; + } + + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + /* Write address */ + ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write address register.\n", __func__); + goto out; + } + + /* Read CONFIG Register Value - We will read 32 bits */ + ret = __xradio_read_reg32(hw_priv, HIF_CONFIG_REG_ID, &val32); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't read config register.\n", __func__); + goto out; + } + + /* Set PREFETCH bit */ + ret = __xradio_write_reg32(hw_priv, HIF_CONFIG_REG_ID, val32 | prefetch); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write prefetch bit.\n", __func__); + goto out; + } + + /* Check for PRE-FETCH bit to be cleared */ + for (i = 0; i < 20; i++) { + ret = __xradio_read_reg32(hw_priv, HIF_CONFIG_REG_ID, &val32); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't check prefetch bit.\n", __func__); + goto out; + } + if (!(val32 & prefetch)) + break; + mdelay(i); + } + + if (val32 & prefetch) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Prefetch bit is not cleared.\n", __func__); + goto out; + } + + /* Read data port */ + ret = __xradio_read(hw_priv, port_addr, buf, buf_len, 0); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't read data port.\n", __func__); + goto out; + } + +out: + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} + +int xradio_apb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't wrire more than 0xfff words.\n", __func__); + return -EINVAL; + } + + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + + /* Write address */ + ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write address register.\n", __func__); + goto out; + } + + /* Write data port */ + ret = __xradio_write(hw_priv, HIF_SRAM_DPORT_REG_ID, buf, buf_len, 0); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write data port.\n", __func__); + goto out; + } + +out: + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} + +int xradio_ahb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, + size_t buf_len) +{ + int ret; + + if ((buf_len / 2) >= 0x1000) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't wrire more than 0xfff words.\n", __func__); + return -EINVAL; + } + + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + + /* Write address */ + ret = __xradio_write_reg32(hw_priv, HIF_SRAM_BASE_ADDR_REG_ID, addr); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write address register.\n", __func__); + goto out; + } + + /* Write data port */ + ret = __xradio_write(hw_priv, HIF_AHB_DPORT_REG_ID, buf, buf_len, 0); + if (ret < 0) { + sbus_printk(XRADIO_DBG_ERROR, "%s: Can't write data port.\n", __func__); + goto out; + } + +out: + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + return ret; +} diff --git a/drivers/net/wireless/xradio/hwio.h b/drivers/net/wireless/xradio/hwio.h new file mode 100644 index 0000000..531c2b8 --- /dev/null +++ b/drivers/net/wireless/xradio/hwio.h @@ -0,0 +1,229 @@ +/* + * hardware interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_HWIO_H_INCLUDED +#define XRADIO_HWIO_H_INCLUDED + +/* extern */ struct xradio_common; + +/* DPLL initial values */ +#define DPLL_INIT_VAL_XRADIO (0x0EC4F121) + +/* Hardware Type Definitions */ +#define HIF_HW_TYPE_XRADIO (1) + + +/* boot loader start address in SRAM */ +#define DOWNLOAD_BOOT_LOADER_OFFSET (0x00000000) +/* 32K, 0x4000 to 0xDFFF */ +#define DOWNLOAD_FIFO_OFFSET (0x00004000) +/* 32K */ +#define DOWNLOAD_FIFO_SIZE (0x00008000) +/* 128 bytes, 0xFF80 to 0xFFFF */ +#define DOWNLOAD_CTRL_OFFSET (0x0000FF80) +#define DOWNLOAD_CTRL_DATA_DWORDS (32-6) + +/* Download control area */ +struct download_cntl_t { + /* size of whole firmware file (including Cheksum), host init */ + u32 ImageSize; + /* downloading flags */ + u32 Flags; + /* No. of bytes put into the download, init & updated by host */ + u32 Put; + /* last traced program counter, last ARM reg_pc */ + u32 TracePc; + /* No. of bytes read from the download, host init, device updates */ + u32 Get; + /* r0, boot losader status, host init to pending, device updates */ + u32 Status; + /* Extra debug info, r1 to r14 if status=r0=DOWNLOAD_EXCEPTION */ + u32 DebugData[DOWNLOAD_CTRL_DATA_DWORDS]; +}; + +#define DOWNLOAD_IMAGE_SIZE_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, ImageSize)) +#define DOWNLOAD_FLAGS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Flags)) +#define DOWNLOAD_PUT_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Put)) +#define DOWNLOAD_TRACE_PC_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, TracePc)) +#define DOWNLOAD_GET_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Get)) +#define DOWNLOAD_STATUS_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, Status)) +#define DOWNLOAD_DEBUG_DATA_REG \ + (DOWNLOAD_CTRL_OFFSET + offsetof(struct download_cntl_t, DebugData)) + +#define DOWNLOAD_DEBUG_DATA_LEN (108) +#define DOWNLOAD_BLOCK_SIZE (1024) + +/* For boot loader detection */ +#define DOWNLOAD_ARE_YOU_HERE (0x87654321) +#define DOWNLOAD_I_AM_HERE (0x12345678) + +/* Download error code */ +#define DOWNLOAD_PENDING (0xFFFFFFFF) +#define DOWNLOAD_SUCCESS (0) +#define DOWNLOAD_EXCEPTION (1) +#define DOWNLOAD_ERR_MEM_1 (2) +#define DOWNLOAD_ERR_MEM_2 (3) +#define DOWNLOAD_ERR_SOFTWARE (4) +#define DOWNLOAD_ERR_FILE_SIZE (5) +#define DOWNLOAD_ERR_CHECKSUM (6) +#define DOWNLOAD_ERR_OVERFLOW (7) +#define DOWNLOAD_ERR_IMAGE (8) +#define DOWNLOAD_ERR_HOST (9) +#define DOWNLOAD_ERR_ABORT (10) + +#define SYS_BASE_ADDR_SILICON (0) +#define AHB_MEMORY_ADDRESS (SYS_BASE_ADDR_SILICON + 0x08000000) +#define PAC_BASE_ADDRESS_SILICON (SYS_BASE_ADDR_SILICON + 0x09000000) +#define PAC_SHARED_MEMORY_SILICON (PAC_BASE_ADDRESS_SILICON) +#define APB_ADDR(addr) (PAC_SHARED_MEMORY_SILICON + (addr)) + +/* *************************************************************** +*Device register definitions +*************************************************************** */ +/* WBF - SPI Register Addresses */ +#define HIF_ADDR_ID_BASE (0x0000) +/* 16/32 bits */ +#define HIF_CONFIG_REG_ID (0x0000) +/* 16/32 bits */ +#define HIF_CONTROL_REG_ID (0x0001) +/* 16 bits, Q mode W/R */ +#define HIF_IN_OUT_QUEUE_REG_ID (0x0002) +/* 32 bits, AHB bus R/W */ +#define HIF_AHB_DPORT_REG_ID (0x0003) +/* 16/32 bits */ +#define HIF_SRAM_BASE_ADDR_REG_ID (0x0004) +/* 32 bits, APB bus R/W */ +#define HIF_SRAM_DPORT_REG_ID (0x0005) +/* 32 bits, t_settle/general */ +#define HIF_TSET_GEN_R_W_REG_ID (0x0006) +/* 16 bits, Q mode read, no length */ +#define HIF_FRAME_OUT_REG_ID (0x0007) +#define HIF_ADDR_ID_MAX (HIF_FRAME_OUT_REG_ID) + +/* WBF - Control register bit set */ +/* next o/p length, bit 11 to 0 */ +#define HIF_CTRL_NEXT_LEN_MASK (0x0FFF) +#define HIF_CTRL_WUP_BIT (BIT(12)) +#define HIF_CTRL_RDY_BIT (BIT(13)) +#define HIF_CTRL_IRQ_ENABLE (BIT(14)) +#define HIF_CTRL_RDY_ENABLE (BIT(15)) +#define HIF_CTRL_IRQ_RDY_ENABLE (BIT(14)|BIT(15)) + +/* SPI Config register bit set */ +#define HIF_CONFIG_FRAME_BIT (BIT(2)) +#define HIF_CONFIG_WORD_MODE_BITS (BIT(3)|BIT(4)) +#define HIF_CONFIG_WORD_MODE_1 (BIT(3)) +#define HIF_CONFIG_WORD_MODE_2 (BIT(4)) +#define HIF_CONFIG_ERROR_0_BIT (BIT(5)) +#define HIF_CONFIG_ERROR_1_BIT (BIT(6)) +#define HIF_CONFIG_ERROR_2_BIT (BIT(7)) +/* TBD: Sure??? */ +#define HIF_CONFIG_CSN_FRAME_BIT (BIT(7)) +#define HIF_CONFIG_ERROR_3_BIT (BIT(8)) +#define HIF_CONFIG_ERROR_4_BIT (BIT(9)) +/* QueueM */ +#define HIF_CONFIG_ACCESS_MODE_BIT (BIT(10)) +/* AHB bus */ +#define HIF_CONFIG_AHB_PFETCH_BIT (BIT(11)) +#define HIF_CONFIG_CPU_CLK_DIS_BIT (BIT(12)) +/* APB bus */ +#define HIF_CONFIG_PFETCH_BIT (BIT(13)) +/* cpu reset */ +#define HIF_CONFIG_CPU_RESET_BIT (BIT(14)) +#define HIF_CONFIG_CLEAR_INT_BIT (BIT(15)) + +/* For XRADIO the IRQ Enable and Ready Bits are in CONFIG register */ +#define HIF_CONF_IRQ_RDY_ENABLE (BIT(16)|BIT(17)) + +int xradio_data_read(struct xradio_common *hw_priv, void *buf, size_t buf_len); +int xradio_data_write(struct xradio_common *hw_priv, const void *buf, size_t buf_len); +int xradio_reg_read(struct xradio_common *hw_priv, u16 addr, void *buf, size_t buf_len); +int xradio_reg_write(struct xradio_common *hw_priv, u16 addr, const void *buf, size_t buf_len); +int xradio_indirect_read(struct xradio_common *hw_priv, u32 addr, void *buf, + size_t buf_len, u32 prefetch, u16 port_addr); +int xradio_apb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, size_t buf_len); +int xradio_ahb_write(struct xradio_common *hw_priv, u32 addr, const void *buf, size_t buf_len); + + +static inline int xradio_reg_read_16(struct xradio_common *hw_priv, + u16 addr, u16 *val) +{ + int ret = 0; + u32 bigVal = 0; + ret = xradio_reg_read(hw_priv, addr, &bigVal, sizeof(bigVal)); + *val = (u16)bigVal; + return ret; +} + +static inline int xradio_reg_write_16(struct xradio_common *hw_priv, + u16 addr, u16 val) +{ + u32 bigVal = (u32)val; + return xradio_reg_write(hw_priv, addr, &bigVal, sizeof(bigVal)); +} + +static inline int xradio_reg_read_32(struct xradio_common *hw_priv, + u16 addr, u32 *val) +{ + return xradio_reg_read(hw_priv, addr, val, sizeof(val)); +} + +static inline int xradio_reg_write_32(struct xradio_common *hw_priv, + u16 addr, u32 val) +{ + return xradio_reg_write(hw_priv, addr, &val, sizeof(val)); +} + +static inline int xradio_apb_read(struct xradio_common *hw_priv, u32 addr, + void *buf, size_t buf_len) +{ + return xradio_indirect_read(hw_priv, addr, buf, buf_len, HIF_CONFIG_PFETCH_BIT, + HIF_SRAM_DPORT_REG_ID); +} + +static inline int xradio_ahb_read(struct xradio_common *hw_priv, u32 addr, + void *buf, size_t buf_len) +{ + return xradio_indirect_read(hw_priv, addr, buf, buf_len, HIF_CONFIG_AHB_PFETCH_BIT, + HIF_AHB_DPORT_REG_ID); +} + +static inline int xradio_apb_read_32(struct xradio_common *hw_priv, + u32 addr, u32 *val) +{ + return xradio_apb_read(hw_priv, addr, val, sizeof(val)); +} + +static inline int xradio_apb_write_32(struct xradio_common *hw_priv, + u32 addr, u32 val) +{ + return xradio_apb_write(hw_priv, addr, &val, sizeof(val)); +} + +static inline int xradio_ahb_read_32(struct xradio_common *hw_priv, + u32 addr, u32 *val) +{ + return xradio_ahb_read(hw_priv, addr, val, sizeof(val)); +} + +static inline int xradio_ahb_write_32(struct xradio_common *hw_priv, + u32 addr, u32 val) +{ + return xradio_ahb_write(hw_priv, addr, &val, sizeof(val)); +} + +#endif /* XRADIO_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/index.html b/drivers/net/wireless/xradio/index.html new file mode 100644 index 0000000..99f7b53 --- /dev/null +++ b/drivers/net/wireless/xradio/index.html @@ -0,0 +1,70 @@ + + + + +Index of /allwinner/h2/201609022/lichee/linux-3.4/drivers/net/wireless/xradio/ + + + +

Index of /allwinner/h2/201609022/lichee/linux-3.4/drivers/net/wireless/xradio/

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameLast ModifiedSizeType
Parent Directory/ -  Directory
Kconfig2016-Sep-22 09:50:361.4Kapplication/octet-stream
Makefile2016-Sep-22 09:50:361.5Kapplication/octet-stream
ap.c2016-Sep-22 09:50:3657.6Kapplication/octet-stream
ap.h2016-Sep-22 09:50:362.1Kapplication/octet-stream
bh.c2016-Sep-22 09:50:3626.8Kapplication/octet-stream
bh.h2016-Sep-22 09:50:361.2Kapplication/octet-stream
common.c2016-Sep-22 09:50:363.6Kapplication/octet-stream
common.h2016-Sep-22 09:50:364.4Kapplication/octet-stream
debug.c2016-Sep-22 09:50:3692.2Kapplication/octet-stream
debug.h2016-Sep-22 09:50:3614.7Kapplication/octet-stream
fwio.c2016-Sep-22 09:50:3617.1Kapplication/octet-stream
fwio.h2016-Sep-22 09:50:361.1Kapplication/octet-stream
ht.h2016-Sep-22 09:50:361.0Kapplication/octet-stream
hwio.c2016-Sep-22 09:50:367.7Kapplication/octet-stream
hwio.h2016-Sep-22 09:50:368.0Kapplication/octet-stream
itp.c2016-Sep-22 09:50:3617.8Kapplication/octet-stream
itp.h2016-Sep-22 09:50:363.1Kapplication/octet-stream
main.c2016-Sep-22 09:50:3634.5Kapplication/octet-stream
nl80211_testmode_msg_copy.h2016-Sep-22 09:50:363.9Kapplication/octet-stream
platform.c2016-Sep-22 09:50:3624.0Kapplication/octet-stream
platform.h2016-Sep-22 09:50:361.5Kapplication/octet-stream
pm.c2016-Sep-22 09:50:3622.8Kapplication/octet-stream
pm.h2016-Sep-22 09:50:361.5Kapplication/octet-stream
queue.c2016-Sep-22 09:50:3624.6Kapplication/octet-stream
queue.h2016-Sep-22 09:50:365.0Kapplication/octet-stream
sbus.h2016-Sep-22 09:50:361.6Kapplication/octet-stream
scan.c2016-Sep-22 09:50:3634.5Kapplication/octet-stream
scan.h2016-Sep-22 09:50:362.3Kapplication/octet-stream
sdio.c2016-Sep-22 09:50:369.1Kapplication/octet-stream
sta.c2016-Sep-22 09:50:36100.2Kapplication/octet-stream
sta.h2016-Sep-22 09:50:365.9Kapplication/octet-stream
txrx.c2016-Sep-22 09:50:3671.3Kapplication/octet-stream
txrx.h2016-Sep-22 09:50:362.8Kapplication/octet-stream
wsm.c2016-Sep-22 09:50:3688.1Kapplication/octet-stream
wsm.h2016-Sep-22 09:50:3662.0Kapplication/octet-stream
xr_version.h2016-Sep-22 09:50:360.4Kapplication/octet-stream
xradio.h2016-Sep-22 09:50:3618.0Kapplication/octet-stream
+
+
lighttpd/1.4.35
+ + diff --git a/drivers/net/wireless/xradio/itp.c b/drivers/net/wireless/xradio/itp.c new file mode 100644 index 0000000..0ae8ea5 --- /dev/null +++ b/drivers/net/wireless/xradio/itp.c @@ -0,0 +1,751 @@ +/* + * Test code implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include "xradio.h" +#include "itp.h" +#include "sta.h" + +static int __xradio_itp_open(struct xradio_common *priv); +static int __xradio_itp_close(struct xradio_common *priv); +static void xradio_itp_rx_start(struct xradio_common *priv); +static void xradio_itp_rx_stop(struct xradio_common *priv); +static void xradio_itp_rx_stats(struct xradio_common *priv); +static void xradio_itp_rx_reset(struct xradio_common *priv); +static void xradio_itp_tx_stop(struct xradio_common *priv); +static void xradio_itp_handle(struct xradio_common *priv, + struct sk_buff *skb); +static void xradio_itp_err(struct xradio_common *priv, + int err, + int arg); +static void __xradio_itp_tx_stop(struct xradio_common *priv); + +static ssize_t xradio_itp_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + struct xradio_common *priv = file->private_data; + struct xradio_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + int ret; + + if (skb_queue_empty(&itp->log_queue)) + return 0; + + skb = skb_dequeue(&itp->log_queue); + ret = copy_to_user(user_buf, skb->data, skb->len); + *ppos += skb->len; + skb->data[skb->len] = 0; + itp_printk(XRADIO_DBG_MSG, "[ITP] >>> %s", skb->data); + consume_skb(skb); + + return skb->len - ret; +} + +static ssize_t xradio_itp_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct xradio_common *priv = file->private_data; + struct sk_buff *skb; + + if (!count || count > 1024) + return -EINVAL; + skb = xr_alloc_skb(count + 1); + if (!skb) + return -ENOMEM; + skb_trim(skb, 0); + skb_put(skb, count + 1); + if (copy_from_user(skb->data, user_buf, count)) { + kfree_skb(skb); + return -EFAULT; + } + skb->data[count] = 0; + + xradio_itp_handle(priv, skb); + consume_skb(skb); + return count; +} + +static unsigned int xradio_itp_poll(struct file *file, poll_table *wait) +{ + struct xradio_common *priv = file->private_data; + struct xradio_itp *itp = &priv->debug->itp; + unsigned int mask = 0; + + poll_wait(file, &itp->read_wait, wait); + + if (!skb_queue_empty(&itp->log_queue)) + mask |= POLLIN | POLLRDNORM; + + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + +static int xradio_itp_open(struct inode *inode, struct file *file) +{ + struct xradio_common *priv = inode->i_private; + struct xradio_itp *itp = &priv->debug->itp; + int ret = 0; + + file->private_data = priv; + if (atomic_inc_return(&itp->open_count) == 1) { + ret = __xradio_itp_open(priv); + if (ret && !atomic_dec_return(&itp->open_count)) + __xradio_itp_close(priv); + } else { + atomic_dec(&itp->open_count); + ret = -EBUSY; + } + + return ret; +} + +static int xradio_itp_close(struct inode *inode, struct file *file) +{ + struct xradio_common *priv = file->private_data; + struct xradio_itp *itp = &priv->debug->itp; + if (!atomic_dec_return(&itp->open_count)) { + __xradio_itp_close(priv); + wake_up(&itp->close_wait); + } + return 0; +} + +static const struct file_operations fops_itp = { + .open = xradio_itp_open, + .read = xradio_itp_read, + .write = xradio_itp_write, + .poll = xradio_itp_poll, + .release = xradio_itp_close, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void xradio_itp_fill_pattern(u8 *data, int size, + enum xradio_itp_data_modes mode) +{ + u8 *p = data; + + if (size <= 0) + return; + + switch (mode) { + default: + case ITP_DATA_ZEROS: + memset(data, 0x0, size); + break; + case ITP_DATA_ONES: + memset(data, 0xff, size); + break; + case ITP_DATA_ZERONES: + memset(data, 0x55, size); + break; + case ITP_DATA_RANDOM: + while (p < data+size-sizeof(u32)) { + (*(u32 *)p) = random32(); + p += sizeof(u32); + } + while (p < data+size) { + (*p) = random32() & 0xFF; + p++; + } + break; + } + return; +} + +static void xradio_itp_tx_work(struct work_struct *work) +{ + struct xradio_itp *itp = container_of(work, struct xradio_itp, + tx_work.work); + struct xradio_common *priv = itp->priv; + atomic_set(&priv->bh_tx, 1); + +#ifdef BH_USE_SEMAPHORE + up(&priv->bh_sem); +#else + wake_up(&priv->bh_wq); +#endif +} + +static void xradio_itp_tx_finish(struct work_struct *work) +{ + struct xradio_itp *itp = container_of(work, struct xradio_itp, + tx_finish.work); + __xradio_itp_tx_stop(itp->priv); +} + +int xradio_itp_init(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + + itp->priv = priv; + atomic_set(&itp->open_count, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&itp->awaiting_confirm, 0); + skb_queue_head_init(&itp->log_queue); + spin_lock_init(&itp->tx_lock); + init_waitqueue_head(&itp->read_wait); + init_waitqueue_head(&itp->write_wait); + init_waitqueue_head(&itp->close_wait); + INIT_DELAYED_WORK(&itp->tx_work, xradio_itp_tx_work); + INIT_DELAYED_WORK(&itp->tx_finish, xradio_itp_tx_finish); + itp->data = NULL; + itp->hdr_len = WSM_TX_EXTRA_HEADROOM + + sizeof(struct ieee80211_hdr_3addr); + itp->id = 0; + + if (!debugfs_create_file("itp", S_IRUSR | S_IWUSR, + priv->debug->debugfs_phy, priv, &fops_itp)) + return -ENOMEM; + + return 0; +} + +void xradio_itp_release(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + + wait_event_interruptible(itp->close_wait, + !atomic_read(&itp->open_count)); + + SYS_WARN(atomic_read(&itp->open_count)); + + skb_queue_purge(&itp->log_queue); + xradio_itp_tx_stop(priv); +} + +static int __xradio_itp_open(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + + if (!priv->vif) + return -EINVAL; + if (priv->join_status) + return -EINVAL; + itp->saved_channel = priv->channel; + if (!priv->channel) + priv->channel = &priv->hw-> + wiphy->bands[IEEE80211_BAND_2GHZ]->channels[0]; + wsm_set_bssid_filtering(priv, false); + xradio_itp_rx_reset(priv); + return 0; +} + +static int __xradio_itp_close(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) + xradio_itp_rx_stop(priv); + xradio_itp_tx_stop(priv); + xradio_disable_listening(priv); + xradio_update_filtering(priv); + priv->channel = itp->saved_channel; + return 0; +} + +bool xradio_is_itp(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + return atomic_read(&itp->open_count) != 0; +} + +static void xradio_itp_rx_reset(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; +} + +static void xradio_itp_rx_start(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + + itp_printk(XRADIO_DBG_MSG, "[ITP] RX start, band = %d, ch = %d\n", + itp->band, itp->ch); + atomic_set(&itp->test_mode, TEST_MODE_RX_TEST); + xradio_disable_listening(priv, false); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + xradio_enable_listening(priv, priv->channel); + wsm_set_bssid_filtering(priv, false); +} + +static void xradio_itp_rx_stop(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + itp_printk(XRADIO_DBG_MSG, "[ITP] RX stop\n"); + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + xradio_itp_rx_reset(priv); +} + +static void xradio_itp_rx_stats(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[128]; + int len, ret; + struct wsm_counters_table counters; + + ret = wsm_get_counters_table(priv, &counters); + + if (ret) + xradio_itp_err(priv, -EBUSY, 20); + + if (!itp->rx_cnt) + len = snprintf(buf, sizeof(buf), "1,0,0,0,0,%d\n", + counters.countRxPacketErrors); + else + len = snprintf(buf, sizeof(buf), "1,%d,%ld,%d,%d,%d\n", + itp->rx_cnt, + itp->rx_cnt ? itp->rx_rssi / itp->rx_cnt : 0, + itp->rx_rssi_min, itp->rx_rssi_max, + counters.countRxPacketErrors); + + if (len <= 0) { + xradio_itp_err(priv, -EBUSY, 21); + return; + } + + skb = xr_alloc_skb(len); + if (!skb) { + xradio_itp_err(priv, -ENOMEM, 22); + return; + } + + itp->rx_cnt = 0; + itp->rx_rssi = 0; + itp->rx_rssi_max = -1000; + itp->rx_rssi_min = 1000; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +static void xradio_itp_tx_start(struct xradio_common *priv) +{ + struct wsm_tx *tx; + struct ieee80211_hdr_3addr *hdr; + struct xradio_itp *itp = &priv->debug->itp; + struct wsm_association_mode assoc_mode = { + .flags = WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE, + .preambleType = itp->preamble, + }; + int len; + u8 da_addr[6] = ITP_DEFAULT_DA_ADDR; + + /* Rates index 4 and 5 are not supported */ + if (itp->rate > 3) + itp->rate += 2; + + itp_printk(XRADIO_DBG_MSG, "[ITP] TX start: band = %d, ch = %d, rate = %d," + " preamble = %d, number = %d, data_mode = %d," + " interval = %d, power = %d, data_len = %d\n", + itp->band, itp->ch, itp->rate, itp->preamble, + itp->number, itp->data_mode, itp->interval_us, + itp->power, itp->data_len); + + len = itp->hdr_len + itp->data_len; + + itp->data = xr_kmalloc(len, false); + tx = (struct wsm_tx *)itp->data; + tx->hdr.len = itp->data_len + itp->hdr_len; + tx->hdr.id = __cpu_to_le16(0x0004 | 1 << 6); + tx->maxTxRate = itp->rate; + tx->queueId = 3; + tx->more = 0; + tx->flags = 0xc; + tx->packetID = 0; + tx->reserved = 0; + tx->expireTime = 0; + + if (itp->preamble == ITP_PREAMBLE_GREENFIELD) + tx->htTxParameters = WSM_HT_TX_GREENFIELD; + else if (itp->preamble == ITP_PREAMBLE_MIXED) + tx->htTxParameters = WSM_HT_TX_MIXED; + + hdr = (struct ieee80211_hdr_3addr *)&itp->data[sizeof(struct wsm_tx)]; + memset(hdr, 0, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_FCTL_TODS); + memcpy(hdr->addr1, da_addr, ETH_ALEN); + memcpy(hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(hdr->addr3, da_addr, ETH_ALEN); + + xradio_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + xradio_disable_listening(priv); + priv->channel = &priv->hw-> + wiphy->bands[itp->band]->channels[itp->ch]; + SYS_WARN(wsm_set_output_power(priv, itp->power)); + if (itp->preamble == ITP_PREAMBLE_SHORT || + itp->preamble == ITP_PREAMBLE_LONG) + SYS_WARN(wsm_set_association_mode(priv, + &assoc_mode)); + wsm_set_bssid_filtering(priv, false); + xradio_enable_listening(priv, priv->channel); + + spin_lock_bh(&itp->tx_lock); + atomic_set(&itp->test_mode, TEST_MODE_TX_TEST); + atomic_set(&itp->awaiting_confirm, 0); + atomic_set(&itp->stop_tx, 0); + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); +#ifdef BH_USE_SEMAPHORE + up(&priv->bh_sem); +#else + wake_up(&priv->bh_wq); +#endif + spin_unlock_bh(&itp->tx_lock); +} + +void __xradio_itp_tx_stop(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + spin_lock_bh(&itp->tx_lock); + kfree(itp->data); + itp->data = NULL; + atomic_set(&itp->test_mode, TEST_MODE_NO_TEST); + spin_unlock_bh(&itp->tx_lock); +} + +static void xradio_itp_tx_stop(struct xradio_common *priv) +{ + struct xradio_itp *itp = &priv->debug->itp; + itp_printk(XRADIO_DBG_MSG, "[ITP] TX stop\n"); + atomic_set(&itp->stop_tx, 1); + flush_workqueue(priv->workqueue); + + /* time for FW to confirm all tx requests */ + msleep(500); + + __xradio_itp_tx_stop(priv); +} + +static void xradio_itp_get_version(struct xradio_common *priv, + enum xradio_itp_version_type type) +{ + struct xradio_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + char buf[ITP_BUF_SIZE]; + size_t size = 0; + int len; + itp_printk(XRADIO_DBG_MSG, "[ITP] print %s version\n", type == ITP_CHIP_ID ? + "chip" : "firmware"); + + len = snprintf(buf, ITP_BUF_SIZE, "2,"); + if (len <= 0) { + xradio_itp_err(priv, -EINVAL, 40); + return; + } + size += len; + + switch (type) { + case ITP_CHIP_ID: + len = xradio_print_fw_version(priv, buf+size, + ITP_BUF_SIZE - size); + + if (len <= 0) { + xradio_itp_err(priv, -EINVAL, 41); + return; + } + size += len; + break; + case ITP_FW_VER: + len = snprintf(buf+size, ITP_BUF_SIZE - size, + "%d.%d", priv->wsm_caps.hardwareId, + priv->wsm_caps.hardwareSubId); + if (len <= 0) { + xradio_itp_err(priv, -EINVAL, 42); + return; + } + size += len; + break; + default: + xradio_itp_err(priv, -EINVAL, 43); + break; + } + + len = snprintf(buf+size, ITP_BUF_SIZE-size, "\n"); + if (len <= 0) { + xradio_itp_err(priv, -EINVAL, 44); + return; + } + size += len; + + skb = xr_alloc_skb(size); + if (!skb) { + xradio_itp_err(priv, -ENOMEM, 45); + return; + } + + skb_trim(skb, 0); + skb_put(skb, size); + + memcpy(skb->data, buf, size); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); +} + +int xradio_itp_get_tx(struct xradio_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + struct xradio_itp *itp; + struct wsm_tx *tx; + struct timespec now; + int time_left_us; + + if (!priv->debug) + return 0; + + itp = &priv->debug->itp; + + if (!itp) + return 0; + + spin_lock_bh(&itp->tx_lock); + if (atomic_read(&itp->test_mode) != TEST_MODE_TX_TEST) + goto out; + + if (atomic_read(&itp->stop_tx)) + goto out; + + if (itp->number == 0) { + atomic_set(&itp->stop_tx, 1); + queue_delayed_work(priv->workqueue, &itp->tx_finish, + HZ/10); + goto out; + } + + if (!itp->data) + goto out; + + if (priv->hw_bufs_used >= 2) { + if (!atomic_read(&priv->bh_rx)) + atomic_set(&priv->bh_rx, 1); + atomic_set(&priv->bh_tx, 1); + goto out; + } + + ktime_get_ts(&now); + time_left_us = (itp->last_sent.tv_sec - + now.tv_sec)*1000000 + + (itp->last_sent.tv_nsec - now.tv_nsec)/1000 + + itp->interval_us; + + if (time_left_us > ITP_TIME_THRES_US) { + queue_delayed_work(priv->workqueue, &itp->tx_work, + ITP_US_TO_MS(time_left_us)*HZ/1000); + goto out; + } + + if (time_left_us > 50) + udelay(time_left_us); + + if (itp->number > 0) + itp->number--; + + *data = itp->data; + *tx_len = itp->data_len + itp->hdr_len; + + if (itp->data_mode == ITP_DATA_RANDOM) + xradio_itp_fill_pattern(&itp->data[itp->hdr_len], + itp->data_len, itp->data_mode); + + tx = (struct wsm_tx *)itp->data; + tx->packetID = __cpu_to_le32(itp->id++); + *burst = 2; + atomic_set(&priv->bh_tx, 1); + ktime_get_ts(&itp->last_sent); + atomic_add(1, &itp->awaiting_confirm); + spin_unlock_bh(&itp->tx_lock); + return 1; + +out: + spin_unlock_bh(&itp->tx_lock); + return 0; +} + +bool xradio_itp_rxed(struct xradio_common *priv, struct sk_buff *skb) +{ + struct xradio_itp *itp = &priv->debug->itp; + struct ieee80211_rx_status *rx = IEEE80211_SKB_RXCB(skb); + int signal; + + if (atomic_read(&itp->test_mode) != TEST_MODE_RX_TEST) + return xradio_is_itp(priv); + if (rx->freq != priv->channel->center_freq) + return true; + + signal = rx->signal; + itp->rx_cnt++; + itp->rx_rssi += signal; + if (itp->rx_rssi_min > rx->signal) + itp->rx_rssi_min = rx->signal; + if (itp->rx_rssi_max < rx->signal) + itp->rx_rssi_max = rx->signal; + + return true; +} + +void xradio_itp_wake_up_tx(struct xradio_common *priv) +{ + wake_up(&priv->debug->itp.write_wait); +} + +bool xradio_itp_tx_running(struct xradio_common *priv) +{ + if (atomic_read(&priv->debug->itp.awaiting_confirm) || + atomic_read(&priv->debug->itp.test_mode) == + TEST_MODE_TX_TEST) { + atomic_sub(1, &priv->debug->itp.awaiting_confirm); + return true; + } + return false; +} + +static void xradio_itp_handle(struct xradio_common *priv, + struct sk_buff *skb) +{ + struct xradio_itp *itp = &priv->debug->itp; + const struct wiphy *wiphy = priv->hw->wiphy; + int cmd; + int ret; + + itp_printk(XRADIO_DBG_MSG, "[ITP] <<< %s", skb->data); + if (sscanf(skb->data, "%d", &cmd) != 1) { + xradio_itp_err(priv, -EINVAL, 1); + return; + } + + switch (cmd) { + case 1: /* RX test */ + if (atomic_read(&itp->test_mode)) { + xradio_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d", + &cmd, &itp->band, &itp->ch); + if (ret != 3) { + xradio_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + xradio_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + xradio_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + xradio_itp_err(priv, -EINVAL, 3); + else { + xradio_itp_rx_stats(priv); + xradio_itp_rx_start(priv); + } + break; + case 2: /* RX stat */ + xradio_itp_rx_stats(priv); + break; + case 3: /* RX/TX stop */ + if (atomic_read(&itp->test_mode) == TEST_MODE_RX_TEST) { + xradio_itp_rx_stats(priv); + xradio_itp_rx_stop(priv); + } else if (atomic_read(&itp->test_mode) == TEST_MODE_TX_TEST) { + xradio_itp_tx_stop(priv); + } else + xradio_itp_err(priv, -EBUSY, 0); + break; + case 4: /* TX start */ + if (atomic_read(&itp->test_mode) != TEST_MODE_NO_TEST) { + xradio_itp_err(priv, -EBUSY, 0); + return; + } + ret = sscanf(skb->data, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &cmd, &itp->band, &itp->ch, &itp->rate, + &itp->preamble, &itp->number, &itp->data_mode, + &itp->interval_us, &itp->power, &itp->data_len); + if (ret != 10) { + xradio_itp_err(priv, -EINVAL, ret + 1); + return; + } + if (itp->band >= 2) + xradio_itp_err(priv, -EINVAL, 2); + else if (!wiphy->bands[itp->band]) + xradio_itp_err(priv, -EINVAL, 2); + else if (itp->ch >= + wiphy->bands[itp->band]->n_channels) + xradio_itp_err(priv, -EINVAL, 3); + else if (itp->rate >= 20) + xradio_itp_err(priv, -EINVAL, 4); + else if (itp->preamble >= ITP_PREAMBLE_MAX) + xradio_itp_err(priv, -EINVAL, 5); + else if (itp->data_mode >= ITP_DATA_MAX_MODE) + xradio_itp_err(priv, -EINVAL, 7); + else if (itp->data_len < ITP_MIN_DATA_SIZE || + itp->data_len > priv->wsm_caps.sizeInpChBuf - + itp->hdr_len) + xradio_itp_err(priv, -EINVAL, 8); + else { + xradio_itp_tx_start(priv); + } + break; + case 5: + xradio_itp_get_version(priv, ITP_CHIP_ID); + break; + case 6: + xradio_itp_get_version(priv, ITP_FW_VER); + break; + + } +} + +static void xradio_itp_err(struct xradio_common *priv, + int err, int arg) +{ + struct xradio_itp *itp = &priv->debug->itp; + struct sk_buff *skb; + static char buf[255]; + int len; + + len = snprintf(buf, sizeof(buf), "%d,%d\n", + err, arg); + if (len <= 0) + return; + + skb = xr_alloc_skb(len); + if (!skb) + return; + + skb_trim(skb, 0); + skb_put(skb, len); + + memcpy(skb->data, buf, len); + skb_queue_tail(&itp->log_queue, skb); + wake_up(&itp->read_wait); + + len = sprint_symbol(buf, + (unsigned long)__builtin_return_address(0)); + if (len <= 0) + return; + itp_printk(XRADIO_DBG_MSG, "[ITP] error %d,%d from %s\n", + err, arg, buf); +} diff --git a/drivers/net/wireless/xradio/itp.h b/drivers/net/wireless/xradio/itp.h new file mode 100644 index 0000000..0d4475e --- /dev/null +++ b/drivers/net/wireless/xradio/itp.h @@ -0,0 +1,150 @@ +/* + * ITP interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_ITP_H_INCLUDED +#define XRADIO_ITP_H_INCLUDED + +struct wsm_tx_confirm; +struct dentry; + +#ifdef CONFIG_XRADIO_ITP + +/*extern*/ struct ieee80211_channel; + +#define TEST_MODE_NO_TEST (0) +#define TEST_MODE_RX_TEST (1) +#define TEST_MODE_TX_TEST (2) + +#define ITP_DEFAULT_DA_ADDR {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} +#define ITP_MIN_DATA_SIZE 6 +#define ITP_MAX_DATA_SIZE 1600 +#define ITP_TIME_THRES_US 10000 +#define ITP_US_TO_MS(x) ((x)/1000) +#define ITP_MS_TO_US(x) ((x)*1000) +#if ((ITP_US_TO_MS(ITP_TIME_THRES_US))*HZ/1000) < 1 +#warning not able to achieve non-busywaiting ITP_TIME_THRES_US\ +precision with current HZ value ! +#endif +#define ITP_BUF_SIZE 255 + + +enum xradio_itp_data_modes { + ITP_DATA_ZEROS, + ITP_DATA_ONES, + ITP_DATA_ZERONES, + ITP_DATA_RANDOM, + ITP_DATA_MAX_MODE, +}; + +enum xradio_itp_version_type { + ITP_CHIP_ID, + ITP_FW_VER, +}; + +enum xradio_itp_preamble_type { + ITP_PREAMBLE_LONG, + ITP_PREAMBLE_SHORT, + ITP_PREAMBLE_OFDM, + ITP_PREAMBLE_MIXED, + ITP_PREAMBLE_GREENFIELD, + ITP_PREAMBLE_MAX, +}; + + +struct xradio_itp { + struct xradio_common *priv; + atomic_t open_count; + atomic_t awaiting_confirm; + struct sk_buff_head log_queue; + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; + wait_queue_head_t close_wait; + struct ieee80211_channel *saved_channel; + atomic_t stop_tx; + struct delayed_work tx_work; + struct delayed_work tx_finish; + spinlock_t tx_lock; + struct timespec last_sent; + atomic_t test_mode; + int rx_cnt; + long rx_rssi; + int rx_rssi_max; + int rx_rssi_min; + unsigned band; + unsigned ch; + unsigned rate; + unsigned preamble; + unsigned int number; + unsigned data_mode; + int interval_us; + int power; + u8 *data; + int hdr_len; + int data_len; + int id; +}; + +int xradio_itp_init(struct xradio_common *priv); +void xradio_itp_release(struct xradio_common *priv); + +bool xradio_is_itp(struct xradio_common *priv); +bool xradio_itp_rxed(struct xradio_common *priv, struct sk_buff *skb); +void xradio_itp_wake_up_tx(struct xradio_common *priv); +int xradio_itp_get_tx(struct xradio_common *priv, u8 **data, + size_t *tx_len, int *burst); +bool xradio_itp_tx_running(struct xradio_common *priv); + +#else /* CONFIG_XRADIO_ITP */ + +static inline int +xradio_itp_init(struct xradio_common *priv) +{ + return 0; +} + +static inline void xradio_itp_release(struct xradio_common *priv) +{ +} + +static inline bool xradio_is_itp(struct xradio_common *priv) +{ + return false; +} + +static inline bool xradio_itp_rxed(struct xradio_common *priv, + struct sk_buff *skb) +{ + return false; +} + + +static inline void xradio_itp_consume_txed(struct xradio_common *priv) +{ +} + +static inline void xradio_itp_wake_up_tx(struct xradio_common *priv) +{ +} + +static inline int xradio_itp_get_tx(struct xradio_common *priv, u8 **data, + size_t *tx_len, int *burst) +{ + return 0; +} + +static inline bool xradio_itp_tx_running(struct xradio_common *priv) +{ + return false; +} + +#endif /* CONFIG_XRADIO_ITP */ + +#endif /* XRADIO_ITP_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/main.c b/drivers/net/wireless/xradio/main.c new file mode 100644 index 0000000..098334b --- /dev/null +++ b/drivers/net/wireless/xradio/main.c @@ -0,0 +1,1195 @@ +/* + * Main code of XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/*Linux version 3.4.0 compilation*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "xradio.h" +#include "txrx.h" +#include "sbus.h" +#include "fwio.h" +#include "hwio.h" +#include "bh.h" +#include "sta.h" +#include "ap.h" +#include "scan.h" +#include "pm.h" +#include "xr_version.h" + +MODULE_AUTHOR("XRadioTech"); +MODULE_DESCRIPTION("XRadioTech WLAN driver core"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("xradio_core"); + +char *drv_version = XRADIO_VERSION; +char *drv_buildtime = __DATE__" "__TIME__; + +#define XRADIO_MAC_CHARLEN 18 +#ifdef XRADIO_MACPARAM_HEX +/* insmod xradio_wlan.ko macaddr=0xDC,0x44,0x6D,0x00,0x00,0x00 */ +static u8 xradio_macaddr_param[ETH_ALEN] = { 0x0 }; +module_param_array_named(macaddr, xradio_macaddr_param, byte, NULL, S_IRUGO); +#else +/* insmod xradio_wlan.ko macaddr=xx:xx:xx:xx:xx:xx */ +static char *xradio_macaddr_param = NULL; +module_param_named(macaddr, xradio_macaddr_param, charp, S_IRUGO); +#endif + +MODULE_PARM_DESC(macaddr, "First MAC address"); + +#ifdef HW_RESTART +void xradio_restart_work(struct work_struct *work); +#endif + +/* TODO: use rates and channels from the device */ +#define RATETAB_ENT(_rate, _rateid, _flags) \ + { \ + .bitrate = (_rate), \ + .hw_value = (_rateid), \ + .flags = (_flags), \ + } + +static struct ieee80211_rate xradio_rates[] = { + RATETAB_ENT(10, 0, 0), + RATETAB_ENT(20, 1, 0), + RATETAB_ENT(55, 2, 0), + RATETAB_ENT(110, 3, 0), + RATETAB_ENT(60, 6, 0), + RATETAB_ENT(90, 7, 0), + RATETAB_ENT(120, 8, 0), + RATETAB_ENT(180, 9, 0), + RATETAB_ENT(240, 10, 0), + RATETAB_ENT(360, 11, 0), + RATETAB_ENT(480, 12, 0), + RATETAB_ENT(540, 13, 0), +}; + +static struct ieee80211_rate xradio_mcs_rates[] = { + RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS), + RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS), + RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS), + RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS), + RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS), + RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS), + RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS), + RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS), +}; + +#define xradio_g_rates (xradio_rates + 0) +#define xradio_a_rates (xradio_rates + 4) +#define xradio_n_rates (xradio_mcs_rates) + +#define xradio_g_rates_size (ARRAY_SIZE(xradio_rates)) +#define xradio_a_rates_size (ARRAY_SIZE(xradio_rates) - 4) +#define xradio_n_rates_size (ARRAY_SIZE(xradio_mcs_rates)) + +#define CHAN2G(_channel, _freq, _flags) { \ + .band = IEEE80211_BAND_2GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +#define CHAN5G(_channel, _flags) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = 5000 + (5 * (_channel)), \ + .hw_value = (_channel), \ + .flags = (_flags), \ + .max_antenna_gain = 0, \ + .max_power = 30, \ +} + +static struct ieee80211_channel xradio_2ghz_chantable[] = { + CHAN2G(1, 2412, 0), + CHAN2G(2, 2417, 0), + CHAN2G(3, 2422, 0), + CHAN2G(4, 2427, 0), + CHAN2G(5, 2432, 0), + CHAN2G(6, 2437, 0), + CHAN2G(7, 2442, 0), + CHAN2G(8, 2447, 0), + CHAN2G(9, 2452, 0), + CHAN2G(10, 2457, 0), + CHAN2G(11, 2462, 0), + CHAN2G(12, 2467, 0), + CHAN2G(13, 2472, 0), + CHAN2G(14, 2484, 0), +}; + +#ifdef CONFIG_XRADIO_5GHZ_SUPPORT +static struct ieee80211_channel xradio_5ghz_chantable[] = { + CHAN5G(34, 0), CHAN5G(36, 0), + CHAN5G(38, 0), CHAN5G(40, 0), + CHAN5G(42, 0), CHAN5G(44, 0), + CHAN5G(46, 0), CHAN5G(48, 0), + CHAN5G(52, 0), CHAN5G(56, 0), + CHAN5G(60, 0), CHAN5G(64, 0), + CHAN5G(100, 0), CHAN5G(104, 0), + CHAN5G(108, 0), CHAN5G(112, 0), + CHAN5G(116, 0), CHAN5G(120, 0), + CHAN5G(124, 0), CHAN5G(128, 0), + CHAN5G(132, 0), CHAN5G(136, 0), + CHAN5G(140, 0), CHAN5G(149, 0), + CHAN5G(153, 0), CHAN5G(157, 0), + CHAN5G(161, 0), CHAN5G(165, 0), + CHAN5G(184, 0), CHAN5G(188, 0), + CHAN5G(192, 0), CHAN5G(196, 0), + CHAN5G(200, 0), CHAN5G(204, 0), + CHAN5G(208, 0), CHAN5G(212, 0), + CHAN5G(216, 0), +}; +#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ + +static struct ieee80211_supported_band xradio_band_2ghz = { + .channels = xradio_2ghz_chantable, + .n_channels = ARRAY_SIZE(xradio_2ghz_chantable), + .bitrates = xradio_g_rates, + .n_bitrates = xradio_g_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; + +#ifdef CONFIG_XRADIO_5GHZ_SUPPORT +static struct ieee80211_supported_band xradio_band_5ghz = { + .channels = xradio_5ghz_chantable, + .n_channels = ARRAY_SIZE(xradio_5ghz_chantable), + .bitrates = xradio_a_rates, + .n_bitrates = xradio_a_rates_size, + .ht_cap = { + .cap = IEEE80211_HT_CAP_GRN_FLD | + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT), + .ht_supported = 1, + .ampdu_factor = IEEE80211_HT_MAX_AMPDU_8K, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE, + .mcs = { + .rx_mask[0] = 0xFF, + .rx_highest = __cpu_to_le16(0x41), + .tx_params = IEEE80211_HT_MCS_TX_DEFINED, + }, + }, +}; +#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ + +static const unsigned long xradio_ttl[] = { + 1 * HZ, /* VO */ + 2 * HZ, /* VI */ + 5 * HZ, /* BE */ + 10 * HZ /* BK */ +}; + +static const struct ieee80211_ops xradio_ops = { + .start = xradio_start, + .stop = xradio_stop, + .add_interface = xradio_add_interface, + .remove_interface = xradio_remove_interface, + .change_interface = xradio_change_interface, + .tx = xradio_tx, + .hw_scan = xradio_hw_scan, +#ifdef ROAM_OFFLOAD + .sched_scan_start = xradio_hw_sched_scan_start, + .sched_scan_stop = xradio_hw_sched_scan_stop, +#endif /*ROAM_OFFLOAD*/ + .set_tim = xradio_set_tim, + .sta_notify = xradio_sta_notify, + .sta_add = xradio_sta_add, + .sta_remove = xradio_sta_remove, + .set_key = xradio_set_key, + .set_rts_threshold = xradio_set_rts_threshold, + .config = xradio_config, + .bss_info_changed = xradio_bss_info_changed, + .prepare_multicast = xradio_prepare_multicast, + .configure_filter = xradio_configure_filter, + .conf_tx = xradio_conf_tx, + .get_stats = xradio_get_stats, + .ampdu_action = xradio_ampdu_action, + .flush = xradio_flush, +#ifdef CONFIG_PM + .suspend = xradio_wow_suspend, + .resume = xradio_wow_resume, +#endif /* CONFIG_PM */ + /* Intentionally not offloaded: */ + /*.channel_switch = xradio_channel_switch, */ + .remain_on_channel = xradio_remain_on_channel, + .cancel_remain_on_channel = xradio_cancel_remain_on_channel, +#ifdef IPV6_FILTERING + /*in linux3.4 mac,it does't have the api*/ + //.set_data_filter = xradio_set_data_filter, +#endif /*IPV6_FILTERING*/ +#ifdef CONFIG_XRADIO_TESTMODE + .testmode_cmd = xradio_testmode_cmd, +#endif /* CONFIG_XRADIO_TESTMODE */ +}; + +struct xradio_common *g_hw_priv; + +/*************************************** functions ***************************************/ +void xradio_version_show(void) +{ +/* Show XRADIO version and compile time */ + xradio_dbg(XRADIO_DBG_ALWY, "Driver Label:%s %s\n", + DRV_VERSION, DRV_BUILDTIME); + +/************* Linux Kernel config *************/ +#ifdef CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_NON_POWER_OF_TWO_BLOCKSIZES]\n"); +#endif + +#ifdef CONFIG_XRADIO_USE_GPIO_IRQ + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_GPIO_IRQ]\n"); +#endif + +#ifdef CONFIG_XRADIO_5GHZ_SUPPORT + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_5GHZ_SUPPORT]\n"); +#endif + +#ifdef CONFIG_XRADIO_WAPI_SUPPORT + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_WAPI_SUPPORT]\n"); +#endif + +#ifdef CONFIG_XRADIO_USE_EXTENSIONS + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_EXTENSIONS]\n"); +#endif + +#ifdef CONFIG_XRADIO_USE_EXTENSIONS + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_USE_EXTENSIONS]\n"); +#endif + +#ifdef CONFIG_PM + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_PM]\n"); +#endif + +#ifdef CONFIG_XRADIO_SDIO + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_SDIO]\n"); +#endif + +#ifdef CONFIG_XRADIO_DUMP_ON_ERROR + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_DUMP_ON_ERROR]\n"); +#endif + +#ifdef CONFIG_XRADIO_DEBUGFS + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_DEBUGFS]\n"); +#endif + +#ifdef CONFIG_XRADIO_ITP + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_ITP]\n"); +#endif + +#ifdef CONFIG_XRADIO_TESTMODE + xradio_dbg(XRADIO_DBG_NIY, "[CONFIG_XRADIO_TESTMODE]\n"); +#endif + +/************ XRADIO Make File config ************/ +#ifdef P2P_MULTIVIF + xradio_dbg(XRADIO_DBG_NIY, "[P2P_MULTIVIF]\n"); +#endif + +#ifdef MCAST_FWDING + xradio_dbg(XRADIO_DBG_NIY, "[MCAST_FWDING]\n"); +#endif + +#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE + xradio_dbg(XRADIO_DBG_NIY, "[XRADIO_SUSPEND_RESUME_FILTER_ENABLE]\n"); +#endif + +#ifdef AP_AGGREGATE_FW_FIX + xradio_dbg(XRADIO_DBG_NIY, "[AP_AGGREGATE_FW_FIX]\n"); +#endif + +#ifdef AP_HT_CAP_UPDATE + xradio_dbg(XRADIO_DBG_NIY, "[AP_HT_CAP_UPDATE]\n"); +#endif + +#ifdef PROBE_RESP_EXTRA_IE + xradio_dbg(XRADIO_DBG_NIY, "[PROBE_RESP_EXTRA_IE]\n"); +#endif + +#ifdef IPV6_FILTERING + xradio_dbg(XRADIO_DBG_NIY, "[IPV6_FILTERING]\n"); +#endif + +#ifdef ROAM_OFFLOAD + xradio_dbg(XRADIO_DBG_NIY, "[ROAM_OFFLOAD]\n"); +#endif + +#ifdef TES_P2P_0002_ROC_RESTART + xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_0002_ROC_RESTART]\n"); +#endif + +#ifdef TES_P2P_000B_EXTEND_INACTIVITY_CNT + xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_000B_EXTEND_INACTIVITY_CNT]\n"); +#endif + +#ifdef TES_P2P_000B_DISABLE_EAPOL_FILTER + xradio_dbg(XRADIO_DBG_NIY, "[TES_P2P_000B_DISABLE_EAPOL_FILTER]\n"); +#endif + +#ifdef HAS_PUT_TASK_STRUCT + xradio_dbg(XRADIO_DBG_NIY, "[HAS_PUT_TASK_STRUCT]\n"); +#endif + +/************* XRADIO.h config *************/ +#ifdef HIDDEN_SSID + xradio_dbg(XRADIO_DBG_NIY, "[HIDDEN_SSID]\n"); +#endif + +#ifdef ROC_DEBUG + xradio_dbg(XRADIO_DBG_NIY, "[ROC_DEBUG]\n"); +#endif + +#ifdef XRADIO_RRM + xradio_dbg(XRADIO_DBG_NIY, "[XRADIO_RRM]\n"); +#endif +} + +/* return 0: failed*/ +static inline int xradio_macaddr_val2char(char *c_mac, const u8* v_mac) +{ + SYS_BUG(!v_mac || !c_mac); + return sprintf(c_mac, "%02x:%02x:%02x:%02x:%02x:%02x\n", + v_mac[0], v_mac[1], v_mac[2], + v_mac[3], v_mac[4], v_mac[5]); +} + +#ifndef XRADIO_MACPARAM_HEX +static int xradio_macaddr_char2val(u8* v_mac, const char *c_mac) +{ + int i = 0; + const char *tmp_char = c_mac; + SYS_BUG(!v_mac || !c_mac); + + for (i = 0; i < ETH_ALEN; i++) { + if (*tmp_char != 0) { + v_mac[i] = simple_strtoul(tmp_char, (char **)&tmp_char, 16); + } else { + xradio_dbg(XRADIO_DBG_ERROR, "%s, Len Error\n", __func__); + return -1; + } + if (i < ETH_ALEN -1 && *tmp_char != ':') { + xradio_dbg(XRADIO_DBG_ERROR, "%s, Format or Len Error\n", __func__); + return -1; + } + tmp_char++; + } + return 0; +} +#endif + +#ifdef XRADIO_MACADDR_FROM_CHIPID +extern void wifi_hwaddr_from_chipid(u8 *addr); +#endif + +#define MACADDR_VAILID(a) ( \ +(a[0] != 0 || a[1] != 0 || \ + a[2] != 0 || a[3] != 0 || \ + a[4] != 0 || a[5] != 0) && \ + !(a[0] & 0x3)) + +static void xradio_get_mac_addrs(u8 *macaddr) +{ + int ret = 0; + SYS_BUG(!macaddr); + /* Check mac addrs param, if exsist, use it first.*/ +#ifdef XRADIO_MACPARAM_HEX + memcpy(macaddr, xradio_macaddr_param, ETH_ALEN); +#else + if (xradio_macaddr_param) { + ret = xradio_macaddr_char2val(macaddr, xradio_macaddr_param); + } +#endif + +#ifdef XRADIO_MACADDR_FROM_CHIPID + if (ret < 0 || !MACADDR_VAILID(macaddr)) { + wifi_hwaddr_from_chipid(macaddr); + } +#endif + /* Use random value to set mac addr for the first time, + * and save it in wifi config file. TODO: read from product ID*/ + if (ret < 0 || !MACADDR_VAILID(macaddr)) { +#ifdef XRADIO_MACPARAM_HEX + ret = access_file(WIFI_CONF_PATH, macaddr, ETH_ALEN, 1); +#else + char c_mac[XRADIO_MAC_CHARLEN+2] = {0}; + ret = access_file(WIFI_CONF_PATH, c_mac, XRADIO_MAC_CHARLEN, 1); + if (ret >= 0) { + ret = xradio_macaddr_char2val(macaddr, c_mac); + } +#endif + if(ret<0 || !MACADDR_VAILID(macaddr)) { + get_random_bytes(macaddr, 6); + macaddr[0] &= 0xFC; +#ifdef XRADIO_MACPARAM_HEX + ret = access_file(WIFI_CONF_PATH, macaddr, ETH_ALEN, 0); +#else + ret = xradio_macaddr_val2char(c_mac, macaddr); + ret = access_file(WIFI_CONF_PATH, c_mac, ret, 0); +#endif + if(ret<0) + xradio_dbg(XRADIO_DBG_ERROR, "Access_file failed, path:%s!\n", + WIFI_CONF_PATH); + if (!MACADDR_VAILID(macaddr)) { + xradio_dbg(XRADIO_DBG_WARN, "Use default Mac addr!\n"); + macaddr[0] = 0xDC; + macaddr[1] = 0x44; + macaddr[2] = 0x6D; + } else { + xradio_dbg(XRADIO_DBG_NIY, "Use random Mac addr!\n"); + } + } else { + xradio_dbg(XRADIO_DBG_NIY, "Use Mac addr in file!\n"); + } + } + xradio_dbg(XRADIO_DBG_NIY, "MACADDR=%02x:%02x:%02x:%02x:%02x:%02x\n", + macaddr[0], macaddr[1], macaddr[2], + macaddr[3], macaddr[4], macaddr[5]); +} + +static void xradio_set_ifce_comb(struct xradio_common *hw_priv, + struct ieee80211_hw *hw) +{ + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); +#ifdef P2P_MULTIVIF + hw_priv->if_limits1[0].max = 2; +#else + hw_priv->if_limits1[0].max = 1; +#endif + + hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION); + hw_priv->if_limits1[1].max = 1; + hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP); + +#ifdef P2P_MULTIVIF + hw_priv->if_limits2[0].max = 3; +#else + hw_priv->if_limits2[0].max = 2; +#endif + hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION); + +#ifdef P2P_MULTIVIF + hw_priv->if_limits3[0].max = 2; +#else + hw_priv->if_limits3[0].max = 1; +#endif + + hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION); + hw_priv->if_limits3[1].max = 1; + hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + /* TODO:COMBO: mac80211 doesn't yet support more than 1 + * different channel */ + hw_priv->if_combs[0].num_different_channels = 1; +#ifdef P2P_MULTIVIF + hw_priv->if_combs[0].max_interfaces = 3; +#else + hw_priv->if_combs[0].max_interfaces = 2; +#endif + hw_priv->if_combs[0].limits = hw_priv->if_limits1; + hw_priv->if_combs[0].n_limits = 2; + + hw_priv->if_combs[1].num_different_channels = 1; + +#ifdef P2P_MULTIVIF + hw_priv->if_combs[1].max_interfaces = 3; +#else + hw_priv->if_combs[1].max_interfaces = 2; +#endif + hw_priv->if_combs[1].limits = hw_priv->if_limits2; + hw_priv->if_combs[1].n_limits = 1; + + hw_priv->if_combs[2].num_different_channels = 1; +#ifdef P2P_MULTIVIF + hw_priv->if_combs[2].max_interfaces = 3; +#else + hw_priv->if_combs[2].max_interfaces = 2; +#endif + hw_priv->if_combs[2].limits = hw_priv->if_limits3; + hw_priv->if_combs[2].n_limits = 2; + + hw->wiphy->iface_combinations = &hw_priv->if_combs[0]; + hw->wiphy->n_iface_combinations = 3; +} + +struct ieee80211_hw *xradio_init_common(size_t hw_priv_data_len) +{ + int i; + struct ieee80211_hw *hw; + struct xradio_common *hw_priv; + struct ieee80211_supported_band *sband; + int band; + + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + /* Alloc ieee_802.11 hw and xradio_common struct. */ + hw = ieee80211_alloc_hw(hw_priv_data_len, &xradio_ops); + if (!hw) + return NULL; + hw_priv = hw->priv; + xradio_dbg(XRADIO_DBG_ALWY, "Allocated hw_priv @ %p\n", hw_priv); + memset(hw_priv, 0, sizeof(*hw_priv)); + + /* Get MAC address. */ + xradio_get_mac_addrs((u8 *)&hw_priv->addresses[0]); + memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN); + hw_priv->addresses[1].addr[5] += 0x01; +#ifdef P2P_MULTIVIF + memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr, ETH_ALEN); + hw_priv->addresses[2].addr[4] ^= 0x80; +#endif + + /* Initialize members of hw_priv. */ + hw_priv->hw = hw; + hw_priv->if_id_slot = 0; + hw_priv->roc_if_id = -1; + atomic_set(&hw_priv->num_vifs, 0); + /* initial rates and channels TODO: fetch from FW */ + hw_priv->rates = xradio_rates; + hw_priv->mcs_rates = xradio_n_rates; +#ifdef ROAM_OFFLOAD + hw_priv->auto_scanning = 0; + hw_priv->frame_rcvd = 0; + hw_priv->num_scanchannels = 0; + hw_priv->num_2g_channels = 0; + hw_priv->num_5g_channels = 0; +#endif /*ROAM_OFFLOAD*/ +#ifdef AP_AGGREGATE_FW_FIX + /* Enable block ACK for 4 TID (BE,VI,VI,VO). */ + hw_priv->ba_tid_mask = 0xB1; /*due to HW limitations*/ +#else + /* Enable block ACK for every TID but voice. */ + hw_priv->ba_tid_mask = 0x3F; +#endif + hw_priv->noise = -94; + /* hw_priv->beacon_req_id = cpu_to_le32(0); */ + + /* Initialize members of ieee80211_hw, it works in UMAC. */ + hw->sta_data_size = sizeof(struct xradio_sta_priv); + hw->vif_data_size = sizeof(struct xradio_vif); + + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_SUPPORTS_UAPSD | + IEEE80211_HW_CONNECTION_MONITOR; + //IEEE80211_HW_SUPPORTS_CQM_RSSI | + /* Aggregation is fully controlled by firmware. + * Do not need any support from the mac80211 stack */ + /* IEEE80211_HW_AMPDU_AGGREGATION | */ +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //IEEE80211_HW_SUPPORTS_P2P_PS | + //IEEE80211_HW_SUPPORTS_CQM_BEACON_MISS | + // IEEE80211_HW_SUPPORTS_CQM_TX_FAIL | +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + //IEEE80211_HW_BEACON_FILTER; + + hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO); + + /* Support only for limited wowlan functionalities */ + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT; + hw->wiphy->wowlan.n_patterns = 0; + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + /* fix the problem that driver can not set pro-resp templet frame to fw */ + hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + +#if defined(CONFIG_XRADIO_DISABLE_BEACON_HINTS) + hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS; +#endif + hw->wiphy->n_addresses = XRWL_MAX_VIFS; + hw->wiphy->addresses = hw_priv->addresses; + hw->wiphy->max_remain_on_channel_duration = 500; + hw->channel_change_time = 500; /* TODO: find actual value */ + hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM + + 8 /* TKIP IV */ + + 12 /* TKIP ICV and MIC */; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &xradio_band_2ghz; +#ifdef CONFIG_XRADIO_5GHZ_SUPPORT + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &xradio_band_5ghz; +#endif /* CONFIG_XRADIO_5GHZ_SUPPORT */ + hw->queues = AC_QUEUE_NUM; + hw->max_rates = MAX_RATES_STAGE; + hw->max_rate_tries = MAX_RATES_RETRY; + /* Channel params have to be cleared before registering wiphy again */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = hw->wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].flags = 0; + sband->channels[i].max_antenna_gain = 0; + sband->channels[i].max_power = 30; + } + } + /* hw_priv->channel init value is the local->oper_channel init value;when transplanting,take care */ + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = hw->wiphy->bands[band]; + if (!sband) + continue; + if(!hw_priv->channel){ + hw_priv->channel = &sband->channels[2]; + } + } + hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS; + hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr); + + /* Initialize locks. */ + spin_lock_init(&hw_priv->vif_list_lock); + mutex_init(&hw_priv->wsm_cmd_mux); + mutex_init(&hw_priv->conf_mutex); + mutex_init(&hw_priv->wsm_oper_lock); + atomic_set(&hw_priv->tx_lock, 0); + sema_init(&hw_priv->tx_lock_sem, 1); + + hw_priv->workqueue = create_singlethread_workqueue(XRADIO_WORKQUEUE); + sema_init(&hw_priv->scan.lock, 1); + sema_init(&hw_priv->scan.status_lock,1); + INIT_WORK(&hw_priv->scan.work, xradio_scan_work); +#ifdef ROAM_OFFLOAD + INIT_WORK(&hw_priv->scan.swork, xradio_sched_scan_work); +#endif /*ROAM_OFFLOAD*/ + INIT_DELAYED_WORK(&hw_priv->scan.probe_work, xradio_probe_work); + INIT_DELAYED_WORK(&hw_priv->scan.timeout, xradio_scan_timeout); + INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, xradio_rem_chan_timeout); + INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work); + atomic_set(&hw_priv->upload_count, 0); + memset(&hw_priv->connet_time, 0, sizeof(hw_priv->connet_time)); + + spin_lock_init(&hw_priv->event_queue_lock); + INIT_LIST_HEAD(&hw_priv->event_queue); + INIT_WORK(&hw_priv->event_handler, xradio_event_handler); + INIT_WORK(&hw_priv->ba_work, xradio_ba_work); + spin_lock_init(&hw_priv->ba_lock); + init_timer(&hw_priv->ba_timer); + hw_priv->ba_timer.data = (unsigned long)hw_priv; + hw_priv->ba_timer.function = xradio_ba_timer; + + if (unlikely(xradio_queue_stats_init(&hw_priv->tx_queue_stats, + WLAN_LINK_ID_MAX,xradio_skb_dtor, hw_priv))) { + ieee80211_free_hw(hw); + return NULL; + } + for (i = 0; i < AC_QUEUE_NUM; ++i) { + if (unlikely(xradio_queue_init(&hw_priv->tx_queue[i], + &hw_priv->tx_queue_stats, i, XRWL_MAX_QUEUE_SZ, xradio_ttl[i]))) { + for (; i > 0; i--) + xradio_queue_deinit(&hw_priv->tx_queue[i - 1]); + xradio_queue_stats_deinit(&hw_priv->tx_queue_stats); + ieee80211_free_hw(hw); + return NULL; + } + } + + init_waitqueue_head(&hw_priv->channel_switch_done); + init_waitqueue_head(&hw_priv->wsm_cmd_wq); + init_waitqueue_head(&hw_priv->wsm_startup_done); + init_waitqueue_head(&hw_priv->offchannel_wq); + hw_priv->wsm_caps.firmwareReady = 0; + hw_priv->driver_ready = 0; + hw_priv->offchannel_done = 0; + wsm_buf_init(&hw_priv->wsm_cmd_buf); + spin_lock_init(&hw_priv->wsm_cmd.lock); + tx_policy_init(hw_priv); + xradio_init_resv_skb(hw_priv); + /* add for setting short_frame_max_tx_count(mean wdev->retry_short) to drv,init the max_rate_tries */ + spin_lock_bh(&hw_priv->tx_policy_cache.lock); + hw_priv->long_frame_max_tx_count = hw->conf.long_frame_max_tx_count; + hw_priv->short_frame_max_tx_count = + (hw->conf.short_frame_max_tx_count< 0x0F) ? + hw->conf.short_frame_max_tx_count : 0x0F; + hw_priv->hw->max_rate_tries = hw->conf.short_frame_max_tx_count; + spin_unlock_bh(&hw_priv->tx_policy_cache.lock); + + for (i = 0; i < XRWL_MAX_VIFS; i++) + hw_priv->hw_bufs_used_vif[i] = 0; + +#ifdef MCAST_FWDING + for (i = 0; i < WSM_MAX_BUF; i++) + wsm_init_release_buffer_request(hw_priv, i); + hw_priv->buf_released = 0; +#endif + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; + +#if defined(CONFIG_XRADIO_DEBUG) + hw_priv->wsm_enable_wsm_dumps = 0; + hw_priv->wsm_dump_max_size = WSM_DUMP_MAX_SIZE; +#endif /* CONFIG_XRADIO_DEBUG */ + hw_priv->query_packetID = 0; + atomic_set(&hw_priv->query_cnt, 0); + INIT_WORK(&hw_priv->query_work, wsm_query_work); + +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF + atomic_set(&hw_priv->suspend_state, XRADIO_RESUME); +#endif +#ifdef HW_RESTART + hw_priv->hw_restart = false; + INIT_WORK(&hw_priv->hw_restart_work, xradio_restart_work); +#endif +#ifdef CONFIG_XRADIO_TESTMODE + hw_priv->test_frame.data = NULL; + hw_priv->test_frame.len = 0; + spin_lock_init(&hw_priv->tsm_lock); + INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout, + xradio_advance_scan_timeout); +#endif /*CONFIG_XRADIO_TESTMODE*/ + + xradio_set_ifce_comb(hw_priv, hw_priv->hw); + + if (!g_hw_priv) { + g_hw_priv = hw_priv; + return hw; + } else { //error:didn't release hw_priv last time. + ieee80211_free_hw(hw); + xradio_dbg(XRADIO_DBG_ERROR, "g_hw_priv is not NULL @ %p!\n", g_hw_priv); + return NULL; + } +} + +void xradio_free_common(struct ieee80211_hw *dev) +{ + int i; + struct xradio_common *hw_priv = dev->priv; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + +#ifdef CONFIG_XRADIO_TESTMODE + kfree(hw_priv->test_frame.data); +#endif /* CONFIG_XRADIO_TESTMODE */ + + cancel_work_sync(&hw_priv->query_work); + del_timer_sync(&hw_priv->ba_timer); + mutex_destroy(&hw_priv->wsm_oper_lock); + mutex_destroy(&hw_priv->conf_mutex); + mutex_destroy(&hw_priv->wsm_cmd_mux); + wsm_buf_deinit(&hw_priv->wsm_cmd_buf); + flush_workqueue(hw_priv->workqueue); + destroy_workqueue(hw_priv->workqueue); + hw_priv->workqueue = NULL; + + xradio_deinit_resv_skb(hw_priv); + if (hw_priv->skb_cache) { + dev_kfree_skb(hw_priv->skb_cache); + hw_priv->skb_cache = NULL; + } + + for (i = 0; i < 4; ++i) + xradio_queue_deinit(&hw_priv->tx_queue[i]); + xradio_queue_stats_deinit(&hw_priv->tx_queue_stats); + + for (i = 0; i < XRWL_MAX_VIFS; i++) { + kfree(hw_priv->vif_list[i]); + hw_priv->vif_list[i] = NULL; + } + +//fixed memory leakage by yangfh +#ifdef MCAST_FWDING + wsm_deinit_release_buffer(hw_priv); +#endif + /* unsigned int i; */ + ieee80211_free_hw(dev); + g_hw_priv = NULL; +} + +int xradio_register_common(struct ieee80211_hw *dev) +{ + int err = 0; + struct xradio_common *hw_priv = dev->priv; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + SET_IEEE80211_DEV(dev, hw_priv->pdev); + err = ieee80211_register_hw(dev); + if (err) { + xradio_dbg(XRADIO_DBG_ERROR, "Cannot register device (%d).\n", err); + return err; + } + xradio_dbg(XRADIO_DBG_MSG, "is registered as '%s'\n", + wiphy_name(dev->wiphy)); + + xradio_debug_init_common(hw_priv); + hw_priv->driver_ready = 1; + wake_up(&hw_priv->wsm_startup_done); + return 0; +} + +void xradio_unregister_common(struct ieee80211_hw *dev) +{ + struct xradio_common *hw_priv = dev->priv; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + if (wiphy_dev(dev->wiphy)) { + ieee80211_unregister_hw(dev); + SET_IEEE80211_DEV(dev, NULL); + xradio_debug_release_common(hw_priv); + } + hw_priv->driver_ready = 0; +} + +#ifdef HW_RESTART +int xradio_core_reinit(struct xradio_common *hw_priv) +{ + int ret = 0; + u16 ctrl_reg; + int i = 0; + struct xradio_vif *priv = NULL; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + + if (!hw_priv) { + xradio_dbg(XRADIO_DBG_ERROR, "%s hw_priv is NULL!\n", __func__); + return -1; + } + + /* Need some time for restart hardware, don't suspend again.*/ +#ifdef CONFIG_PM + xradio_pm_lock_awake(&hw_priv->pm_state); +#endif + + xradio_dbg(XRADIO_DBG_ALWY, "%s %d!\n", __func__, __LINE__); + /* Disconnect with AP or STAs. */ + xradio_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + ieee80211_connection_loss(priv->vif); + msleep(200); + } else if (priv->join_status == XRADIO_JOIN_STATUS_AP) { + wms_send_disassoc_to_self(hw_priv, priv); + msleep(200); + } + } + xradio_unregister_common(hw_priv->hw); + + /*deinit dev */ + xradio_dev_deinit(hw_priv); + + /*reinit status refer to hif. */ + hw_priv->powersave_enabled = false; + hw_priv->wsm_caps.firmwareReady = 0; + atomic_set(&hw_priv->bh_rx, 0); + atomic_set(&hw_priv->bh_tx, 0); + atomic_set(&hw_priv->bh_term, 0); + hw_priv->buf_id_tx = 0; + hw_priv->buf_id_rx = 0; + hw_priv->wsm_rx_seq = 0; + hw_priv->wsm_tx_seq = 0; + hw_priv->device_can_sleep = 0; + hw_priv->hw_bufs_used = 0; + memset(&hw_priv->hw_bufs_used_vif, 0, sizeof(hw_priv->hw_bufs_used_vif)); + memset(&hw_priv->connet_time, 0, sizeof(hw_priv->connet_time)); + atomic_set(&hw_priv->query_cnt, 0); + hw_priv->query_packetID = 0; + tx_policy_init(hw_priv); + + /*reinit sdio sbus. */ + sbus_sdio_deinit(); + msleep(100); + hw_priv->pdev = sbus_sdio_init((struct sbus_ops **)&hw_priv->sbus_ops, + &hw_priv->sbus_priv); + if (!hw_priv->pdev) { + xradio_dbg(XRADIO_DBG_ERROR,"%s:sbus_sdio_init failed\n", __func__); + ret = -ETIMEDOUT; + goto exit; + } + + /*wake up bh thread. */ + if (hw_priv->bh_thread == NULL) { + hw_priv->bh_error = 0; + atomic_set(&hw_priv->tx_lock, 0); + xradio_register_bh(hw_priv); + } else { +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF + WARN_ON(xradio_bh_resume(hw_priv)); +#endif + } + + /* Load firmware and register Interrupt Handler */ + + ret = xradio_load_firmware(hw_priv); + if (ret) { + xradio_dbg(XRADIO_DBG_ERROR, "%s:xradio_load_firmware failed(%d).\n", + __func__, ret); + goto exit; + } + + /* Set sdio blocksize. */ + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + SYS_WARN(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv, + SDIO_BLOCK_SIZE)); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { + + /* TODO: Needs to find how to reset device */ + /* in QUEUE mode properly. */ + xradio_dbg(XRADIO_DBG_ERROR, "%s:Firmware Startup Timeout!\n", + __func__); + ret = -ETIMEDOUT; + goto exit; + } + xradio_dbg(XRADIO_DBG_ALWY, "%s:Firmware Startup Done.\n", __func__); + + hw_priv->hw_restart = false; +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF + atomic_set(&hw_priv->suspend_state, XRADIO_RESUME); +#endif + + /* Keep device wake up. */ + ret = xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT); + if (xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &ctrl_reg)) + ret = xradio_reg_read_16(hw_priv, HIF_CONTROL_REG_ID, &ctrl_reg); + SYS_WARN(!(ctrl_reg & HIF_CTRL_RDY_BIT)); + + /* Set device mode parameter. */ + for (i = 0; i < xrwl_get_nr_hw_ifaces(hw_priv); i++) { + /* Set low-power mode. */ + ret = wsm_set_operational_mode(hw_priv, &mode, i); + /* Enable multi-TX confirmation */ + ret = wsm_use_multi_tx_conf(hw_priv, true, i); + } + + /* re-Register wireless net device. */ + if (!ret) + ret = xradio_register_common(hw_priv->hw); + + /* unlock queue if need. */ + for (i = 0; i < 4; ++i) { + struct xradio_queue *queue = &hw_priv->tx_queue[i]; + spin_lock_bh(&queue->lock); + if (queue->tx_locked_cnt > 0) { + queue->tx_locked_cnt = 0; + ieee80211_wake_queue(hw_priv->hw, queue->queue_id); + xradio_dbg(XRADIO_DBG_WARN, "%s: unlock queue!\n", __func__); + } + spin_unlock_bh(&queue->lock); + } +exit: +#ifdef CONFIG_PM + xradio_pm_unlock_awake(&hw_priv->pm_state); +#endif + xradio_dbg(XRADIO_DBG_ALWY, "%s end!\n", __func__); + + return ret; +} + +void xradio_restart_work(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, hw_restart_work); + xradio_dbg(XRADIO_DBG_ALWY, "%s\n", __func__); + + if (hw_priv->bh_error) { + xradio_unregister_bh(hw_priv); + } + if (unlikely(xradio_core_reinit(hw_priv))) { + pm_printk(XRADIO_DBG_ALWY, "%s again!\n", __func__); + mutex_lock(&hw_priv->wsm_cmd_mux); + hw_priv->hw_restart = true; + mutex_unlock(&hw_priv->wsm_cmd_mux); + xradio_unregister_bh(hw_priv); + xradio_core_reinit(hw_priv); + } +} +#endif + +int xradio_core_init(void) +{ + int err = -ENOMEM; + u16 ctrl_reg; + int if_id; + struct ieee80211_hw *dev; + struct xradio_common *hw_priv; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + xradio_version_show(); + + //init xradio_common + dev = xradio_init_common(sizeof(struct xradio_common)); + if (!dev) { + xradio_dbg(XRADIO_DBG_ERROR,"xradio_init_common failed\n"); + return err; + } + hw_priv = dev->priv; + + //init sdio sbus + hw_priv->pdev = sbus_sdio_init((struct sbus_ops **)&hw_priv->sbus_ops, + &hw_priv->sbus_priv); + if (!hw_priv->pdev) { + err = -ETIMEDOUT; + xradio_dbg(XRADIO_DBG_ERROR,"sbus_sdio_init failed\n"); + goto err1; + } + + /* WSM callbacks. */ + hw_priv->wsm_cbc.scan_complete = xradio_scan_complete_cb; + hw_priv->wsm_cbc.tx_confirm = xradio_tx_confirm_cb; + hw_priv->wsm_cbc.rx = xradio_rx_cb; + hw_priv->wsm_cbc.suspend_resume = xradio_suspend_resume; + /* hw_priv->wsm_cbc.set_pm_complete = xradio_set_pm_complete_cb; */ + hw_priv->wsm_cbc.channel_switch = xradio_channel_switch_cb; + + /*init pm and wakelock. */ +#ifdef CONFIG_PM + err = xradio_pm_init(&hw_priv->pm_state, hw_priv); + if (err) { + xradio_dbg(XRADIO_DBG_ERROR, "xradio_pm_init failed(%d).\n", err); + goto err2; + } +#endif + /* Register bh thread*/ + err = xradio_register_bh(hw_priv); + if (err) { + xradio_dbg(XRADIO_DBG_ERROR, "xradio_register_bh failed(%d).\n", + err); + goto err3; + } + + /* Load firmware and register Interrupt Handler */ + err = xradio_load_firmware(hw_priv); + if (err) { + xradio_dbg(XRADIO_DBG_ERROR, "xradio_load_firmware failed(%d).\n", + err); + goto err4; + } + + /* Set sdio blocksize. */ + hw_priv->sbus_ops->lock(hw_priv->sbus_priv); + SYS_WARN(hw_priv->sbus_ops->set_block_size(hw_priv->sbus_priv, + SDIO_BLOCK_SIZE)); + hw_priv->sbus_ops->unlock(hw_priv->sbus_priv); + + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->wsm_caps.firmwareReady, 3*HZ) <= 0) { + + /* TODO: Needs to find how to reset device */ + /* in QUEUE mode properly. */ + xradio_dbg(XRADIO_DBG_ERROR, "Firmware Startup Timeout!\n"); + err = -ETIMEDOUT; + goto err5; + } + xradio_dbg(XRADIO_DBG_ALWY,"Firmware Startup Done.\n"); + + /* Keep device wake up. */ + SYS_WARN(xradio_reg_write_16(hw_priv, HIF_CONTROL_REG_ID, HIF_CTRL_WUP_BIT)); + if (xradio_reg_read_16(hw_priv,HIF_CONTROL_REG_ID, &ctrl_reg)) + SYS_WARN(xradio_reg_read_16(hw_priv,HIF_CONTROL_REG_ID, &ctrl_reg)); + SYS_WARN(!(ctrl_reg & HIF_CTRL_RDY_BIT)); + + /* Set device mode parameter. */ + for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); if_id++) { + /* Set low-power mode. */ + SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, if_id)); + /* Enable multi-TX confirmation */ + SYS_WARN(wsm_use_multi_tx_conf(hw_priv, true, if_id)); + } + + /* Register wireless net device. */ + err = xradio_register_common(dev); + if (err) { + xradio_dbg(XRADIO_DBG_ERROR,"xradio_register_common failed(%d)!\n", err); + goto err5; + } + + return err; + +err5: + xradio_dev_deinit(hw_priv); +err4: + xradio_unregister_bh(hw_priv); +err3: + xradio_pm_deinit(&hw_priv->pm_state); +err2: + sbus_sdio_deinit(); +err1: + xradio_free_common(dev); + return err; +} +EXPORT_SYMBOL_GPL(xradio_core_init); + +void xradio_core_deinit(void) +{ + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + if (g_hw_priv) { +#ifdef HW_RESTART + cancel_work_sync(&g_hw_priv->hw_restart_work); +#endif + xradio_unregister_common(g_hw_priv->hw); + xradio_dev_deinit(g_hw_priv); + xradio_unregister_bh(g_hw_priv); + xradio_pm_deinit(&g_hw_priv->pm_state); + xradio_free_common(g_hw_priv->hw); + sbus_sdio_deinit(); + } + return; +} +EXPORT_SYMBOL_GPL(xradio_core_deinit); + +/* Init Module function -> Called by insmod */ +static int __init xradio_core_entry(void) +{ + int ret = 0; + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + ret = xradio_plat_init(); + if (ret) { + xradio_dbg(XRADIO_DBG_ERROR,"xradio_plat_init failed(%d)!\n", ret); + } + ret = xradio_host_dbg_init(); + ret = xradio_core_init(); + return ret; +} + +/* Called at Driver Unloading */ +static void __exit xradio_core_exit(void) +{ + xradio_core_deinit(); + xradio_host_dbg_deinit(); + xradio_plat_deinit(); + xradio_dbg(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); +} + +module_init(xradio_core_entry); +module_exit(xradio_core_exit); + diff --git a/drivers/net/wireless/xradio/nl80211_testmode_msg_copy.h b/drivers/net/wireless/xradio/nl80211_testmode_msg_copy.h new file mode 100644 index 0000000..0bd3286 --- /dev/null +++ b/drivers/net/wireless/xradio/nl80211_testmode_msg_copy.h @@ -0,0 +1,157 @@ +/* + * test mode interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XR_NL80211_TESTMODE_MSG_COPY_H +#define XR_NL80211_TESTMODE_MSG_COPY_H + +/* example command structure for test purposes */ +struct xr_msg_test_t { + int dummy; +}; + +/* example reply structure for test purposes */ +struct xr_reply_test_t { + int dummy; +}; + +/* example event structure for test purposes */ +struct xr_event_test_t { + int dummy; +}; + +enum xr_msg_id { + XR_MSG_TEST = 0, /* for test purposes */ + XR_MSG_EVENT_TEST, /* for test purposes */ + XR_MSG_SET_SNAP_FRAME, + XR_MSG_EVENT_FRAME_DATA, +#ifdef CONFIG_XRADIO_TESTMODE + XR_MSG_GET_TX_POWER_LEVEL, + XR_MSG_GET_TX_POWER_RANGE, + XR_MSG_SET_ADVANCE_SCAN_ELEMS, + XR_MSG_SET_TX_QUEUE_PARAMS, + XR_MSG_START_STOP_TSM, + XR_MSG_GET_TSM_PARAMS, + XR_MSG_GET_ROAM_DELAY, +#endif /*CONFIG_XRADIO_TESTMODE*/ + XR_MSG_SET_POWER_SAVE, + /* Add new IDs here */ + + XR_MSG_ID_MAX, +}; + +enum xr_nl80211_testmode_data_attributes { + XR_TM_MSG_ID = 0x0001, /* u32 type containing the XR message ID */ + XR_TM_MSG_DATA, /* message payload */ + + /* Max indicator so module test may add its own attributes */ + XR_TM_MSG_ATTR_MAX, +}; + +/** + * xr_msg_set_snap_frame - set SNAP frame format + * @len: length of SNAP frame, if 0 SNAP frame disabled + * @frame: SNAP frame format + * + * In this structure is difference between user space because + * format and length have to be hidden + * + */ +struct xr_msg_set_snap_frame { + u8 len; + u8 frame[0]; +}; + +#ifdef CONFIG_XRADIO_TESTMODE +/** + * xr_msg_set_txqueue_params - store Tx queue params + * @user_priority: User priority for which TSPEC negotiated + * @medium_time: Allowed medium time + * @expiry_time: The expiry time of MSDU + * + */ +struct xr_msg_set_txqueue_params { + u8 user_priority; + u16 medium_time; + u16 expiry_time; +}; + +/** + * xr_tsm_stats - To retrieve the Transmit Stream Measurement stats + * @actual_msrmt_start_time: The TSF at the time at which the measurement + * started + * @msrmt_duration: Duration for measurement + * @peer_sta_addr: Peer STA address + * @tid: TID for which measurements were made + * @reporting_reason: Reason for report sent + * @txed_msdu_count: The number of MSDUs transmitted for the specified TID + * @msdu_discarded_count: The number of discarded MSDUs for the specified TID + * @msdu_failed_count: The number of failed MSDUs for the specified TID + * @multi_retry_count: The number of MSDUs which were retried + * @qos_cfpolls_lost_count: The number of QOS CF polls frames lost + * @avg_q_delay: Average queue delay + * @avg_transmit_delay: Average transmit delay + * @bin0_range: Delay range of the first bin (Bin 0) + * @bin0: bin0 transmit delay histogram + * @bin1: bin1 transmit delay histogram + * @bin2: bin2 transmit delay histogram + * @bin3: bin3 transmit delay histogram + * @bin4: bin4 transmit delay histogram + * @bin5: bin5 transmit delay histogram + * + */ +struct xr_tsm_stats { + u64 actual_msrmt_start_time; + u16 msrmt_duration; + u8 peer_sta_addr[6]; + u8 tid; + u8 reporting_reason; + u32 txed_msdu_count; + u32 msdu_discarded_count; + u32 msdu_failed_count; + u32 multi_retry_count; + u32 qos_cfpolls_lost_count; + u32 avg_q_delay; + u32 avg_transmit_delay; + u8 bin0_range; + u32 bin0; + u32 bin1; + u32 bin2; + u32 bin3; + u32 bin4; + u32 bin5; +} __packed; + + +/** + * xr_msg_set_start_stop_tsm - To start or stop collecting TSM metrics in + * xradio driver + * @start: To start or stop collecting TSM metrics + * @up: up for which metrics to be collected + * @packetization_delay: Packetization period for this TID + * + */ +struct xr_msg_start_stop_tsm { + u8 start; /*1: To start, 0: To stop*/ + u8 up; + u16 packetization_delay; +}; + +/** + * power_save_elems - To enable/disable legacy power Save + */ +struct power_save_elems { + int powerSave; +}; +#endif /*CONFIG_XRADIO_TESTMODE*/ + +#define XR_TM_MAX_ATTRIB_SIZE 1024 + +#endif /* XR_NL80211_TESTMODE_MSG_COPY_H*/ diff --git a/drivers/net/wireless/xradio/platform.c b/drivers/net/wireless/xradio/platform.c new file mode 100644 index 0000000..fc33a8b --- /dev/null +++ b/drivers/net/wireless/xradio/platform.c @@ -0,0 +1,942 @@ +/* + * Platform interfaces for XRadio drivers + * + * Implemented by platform vendor(such as AllwinnerTech). + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "xradio.h" +#include "platform.h" +#include "sbus.h" + +#define PLATFORM_RFKILL_PM 1 //if 1, Use system rf-kill for wifi power manage, default 0. +#define PLATFORM_SYSCONFIG 1 //if 1, Get Hardware settting from sysconfig.fex, default 0. +#define PMU_POWER_WLAN_RETAIN 1 //if 1, PMU power still up when wifi is closed, default 0. + +//hardware platform config. +static int wlan_bus_id = 0; +static u32 wlan_irq_gpio = 0; +static u32 wlan_reset_gpio = 0; + +#if (PLATFORM_SYSCONFIG) +#define WIFI_CONFIG "wifi_para" +#define MAX_POWER_NUM 3 +static char * axp_name[MAX_POWER_NUM] = {0}; +#endif + +//-------------------------------------------------- +//Select one platform if PLATFORM_RFKILL_PM=0. +//And you can platforms too. +//-------------------------------------------------- +//#define V819MINI +//#define HP8_G2 +//#define EVB_A31_CARD0 +//#define EVB_A31_CARD1 +//#define EVB_A83T_ONBOARD +#define EVB_A33_XR819 +//#define EVB_A33W_XR819 +//#define A33_CHIPHD +//#define A33_YHK +//#define A33W_SUNFLOWER_S1 + +#if (!PLATFORM_SYSCONFIG) +//-------------------------------------------------- +// sdio and gpio settings of platforms +// use following settings if PLATFORM_SYSCONFIG=0 +// You can add configs for other platforms +//-------------------------------------------------- +//V819MINI +#ifdef V819MINI +#define PLAT_GPIO_WRESET (GPIOM(7)) +#define PLAT_GPIO_WLAN_INT (GPIOL(6)) +#define PLAT_MMC_ID 1 + +//HP8 G2 +#elif defined(HP8_G2) +#define PLAT_GPIO_WRESET (GPIOL(6)) +#define PLAT_GPIO_WLAN_INT (GPIOL(7)) +#define PLAT_MMC_ID 1 + +//EVB_A83T_ONBOARD +#elif defined(EVB_A83T_ONBOARD) +#define PLAT_GPIO_WRESET (GPIOL(9)) +#define PLAT_GPIO_WLAN_INT (GPIOL(11)) +#define PLAT_MMC_ID 1 + +//EVB_A33_XR819_test +#elif defined(EVB_A33_XR819) +#define PLAT_GPIO_WRESET (GPIOL(10)) +#define PLAT_GPIO_WLAN_INT (GPIOL(8)) +#define PLAT_MMC_ID 1 + +//EVB_A33W_XR819_test +#elif defined(EVB_A33W_XR819) +#define PLAT_GPIO_WRESET (GPIOL(6)) +#define PLAT_GPIO_WLAN_INT (GPIOL(7)) +#define PLAT_MMC_ID 1 + +//A33W_SUNFLOWER_S1 +#elif defined(A33W_SUNFLOWER_S1) +#define PLAT_GPIO_WRESET (GPIOL(8)) +#define PLAT_GPIO_WLAN_INT (GPIOL(9)) +#define PLAT_MMC_ID 1 + +//A33_CHIPHD +#elif defined(A33_CHIPHD) +#define PLAT_GPIO_WRESET (GPIOL(6)) +#define PLAT_GPIO_WLAN_INT (GPIOL(7)) +#define PLAT_MMC_ID 1 + +//A33_YHK +#elif defined(A33_YHK) +#define PLAT_GPIO_WRESET (GPIOL(5)) +#define PLAT_GPIO_WLAN_INT (GPIOL(7)) +#define PLAT_MMC_ID 1 + +//defualt EVB_A31_CARD0. +#elif defined(EVB_A31_CARD0) +#define PLAT_GPIO_WRESET (GPIOM(4)) +#define PLAT_GPIO_WLAN_INT (GPIOM(6)) +#define PLAT_MMC_ID 0 + +#else //defualt EVB_A31_CARD1. +#define PLAT_GPIO_WRESET (GPIOM(4)) +#define PLAT_GPIO_WLAN_INT (GPIOM(6)) +#define PLAT_MMC_ID 1 + +#endif //end of V819MINI + +#endif //PLATFORM_SYSCONFIG + +/*********************Interfaces on Allwinner platform. *********************/ +#if (!PLATFORM_RFKILL_PM) + +#if (defined(EVB_A31_CARD0)||defined(EVB_A31_CARD1)) +static int plat_evb_a31_pwr(int on) +{ + struct regulator *ldo = NULL; + unsigned int clk; + int val; + int ret = 0; + + if (!on) return ret; //no need power down on evb_a31. + +#ifdef EVB_A31_CARD0 + printk(KERN_ERR "evb card0 (red card) present!.\n"); + //set sd card 0 to 1.8v + ldo = regulator_get(NULL, "axp22_eldo2"); + if(ldo) { + //set new voltage 1.8v + val = 1800000; + if(regulator_set_voltage(ldo, val, val) < 0) + printk(KERN_ERR "regulator_set_voltage failed.\n"); + //enable regulator + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + //check voltage + val = regulator_get_voltage(ldo); + printk(KERN_ERR "sdio_vol(eldo2)=%duV.\n", val); + } else { + printk(KERN_ERR "regulator get eldo2 failed.\n"); + } + regulator_put(ldo); + + //check pm_io voltage + ldo = regulator_get(NULL, "axp22_aldo2"); + if(ldo) { + //try to set 1.8v to pm voltage + val = 1800000; + regulator_set_voltage(ldo, val, val); + regulator_enable(ldo); + + val = regulator_get_voltage(ldo); + printk(KERN_ERR "pm_io_vol(aldo2)=%duV.\n", val); + } else { + printk(KERN_ERR "regulator get aldo2 failed.\n"); + } + regulator_put(ldo); + +#else + printk(KERN_ERR "evb card1 (green card) present!.\n"); + + //check pm_io voltage + ldo = regulator_get(NULL, "axp22_aldo2"); + if(ldo) { + //try to set 1.8v to pm voltage + val = 1800000; + regulator_set_voltage(ldo, val, val); + regulator_enable(ldo); + + val = regulator_get_voltage(ldo); + printk(KERN_ERR "pm_io_vol(aldo2)=%duV.\n", val); + } else { + printk(KERN_ERR "regulator get aldo2 failed.\n"); + } + regulator_put(ldo); + + /* pm7Ƥ׃Ϊrtc_clk0 */ + clk = ioread32(0xf1f02c24); + clk &= ~(0x3 << 28); + clk |= 0x3 << 28; + iowrite32(clk, 0xf1f02c24); + + /* loscƤԉexternal 32.768KHz */ + clk = ioread32(0xf1f00004); + clk &= ~0x1; + clk |= 0x1; + iowrite32(clk, 0xf1f00004); + + /* clk_outputdʹŜ */ + clk = ioread32(0xf1f014f0); + clk = 0x80000000; + iowrite32(clk, 0xf1f014f0); + + printk(KERN_ERR "[clock] clock init RTC-CKO 32768Hz," \ + " 0xf1f02c24=%#x, 0xf1f00004=%#x, 0xf1f014f0=%#x\n", + ioread32(0xf1f02c24), ioread32(0xf1f00004), ioread32(0xf1f014f0)); + + gpio_request(GPIOM(3), "pmu_en"); + gpio_direction_output(GPIOM(3), 0); + gpio_set_value(GPIOM(3), 1); + udelay(300); + + gpio_request(GPIOG(12), "gbf_ena_reset"); + gpio_direction_output(GPIOG(12), 0); + gpio_set_value(GPIOG(12), 1); + udelay(300); + + gpio_free(GPIOM(3)); + gpio_free(GPIOG(12)); +#endif //EVB_CARD0 + + return ret; +} +#endif + +#ifdef V819MINI +static int plat_v819mini_pwr(int on) +{ + struct regulator *ldo = NULL; + int ret = 0; + + xradio_dbg(XRADIO_DBG_ERROR, "V819mini Power %s!.\n", on? "Up":"Down"); + + if (on) { + //wifi vcc 3.3v for XR819 + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + //pg sdio pin 3.3v for XR819 + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi reset 3.3v for XR819 + ldo = regulator_get(NULL, "axp22_dldo4"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo4=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + } else { + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + } + + return ret; +} +#endif + +#if defined(HP8_G2) +static int plat_hp8_g2_pwr(int on) +{ + struct regulator *ldo = NULL; + int ret = 0; + + printk(KERN_ERR "hp8_g2 Power %s!.\n", on? "Up":"Down"); + + if (on) { + //wifi vcc 3.3v for XR819. + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi vcc 1.8v for XR819. + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_eldo2"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable eldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi io pin 1.8~3.3v for XR819. + ldo = regulator_get(NULL, "axp22_aldo1"); + if (ldo) { + regulator_set_voltage(ldo, 3000000, 3000000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable aldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + //wifi reset & interrupt 1.8~3.3v for XR819. + } else { + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_eldo2"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + } + + return ret; +} +#endif + +#if defined(EVB_A83T_ONBOARD) +static int plat_evb_a83t_onboard_pwr(int on) +{ + struct regulator *ldo = NULL; + int ret = 0; + printk(KERN_ERR "evb_a83t_onboard Power %s!.\n", on? "Up":"Down"); + if (on) { + //wifi vcc 3.3v for XR819. + ldo = regulator_get(NULL, "axp81x_dldo2"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi vcc 1.8v for XR819. + ldo = regulator_get(NULL, "axp81x_aldo1"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable aldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi io pin 1.8~3.3v for XR819b. + ldo = regulator_get(NULL, "axp81x_aldo2"); + if (ldo) { + regulator_set_voltage(ldo, 3000000, 3000000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable aldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi reset & interrupt 1.8~3.3v for XR819. + ldo = regulator_get(NULL, "axp81x_aldo3"); + if (ldo) { + regulator_set_voltage(ldo, 3000000, 3000000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable aldo3=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + } else { + ldo = regulator_get(NULL, "axp81x_dldo2"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp81x_aldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + } + + return ret; +} +#endif + +#if (defined(EVB_A33_XR819) || defined(EVB_A33W_XR819)) +static int plat_a33_xr819_pwr(int on) +{ + struct regulator *ldo = NULL; + int ret = 0; + + printk(KERN_ERR "plat_a33_xr819_pwr Power %s!.\n", on? "Up":"Down"); + if (on) { + //wifi vcc 3.3v for XR819. + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo4"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo4=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi vcc 1.8v for XR819. + ldo = regulator_get(NULL, "axp22_eldo2"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable eldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_eldo3"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable eldo3=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + //wifi reset & interrupt 1.8~3.3v for XR819. + } else { + //wifi vcc 3.3v + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo4"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + //wifi vcc 1.8v + ldo = regulator_get(NULL, "axp22_eldo2"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_eldo3"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + } + return ret; +} +#endif + +#if (defined(A33W_SUNFLOWER_S1)) +static int plat_a33w_sunflower_s1_pwr(int on) +{ + struct regulator *ldo = NULL; + int ret = 0; + struct clk *ap_32k = NULL; + + printk(KERN_ERR "plat_a33w_sunflower_s1_pwr Power %s!.\n", on? "Up":"Down"); + if (on) { + //wifi vcc 3.3v for XR819. + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + //wifi vcc 1.8v for XR819. + ldo = regulator_get(NULL, "axp22_eldo1"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable eldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + } else { + //wifi vcc 3.3v + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + //wifi vcc 1.8v + ldo = regulator_get(NULL, "axp22_eldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + } + //use external 32k clock. + ap_32k = clk_get(NULL, "losc_out"); + if (!ap_32k){ + printk(KERN_ERR "Get ap 32k clk out failed!\n"); + return -1; + } + ret = clk_prepare_enable(ap_32k); + if (ret){ + printk(KERN_ERR "losc out enable failed!\n"); + } + return ret; +} +#endif + +#if (defined(A33_CHIPHD) || defined(A33_YHK)) +static int plat_a33_chiphd_pwr(int on) +{ + struct regulator *ldo = NULL; + int ret = 0; + struct clk *ap_32k = NULL; + + printk(KERN_ERR "plat_a33_chiphd_pwr Power %s!.\n", on? "Up":"Down"); + if (on) { + //wifi vcc 3.3v for XR819. + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + regulator_set_voltage(ldo, 3300000, 3300000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable dldo2=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + + //wifi vcc 1.8v for XR819. + ldo = regulator_get(NULL, "axp22_eldo1"); + if (ldo) { + regulator_set_voltage(ldo, 1800000, 1800000); + ret = regulator_enable(ldo); + if(ret < 0){ + printk(KERN_ERR "regulator_enable failed.\n"); + } + printk(KERN_ERR "regulator_enable eldo1=%d.\n", regulator_get_voltage(ldo)); + regulator_put(ldo); + } + //wifi reset & interrupt 1.8~3.3v for XR819. + } else { + //wifi vcc 3.3v + ldo = regulator_get(NULL, "axp22_dldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + ldo = regulator_get(NULL, "axp22_dldo2"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + //wifi vcc 1.8v + ldo = regulator_get(NULL, "axp22_eldo1"); + if (ldo) { + ret = regulator_disable(ldo); + regulator_put(ldo); + } + } + + //use external 32k clock. + ap_32k = clk_get(NULL, "losc_out"); + if (!ap_32k){ + printk(KERN_ERR "Get ap 32k clk out failed!\n"); + return -1; + } +#if 1 + ret = clk_prepare_enable(ap_32k); + if (ret){ + printk(KERN_ERR "losc out enable failed!\n"); + } +#else + clk_disable(ap_32k); //disable 32k clk. +#endif + + return ret; +} +#endif + +int plat_module_power(int enable) +{ + int ret = 0; + +#ifdef V819MINI + ret = plat_v819mini_pwr(enable); +#elif defined(HP8_G2) + ret = plat_hp8_g2_pwr(enable); +#elif defined(EVB_A83T_ONBOARD) + ret = plat_evb_a83t_onboard_pwr(enable); +#elif (defined(EVB_A33_XR819) || defined(EVB_A33W_XR819)) + ret = plat_a33_xr819_pwr(enable); +#elif (defined(A33W_SUNFLOWER_S1)) + ret = plat_a33w_sunflower_s1_pwr(enable); +#elif (defined(A33_CHIPHD) || defined(A33_YHK)) + ret = plat_a33_chiphd_pwr(enable); +#elif (defined(EVB_A31_CARD0)||defined(EVB_A31_CARD1)) + ret = plat_evb_a31_pwr(enable); +#else + printk(KERN_ERR "plat_module_power: Fail to setup power!\n"); +#endif + + if (enable) { //delay for power stable period. + mdelay(50); + } + return ret; +} +#endif //PLATFORM_RFKILL_PM + +/*********************Interfaces called by xradio core. *********************/ +#if (PLATFORM_SYSCONFIG) +static int xradio_get_syscfg(void) +{ + script_item_u val; + script_item_value_type_e type; + + /* Get SDIO/USB config. */ +#if defined(CONFIG_XRADIO_SDIO) + type = script_get_item(WIFI_CONFIG, "wifi_sdc_id", &val); + if (SCIRPT_ITEM_VALUE_TYPE_INT != type) { + printk(KERN_ERR "failed to fetch sdio card's sdcid\n"); + return -1; + } +#elif defined(CONFIG_XRADIO_USB) + type = script_get_item(WIFI_CONFIG, "wifi_usbc_id", &val); + if (SCIRPT_ITEM_VALUE_TYPE_INT != type) { + printk(KERN_ERR "failed to fetch usb's id\n"); + return -1; + } +#endif + wlan_bus_id = val.val; + + /* Get GPIO config. */ + type = script_get_item(WIFI_CONFIG, "wl_reg_on", &val); + if (SCIRPT_ITEM_VALUE_TYPE_PIO != type) { + printk(KERN_ERR "failed to fetch wl_reg_on\n"); + return -1; + } + wlan_reset_gpio = val.gpio.gpio; + + type = script_get_item(WIFI_CONFIG, "wl_host_wake", &val); + if (SCIRPT_ITEM_VALUE_TYPE_PIO != type) { + printk(KERN_ERR "failed to fetch xradio_wl_host_wake\n"); + return -1; + } + wlan_irq_gpio = val.gpio.gpio; + + /* Get Power config. */ +#if 0 + type = script_get_item(WIFI_CONFIG, "wifi_power", &val); + if (SCIRPT_ITEM_VALUE_TYPE_STR == type) { + axp_name[0] = val.str; + printk("wifi_power=%s\n", axp_name[0]); + } else { + printk(KERN_ERR "wifi_power not config\n"); + } + type = script_get_item(WIFI_CONFIG, "wifi_power_ext1", &val); + if (SCIRPT_ITEM_VALUE_TYPE_STR == type) { + axp_name[1] = val.str; + printk("wifi_power_ext1=%s\n", axp_name[1]); + } else { + printk(KERN_ERR "wifi_power_ext1 not config\n"); + } + type = script_get_item(WIFI_CONFIG, "wifi_power_ext2", &val); + if (SCIRPT_ITEM_VALUE_TYPE_STR == type) { + axp_name[2] = val.str; + printk("wifi_power_ext2=%s\n", axp_name[2]); + } else { + printk(KERN_ERR "wifi_power_ext2 not config\n"); + } +#endif + return 0; +} +#endif + +int xradio_plat_init(void) +{ + int ret = 0; +#if (!PLATFORM_SYSCONFIG) + wlan_bus_id = PLAT_MMC_ID; + wlan_irq_gpio = PLAT_GPIO_WLAN_INT; + wlan_reset_gpio = PLAT_GPIO_WRESET; +#else //initialize from sys config. + ret = xradio_get_syscfg(); +#endif //PLATFORM_SYSCONFIG + +#if (PMU_POWER_WLAN_RETAIN && !PLATFORM_RFKILL_PM) + plat_module_power(1); +#endif + return ret; +} + +void xradio_plat_deinit(void) +{ +#if (PMU_POWER_WLAN_RETAIN && !PLATFORM_RFKILL_PM) + plat_module_power(0); +#endif +} + +#if (PLATFORM_RFKILL_PM) +//extern void wifi_pm_power(int on); +//void wifi_pm_power(int on) +void wifi_pm(int on) +{ + printk(KERN_ERR "xradio wlan power %s\n", on?"on":"off"); + if (on) { + wifi_pm_gpio_ctrl("wl_reg_on", 1); + mdelay(50); + wifi_pm_gpio_ctrl("wl_reg_on", 0); + mdelay(2); + wifi_pm_gpio_ctrl("wl_reg_on", 1); + mdelay(50); + } else { + wifi_pm_gpio_ctrl("wl_reg_on", 0); + } +} +#endif +int xradio_wlan_power(int on) +{ + int ret = 0; + +#if (PLATFORM_RFKILL_PM) + //wifi_pm_power(on); + wifi_pm(on); + mdelay(50); +#else //PLATFORM_RFKILL_PM + ret = gpio_request(wlan_reset_gpio, "wlan_reset"); + if (!ret) { + if (on) { //power up. +#if (!PMU_POWER_WLAN_RETAIN) + plat_module_power(1); + mdelay(50); +#endif + gpio_direction_output(wlan_reset_gpio, 1); + mdelay(50); + gpio_direction_output(wlan_reset_gpio, 0); + mdelay(2); + gpio_direction_output(wlan_reset_gpio, 1); + mdelay(50); + } else { //power down. + gpio_direction_output(wlan_reset_gpio, 0); +#if (!PMU_POWER_WLAN_RETAIN) + plat_module_power(0); +#endif + } + gpio_free(wlan_reset_gpio); + } else { + xradio_dbg(XRADIO_DBG_ERROR, "%s: gpio_request err: %d\n", __func__, ret); + } +#endif + return ret; +} + +int xradio_sdio_detect(int enable) +{ + int insert = enable; + MCI_RESCAN_CARD(wlan_bus_id, insert); + xradio_dbg(XRADIO_DBG_ALWY, "%s SDIO card %d\n", + enable?"Detect":"Remove", wlan_bus_id); + mdelay(10); + return 0; +} + +#ifdef CONFIG_XRADIO_USE_GPIO_IRQ +static u32 gpio_irq_handle = 0; +#ifdef PLAT_ALLWINNER_SUNXI +static irqreturn_t xradio_gpio_irq_handler(int irq, void *sbus_priv) +{ + struct sbus_priv *self = (struct sbus_priv *)sbus_priv; + unsigned long flags; + + SYS_BUG(!self); + spin_lock_irqsave(&self->lock, flags); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); + return IRQ_HANDLED; +} +#else //PLAT_ALLWINNER_SUN6I +static u32 xradio_gpio_irq_handler(void *sbus_priv) +{ + struct sbus_priv *self = (struct sbus_priv *)sbus_priv; + unsigned long flags; + + SYS_BUG(!self); + spin_lock_irqsave(&self->lock, flags); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); + return 0; +} +#endif + +int xradio_request_gpio_irq(struct device *dev, void *sbus_priv) +{ + int ret = -1; + if(!gpio_irq_handle) { + +#ifdef PLAT_ALLWINNER_SUNXI + gpio_request(wlan_irq_gpio, "xradio_irq"); + gpio_direction_input(wlan_irq_gpio); + gpio_irq_handle = gpio_to_irq(wlan_irq_gpio); + ret = devm_request_irq(dev, gpio_irq_handle, + (irq_handler_t)xradio_gpio_irq_handler, + IRQF_TRIGGER_RISING, "xradio_irq", sbus_priv); + if (IS_ERR_VALUE(ret)) { + gpio_irq_handle = 0; + } +#else//PLAT_ALLWINNER_SUN6I + gpio_irq_handle = sw_gpio_irq_request(wlan_irq_gpio, TRIG_EDGE_POSITIVE, + (peint_handle)xradio_gpio_irq_handler, sbus_priv); +#endif // PLAT_ALLWINNER_SUNXI + } else { + xradio_dbg(XRADIO_DBG_ERROR, "%s: error, irq exist already!\n", __func__); + } + + if (gpio_irq_handle) { + xradio_dbg(XRADIO_DBG_NIY, "%s: request_irq sucess! irq=0x%08x\n", + __func__, gpio_irq_handle); + ret = 0; + } else { + xradio_dbg(XRADIO_DBG_ERROR, "%s: request_irq err: %d\n", __func__, ret); + ret = -1; + } + return ret; +} + +void xradio_free_gpio_irq(struct device *dev, void *sbus_priv) +{ + struct sbus_priv *self = (struct sbus_priv *)sbus_priv; +#ifdef PLAT_ALLWINNER_SUNXI + if(gpio_irq_handle) { + //for linux3.4 + devm_free_irq(dev, gpio_irq_handle, self); + gpio_free(wlan_irq_gpio); + gpio_irq_handle = 0; + } +#else// PLAT_ALLWINNER_SUN6I + if(gpio_irq_handle) { + sw_gpio_irq_free(gpio_irq_handle); + gpio_irq_handle = 0; + } +#endif//PLAT_ALLWINNER_SUNXI +} +#endif /* CONFIG_XRADIO_USE_GPIO_IRQ */ + +/******************************************************************************************/ \ No newline at end of file diff --git a/drivers/net/wireless/xradio/platform.h b/drivers/net/wireless/xradio/platform.h new file mode 100644 index 0000000..71333a7 --- /dev/null +++ b/drivers/net/wireless/xradio/platform.h @@ -0,0 +1,49 @@ +/* + * platform interfaces for XRadio drivers + * + * Implemented by platform vendor(such as AllwinnerTech). + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef XRADIO_PLAT_H_INCLUDED +#define XRADIO_PLAT_H_INCLUDED + +#include +#include +#include + +/* Select hardware platform.*/ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)) +#define PLAT_ALLWINNER_SUNXI +#define MCI_RESCAN_CARD(id, ins) sunxi_mci_rescan_card(id, ins) +#define MCI_CHECK_READY(h, t) sunxi_mci_check_r1_ready(h, t) + +extern void sunxi_mci_rescan_card(unsigned id, unsigned insert); +extern int sunxi_mci_check_r1_ready(struct mmc_host* mmc, unsigned ms); + +#else +#define PLAT_ALLWINNER_SUN6I +#define MCI_RESCAN_CARD(id, ins) sw_mci_rescan_card(id, ins) +#define MCI_CHECK_READY(h, t) sw_mci_check_r1_ready(h, t) + +extern void sw_mci_rescan_card(unsigned id, unsigned insert); +extern int sw_mci_check_r1_ready(struct mmc_host* mmc, unsigned ms); +#endif +/* the rf_pm api */ +extern int wifi_pm_gpio_ctrl(char *name, int level); +/* platform interfaces */ +int xradio_plat_init(void); +void xradio_plat_deinit(void); +int xradio_sdio_detect(int enable); +int xradio_request_gpio_irq(struct device *dev, void *sbus_priv); +void xradio_free_gpio_irq(struct device *dev, void *sbus_priv); +int xradio_wlan_power(int on); + +#endif /* XRADIO_PLAT_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/pm.c b/drivers/net/wireless/xradio/pm.c new file mode 100644 index 0000000..f3c74dd --- /dev/null +++ b/drivers/net/wireless/xradio/pm.c @@ -0,0 +1,821 @@ +/* + * PM implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include "xradio.h" +#include "pm.h" +#include "sta.h" +#include "bh.h" +#include "sbus.h" +#include "platform.h" + +#define XRADIO_BEACON_SKIPPING_MULTIPLIER 3 + +struct xradio_udp_port_filter { + struct wsm_udp_port_filter_hdr hdr; + struct wsm_udp_port_filter dhcp; + struct wsm_udp_port_filter upnp; +} __packed; + +struct xradio_ether_type_filter { + struct wsm_ether_type_filter_hdr hdr; + struct wsm_ether_type_filter ip; + struct wsm_ether_type_filter pae; + struct wsm_ether_type_filter wapi; +} __packed; + +static struct xradio_udp_port_filter xradio_udp_port_filter_on = { + .hdr.nrFilters = 2, + .dhcp = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(67), + }, + .upnp = { + .filterAction = WSM_FILTER_ACTION_FILTER_OUT, + .portType = WSM_FILTER_PORT_TYPE_DST, + .udpPort = __cpu_to_le16(1900), + }, + /* Please add other known ports to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; + +static struct wsm_udp_port_filter_hdr xradio_udp_port_filter_off = { + .nrFilters = 0, +}; + +#ifndef ETH_P_WAPI +#define ETH_P_WAPI 0x88B4 +#endif + +#ifdef TES_P2P_000B_DISABLE_EAPOL_FILTER +/* TES_P2P_000B WorkAround: + * when the link keep 10min more or less(i am not sure), + * wpa_s session maybe expired, and want to update group key. + * it will use eapol frame(802.1x,0x888E). + * if driver suspend, and discard eapol frame, then session end. + * i don't know why original code discards eapol frame in suspend. + * but now make this filter disable as WorkAround. wzw */ +static struct xradio_ether_type_filter xradio_ether_type_filter_on = { + .hdr.nrFilters = 1, +/* .ip = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_IP), + },*/ +/* .pae = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_PAE), + },*/ + .wapi = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_WAPI), + }, + /* Please add other known ether types to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; +#else +static struct xradio_ether_type_filter xradio_ether_type_filter_on = { + .hdr.nrFilters = 2, +/* .ip = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_IP), + },*/ + .pae = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_PAE), + }, + .wapi = { + .filterAction = WSM_FILTER_ACTION_FILTER_IN, + .etherType = __cpu_to_le16(ETH_P_WAPI), + }, + /* Please add other known ether types to be filtered out here and + * update nrFilters field in the header. + * Up to 4 filters are allowed. */ +}; +#endif + +static struct wsm_ether_type_filter_hdr xradio_ether_type_filter_off = { + .nrFilters = 0, +}; + +static int xradio_suspend_late(struct device *dev); +static void xradio_pm_release(struct device *dev); +static int xradio_pm_probe(struct platform_device *pdev); +static int __xradio_wow_suspend(struct xradio_vif *priv, + struct cfg80211_wowlan *wowlan); +static int __xradio_wow_resume(struct xradio_vif *priv); +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF +static int xradio_poweroff_suspend(struct xradio_common *hw_priv); +static int xradio_poweroff_resume(struct xradio_common *hw_priv); +#endif + + +/* private */ +struct xradio_suspend_state { + unsigned long bss_loss_tmo; + unsigned long connection_loss_tmo; + unsigned long join_tmo; + unsigned long direct_probe; + unsigned long link_id_gc; + bool beacon_skipping; +}; + +static const struct dev_pm_ops xradio_pm_ops = { + .suspend_noirq = xradio_suspend_late, +}; + +static struct platform_driver xradio_power_driver = { + .probe = xradio_pm_probe, + .driver = { + .name = XRADIO_PM_DEVICE, + .pm = &xradio_pm_ops, + }, +}; + +static int xradio_pm_init_common(struct xradio_pm_state *pm, + struct xradio_common *hw_priv) +{ + int ret; + pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + spin_lock_init(&pm->lock); + /* Register pm driver. */ + ret = platform_driver_register(&xradio_power_driver); + if (ret) { + pm_printk(XRADIO_DBG_ERROR, "%s:platform_driver_register failed(%d)!\n", + __FUNCTION__, ret); + return ret; + } + + /* Add pm device. */ + pm->pm_dev = platform_device_alloc(XRADIO_PM_DEVICE, 0); + if (!pm->pm_dev) { + pm_printk(XRADIO_DBG_ERROR, "%s:platform_device_alloc failed!\n", + __FUNCTION__); + platform_driver_unregister(&xradio_power_driver); + return -ENOMEM; + } + pm->pm_dev->dev.platform_data = hw_priv; + ret = platform_device_add(pm->pm_dev); + if (ret) { + pm_printk(XRADIO_DBG_ERROR, "%s:platform_device_add failed(%d)!\n", + __FUNCTION__, ret); + platform_driver_unregister(&xradio_power_driver); + kfree(pm->pm_dev); + pm->pm_dev = NULL; + } + + return ret; +} + +static void xradio_pm_deinit_common(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + platform_driver_unregister(&xradio_power_driver); + if (pm->pm_dev) { + pm->pm_dev->dev.platform_data = NULL; + platform_device_unregister(pm->pm_dev); /* kfree is already do */ + pm->pm_dev = NULL; + } +} + +#ifdef CONFIG_WAKELOCK + +int xradio_pm_init(struct xradio_pm_state *pm, + struct xradio_common *hw_priv) +{ + int ret = 0; + pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + + ret = xradio_pm_init_common(pm, hw_priv); + if (!ret) + wake_lock_init(&pm->wakelock, WAKE_LOCK_SUSPEND, XRADIO_WAKE_LOCK); + else + pm_printk(XRADIO_DBG_ERROR,"xradio_pm_init_common failed!\n"); + return ret; +} + +void xradio_pm_deinit(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + if (wake_lock_active(&pm->wakelock)) + wake_unlock(&pm->wakelock); + wake_lock_destroy(&pm->wakelock); + xradio_pm_deinit_common(pm); +} + +void xradio_pm_stay_awake(struct xradio_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + pm_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + + spin_lock_bh(&pm->lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + cur_tmo = pm->wakelock.ws.timer.expires - jiffies; +#else + cur_tmo = pm->wakelock.expires - jiffies; +#endif + if (!wake_lock_active(&pm->wakelock) || cur_tmo < (long)tmo) + wake_lock_timeout(&pm->wakelock, tmo); + spin_unlock_bh(&pm->lock); +} +void xradio_pm_lock_awake(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + spin_lock_bh(&pm->lock); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + pm->expires_save = pm->wakelock.ws.timer.expires; +#else + pm->expires_save = pm->wakelock.expires; +#endif + wake_lock_timeout(&pm->wakelock, LONG_MAX); + spin_unlock_bh(&pm->lock); +} +void xradio_pm_unlock_awake(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + spin_lock_bh(&pm->lock); + pm->expires_save -= jiffies; + if (pm->expires_save) + wake_lock_timeout(&pm->wakelock, pm->expires_save); + else + wake_lock_timeout(&pm->wakelock, 1); + spin_unlock_bh(&pm->lock); +} + +#else /* CONFIG_WAKELOCK */ + +static void xradio_pm_stay_awake_tmo(unsigned long arg) +{ +} + +int xradio_pm_init(struct xradio_pm_state *pm, + struct xradio_common *hw_priv) +{ + int ret = 0; + pm_printk(XRADIO_DBG_MSG,"%s\n", __FUNCTION__); + + ret = xradio_pm_init_common(pm, hw_priv); + if (!ret) { + init_timer(&pm->stay_awake); + pm->stay_awake.data = (unsigned long)pm; + pm->stay_awake.function = xradio_pm_stay_awake_tmo; + } else + pm_printk(XRADIO_DBG_ERROR,"xradio_pm_init_common failed!\n"); + return ret; +} + +void xradio_pm_deinit(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_TRC,"%s\n", __FUNCTION__); + del_timer_sync(&pm->stay_awake); + xradio_pm_deinit_common(pm); +} + +void xradio_pm_stay_awake(struct xradio_pm_state *pm, + unsigned long tmo) +{ + long cur_tmo; + pm_printk(XRADIO_DBG_MSG, "%s\n", __func__); + + spin_lock_bh(&pm->lock); + cur_tmo = pm->stay_awake.expires - jiffies; + if (!timer_pending(&pm->stay_awake) || cur_tmo < (long)tmo) + mod_timer(&pm->stay_awake, jiffies + tmo); + spin_unlock_bh(&pm->lock); +} +void xradio_pm_lock_awake(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + spin_lock_bh(&pm->lock); + pm->expires_save = pm->stay_awake.expires; + mod_timer(&pm->stay_awake, jiffies + LONG_MAX); + spin_unlock_bh(&pm->lock); +} +void xradio_pm_unlock_awake(struct xradio_pm_state *pm) +{ + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + spin_lock_bh(&pm->lock); + if (time_before(jiffies, pm->expires_save)) + mod_timer(&pm->stay_awake, pm->expires_save); + else + mod_timer(&pm->stay_awake, jiffies + 1); + spin_unlock_bh(&pm->lock); +} +#endif /* CONFIG_WAKELOCK */ + +static long xradio_suspend_work(struct delayed_work *work) +{ + int ret = cancel_delayed_work(work); + long tmo; + pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); + + if (ret > 0) { + /* Timer is pending */ + tmo = work->timer.expires - jiffies; + if (tmo < 0) + tmo = 0; + } else { + tmo = -1; + } + return tmo; +} + +static int xradio_resume_work(struct xradio_common *hw_priv, + struct delayed_work *work, + unsigned long tmo) +{ + pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); + if ((long)tmo < 0) + return 1; + + return queue_delayed_work(hw_priv->workqueue, work, tmo); +} + +static int xradio_suspend_late(struct device *dev) +{ + struct xradio_common *hw_priv = dev->platform_data; + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF + if (XRADIO_POWEROFF_SUSP == atomic_read(&hw_priv->suspend_state)) { + return 0; /* we don't rx data when power down wifi.*/ + } +#endif + + if (atomic_read(&hw_priv->bh_rx)) { + pm_printk(XRADIO_DBG_WARN, "%s: Suspend interrupted.\n", __func__); + return -EAGAIN; + } + return 0; +} + +static void xradio_pm_release(struct device *dev) +{ + pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); +} + +static int xradio_pm_probe(struct platform_device *pdev) +{ + pm_printk(XRADIO_DBG_TRC, "%s\n", __func__); + pdev->dev.release = xradio_pm_release; + return 0; +} + +int xradio_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv; + int i, ret = 0; + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + + if(hw_priv->bh_error) return -EBUSY; + WARN_ON(!atomic_read(&hw_priv->num_vifs)); + +#ifdef HW_RESTART + if (work_pending(&hw_priv->hw_restart_work)) + return -EBUSY; +#endif + if (work_pending(&hw_priv->query_work)) + return -EBUSY; + +#ifdef ROAM_OFFLOAD + xradio_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + if((priv->vif->type == NL80211_IFTYPE_STATION) + && (priv->join_status == XRADIO_JOIN_STATUS_STA)) { + down(&hw_priv->scan.lock); + hw_priv->scan.if_id = priv->if_id; + xradio_sched_scan_work(&hw_priv->scan.swork); + } + } +#endif /*ROAM_OFFLOAD*/ + + /* Do not suspend when datapath is not idle */ + if (hw_priv->tx_queue_stats.num_queued[0] + + hw_priv->tx_queue_stats.num_queued[1]) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of tx_queue is not empty.\n"); + return -EBUSY; + } + + /* Make sure there is no configuration requests in progress. */ + if (!mutex_trylock(&hw_priv->conf_mutex)) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of configuration requests.\n"); + return -EBUSY; + } + + /* Make sure there is no wsm_oper_lock in progress. */ + if (!mutex_trylock(&hw_priv->wsm_oper_lock)) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of wsm_oper_lock.\n"); + mutex_unlock(&hw_priv->conf_mutex); + return -EBUSY; + } + + /* Do not suspend when scanning or ROC*/ + if (down_trylock(&hw_priv->scan.lock)) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of scan requests.\n"); + goto revert1; + } + + if (delayed_work_pending(&hw_priv->scan.probe_work)) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of probe frames tx in progress.\n"); + goto revert2; + } + + /* Lock TX. */ + wsm_lock_tx_async(hw_priv); + + /* Wait to avoid possible race with bh code. + * But do not wait too long... */ + if (wait_event_timeout(hw_priv->bh_evt_wq, + !hw_priv->hw_bufs_used, HZ / 10) <= 0) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of there are frames not confirm.\n"); + goto revert3; + } + +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF +// if (STANDBY_WITH_POWER_OFF == standby_level) { + if (1) { + return xradio_poweroff_suspend(hw_priv); + } +#endif + + xradio_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + + ret = __xradio_wow_suspend(priv, wowlan); + if (ret) { + for (; i >= 0; i--) { + if (!hw_priv->vif_list[i]) + continue; + priv = (struct xradio_vif *)hw_priv->vif_list[i]->drv_priv; + __xradio_wow_resume(priv); + } + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of __xradio_wow_suspend failed!\n"); + goto revert3; + } + } + + /* Stop serving thread */ + if (xradio_bh_suspend(hw_priv)) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of xradio_bh_suspend failed!\n"); + xradio_wow_resume(hw); + return -EBUSY; + } + + /* Enable IRQ wake */ + ret = hw_priv->sbus_ops->power_mgmt(hw_priv->sbus_priv, true); + if (ret) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend sbus pm failed\n"); + xradio_wow_resume(hw); + return -EBUSY; + } + + /* Force resume if event is coming from the device. */ + if (atomic_read(&hw_priv->bh_rx)) { + pm_printk(XRADIO_DBG_WARN, "Don't suspend " + "because of recieved rx event!\n"); + xradio_wow_resume(hw); + return -EAGAIN; + } + return 0; + +revert3: + wsm_unlock_tx(hw_priv); +revert2: + up(&hw_priv->scan.lock); +revert1: + mutex_unlock(&hw_priv->conf_mutex); + mutex_unlock(&hw_priv->wsm_oper_lock); + return -EBUSY; +} + +static int __xradio_wow_suspend(struct xradio_vif *priv, + struct cfg80211_wowlan *wowlan) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct xradio_pm_state_vif *pm_state_vif = &priv->pm_state_vif; + struct xradio_suspend_state *state; + int ret; +#ifdef MCAST_FWDING + struct wsm_forwarding_offload fwdoffload = { + .fwenable = 0x1, + .flags = 0x1, + }; +#endif + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + + /* Do not suspend when join work is scheduled */ + if (work_pending(&priv->join_work)) { + pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend " + "when join work is scheduled\n", __func__); + goto revert1; + } + + /* Set UDP filter */ + wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_on.hdr, + priv->if_id); + + /* Set ethernet frame type filter */ + wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_on.hdr, + priv->if_id); + + /* Set IP multicast filter */ + wsm_set_host_sleep(hw_priv, 1, priv->if_id); + + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + WARN_ON(wsm_set_keepalive_filter(priv, true)); + +#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE + /* Set Multicast Address Filter */ + if (priv->multicast_filter.numOfAddresses) { + priv->multicast_filter.enable = 1; + wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } + + /* Set Enable Broadcast Address Filter */ + priv->broadcast_filter.action_mode = 1; + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + priv->broadcast_filter.address_mode = 3; + + xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); +#endif + +#ifdef MCAST_FWDING + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); +#endif + + /* Allocate state */ + state = xr_kzalloc(sizeof(struct xradio_suspend_state), false); + if (!state) { + pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend " + "alloc xradio_suspend_state failed.\n", __func__); + goto revert2; + } + /* Store delayed work states. */ + state->bss_loss_tmo = xradio_suspend_work(&priv->bss_loss_work); + state->connection_loss_tmo = xradio_suspend_work(&priv->connection_loss_work); + state->join_tmo = xradio_suspend_work(&priv->join_timeout); + state->link_id_gc = xradio_suspend_work(&priv->link_id_gc_work); + + /* Enable beacon skipping */ + if (priv->join_status == XRADIO_JOIN_STATUS_STA && + priv->join_dtim_period && !priv->has_multicast_subscription) { + state->beacon_skipping = true; + wsm_set_beacon_wakeup_period(hw_priv, priv->join_dtim_period, + XRADIO_BEACON_SKIPPING_MULTIPLIER * \ + priv->join_dtim_period, priv->if_id); + } + + ret = timer_pending(&priv->mcast_timeout); + if (ret) { + pm_printk(XRADIO_DBG_WARN, "%s:Do not suspend " + "mcast timeout timer_pending failed.\n", __func__); + goto revert3; + } + + /* Store suspend state */ + pm_state_vif->suspend_state = state; + + return 0; + +revert3: + xradio_resume_work(hw_priv, &priv->bss_loss_work, state->bss_loss_tmo); + xradio_resume_work(hw_priv, &priv->connection_loss_work, + state->connection_loss_tmo); + xradio_resume_work(hw_priv, &priv->join_timeout, state->join_tmo); + xradio_resume_work(hw_priv, &priv->link_id_gc_work, state->link_id_gc); + kfree(state); + +revert2: + wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_off, priv->if_id); + wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_off, priv->if_id); + wsm_set_host_sleep(hw_priv, 0, priv->if_id); + + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + WARN_ON(wsm_set_keepalive_filter(priv, false)); + +#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE + /* Set Multicast Address Filter */ + if (priv->multicast_filter.numOfAddresses) { + priv->multicast_filter.enable = 0; + wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } + + /* Set Enable Broadcast Address Filter */ + priv->broadcast_filter.action_mode = 0; + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + priv->broadcast_filter.address_mode = 0; + xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); +#endif + +#ifdef MCAST_FWDING + fwdoffload.flags = 0x0; + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); +#endif + +revert1: + /* mutex_unlock(&hw_priv->conf_mutex); */ + return -EBUSY; +} + +int xradio_wow_resume(struct ieee80211_hw *hw) +{ + + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv; + int i, ret = 0; + + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + WARN_ON(!atomic_read(&hw_priv->num_vifs)); + if(hw_priv->bh_error) return 0; + +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF + if (XRADIO_POWEROFF_SUSP == atomic_read(&hw_priv->suspend_state)) { + return xradio_poweroff_resume(hw_priv); + } +#endif + + /* Disable IRQ wake */ + hw_priv->sbus_ops->power_mgmt(hw_priv->sbus_priv, false); + + up(&hw_priv->scan.lock); + + /* Resume BH thread */ + WARN_ON(xradio_bh_resume(hw_priv)); + + xradio_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + ret = __xradio_wow_resume(priv); + if (ret) { + pm_printk(XRADIO_DBG_ERROR, "%s:__xradio_wow_resume failed!\n", __func__); + break; + } + } + + wsm_unlock_tx(hw_priv); + + /* Unlock configuration mutex */ + mutex_unlock(&hw_priv->conf_mutex); + mutex_unlock(&hw_priv->wsm_oper_lock); + + return ret; +} + +static int __xradio_wow_resume(struct xradio_vif *priv) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct xradio_pm_state_vif *pm_state_vif = &priv->pm_state_vif; + struct xradio_suspend_state *state; +#ifdef MCAST_FWDING + struct wsm_forwarding_offload fwdoffload = { + .fwenable = 0x1, + .flags = 0x0, + }; +#endif + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + + /* Restore suspend state */ + state = pm_state_vif->suspend_state; + pm_state_vif->suspend_state = NULL; + +#ifdef ROAM_OFFLOAD + if((priv->vif->type == NL80211_IFTYPE_STATION) + && (priv->join_status == XRADIO_JOIN_STATUS_STA)) + xradio_hw_sched_scan_stop(hw_priv); +#endif /*ROAM_OFFLOAD*/ + + if (state->beacon_skipping) { +#ifdef XRADIO_USE_LONG_DTIM_PERIOD + int join_dtim_period_extend; + if (priv->join_dtim_period <= 3) { + join_dtim_period_extend = priv->join_dtim_period * 3; + } else if (priv->join_dtim_period <= 5) { + join_dtim_period_extend = priv->join_dtim_period * 2; + } else { + join_dtim_period_extend = priv->join_dtim_period; + } + wsm_set_beacon_wakeup_period(hw_priv, + ((priv->beacon_int * join_dtim_period_extend) > MAX_BEACON_SKIP_TIME_MS ? + 1 : join_dtim_period_extend) , 0, priv->if_id); +#else + wsm_set_beacon_wakeup_period(hw_priv, priv->beacon_int * + (priv->join_dtim_period > MAX_BEACON_SKIP_TIME_MS ? 1 : priv->join_dtim_period), + 0, priv->if_id); +#endif + state->beacon_skipping = false; + } + + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + WARN_ON(wsm_set_keepalive_filter(priv, false)); + +#ifdef XRADIO_SUSPEND_RESUME_FILTER_ENABLE + /* Set Multicast Address Filter */ + if (priv->multicast_filter.numOfAddresses) { + priv->multicast_filter.enable = 0; + wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } + /* Set Enable Broadcast Address Filter */ + priv->broadcast_filter.action_mode = 0; + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + priv->broadcast_filter.address_mode = 0; + xradio_set_macaddrfilter(hw_priv, priv, (u8 *)&priv->broadcast_filter); +#endif + +#ifdef MCAST_FWDING + if (priv->join_status == XRADIO_JOIN_STATUS_AP) + WARN_ON(wsm_set_forwarding_offlad(hw_priv, &fwdoffload,priv->if_id)); +#endif + + /* Resume delayed work */ + xradio_resume_work(hw_priv, &priv->bss_loss_work, state->bss_loss_tmo); + xradio_resume_work(hw_priv, &priv->connection_loss_work, + state->connection_loss_tmo); + xradio_resume_work(hw_priv, &priv->join_timeout, state->join_tmo); + xradio_resume_work(hw_priv, &priv->link_id_gc_work, state->link_id_gc); + + /* Remove UDP port filter */ + wsm_set_udp_port_filter(hw_priv, &xradio_udp_port_filter_off, priv->if_id); + + /* Remove ethernet frame type filter */ + wsm_set_ether_type_filter(hw_priv, &xradio_ether_type_filter_off, priv->if_id); + + /* Remove IP multicast filter */ + wsm_set_host_sleep(hw_priv, 0, priv->if_id); + /* Free memory */ + kfree(state); + + return 0; +} +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF +static int xradio_poweroff_suspend(struct xradio_common *hw_priv) +{ + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + //flush all work. + cancel_work_sync(&hw_priv->query_work); + flush_workqueue(hw_priv->workqueue); + /* Schedule hardware restart, ensure no cmds in progress.*/ + mutex_lock(&hw_priv->wsm_cmd_mux); + atomic_set(&hw_priv->suspend_state, XRADIO_POWEROFF_SUSP); + hw_priv->hw_restart = true; + mutex_unlock(&hw_priv->wsm_cmd_mux); + /* Stop serving thread */ + if (xradio_bh_suspend(hw_priv)) { + pm_printk(XRADIO_DBG_WARN, "%s, xradio_bh_suspend failed!\n", __func__); + return -EBUSY; + } + + /* Going to sleep with wifi power down. */ + xradio_wlan_power(0); + return 0; +} + +static int xradio_poweroff_resume(struct xradio_common *hw_priv) +{ + pm_printk(XRADIO_DBG_NIY, "%s\n", __func__); + /* Revert locks */ + wsm_unlock_tx(hw_priv); + up(&hw_priv->scan.lock); + mutex_unlock(&hw_priv->conf_mutex); + mutex_unlock(&hw_priv->wsm_oper_lock); + if (schedule_work(&hw_priv->hw_restart_work) <= 0) + pm_printk(XRADIO_DBG_ERROR, "%s restart_work failed!\n", __func__); + return 0; +} +#endif diff --git a/drivers/net/wireless/xradio/pm.h b/drivers/net/wireless/xradio/pm.h new file mode 100644 index 0000000..ec77ff4 --- /dev/null +++ b/drivers/net/wireless/xradio/pm.h @@ -0,0 +1,64 @@ +/* + * power management interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef PM_H_INCLUDED +#define PM_H_INCLUDED + +#ifdef CONFIG_WAKELOCK +#include +#endif + +/* ******************************************************************** */ +/* mac80211 API */ + +#ifdef CONFIG_PM + +#define XRADIO_PM_DEVICE "xradio_pm" +#define XRADIO_WAKE_LOCK "xradio_wlan" + +/* extern */ struct xradio_common; + /* private */ struct xradio_suspend_state; + +struct xradio_pm_state { +#ifdef CONFIG_WAKELOCK + struct wake_lock wakelock; +#else + struct timer_list stay_awake; +#endif + struct platform_device *pm_dev; + spinlock_t lock; + long expires_save; +}; + +struct xradio_pm_state_vif { + struct xradio_suspend_state *suspend_state; +}; + +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF +enum suspend_state { + XRADIO_RESUME = 0, + XRADIO_CONNECT_SUSP, + XRADIO_DISCONNECT_SUSP, + XRADIO_POWEROFF_SUSP +}; +#endif +int xradio_pm_init(struct xradio_pm_state *pm, struct xradio_common *priv); +void xradio_pm_deinit(struct xradio_pm_state *pm); +void xradio_pm_stay_awake(struct xradio_pm_state *pm, unsigned long tmo); +void xradio_pm_lock_awake(struct xradio_pm_state *pm); +void xradio_pm_unlock_awake(struct xradio_pm_state *pm); +int xradio_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); +int xradio_wow_resume(struct ieee80211_hw *hw); + +#endif /* CONFIG_PM */ + +#endif diff --git a/drivers/net/wireless/xradio/queue.c b/drivers/net/wireless/xradio/queue.c new file mode 100644 index 0000000..366cced --- /dev/null +++ b/drivers/net/wireless/xradio/queue.c @@ -0,0 +1,898 @@ +/* + * Queue implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#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 = xr_kmalloc(sizeof(struct xradio_queue_item), false); + SYS_BUG(!gc_item); + memcpy(gc_item, item, sizeof(struct xradio_queue_item)); + list_add_tail(&gc_item->head, gc_list); +} + +static void __xradio_queue_gc(struct xradio_queue *queue, + struct list_head *head, + bool unlock) +{ + struct xradio_queue_stats *stats = queue->stats; + struct xradio_queue_item *item = NULL; + //struct xradio_vif *priv; + int if_id; + bool wakeup_stats = false; + + while (!list_empty(&queue->queue)) { + struct xradio_txpriv *txpriv; + item = list_first_entry( + &queue->queue, struct xradio_queue_item, head); + if (time_before(jiffies, item->queue_timestamp+queue->ttl)) + break; + + txpriv = &item->txpriv; + if_id = txpriv->if_id; + --queue->num_queued; + --queue->num_queued_vif[if_id]; + --queue->link_map_cache[if_id][txpriv->link_id]; + spin_lock_bh(&stats->lock); + --stats->num_queued[if_id]; + if (!--stats->link_map_cache[if_id][txpriv->link_id]) + wakeup_stats = true; + spin_unlock_bh(&stats->lock); + //priv = xrwl_hwpriv_to_vifpriv(stats->hw_priv, if_id); + //if (priv) { + // xradio_debug_tx_ttl(priv); + // spin_unlock(&priv->vif_lock); + //} + xradio_queue_register_post_gc(head, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + } + + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + //modified by yangfh for test WFD + if (queue->overfull) { + if (queue->num_queued <= ((stats->hw_priv->vif0_throttle + + stats->hw_priv->vif1_throttle+2)>>1)) { + queue->overfull = false; + if (unlock) { + __xradio_queue_unlock(queue); + } + } else if (item) { + unsigned long tmo = item->queue_timestamp + queue->ttl; + mod_timer(&queue->gc, tmo); + xradio_pm_stay_awake(&stats->hw_priv->pm_state, + tmo - jiffies); + } + } +} + +static void xradio_queue_gc(unsigned long arg) +{ + LIST_HEAD(list); + struct xradio_queue *queue = + (struct xradio_queue *)arg; + + spin_lock_bh(&queue->lock); + __xradio_queue_gc(queue, &list, true); + spin_unlock_bh(&queue->lock); + xradio_queue_post_gc(queue->stats, &list); +} + +int xradio_queue_stats_init(struct xradio_queue_stats *stats, + size_t map_capacity, + xradio_queue_skb_dtor_t skb_dtor, + struct xradio_common *hw_priv) +{ + int i; + + memset(stats, 0, sizeof(*stats)); + stats->map_capacity = map_capacity; + stats->skb_dtor = skb_dtor; + stats->hw_priv = hw_priv; + spin_lock_init(&stats->lock); + init_waitqueue_head(&stats->wait_link_id_empty); + for (i = 0; i < XRWL_MAX_VIFS; i++) { + stats->link_map_cache[i] = xr_kzalloc(sizeof(int[map_capacity]), false); + if (!stats->link_map_cache[i]) { + for (; i >= 0; i--) + kfree(stats->link_map_cache[i]); + return -ENOMEM; + } + } + + return 0; +} + +int xradio_queue_init(struct xradio_queue *queue, + struct xradio_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl) +{ + int i; + + memset(queue, 0, sizeof(*queue)); + queue->stats = stats; + queue->capacity = capacity; + queue->queue_id = queue_id; + queue->ttl = ttl; + INIT_LIST_HEAD(&queue->queue); + INIT_LIST_HEAD(&queue->pending); + INIT_LIST_HEAD(&queue->free_pool); + spin_lock_init(&queue->lock); + init_timer(&queue->gc); + queue->gc.data = (unsigned long)queue; + queue->gc.function = xradio_queue_gc; + + queue->pool = xr_kzalloc(sizeof(struct xradio_queue_item) * capacity, + false); + if (!queue->pool) + return -ENOMEM; + + for (i = 0; i < XRWL_MAX_VIFS; i++) { + queue->link_map_cache[i] = + xr_kzalloc(sizeof(int[stats->map_capacity]), false); + if (!queue->link_map_cache[i]) { + for (; i >= 0; i--) + kfree(queue->link_map_cache[i]); + kfree(queue->pool); + queue->pool = NULL; + return -ENOMEM; + } + } + + for (i = 0; i < capacity; ++i) + list_add_tail(&queue->pool[i].head, &queue->free_pool); + + return 0; +} + +/* TODO:COMBO: Flush only a particular interface specific parts */ +int xradio_queue_clear(struct xradio_queue *queue, int if_id) +{ + int i, cnt, iter; + struct xradio_queue_stats *stats = queue->stats; + LIST_HEAD(gc_list); + + cnt = 0; + spin_lock_bh(&queue->lock); + queue->generation++; + queue->generation &= 0xf; + list_splice_tail_init(&queue->queue, &queue->pending); + while (!list_empty(&queue->pending)) { + struct xradio_queue_item *item = list_first_entry( + &queue->pending, struct xradio_queue_item, head); + SYS_WARN(!item->skb); + if (XRWL_ALL_IFS == if_id || item->txpriv.if_id == if_id) { + xradio_queue_register_post_gc(&gc_list, item); + item->skb = NULL; + list_move_tail(&item->head, &queue->free_pool); + cnt++; + } + } + queue->num_queued -= cnt; + queue->num_pending = 0; + if (XRWL_ALL_IFS != if_id) { + queue->num_queued_vif[if_id] = 0; + queue->num_pending_vif[if_id] = 0; + } else { + for (iter = 0; iter < XRWL_MAX_VIFS; iter++) { + queue->num_queued_vif[iter] = 0; + queue->num_pending_vif[iter] = 0; + } + } + spin_lock_bh(&stats->lock); + if (XRWL_ALL_IFS != if_id) { + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued[if_id] -= + queue->link_map_cache[if_id][i]; + stats->link_map_cache[if_id][i] -= + queue->link_map_cache[if_id][i]; + queue->link_map_cache[if_id][i] = 0; + } + } else { + for (iter = 0; iter < XRWL_MAX_VIFS; iter++) { + for (i = 0; i < stats->map_capacity; ++i) { + stats->num_queued[iter] -= + queue->link_map_cache[iter][i]; + stats->link_map_cache[iter][i] -= + queue->link_map_cache[iter][i]; + queue->link_map_cache[iter][i] = 0; + } + } + } + spin_unlock_bh(&stats->lock); + if (unlikely(queue->overfull)) { + queue->overfull = false; + __xradio_queue_unlock(queue); + } + spin_unlock_bh(&queue->lock); + wake_up(&stats->wait_link_id_empty); + xradio_queue_post_gc(stats, &gc_list); + return 0; +} + +void xradio_queue_stats_deinit(struct xradio_queue_stats *stats) +{ + int i; + + for (i = 0; i < XRWL_MAX_VIFS ; i++) { + kfree(stats->link_map_cache[i]); + stats->link_map_cache[i] = NULL; + } +} + +void xradio_queue_deinit(struct xradio_queue *queue) +{ + int i; + + xradio_queue_clear(queue, XRWL_ALL_IFS); + del_timer_sync(&queue->gc); + INIT_LIST_HEAD(&queue->free_pool); + kfree(queue->pool); + for (i = 0; i < XRWL_MAX_VIFS; i++) { + kfree(queue->link_map_cache[i]); + queue->link_map_cache[i] = NULL; + } + queue->pool = NULL; + queue->capacity = 0; +} + +size_t xradio_queue_get_num_queued(struct xradio_vif *priv, + struct xradio_queue *queue, + u32 link_id_map) +{ + size_t ret; + int i, bit; + size_t map_capacity = queue->stats->map_capacity; + + if (!link_id_map) + return 0; + + spin_lock_bh(&queue->lock); + if (likely(link_id_map == (u32) -1)) { + ret = queue->num_queued_vif[priv->if_id] - + queue->num_pending_vif[priv->if_id]; + } else { + ret = 0; + for (i = 0, bit = 1; i < map_capacity; ++i, bit <<= 1) { + if (link_id_map & bit) + ret += + queue->link_map_cache[priv->if_id][i]; + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int xradio_queue_put(struct xradio_queue *queue, struct sk_buff *skb, + struct xradio_txpriv *txpriv) +{ + int ret = 0; +#ifdef CONFIG_XRADIO_TESTMODE + struct timeval tmval; +#endif /*CONFIG_XRADIO_TESTMODE*/ + LIST_HEAD(gc_list); + struct xradio_queue_stats *stats = queue->stats; + /* TODO:COMBO: Add interface ID info to queue item */ + + if (txpriv->link_id >= queue->stats->map_capacity) + return -EINVAL; + + spin_lock_bh(&queue->lock); + if (!SYS_WARN(list_empty(&queue->free_pool))) { + struct xradio_queue_item *item = list_first_entry( + &queue->free_pool, struct xradio_queue_item, head); + SYS_BUG(item->skb); + + list_move_tail(&item->head, &queue->queue); + item->skb = skb; + item->txpriv = *txpriv; + item->generation = 1; /* avoid packet ID is 0.*/ + item->pack_stk_wr = 0; + item->packetID = xradio_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool, + txpriv->if_id, txpriv->raw_link_id); + item->queue_timestamp = jiffies; +#ifdef CONFIG_XRADIO_TESTMODE + do_gettimeofday(&tmval); + item->qdelay_timestamp = tmval.tv_usec; +#endif /*CONFIG_XRADIO_TESTMODE*/ + +#ifdef TES_P2P_0002_ROC_RESTART + if (TES_P2P_0002_state == TES_P2P_0002_STATE_SEND_RESP) { + TES_P2P_0002_packet_id = item->packetID; + TES_P2P_0002_state = TES_P2P_0002_STATE_GET_PKTID; + txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_GET_PKTID]\n"); + } +#endif + + ++queue->num_queued; + ++queue->num_queued_vif[txpriv->if_id]; + ++queue->link_map_cache[txpriv->if_id][txpriv->link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued[txpriv->if_id]; + ++stats->link_map_cache[txpriv->if_id][txpriv->link_id]; + spin_unlock_bh(&stats->lock); + + /* + * TX may happen in parallel sometimes. + * Leave extra queue slots so we don't overflow. + */ + if (queue->overfull == false && + queue->num_queued >= + ((stats->hw_priv->vif0_throttle +stats->hw_priv->vif1_throttle) + - (num_present_cpus() - 1))) { + queue->overfull = true; + __xradio_queue_lock(queue); + mod_timer(&queue->gc, jiffies); + txrx_printk(XRADIO_DBG_NIY,"!lock queue\n"); + } + } else { + ret = -ENOENT; + } +#if 0 + txrx_printk(XRADIO_DBG_ERROR, "queue_put queue %d, %d, %d\n", + queue->num_queued, + queue->link_map_cache[txpriv->if_id][txpriv->link_id], + queue->num_pending); + txrx_printk(XRADIO_DBG_ERROR, "queue_put stats %d, %d\n", stats->num_queued, + stats->link_map_cache[txpriv->if_id][txpriv->link_id]); +#endif + spin_unlock_bh(&queue->lock); + return ret; +} + +int xradio_queue_get(struct xradio_queue *queue, + int if_id, + u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + struct xradio_txpriv **txpriv) +{ + int ret = -ENOENT; + struct xradio_queue_item *item; + struct xradio_queue_stats *stats = queue->stats; + bool wakeup_stats = false; +#ifdef CONFIG_XRADIO_TESTMODE + struct timeval tmval; +#endif /*CONFIG_XRADIO_TESTMODE*/ + + spin_lock_bh(&queue->lock); + list_for_each_entry(item, &queue->queue, head) { + if ((item->txpriv.if_id == if_id) && + (link_id_map & BIT(item->txpriv.link_id))) { + ret = 0; + break; + } + } + + if (!SYS_WARN(ret)) { + *tx = (struct wsm_tx *)item->skb->data; + *tx_info = IEEE80211_SKB_CB(item->skb); + *txpriv = &item->txpriv; + (*tx)->packetID = __cpu_to_le32(item->packetID); + list_move_tail(&item->head, &queue->pending); + ++queue->num_pending; + ++queue->num_pending_vif[item->txpriv.if_id]; + --queue->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]; + item->xmit_timestamp = jiffies; +#ifdef CONFIG_XRADIO_TESTMODE + do_gettimeofday(&tmval); + item->mdelay_timestamp = tmval.tv_usec; +#endif /*CONFIG_XRADIO_TESTMODE*/ + + spin_lock_bh(&stats->lock); + --stats->num_queued[item->txpriv.if_id]; + if (!--stats->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]) + wakeup_stats = true; + + spin_unlock_bh(&stats->lock); +#if 0 + txrx_printk(XRADIO_DBG_ERROR, "queue_get queue %d, %d, %d\n", + queue->num_queued, + queue->link_map_cache[item->txpriv.if_id][item->txpriv.link_id], + queue->num_pending); + txrx_printk(XRADIO_DBG_ERROR, "queue_get stats %d, %d\n", stats->num_queued, + stats->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]); +#endif + } + spin_unlock_bh(&queue->lock); + if (wakeup_stats) + wake_up(&stats->wait_link_id_empty); + + return ret; +} + +#ifdef CONFIG_XRADIO_TESTMODE +int xradio_queue_requeue(struct xradio_common *hw_priv, + struct xradio_queue *queue, u32 packetID, bool check) +#else +int xradio_queue_requeue(struct xradio_queue *queue, u32 packetID, bool check) +#endif +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct xradio_queue_item *item; + struct xradio_queue_stats *stats = queue->stats; + + xradio_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + + item = &queue->pool[item_id]; +#ifdef P2P_MULTIVIF + if (check && item->txpriv.if_id == XRWL_GENERIC_IF_ID) { +#else + if (check && item->txpriv.offchannel_if_id == XRWL_GENERIC_IF_ID) { +#endif + txrx_printk(XRADIO_DBG_MSG, "Requeued frame dropped for " + "generic interface id.\n"); +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_remove(hw_priv, queue, packetID); +#else + xradio_queue_remove(queue, packetID); +#endif + return 0; + } + +#ifndef P2P_MULTIVIF + if (!check) + item->txpriv.offchannel_if_id = XRWL_GENERIC_IF_ID; +#endif + + /*if_id = item->txpriv.if_id;*/ + + spin_lock_bh(&queue->lock); + SYS_BUG(queue_id != queue->queue_id); + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + SYS_WARN(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + SYS_WARN(1); + ret = -ENOENT; + } else { + --queue->num_pending; + --queue->num_pending_vif[if_id]; + ++queue->link_map_cache[if_id][item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued[item->txpriv.if_id]; + ++stats->link_map_cache[if_id][item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + item->generation = ++item_generation; + item->packetID = xradio_queue_make_packet_id( + queue_generation, queue_id, item_generation, item_id, + if_id, link_id); + list_move(&item->head, &queue->queue); +#if 0 + txrx_printk(XRADIO_DBG_ERROR, "queue_requeue queue %d, %d, %d\n", + queue->num_queued, + queue->link_map_cache[if_id][item->txpriv.link_id], + queue->num_pending); + txrx_printk(XRADIO_DBG_ERROR, "queue_requeue stats %d, %d\n", + stats->num_queued, + stats->link_map_cache[if_id][item->txpriv.link_id]); +#endif + } + spin_unlock_bh(&queue->lock); + return ret; +} + +int xradio_queue_requeue_all(struct xradio_queue *queue) +{ + struct xradio_queue_stats *stats = queue->stats; + spin_lock_bh(&queue->lock); + while (!list_empty(&queue->pending)) { + struct xradio_queue_item *item = list_entry( + queue->pending.prev, struct xradio_queue_item, head); + + --queue->num_pending; + --queue->num_pending_vif[item->txpriv.if_id]; + ++queue->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]; + + spin_lock_bh(&stats->lock); + ++stats->num_queued[item->txpriv.if_id]; + ++stats->link_map_cache[item->txpriv.if_id] + [item->txpriv.link_id]; + spin_unlock_bh(&stats->lock); + + ++item->generation; + item->packetID = xradio_queue_make_packet_id( + queue->generation, queue->queue_id, + item->generation, item - queue->pool, + item->txpriv.if_id, item->txpriv.raw_link_id); + list_move(&item->head, &queue->queue); + } + spin_unlock_bh(&queue->lock); + + return 0; +} +#ifdef CONFIG_XRADIO_TESTMODE +int xradio_queue_remove(struct xradio_common *hw_priv, + struct xradio_queue *queue, u32 packetID) +#else +int xradio_queue_remove(struct xradio_queue *queue, u32 packetID) +#endif /*CONFIG_XRADIO_TESTMODE*/ +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct xradio_queue_item *item; + struct xradio_queue_stats *stats = queue->stats; + struct sk_buff *gc_skb = NULL; + struct xradio_txpriv gc_txpriv; + + xradio_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + SYS_BUG(queue_id != queue->queue_id); + /*TODO:COMBO:Add check for interface ID also */ + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + SYS_WARN(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + SYS_WARN(1); + ret = -ENOENT; + } else { + gc_txpriv = item->txpriv; + gc_skb = item->skb; + item->skb = NULL; + --queue->num_pending; + --queue->num_pending_vif[if_id]; + --queue->num_queued; + --queue->num_queued_vif[if_id]; + ++queue->num_sent; + ++item->generation; +#ifdef CONFIG_XRADIO_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->start_stop_tsm.start) { + if (queue_id == hw_priv->tsm_info.ac) { + struct timeval tmval; + unsigned long queue_delay; + unsigned long media_delay; + do_gettimeofday(&tmval); + + if (tmval.tv_usec > item->qdelay_timestamp) + queue_delay = tmval.tv_usec - + item->qdelay_timestamp; + else + queue_delay = tmval.tv_usec + + 1000000 - item->qdelay_timestamp; + + if (tmval.tv_usec > item->mdelay_timestamp) + media_delay = tmval.tv_usec - + item->mdelay_timestamp; + else + media_delay = tmval.tv_usec + + 1000000 - item->mdelay_timestamp; + hw_priv->tsm_info.sum_media_delay += + media_delay; + hw_priv->tsm_info.sum_pkt_q_delay += queue_delay; + if (queue_delay <= 10000) + hw_priv->tsm_stats.bin0++; + else if (queue_delay <= 20000) + hw_priv->tsm_stats.bin1++; + else if (queue_delay <= 40000) + hw_priv->tsm_stats.bin2++; + else + hw_priv->tsm_stats.bin3++; + } + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_XRADIO_TESTMODE*/ + /* Do not use list_move_tail here, but list_move: + * try to utilize cache row. + */ + list_move(&item->head, &queue->free_pool); + + if (unlikely(queue->overfull) && + (queue->num_queued <= ((stats->hw_priv->vif0_throttle + stats->hw_priv->vif1_throttle + 2)>>1))) { + queue->overfull = false; + __xradio_queue_unlock(queue); + } + } + spin_unlock_bh(&queue->lock); + +#if 0 + txrx_printk(XRADIO_DBG_ERROR, "queue_drop queue %d, %d, %d\n", + queue->num_queued, queue->link_map_cache[if_id][0], + queue->num_pending); + txrx_printk(XRADIO_DBG_ERROR, "queue_drop stats %d, %d\n", stats->num_queued, + stats->link_map_cache[if_id][0]); +#endif + if (gc_skb) + stats->skb_dtor(stats->hw_priv, gc_skb, &gc_txpriv); + + return ret; +} + +int xradio_queue_get_skb(struct xradio_queue *queue, u32 packetID, + struct sk_buff **skb, + const struct xradio_txpriv **txpriv) +{ + int ret = 0; + u8 queue_generation, queue_id, item_generation, item_id, if_id, link_id; + struct xradio_queue_item *item; + + xradio_queue_parse_id(packetID, &queue_generation, &queue_id, + &item_generation, &item_id, &if_id, &link_id); + + item = &queue->pool[item_id]; + + spin_lock_bh(&queue->lock); + SYS_BUG(queue_id != queue->queue_id); + /* TODO:COMBO: Add check for interface ID here */ + if (unlikely(queue_generation != queue->generation)) { + ret = -ENOENT; + } else if (unlikely(item_id >= (unsigned) queue->capacity)) { + SYS_WARN(1); + ret = -EINVAL; + } else if (unlikely(item->generation != item_generation)) { + SYS_WARN(1); + ret = -ENOENT; + } else { + *skb = item->skb; + *txpriv = &item->txpriv; + } + spin_unlock_bh(&queue->lock); + return ret; +} + +void xradio_queue_lock(struct xradio_queue *queue) +{ + spin_lock_bh(&queue->lock); + __xradio_queue_lock(queue); + spin_unlock_bh(&queue->lock); +} + +void xradio_queue_unlock(struct xradio_queue *queue) +{ + spin_lock_bh(&queue->lock); + __xradio_queue_unlock(queue); + spin_unlock_bh(&queue->lock); +} + +bool xradio_queue_get_xmit_timestamp(struct xradio_queue *queue, + unsigned long *timestamp, int if_id, + u32 pending_frameID, u32 *Old_frame_ID) +{ + struct xradio_queue_item *item; + bool ret; + + spin_lock_bh(&queue->lock); + ret = !list_empty(&queue->pending); + if (ret) { + list_for_each_entry(item, &queue->pending, head) { + if (((if_id == XRWL_GENERIC_IF_ID) || + (if_id == XRWL_ALL_IFS) || + (item->txpriv.if_id == if_id)) && + (item->packetID != pending_frameID)) { + if (time_before(item->xmit_timestamp, + *timestamp)) { + *timestamp = item->xmit_timestamp; + *Old_frame_ID = item->packetID; + } + } + } + } + spin_unlock_bh(&queue->lock); + return ret; +} + +bool xradio_queue_stats_is_empty(struct xradio_queue_stats *stats, + u32 link_id_map, int if_id) +{ + bool empty = true; + + spin_lock_bh(&stats->lock); + if (link_id_map == (u32)-1) + empty = stats->num_queued[if_id] == 0; + else { + int i, if_id; + for (if_id = 0; if_id < XRWL_MAX_VIFS; if_id++) { + for (i = 0; i < stats->map_capacity; ++i) { + if (link_id_map & BIT(i)) { + if (stats->link_map_cache[if_id][i]) { + empty = false; + break; + } + } + } + } + } + spin_unlock_bh(&stats->lock); + + return empty; +} + +bool xradio_query_txpkt_timeout(struct xradio_common *hw_priv, int if_id, + u32 pending_pkt_id, long *timeout) +{ + int i; + bool pending = false; + unsigned long timestamp = jiffies; + struct xradio_queue *queue = NULL; + struct xradio_queue_item *item = NULL; + struct xradio_queue *old_queue = NULL; + struct xradio_queue_item *old_item = NULL; + u8 pack_stk_wr = 0; + + /* Get oldest frame.*/ + for (i = 0; i < AC_QUEUE_NUM; ++i) { + queue = &hw_priv->tx_queue[i]; + spin_lock_bh(&queue->lock); + if (!list_empty(&queue->pending)) { + list_for_each_entry(item, &queue->pending, head) { + if (((if_id == XRWL_GENERIC_IF_ID) || + (if_id == XRWL_ALL_IFS) || + (item->txpriv.if_id == if_id)) && + (item->packetID != pending_pkt_id)) { + if (time_before(item->xmit_timestamp, timestamp)) { + timestamp = item->xmit_timestamp; + pack_stk_wr = item->pack_stk_wr; + old_queue = queue; + old_item = item; + } + } + } + pending = true; + } + spin_unlock_bh(&queue->lock); + } + if (!pending) + return false; + + /* Check if frame transmission is timed out. + * add (WSM_CMD_LAST_CHANCE_TIMEOUT>>1) for stuck workaround.*/ + *timeout = timestamp + WSM_CMD_LAST_CHANCE_TIMEOUT - jiffies; + if (unlikely(*timeout < 0) && !pack_stk_wr) { + struct ieee80211_hdr *frame = NULL; + const struct xradio_txpriv *txpriv = NULL; + u16 fctl = 0x0; + u32 len = 0x0; + u8 if_id = 0, link_id = 0, tid = 0; + + /* query the timeout frame. */ + spin_lock_bh(&old_queue->lock); + if (likely(old_item->skb && !hw_priv->query_packetID)) { + hw_priv->query_packetID = old_item->packetID; + old_item->pack_stk_wr = 1; + atomic_add(1, &hw_priv->query_cnt); + + /* Info of stuck frames for debug.*/ + txpriv = &old_item->txpriv; + frame = (struct ieee80211_hdr *)(&old_item->skb->data[txpriv->offset]); + fctl = frame->frame_control; + len = old_item->skb->len; + if_id = txpriv->if_id; + link_id = txpriv->link_id; + tid = txpriv->tid; + } + spin_unlock_bh(&old_queue->lock); + /* Dump Info of stuck frames. */ + if (frame) { + txrx_printk(XRADIO_DBG_ERROR, "TX confirm timeout(%ds).\n", + WSM_CMD_LAST_CHANCE_TIMEOUT/HZ); + txrx_printk(XRADIO_DBG_ERROR, "if=%d, linkid=%d, tid=%d, " \ + "old_packetID=0x%08x, fctl=0x%04x, len=%d, wr=%d\n", + if_id, link_id, tid, hw_priv->query_packetID, fctl, len, + pack_stk_wr); + } + /* Return half of timeout for query packet. */ + *timeout = (WSM_CMD_LAST_CHANCE_TIMEOUT>>1); + } else if (unlikely(pack_stk_wr)){ + *timeout = *timeout + (WSM_CMD_LAST_CHANCE_TIMEOUT>>1); + txrx_printk(XRADIO_DBG_MSG,"%s, wr and timeout=%ld\n", __func__, *timeout); + } + return pending; +} diff --git a/drivers/net/wireless/xradio/queue.h b/drivers/net/wireless/xradio/queue.h new file mode 100644 index 0000000..db55c4c --- /dev/null +++ b/drivers/net/wireless/xradio/queue.h @@ -0,0 +1,153 @@ +/* + * queue operations for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef XRADIO_QUEUE_H_INCLUDED +#define XRADIO_QUEUE_H_INCLUDED + +/* private */ struct xradio_queue_item; + +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct xradio_common; +/* extern */ struct xradio_vif; +/* extern */ struct ieee80211_tx_queue_stats; +/* extern */ struct xradio_txpriv; + +/* forward */ struct xradio_queue_stats; + +typedef void (*xradio_queue_skb_dtor_t)(struct xradio_common *priv, + struct sk_buff *skb, + const struct xradio_txpriv *txpriv); + +struct xradio_queue { + struct xradio_queue_stats *stats; + size_t capacity; + size_t num_queued; + size_t num_queued_vif[XRWL_MAX_VIFS]; + size_t num_pending; + size_t num_pending_vif[XRWL_MAX_VIFS]; + size_t num_sent; + struct xradio_queue_item *pool; + struct list_head queue; + struct list_head free_pool; + struct list_head pending; + int tx_locked_cnt; + int *link_map_cache[XRWL_MAX_VIFS]; + bool overfull; + spinlock_t lock; + u8 queue_id; + u8 generation; + struct timer_list gc; + unsigned long ttl; +}; + +struct xradio_queue_stats { + spinlock_t lock; + int *link_map_cache[XRWL_MAX_VIFS]; + int num_queued[XRWL_MAX_VIFS]; + size_t map_capacity; + wait_queue_head_t wait_link_id_empty; + xradio_queue_skb_dtor_t skb_dtor; + struct xradio_common *hw_priv; +}; + +struct xradio_txpriv { + u8 link_id; + u8 raw_link_id; + u8 tid; + u8 rate_id; + u8 offset; + u8 if_id; +#ifndef P2P_MULTIVIF + u8 offchannel_if_id; +#else + u8 raw_if_id; +#endif + u8 use_bg_rate; +}; + +int xradio_queue_stats_init(struct xradio_queue_stats *stats, + size_t map_capacity, + xradio_queue_skb_dtor_t skb_dtor, + struct xradio_common *priv); +int xradio_queue_init(struct xradio_queue *queue, + struct xradio_queue_stats *stats, + u8 queue_id, + size_t capacity, + unsigned long ttl); +int xradio_queue_clear(struct xradio_queue *queue, int if_id); +void xradio_queue_stats_deinit(struct xradio_queue_stats *stats); +void xradio_queue_deinit(struct xradio_queue *queue); + +size_t xradio_queue_get_num_queued(struct xradio_vif *priv, + struct xradio_queue *queue, + u32 link_id_map); +int xradio_queue_put(struct xradio_queue *queue, + struct sk_buff *skb, struct xradio_txpriv *txpriv); +int xradio_queue_get(struct xradio_queue *queue, + int if_id, u32 link_id_map, + struct wsm_tx **tx, + struct ieee80211_tx_info **tx_info, + struct xradio_txpriv **txpriv); + +#ifdef CONFIG_XRADIO_TESTMODE +int xradio_queue_requeue(struct xradio_common *hw_priv, + struct xradio_queue *queue, + u32 packetID, bool check); +#else +int xradio_queue_requeue(struct xradio_queue *queue, u32 packetID, bool check); +#endif +int xradio_queue_requeue_all(struct xradio_queue *queue); +#ifdef CONFIG_XRADIO_TESTMODE +int xradio_queue_remove(struct xradio_common *hw_priv, + struct xradio_queue *queue, + u32 packetID); +#else +int xradio_queue_remove(struct xradio_queue *queue, + u32 packetID); +#endif /*CONFIG_XRADIO_TESTMODE*/ +int xradio_queue_get_skb(struct xradio_queue *queue, u32 packetID, + struct sk_buff **skb, + const struct xradio_txpriv **txpriv); +void xradio_queue_lock(struct xradio_queue *queue); +void xradio_queue_unlock(struct xradio_queue *queue); +bool xradio_queue_get_xmit_timestamp(struct xradio_queue *queue, + unsigned long *timestamp, int if_id, + u32 pending_frameID, u32 *Old_frame_ID); +bool xradio_query_txpkt_timeout(struct xradio_common *hw_priv, int if_id, + u32 pending_pkt_id, long *timeout); + + +bool xradio_queue_stats_is_empty(struct xradio_queue_stats *stats, + u32 link_id_map, int if_id); + +static inline u8 xradio_queue_get_queue_id(u32 packetID) +{ + return (packetID >> 16) & 0xF; +} + +static inline u8 xradio_queue_get_if_id(u32 packetID) +{ + return (packetID >> 20) & 0xF; +} + +static inline u8 xradio_queue_get_link_id(u32 packetID) +{ + return (packetID >> 24) & 0xF; +} + +static inline u8 xradio_queue_get_generation(u32 packetID) +{ + return (packetID >> 8) & 0xFF; +} + +#endif /* XRADIO_QUEUE_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/sbus.h b/drivers/net/wireless/xradio/sbus.h new file mode 100644 index 0000000..67c1c6c --- /dev/null +++ b/drivers/net/wireless/xradio/sbus.h @@ -0,0 +1,59 @@ +/* + * Sbus interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef __SBUS_H +#define __SBUS_H + +#include +#include +/* + * sbus priv forward definition. + * Implemented and instantiated in particular modules. + */ + +struct xradio_common; +/*sdio bus private struct*/ +#define SDIO_UNLOAD 0 +#define SDIO_LOAD 1 + +typedef void (*sbus_irq_handler)(void *priv); +struct sbus_priv { + struct sdio_func *func; + spinlock_t lock; + sbus_irq_handler irq_handler; + void *irq_priv; + wait_queue_head_t init_wq; + int load_state; +}; + +struct sbus_ops { + int (*sbus_data_read)(struct sbus_priv *self, unsigned int addr, + void *dst, int count); + int (*sbus_data_write)(struct sbus_priv *self, unsigned int addr, + const void *src, int count); + void (*lock)(struct sbus_priv *self); + void (*unlock)(struct sbus_priv *self); + size_t (*align_size)(struct sbus_priv *self, size_t size); + int (*set_block_size)(struct sbus_priv *self, size_t size); + int (*irq_subscribe)(struct sbus_priv *self, + sbus_irq_handler handler, void *priv); + int (*irq_unsubscribe)(struct sbus_priv *self); + int (*power_mgmt)(struct sbus_priv *self, bool suspend); + int (*reset)(struct sbus_priv *self); +}; + +//sbus init functions +struct device * sbus_sdio_init(struct sbus_ops **sdio_ops, + struct sbus_priv **sdio_priv); +void sbus_sdio_deinit(void); + +#endif /* __SBUS_H */ diff --git a/drivers/net/wireless/xradio/scan.c b/drivers/net/wireless/xradio/scan.c new file mode 100644 index 0000000..6ab5c78 --- /dev/null +++ b/drivers/net/wireless/xradio/scan.c @@ -0,0 +1,1144 @@ +/* + * Scan implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include "xradio.h" +#include "scan.h" +#include "sta.h" +#include "pm.h" + +static void xradio_scan_restart_delayed(struct xradio_vif *priv); + +#ifdef CONFIG_XRADIO_TESTMODE +static int xradio_advance_scan_start(struct xradio_common *hw_priv) +{ + int tmo = 0; + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + tmo += hw_priv->advanceScanElems.duration; + xradio_pm_stay_awake(&hw_priv->pm_state, tmo * HZ / 1000); + /* Invoke Advance Scan Duration Timeout Handler */ + queue_delayed_work(hw_priv->workqueue, + &hw_priv->advance_scan_timeout, tmo * HZ / 1000); + return 0; +} +#endif + +static void xradio_remove_wps_p2p_ie(struct wsm_template_frame *frame) +{ + u8 *ies; + u32 ies_len; + u32 ie_len; + u32 p2p_ie_len = 0; + u32 wps_ie_len = 0; + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + ies = &frame->skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame->skb->len - sizeof(struct ieee80211_hdr_3addr); + while (ies_len >= 6) { + ie_len = ies[1] + 2; + ies_len -= ie_len; + if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) && + (ies[2] == 0x00 && ies[3] == 0x50 && + ies[4] == 0xf2 && ies[5] == 0x04)) { + wps_ie_len = ie_len; + memmove(ies, ies + ie_len, ies_len); + } else if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) && + (ies[2] == 0x50 && ies[3] == 0x6f && + ies[4] == 0x9a && ies[5] == 0x09)) { + p2p_ie_len = ie_len; + memmove(ies, ies + ie_len, ies_len); + } else { + ies += ie_len; + } + } + + if (p2p_ie_len || wps_ie_len) { + skb_trim(frame->skb, frame->skb->len - (p2p_ie_len + wps_ie_len)); + } +} + +#ifdef CONFIG_XRADIO_TESTMODE +static int xradio_disable_filtering(struct xradio_vif *priv) +{ + int ret = 0; + bool bssid_filtering = 0; + struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_control bf_control; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* RX Filter Disable */ + rx_filter.promiscuous = 0; + rx_filter.bssid = 0; + rx_filter.fcs = 0; + ret = wsm_set_rx_filter(hw_priv, &rx_filter, priv->if_id); + + /* Beacon Filter Disable */ + bf_control.enabled = 0; + bf_control.bcn_count = 1; + if (!ret) + ret = wsm_beacon_filter_control(hw_priv, &bf_control, priv->if_id); + + /* BSSID Filter Disable */ + if (!ret) + ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, priv->if_id); + + return ret; +} +#endif + +static int xradio_scan_start(struct xradio_vif *priv, struct wsm_scan *scan) +{ + int ret, i; +#ifdef FPGA_SETUP + int tmo = 5000; +#else + int tmo = 5000; +#endif + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + for (i = 0; i < scan->numOfChannels; ++i) + tmo += scan->ch[i].maxChannelTime + 10; + + atomic_set(&hw_priv->scan.in_progress, 1); + atomic_set(&hw_priv->recent_scan, 1); + xradio_pm_stay_awake(&hw_priv->pm_state, tmo * HZ / 1000); +#ifdef P2P_MULTIVIF + ret = wsm_scan(hw_priv, scan, priv->if_id ? 1 : 0); +#else + ret = wsm_scan(hw_priv, scan, priv->if_id); +#endif + if (unlikely(ret)) { + scan_printk(XRADIO_DBG_WARN, "%s,wsm_scan failed!\n", __func__); + atomic_set(&hw_priv->scan.in_progress, 0); + xradio_scan_restart_delayed(priv); + } else { + queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, + tmo * HZ / 1000); + } + return ret; +} + +#ifdef ROAM_OFFLOAD +static int xradio_sched_scan_start(struct xradio_vif *priv, struct wsm_scan *scan) +{ + int ret; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + ret = wsm_scan(hw_priv, scan, priv->if_id); + if (unlikely(ret)) { + atomic_set(&hw_priv->scan.in_progress, 0); + scan_printk(XRADIO_DBG_WARN,"%s,wsm_scan failed!\n", __func__); + } + return ret; +} +#endif /*ROAM_OFFLOAD*/ + +int xradio_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i; +#ifdef CONFIG_XRADIO_TESTMODE + int ret = 0; + u16 advance_scan_req_channel; +#endif + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == XRADIO_JOIN_STATUS_AP) { + scan_printk(XRADIO_DBG_WARN,"%s, can't scan in AP mode!\n", __func__); + return -EOPNOTSUPP; + } + + if (hw_priv->bh_error) { + scan_printk(XRADIO_DBG_NIY, "Ignoring scan bh error occur!\n"); + return -EBUSY; + } + +#ifdef HW_RESTART + if (hw_priv->hw_restart) { + scan_printk(XRADIO_DBG_NIY, "Ignoring scan in hw reset!\n"); + return -EBUSY; + } +#endif + + if (work_pending(&priv->offchannel_work) || + (hw_priv->roc_if_id != -1)) { + scan_printk(XRADIO_DBG_WARN, "Offchannel work pending, " + "ignoring scan work %d\n", hw_priv->roc_if_id); + return -EBUSY; + } + + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + scan_printk(XRADIO_DBG_NIY, "vif%d Scan request(%s-%dchs) for %d SSIDs.\n", + priv->if_id, (req->channels[0]->band==IEEE80211_BAND_2GHZ)?"2.4G":"5G", + req->n_channels, req->n_ssids); + + /*delay multiple ssids scan of vif0 for 3s when connnetting to a node*/ + if(hw_priv->connet_time[0] > 0 && req->n_ssids == 0 && priv->if_id == 0) { + long timeleft0 = hw_priv->connet_time[0] + SCAN_MAX_DELAY - jiffies; + if(jiffies >= hw_priv->connet_time[0] && timeleft0 > 0) { + scan_printk(XRADIO_DBG_NIY, "vif0 connetting, scan delay %ldms\n", + timeleft0*1000/HZ); + return -EBUSY; + } + hw_priv->connet_time[0] = 0; + } + + if (req->n_ssids > hw->wiphy->max_scan_ssids){ + scan_printk(XRADIO_DBG_ERROR, "%s: ssids is too much(%d)\n", + __func__, req->n_ssids); + return -EINVAL; + } + + frame.skb = ieee80211_probereq_get(hw, vif, NULL, 0, req->ie, req->ie_len); + if (!frame.skb) { + scan_printk(XRADIO_DBG_ERROR, "%s: ieee80211_probereq_get failed!\n", + __func__); + return -ENOMEM; + } + +#ifdef ROAM_OFFLOAD + if (priv->join_status != XRADIO_JOIN_STATUS_STA) { + if (req->channels[0]->band == IEEE80211_BAND_2GHZ) + hw_priv->num_scanchannels = 0; + else + hw_priv->num_scanchannels = hw_priv->num_2g_channels; + + for (i=0; i < req->n_channels; i++) { + hw_priv->scan_channels[hw_priv->num_scanchannels + i].number = \ + req->channels[i]->hw_value; + if (req->channels[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + hw_priv->scan_channels[hw_priv->num_scanchannels + i].minChannelTime = 50; + hw_priv->scan_channels[hw_priv->num_scanchannels + i].maxChannelTime = 110; + } + else { + hw_priv->scan_channels[hw_priv->num_scanchannels + i].minChannelTime = 10; + hw_priv->scan_channels[hw_priv->num_scanchannels + i].maxChannelTime = 40; + hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \ + XRADIO_SCAN_TYPE_ACTIVE; + } + hw_priv->scan_channels[hw_priv->num_scanchannels + i].txPowerLevel = \ + req->channels[i]->max_power; + if (req->channels[0]->band == IEEE80211_BAND_5GHZ) + hw_priv->scan_channels[hw_priv->num_scanchannels + i].number |= \ + XRADIO_SCAN_BAND_5G; + } + if (req->channels[0]->band == IEEE80211_BAND_2GHZ) + hw_priv->num_2g_channels = req->n_channels; + else + hw_priv->num_5g_channels = req->n_channels; + } + hw_priv->num_scanchannels = hw_priv->num_2g_channels + hw_priv->num_5g_channels; +#endif /*ROAM_OFFLOAD*/ + + /* will be unlocked in xradio_scan_work() */ + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + +#ifdef CONFIG_XRADIO_TESTMODE + /* Active Scan - Serving Channel Request Handling */ + advance_scan_req_channel = req->channels[0]->hw_value; + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_ACTIVE) && + (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == advance_scan_req_channel)) { + SYS_BUG(hw_priv->scan.req); + /* wsm_lock_tx(hw_priv); */ + wsm_vif_lock_tx(priv); + hw_priv->scan.if_id = priv->if_id; + /* Disable Power Save */ + if (priv->powersave_mode.pmMode & WSM_PSM_PS) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_ACTIVE; + wsm_set_pm(hw_priv, &pm, priv->if_id); + } + /* Disable Rx Beacon and Bssid filter */ + ret = xradio_disable_filtering(priv); + if (ret) + scan_printk(XRADIO_DBG_ERROR, "%s: Disable BSSID or Beacon filtering " + "failed: %d.\n", __func__, ret); + wsm_unlock_tx(hw_priv); + mutex_unlock(&hw_priv->conf_mutex); + /* Transmit Probe Request with Broadcast SSID */ + xradio_tx(hw, frame.skb); + /* Start Advance Scan Timer */ + xradio_advance_scan_start(hw_priv); + } else { +#endif /* CONFIG_XRADIO_TESTMODE */ + + if (frame.skb) { + int ret = 0; + if (priv->if_id == 0) + xradio_remove_wps_p2p_ie(&frame); +#ifdef P2P_MULTIVIF + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id ? 1 : 0); +#else + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); +#endif + if (ret) { + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); + dev_kfree_skb(frame.skb); + scan_printk(XRADIO_DBG_ERROR, "%s: wsm_set_template_frame failed: %d.\n", + __func__, ret); + return ret; + } + } + + wsm_vif_lock_tx(priv); + + SYS_BUG(hw_priv->scan.req); + hw_priv->scan.req = req; + hw_priv->scan.n_ssids = 0; + hw_priv->scan.status = 0; + hw_priv->scan.begin = &req->channels[0]; + hw_priv->scan.curr = hw_priv->scan.begin; + hw_priv->scan.end = &req->channels[req->n_channels]; + hw_priv->scan.output_power = hw_priv->output_power; + hw_priv->scan.if_id = priv->if_id; + /* TODO:COMBO: Populate BIT4 in scanflags to decide on which MAC + * address the SCAN request will be sent */ + + for (i = 0; i < req->n_ssids; ++i) { + struct wsm_ssid *dst = &hw_priv->scan.ssids[hw_priv->scan.n_ssids]; + SYS_BUG(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++hw_priv->scan.n_ssids; + } + + mutex_unlock(&hw_priv->conf_mutex); + + if (frame.skb) + dev_kfree_skb(frame.skb); + queue_work(hw_priv->workqueue, &hw_priv->scan.work); + +#ifdef CONFIG_XRADIO_TESTMODE + } +#endif + + return 0; +} + +#ifdef ROAM_OFFLOAD +int xradio_hw_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies) +{ + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + int i; + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + scan_printk(XRADIO_DBG_WARN, "Scheduled scan request-->.\n"); + if (!priv->vif) + return -EINVAL; + + /* Scan when P2P_GO corrupt firmware MiniAP mode */ + if (priv->join_status == XRADIO_JOIN_STATUS_AP) { + scan_printk(XRADIO_DBG_WARN,"%s, can't scan in AP mode!\n", __func__); + return -EOPNOTSUPP; + } + + scan_printk(XRADIO_DBG_WARN, "Scheduled scan: n_ssids %d, ssid[0].len = %d\n", + req->n_ssids, req->ssids[0].ssid_len); + if (req->n_ssids == 1 && !req->ssids[0].ssid_len) + req->n_ssids = 0; + + scan_printk(XRADIO_DBG_NIY, "[SCAN] Scan request for %d SSIDs.\n", + req->n_ssids); + + if (req->n_ssids > hw->wiphy->max_scan_ssids) [ + scan_printk(XRADIO_DBG_ERROR, "%s: ssids is too much(%d)\n", + __func__, req->n_ssids); + return -EINVAL; + } + + frame.skb = ieee80211_probereq_get(hw, priv->vif, NULL, 0, + ies->ie[0], ies->len[0]); + if (!frame.skb) { + scan_printk(XRADIO_DBG_ERROR, "%s: ieee80211_probereq_get failed!\n", + __func__); + return -ENOMEM; + } + + /* will be unlocked in xradio_scan_work() */ + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + if (frame.skb) { + int ret; + if (priv->if_id == 0) + xradio_remove_wps_p2p_ie(&frame); + ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id); + if (0 == ret) { + /* Host want to be the probe responder. */ + ret = wsm_set_probe_responder(priv, true); + } + if (ret) { + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); + dev_kfree_skb(frame.skb); + scan_printk(XRADIO_DBG_ERROR, "%s: wsm_set_probe_responder failed: %d.\n", + __func__, ret); + return ret; + } + } + + wsm_lock_tx(hw_priv); + SYS_BUG(hw_priv->scan.req); + hw_priv->scan.sched_req = req; + hw_priv->scan.n_ssids = 0; + hw_priv->scan.status = 0; + hw_priv->scan.begin = &req->channels[0]; + hw_priv->scan.curr = hw_priv->scan.begin; + hw_priv->scan.end = &req->channels[req->n_channels]; + hw_priv->scan.output_power = hw_priv->output_power; + + for (i = 0; i < req->n_ssids; ++i) { + u8 j; + struct wsm_ssid *dst = &hw_priv->scan.ssids[hw_priv->scan.n_ssids]; + SYS_BUG(req->ssids[i].ssid_len > sizeof(dst->ssid)); + memcpy(&dst->ssid[0], req->ssids[i].ssid, sizeof(dst->ssid)); + dst->length = req->ssids[i].ssid_len; + ++hw_priv->scan.n_ssids; + scan_printk(XRADIO_DBG_NIY, "SSID %d\n",i); + for(j=0; 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 + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + priv = __xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + + /*TODO: COMBO: introduce locking so vif is not removed in meanwhile */ + if (!priv) { + scan_printk(XRADIO_DBG_WARN, "interface removed, " + "ignoring scan work\n"); + return; + } + + if (priv->if_id) + scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1; + else + scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1; + + /* No need to set WSM_SCAN_FLAG_FORCE_BACKGROUND in BSS_LOSS work. + * yangfh 2015-11-11 18:45:02 */ + //xradio_for_each_vif(hw_priv, vif, i) { + // if (!vif) + // continue; + // if (vif->bss_loss_status > XRADIO_BSS_LOSS_NONE) + // scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND; + //} + + first_run = (hw_priv->scan.begin == hw_priv->scan.curr && + hw_priv->scan.begin != hw_priv->scan.end); + if (first_run) { + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) + xradio_join_timeout(&priv->join_timeout.work); + } + + mutex_lock(&hw_priv->conf_mutex); + if (first_run) { + +#ifdef CONFIG_XRADIO_TESTMODE + /* Passive Scan - Serving Channel Request Handling */ + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == advance_scan_req_channel)) { + /* If Advance Scan Request is for Serving Channel Device + * should be Active and Filtering Should be Disable */ + if (priv->powersave_mode.pmMode & WSM_PSM_PS) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_ACTIVE; + wsm_set_pm(hw_priv, &pm, priv->if_id); + } + /* Disable Rx Beacon and Bssid filter */ + ret = xradio_disable_filtering(priv); + if (ret) + scan_printk(XRADIO_DBG_ERROR, "%s: Disable BSSID or Beacon" + "filtering failed: %d.\n", __func__, ret); + } else if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + XRADIO_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == XRADIO_JOIN_STATUS_STA)) { + if (priv->join_status == XRADIO_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + xradio_set_pm(priv, &pm); + } + } else { +#endif + +#if 0 + if (priv->join_status == XRADIO_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) { + struct wsm_set_pm pm = priv->powersave_mode; + pm.pmMode = WSM_PSM_PS; + xradio_set_pm(priv, &pm); + } else +#endif + if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) { + /* FW bug: driver has to restart p2p-dev mode + * after scan */ + xradio_disable_listening(priv); + } +#ifdef CONFIG_XRADIO_TESTMODE + } +#endif + } + + if (!hw_priv->scan.req || (hw_priv->scan.curr == hw_priv->scan.end)) { + +#ifdef CONFIG_XRADIO_TESTMODE + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + XRADIO_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == advance_scan_req_channel)) { + /* WSM Lock should be held here for WSM APIs */ + wsm_vif_lock_tx(priv); + + /* wsm_lock_tx(priv); */ + /* Once Duration is Over, enable filtering + * and Revert Back Power Save */ + if (priv->powersave_mode.pmMode & WSM_PSM_PS) + wsm_set_pm(hw_priv, &priv->powersave_mode, priv->if_id); + xradio_update_filtering(priv); + } else if (!hw_priv->enable_advance_scan) { +#endif + if (hw_priv->scan.output_power != hw_priv->output_power) { + /* TODO:COMBO: Change when mac80211 implementation + * is available for output power also */ +#ifdef P2P_MULTIVIF + WARN_ON(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, + priv->if_id ? 1 : 0)); +#else + WARN_ON(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, + priv->if_id)); +#endif + } +#ifdef CONFIG_XRADIO_TESTMODE + } +#endif + +#if 0 + if (priv->join_status == XRADIO_JOIN_STATUS_STA && + !(priv->powersave_mode.pmMode & WSM_PSM_PS)) + xradio_set_pm(priv, &priv->powersave_mode); +#endif + + if (hw_priv->scan.status < 0) + scan_printk(XRADIO_DBG_ERROR, "Scan failed (%d).\n", hw_priv->scan.status); + else if (hw_priv->scan.req) + scan_printk(XRADIO_DBG_NIY, "Scan completed.\n"); + else + scan_printk(XRADIO_DBG_NIY, "Scan canceled.\n"); + + hw_priv->scan.req = NULL; + xradio_scan_restart_delayed(priv); +#ifdef CONFIG_XRADIO_TESTMODE + hw_priv->enable_advance_scan = false; +#endif /* CONFIG_XRADIO_TESTMODE */ + wsm_unlock_tx(hw_priv); + mutex_unlock(&hw_priv->conf_mutex); + ieee80211_scan_completed(hw_priv->hw, hw_priv->scan.status ? 1 : 0); + up(&hw_priv->scan.lock); + return; + + } else { + struct ieee80211_channel *first = *hw_priv->scan.curr; + for (it = hw_priv->scan.curr + 1, i = 1; + it != hw_priv->scan.end && i < WSM_SCAN_MAX_NUM_OF_CHANNELS; + ++it, ++i) { + if ((*it)->band != first->band) + break; + if (((*it)->flags ^ first->flags) & IEEE80211_CHAN_PASSIVE_SCAN) + break; + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + (*it)->max_power != first->max_power) + break; + } + scan.band = first->band; + + if (hw_priv->scan.req->no_cck) + scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; + else + scan.maxTransmitRate = WSM_TRANSMIT_RATE_1; + +#ifdef CONFIG_XRADIO_TESTMODE + if (hw_priv->enable_advance_scan) { + if (hw_priv->advanceScanElems.scanMode == XRADIO_SCAN_MEASUREMENT_PASSIVE) + scan.numOfProbeRequests = 0; + else + scan.numOfProbeRequests = 1; + } else { +#endif + /* TODO: Is it optimal? */ + scan.numOfProbeRequests = (first->flags & IEEE80211_CHAN_PASSIVE_SCAN) ? 0 : 2; +#ifdef CONFIG_XRADIO_TESTMODE + } +#endif /* CONFIG_XRADIO_TESTMODE */ + + scan.numOfSSIDs = hw_priv->scan.n_ssids; + scan.ssids = &hw_priv->scan.ssids[0]; + scan.numOfChannels = it - hw_priv->scan.curr; + /* TODO: Is it optimal? */ + scan.probeDelay = 100; + /* It is not stated in WSM specification, however + * FW team says that driver may not use FG scan + * when joined. */ + if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + } + scan.ch = xr_kzalloc(sizeof(struct wsm_scan_ch[it - hw_priv->scan.curr]), false); + if (!scan.ch) { + hw_priv->scan.status = -ENOMEM; + scan_printk(XRADIO_DBG_ERROR, "xr_kzalloc wsm_scan_ch failed.\n"); + goto fail; + } + maxChannelTime = (scan.numOfSSIDs * scan.numOfProbeRequests *ProbeRequestTime) + + ChannelRemainTime; + maxChannelTime = (maxChannelTime < 35) ? 35 : maxChannelTime; + for (i = 0; i < scan.numOfChannels; ++i) { + scan.ch[i].number = hw_priv->scan.curr[i]->hw_value; + +#ifdef CONFIG_XRADIO_TESTMODE + if (hw_priv->enable_advance_scan) { + scan.ch[i].minChannelTime = hw_priv->advanceScanElems.duration; + scan.ch[i].maxChannelTime = hw_priv->advanceScanElems.duration; + } else { +#endif + if (hw_priv->scan.curr[i]->flags & IEEE80211_CHAN_PASSIVE_SCAN) { + scan.ch[i].minChannelTime = 50; + scan.ch[i].maxChannelTime = 110; + } else { + scan.ch[i].minChannelTime = 15; + scan.ch[i].maxChannelTime = maxChannelTime; + } + +#ifdef CONFIG_XRADIO_TESTMODE + } +#endif + } + +#ifdef CONFIG_XRADIO_TESTMODE + if (!hw_priv->enable_advance_scan) { +#endif + if (!(first->flags & IEEE80211_CHAN_PASSIVE_SCAN) && + hw_priv->scan.output_power != first->max_power) { + hw_priv->scan.output_power = first->max_power; + /* TODO:COMBO: Change after mac80211 implementation + * complete */ +#ifdef P2P_MULTIVIF + WARN_ON(wsm_set_output_power(hw_priv, hw_priv->scan.output_power * 10, + priv->if_id ? 1 : 0)); +#else + WARN_ON(wsm_set_output_power(hw_priv, hw_priv->scan.output_power * 10, + priv->if_id)); +#endif + } +#ifdef CONFIG_XRADIO_TESTMODE + } +#endif + +#ifdef CONFIG_XRADIO_TESTMODE + if (hw_priv->enable_advance_scan && + (hw_priv->advanceScanElems.scanMode == + XRADIO_SCAN_MEASUREMENT_PASSIVE) && + (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (hw_priv->channel->hw_value == advance_scan_req_channel)) { + /* Start Advance Scan Timer */ + hw_priv->scan.status = xradio_advance_scan_start(hw_priv); + wsm_unlock_tx(hw_priv); + } else +#endif + down(&hw_priv->scan.status_lock); + hw_priv->scan.status = xradio_scan_start(priv, &scan); + + kfree(scan.ch); + if (WARN_ON(hw_priv->scan.status)) { + scan_printk(XRADIO_DBG_ERROR, "scan failed, status=%d.\n", + hw_priv->scan.status); + up(&hw_priv->scan.status_lock); + goto fail; + } + up(&hw_priv->scan.status_lock); + hw_priv->scan.curr = it; + } + mutex_unlock(&hw_priv->conf_mutex); + return; + +fail: + hw_priv->scan.curr = hw_priv->scan.end; + mutex_unlock(&hw_priv->conf_mutex); + queue_work(hw_priv->workqueue, &hw_priv->scan.work); + return; +} + +#ifdef ROAM_OFFLOAD +void xradio_sched_scan_work(struct work_struct *work) +{ + struct xradio_common *hw_priv = container_of(work, struct xradio_common, + scan.swork); + struct wsm_scan scan; + struct wsm_ssid scan_ssid; + int i; + struct xradio_vif *priv = NULL; + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + if (unlikely(!priv)) { + WARN_ON(1); + return; + } + + spin_unlock(&priv->vif_lock); + /* Firmware gets crazy if scan request is sent + * when STA is joined but not yet associated. + * Force unjoin in this case. */ + if (cancel_delayed_work_sync(&priv->join_timeout) > 0) { + xradio_join_timeout(&priv->join_timeout.work); + } + mutex_lock(&hw_priv->conf_mutex); + hw_priv->auto_scanning = 1; + scan.band = 0; + + if (priv->join_status == XRADIO_JOIN_STATUS_STA) + scan.scanType = 3; /* auto background */ + else + scan.scanType = 2; /* auto foreground */ + + scan.scanFlags = 0x01; /* bit 0 set => forced background scan */ + scan.maxTransmitRate = WSM_TRANSMIT_RATE_6; + scan.autoScanInterval = (0xba << 24)|(30 * 1024); /* 30 seconds, -70 rssi */ + scan.numOfProbeRequests = 1; + //scan.numOfChannels = 11; + scan.numOfChannels = hw_priv->num_scanchannels; + scan.numOfSSIDs = 1; + scan.probeDelay = 100; + scan_ssid.length = priv->ssid_length; + memcpy(scan_ssid.ssid, priv->ssid, priv->ssid_length); + scan.ssids = &scan_ssid; + + scan.ch = xr_kzalloc(sizeof(struct wsm_scan_ch[scan.numOfChannels]), false); + if (!scan.ch) { + scan_printk(XRADIO_DBG_ERROR, "xr_kzalloc wsm_scan_ch failed.\n"); + hw_priv->scan.status = -ENOMEM; + goto fail; + } + + for (i = 0; i < scan.numOfChannels; i++) { + scan.ch[i].number = hw_priv->scan_channels[i].number; + scan.ch[i].minChannelTime = hw_priv->scan_channels[i].minChannelTime; + scan.ch[i].maxChannelTime = hw_priv->scan_channels[i].maxChannelTime; + scan.ch[i].txPowerLevel = hw_priv->scan_channels[i].txPowerLevel; + } + +#if 0 + for (i = 1; i <= scan.numOfChannels; i++) { + scan.ch[i-1].number = i; + scan.ch[i-1].minChannelTime = 10; + scan.ch[i-1].maxChannelTime = 40; + } +#endif + + hw_priv->scan.status = xradio_sched_scan_start(priv, &scan); + kfree(scan.ch); + if (hw_priv->scan.status) { + scan_printk(XRADIO_DBG_ERROR, "scan failed, status=%d.\n", + hw_priv->scan.status); + goto fail; + } + mutex_unlock(&hw_priv->conf_mutex); + return; + +fail: + mutex_unlock(&hw_priv->conf_mutex); + queue_work(hw_priv->workqueue, &hw_priv->scan.swork); + return; +} + +void xradio_hw_sched_scan_stop(struct xradio_common *hw_priv) +{ + struct xradio_vif *priv = NULL; + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + priv = xrwl_hwpriv_to_vifpriv(hw_priv,hw_priv->scan.if_id); + if (unlikely(!priv)) + return; + + spin_unlock(&priv->vif_lock); + wsm_stop_scan(hw_priv, priv->if_id); + + return; +} +#endif /*ROAM_OFFLOAD*/ + + +static void xradio_scan_restart_delayed(struct xradio_vif *priv) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (priv->delayed_link_loss) { + int tmo = hw_priv->scan.direct_probe ? 0 : priv->cqm_beacon_loss_count; + + priv->delayed_link_loss = 0; + /* Restart beacon loss timer and requeue + BSS loss work. */ + scan_printk(XRADIO_DBG_WARN, "[CQM] Requeue BSS loss in %d " + "beacons.\n", tmo); + cancel_delayed_work_sync(&priv->bss_loss_work); + queue_delayed_work(hw_priv->workqueue, &priv->bss_loss_work, + tmo * HZ / 10); + + } + + /* FW bug: driver has to restart p2p-dev mode after scan. */ + if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) { + /*xradio_enable_listening(priv);*/ + WARN_ON(1); + xradio_update_filtering(priv); + scan_printk(XRADIO_DBG_WARN, "driver has to restart " + "p2p-dev mode after scan"); + } + + if (atomic_xchg(&priv->delayed_unjoin, 0)) { + if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } +} + +static void xradio_scan_complete(struct xradio_common *hw_priv, int if_id) +{ + struct xradio_vif *priv; + atomic_xchg(&hw_priv->recent_scan, 0); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (hw_priv->scan.direct_probe) { + mutex_lock(&hw_priv->conf_mutex); + priv = __xrwl_hwpriv_to_vifpriv(hw_priv, if_id); + if (priv) { + scan_printk(XRADIO_DBG_MSG, "Direct probe complete.\n"); + xradio_scan_restart_delayed(priv); + } else { + scan_printk(XRADIO_DBG_MSG, "Direct probe complete without interface!\n"); + } + mutex_unlock(&hw_priv->conf_mutex); + hw_priv->scan.direct_probe = 0; + up(&hw_priv->scan.lock); + wsm_unlock_tx(hw_priv); + } else { + xradio_scan_work(&hw_priv->scan.work); + } +} + +void xradio_scan_complete_cb(struct xradio_common *hw_priv, + struct wsm_scan_complete *arg) +{ + struct xradio_vif *priv = xrwl_hwpriv_to_vifpriv(hw_priv, + hw_priv->scan.if_id); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (unlikely(!priv)) + return; + +#ifdef ROAM_OFFLOAD + if (hw_priv->auto_scanning) + queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0); +#endif /*ROAM_OFFLOAD*/ + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + spin_unlock(&priv->vif_lock); + scan_printk(XRADIO_DBG_WARN, "%s: priv->mode UNSPECIFIED.\n", __func__); + return; + } + spin_unlock(&priv->vif_lock); + + /* + if(hw_priv->scan.status == -ETIMEDOUT) + scan_printk(XRADIO_DBG_WARN, "Scan timeout already occured. " + "Don't cancel work"); + if ((hw_priv->scan.status != -ETIMEDOUT) && + (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0)) { + hw_priv->scan.status = 1; + queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0); + } + */ // should not involve status as a condition + + if (cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0) { + down(&hw_priv->scan.status_lock); + hw_priv->scan.status = 1; + up(&hw_priv->scan.status_lock); + queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout, 0); + } +} + +void xradio_scan_timeout(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, scan.timeout.work); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (likely(atomic_xchg(&hw_priv->scan.in_progress, 0))) { + if (hw_priv->scan.status > 0) + hw_priv->scan.status = 0; + else if (!hw_priv->scan.status) { + scan_printk(XRADIO_DBG_WARN, "Timeout waiting for scan " + "complete notification.\n"); + hw_priv->scan.status = -ETIMEDOUT; + hw_priv->scan.curr = hw_priv->scan.end; + WARN_ON(wsm_stop_scan(hw_priv, hw_priv->scan.if_id ? 1 : 0)); + } + xradio_scan_complete(hw_priv, hw_priv->scan.if_id); +#ifdef ROAM_OFFLOAD + } else if (hw_priv->auto_scanning) { + hw_priv->auto_scanning = 0; + ieee80211_sched_scan_results(hw_priv->hw); +#endif /*ROAM_OFFLOAD*/ + } +} + +#ifdef CONFIG_XRADIO_TESTMODE +void xradio_advance_scan_timeout(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, advance_scan_timeout.work); + + struct xradio_vif *priv = xrwl_hwpriv_to_vifpriv(hw_priv, + hw_priv->scan.if_id); + scan_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (WARN_ON(!priv)) + return; + spin_unlock(&priv->vif_lock); + + hw_priv->scan.status = 0; + if (hw_priv->advanceScanElems.scanMode == + XRADIO_SCAN_MEASUREMENT_PASSIVE) { + /* Passive Scan on Serving Channel + * Timer Expire */ + xradio_scan_complete(hw_priv, hw_priv->scan.if_id); + } else { + /* Active Scan on Serving Channel + * Timer Expire */ + mutex_lock(&hw_priv->conf_mutex); + //wsm_lock_tx(priv); + wsm_vif_lock_tx(priv); + /* Once Duration is Over, enable filtering + * and Revert Back Power Save */ + if ((priv->powersave_mode.pmMode & WSM_PSM_PS)) + wsm_set_pm(hw_priv, &priv->powersave_mode, priv->if_id); + hw_priv->scan.req = NULL; + xradio_update_filtering(priv); + hw_priv->enable_advance_scan = false; + wsm_unlock_tx(hw_priv); + mutex_unlock(&hw_priv->conf_mutex); + ieee80211_scan_completed(hw_priv->hw, hw_priv->scan.status ? true : false); + up(&hw_priv->scan.lock); + } +} +#endif + +void xradio_probe_work(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, scan.probe_work.work); + struct xradio_vif *priv; + u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); + struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; + const struct xradio_txpriv *txpriv; + struct wsm_tx *wsm; + struct wsm_template_frame frame = { + .frame_type = WSM_FRAME_TYPE_PROBE_REQUEST, + }; + struct wsm_ssid ssids[1] = {{ + .length = 0, + } }; + struct wsm_scan_ch ch[1] = {{ + .minChannelTime = 0, + .maxChannelTime = 10, + } }; + struct wsm_scan scan = { + .scanType = WSM_SCAN_TYPE_FOREGROUND, + .numOfProbeRequests = 1, + .probeDelay = 0, + .numOfChannels = 1, + .ssids = ssids, + .ch = ch, + }; + u8 *ies; + size_t ies_len; + int ret = 1; + scan_printk(XRADIO_DBG_MSG, "%s:Direct probe.\n", __func__); + + SYS_BUG(queueId >= 4); + SYS_BUG(!hw_priv->channel); + + mutex_lock(&hw_priv->conf_mutex); + if (unlikely(down_trylock(&hw_priv->scan.lock))) { + /* Scan is already in progress. Requeue self. */ + schedule(); + queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.probe_work, + HZ / 10); + mutex_unlock(&hw_priv->conf_mutex); + return; + } + + if (xradio_queue_get_skb(queue, hw_priv->pending_frame_id, &frame.skb, &txpriv)) { + up(&hw_priv->scan.lock); + mutex_unlock(&hw_priv->conf_mutex); + wsm_unlock_tx(hw_priv); + scan_printk(XRADIO_DBG_ERROR, "%s:xradio_queue_get_skb error!\n", __func__); + return; + } + priv = __xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + if (!priv) { + up(&hw_priv->scan.lock); + mutex_unlock(&hw_priv->conf_mutex); + scan_printk(XRADIO_DBG_ERROR, "%s:priv error!\n", __func__); + return; + } + wsm = (struct wsm_tx *)frame.skb->data; + scan.maxTransmitRate = wsm->maxTxRate; + scan.band = (hw_priv->channel->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G; + if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + scan.scanType = WSM_SCAN_TYPE_BACKGROUND; + scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND; + if (priv->if_id) + scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1; + else + scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1; + } + + /* No need to set WSM_SCAN_FLAG_FORCE_BACKGROUND in BSS_LOSS work. + * yangfh 2015-11-11 18:45:02 */ + //xradio_for_each_vif(hw_priv, vif, i) { + // if (!vif) + // continue; + // if (vif->bss_loss_status > XRADIO_BSS_LOSS_NONE) + // scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND; + //} + + ch[0].number = hw_priv->channel->hw_value; + skb_pull(frame.skb, txpriv->offset); + ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)]; + ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr); + + if (ies_len) { + u8 *ssidie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len); + if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) { + u8 *nextie = &ssidie[2 + ssidie[1]]; + /* Remove SSID from the IE list. It has to be provided + * as a separate argument in xradio_scan_start call */ + + /* Store SSID localy */ + ssids[0].length = ssidie[1]; + memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length); + scan.numOfSSIDs = 1; + + /* Remove SSID from IE list */ + ssidie[1] = 0; + memmove(&ssidie[2], nextie, &ies[ies_len] - nextie); + skb_trim(frame.skb, frame.skb->len - ssids[0].length); + } + } + + if (priv->if_id == 0) + xradio_remove_wps_p2p_ie(&frame); + + /* FW bug: driver has to restart p2p-dev mode after scan */ + if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) { + WARN_ON(1); + /*xradio_disable_listening(priv);*/ + } + ret = WARN_ON(wsm_set_template_frame(hw_priv, &frame, + priv->if_id)); + + hw_priv->scan.direct_probe = 1; + hw_priv->scan.if_id = priv->if_id; + if (!ret) { + wsm_flush_tx(hw_priv); + ret = WARN_ON(xradio_scan_start(priv, &scan)); + } + mutex_unlock(&hw_priv->conf_mutex); + + skb_push(frame.skb, txpriv->offset); + if (!ret) + IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK; +#ifdef CONFIG_XRADIO_TESTMODE + SYS_BUG(xradio_queue_remove(hw_priv, queue, hw_priv->pending_frame_id)); +#else + SYS_BUG(xradio_queue_remove(queue, hw_priv->pending_frame_id)); +#endif + + if (ret) { + hw_priv->scan.direct_probe = 0; + up(&hw_priv->scan.lock); + wsm_unlock_tx(hw_priv); + } + + return; +} diff --git a/drivers/net/wireless/xradio/scan.h b/drivers/net/wireless/xradio/scan.h new file mode 100644 index 0000000..191b4b8 --- /dev/null +++ b/drivers/net/wireless/xradio/scan.h @@ -0,0 +1,75 @@ +/* + * Scan interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + + +#ifndef SCAN_H_INCLUDED +#define SCAN_H_INCLUDED + +#include +#include "wsm.h" + +/* external */ struct sk_buff; +/* external */ struct cfg80211_scan_request; +/* external */ struct ieee80211_channel; +/* external */ struct ieee80211_hw; +/* external */ struct work_struct; + +#define SCAN_MAX_DELAY (3*HZ) //3s, add by yangfh for connect + +struct xradio_scan { + struct semaphore lock; + struct work_struct work; +#ifdef ROAM_OFFLOAD + struct work_struct swork; /* scheduled scan work */ + struct cfg80211_sched_scan_request *sched_req; +#endif /*ROAM_OFFLOAD*/ + struct delayed_work timeout; + struct cfg80211_scan_request *req; + struct ieee80211_channel **begin; + struct ieee80211_channel **curr; + struct ieee80211_channel **end; + struct wsm_ssid ssids[WSM_SCAN_MAX_NUM_OF_SSIDS]; + int output_power; + int n_ssids; + //add by liwei, for h64 ping WS550 BUG + struct semaphore status_lock; + int status; + atomic_t in_progress; + /* Direct probe requests workaround */ + struct delayed_work probe_work; + int direct_probe; + u8 if_id; +}; + +int xradio_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); +#ifdef ROAM_OFFLOAD +int xradio_hw_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_sched_scan_ies *ies); +void xradio_hw_sched_scan_stop(struct xradio_common *priv); +void xradio_sched_scan_work(struct work_struct *work); +#endif /*ROAM_OFFLOAD*/ +void xradio_scan_work(struct work_struct *work); +void xradio_scan_timeout(struct work_struct *work); +void xradio_scan_complete_cb(struct xradio_common *priv, + struct wsm_scan_complete *arg); + +/* ******************************************************************** */ +/* Raw probe requests TX workaround */ +void xradio_probe_work(struct work_struct *work); +#ifdef CONFIG_XRADIO_TESTMODE +/* Advance Scan Timer */ +void xradio_advance_scan_timeout(struct work_struct *work); +#endif + +#endif diff --git a/drivers/net/wireless/xradio/sdio.c b/drivers/net/wireless/xradio/sdio.c new file mode 100644 index 0000000..e5a95b2 --- /dev/null +++ b/drivers/net/wireless/xradio/sdio.c @@ -0,0 +1,359 @@ +/* + * SDIO driver for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "platform.h" +#include "xradio.h" +#include "sbus.h" + +/* sdio vendor id and device id*/ +#define SDIO_VENDOR_ID_XRADIO 0x0020 +#define SDIO_DEVICE_ID_XRADIO 0x2281 +static const struct sdio_device_id xradio_sdio_ids[] = { + { SDIO_DEVICE(SDIO_VENDOR_ID_XRADIO, SDIO_DEVICE_ID_XRADIO) }, + //{ SDIO_DEVICE(SDIO_ANY_ID, SDIO_ANY_ID) }, + { /* end: all zeroes */ }, +}; + +/* sbus_ops implemetation */ +static int sdio_data_read(struct sbus_priv *self, unsigned int addr, + void *dst, int count) +{ + return sdio_memcpy_fromio(self->func, dst, addr, count); +} + +static int sdio_data_write(struct sbus_priv *self, unsigned int addr, + const void *src, int count) +{ + return sdio_memcpy_toio(self->func, addr, (void *)src, count); +} + +static void sdio_lock(struct sbus_priv *self) +{ + sdio_claim_host(self->func); +} + +static void sdio_unlock(struct sbus_priv *self) +{ + sdio_release_host(self->func); +} + +static size_t sdio_align_len(struct sbus_priv *self, size_t size) +{ + return sdio_align_size(self->func, size); +} + +static int sdio_set_blk_size(struct sbus_priv *self, size_t size) +{ + return sdio_set_block_size(self->func, size); +} + +#ifndef CONFIG_XRADIO_USE_GPIO_IRQ +static void sdio_irq_handler(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + unsigned long flags; + sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__); + + SYS_BUG(!self); + spin_lock_irqsave(&self->lock, flags); + if (self->irq_handler) + self->irq_handler(self->irq_priv); + spin_unlock_irqrestore(&self->lock, flags); +} +#endif + +static int sdio_irq_subscribe(struct sbus_priv *self, + sbus_irq_handler handler, + void *priv) +{ + int ret = 0; + unsigned long flags; + + + if (!handler) + return -EINVAL; + sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__); + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = priv; + self->irq_handler = handler; + spin_unlock_irqrestore(&self->lock, flags); + + sdio_claim_host(self->func); +#ifndef CONFIG_XRADIO_USE_GPIO_IRQ + ret = sdio_claim_irq(self->func, sdio_irq_handler); +#else + ret = xradio_request_gpio_irq(&(self->func->dev), self); + if (!ret) { + /* Hack to access Fuction-0 */ + u8 cccr; + int func_num = self->func->num; + self->func->num = 0; + cccr = sdio_readb(self->func, SDIO_CCCR_IENx, &ret); + cccr |= BIT(0); /* Master interrupt enable ... */ + cccr |= BIT(func_num); /* ... for our function */ + sdio_writeb(self->func, cccr, SDIO_CCCR_IENx, &ret); + if (ret) { + xradio_free_gpio_irq(&(self->func->dev), self); + if (MCI_CHECK_READY(self->func->card->host, 1000) != 0) + sbus_printk(XRADIO_DBG_ERROR, "%s:MCI_CHECK_READY timeout\n", __func__); + } + /* Restore the WLAN function number */ + self->func->num = func_num; + } +#endif + sdio_release_host(self->func); + + return ret; +} + +static int sdio_irq_unsubscribe(struct sbus_priv *self) +{ + int ret = 0; + unsigned long flags; + + sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__); + + if (!self->irq_handler) { + sbus_printk(XRADIO_DBG_ERROR, "%s:irq_handler is NULL!\n", __FUNCTION__); + return 0; + } + +#ifndef CONFIG_XRADIO_USE_GPIO_IRQ + sdio_claim_host(self->func); + ret = sdio_release_irq(self->func); + sdio_release_host(self->func); +#else + xradio_free_gpio_irq(&(self->func->dev), self); +#endif //CONFIG_XRADIO_USE_GPIO_IRQ + + spin_lock_irqsave(&self->lock, flags); + self->irq_priv = NULL; + self->irq_handler = NULL; + spin_unlock_irqrestore(&self->lock, flags); + + return ret; +} + +static int sdio_pm(struct sbus_priv *self, bool suspend) +{ + int ret = 0; + if (suspend) { + /* Notify SDIO that XRADIO will remain powered during suspend */ + ret = sdio_set_host_pm_flags(self->func, MMC_PM_KEEP_POWER); + if (ret) + sbus_printk(XRADIO_DBG_ERROR, + "Error setting SDIO pm flags: %i\n", ret); + } + + return ret; +} + +static int sdio_reset(struct sbus_priv *self) +{ + return 0; +} + +static struct sbus_ops sdio_sbus_ops = { + .sbus_data_read = sdio_data_read, + .sbus_data_write = sdio_data_write, + .lock = sdio_lock, + .unlock = sdio_unlock, + .align_size = sdio_align_len, + .set_block_size = sdio_set_blk_size, + .irq_subscribe = sdio_irq_subscribe, + .irq_unsubscribe = sdio_irq_unsubscribe, + .power_mgmt = sdio_pm, + .reset = sdio_reset, +}; +static struct sbus_priv sdio_self; + +//for sdio debug 2015-5-26 11:01:21 +#if (defined(CONFIG_XRADIO_DEBUGFS)) +u32 dbg_sdio_clk = 0; +static int sdio_set_clk(struct sdio_func *func, u32 clk) +{ + if (func) { + if (func->card->host->ops->set_ios && clk >= 1000000) { //set min to 1M + sdio_claim_host(func); + func->card->host->ios.clock = (clk < 50000000) ? clk : 50000000; + func->card->host->ops->set_ios(func->card->host, &func->card->host->ios); + sdio_release_host(func); + sbus_printk(XRADIO_DBG_ALWY, "%s:change mmc clk=%d\n", __func__, + func->card->host->ios.clock); + } else { + sbus_printk(XRADIO_DBG_ALWY, "%s:fail change mmc clk=%d\n", __func__, clk); + } + } + return 0; + sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__); +} +#endif + +/* Probe Function to be called by SDIO stack when device is discovered */ +static int sdio_probe(struct sdio_func *func, + const struct sdio_device_id *id) +{ + sbus_printk(XRADIO_DBG_ALWY, "XRadio Device:sdio clk=%d\n", + func->card->host->ios.clock); + sbus_printk(XRADIO_DBG_NIY, "sdio func->class=%x\n", func->class); + sbus_printk(XRADIO_DBG_NIY, "sdio_vendor: 0x%04x\n", func->vendor); + sbus_printk(XRADIO_DBG_NIY, "sdio_device: 0x%04x\n", func->device); + sbus_printk(XRADIO_DBG_NIY, "Function#: 0x%04x\n", func->num); + +#if (defined(CONFIG_XRADIO_DEBUGFS)) + if (dbg_sdio_clk) + sdio_set_clk(func, dbg_sdio_clk); +#endif + +#if 0 //for odly and sdly debug. +{ + u32 sdio_param = 0; + sdio_param = readl(__io_address(0x01c20088)); + sdio_param &= ~(0xf<<8); + sdio_param |= 3<<8; + sdio_param &= ~(0xf<<20); + sdio_param |= s_dly<<20; + writel(sdio_param, __io_address(0x01c20088)); + sbus_printk(XRADIO_DBG_ALWY, "%s: 0x01c20088=0x%08x\n", __func__, sdio_param); +} +#endif + + sdio_self.func = func; + sdio_self.func->card->quirks |= MMC_QUIRK_BROKEN_BYTE_MODE_512; + sdio_set_drvdata(func, &sdio_self); + sdio_claim_host(func); + sdio_enable_func(func); + sdio_release_host(func); + + sdio_self.load_state = SDIO_LOAD; + wake_up(&sdio_self.init_wq); + + return 0; +} +/* Disconnect Function to be called by SDIO stack when + * device is disconnected */ +static void sdio_remove(struct sdio_func *func) +{ + struct sbus_priv *self = sdio_get_drvdata(func); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + sdio_set_drvdata(func, NULL); + if (self) { + self->func = NULL; + } +} + +static int sdio_suspend(struct device *dev) +{ + int ret = 0; + /* + struct sdio_func *func = dev_to_sdio_func(dev); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + if (ret) + sbus_printk(XRADIO_DBG_ERROR, "set MMC_PM_KEEP_POWER error\n"); + */ + return ret; +} + +static int sdio_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops sdio_pm_ops = { + .suspend = sdio_suspend, + .resume = sdio_resume, +}; + +static struct sdio_driver sdio_driver = { + .name = "xradio_wlan", + .id_table = xradio_sdio_ids, + .probe = sdio_probe, + .remove = sdio_remove, + .drv = { + .pm = &sdio_pm_ops, + } +}; + +/* Init Module function -> Called by insmod */ +struct device * sbus_sdio_init(struct sbus_ops **sdio_ops, + struct sbus_priv **sdio_priv) +{ + int ret = 0; + struct device * sdio_dev = NULL; + sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__); + + //initialize sbus_priv. + if (sdio_self.load_state == SDIO_UNLOAD) { + spin_lock_init(&sdio_self.lock); + init_waitqueue_head(&sdio_self.init_wq); + + //setup sdio driver. + ret = sdio_register_driver(&sdio_driver); + if (ret) { + sbus_printk(XRADIO_DBG_ERROR,"sdio_register_driver failed!\n"); + return NULL; + } + + //module power up. + xradio_wlan_power(1); + //detect sdio card. + xradio_sdio_detect(1); + if (wait_event_interruptible_timeout(sdio_self.init_wq, + sdio_self.load_state == SDIO_LOAD, 2*HZ) <= 0) { + sdio_unregister_driver(&sdio_driver); + sdio_self.load_state = SDIO_UNLOAD; + + xradio_wlan_power(0); //power down. + xradio_sdio_detect(0); + sbus_printk(XRADIO_DBG_ERROR,"sdio probe timeout!\n"); + return NULL; + } + } + + //register sbus. + sdio_dev = &(sdio_self.func->dev); + *sdio_ops = &sdio_sbus_ops; + *sdio_priv = &sdio_self; + + return sdio_dev; +} + +/* SDIO Driver Unloading */ +void sbus_sdio_deinit() +{ + sbus_printk(XRADIO_DBG_TRC, "%s\n", __FUNCTION__); + if (sdio_self.load_state != SDIO_UNLOAD) { + sdio_unregister_driver(&sdio_driver); + memset(&sdio_self, 0, sizeof(sdio_self)); + sdio_self.load_state = SDIO_UNLOAD; + + xradio_wlan_power(0); //power down. + xradio_sdio_detect(0); + mdelay(10); + } +} diff --git a/drivers/net/wireless/xradio/sta.c b/drivers/net/wireless/xradio/sta.c new file mode 100644 index 0000000..8c86564 --- /dev/null +++ b/drivers/net/wireless/xradio/sta.c @@ -0,0 +1,3442 @@ +/* + * STA APIs for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "xradio.h" +#include "sta.h" +#include "ap.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*/ + +#ifdef IPV6_FILTERING +#define MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE 144 +#endif /*IPV6_FILTERING*/ + +static inline void __xradio_free_event_queue(struct list_head *list) +{ + while (!list_empty(list)) { + struct xradio_wsm_event *event = + list_first_entry(list, struct xradio_wsm_event,link); + list_del(&event->link); + kfree(event); + } +} + +#ifdef CONFIG_XRADIO_TESTMODE +/* User priority to WSM queue mapping */ +const int xradio_priority_to_queueId[8] = { + WSM_QUEUE_BEST_EFFORT, + WSM_QUEUE_BACKGROUND, + WSM_QUEUE_BACKGROUND, + WSM_QUEUE_BEST_EFFORT, + WSM_QUEUE_VIDEO, + WSM_QUEUE_VIDEO, + WSM_QUEUE_VOICE, + WSM_QUEUE_VOICE +}; +#endif /*CONFIG_XRADIO_TESTMODE*/ +static inline void __xradio_bf_configure(struct xradio_vif *priv) +{ + priv->bf_table.numOfIEs = __cpu_to_le32(3); + priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC; + priv->bf_table.entry[0].actionFlags = + WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + + priv->bf_table.entry[0].oui[0] = 0x50; + priv->bf_table.entry[0].oui[1] = 0x6F; + priv->bf_table.entry[0].oui[2] = 0x9A; + + priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO; + priv->bf_table.entry[1].actionFlags = + WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + + priv->bf_table.entry[2].ieId = WLAN_EID_HT_INFORMATION; + priv->bf_table.entry[2].actionFlags = + WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED; + + priv->bf_control.enabled = WSM_BEACON_FILTER_ENABLE; +} + +/* ******************************************************************** */ +/* STA API */ + +int xradio_start(struct ieee80211_hw *dev) +{ + struct xradio_common *hw_priv = dev->priv; + int ret = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->driver_ready, 3*HZ) <= 0) { + sta_printk(XRADIO_DBG_ERROR,"%s driver is not ready!\n", __func__); + return -ETIMEDOUT; + } + + mutex_lock(&hw_priv->conf_mutex); + +#ifdef CONFIG_XRADIO_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + memset(&hw_priv->tsm_stats, 0, sizeof(struct xr_tsm_stats)); + memset(&hw_priv->tsm_info, 0, sizeof(struct xradio_tsm_info)); + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_XRADIO_TESTMODE*/ + memcpy(hw_priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN); + hw_priv->softled_state = 0; + + ret = xradio_setup_mac(hw_priv); + if (SYS_WARN(ret)) { + sta_printk(XRADIO_DBG_ERROR,"%s, xradio_setup_mac failed(%d)\n", + __func__, ret); + goto out; + } + +out: + mutex_unlock(&hw_priv->conf_mutex); + return ret; +} + +void xradio_stop(struct ieee80211_hw *dev) +{ + struct xradio_common *hw_priv = dev->priv; + struct xradio_vif *priv = NULL; + LIST_HEAD(list); + int i; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + wsm_lock_tx(hw_priv); + while (down_trylock(&hw_priv->scan.lock)) { + /* Scan is in progress. Force it to stop. */ + hw_priv->scan.req = NULL; + schedule(); + } + up(&hw_priv->scan.lock); + + cancel_delayed_work_sync(&hw_priv->scan.probe_work); + cancel_delayed_work_sync(&hw_priv->scan.timeout); +#ifdef CONFIG_XRADIO_TESTMODE + cancel_delayed_work_sync(&hw_priv->advance_scan_timeout); +#endif + flush_workqueue(hw_priv->workqueue); + del_timer_sync(&hw_priv->ba_timer); + + mutex_lock(&hw_priv->conf_mutex); + + hw_priv->softled_state = 0; + /* xradio_set_leds(hw_priv); */ + + spin_lock(&hw_priv->event_queue_lock); + list_splice_init(&hw_priv->event_queue, &list); + spin_unlock(&hw_priv->event_queue_lock); + __xradio_free_event_queue(&list); + + for (i = 0; i < 4; i++) + xradio_queue_clear(&hw_priv->tx_queue[i], XRWL_ALL_IFS); + + /* HACK! */ + if (atomic_xchg(&hw_priv->tx_lock, 1) != 1) + sta_printk(XRADIO_DBG_WARN, "TX is force-unlocked due to stop request.\n"); + + xradio_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + priv->mode = NL80211_IFTYPE_UNSPECIFIED; + priv->listening = false; + priv->delayed_link_loss = 0; + priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + del_timer_sync(&priv->mcast_timeout); + } + + wsm_unlock_tx(hw_priv); + + mutex_unlock(&hw_priv->conf_mutex); +} + +int xradio_add_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + int ret; + struct xradio_common *hw_priv = dev->priv; + struct xradio_vif *priv; + struct xradio_vif **drv_priv = (void *)vif->drv_priv; +#ifndef P2P_MULTIVIF + int i; + if (atomic_read(&hw_priv->num_vifs) >= XRWL_MAX_VIFS) + return -EOPNOTSUPP; +#endif + + if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done, + hw_priv->driver_ready, 3*HZ) <= 0) { + sta_printk(XRADIO_DBG_ERROR,"%s driver is not ready!\n", __func__); + return -ETIMEDOUT; + } + + /* fix the problem that when connected,then deauth */ + vif->driver_flags = vif->driver_flags | IEEE80211_VIF_BEACON_FILTER; + priv = xrwl_get_vif_from_ieee80211(vif); + atomic_set(&priv->enabled, 0); + + *drv_priv = priv; + /* __le32 auto_calibration_mode = __cpu_to_le32(1); */ + + mutex_lock(&hw_priv->conf_mutex); + + priv->mode = vif->type; + + spin_lock(&hw_priv->vif_list_lock); + if (atomic_read(&hw_priv->num_vifs) < XRWL_MAX_VIFS) { +#ifdef P2P_MULTIVIF + if (!memcmp(vif->addr, hw_priv->addresses[0].addr, ETH_ALEN)) { + priv->if_id = 0; + } else if (!memcmp(vif->addr, hw_priv->addresses[1].addr, + ETH_ALEN)) { + priv->if_id = 2; + } else if (!memcmp(vif->addr, hw_priv->addresses[2].addr, + ETH_ALEN)) { + priv->if_id = 1; + } + sta_printk(XRADIO_DBG_MSG, "%s: if_id %d mac %pM\n", + __func__, priv->if_id, vif->addr); +#else + for (i = 0; i < XRWL_MAX_VIFS; i++) + if (!memcmp(vif->addr, hw_priv->addresses[i].addr, ETH_ALEN)) + break; + if (i == XRWL_MAX_VIFS) { + spin_unlock(&hw_priv->vif_list_lock); + mutex_unlock(&hw_priv->conf_mutex); + return -EINVAL; + } + priv->if_id = i; +#endif + hw_priv->if_id_slot |= BIT(priv->if_id); + priv->hw_priv = hw_priv; + priv->hw = dev; + priv->vif = vif; + hw_priv->vif_list[priv->if_id] = vif; + atomic_inc(&hw_priv->num_vifs); + } else { + spin_unlock(&hw_priv->vif_list_lock); + mutex_unlock(&hw_priv->conf_mutex); + return -EOPNOTSUPP; + } + spin_unlock(&hw_priv->vif_list_lock); + /* TODO:COMBO :Check if MAC address matches the one expected by FW */ + memcpy(hw_priv->mac_addr, vif->addr, ETH_ALEN); + + /* Enable auto-calibration */ + /* Exception in subsequent channel switch; disabled. + SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE, + &auto_calibration_mode, sizeof(auto_calibration_mode))); + */ + sta_printk(XRADIO_DBG_MSG, "Interface ID:%d of type:%d added\n", + priv->if_id, priv->mode); + mutex_unlock(&hw_priv->conf_mutex); + + xradio_vif_setup(priv); + + ret = SYS_WARN(xradio_setup_mac_pvif(priv)); + + return ret; +} + +void xradio_remove_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif) +{ + struct xradio_common *hw_priv = dev->priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct wsm_reset reset = { + .reset_statistics = true, + }; + int i; + bool is_htcapie = false; + struct xradio_vif *tmp_priv; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + sta_printk(XRADIO_DBG_WARN, "!!! %s: vif_id=%d\n", __func__, priv->if_id); + atomic_set(&priv->enabled, 0); + down(&hw_priv->scan.lock); + if(priv->join_status == XRADIO_JOIN_STATUS_STA){ + if (atomic_xchg(&priv->delayed_unjoin, 0)) { + wsm_unlock_tx(hw_priv); + sta_printk(XRADIO_DBG_ERROR, "%s:delayed_unjoin exist!\n", __func__); + } + cancel_work_sync(&priv->unjoin_work); + wsm_lock_tx(hw_priv); + xradio_unjoin_work(&priv->unjoin_work); + } + mutex_lock(&hw_priv->conf_mutex); + xradio_tx_queues_lock(hw_priv); + wsm_lock_tx(hw_priv); + switch (priv->join_status) { + case XRADIO_JOIN_STATUS_AP: + for (i = 0; priv->link_id_map; ++i) { + if (priv->link_id_map & BIT(i)) { + xrwl_unmap_link(priv, i); + priv->link_id_map &= ~BIT(i); + } + } + memset(priv->link_id_db, 0, + sizeof(priv->link_id_db)); + priv->sta_asleep_mask = 0; + priv->enable_beacon = false; + priv->tx_multicast = false; + priv->aid0_bit_set = false; + priv->buffered_multicasts = false; + priv->pspoll_mask = 0; + reset.link_id = 0; + wsm_reset(hw_priv, &reset, priv->if_id); + SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); + xradio_for_each_vif(hw_priv, tmp_priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv) +#else + if (!tmp_priv) +#endif + continue; + if ((tmp_priv->join_status == XRADIO_JOIN_STATUS_STA) && tmp_priv->htcap) + is_htcapie = true; + } + + if (is_htcapie) { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE; + sta_printk(XRADIO_DBG_NIY, "AP REMOVE HTCAP 11N %d\n",hw_priv->vif0_throttle); + } else { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; + sta_printk(XRADIO_DBG_NIY, "AP REMOVE 11BG %d\n",hw_priv->vif0_throttle); + } + break; + case XRADIO_JOIN_STATUS_MONITOR: + xradio_disable_listening(priv); + break; + default: + break; + } + /* TODO:COMBO: Change Queue Module */ + __xradio_flush(hw_priv, false, priv->if_id); + + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + cancel_delayed_work_sync(&priv->link_id_gc_work); + cancel_delayed_work_sync(&priv->join_timeout); + cancel_delayed_work_sync(&priv->set_cts_work); + cancel_delayed_work_sync(&priv->pending_offchanneltx_work); + + del_timer_sync(&priv->mcast_timeout); + /* TODO:COMBO: May be reset of these variables "delayed_link_loss and + * join_status to default can be removed as dev_priv will be freed by + * mac80211 */ + priv->delayed_link_loss = 0; + priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; + wsm_unlock_tx(hw_priv); + + if ((priv->if_id ==1) && (priv->mode == NL80211_IFTYPE_AP + || priv->mode == NL80211_IFTYPE_P2P_GO)) { + hw_priv->is_go_thru_go_neg = false; + } + spin_lock(&hw_priv->vif_list_lock); + spin_lock(&priv->vif_lock); + hw_priv->vif_list[priv->if_id] = NULL; + hw_priv->if_id_slot &= (~BIT(priv->if_id)); + atomic_dec(&hw_priv->num_vifs); + if (atomic_read(&hw_priv->num_vifs) == 0) { + xradio_free_keys(hw_priv); + memset(hw_priv->mac_addr, 0, ETH_ALEN); + } + spin_unlock(&priv->vif_lock); + spin_unlock(&hw_priv->vif_list_lock); + priv->listening = false; + + xradio_debug_release_priv(priv); + + xradio_tx_queues_unlock(hw_priv); + mutex_unlock(&hw_priv->conf_mutex); + + if (atomic_read(&hw_priv->num_vifs) == 0) + flush_workqueue(hw_priv->workqueue); + memset(priv, 0, sizeof(struct xradio_vif)); + up(&hw_priv->scan.lock); +} + +int xradio_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p) +{ + int ret = 0; + sta_printk(XRADIO_DBG_WARN, "%s: new type=%d(%d), p2p=%d(%d)\n", + __func__, new_type, vif->type, p2p, vif->p2p); + if (new_type != vif->type || vif->p2p != p2p) { + xradio_remove_interface(dev, vif); + vif->type = new_type; + vif->p2p = p2p; + ret = xradio_add_interface(dev, vif); + } + + return ret; +} + +int xradio_config(struct ieee80211_hw *dev, u32 changed) +{ + int ret = 0; + struct xradio_common *hw_priv = dev->priv; + struct ieee80211_conf *conf = &dev->conf; +#ifdef CONFIG_XRADIO_TESTMODE + int max_power_level = 0; + int min_power_level = 0; +#endif + /* TODO:COMBO: adjust to multi vif interface + * IEEE80211_CONF_CHANGE_IDLE is still handled per xradio_vif*/ + int if_id = 0; + struct xradio_vif *priv; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (changed & + (IEEE80211_CONF_CHANGE_MONITOR|IEEE80211_CONF_CHANGE_IDLE)) { + /* TBD: It looks like it's transparent + * there's a monitor interface present -- use this + * to determine for example whether to calculate + * timestamps for packets or not, do not use instead + * of filter flags! */ + sta_printk(XRADIO_DBG_NIY, "ignore IEEE80211_CONF_CHANGE_MONITOR (%d)" + "IEEE80211_CONF_CHANGE_IDLE (%d)\n", + (changed & IEEE80211_CONF_CHANGE_MONITOR) ? 1 : 0, + (changed & IEEE80211_CONF_CHANGE_IDLE) ? 1 : 0); + return ret; + } + + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + priv = __xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + /* TODO: IEEE80211_CONF_CHANGE_QOS */ + /* TODO:COMBO:Change when support is available mac80211*/ + if (changed & IEEE80211_CONF_CHANGE_POWER) { + /*hw_priv->output_power = conf->power_level;*/ + hw_priv->output_power = 20; +#ifdef CONFIG_XRADIO_TESTMODE + /* Testing if Power Level to set is out of device power range */ + if (conf->chan_conf->channel->band == IEEE80211_BAND_2GHZ) { + max_power_level = hw_priv->txPowerRange[0].max_power_level; + min_power_level = hw_priv->txPowerRange[0].min_power_level; + } else { + max_power_level = hw_priv->txPowerRange[1].max_power_level; + min_power_level = hw_priv->txPowerRange[1].min_power_level; + } + if (hw_priv->output_power > max_power_level) + hw_priv->output_power = max_power_level; + else if (hw_priv->output_power < min_power_level) + hw_priv->output_power = min_power_level; +#endif /* CONFIG_XRADIO_TESTMODE */ + + sta_printk(XRADIO_DBG_NIY, "Config Tx power=%d, but real=%d\n", + conf->power_level, hw_priv->output_power); + SYS_WARN(wsm_set_output_power(hw_priv, hw_priv->output_power * 10, if_id)); + } + + if ((changed & IEEE80211_CONF_CHANGE_CHANNEL) && + (hw_priv->channel != conf->channel)) { + /* Switch Channel commented for CC Mode */ + struct ieee80211_channel *ch = conf->channel; + sta_printk(XRADIO_DBG_WARN, "Freq %d (wsm ch: %d).\n", + ch->center_freq, ch->hw_value); + /* Earlier there was a call to __xradio_flush(). + Removed as deemed unnecessary */ + hw_priv->channel = ch; + hw_priv->channel_changed = 1; + } + + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); + return ret; +} + +void xradio_update_filtering(struct xradio_vif *priv) +{ + int ret; + bool bssid_filtering = !priv->rx_filter.bssid; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + static struct wsm_beacon_filter_control bf_disabled = { + .enabled = 0, + .bcn_count = 1, + }; + bool ap_mode = 0; + static struct wsm_beacon_filter_table bf_table_auto = { + .numOfIEs = __cpu_to_le32(2), + .entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC, + .entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + .entry[0].oui[0] = 0x50, + .entry[0].oui[1] = 0x6F, + .entry[0].oui[2] = 0x9A, + + .entry[1].ieId = WLAN_EID_HT_INFORMATION, + .entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED | + WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT | + WSM_BEACON_FILTER_IE_HAS_APPEARED, + }; + static struct wsm_beacon_filter_control bf_auto = { + .enabled = WSM_BEACON_FILTER_ENABLE | + WSM_BEACON_FILTER_AUTO_ERP, + .bcn_count = 1, + }; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + bf_auto.bcn_count = priv->bf_control.bcn_count; + + if (priv->join_status == XRADIO_JOIN_STATUS_PASSIVE) + return; + else if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) + bssid_filtering = false; + + if (priv->vif && (priv->vif->type == NL80211_IFTYPE_AP)) + ap_mode = true; + /* + * When acting as p2p client being connected to p2p GO, in order to + * receive frames from a different p2p device, turn off bssid filter. + * + * WARNING: FW dependency! + * This can only be used with FW WSM371 and its successors. + * In that FW version even with bssid filter turned off, + * device will block most of the unwanted frames. + */ + if (priv->vif && priv->vif->p2p) + bssid_filtering = false; + + ret = wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); + if (!ret && !ap_mode) { + if (priv->vif) { + if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type) + ret = wsm_set_beacon_filter_table(hw_priv, &priv->bf_table, priv->if_id); + else + ret = wsm_set_beacon_filter_table(hw_priv, &bf_table_auto, priv->if_id); + } else + SYS_WARN(1); + } + if (!ret && !ap_mode) { + if (priv->disable_beacon_filter) + ret = wsm_beacon_filter_control(hw_priv, &bf_disabled, priv->if_id); + else { + if (priv->vif) { + if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type) + ret = wsm_beacon_filter_control(hw_priv, &priv->bf_control, + priv->if_id); + else + ret = wsm_beacon_filter_control(hw_priv, &bf_auto, priv->if_id); + } else + SYS_WARN(1); + } + } + + if (!ret) + ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering, priv->if_id); +#if 0 + if (!ret) { + ret = wsm_set_multicast_filter(hw_priv, &priv->multicast_filter, priv->if_id); + } +#endif + if (ret) + sta_printk(XRADIO_DBG_ERROR, "%s: Update filtering failed: %d.\n", + __func__, ret); + return; +} + +void xradio_update_filtering_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, + update_filtering_work); + + xradio_update_filtering(priv); +} + +void xradio_set_beacon_wakeup_period_work(struct work_struct *work) +{ + + struct xradio_vif *priv = + container_of(work, struct xradio_vif, set_beacon_wakeup_period_work); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + +#ifdef XRADIO_USE_LONG_DTIM_PERIOD +{ + int join_dtim_period_extend; + if (priv->join_dtim_period <= 3) { + join_dtim_period_extend = priv->join_dtim_period * 3; + } else if (priv->join_dtim_period <= 5) { + join_dtim_period_extend = priv->join_dtim_period * 2; + } else { + join_dtim_period_extend = priv->join_dtim_period; + } + SYS_WARN(wsm_set_beacon_wakeup_period(priv->hw_priv, + priv->beacon_int * join_dtim_period_extend > + MAX_BEACON_SKIP_TIME_MS ? 1 : join_dtim_period_extend, + 0, priv->if_id)); +} +#else + SYS_WARN(wsm_set_beacon_wakeup_period(priv->hw_priv, + priv->beacon_int * priv->join_dtim_period > + MAX_BEACON_SKIP_TIME_MS ? 1 :priv->join_dtim_period, + 0, priv->if_id)); +#endif +} + +u64 xradio_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv = NULL; + static u8 broadcast_ipv6[ETH_ALEN] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 + }; + static u8 broadcast_ipv4[ETH_ALEN] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x01 + }; + + int i= 0; + xradio_for_each_vif(hw_priv,priv,i) { + struct netdev_hw_addr *ha = NULL; + int count = 0; + if ((!priv)) + continue; +#ifdef P2P_MULTIVIF + if (priv->if_id ==XRWL_GENERIC_IF_ID) + return netdev_hw_addr_list_count(mc_list); +#endif + + /* Disable multicast filtering */ + priv->has_multicast_subscription = false; + memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter)); + if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES) + return 0; + + /* Enable if requested */ + netdev_hw_addr_list_for_each(ha, mc_list) { + sta_printk(XRADIO_DBG_MSG, "multicast: %pM\n", ha->addr); + memcpy(&priv->multicast_filter.macAddress[count], ha->addr, ETH_ALEN); + if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) && + memcmp(ha->addr, broadcast_ipv6, ETH_ALEN)) + priv->has_multicast_subscription = true; + count++; + } + if (count) { + priv->multicast_filter.enable = __cpu_to_le32(1); + priv->multicast_filter.numOfAddresses = __cpu_to_le32(count); + } + } + return netdev_hw_addr_list_count(mc_list); +} + +void xradio_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv = NULL; + int i = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + /* delete umac warning */ + if (hw_priv->vif_list[0] == NULL && hw_priv->vif_list[1] == NULL && + hw_priv->vif_list[2] == NULL) + *total_flags &= ~(1<<31); + + xradio_for_each_vif(hw_priv, priv, i) { + if(NULL == priv) + continue; +#ifdef P2P_MULTIVIF + if (priv->if_id == XRWL_GENERIC_IF_ID) { + *total_flags &= ~(1<<31); + continue; + } +#endif + +#if 0 + bool listening = !!(*total_flags & + (FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ)); +#endif + + *total_flags &= FIF_PROMISC_IN_BSS | + FIF_OTHER_BSS | + FIF_FCSFAIL | + FIF_BCN_PRBRESP_PROMISC | + FIF_PROBE_REQ; + + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + + priv->rx_filter.promiscuous = (*total_flags & FIF_PROMISC_IN_BSS)? 1 : 0; + priv->rx_filter.bssid = (*total_flags & + (FIF_OTHER_BSS | FIF_PROBE_REQ)) ? 1 : 0; + priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0; + priv->bf_control.bcn_count = (*total_flags & + (FIF_BCN_PRBRESP_PROMISC | + FIF_PROMISC_IN_BSS | + FIF_PROBE_REQ)) ? 1 : 0; + + /*add for handle ap FIF_PROBE_REQ message,*/ + priv->rx_filter.promiscuous = 0; + priv->rx_filter.fcs = 0; + if(NL80211_IFTYPE_AP == priv->vif->type){ + priv->bf_control.bcn_count = 1; + priv->rx_filter.bssid = 1; + }else{ + priv->bf_control.bcn_count = 0; + priv->rx_filter.bssid = 0; + } +#if 0 + if (priv->listening ^ listening) { + priv->listening = listening; + wsm_lock_tx(hw_priv); + xradio_update_listening(priv, listening); + wsm_unlock_tx(hw_priv); + } +#endif + xradio_update_filtering(priv); + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); + } +} + +int xradio_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params) +{ + struct xradio_common *hw_priv = dev->priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + int ret = 0; + /* To prevent re-applying PM request OID again and again*/ + bool old_uapsdFlags; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (SYS_WARN(!priv)) + return -EOPNOTSUPP; + +#ifdef P2P_MULTIVIF + if (priv->if_id == XRWL_GENERIC_IF_ID) + return 0; +#endif + + mutex_lock(&hw_priv->conf_mutex); + + if (queue < dev->queues) { + old_uapsdFlags = priv->uapsd_info.uapsdFlags; + + WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0); + ret = wsm_set_tx_queue_params(hw_priv, + &priv->tx_queue_params.params[queue], + queue, priv->if_id); + if (ret) { + sta_printk(XRADIO_DBG_ERROR,"%s:wsm_set_tx_queue_params failed!\n", __func__); + ret = -EINVAL; + goto out; + } + + WSM_EDCA_SET(&priv->edca, queue, params->aifs, + params->cw_min, params->cw_max, + params->txop, 0xc8, params->uapsd); + /* sta role is not support the uapsd */ + if (priv->mode == NL80211_IFTYPE_STATION || + priv->mode == NL80211_IFTYPE_P2P_CLIENT) + priv->edca.params[queue].uapsdEnable = 0; + + ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); + if (ret) { + sta_printk(XRADIO_DBG_ERROR,"%s:wsm_set_edca_params failed!\n", __func__); + ret = -EINVAL; + goto out; + } + + if (priv->mode == NL80211_IFTYPE_STATION) { + ret = xradio_set_uapsd_param(priv, &priv->edca); + if (!ret && priv->setbssparams_done && + (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (old_uapsdFlags != priv->uapsd_info.uapsdFlags)) + xradio_set_pm(priv, &priv->powersave_mode); + } + } else { + sta_printk(XRADIO_DBG_ERROR,"%s:queue is to large!\n", __func__); + ret = -EINVAL; + } + +out: + mutex_unlock(&hw_priv->conf_mutex); + return ret; +} + +int xradio_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats) +{ + struct xradio_common *hw_priv = dev->priv; + + memcpy(stats, &hw_priv->stats, sizeof(*stats)); + return 0; +} + +/* +int xradio_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats) +{ + int i; + struct xradio_common *priv = dev->priv; + + for (i = 0; i < dev->queues; ++i) + xradio_queue_get_stats(&priv->tx_queue[i], &stats[i]); + + return 0; +} +*/ + +/* for ps debug */ +#ifdef CONFIG_XRADIO_DEBUGFS +u8 ps_disable = 0; +u8 ps_idleperiod = 0; +u8 ps_changeperiod = 0; +#endif + +int xradio_set_pm(struct xradio_vif *priv, const struct wsm_set_pm *arg) +{ + struct wsm_set_pm pm = *arg; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + +#ifdef CONFIG_XRADIO_DEBUGFS + if (ps_disable) + pm.pmMode = WSM_PSM_ACTIVE; + if (ps_idleperiod) { + pm.fastPsmIdlePeriod = ps_idleperiod << 1; + pm.apPsmChangePeriod = ps_changeperiod << 1; + } +#endif + + if (priv->uapsd_info.uapsdFlags != 0) + pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG; + + if (memcmp(&pm, &priv->firmware_ps_mode, sizeof(struct wsm_set_pm))) { + priv->firmware_ps_mode = pm; + return wsm_set_pm(priv->hw_priv, &pm, priv->if_id); + } else { + return 0; + } +} + +int xradio_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + int ret = -EOPNOTSUPP; + struct xradio_common *hw_priv = dev->priv; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + +#ifdef P2P_MULTIVIF + SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); +#endif + mutex_lock(&hw_priv->conf_mutex); + + if (cmd == SET_KEY) { + u8 *peer_addr = NULL; + int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ? 1 : 0; + int idx = xradio_alloc_key(hw_priv); + struct wsm_add_key *wsm_key = &hw_priv->keys[idx]; + + if (idx < 0) { + sta_printk(XRADIO_DBG_ERROR,"%s:xradio_alloc_key failed!\n", + __func__); + ret = -EINVAL; + goto finally; + } + + SYS_BUG(pairwise && !sta); + if (sta) + peer_addr = sta->addr; + + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + + priv->cipherType = key->cipher; + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (key->keylen > 16) { + xradio_free_key(hw_priv, idx); + sta_printk(XRADIO_DBG_ERROR,"%s: keylen too long=%d!\n", + __func__, key->keylen); + ret = -EINVAL; + goto finally; + } + + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE; + memcpy(wsm_key->wepPairwiseKey.peerAddress, peer_addr, ETH_ALEN); + memcpy(wsm_key->wepPairwiseKey.keyData, &key->key[0], key->keylen); + wsm_key->wepPairwiseKey.keyLength = key->keylen; + sta_printk(XRADIO_DBG_NIY,"%s: WEP_PAIRWISE keylen=%d!\n", + __func__, key->keylen); + } else { + wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT; + memcpy(wsm_key->wepGroupKey.keyData, &key->key[0], key->keylen); + wsm_key->wepGroupKey.keyLength = key->keylen; + wsm_key->wepGroupKey.keyId = key->keyidx; + sta_printk(XRADIO_DBG_NIY,"%s: WEP_GROUP keylen=%d!\n", + __func__, key->keylen); + } + break; + case WLAN_CIPHER_SUITE_TKIP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE; + memcpy(wsm_key->tkipPairwiseKey.peerAddress, peer_addr, ETH_ALEN); + memcpy(wsm_key->tkipPairwiseKey.tkipKeyData, &key->key[0], 16); + memcpy(wsm_key->tkipPairwiseKey.txMicKey, &key->key[16], 8); + memcpy(wsm_key->tkipPairwiseKey.rxMicKey, &key->key[24], 8); + sta_printk(XRADIO_DBG_NIY,"%s: TKIP_PAIRWISE keylen=%d!\n", + __func__, key->keylen); + } else { + size_t mic_offset = (priv->mode == NL80211_IFTYPE_AP) ? 16 : 24; + wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP; + memcpy(wsm_key->tkipGroupKey.tkipKeyData,&key->key[0], 16); + memcpy(wsm_key->tkipGroupKey.rxMicKey, &key->key[mic_offset], 8); + + /* TODO: Where can I find TKIP SEQ? */ + memset(wsm_key->tkipGroupKey.rxSeqCounter, 0, 8); + wsm_key->tkipGroupKey.keyId = key->keyidx; + sta_printk(XRADIO_DBG_NIY,"%s: TKIP_GROUP keylen=%d!\n", + __func__, key->keylen); + } + break; + case WLAN_CIPHER_SUITE_CCMP: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE; + memcpy(wsm_key->aesPairwiseKey.peerAddress, peer_addr, ETH_ALEN); + memcpy(wsm_key->aesPairwiseKey.aesKeyData, &key->key[0], 16); + sta_printk(XRADIO_DBG_NIY,"%s: CCMP_PAIRWISE keylen=%d!\n", + __func__, key->keylen); + } else { + wsm_key->type = WSM_KEY_TYPE_AES_GROUP; + memcpy(wsm_key->aesGroupKey.aesKeyData, &key->key[0], 16); + /* TODO: Where can I find AES SEQ? */ + memset(wsm_key->aesGroupKey.rxSeqCounter, 0, 8); + wsm_key->aesGroupKey.keyId = key->keyidx; + sta_printk(XRADIO_DBG_NIY,"%s: CCMP_GROUP keylen=%d!\n", + __func__, key->keylen); + } + break; +#ifdef CONFIG_XRADIO_WAPI_SUPPORT + case WLAN_CIPHER_SUITE_SMS4: + if (pairwise) { + wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE; + memcpy(wsm_key->wapiPairwiseKey.peerAddress, peer_addr, ETH_ALEN); + memcpy(wsm_key->wapiPairwiseKey.wapiKeyData, &key->key[0], 16); + memcpy(wsm_key->wapiPairwiseKey.micKeyData, &key->key[16], 16); + wsm_key->wapiPairwiseKey.keyId = key->keyidx; + sta_printk(XRADIO_DBG_NIY,"%s: WAPI_PAIRWISE keylen=%d!\n", + __func__, key->keylen); + } else { + wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP; + memcpy(wsm_key->wapiGroupKey.wapiKeyData, &key->key[0], 16); + memcpy(wsm_key->wapiGroupKey.micKeyData, &key->key[16], 16); + wsm_key->wapiGroupKey.keyId = key->keyidx; + sta_printk(XRADIO_DBG_NIY,"%s: WAPI_GROUP keylen=%d!\n", + __func__, key->keylen); + } + break; +#endif /* CONFIG_XRADIO_WAPI_SUPPORT */ + default: + sta_printk(XRADIO_DBG_ERROR,"%s: key->cipher unknown(%d)!\n", + __func__, key->cipher); + xradio_free_key(hw_priv, idx); + ret = -EOPNOTSUPP; + goto finally; + } + ret = SYS_WARN(wsm_add_key(hw_priv, wsm_key, priv->if_id)); + if (!ret) + key->hw_key_idx = idx; + else + xradio_free_key(hw_priv, idx); + + if (!ret && (pairwise || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) && + (priv->filter4.enable & 0x2)) + xradio_set_arpreply(dev, vif); +#ifdef IPV6_FILTERING + if (!ret && (pairwise || wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT) && + (priv->filter6.enable & 0x2)) + xradio_set_na(dev, vif); +#endif /*IPV6_FILTERING*/ + + } else if (cmd == DISABLE_KEY) { + struct wsm_remove_key wsm_key = { + .entryIndex = key->hw_key_idx, + }; + + if (wsm_key.entryIndex > WSM_KEY_MAX_IDX) { + ret = -EINVAL; + goto finally; + } + + xradio_free_key(hw_priv, wsm_key.entryIndex); + ret = wsm_remove_key(hw_priv, &wsm_key, priv->if_id); + } else { + sta_printk(XRADIO_DBG_ERROR, "%s: Unsupported command", __func__); + } + +finally: + mutex_unlock(&hw_priv->conf_mutex); + return ret; +} + +void xradio_wep_key_work(struct work_struct *work) +{ + struct xradio_vif *priv = container_of(work, struct xradio_vif , wep_key_work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); + struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; + __le32 wep_default_key_id = __cpu_to_le32(priv->wep_default_key_id); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + SYS_BUG(queueId >= 4); + + sta_printk(XRADIO_DBG_MSG, "Setting default WEP key: %d\n", + priv->wep_default_key_id); + + wsm_flush_tx(hw_priv); + SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID, + &wep_default_key_id, sizeof(wep_default_key_id), + priv->if_id)); + +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_requeue(hw_priv, queue, hw_priv->pending_frame_id, true); +#else + xradio_queue_requeue(queue, hw_priv->pending_frame_id, true); +#endif + wsm_unlock_tx(hw_priv); +} + +int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + struct xradio_common *hw_priv = hw->priv; + int ret = 0; + __le32 val32; + struct xradio_vif *priv = NULL; + int i =0; + int if_id; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + xradio_for_each_vif(hw_priv,priv,i) { + if (!priv) + continue; + if_id = priv->if_id; +#ifdef P2P_MULTIVIF + SYS_WARN(priv->if_id == XRWL_GENERIC_IF_ID); +#endif + + if (value != (u32) -1) + val32 = __cpu_to_le32(value); + else + val32 = 0; /* disabled */ + + /* mutex_lock(&priv->conf_mutex); */ + ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD, + &val32, sizeof(val32), if_id)); + /* mutex_unlock(&priv->conf_mutex); */ + } + return ret; +} + +/* TODO: COMBO: Flush only a particular interface specific parts */ +int __xradio_flush(struct xradio_common *hw_priv, bool drop, int if_id) +{ + int i, ret; + struct xradio_vif *priv = + __xrwl_hwpriv_to_vifpriv(hw_priv, if_id); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + for (;;) { + /* TODO: correct flush handling is required when dev_stop. + * Temporary workaround: 2s + */ + if (drop) { + for (i = 0; i < 4; ++i) + xradio_queue_clear(&hw_priv->tx_queue[i],if_id); + } else if(!hw_priv->bh_error){ + ret = wait_event_timeout( + hw_priv->tx_queue_stats.wait_link_id_empty, + xradio_queue_stats_is_empty(&hw_priv->tx_queue_stats, -1, if_id), + 2 * HZ); + } else { //add by yangfh, don't wait when bh error + sta_printk(XRADIO_DBG_ERROR, " %s:bh_error occur.\n", __func__); + ret = -1; + break; + } + + if (!drop && unlikely(ret <= 0)) { + sta_printk(XRADIO_DBG_ERROR, " %s: timeout...\n", __func__); + ret = -ETIMEDOUT; + break; + } else { + ret = 0; + } + + wsm_vif_lock_tx(priv); + if (unlikely(!xradio_queue_stats_is_empty(&hw_priv->tx_queue_stats, + -1, if_id))) { + /* Highly unlekely: WSM requeued frames. */ + wsm_unlock_tx(hw_priv); + continue; + } + wsm_unlock_tx(hw_priv); + break; + } + return ret; +} + +void xradio_flush(struct ieee80211_hw *hw, bool drop) +{ + struct xradio_vif *priv = NULL; + struct xradio_common *hw_priv = hw->priv; + int i = 0; + //struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + + /*TODO:COMBO: reenable this part of code when flush callback + * is implemented per vif */ + /*switch (hw_priv->mode) { + case NL80211_IFTYPE_MONITOR: + drop = true; + break; + case NL80211_IFTYPE_AP: + if (!hw_priv->enable_beacon) + drop = true; + break; + }*/ + + //if (!(hw_priv->if_id_slot & BIT(priv->if_id))) + // return; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + xradio_for_each_vif(hw_priv, priv, i) { + if(NULL == priv) + continue; + if ((hw_priv->if_id_slot & BIT(priv->if_id))) + __xradio_flush(hw_priv, drop, priv->if_id); + } + return; +} + +int xradio_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + int duration) +{ + int ret = 0; + struct xradio_common *hw_priv = hw->priv; + struct xradio_vif *priv = NULL; + int i = 0; + int if_id; +#ifdef TES_P2P_0002_ROC_RESTART + struct timeval TES_P2P_0002_tmval; +#endif + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + +#ifdef TES_P2P_0002_ROC_RESTART + do_gettimeofday(&TES_P2P_0002_tmval); + TES_P2P_0002_roc_dur = (s32)duration; + TES_P2P_0002_roc_sec = (s32)TES_P2P_0002_tmval.tv_sec; + TES_P2P_0002_roc_usec = (s32)TES_P2P_0002_tmval.tv_usec; +#endif + + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + xradio_for_each_vif(hw_priv, priv, i) { + if(NULL == priv) + continue; + if_id = priv->if_id; + +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_WARN, "ROC IN %d ch %d\n", + priv->if_id, chan->hw_value); +#endif + /* default only p2p interface if_id can remain on */ + if((priv->if_id == 0) || (priv->if_id == 1)) + continue; + hw_priv->roc_if_id = priv->if_id; + ret = SYS_WARN(__xradio_flush(hw_priv, false, if_id)); + xradio_enable_listening(priv, chan); + + if (!ret) { + atomic_set(&hw_priv->remain_on_channel, 1); + queue_delayed_work(hw_priv->workqueue, &hw_priv->rem_chan_timeout, + duration * HZ / 1000); + priv->join_status = XRADIO_JOIN_STATUS_MONITOR; + ieee80211_ready_on_channel(hw); + } else { + hw_priv->roc_if_id = -1; + up(&hw_priv->scan.lock); + } + +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_WARN, "ROC OUT %d\n", priv->if_id); +#endif + } + /* set the channel to supplied ieee80211_channel pointer, if it + is not set. This is to remove the crash while sending a probe res + in listen state. Later channel will updated on + IEEE80211_CONF_CHANGE_CHANNEL event*/ + if(!hw_priv->channel) { + hw_priv->channel = chan; + } + mutex_unlock(&hw_priv->conf_mutex); + return ret; +} + +int xradio_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct xradio_common *hw_priv = hw->priv; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + sta_printk(XRADIO_DBG_NIY, "Cancel remain on channel\n"); +#ifdef TES_P2P_0002_ROC_RESTART + if (TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) { + TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; + sta_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE][Cancel ROC]\n"); + } +#endif + + if (atomic_read(&hw_priv->remain_on_channel)) + cancel_delayed_work_sync(&hw_priv->rem_chan_timeout); + + if (atomic_read(&hw_priv->remain_on_channel)) + xradio_rem_chan_timeout(&hw_priv->rem_chan_timeout.work); + + return 0; +} + +/* ******************************************************************** */ +/* WSM callbacks */ + +void xradio_channel_switch_cb(struct xradio_common *hw_priv) +{ + wsm_unlock_tx(hw_priv); +} + +void xradio_free_event_queue(struct xradio_common *hw_priv) +{ + LIST_HEAD(list); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + spin_lock(&hw_priv->event_queue_lock); + list_splice_init(&hw_priv->event_queue, &list); + spin_unlock(&hw_priv->event_queue_lock); + + __xradio_free_event_queue(&list); +} + +void xradio_event_handler(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, event_handler); + struct xradio_vif *priv; + struct xradio_wsm_event *event; + LIST_HEAD(list); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + spin_lock(&hw_priv->event_queue_lock); + list_splice_init(&hw_priv->event_queue, &list); + spin_unlock(&hw_priv->event_queue_lock); + + mutex_lock(&hw_priv->conf_mutex); + list_for_each_entry(event, &list, link) { + priv = __xrwl_hwpriv_to_vifpriv(hw_priv, event->if_id); + if (!priv) { + sta_printk(XRADIO_DBG_WARN, "[CQM] Event for non existing " + "interface, ignoring.\n"); + continue; + } + switch (event->evt.eventId) { + case WSM_EVENT_ERROR: + /* I even don't know what is it about.. */ + //STUB(); + break; + case WSM_EVENT_BSS_LOST: + { + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status > XRADIO_BSS_LOSS_NONE) { + spin_unlock(&priv->bss_loss_lock); + break; + } + priv->bss_loss_status = XRADIO_BSS_LOSS_CHECKING; + spin_unlock(&priv->bss_loss_lock); + sta_printk(XRADIO_DBG_WARN, "[CQM] BSS lost, Beacon miss=%d, event=%x.\n", + (event->evt.eventData>>8)&0xff, event->evt.eventData&0xff); + + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + if (!down_trylock(&hw_priv->scan.lock)) { + up(&hw_priv->scan.lock); + priv->delayed_link_loss = 0; + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, HZ/10); //100ms + } else { + /* Scan is in progress. Delay reporting. */ + /* Scan complete will trigger bss_loss_work */ + priv->delayed_link_loss = 1; + /* Also we're starting watchdog. */ + queue_delayed_work(hw_priv->workqueue, + &priv->bss_loss_work, 10 * HZ); + } + break; + } + case WSM_EVENT_BSS_REGAINED: + { + sta_printk(XRADIO_DBG_WARN, "[CQM] BSS regained.\n"); + priv->delayed_link_loss = 0; + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = XRADIO_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + cancel_delayed_work_sync(&priv->bss_loss_work); + cancel_delayed_work_sync(&priv->connection_loss_work); + break; + } + case WSM_EVENT_RADAR_DETECTED: + //STUB(); + break; + case WSM_EVENT_RCPI_RSSI: + { + /* RSSI: signed Q8.0, RCPI: unsigned Q7.1 + * RSSI = RCPI / 2 - 110 */ + int rcpiRssi = (int)(event->evt.eventData & 0xFF); + int cqm_evt; + if (priv->cqm_use_rssi) + rcpiRssi = (s8)rcpiRssi; + else + rcpiRssi = rcpiRssi / 2 - 110; + + cqm_evt = (rcpiRssi <= priv->cqm_rssi_thold) ? + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW : + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH; + sta_printk(XRADIO_DBG_NIY, "[CQM] RSSI event: %d", rcpiRssi); + ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, + GFP_KERNEL); + break; + } + case WSM_EVENT_BT_INACTIVE: + //STUB(); + break; + case WSM_EVENT_BT_ACTIVE: + //STUB(); + break; + case WSM_EVENT_INACTIVITY: + { + int link_id = ffs((u32)(event->evt.eventData)) - 1; + struct sk_buff *skb; + struct ieee80211_mgmt *deauth; + struct xradio_link_entry *entry = NULL; + + sta_printk(XRADIO_DBG_WARN, "Inactivity Event Recieved for " + "link_id %d\n", link_id); + skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); + if (!skb) + break; + skb_reserve(skb, 64); + xrwl_unmap_link(priv, link_id); + deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); + SYS_WARN(!deauth); + entry = &priv->link_id_db[link_id - 1]; + deauth->duration = 0; + memcpy(deauth->da, priv->vif->addr, ETH_ALEN); + memcpy(deauth->sa, entry->mac/*priv->link_id_db[i].mac*/, ETH_ALEN); + memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN); + deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_DEAUTH | + IEEE80211_FCTL_TODS); + deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; + deauth->seq_ctrl = 0; + ieee80211_rx_irqsafe(priv->hw, skb); + sta_printk(XRADIO_DBG_WARN, " Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da); + queue_work(priv->hw_priv->workqueue, &priv->set_tim_work); + break; + } + case WSM_EVENT_PS_MODE_ERROR: + { + if (!priv->uapsd_info.uapsdFlags && + (priv->user_pm_mode != WSM_PSM_PS)) + { + struct wsm_set_pm pm = priv->powersave_mode; + int ret = 0; + + priv->powersave_mode.pmMode = WSM_PSM_ACTIVE; + ret = xradio_set_pm (priv, &priv->powersave_mode); + if(ret) + priv->powersave_mode = pm; + } + break; + } + } + } + mutex_unlock(&hw_priv->conf_mutex); + __xradio_free_event_queue(&list); +} + +void xradio_bss_loss_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, bss_loss_work.work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + int timeout; /* in beacons */ + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + timeout = priv->cqm_link_loss_count - priv->cqm_beacon_loss_count; + /* Skip the confimration procedure in P2P case */ + if (priv->vif->p2p) + goto report; + + spin_lock(&priv->bss_loss_lock); + if (priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING) { + //do loss report next time. + priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMED; + spin_unlock(&priv->bss_loss_lock); + //wait for more 1s to loss confirm. + queue_delayed_work(hw_priv->workqueue, &priv->bss_loss_work, 1 * HZ); + return; + } else if (priv->bss_loss_status == XRADIO_BSS_LOSS_NONE) { + spin_unlock(&priv->bss_loss_lock); + //link is alive. + cancel_delayed_work_sync(&priv->connection_loss_work); + return; + } else if (priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING) { + /* it mean no confirming packets, just report loss. */ + } + spin_unlock(&priv->bss_loss_lock); + +report: + if (priv->cqm_beacon_loss_count) { + sta_printk(XRADIO_DBG_WARN, "[CQM] Beacon loss.\n"); + if (timeout <= 0) + timeout = 0; +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL); +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + } else { + timeout = 0; + } + + cancel_delayed_work_sync(&priv->connection_loss_work); + queue_delayed_work(hw_priv->workqueue, &priv->connection_loss_work, + timeout * HZ / 10); + + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = XRADIO_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); +} + +void xradio_connection_loss_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, connection_loss_work.work); + sta_printk(XRADIO_DBG_ERROR, "[CQM] if%d Reporting connection loss.\n", + priv->if_id); + ieee80211_connection_loss(priv->vif); +} + +void xradio_tx_failure_work(struct work_struct *work) +{ + //struct xradio_vif *priv = + // container_of(work, struct xradio_vif, tx_failure_work); + sta_printk(XRADIO_DBG_WARN, "[CQM] Reporting TX failure.\n"); +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + //ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL); +#else /* CONFIG_XRADIO_USE_EXTENSIONS */ + //(void)priv; +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ +} + +#ifdef CONFIG_XRADIO_TESTMODE +/** + * xradio_device_power_calc- Device power calculation + * from values fetch from SDD File. + * + * @priv: the private structure + * @Max_output_power: Power fetch from SDD + * @fe_cor: front-end loss correction + * @band: Either 2GHz or 5GHz + * + */ +void xradio_device_power_calc(struct xradio_common *hw_priv, + s16 max_output_power, s16 fe_cor, u32 band) +{ + s16 power_calc; + + power_calc = max_output_power - fe_cor; + if ((power_calc % 16) != 0) + power_calc += 16; + + hw_priv->txPowerRange[band].max_power_level = power_calc/16; + /* + * 12dBm is control range supported by firmware. + * This means absolute min power is + * max_power_level - 12. + */ + hw_priv->txPowerRange[band].min_power_level = + hw_priv->txPowerRange[band].max_power_level - 12; + hw_priv->txPowerRange[band].stepping = 1; + +} +#endif +/* ******************************************************************** */ +#ifdef CONFIG_XRADIO_TESTMODE +#define SDD_MAX_OUTPUT_POWER_2G4_ELT_ID 0xE3 +#define SDD_MAX_OUTPUT_POWER_5G_ELT_ID 0xE4 +#define SDD_FE_COR_2G4_ELT_ID 0x30 +#define SDD_FE_COR_5G_ELT_ID 0x31 +#define MIN(x, y, z) (x < y ? (x < z ? x : z) : (y < z ? y : z)) +static int xradio_test_pwrlevel(struct xradio_common *hw_priv) +{ + int ret = -1; + int parsedLength = 0; + struct xradio_sdd *pElement = (struct xradio_sdd *)hw_priv->sdd->data; + + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); + pElement = FIND_NEXT_ELT(pElement); + + while (parsedLength <= hw_priv->sdd->size) { + switch (pElement->id) { + case SDD_MAX_OUTPUT_POWER_2G4_ELT_ID: + max_output_power_2G = *((s16 *)pElement->data); + break; + case SDD_FE_COR_2G4_ELT_ID: + fe_cor_2G = *((s16 *)pElement->data); + break; + case SDD_MAX_OUTPUT_POWER_5G_ELT_ID: + max_output_power_5G = *((s16 *)(pElement->data + 4)); + break; + case SDD_FE_COR_5G_ELT_ID: + fe_cor_5G = MIN(*((s16 *)pElement->data), + *((s16 *)(pElement->data + 2)), + *((s16 *)(pElement->data + 4))); + fe_cor_5G = MIN(fe_cor_5G, + *((s16 *)(pElement->data + 6)), + *((s16 *)(pElement->data + 8))); + break; + default: + break; + } + parsedLength += (FIELD_OFFSET(struct xradio_sdd, data) + pElement->length); + pElement = FIND_NEXT_ELT(pElement); + } + + /* Max/Min Power Calculation for 2.4G */ + xradio_device_power_calc(hw_priv, max_output_power_2G, fe_cor_2G, IEEE80211_BAND_2GHZ); + /* Max/Min Power Calculation for 5G */ + xradio_device_power_calc(hw_priv, max_output_power_5G, fe_cor_5G, IEEE80211_BAND_5GHZ); + for (i = 0; i < 2; ++i) { + sta_printk(XRADIO_DBG_MSG, "Power Values Read from SDD %s:" + "min_power_level[%d]: %d max_power_level[%d]:" + "%d stepping[%d]: %d\n", __func__, i, + hw_priv->txPowerRange[i].min_power_level, i, + hw_priv->txPowerRange[i].max_power_level, i, + hw_priv->txPowerRange[i].stepping); + } + return 0; +} +#endif + +/* Internal API */ +int xradio_setup_mac(struct xradio_common *hw_priv) +{ + int ret = 0, if_id; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (hw_priv->sdd) { + struct wsm_configuration cfg = { + .dot11StationId = &hw_priv->mac_addr[0], + .dpdData = hw_priv->sdd->data, + .dpdData_size = hw_priv->sdd->size, + }; + for (if_id = 0; if_id < xrwl_get_nr_hw_ifaces(hw_priv); + if_id++) { + /* Set low-power mode. */ + ret |= SYS_WARN(wsm_configuration(hw_priv, &cfg, + if_id)); + } +#ifdef CONFIG_XRADIO_TESTMODE + /* Parse SDD file for power level test */ + xradio_test_pwrlevel(hw_priv); +#endif + /* wsm_configuration only once, so release it */ +#ifdef USE_VFS_FIRMWARE + xr_fileclose(hw_priv->sdd); +#else + release_firmware(hw_priv->sdd); +#endif + hw_priv->sdd = NULL; + } + + /* BUG:TX output power is not set untill config_xradio is called. + * This would lead to 0 power set in fw and would effect scan & p2p-find + * Setting to default value here from sdd which would be overwritten when + * we make connection to AP.This value is used only during scan & p2p-ops + * untill AP connection is made */ + /*BUG:TX output power: Hardcoding to 20dbm if CCX is not enabled*/ + /*TODO: This might change*/ + if (!hw_priv->output_power) + hw_priv->output_power=20; + sta_printk(XRADIO_DBG_MSG, "%s output power %d\n",__func__,hw_priv->output_power); + + return ret; +} + +void xradio_pending_offchanneltx_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, pending_offchanneltx_work.work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + mutex_lock(&hw_priv->conf_mutex); +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_WARN, "OFFCHAN PEND IN\n"); +#endif + xradio_disable_listening(priv); + hw_priv->roc_if_id = -1; +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_WARN, "OFFCHAN PEND OUT\n"); +#endif + up(&hw_priv->scan.lock); + mutex_unlock(&hw_priv->conf_mutex); +} + +void xradio_offchannel_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, offchannel_work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); + struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + SYS_BUG(queueId >= 4); + SYS_BUG(!hw_priv->channel); + + if (unlikely(down_trylock(&hw_priv->scan.lock))) { + int ret; + sta_printk(XRADIO_DBG_ERROR, "xradio_offchannel_work***** drop frame\n"); +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id); +#else + ret = xradio_queue_remove(queue, hw_priv->pending_frame_id); +#endif + if (ret) + sta_printk(XRADIO_DBG_ERROR, "xradio_offchannel_work: " + "queue_remove failed %d\n", ret); + wsm_unlock_tx(hw_priv); + //workaround by yangfh + LOG_FILE(1, "xradio_offchannel_work error\n"); + up(&hw_priv->scan.lock); + ieee80211_connection_loss(priv->vif); + sta_printk(XRADIO_DBG_ERROR,"lock %d\n", hw_priv->scan.lock.count); + + return; + } + mutex_lock(&hw_priv->conf_mutex); +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_WARN, "OFFCHAN WORK IN %d\n", priv->if_id); +#endif + hw_priv->roc_if_id = priv->if_id; + if (likely(!priv->join_status)) { + wsm_vif_flush_tx(priv); + xradio_enable_listening(priv, hw_priv->channel); + /* xradio_update_filtering(priv); */ + } + if (unlikely(!priv->join_status)) +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id); +#else + xradio_queue_remove(queue, hw_priv->pending_frame_id); +#endif /*CONFIG_XRADIO_TESTMODE*/ + else +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_requeue(hw_priv, queue, + hw_priv->pending_frame_id, false); +#else + xradio_queue_requeue(queue, hw_priv->pending_frame_id, false); +#endif + + queue_delayed_work(hw_priv->workqueue, + &priv->pending_offchanneltx_work, 204 * HZ/1000); +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_WARN, "OFFCHAN WORK OUT %d\n", priv->if_id); +#endif + mutex_unlock(&hw_priv->conf_mutex); + wsm_unlock_tx(hw_priv); +} + +void xradio_join_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, join_work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + u8 queueId = xradio_queue_get_queue_id(hw_priv->pending_frame_id); + struct xradio_queue *queue = &hw_priv->tx_queue[queueId]; + const struct xradio_txpriv *txpriv = NULL; + struct sk_buff *skb = NULL; + const struct wsm_tx *wsm; + const struct ieee80211_hdr *frame; + const u8 *bssid; + struct cfg80211_bss *bss; + const u8 *ssidie; + const u8 *dtimie; + const struct ieee80211_tim_ie *tim = NULL; + struct wsm_protected_mgmt_policy mgmt_policy; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + //struct wsm_reset reset = { + // .reset_statistics = true, + //}; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + + SYS_BUG(queueId >= 4); + if (xradio_queue_get_skb(queue, hw_priv->pending_frame_id, + &skb, &txpriv)) { + wsm_unlock_tx(hw_priv); + return; + } + wsm = (struct wsm_tx *)&skb->data[0]; + frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; + bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */ + + SYS_BUG(!wsm); + SYS_BUG(!hw_priv->channel); + + if (unlikely(priv->join_status)) { + sta_printk(XRADIO_DBG_WARN, "%s, pre join_status=%d.\n", + __func__, priv->join_status); + wsm_lock_tx(hw_priv); + xradio_unjoin_work(&priv->unjoin_work); + } + + cancel_delayed_work_sync(&priv->join_timeout); + + bss = cfg80211_get_bss(hw_priv->hw->wiphy, hw_priv->channel, + bssid, NULL, 0, 0, 0); + if (!bss) { +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_remove(hw_priv, queue, hw_priv->pending_frame_id); +#else + xradio_queue_remove(queue, hw_priv->pending_frame_id); +#endif /*CONFIG_XRADIO_TESTMODE*/ + wsm_unlock_tx(hw_priv); + return; + } + ssidie = cfg80211_find_ie(WLAN_EID_SSID, + bss->information_elements, + bss->len_information_elements); + dtimie = cfg80211_find_ie(WLAN_EID_TIM, + bss->information_elements, + bss->len_information_elements); + if (dtimie) + tim = (struct ieee80211_tim_ie *)&dtimie[2]; + + mutex_lock(&hw_priv->conf_mutex); + { + struct wsm_join join = { + .mode = (bss->capability & WLAN_CAPABILITY_IBSS) ? + WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS, + /* default changed to LONG, by HuangLu, fix 2/5.5/11m tx fail*/ + .preambleType = WSM_JOIN_PREAMBLE_LONG, + .probeForJoin = 1, + /* dtimPeriod will be updated after association */ + .dtimPeriod = 1, + .beaconInterval = bss->beacon_interval, + }; + + if (priv->if_id) + join.flags |= WSM_FLAG_MAC_INSTANCE_1; + else + join.flags &= ~WSM_FLAG_MAC_INSTANCE_1; + + /* BT Coex related changes */ + if (hw_priv->is_BT_Present) { + if (((hw_priv->conf_listen_interval * 100) % + bss->beacon_interval) == 0) + priv->listen_interval = + ((hw_priv->conf_listen_interval * 100) / + bss->beacon_interval); + else + priv->listen_interval = + ((hw_priv->conf_listen_interval * 100) / + bss->beacon_interval + 1); + } + + if (tim && tim->dtim_period > 1) { + join.dtimPeriod = tim->dtim_period; + priv->join_dtim_period = tim->dtim_period; + } + priv->beacon_int = bss->beacon_interval; + sta_printk(XRADIO_DBG_NIY, "Join DTIM: %d, interval: %d\n", + join.dtimPeriod, priv->beacon_int); + + hw_priv->is_go_thru_go_neg = false; + join.channelNumber = hw_priv->channel->hw_value; + + /* basicRateSet will be updated after association. + Currently these values are hardcoded */ + if (hw_priv->channel->band == IEEE80211_BAND_5GHZ) { + join.band = WSM_PHY_BAND_5G; + join.basicRateSet = 64; /*6 mbps*/ + }else{ + join.band = WSM_PHY_BAND_2_4G; + join.basicRateSet = 7; /*1, 2, 5.5 mbps*/ + } + memcpy(&join.bssid[0], bssid, sizeof(join.bssid)); + memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid)); + + if (ssidie) { + join.ssidLength = ssidie[1]; + if (SYS_WARN(join.ssidLength > sizeof(join.ssid))) + join.ssidLength = sizeof(join.ssid); + memcpy(&join.ssid[0], &ssidie[2], join.ssidLength); + if(strstr(&join.ssid[0],"5.1.4")) + msleep(200); +#ifdef ROAM_OFFLOAD + if((priv->vif->type == NL80211_IFTYPE_STATION)) { + priv->ssid_length = join.ssidLength; + memcpy(priv->ssid, &join.ssid[0], priv->ssid_length); + } +#endif /*ROAM_OFFLOAD*/ + } + + if (priv->vif->p2p) { + join.flags |= WSM_JOIN_FLAGS_P2P_GO; +#ifdef P2P_MULTIVIF + join.flags |= (1 << 6); +#endif + join.basicRateSet = + xradio_rate_mask_to_wsm(hw_priv, 0xFF0); + } + + wsm_flush_tx(hw_priv); + + /* Queue unjoin if not associated in 3 sec. */ + queue_delayed_work(hw_priv->workqueue, + &priv->join_timeout, 3 * HZ); + /*Stay Awake for Join Timeout*/ + xradio_pm_stay_awake(&hw_priv->pm_state, 3 * HZ); + + xradio_disable_listening(priv); + + //SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id)); + SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + 0, hw_priv->ba_tid_mask, priv->if_id)); + spin_lock_bh(&hw_priv->ba_lock); + hw_priv->ba_ena = false; + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_hist = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + spin_unlock_bh(&hw_priv->ba_lock); + + mgmt_policy.protectedMgmtEnable = 0; + mgmt_policy.unprotectedMgmtFramesAllowed = 1; + mgmt_policy.encryptionForAuthFrame = 1; + wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, priv->if_id); + + if (wsm_join(hw_priv, &join, priv->if_id)) { + memset(&priv->join_bssid[0], + 0, sizeof(priv->join_bssid)); +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_remove(hw_priv, queue, + hw_priv->pending_frame_id); +#else + xradio_queue_remove(queue, hw_priv->pending_frame_id); +#endif /*CONFIG_XRADIO_TESTMODE*/ + cancel_delayed_work_sync(&priv->join_timeout); + } else { + /* Upload keys */ +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_requeue(hw_priv, queue, + hw_priv->pending_frame_id, true); +#else + xradio_queue_requeue(queue, hw_priv->pending_frame_id, + true); +#endif + priv->join_status = XRADIO_JOIN_STATUS_STA; + + /* Due to beacon filtering it is possible that the + * AP's beacon is not known for the mac80211 stack. + * Disable filtering temporary to make sure the stack + * receives at least one */ + priv->disable_beacon_filter = true; + + } + xradio_update_filtering(priv); + } + mutex_unlock(&hw_priv->conf_mutex); + cfg80211_put_bss(bss); + wsm_unlock_tx(hw_priv); +} + +void xradio_join_timeout(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, join_timeout.work); + sta_printk(XRADIO_DBG_WARN, "[WSM] Issue unjoin command (TMO).\n"); + wsm_lock_tx(priv->hw_priv); + xradio_unjoin_work(&priv->unjoin_work); +} + +void xradio_unjoin_work(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, unjoin_work); + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + + struct wsm_reset reset = { + .reset_statistics = true, + }; + bool is_htcapie = false; + int i; + struct xradio_vif *tmp_priv; + struct wsm_operational_mode mode = { + .power_mode = wsm_power_mode_quiescent, + .disableMoreFlagUsage = true, + }; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + //add by yangfh. + hw_priv->connet_time[priv->if_id] = 0; +#ifdef AP_HT_COMPAT_FIX + priv->ht_compat_det &= ~1; + priv->ht_compat_cnt = 0; +#endif + + del_timer_sync(&hw_priv->ba_timer); + mutex_lock(&hw_priv->conf_mutex); + if (unlikely(atomic_read(&hw_priv->scan.in_progress))) { + if (atomic_xchg(&priv->delayed_unjoin, 1)) { + sta_printk(XRADIO_DBG_NIY, + "%s: Delayed unjoin " + "is already scheduled.\n", + __func__); + wsm_unlock_tx(hw_priv); + } + mutex_unlock(&hw_priv->conf_mutex); + return; + } + + if (priv->join_status && + priv->join_status > XRADIO_JOIN_STATUS_STA) { + sta_printk(XRADIO_DBG_ERROR, + "%s: Unexpected: join status: %d\n", + __func__, priv->join_status); + SYS_BUG(1); + } + if (priv->join_status) { + cancel_work_sync(&priv->update_filtering_work); + cancel_work_sync(&priv->set_beacon_wakeup_period_work); + memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid)); + priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; + + /* Unjoin is a reset. */ + wsm_flush_tx(hw_priv); + SYS_WARN(wsm_keep_alive_period(hw_priv, 0, priv->if_id)); + SYS_WARN(wsm_reset(hw_priv, &reset, priv->if_id)); + SYS_WARN(wsm_set_operational_mode(hw_priv, &mode, priv->if_id)); + SYS_WARN(wsm_set_output_power(hw_priv, + hw_priv->output_power * 10, priv->if_id)); + priv->join_dtim_period = 0; + priv->cipherType = 0; + SYS_WARN(xradio_setup_mac_pvif(priv)); + xradio_free_event_queue(hw_priv); + cancel_work_sync(&hw_priv->event_handler); + cancel_delayed_work_sync(&priv->connection_loss_work); + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + 0, hw_priv->ba_tid_mask, priv->if_id)); + priv->disable_beacon_filter = false; + xradio_update_filtering(priv); + priv->setbssparams_done = false; + memset(&priv->association_mode, 0, + sizeof(priv->association_mode)); + memset(&priv->bss_params, 0, sizeof(priv->bss_params)); + memset(&priv->firmware_ps_mode, 0, + sizeof(priv->firmware_ps_mode)); + priv->htcap = false; + xradio_for_each_vif(hw_priv, tmp_priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !tmp_priv) +#else + if (!tmp_priv) +#endif + continue; + if ((tmp_priv->join_status == XRADIO_JOIN_STATUS_STA) && tmp_priv->htcap) + is_htcapie = true; + } + + if (is_htcapie) { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11N_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11N_THROTTLE; + sta_printk(XRADIO_DBG_NIY, "UNJOIN HTCAP 11N %d\n",hw_priv->vif0_throttle); + } else { + hw_priv->vif0_throttle = XRWL_HOST_VIF0_11BG_THROTTLE; + hw_priv->vif1_throttle = XRWL_HOST_VIF1_11BG_THROTTLE; + sta_printk(XRADIO_DBG_NIY, "UNJOIN 11BG %d\n",hw_priv->vif0_throttle); + } + sta_printk(XRADIO_DBG_NIY, "Unjoin.\n"); + } + mutex_unlock(&hw_priv->conf_mutex); + wsm_unlock_tx(hw_priv); +} + +int xradio_enable_listening(struct xradio_vif *priv, + struct ieee80211_channel *chan) +{ + /* TODO:COMBO: Channel is common to HW currently in mac80211. + Change the code below once channel is made per VIF */ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct wsm_start start = { +#ifdef P2P_MULTIVIF + .mode = WSM_START_MODE_P2P_DEV | (priv->if_id ? (1 << 4) : 0), +#else + .mode = WSM_START_MODE_P2P_DEV | (priv->if_id << 4), +#endif + .band = (chan->band == IEEE80211_BAND_5GHZ) ? + WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G, + .channelNumber = chan->hw_value, + .beaconInterval = 100, + .DTIMPeriod = 1, + .probeDelay = 0, + .basicRateSet = 0x0F, + }; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if(priv->if_id != 2) { + SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); + return 0; + } + if (priv->join_status == XRADIO_JOIN_STATUS_MONITOR) + return 0; + if (priv->join_status == XRADIO_JOIN_STATUS_PASSIVE) + priv->join_status = XRADIO_JOIN_STATUS_MONITOR; + + SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); + + return wsm_start(hw_priv, &start, XRWL_GENERIC_IF_ID); +} + +int xradio_disable_listening(struct xradio_vif *priv) +{ + int ret; + struct wsm_reset reset = { + .reset_statistics = true, + }; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if(priv->if_id != 2) { + SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); + return 0; + } + priv->join_status = XRADIO_JOIN_STATUS_PASSIVE; + + SYS_WARN(priv->join_status > XRADIO_JOIN_STATUS_MONITOR); + + if (priv->hw_priv->roc_if_id == -1) + return 0; + + ret = wsm_reset(priv->hw_priv, &reset, XRWL_GENERIC_IF_ID); + return ret; +} + +/* TODO:COMBO:UAPSD will be supported only on one interface */ +int xradio_set_uapsd_param(struct xradio_vif *priv, + const struct wsm_edca_params *arg) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + int ret; + u16 uapsdFlags = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* Here's the mapping AC [queue, bit] + VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/ + + if (arg->params[0].uapsdEnable) + uapsdFlags |= 1 << 3; + + if (arg->params[1].uapsdEnable) + uapsdFlags |= 1 << 2; + + if (arg->params[2].uapsdEnable) + uapsdFlags |= 1 << 1; + + if (arg->params[3].uapsdEnable) + uapsdFlags |= 1; + + /* Currently pseudo U-APSD operation is not supported, so setting + * MinAutoTriggerInterval, MaxAutoTriggerInterval and + * AutoTriggerStep to 0 */ + + priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags); + priv->uapsd_info.minAutoTriggerInterval = 0; + priv->uapsd_info.maxAutoTriggerInterval = 0; + priv->uapsd_info.autoTriggerStep = 0; + + ret = wsm_set_uapsd_info(hw_priv, &priv->uapsd_info, + priv->if_id); + return ret; +} + +void xradio_ba_work(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, ba_work); + u8 tx_ba_tid_mask; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* TODO:COMBO: reenable this part of code */ +/* if (priv->join_status != XRADIO_JOIN_STATUS_STA) + return; + if (!priv->setbssparams_done) + return;*/ + + sta_printk(XRADIO_DBG_WARN, "BA work****\n"); + spin_lock_bh(&hw_priv->ba_lock); +// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0; + tx_ba_tid_mask = hw_priv->ba_tid_mask; + spin_unlock_bh(&hw_priv->ba_lock); + + wsm_lock_tx(hw_priv); + + SYS_WARN(wsm_set_block_ack_policy(hw_priv, + tx_ba_tid_mask, hw_priv->ba_tid_mask, -1)); /*TODO:COMBO*/ + + wsm_unlock_tx(hw_priv); +} + +void xradio_ba_timer(unsigned long arg) +{ + bool ba_ena; + struct xradio_common *hw_priv = (struct xradio_common *)arg; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + spin_lock_bh(&hw_priv->ba_lock); + xradio_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc, + hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx); + + if (atomic_read(&hw_priv->scan.in_progress)) { + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + goto skip_statistic_update; + } + + if (hw_priv->ba_cnt >= XRADIO_BLOCK_ACK_CNT && + (hw_priv->ba_acc / hw_priv->ba_cnt >= XRADIO_BLOCK_ACK_THLD || + (hw_priv->ba_cnt_rx >= XRADIO_BLOCK_ACK_CNT && + hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >= + XRADIO_BLOCK_ACK_THLD))) + ba_ena = true; + else + ba_ena = false; + + hw_priv->ba_cnt = 0; + hw_priv->ba_acc = 0; + hw_priv->ba_cnt_rx = 0; + hw_priv->ba_acc_rx = 0; + + if (ba_ena != hw_priv->ba_ena) { + if (ba_ena || ++hw_priv->ba_hist >= XRADIO_BLOCK_ACK_HIST) { + hw_priv->ba_ena = ba_ena; + hw_priv->ba_hist = 0; +#if 0 + sta_printk(XRADIO_DBG_NIY, "%s block ACK:\n", + ba_ena ? "enable" : "disable"); + queue_work(hw_priv->workqueue, &hw_priv->ba_work); +#endif + } + } else if (hw_priv->ba_hist) + --hw_priv->ba_hist; + +skip_statistic_update: + spin_unlock_bh(&hw_priv->ba_lock); +} + +int xradio_vif_setup(struct xradio_vif *priv) +{ + struct xradio_common *hw_priv = priv->hw_priv; + int ret = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + //reset channel change flag, yangfh 2015-5-15 17:12:14 + hw_priv->channel_changed = 0; + /* Setup per vif workitems and locks */ + spin_lock_init(&priv->vif_lock); + INIT_WORK(&priv->join_work, xradio_join_work); + INIT_DELAYED_WORK(&priv->join_timeout, xradio_join_timeout); + INIT_WORK(&priv->unjoin_work, xradio_unjoin_work); + INIT_WORK(&priv->wep_key_work, xradio_wep_key_work); + INIT_WORK(&priv->offchannel_work, xradio_offchannel_work); + INIT_DELAYED_WORK(&priv->bss_loss_work, xradio_bss_loss_work); + INIT_DELAYED_WORK(&priv->connection_loss_work, xradio_connection_loss_work); + priv->bss_loss_status = XRADIO_BSS_LOSS_NONE; + spin_lock_init(&priv->bss_loss_lock); + INIT_WORK(&priv->tx_failure_work, xradio_tx_failure_work); + spin_lock_init(&priv->ps_state_lock); + INIT_DELAYED_WORK(&priv->set_cts_work, xradio_set_cts_work); + INIT_WORK(&priv->set_tim_work, xradio_set_tim_work); + INIT_WORK(&priv->multicast_start_work, xradio_multicast_start_work); + INIT_WORK(&priv->multicast_stop_work, xradio_multicast_stop_work); + INIT_WORK(&priv->link_id_work, xradio_link_id_work); + INIT_DELAYED_WORK(&priv->link_id_gc_work, xradio_link_id_gc_work); +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + INIT_WORK(&priv->linkid_reset_work, xradio_link_id_reset); +#endif + INIT_WORK(&priv->update_filtering_work, xradio_update_filtering_work); + INIT_DELAYED_WORK(&priv->pending_offchanneltx_work, + xradio_pending_offchanneltx_work); + INIT_WORK(&priv->set_beacon_wakeup_period_work, + xradio_set_beacon_wakeup_period_work); +#ifdef AP_HT_CAP_UPDATE + INIT_WORK(&priv->ht_info_update_work, xradio_ht_info_update_work); +#endif + init_timer(&priv->mcast_timeout); + priv->mcast_timeout.data = (unsigned long)priv; + priv->mcast_timeout.function = xradio_mcast_timeout; + priv->setbssparams_done = false; + priv->power_set_true = 0; + priv->user_power_set_true = 0; + priv->user_pm_mode = 0; + SYS_WARN(xradio_debug_init_priv(hw_priv, priv)); + + /* Initialising the broadcast filter */ + memset(priv->broadcast_filter.MacAddr, 0xFF, ETH_ALEN); + priv->broadcast_filter.nummacaddr = 1; + priv->broadcast_filter.address_mode = 1; + priv->broadcast_filter.filter_mode = 1; + priv->htcap = false; +#ifdef AP_HT_COMPAT_FIX + priv->ht_compat_det = 0; + priv->ht_compat_cnt = 0; +#endif + + sta_printk(XRADIO_DBG_ALWY, "!!!%s: id=%d, type=%d, p2p=%d\n", + __func__, priv->if_id, priv->vif->type, priv->vif->p2p); + + atomic_set(&priv->enabled, 1); + +#ifdef P2P_MULTIVIF + if (priv->if_id < 2) { +#endif + /* default EDCA */ + WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007, + 47, 0xc8, false); + WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f, + 94, 0xc8, false); + +// if(priv->vif->p2p == true) { + WSM_EDCA_SET(&priv->edca, 2, 0x0002, 0x0003, 0x0007, + 0, 0xc8, false); + sta_printk(XRADIO_DBG_MSG, "EDCA params Best effort for sta/p2p is " \ + "aifs=%u, cw_min=%u, cw_max=%u \n", + priv->edca.params[2].aifns, priv->edca.params[2].cwMin, + priv->edca.params[2].cwMax); +#if 0 + }else { + WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff, + 0, 0xc8, false); + sta_printk(XRADIO_DBG_MSG, "EDCA params Best effort for sta is " \ + "aifs=%u, cw_min=%u, cw_max=%u \n", + priv->edca.params[2].aifns, priv->edca.params[2].cwMin, + priv->edca.params[2].cwMax); + } +#endif + WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff, + 0, 0xc8, false); + + ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id); + if (SYS_WARN(ret)) + goto out; + + ret = xradio_set_uapsd_param(priv, &priv->edca); + if (SYS_WARN(ret)) + goto out; + + memset(priv->bssid, ~0, ETH_ALEN); + priv->wep_default_key_id = -1; + priv->cipherType = 0; + priv->cqm_link_loss_count = XRADIO_LINK_LOSS_THOLD_DEF; + priv->cqm_beacon_loss_count = XRADIO_BSS_LOSS_THOLD_DEF; + + /* Temporary configuration - beacon filter table */ + __xradio_bf_configure(priv); +#ifdef P2P_MULTIVIF + } +#endif +out: + return ret; +} + +int xradio_setup_mac_pvif(struct xradio_vif *priv) +{ + int ret = 0; + /* NOTE: There is a bug in FW: it reports signal + * as RSSI if RSSI subscription is enabled. + * It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */ + /* NOTE2: RSSI based reports have been switched to RCPI, since + * FW has a bug and RSSI reported values are not stable, + * what can leads to signal level oscilations in user-end applications */ + struct wsm_rcpi_rssi_threshold threshold = { + .rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE | + WSM_RCPI_RSSI_DONT_USE_UPPER | + WSM_RCPI_RSSI_DONT_USE_LOWER, + .rollingAverageCount = 16, + }; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* Remember the decission here to make sure, we will handle + * the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */ + if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI) + priv->cqm_use_rssi = true; + + + /* Configure RSSI/SCPI reporting as RSSI. */ +#ifdef P2P_MULTIVIF + ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, priv->if_id ? 1 : 0); +#else + ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold, priv->if_id); +#endif + return ret; +} + +void xradio_rem_chan_timeout(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, rem_chan_timeout.work); + int ret, if_id; + struct xradio_vif *priv; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + +#ifdef TES_P2P_0002_ROC_RESTART + if(TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) { + sta_printk(XRADIO_DBG_WARN, "[Restart rem_chan_timeout:Timeout]\n"); + return; + } +#endif + + if (atomic_read(&hw_priv->remain_on_channel) == 0) { + return; + } + ieee80211_remain_on_channel_expired(hw_priv->hw); + + mutex_lock(&hw_priv->conf_mutex); + if_id = hw_priv->roc_if_id; +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_ERROR, "ROC TO IN %d\n", if_id); +#endif + priv = __xrwl_hwpriv_to_vifpriv(hw_priv, if_id); + ret = SYS_WARN(__xradio_flush(hw_priv, false, if_id)); + if (!ret) { + xradio_disable_listening(priv); + } + atomic_set(&hw_priv->remain_on_channel, 0); + hw_priv->roc_if_id = -1; + +#ifdef ROC_DEBUG + sta_printk(XRADIO_DBG_ERROR, "ROC TO OUT %d\n", if_id); +#endif + + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); +} +const u8 *xradio_get_ie(u8 *start, size_t len, u8 ie) +{ + u8 *end, *pos; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + pos = start; + if (pos == NULL) + return NULL; + end = pos + len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + +/** + * xradio_set_macaddrfilter -called when tesmode command + * is for setting mac address filter + * + * @hw: the hardware + * @data: incoming data + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_set_macaddrfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data) +{ + struct wsm_mac_addr_filter *mac_addr_filter = NULL; + struct wsm_mac_addr_info *addr_info = NULL; + u8 action_mode = 0, no_of_mac_addr = 0, i = 0; + int ret = 0; + u16 macaddrfiltersize = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* Retrieving Action Mode */ + action_mode = data[0]; + /* Retrieving number of address entries */ + no_of_mac_addr = data[1]; + + addr_info = (struct wsm_mac_addr_info *)&data[2]; + + /* Computing sizeof Mac addr filter */ + macaddrfiltersize = sizeof(*mac_addr_filter) + \ + (no_of_mac_addr * sizeof(struct wsm_mac_addr_info)); + + mac_addr_filter = xr_kzalloc(macaddrfiltersize, false); + if (!mac_addr_filter) { + ret = -ENOMEM; + goto exit_p; + } + mac_addr_filter->action_mode = action_mode; + mac_addr_filter->numfilter = no_of_mac_addr; + + for (i = 0; i < no_of_mac_addr; i++) { + mac_addr_filter->macaddrfilter[i].address_mode = \ + addr_info[i].address_mode; + memcpy(mac_addr_filter->macaddrfilter[i].MacAddr, \ + addr_info[i].MacAddr , ETH_ALEN); + mac_addr_filter->macaddrfilter[i].filter_mode = \ + addr_info[i].filter_mode; + } + ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_MAC_ADDR_FILTER, \ + mac_addr_filter, macaddrfiltersize, priv->if_id)); + + kfree(mac_addr_filter); +exit_p: + return ret; +} + +#if 0 +/** + * xradio_set_multicastaddrfilter -called when tesmode command + * is for setting the ipv4 address filter + * + * @hw: the hardware + * @data: incoming data + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_set_multicastfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data) +{ + u8 i = 0; + int ret = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + memset(&priv->multicast_filter, 0, sizeof(priv->multicast_filter)); + priv->multicast_filter.enable = (u32)data[0]; + priv->multicast_filter.numOfAddresses = (u32)data[1]; + + for (i = 0; i < priv->multicast_filter.numOfAddresses; i++) { + memcpy(&priv->multicast_filter.macAddress[i], \ + &data[2+(i*ETH_ALEN)], ETH_ALEN); + } + /* Configure the multicast mib in case of drop all multicast */ + if (priv->multicast_filter.enable != 2) + return ret; + + ret = wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, \ + &priv->multicast_filter, sizeof(priv->multicast_filter), priv->if_id); + + return ret; +} +#endif + +#ifdef IPV6_FILTERING +/** + * xradio_set_ipv6addrfilter -called when tesmode command + * is for setting the ipv6 address filter + * + * @hw: the hardware + * @data: incoming data + * @if_id: interface id + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_set_ipv6addrfilter(struct ieee80211_hw *hw, + u8 *data, int if_id) +{ + struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; + struct wsm_ipv6_filter *ipv6_filter = NULL; + struct ipv6_addr_info *ipv6_info = NULL; + u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0; + u16 ipaddrfiltersize = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* Retrieving Action Mode */ + action_mode = data[0]; + /* Retrieving number of ipv4 address entries */ + no_of_ip_addr = data[1]; + + ipv6_info = (struct ipv6_addr_info *)&data[2]; + + /* Computing sizeof Mac addr filter */ + ipaddrfiltersize = sizeof(*ipv6_filter) + \ + (no_of_ip_addr * sizeof(struct wsm_ip6_addr_info)); + + + ipv6_filter = xr_kzalloc(ipaddrfiltersize, false); + if (!ipv6_filter) { + ret = -ENOMEM; + goto exit_p; + } + ipv6_filter->action_mode = action_mode; + ipv6_filter->numfilter = no_of_ip_addr; + + for (i = 0; i < no_of_ip_addr; i++) { + ipv6_filter->ipv6filter[i].address_mode = \ + ipv6_info[i].address_mode; + ipv6_filter->ipv6filter[i].filter_mode = \ + ipv6_info[i].filter_mode; + memcpy(ipv6_filter->ipv6filter[i].ipv6, \ + (u8 *)(ipv6_info[i].ipv6), 16); + } + + ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_IP_IPV6_ADDR_FILTER, \ + ipv6_filter, ipaddrfiltersize, \ + if_id)); + + kfree(ipv6_filter); +exit_p: + return ret; +} +#endif /*IPV6_FILTERING*/ + +/** + * xradio_set_data_filter -configure data filter in device +* + * @hw: the hardware + * @vif: vif + * @data: incoming data + * @len: incoming data length + * + */ +void xradio_set_data_filter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + int ret = 0; + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + int filter_id; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (!data) { + ret = -EINVAL; + goto exit_p; + } + filter_id=*((enum xradio_data_filterid*)data); + + switch (filter_id) { +#ifdef IPV6_FILTERING + case IPV6ADDR_FILTER_ID: + ret = xradio_set_ipv6addrfilter(hw, \ + &((u8 *)data)[4], priv->if_id); + break; +#endif /*IPV6_FILTERING*/ + default: + ret = -EINVAL; + break; + } +exit_p: + + return ; +} + +/** + * xradio_set_arpreply -called for creating and + * configuring arp response template frame + * + * @hw: the hardware + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct xradio_common *hw_priv = (struct xradio_common *)hw->priv; + u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0; + bool encrypt = false; + int ret = 0; + u8 *template_frame = NULL; + struct ieee80211_hdr_3addr *dot11hdr = NULL; + struct ieee80211_snap_hdr *snaphdr = NULL; + struct arphdr *arp_hdr = NULL; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + template_frame = xr_kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, false); + if (!template_frame) { + sta_printk(XRADIO_DBG_ERROR, "Template frame memory failed\n"); + ret = -ENOMEM; + goto exit_p; + } + dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4]; + + framehdrlen = sizeof(*dot11hdr); + if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) + priv->cipherType = WLAN_CIPHER_SUITE_CCMP; + switch (priv->cipherType) { + + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + sta_printk(XRADIO_DBG_NIY, "WEP\n"); + encrypthdr = WEP_ENCRYPT_HDR_SIZE; + encrypttailsize = WEP_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + + case WLAN_CIPHER_SUITE_TKIP: + sta_printk(XRADIO_DBG_NIY, "WPA\n"); + encrypthdr = WPA_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_CCMP: + sta_printk(XRADIO_DBG_NIY, "WPA2\n"); + encrypthdr = WPA2_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_SMS4: + sta_printk(XRADIO_DBG_NIY, "WAPI\n"); + encrypthdr = WAPI_ENCRYPT_HDR_SIZE; + encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + default: + encrypthdr = 0; + encrypttailsize = 0; + encrypt = 0; + break; + } + + framehdrlen += encrypthdr; + /* Filling the 802.11 Hdr */ + dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + if (priv->vif->type == NL80211_IFTYPE_STATION) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS); + else + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + + if (encrypt) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP); + + if (priv->vif->bss_conf.qos) { + sta_printk(XRADIO_DBG_NIY, "QOS Enabled\n"); + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP); + *(u16 *)(dot11hdr + 1) = 0x0; + framehdrlen += 2; + } else { + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA); + } + + memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); + memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); + + /* Filling the LLC/SNAP Hdr */ + snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen); + memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \ + sizeof(*snaphdr)); + *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_ARP); + /* Updating the framebdylen with snaphdr and LLC hdr size */ + framebdylen = sizeof(*snaphdr) + 2; + + /* Filling the ARP Reply Payload */ + arp_hdr = (struct arphdr *)((u8 *)dot11hdr + framehdrlen + framebdylen); + arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER); + arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP); + arp_hdr->ar_hln = ETH_ALEN; + arp_hdr->ar_pln = 4; + arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY); + + /* Updating the frmbdylen with Arp Reply Hdr and Arp payload size(20) */ + framebdylen += sizeof(*arp_hdr) + 20; + + /* Updating the framebdylen with Encryption Tail Size */ + framebdylen += encrypttailsize; + + /* Filling the Template Frame Hdr */ + template_frame[0] = WSM_FRAME_TYPE_ARP_REPLY; /* Template frame type */ + template_frame[1] = 0xFF; /* Rate to be fixed */ + ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen; + + ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \ + template_frame, (framehdrlen+framebdylen+4), + priv->if_id)); + kfree(template_frame); +exit_p: + return ret; +} + +#ifdef ROAM_OFFLOAD +/** + * xradio_testmode_event -send asynchronous event + * to userspace + * + * @wiphy: the wiphy + * @msg_id: XR msg ID + * @data: data to be sent + * @len: data length + * @gfp: allocation flag + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_testmode_event(struct wiphy *wiphy, const u32 msg_id, + const void *data, int len, gfp_t gfp) +{ + struct sk_buff *skb = NULL; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + skb = cfg80211_testmode_alloc_event_skb(wiphy, + nla_total_size(len+sizeof(msg_id)), gfp); + + if (!skb) + return -ENOMEM; + + cfg80211_testmode_event(skb, gfp); + return 0; +} +#endif /*ROAM_OFFLOAD*/ + +#ifdef IPV6_FILTERING +/** + * xradio_set_na -called for creating and + * configuring NDP Neighbor Advertisement (NA) template frame + * + * @hw: the hardware + * @vif: vif + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_set_na(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct xradio_vif *priv = xrwl_get_vif_from_ieee80211(vif); + struct xradio_common *hw_priv = (struct xradio_common *)hw->priv; + u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0; + bool encrypt = false; + int ret = 0; + u8 *template_frame = NULL; + struct ieee80211_hdr_3addr *dot11hdr = NULL; + struct ieee80211_snap_hdr *snaphdr = NULL; + struct ipv6hdr *ipv6_hdr = NULL; + struct icmp6hdr *icmp6_hdr = NULL; + struct nd_msg *na = NULL; + struct nd_opt_hdr *opt_hdr = NULL; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + template_frame = xr_kzalloc(MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE, false); + if (!template_frame) { + sta_printk(XRADIO_DBG_ERROR, "Template frame memory failed\n"); + ret = -ENOMEM; + goto exit_p; + } + dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4]; + + framehdrlen = sizeof(*dot11hdr); + if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p) + priv->cipherType = WLAN_CIPHER_SUITE_CCMP; + switch (priv->cipherType) { + + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + sta_printk(XRADIO_DBG_NIY, "WEP\n"); + encrypthdr = WEP_ENCRYPT_HDR_SIZE; + encrypttailsize = WEP_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + + case WLAN_CIPHER_SUITE_TKIP: + sta_printk(XRADIO_DBG_NIY, "WPA\n"); + encrypthdr = WPA_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_CCMP: + sta_printk(XRADIO_DBG_NIY, "WPA2\n"); + encrypthdr = WPA2_ENCRYPT_HDR_SIZE; + encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + case WLAN_CIPHER_SUITE_SMS4: + sta_printk(XRADIO_DBG_NIY, "WAPI\n"); + encrypthdr = WAPI_ENCRYPT_HDR_SIZE; + encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE; + encrypt = 1; + break; + + default: + encrypthdr = 0; + encrypttailsize = 0; + encrypt = 0; + break; + } + + framehdrlen += encrypthdr; + + /* Filling the 802.11 Hdr */ + dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA); + if (priv->vif->type == NL80211_IFTYPE_STATION) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS); + else + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS); + + if (encrypt) + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP); + + if (priv->vif->bss_conf.qos) { + sta_printk(XRADIO_DBG_MSG, "QOS Enabled\n"); + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP); + /* Filling QOS Control Field */ + *(u16 *)(dot11hdr + 1) = 0x0; + framehdrlen += 2; + } else { + dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA); + } + + memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN); + memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN); + memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN); + + /* Filling the LLC/SNAP Hdr */ + snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen); + memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \ + sizeof(*snaphdr)); + *(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IPV6); + /* Updating the framebdylen with snaphdr and LLC hdr size */ + framebdylen = sizeof(*snaphdr) + 2; + + /* Filling the ipv6 header */ + ipv6_hdr = (struct ipv6hdr *)((u8 *)dot11hdr + framehdrlen + framebdylen); + ipv6_hdr->version = 6; + ipv6_hdr->priority = 0; + ipv6_hdr->payload_len = cpu_to_be16(32); /* ??? check the be or le ??? whether to use cpu_to_be16(32)*/ + ipv6_hdr->nexthdr = 58; + ipv6_hdr->hop_limit = 255; + + /* Updating the framebdylen with ipv6 Hdr */ + framebdylen += sizeof(*ipv6_hdr); + + /* Filling the Neighbor Advertisement */ + na = (struct nd_msg *)((u8 *)dot11hdr + framehdrlen + framebdylen); + icmp6_hdr = (struct icmp6hdr *)(&na->icmph); + icmp6_hdr->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT; + icmp6_hdr->icmp6_code = 0; + /* checksum (2 bytes), RSO fields (4 bytes) and target IP address (16 bytes) shall be filled by firmware */ + + /* Filling the target link layer address in the optional field */ + opt_hdr = (struct nd_opt_hdr *)(&na->opt[0]); + opt_hdr->nd_opt_type = 2; + opt_hdr->nd_opt_len = 1; + /* optional target link layer address (6 bytes) shall be filled by firmware */ + + /* Updating the framebdylen with the ipv6 payload length */ + framebdylen += 32; + + /* Updating the framebdylen with Encryption Tail Size */ + framebdylen += encrypttailsize; + + /* Filling the Template Frame Hdr */ + template_frame[0] = WSM_FRAME_TYPE_NA; /* Template frame type */ + template_frame[1] = 0xFF; /* Rate to be fixed */ + ((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen; + + ret = SYS_WARN(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \ + template_frame, (framehdrlen+framebdylen+4), \ + priv->if_id)); + + kfree(template_frame); + +exit_p: + return ret; +} +#endif /*IPV6_FILTERING*/ + +#ifdef CONFIG_XRADIO_TESTMODE +/** + * xradio_set_snap_frame -Set SNAP frame format + * + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_set_snap_frame(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct xr_msg_set_snap_frame *snap_frame = + (struct xr_msg_set_snap_frame *) data; + struct xradio_common *priv = (struct xradio_common *) hw->priv; + u8 frame_len = snap_frame->len; + u8 *frame = &snap_frame->frame[0]; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* + * Check length of incoming frame format: + * SNAP + SNAP_LEN (u8) + */ + if (frame_len + sizeof(snap_frame->len) != len) + return -EINVAL; + + if (frame_len > 0) { + priv->test_frame.data = (u8 *) xr_krealloc(priv->test_frame.data, + sizeof(u8) * frame_len, false); + if (priv->test_frame.data == NULL) { + sta_printk(XRADIO_DBG_ERROR, "xradio_set_snap_frame memory" \ + "allocation failed"); + priv->test_frame.len = 0; + return -EINVAL; + } + memcpy(priv->test_frame.data, frame, frame_len); + } else { + kfree(priv->test_frame.data); + priv->test_frame.data = NULL; + } + priv->test_frame.len = frame_len; + return 0; +} + +#ifdef CONFIG_XRADIO_TESTMODE +/** + * xradio_set_txqueue_params -Set txqueue params after successful TSPEC negotiation + * + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_set_txqueue_params(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct xr_msg_set_txqueue_params *txqueue_params = + (struct xr_msg_set_txqueue_params *) data; + struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; + struct xradio_vif *priv; + /* Interface ID is hard coded here, as interface is not + * passed in testmode command. + * Also it is assumed here that STA will be on interface + * 0 always. + */ + + int if_id = 0; + u16 queueId = xradio_priority_to_queueId[txqueue_params->user_priority]; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + priv = xrwl_hwpriv_to_vifpriv(hw_priv, if_id); + + if (unlikely(!priv)) { + sta_printk(XRADIO_DBG_ERROR, "%s: Warning Priv is Null\n", + __func__); + return 0; + } + spin_unlock(&priv->vif_lock); + + /* Default Ack policy is WSM_ACK_POLICY_NORMAL */ + WSM_TX_QUEUE_SET(&priv->tx_queue_params, + queueId, + WSM_ACK_POLICY_NORMAL, + txqueue_params->medium_time, + txqueue_params->expiry_time); + return SYS_WARN(wsm_set_tx_queue_params(hw_priv, + &priv->tx_queue_params.params[queueId], queueId, + priv->if_id)); +} +#endif /*CONFIG_XRADIO_TESTMODE*/ + +/** + * xradio_tesmode_reply -called inside a testmode command + * handler to send a response to user space + * + * @wiphy: the wiphy + * @data: data to be send to user space + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_tesmode_reply(struct wiphy *wiphy, + const void *data, int len) +{ + int ret = 0; + struct sk_buff *skb = NULL; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + skb = cfg80211_testmode_alloc_reply_skb(wiphy, nla_total_size(len)); + + if (!skb) + return -ENOMEM; + + ret = nla_put(skb, XR_TM_MSG_DATA, len, data); + if (ret) { + kfree_skb(skb); + return ret; + } + + return cfg80211_testmode_reply(skb); +} + +/** + * xradio_tesmode_event -send asynchronous event + * to userspace + * + * @wiphy: the wiphy + * @msg_id: XR msg ID + * @data: data to be sent + * @len: data length + * @gfp: allocation flag + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_tesmode_event(struct wiphy *wiphy, const u32 msg_id, + const void *data, int len, gfp_t gfp) +{ + struct sk_buff *skb = NULL; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + skb = cfg80211_testmode_alloc_event_skb(wiphy, + nla_total_size(len+sizeof(msg_id)), gfp); + if (!skb) + return -ENOMEM; + + NLA_PUT_U32(skb, XR_TM_MSG_ID, msg_id); + if (data) + NLA_PUT(skb, XR_TM_MSG_DATA, len, data); + + cfg80211_testmode_event(skb, gfp); + return 0; +nla_put_failure: + kfree_skb(skb); + return -ENOBUFS; +} + +/** + * example function for test purposes + * sends both: synchronous reply and asynchronous event + */ +static int xradio_test(struct ieee80211_hw *hw, + void *data, int len) +{ + struct xr_msg_test_t *test_p; + struct xr_reply_test_t reply; + struct xr_event_test_t event; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (sizeof(struct xr_msg_test_t) != len) + return -EINVAL; + + test_p = (struct xr_msg_test_t *) data; + + reply.dummy = test_p->dummy + 10; + + event.dummy = test_p->dummy + 20; + + if (xradio_tesmode_event(hw->wiphy, XR_MSG_EVENT_TEST, + &event, sizeof(event), GFP_KERNEL)) + return -1; + + return xradio_tesmode_reply(hw->wiphy, &reply, sizeof(reply)); +} + +/** + * xradio_get_tx_power_level - send tx power level + * to userspace + * + * @hw: the hardware + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_get_tx_power_level(struct ieee80211_hw *hw) +{ + struct xradio_common *hw_priv = hw->priv; + int get_power = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + get_power = hw_priv->output_power; + sta_printk(XRADIO_DBG_MSG, "%s: Power set on Device : %d", + __func__, get_power); + return xradio_tesmode_reply(hw->wiphy, &get_power, sizeof(get_power)); +} + +/** + * xradio_get_tx_power_range- send tx power range + * to userspace for each band + * + * @hw: the hardware + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_get_tx_power_range(struct ieee80211_hw *hw) +{ + struct xradio_common *hw_priv = hw->priv; + struct wsm_tx_power_range txPowerRange[2]; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + size_t len = sizeof(txPowerRange); + memcpy(txPowerRange, hw_priv->txPowerRange, len); + return xradio_tesmode_reply(hw->wiphy, txPowerRange, len); +} + +/** + * xradio_set_advance_scan_elems -Set Advcance Scan + * elements + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_set_advance_scan_elems(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct advance_scan_elems *scan_elems = + (struct advance_scan_elems *) data; + struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; + size_t elems_len = sizeof(struct advance_scan_elems); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (elems_len != len) + return -EINVAL; + + scan_elems = (struct advance_scan_elems *) data; + + /* Locks required to prevent simultaneous scan */ + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + + hw_priv->advanceScanElems.scanMode = scan_elems->scanMode; + hw_priv->advanceScanElems.duration = scan_elems->duration; + hw_priv->enable_advance_scan = true; + + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); + + return 0; +} + +/** + * xradio_set_power_save -Set Power Save + * elements + * @hw: the hardware + * @data: data frame + * @len: data length + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_set_power_save(struct ieee80211_hw *hw, + u8 *data, int len) +{ + struct power_save_elems *ps_elems = + (struct power_save_elems *) data; + struct xradio_common *hw_priv = (struct xradio_common *) hw->priv; + size_t elems_len = sizeof(struct power_save_elems); + struct xradio_vif *priv; + int if_id = 0; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /* Interface ID is hard coded here, as interface is not + * passed in testmode command. + * Also it is assumed here that STA will be on interface + * 0 always. */ + + if (elems_len != len) + return -EINVAL; + + priv = xrwl_hwpriv_to_vifpriv(hw_priv, if_id); + + if (unlikely(!priv)) { + sta_printk(XRADIO_DBG_ERROR, "%s: Warning Priv is Null\n", + __func__); + return 0; + } + + spin_unlock(&priv->vif_lock); + mutex_lock(&hw_priv->conf_mutex); + + ps_elems = (struct power_save_elems *) data; + + if (ps_elems->powerSave == 1) + priv->user_pm_mode = WSM_PSM_PS; + else + priv->user_pm_mode = WSM_PSM_FAST_PS; + + sta_printk(XRADIO_DBG_MSG, "Aid: %d, Joined: %s, Powersave: %s\n", + priv->bss_params.aid, + priv->join_status == XRADIO_JOIN_STATUS_STA ? "yes" : "no", + priv->user_pm_mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" : + priv->user_pm_mode == WSM_PSM_PS ? "WSM_PSM_PS" : + priv->user_pm_mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" : "UNKNOWN"); + if (priv->join_status == XRADIO_JOIN_STATUS_STA && + priv->bss_params.aid && + priv->setbssparams_done && + priv->filter4.enable) { + priv->powersave_mode.pmMode = priv->user_pm_mode; + xradio_set_pm(priv, &priv->powersave_mode); + } + else + priv->user_power_set_true = ps_elems->powerSave; + mutex_unlock(&hw_priv->conf_mutex); + return 0; +} +/** + * xradio_start_stop_tsm - starts/stops collecting TSM + * + * @hw: the hardware + * @data: data frame + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_start_stop_tsm(struct ieee80211_hw *hw, void *data) +{ + struct xr_msg_start_stop_tsm *start_stop_tsm = + (struct xr_msg_start_stop_tsm *) data; + struct xradio_common *hw_priv = hw->priv; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + hw_priv->start_stop_tsm.start = start_stop_tsm->start; + hw_priv->start_stop_tsm.up = start_stop_tsm->up; + hw_priv->start_stop_tsm.packetization_delay = + start_stop_tsm->packetization_delay; + sta_printk(XRADIO_DBG_MSG, "%s: start : %u: up : %u", + __func__, hw_priv->start_stop_tsm.start, + hw_priv->start_stop_tsm.up); + hw_priv->tsm_info.ac = xradio_1d_to_ac[start_stop_tsm->up]; + + if (!hw_priv->start_stop_tsm.start) { + spin_lock_bh(&hw_priv->tsm_lock); + memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats)); + memset(&hw_priv->tsm_info, 0, sizeof(hw_priv->tsm_info)); + spin_unlock_bh(&hw_priv->tsm_lock); + } + return 0; +} + +/** + * xradio_get_tsm_params - Retrieves TSM parameters + * + * @hw: the hardware + * + * Returns: TSM parameters collected + */ +int xradio_get_tsm_params(struct ieee80211_hw *hw) +{ + struct xradio_common *hw_priv = hw->priv; + struct xr_tsm_stats tsm_stats; + u32 pkt_count; + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + spin_lock_bh(&hw_priv->tsm_lock); + pkt_count = hw_priv->tsm_stats.txed_msdu_count - + hw_priv->tsm_stats.msdu_discarded_count; + if (pkt_count) { + hw_priv->tsm_stats.avg_q_delay = + hw_priv->tsm_info.sum_pkt_q_delay/(pkt_count * 1000); + hw_priv->tsm_stats.avg_transmit_delay = + hw_priv->tsm_info.sum_media_delay/pkt_count; + } else { + hw_priv->tsm_stats.avg_q_delay = 0; + hw_priv->tsm_stats.avg_transmit_delay = 0; + } + sta_printk(XRADIO_DBG_MSG, "%s: Txed MSDU count : %u", + __func__, hw_priv->tsm_stats.txed_msdu_count); + sta_printk(XRADIO_DBG_MSG, "%s: Average queue delay : %u", + __func__, hw_priv->tsm_stats.avg_q_delay); + sta_printk(XRADIO_DBG_MSG, "%s: Average transmit delay : %u", + __func__, hw_priv->tsm_stats.avg_transmit_delay); + memcpy(&tsm_stats, &hw_priv->tsm_stats, sizeof(hw_priv->tsm_stats)); + /* Reset the TSM statistics */ + memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats)); + hw_priv->tsm_info.sum_pkt_q_delay = 0; + hw_priv->tsm_info.sum_media_delay = 0; + spin_unlock_bh(&hw_priv->tsm_lock); + return xradio_tesmode_reply(hw->wiphy, &tsm_stats, + sizeof(hw_priv->tsm_stats)); +} + +/** + * xradio_get_roam_delay - Retrieves roam delay + * + * @hw: the hardware + * + * Returns: Returns the last measured roam delay + */ +int xradio_get_roam_delay(struct ieee80211_hw *hw) +{ + struct xradio_common *hw_priv = hw->priv; + u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000; + sta_printk(XRADIO_DBG_MSG, "%s: Roam delay : %u", + __func__, roam_delay); + + spin_lock_bh(&hw_priv->tsm_lock); + hw_priv->tsm_info.roam_delay = 0; + hw_priv->tsm_info.use_rx_roaming = 0; + spin_unlock_bh(&hw_priv->tsm_lock); + return xradio_tesmode_reply(hw->wiphy, &roam_delay, sizeof(u16)); +} + +/** + * xradio_testmode_cmd -called when tesmode command + * reaches xradio + * + * @hw: the hardware + * @data: incoming data + * @len: incoming data length + * + * Returns: 0 on success or non zero value on failure + */ +int xradio_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) +{ + int ret = 0; + struct nlattr *type_p = nla_find(data, len, XR_TM_MSG_ID); + struct nlattr *data_p = nla_find(data, len, XR_TM_MSG_DATA); + sta_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (!type_p || !data_p) + return -EINVAL; + + sta_printk(XRADIO_DBG_MSG, "%s: type: %i", + __func__, nla_get_u32(type_p)); + + switch (nla_get_u32(type_p)) { + case XR_MSG_TEST: + ret = xradio_test(hw, + nla_data(data_p), nla_len(data_p)); + break; + case XR_MSG_SET_SNAP_FRAME: + ret = xradio_set_snap_frame(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case XR_MSG_GET_TX_POWER_LEVEL: + ret = xradio_get_tx_power_level(hw); + break; + case XR_MSG_GET_TX_POWER_RANGE: + ret = xradio_get_tx_power_range(hw); + break; + case XR_MSG_SET_ADVANCE_SCAN_ELEMS: + ret = xradio_set_advance_scan_elems(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case XR_MSG_SET_TX_QUEUE_PARAMS: + ret = xradio_set_txqueue_params(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + case XR_MSG_GET_TSM_PARAMS: + ret = xradio_get_tsm_params(hw); + break; + case XR_MSG_START_STOP_TSM: + ret = xradio_start_stop_tsm(hw, (u8 *) nla_data(data_p)); + break; + case XR_MSG_GET_ROAM_DELAY: + ret = xradio_get_roam_delay(hw); + break; + case XR_MSG_SET_POWER_SAVE: + ret = xradio_set_power_save(hw, (u8 *) nla_data(data_p), + nla_len(data_p)); + break; + default: + break; + } + return ret; +} +#endif /* CONFIG_XRADIO_TESTMODE */ diff --git a/drivers/net/wireless/xradio/sta.h b/drivers/net/wireless/xradio/sta.h new file mode 100644 index 0000000..8e48923 --- /dev/null +++ b/drivers/net/wireless/xradio/sta.h @@ -0,0 +1,137 @@ +/* + * sta interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef STA_H_INCLUDED +#define STA_H_INCLUDED + + +#ifdef XRADIO_USE_LONG_KEEP_ALIVE_PERIOD +#define XRADIO_KEEP_ALIVE_PERIOD (28) +#else +/*For Samsung, it is defined as 4*/ +#define XRADIO_KEEP_ALIVE_PERIOD (4) +#endif + +#ifdef XRADIO_USE_LONG_DTIM_PERIOD +#define XRADIO_BSS_LOSS_THOLD_DEF 30 +#define XRADIO_LINK_LOSS_THOLD_DEF 50 +#else +#define XRADIO_BSS_LOSS_THOLD_DEF 20 +#define XRADIO_LINK_LOSS_THOLD_DEF 40 +#endif + +/* ******************************************************************** */ +/* mac80211 API */ + +int xradio_start(struct ieee80211_hw *dev); +void xradio_stop(struct ieee80211_hw *dev); +int xradio_add_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif); +void xradio_remove_interface(struct ieee80211_hw *dev, struct ieee80211_vif *vif); +int xradio_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); +int xradio_config(struct ieee80211_hw *dev, u32 changed); +int xradio_change_interface(struct ieee80211_hw *dev, + struct ieee80211_vif *vif, + enum nl80211_iftype new_type, + bool p2p); +void xradio_configure_filter(struct ieee80211_hw *dev, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int xradio_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif, + u16 queue, const struct ieee80211_tx_queue_params *params); +int xradio_get_stats(struct ieee80211_hw *dev, + struct ieee80211_low_level_stats *stats); +/* Not more a part of interface? +int xradio_get_tx_stats(struct ieee80211_hw *dev, + struct ieee80211_tx_queue_stats *stats); +*/ +int xradio_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int xradio_set_rts_threshold(struct ieee80211_hw *hw, u32 value); + +void xradio_flush(struct ieee80211_hw *hw, bool drop); + + +int xradio_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + int duration); +int xradio_cancel_remain_on_channel(struct ieee80211_hw *hw); +int xradio_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +u64 xradio_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list); +int xradio_set_pm(struct xradio_vif *priv, const struct wsm_set_pm *arg); +void xradio_set_data_filter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, + int len); + +/* ******************************************************************** */ +/* WSM callbacks */ + +/* void xradio_set_pm_complete_cb(struct xradio_common *hw_priv, + struct wsm_set_pm_complete *arg); */ +void xradio_channel_switch_cb(struct xradio_common *hw_priv); + +/* ******************************************************************** */ +/* WSM events */ + +void xradio_free_event_queue(struct xradio_common *hw_priv); +void xradio_event_handler(struct work_struct *work); +void xradio_bss_loss_work(struct work_struct *work); +void xradio_connection_loss_work(struct work_struct *work); +void xradio_keep_alive_work(struct work_struct *work); +void xradio_tx_failure_work(struct work_struct *work); + +/* ******************************************************************** */ +/* Internal API */ + +int xradio_setup_mac(struct xradio_common *hw_priv); +void xradio_join_work(struct work_struct *work); +void xradio_join_timeout(struct work_struct *work); +void xradio_unjoin_work(struct work_struct *work); +void xradio_offchannel_work(struct work_struct *work); +void xradio_wep_key_work(struct work_struct *work); +void xradio_update_filtering(struct xradio_vif *priv); +void xradio_update_filtering_work(struct work_struct *work); +int __xradio_flush(struct xradio_common *hw_priv, bool drop, int if_id); +void xradio_set_beacon_wakeup_period_work(struct work_struct *work); +int xradio_enable_listening(struct xradio_vif *priv, struct ieee80211_channel *chan); +int xradio_disable_listening(struct xradio_vif *priv); +int xradio_set_uapsd_param(struct xradio_vif *priv, const struct wsm_edca_params *arg); +void xradio_ba_work(struct work_struct *work); +void xradio_ba_timer(unsigned long arg); +const u8 *xradio_get_ie(u8 *start, size_t len, u8 ie); +int xradio_vif_setup(struct xradio_vif *priv); +int xradio_setup_mac_pvif(struct xradio_vif *priv); +void xradio_iterate_vifs(void *data, u8 *mac, struct ieee80211_vif *vif); +void xradio_rem_chan_timeout(struct work_struct *work); +int xradio_set_macaddrfilter(struct xradio_common *hw_priv, struct xradio_vif *priv, u8 *data); +#ifdef ROAM_OFFLOAD +int xradio_testmode_event(struct wiphy *wiphy, const u32 msg_id, + const void *data, int len, gfp_t gfp); +#endif /*ROAM_OFFLOAD*/ +#ifdef IPV6_FILTERING +int xradio_set_na(struct ieee80211_hw *hw, struct ieee80211_vif *vif); +#endif /*IPV6_FILTERING*/ +#ifdef CONFIG_XRADIO_TESTMODE +void xradio_device_power_calc(struct xradio_common *priv, + s16 max_output_power, s16 fe_cor, u32 band); +int xradio_testmode_cmd(struct ieee80211_hw *hw, void *data, int len); +int xradio_tesmode_event(struct wiphy *wiphy, const u32 msg_id, + const void *data, int len, gfp_t gfp); +int xradio_get_tx_power_range(struct ieee80211_hw *hw); +int xradio_get_tx_power_level(struct ieee80211_hw *hw); +#endif /* CONFIG_XRADIO_TESTMODE */ +#endif diff --git a/drivers/net/wireless/xradio/txrx.c b/drivers/net/wireless/xradio/txrx.c new file mode 100644 index 0000000..a89a829 --- /dev/null +++ b/drivers/net/wireless/xradio/txrx.c @@ -0,0 +1,2470 @@ +/* + * Datapath implementation for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "xradio.h" +#include "wsm.h" +#include "bh.h" +#include "ap.h" +#include "sta.h" +#include "sbus.h" + +#define B_RATE_INDEX 0 //11b rate for important short frames in 2.4G. +#define AG_RATE_INDEX 6 //11a/g rate for important short frames in 5G. +#define XRADIO_INVALID_RATE_ID (0xFF) + +/* rate should fall quickly to avoid dropping frames by aps. + * Add by yangfh 2014-9-22 13:39:57 + */ +#define HIGH_RATE_MAX_RETRY 7 + +#ifdef CONFIG_XRADIO_TESTMODE +#include "nl80211_testmode_msg_copy.h" +#endif /* CONFIG_XRADIO_TESTMODE */ +#ifdef TES_P2P_0002_ROC_RESTART +#include +#endif +static const struct ieee80211_rate * +xradio_get_tx_rate(const struct xradio_common *hw_priv, + const struct ieee80211_tx_rate *rate); + +u32 TxedRateIdx_Map[24] = {0}; +u32 RxedRateIdx_Map[24] = {0}; + +#ifdef CONFIG_XRADIO_DEBUGFS +//add by yangfh for tx rates debug. +extern u8 rates_dbg_en; +extern u32 rates_debug[3]; +extern u8 maxRate_dbg; +extern u8 retry_dbg; +#endif +/* ******************************************************************** */ +/* TX policy cache implementation */ + +static void tx_policy_dump(struct tx_policy *policy) +{ + txrx_printk(XRADIO_DBG_MSG, "[TX policy] " + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X" + "%.1X%.1X%.1X%.1X%.1X%.1X%.1X%.1X: %d\n", + policy->raw[0] & 0x0F, policy->raw[0] >> 4, + policy->raw[1] & 0x0F, policy->raw[1] >> 4, + policy->raw[2] & 0x0F, policy->raw[2] >> 4, + policy->raw[3] & 0x0F, policy->raw[3] >> 4, + policy->raw[4] & 0x0F, policy->raw[4] >> 4, + policy->raw[5] & 0x0F, policy->raw[5] >> 4, + policy->raw[6] & 0x0F, policy->raw[6] >> 4, + policy->raw[7] & 0x0F, policy->raw[7] >> 4, + policy->raw[8] & 0x0F, policy->raw[8] >> 4, + policy->raw[9] & 0x0F, policy->raw[9] >> 4, + policy->raw[10] & 0x0F, policy->raw[10] >> 4, + policy->raw[11] & 0x0F, policy->raw[11] >> 4, + policy->defined); +} + +static void xradio_check_go_neg_conf_success(struct xradio_common *hw_priv, + u8 *action) +{ + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A && + action[5] == 0x09 && action[6] == 0x02) { + if(action[17] == 0) { + hw_priv->is_go_thru_go_neg = true; + } + else { + hw_priv->is_go_thru_go_neg = false; + } + } +} + +#ifdef TES_P2P_0002_ROC_RESTART +///w, TES_P2P_0002 WorkAround: +///w, P2P GO Neg Process and P2P FIND may be collision. +///w, When P2P Device is waiting for GO NEG CFM in 30ms, +///w, P2P FIND may end with p2p listen, and then goes to p2p search. +///w, Then xradio scan will occupy phy on other channel in 3+ seconds. +///w, P2P Device will not be able to receive the GO NEG CFM. +///w, We extend the roc period to remaind phy to receive GO NEG CFM as WorkAround. + +s32 TES_P2P_0002_roc_dur; +s32 TES_P2P_0002_roc_sec; +s32 TES_P2P_0002_roc_usec; +u32 TES_P2P_0002_packet_id; +u32 TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; + +static void xradio_frame_monitor(struct xradio_common *hw_priv, struct sk_buff *skb, bool tx) { + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + + u8 *action = (u8*)&mgmt->u.action.category; + u8 *category_code = &(action[0]); + u8 *action_code = &(action[1]); + u8 *oui = &(action[2]); + u8 *subtype = &(action[5]); + u8 *oui_subtype = &(action[6]); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if(ieee80211_is_action(frame->frame_control)) { + if( *category_code == WLAN_CATEGORY_PUBLIC) { + if (*action_code == 0x09) { + if((oui[0] == 0x50) && (oui[1] == 0x6F) && + (oui[2] == 0x9A) && (*subtype == 0x09)) { + if ( *oui_subtype == 0x01 ) { ///w, GO Negotiation Response + if((TES_P2P_0002_state == TES_P2P_0002_STATE_IDLE) && + (tx == true)) { ///w, p2p atturbute:status,id=0 + u8 *go_neg_resp_res = &(action[17]); + if (*go_neg_resp_res == 0x0) { + TES_P2P_0002_state = TES_P2P_0002_STATE_SEND_RESP; + txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_SEND_RESP]\n"); + } + } + } else if ( *oui_subtype == 0x02 ) { ///w, GO Negotiation Confirmation + if( tx == false ) { + TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; + txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE]" + "[GO Negotiation Confirmation]\n"); + } + } else if ( *oui_subtype == 0x08 ) { ///w, Provision Discovery Response + if(tx == false) { + TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; + txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE]" + "[Provision Discovery Response]\n"); + } + } + } + } + } + } +} +#endif + +static void xradio_check_prov_desc_req(struct xradio_common *hw_priv, + u8 *action) +{ + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + if (action[2] == 0x50 && action[3] == 0x6F && action[4] == 0x9A && + action[5] == 0x09 && action[6] == 0x07) { + hw_priv->is_go_thru_go_neg = false; + } +} + +#ifdef AP_HT_COMPAT_FIX +#define AP_COMPAT_THRESHOLD 2000 +#define AP_COMPAT_MIN_CNT 200 +u8 ap_compat_bssid[ETH_ALEN] = {0}; +static int xradio_apcompat_detect(struct xradio_vif *priv, u8 rx_rate) +{ + if (rx_rate < AG_RATE_INDEX) { + priv->ht_compat_cnt++; + txrx_printk(XRADIO_DBG_MSG,"%s:rate=%d.\n", __func__, rx_rate); + } else { + priv->ht_compat_det |= 1; + priv->ht_compat_cnt = 0; + txrx_printk(XRADIO_DBG_NIY,"%s:HT compat detect\n", __func__); + return 0; + } + + /* Enhance compatibility with some illegal APs.*/ + if (priv->ht_compat_cnt > AP_COMPAT_THRESHOLD || + (priv->ht_compat_cnt > AP_COMPAT_MIN_CNT && + priv->bssid[0] == 0xC8 && + priv->bssid[1] == 0x3A && + priv->bssid[2] == 0x35)) { + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + memcpy(ap_compat_bssid, priv->bssid, ETH_ALEN); + wms_send_disassoc_to_self(hw_priv, priv); + txrx_printk(XRADIO_DBG_WARN, "%s:SSID=%s, BSSID=" \ + "%02x:%02x:%02x:%02x:%02x:%02x\n", __func__, priv->ssid, + ap_compat_bssid[0], ap_compat_bssid[1], + ap_compat_bssid[2], ap_compat_bssid[3], + ap_compat_bssid[4], ap_compat_bssid[5]); + return 1; + } + return 0; +} + +static void xradio_remove_ht_ie(struct xradio_vif *priv, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + u8 *ies = NULL; + size_t ies_len = 0; + u8 *ht_ie = NULL; + + if (!mgmt || memcmp(ap_compat_bssid, mgmt->bssid, ETH_ALEN)) + return; + + if (ieee80211_is_probe_resp(mgmt->frame_control)) + ies = mgmt->u.probe_resp.variable; + else if (ieee80211_is_beacon(mgmt->frame_control)) + ies = mgmt->u.beacon.variable; + else if (ieee80211_is_assoc_resp(mgmt->frame_control)) + ies = mgmt->u.assoc_resp.variable; + else if (ieee80211_is_assoc_req(mgmt->frame_control)) + ies = mgmt->u.assoc_req.variable; + else + return; + + ies_len = skb->len - (ies - (u8 *)(skb->data)); + ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY); + if (ht_ie) { + u8 ht_len = *(ht_ie + 1) + 2; + u8 move_len = (ies + ies_len) - (ht_ie + ht_len); + memmove(ht_ie, (ht_ie + ht_len), move_len); + skb_trim(skb, skb->len - ht_len); + ies_len = skb->len - (ies - (u8 *)(skb->data)); + ht_ie = (u8 *)xradio_get_ie(ies, ies_len, WLAN_EID_HT_INFORMATION); + if (ht_ie) { + ht_len = *(ht_ie + 1) + 2; + move_len = (ies + ies_len) - (ht_ie + ht_len); + memmove(ht_ie, (ht_ie + ht_len), move_len); + skb_trim(skb, skb->len - ht_len); + } + } + txrx_printk(XRADIO_DBG_WARN, "%s: BSSID=%02x:%02x:%02x:%02x:%02x:%02x\n", + __func__, + mgmt->bssid[0], mgmt->bssid[1], + mgmt->bssid[2], mgmt->bssid[3], + mgmt->bssid[4], mgmt->bssid[5]); +} +#endif //AP_HT_COMPAT_FIX + +//modified by yangfh +static void tx_policy_build(const struct xradio_common *hw_priv, + /* [out] */ struct tx_policy *policy, + struct ieee80211_tx_rate *rates, size_t count) +{ + int i, j; + struct ieee80211_rate * tmp_rate = NULL; + unsigned limit = hw_priv->short_frame_max_tx_count; + unsigned max_rates_cnt = count; + unsigned total = 0; + SYS_BUG(rates[0].idx < 0); + memset(policy, 0, sizeof(*policy)); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + txrx_printk(XRADIO_DBG_NIY,"============================"); +#if 0 + //debug yangfh + for (i = 0; i < count; ++i) { + if(rates[i].idx>=0) { + tmp_rate = xradio_get_tx_rate(hw_priv, &rates[i]); + txrx_printk(XRADIO_DBG_NIY,"[TX policy] Org %d.%dMps=%d", + tmp_rate->bitrate/10, tmp_rate->bitrate%10, rates[i].count); + } + } + txrx_printk(XRADIO_DBG_NIY,"----------------------------"); +#endif + + /* minstrel is buggy a little bit, so distille + * incoming rates first. + */ + /* Sort rates in descending order. */ + total = rates[0].count; + for (i = 1; i < count; ++i) { + if (rates[i].idx > rates[i-1].idx) { + rates[i].idx = rates[i-1].idx>0?(rates[i-1].idx-1):-1; + } + if (rates[i].idx < 0 || i>=limit) { + count = i; + break; + } else { + total += rates[i].count; + } + } + + /* Add lowest rate to the end when 11a/n. + * Don't apply in 11b/g because p2p unsupport 1Mbps. + * TODO: it's better to do this in rate control of mac80211. + */ + if (((rates[0].flags & IEEE80211_TX_RC_MCS) || + hw_priv->channel->band == IEEE80211_BAND_5GHZ) && + count < max_rates_cnt && rates[count-1].idx != 0) { + rates[count].idx = 0; + rates[count].count = rates[0].count; + rates[count].flags = rates[0].flags; + total += rates[count].count; + count++; + } + + /* adjust tx count to limit, rates should fall quickly + * and lower rates should be more retry, because reorder + * buffer of reciever will be timeout and clear probably. + */ + if (count < 2) { + rates[0].count = limit; + total = limit; + } else { + u8 end_retry = 0; //the retry should be add to last rate. + if (limit > HIGH_RATE_MAX_RETRY) { + end_retry = limit - HIGH_RATE_MAX_RETRY; + limit = HIGH_RATE_MAX_RETRY; + } + for (i = 0; (limit != total) && (i < 100); ++i) { //i<100 to avoid dead loop + j = i % count; + if(limit < total) { + total += (rates[j].count > 1? -1 : 0); + rates[j].count += (rates[j].count > 1? -1 : 0); + } else { + j = count - 1 - j; + if (rates[j].count > 0) { + total++; + rates[j].count++; + } + } + } + if (end_retry) { + rates[count-1].count += end_retry; + limit += end_retry; + } + } + + /* Eliminate duplicates. */ + total = rates[0].count; + for (i = 0, j = 1; j < count; ++j) { + if (rates[j].idx == rates[i].idx) { + rates[i].count += rates[j].count; + } else if (rates[j].idx > rates[i].idx) { + break; + } else { + ++i; + if (i != j) + rates[i] = rates[j]; + } + total += rates[j].count; + } + count = i + 1; + + /* Re-fill policy trying to keep every requested rate and with + * respect to the global max tx retransmission count. + */ + if (limit < count) + limit = count; + if (total > limit) { + for (i = 0; i < count; ++i) { + int left = count - i - 1; + if (rates[i].count > limit - left) + rates[i].count = limit - left; + limit -= rates[i].count; + } + } + + /* HACK!!! Device has problems (at least) switching from + * 54Mbps CTS to 1Mbps. This switch takes enormous amount + * of time (100-200 ms), leading to valuable throughput drop. + * As a workaround, additional g-rates are injected to the + * policy. + */ + if (count == 2 && !(rates[0].flags & IEEE80211_TX_RC_MCS) && + rates[0].idx > 4 && rates[0].count > 2 && + rates[1].idx < 2) { + /* ">> 1" is an equivalent of "/ 2", but faster */ + int mid_rate = (rates[0].idx + 4) >> 1; + + /* Decrease number of retries for the initial rate */ + rates[0].count -= 2; + + if (mid_rate != 4) { + /* Keep fallback rate at 1Mbps. */ + rates[3] = rates[1]; + + /* Inject 1 transmission on lowest g-rate */ + rates[2].idx = 4; + rates[2].count = 1; + rates[2].flags = rates[1].flags; + + /* Inject 1 transmission on mid-rate */ + rates[1].idx = mid_rate; + rates[1].count = 1; + + /* Fallback to 1 Mbps is a really bad thing, + * so let's try to increase probability of + * successful transmission on the lowest g rate + * even more */ + if (rates[0].count >= 3) { + --rates[0].count; + ++rates[2].count; + } + + /* Adjust amount of rates defined */ + count += 2; + } else { + /* Keep fallback rate at 1Mbps. */ + rates[2] = rates[1]; + + /* Inject 2 transmissions on lowest g-rate */ + rates[1].idx = 4; + rates[1].count = 2; + + /* Adjust amount of rates defined */ + count += 1; + } + } + + tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[0]); + if(tmp_rate) + policy->defined = tmp_rate->hw_value + 1; + + for (i = 0; i < count; ++i) { + register unsigned rateid, off, shift, retries; + + tmp_rate = (struct ieee80211_rate *)xradio_get_tx_rate(hw_priv, &rates[i]); + if(tmp_rate) { + rateid = tmp_rate->hw_value; + } else { + break; + } + off = rateid >> 3; /* eq. rateid / 8 */ + shift = (rateid & 0x07) << 2; /* eq. (rateid % 8) * 4 */ + + retries = rates[i].count; + if (unlikely(retries > 0x0F)) + rates[i].count = retries = 0x0F; + policy->tbl[off] |= __cpu_to_le32(retries << shift); + policy->retry_count += retries; + txrx_printk(XRADIO_DBG_NIY,"[TX policy] %d.%dMps=%d", + tmp_rate->bitrate/10, tmp_rate->bitrate%10, retries); + } + + txrx_printk(XRADIO_DBG_MSG, "[TX policy] Dst Policy (%d): " \ + "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + count, + rates[0].idx, rates[0].count, + rates[1].idx, rates[1].count, + rates[2].idx, rates[2].count, + rates[3].idx, rates[3].count, + rates[4].idx, rates[4].count); +} + +static inline bool tx_policy_is_equal(const struct tx_policy *wanted, + const struct tx_policy *cached) +{ + size_t count = wanted->defined >> 1; + + if (wanted->defined > cached->defined) + return false; + if (count) { + if (memcmp(wanted->raw, cached->raw, count)) + return false; + } + if (wanted->defined & 1) { + if ((wanted->raw[count] & 0x0F) != (cached->raw[count] & 0x0F)) + return false; + } + return true; +} + +static int tx_policy_find(struct tx_policy_cache *cache, + const struct tx_policy *wanted) +{ + /* O(n) complexity. Not so good, but there's only 8 entries in + * the cache. + * Also lru helps to reduce search time. */ + struct tx_policy_cache_entry *it; + /* Search for policy in "used" list */ + list_for_each_entry(it, &cache->used, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + /* Then - in "free list" */ + list_for_each_entry(it, &cache->free, link) { + if (tx_policy_is_equal(wanted, &it->policy)) + return it - cache->cache; + } + return -1; +} + +static inline void tx_policy_use(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + ++entry->policy.usage_count; + list_move(&entry->link, &cache->used); +} + +static inline int tx_policy_release(struct tx_policy_cache *cache, + struct tx_policy_cache_entry *entry) +{ + int ret = --entry->policy.usage_count; + if (!ret) + list_move(&entry->link, &cache->free); + return ret; +} + +/* ******************************************************************** */ +/* External TX policy cache API */ + +void tx_policy_init(struct xradio_common *hw_priv) +{ + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + int i; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + memset(cache, 0, sizeof(*cache)); + + spin_lock_init(&cache->lock); + INIT_LIST_HEAD(&cache->used); + INIT_LIST_HEAD(&cache->free); + + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) + list_add(&cache->cache[i].link, &cache->free); +} + +static int tx_policy_get(struct xradio_common *hw_priv, + struct ieee80211_tx_rate *rates, + u8 use_bg_rate, bool *renew) +{ + int idx; + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + struct tx_policy wanted; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if(use_bg_rate) { + u8 rate = (u8)(use_bg_rate & 0x3f); + u8 shitf = ((rate&0x7)<<2); + u8 off = (rate>>3); + memset(&wanted, 0, sizeof(wanted)); + wanted.defined = rate + 1; + wanted.retry_count = (hw_priv->short_frame_max_tx_count&0xf); + wanted.tbl[off] = wanted.retry_count<short_frame_max_tx_count&0xf); + memcpy(&wanted.tbl[0], &rates_debug[0], sizeof(wanted.tbl)); + } +#endif + + spin_lock_bh(&cache->lock); + idx = tx_policy_find(cache, &wanted); + if (idx >= 0) { + txrx_printk(XRADIO_DBG_MSG, "[TX policy] Used TX policy: %d\n", + idx); + *renew = false; + } else { + struct tx_policy_cache_entry *entry; + if (WARN_ON_ONCE(list_empty(&cache->free))) { + spin_unlock_bh(&cache->lock); + txrx_printk(XRADIO_DBG_ERROR, "[TX policy] no policy cache\n"); + return XRADIO_INVALID_RATE_ID; + } + /* If policy is not found create a new one + * using the oldest entry in "free" list */ + *renew = true; + entry = list_entry(cache->free.prev, + struct tx_policy_cache_entry, link); + entry->policy = wanted; + idx = entry - cache->cache; + txrx_printk(XRADIO_DBG_MSG, "[TX policy] New TX policy: %d\n", + idx); + tx_policy_dump(&entry->policy); + } + tx_policy_use(cache, &cache->cache[idx]); + if (unlikely(list_empty(&cache->free))) { + /* Lock TX queues. */ + txrx_printk(XRADIO_DBG_WARN, "[TX policy] policy cache used up\n"); + xradio_tx_queues_lock(hw_priv); + } + spin_unlock_bh(&cache->lock); + + /*force to upload retry limit when using debug rate policy */ +#ifdef CONFIG_XRADIO_DEBUGFS + if (retry_dbg & 0x2) { + retry_dbg &= ~0x2; + //retry dgb need to be applied to policy. + *renew = true; + cache->cache[idx].policy.uploaded = 0; + } +#endif + + return idx; +} + +static void tx_policy_put(struct xradio_common *hw_priv, int idx) +{ + int usage, locked; + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + spin_lock_bh(&cache->lock); + locked = list_empty(&cache->free); + usage = tx_policy_release(cache, &cache->cache[idx]); + if (unlikely(locked) && !usage) { + /* Unlock TX queues. */ + xradio_tx_queues_unlock(hw_priv); + } + spin_unlock_bh(&cache->lock); +} + +/* +bool tx_policy_cache_full(struct xradio_common *hw_priv) +{ + bool ret; + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + spin_lock_bh(&cache->lock); + ret = list_empty(&cache->free); + spin_unlock_bh(&cache->lock); + return ret; +} +*/ +extern u32 policy_upload; +extern u32 policy_num; +static int tx_policy_upload(struct xradio_common *hw_priv) +{ + struct tx_policy_cache *cache = &hw_priv->tx_policy_cache; + int i; + struct wsm_set_tx_rate_retry_policy arg = { + .hdr = { + .numTxRatePolicies = 0, + } + }; + int if_id = 0; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + spin_lock_bh(&cache->lock); + /* Upload only modified entries. */ + for (i = 0; i < TX_POLICY_CACHE_SIZE; ++i) { + struct tx_policy *src = &cache->cache[i].policy; + if (src->retry_count && !src->uploaded) { + struct wsm_set_tx_rate_retry_policy_policy *dst = + &arg.tbl[arg.hdr.numTxRatePolicies]; + dst->policyIndex = i; + dst->shortRetryCount = hw_priv->short_frame_max_tx_count-1; + //only RTS need use longRetryCount, should be short_frame. + dst->longRetryCount = hw_priv->short_frame_max_tx_count-1; + + /* BIT(2) - Terminate retries when Tx rate retry policy + * finishes. + * BIT(3) - Count initial frame transmission as part of + * rate retry counting but not as a retry + * attempt */ + dst->policyFlags = BIT(2) | BIT(3); + memcpy(dst->rateCountIndices, src->tbl, + sizeof(dst->rateCountIndices)); + src->uploaded = 1; + ++arg.hdr.numTxRatePolicies; + } + } + spin_unlock_bh(&cache->lock); + atomic_set(&hw_priv->upload_count, 0); + + xradio_debug_tx_cache_miss(hw_priv); + txrx_printk(XRADIO_DBG_MSG, "[TX policy] Upload %d policies\n", + arg.hdr.numTxRatePolicies); +#ifdef CONFIG_XRADIO_DEBUGFS + //test yangfh + if(arg.tbl[0].policyIndex == 7) + txrx_printk(XRADIO_DBG_MSG, "rate:0x%08x, 0x%08x, 0x%08x\n", + arg.tbl[0].rateCountIndices[2], + arg.tbl[0].rateCountIndices[1], + arg.tbl[0].rateCountIndices[0]); + policy_upload++; + policy_num += arg.hdr.numTxRatePolicies; +#endif + /*TODO: COMBO*/ + return wsm_set_tx_rate_retry_policy(hw_priv, &arg, if_id); +} + +void tx_policy_upload_work(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, tx_policy_upload_work); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + SYS_WARN(tx_policy_upload(hw_priv)); + wsm_unlock_tx(hw_priv); +} + +/* ******************************************************************** */ +/* xradio TX implementation */ + +struct xradio_txinfo { + struct sk_buff *skb; + unsigned queue; + struct ieee80211_tx_info *tx_info; + const struct ieee80211_rate *rate; + struct ieee80211_hdr *hdr; + size_t hdrlen; + const u8 *da; + struct xradio_sta_priv *sta_priv; + struct xradio_txpriv txpriv; +}; + +u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv, u32 rates) +{ + u32 ret = 0; + int i; + u32 n_bitrates = + hw_priv->hw->wiphy->bands[hw_priv->channel->band]->n_bitrates; + struct ieee80211_rate * bitrates = + hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates; + + for (i = 0; i < n_bitrates; ++i) { + if (rates & BIT(i)) + ret |= BIT(bitrates[i].hw_value); + } + return ret; +} + +static const struct ieee80211_rate * +xradio_get_tx_rate(const struct xradio_common *hw_priv, + const struct ieee80211_tx_rate *rate) +{ + if (rate->idx < 0) + return NULL; + if (rate->flags & IEEE80211_TX_RC_MCS) + return &hw_priv->mcs_rates[rate->idx]; + return &hw_priv->hw->wiphy->bands[hw_priv->channel->band]-> + bitrates[rate->idx]; +} + +inline static s8 +xradio_get_rate_idx(const struct xradio_common *hw_priv, u8 flag, u16 hw_value) +{ + s16 ret = (s16)hw_value; + if(flag & IEEE80211_TX_RC_MCS) { //11n + if(hw_value <= hw_priv->mcs_rates[7].hw_value && + hw_value >= hw_priv->mcs_rates[0].hw_value) + ret -= hw_priv->mcs_rates[0].hw_value; + else + ret = -1; + } else { //11b/g + if(hw_value>5 && hw_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 xradio_txinfo *t) +{ +#ifndef P2P_MULTIVIF + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); +#endif + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + +#ifndef P2P_MULTIVIF + if ((t->tx_info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) || + (hw_priv->roc_if_id == priv->if_id)) + t->txpriv.offchannel_if_id = 2; + else + t->txpriv.offchannel_if_id = 0; +#endif + + if (likely(t->tx_info->control.sta && t->sta_priv->link_id)) + t->txpriv.raw_link_id = + t->txpriv.link_id = + t->sta_priv->link_id; + else if (priv->mode != NL80211_IFTYPE_AP) + t->txpriv.raw_link_id = + t->txpriv.link_id = 0; + else if (is_multicast_ether_addr(t->da)) { + if (priv->enable_beacon) { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = priv->link_id_after_dtim; + } else { + t->txpriv.raw_link_id = 0; + t->txpriv.link_id = 0; + } + } else { + t->txpriv.link_id = + xradio_find_link_id(priv, t->da); + /* Do not assign valid link id for deauth/disassoc frame being + transmitted to an unassociated STA */ + if (!(t->txpriv.link_id) && + (ieee80211_is_deauth(t->hdr->frame_control) || + ieee80211_is_disassoc(t->hdr->frame_control))) { + t->txpriv.link_id = 0; + } else { + if (!t->txpriv.link_id) + t->txpriv.link_id = xradio_alloc_link_id(priv, t->da); + if (!t->txpriv.link_id) { + txrx_printk(XRADIO_DBG_ERROR, + "%s: No more link IDs available.\n", __func__); + return -ENOENT; + } + } + t->txpriv.raw_link_id = t->txpriv.link_id; + } + if (t->txpriv.raw_link_id) + priv->link_id_db[t->txpriv.raw_link_id - 1].timestamp = + jiffies; + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + if (t->tx_info->control.sta && + (t->tx_info->control.sta->uapsd_queues & BIT(t->queue))) + t->txpriv.link_id = priv->link_id_uapsd; +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + return 0; +} + +static void +xradio_tx_h_pm(struct xradio_vif *priv, + struct xradio_txinfo *t) +{ + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (unlikely(ieee80211_is_auth(t->hdr->frame_control))) { + u32 mask = ~BIT(t->txpriv.raw_link_id); + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask &= mask; + priv->pspoll_mask &= mask; + spin_unlock_bh(&priv->ps_state_lock); + } +} + +static void +xradio_tx_h_calc_tid(struct xradio_vif *priv, + struct xradio_txinfo *t) +{ + if (ieee80211_is_data_qos(t->hdr->frame_control)) { + u8 *qos = ieee80211_get_qos_ctl(t->hdr); + t->txpriv.tid = qos[0] & IEEE80211_QOS_CTL_TID_MASK; + } else if (ieee80211_is_data(t->hdr->frame_control)) { + t->txpriv.tid = 0; + } +} + +/* IV/ICV injection. */ +/* TODO: Quite unoptimal. It's better co modify mac80211 + * to reserve space for IV */ +static int +xradio_tx_h_crypt(struct xradio_vif *priv, + struct xradio_txinfo *t) +{ + size_t iv_len; + size_t icv_len; + u8 *icv; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (!t->tx_info->control.hw_key || + !(t->hdr->frame_control & + __cpu_to_le32(IEEE80211_FCTL_PROTECTED))) + return 0; + + iv_len = t->tx_info->control.hw_key->iv_len; + icv_len = t->tx_info->control.hw_key->icv_len; + + if (t->tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_TKIP) + icv_len += 8; /* MIC */ + + if ((skb_headroom(t->skb) + skb_tailroom(t->skb) < + iv_len + icv_len + WSM_TX_EXTRA_HEADROOM) || + (skb_headroom(t->skb) < + iv_len + WSM_TX_EXTRA_HEADROOM)) { + txrx_printk(XRADIO_DBG_ERROR, + "Bug: no space allocated for crypto headers.\n" + "headroom: %d, tailroom: %d, " + "req_headroom: %d, req_tailroom: %d\n" + "Please fix it in xradio_get_skb().\n", + skb_headroom(t->skb), skb_tailroom(t->skb), + iv_len + WSM_TX_EXTRA_HEADROOM, icv_len); + return -ENOMEM; + } else if (skb_tailroom(t->skb) < icv_len) { + size_t offset = icv_len - skb_tailroom(t->skb); + u8 *p; + txrx_printk(XRADIO_DBG_ERROR, + "Slowpath: tailroom is not big enough. " + "Req: %d, got: %d.\n", + icv_len, skb_tailroom(t->skb)); + + p = skb_push(t->skb, offset); + memmove(p, &p[offset], t->skb->len - offset); + skb_trim(t->skb, t->skb->len - offset); + } + /* ccmp pkt from umac to driver,it has iv room,,so ccmp pkt do not add iv room */ + if (t->tx_info->control.hw_key->cipher != WLAN_CIPHER_SUITE_CCMP){ + u8 *newhdr; + newhdr = skb_push(t->skb, iv_len); + memmove(newhdr, newhdr + iv_len, t->hdrlen); + t->hdr = (struct ieee80211_hdr *) newhdr; + } + t->hdrlen += iv_len; + icv = skb_put(t->skb, icv_len); + + return 0; +} + +static int +xradio_tx_h_align(struct xradio_vif *priv, struct xradio_txinfo *t, + u8 *flags) +{ + size_t offset = (size_t)t->skb->data & 3; + u8 *newhdr;//add by dingxh + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (!offset) + return 0; + + if (skb_headroom(t->skb) < offset) { + txrx_printk(XRADIO_DBG_ERROR, + "Bug: no space allocated " + "for DMA alignment.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return -ENOMEM; + } + //offset = 1or3 process add by dingxh + if (offset & 1) { + newhdr = skb_push(t->skb, offset); + memmove(newhdr, newhdr + offset, t->skb->len-offset); + skb_trim(t->skb, t->skb->len-offset); + t->hdr = (struct ieee80211_hdr *) newhdr; + xradio_debug_tx_align(priv); + return 0; + } + //add by dingxh + //offset=2 process + skb_push(t->skb, offset); + t->hdrlen += offset; + t->txpriv.offset += offset; + *flags |= WSM_TX_2BYTES_SHIFT; + xradio_debug_tx_align(priv); + return 0; +} + +static int +xradio_tx_h_action(struct xradio_vif *priv, struct xradio_txinfo *t) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)t->hdr; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (ieee80211_is_action(t->hdr->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_BACK) + return 1; + else + return 0; +} + +/* Add WSM header */ +static struct wsm_tx * +xradio_tx_h_wsm(struct xradio_vif *priv, struct xradio_txinfo *t) +{ + struct wsm_tx *wsm; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (skb_headroom(t->skb) < sizeof(struct wsm_tx)) { + txrx_printk(XRADIO_DBG_ERROR, + "Bug: no space allocated " + "for WSM header.\n" + "headroom: %d\n", + skb_headroom(t->skb)); + return NULL; + } + + wsm = (struct wsm_tx *)skb_push(t->skb, sizeof(struct wsm_tx)); + t->txpriv.offset += sizeof(struct wsm_tx); + memset(wsm, 0, sizeof(*wsm)); + wsm->hdr.len = __cpu_to_le16(t->skb->len); + wsm->hdr.id = __cpu_to_le16(0x0004); + wsm->queueId = (t->txpriv.raw_link_id << 2) | wsm_queue_id_to_wsm(t->queue); + if (wsm->hdr.len > hw_priv->wsm_caps.sizeInpChBuf) { + txrx_printk(XRADIO_DBG_ERROR,"%s,msg length too big=%d\n", + __func__, wsm->hdr.len); + wsm = NULL; + } + + return wsm; +} + +/* BT Coex specific handling */ +static void +xradio_tx_h_bt(struct xradio_vif *priv, struct xradio_txinfo *t, struct wsm_tx *wsm) +{ + u8 priority = 0; + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (!hw_priv->is_BT_Present) + return; + + if (unlikely(ieee80211_is_nullfunc(t->hdr->frame_control))) + priority = WSM_EPTA_PRIORITY_MGT; + else if (ieee80211_is_data(t->hdr->frame_control)) { + /* Skip LLC SNAP header (+6) */ + u8 *payload = &t->skb->data[t->hdrlen]; + u16 *ethertype = (u16 *) &payload[6]; + if (unlikely(*ethertype == __be16_to_cpu(ETH_P_PAE))) + priority = WSM_EPTA_PRIORITY_EAPOL; + } else if (unlikely(ieee80211_is_assoc_req(t->hdr->frame_control) || + ieee80211_is_reassoc_req(t->hdr->frame_control))) { + struct ieee80211_mgmt *mgt_frame = + (struct ieee80211_mgmt *)t->hdr; + + if (mgt_frame->u.assoc_req.listen_interval < + priv->listen_interval) { + txrx_printk(XRADIO_DBG_MSG, + "Modified Listen Interval to %d from %d\n", + priv->listen_interval, + mgt_frame->u.assoc_req.listen_interval); + /* Replace listen interval derieved from + * the one read from SDD */ + mgt_frame->u.assoc_req.listen_interval = + priv->listen_interval; + } + } + + if (likely(!priority)) { + if (ieee80211_is_action(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_ACTION; + else if (ieee80211_is_mgmt(t->hdr->frame_control)) + priority = WSM_EPTA_PRIORITY_MGT; + else if ((wsm->queueId == WSM_QUEUE_VOICE)) + priority = WSM_EPTA_PRIORITY_VOICE; + else if ((wsm->queueId == WSM_QUEUE_VIDEO)) + priority = WSM_EPTA_PRIORITY_VIDEO; + else + priority = WSM_EPTA_PRIORITY_DATA; + } + + txrx_printk(XRADIO_DBG_MSG, "[TX] EPTA priority %d.\n", + priority); + + wsm->flags |= priority << 1; +} + +static int +xradio_tx_h_rate_policy(struct xradio_common *hw_priv, struct xradio_txinfo *t, + struct wsm_tx *wsm) +{ + bool tx_policy_renew = false; + struct xradio_vif *priv = + xrwl_get_vif_from_ieee80211(t->tx_info->control.vif); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + /*use debug policy for data frames only*/ +#ifdef CONFIG_XRADIO_DEBUGFS + if((rates_dbg_en & 0x1) && ieee80211_is_data(t->hdr->frame_control)) { + rates_dbg_en |= 0x02; + } +#endif + + t->txpriv.rate_id = tx_policy_get(hw_priv, + t->tx_info->control.rates, t->txpriv.use_bg_rate, + &tx_policy_renew); + if (t->txpriv.rate_id == XRADIO_INVALID_RATE_ID) + return -EFAULT; + + wsm->flags |= t->txpriv.rate_id << 4; + t->rate = xradio_get_tx_rate(hw_priv, &t->tx_info->control.rates[0]); + if (t->txpriv.use_bg_rate) + wsm->maxTxRate = (u8)(t->txpriv.use_bg_rate & 0x3f); + else + wsm->maxTxRate = t->rate->hw_value; + + /*set the maxTxRate and clear the dataframe flag of rates_dbg_en */ +#ifdef CONFIG_XRADIO_DEBUGFS + if(rates_dbg_en & 0x02) { + wsm->maxTxRate = maxRate_dbg; + rates_dbg_en &= ~0x2; + } +#endif + + if (t->rate->flags & IEEE80211_TX_RC_MCS) { + if (priv->association_mode.greenfieldMode) + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_GREENFIELD); + else + wsm->htTxParameters |= + __cpu_to_le32(WSM_HT_TX_MIXED); + } + + if (tx_policy_renew) { + txrx_printk(XRADIO_DBG_MSG, "[TX] TX policy renew.\n"); + /* It's not so optimal to stop TX queues every now and then. + * Maybe it's better to reimplement task scheduling with + * a counter. */ + /* xradio_tx_queues_lock(priv); */ + /* Definetly better. TODO. */ + if (atomic_add_return(1, &hw_priv->upload_count) == 1) { + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, + &hw_priv->tx_policy_upload_work) <= 0) { + atomic_set(&hw_priv->upload_count, 0); + wsm_unlock_tx(hw_priv); + } + } + } + return 0; +} + +static bool +xradio_tx_h_pm_state(struct xradio_vif *priv, struct xradio_txinfo *t) +{ + int was_buffered = 1; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (t->txpriv.link_id == priv->link_id_after_dtim && + !priv->buffered_multicasts) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) + queue_work(priv->hw_priv->workqueue, + &priv->multicast_start_work); + } + + if (t->txpriv.raw_link_id && t->txpriv.tid < XRADIO_MAX_TID) + was_buffered = priv->link_id_db[t->txpriv.raw_link_id - 1] + .buffered[t->txpriv.tid]++; + + return !was_buffered; +} + +static void +xradio_tx_h_ba_stat(struct xradio_vif *priv, + struct xradio_txinfo *t) +{ + struct xradio_common *hw_priv = priv->hw_priv; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (priv->join_status != XRADIO_JOIN_STATUS_STA) + return; + if (!xradio_is_ht(&hw_priv->ht_info)) + return; + if (!priv->setbssparams_done) + return; + if (!ieee80211_is_data(t->hdr->frame_control)) + return; + + spin_lock_bh(&hw_priv->ba_lock); + hw_priv->ba_acc += t->skb->len - t->hdrlen; + if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { + mod_timer(&hw_priv->ba_timer, + jiffies + XRADIO_BLOCK_ACK_INTERVAL); + } + hw_priv->ba_cnt++; + spin_unlock_bh(&hw_priv->ba_lock); +} + +static int +xradio_tx_h_skb_pad(struct xradio_common *priv, + struct wsm_tx *wsm, + struct sk_buff *skb) +{ + size_t len = __le16_to_cpu(wsm->hdr.len); + size_t padded_len = priv->sbus_ops->align_size(priv->sbus_priv, len); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (SYS_WARN(skb_padto(skb, padded_len) != 0)) { + return -EINVAL; + } + return 0; +} + +/* ******************************************************************** */ +#if (defined(CONFIG_XRADIO_DEBUG)) +u16 txparse_flags = 0;//PF_DHCP|PF_8021X|PF_MGMT; +u16 rxparse_flags = 0;//PF_DHCP|PF_8021X|PF_MGMT; +#endif + +void xradio_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +{ + struct xradio_common *hw_priv = dev->priv; + struct xradio_txinfo t = { + .skb = skb, + .queue = skb_get_queue_mapping(skb), + .tx_info = IEEE80211_SKB_CB(skb), + .hdr = (struct ieee80211_hdr *)skb->data, + .txpriv.tid = XRADIO_MAX_TID, + .txpriv.rate_id = XRADIO_INVALID_RATE_ID, +#ifdef P2P_MULTIVIF + .txpriv.raw_if_id = 0, +#endif + .txpriv.use_bg_rate = 0, + }; + struct ieee80211_sta *sta; + struct wsm_tx *wsm; + bool tid_update = 0; + u8 flags = 0; + int ret = 0; + struct xradio_vif *priv; + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + + if (!skb->data) + SYS_BUG(1); + +#ifdef HW_RESTART + if (hw_priv->hw_restart) { + txrx_printk(XRADIO_DBG_WARN, "%s, hw in reset.\n", __func__); + ret = __LINE__; + goto drop; + } +#endif + + if (!(t.tx_info->control.vif)) { + ret = __LINE__; + goto drop; + } + priv = xrwl_get_vif_from_ieee80211(t.tx_info->control.vif); + if (!priv) { + ret = __LINE__; + goto drop; + } + + if (atomic_read(&priv->enabled) == 0) { + ret = __LINE__; + goto drop; + } + +#if (defined(CONFIG_XRADIO_DEBUG)) + /* parse frame for debug. */ + if (txparse_flags){ + u8 temp_iv_len ; + if(t.tx_info->control.hw_key && + (t.hdr->frame_control & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && + (t.tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP)) + temp_iv_len = t.tx_info->control.hw_key->iv_len; + else + temp_iv_len =0; + xradio_parse_frame(skb->data, temp_iv_len, txparse_flags, priv->if_id); + } +#endif + + //dhcp and 8021 frames are important, use b/g rate and delay scan. + //it can make sense, such as accelerate connect. + if (ieee80211_is_auth(frame->frame_control)) { + hw_priv->connet_time[priv->if_id] = jiffies; + } else if (ieee80211_is_data_present(frame->frame_control)) { + /* since Umac had already alloc IV space in ccmp skb, so we need to add this iv_len as the new offset to LLC */ + u8* llc = NULL; + if(t.tx_info->control.hw_key && + (t.hdr->frame_control & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && + (t.tx_info->control.hw_key->cipher == WLAN_CIPHER_SUITE_CCMP)) + llc = skb->data+ieee80211_hdrlen(frame->frame_control) + t.tx_info->control.hw_key->iv_len; + else + llc = skb->data+ieee80211_hdrlen(frame->frame_control); + if (is_dhcp(llc) || is_8021x(llc)) { + t.txpriv.use_bg_rate = + hw_priv->hw->wiphy->bands[hw_priv->channel->band]->bitrates[0].hw_value; + if (priv->vif->p2p) + t.txpriv.use_bg_rate = AG_RATE_INDEX; + t.txpriv.use_bg_rate |= 0x80; + } + if (t.txpriv.use_bg_rate){ + hw_priv->connet_time[priv->if_id] = jiffies; + } + } else if (ieee80211_is_deauth(frame->frame_control) || + ieee80211_is_disassoc(frame->frame_control)) { + hw_priv->connet_time[priv->if_id] = 0; + } + +#ifdef AP_HT_COMPAT_FIX + if (ieee80211_is_assoc_req(frame->frame_control) && + priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) { + xradio_remove_ht_ie(priv, skb); + } +#endif + +#ifdef CONFIG_XRADIO_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->start_stop_tsm.start) { + if (hw_priv->tsm_info.ac == t.queue) + hw_priv->tsm_stats.txed_msdu_count++; + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_XRADIO_TESTMODE*/ + +#ifdef TES_P2P_0002_ROC_RESTART + xradio_frame_monitor(hw_priv,skb,true); +#endif + + if (ieee80211_is_action(frame->frame_control) && + mgmt->u.action.category == WLAN_CATEGORY_PUBLIC) { + u8 *action = (u8*)&mgmt->u.action.category; + xradio_check_go_neg_conf_success(hw_priv, action); + xradio_check_prov_desc_req(hw_priv, action); + } + + t.txpriv.if_id = priv->if_id; + t.hdrlen = ieee80211_hdrlen(t.hdr->frame_control); + t.da = ieee80211_get_DA(t.hdr); + t.sta_priv = + (struct xradio_sta_priv *)&t.tx_info->control.sta->drv_priv; + + if (SYS_WARN(t.queue >= 4)) { + ret = __LINE__; + goto drop; + } + + //spin_lock_bh(&hw_priv->tx_queue[t.queue].lock); + //if ((priv->if_id == 0) && + // (hw_priv->tx_queue[t.queue].num_queued_vif[0] >= + // hw_priv->vif0_throttle)) { + // spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); + // + // ret = __LINE__; + // goto drop; + //} else if ((priv->if_id == 1) && + // (hw_priv->tx_queue[t.queue].num_queued_vif[1] >= + // hw_priv->vif1_throttle)) { + // spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); + // + // ret = __LINE__; + // goto drop; + //} + //spin_unlock_bh(&hw_priv->tx_queue[t.queue].lock); + + ret = xradio_tx_h_calc_link_ids(priv, &t); + if (ret) { + ret = __LINE__; + goto drop; + } + + txrx_printk(XRADIO_DBG_MSG, "[TX] TX %d bytes (if_id: %d," + " queue: %d, link_id: %d (%d)).\n", + skb->len, priv->if_id, t.queue, t.txpriv.link_id, + t.txpriv.raw_link_id); + + xradio_tx_h_pm(priv, &t); + xradio_tx_h_calc_tid(priv, &t); + ret = xradio_tx_h_crypt(priv, &t); + if (ret) { + ret = __LINE__; + goto drop; + } + ret = xradio_tx_h_align(priv, &t, &flags); + if (ret) { + ret = __LINE__; + goto drop; + } + ret = xradio_tx_h_action(priv, &t); + if (ret) { + ret = __LINE__; + goto drop; + } + wsm = xradio_tx_h_wsm(priv, &t); + if (!wsm) { + ret = __LINE__; + goto drop; + } +#ifdef CONFIG_XRADIO_TESTMODE + flags |= WSM_TX_FLAG_EXPIRY_TIME; +#endif /*CONFIG_XRADIO_TESTMODE*/ + wsm->flags |= flags; + xradio_tx_h_bt(priv, &t, wsm); + ret = xradio_tx_h_rate_policy(hw_priv, &t, wsm); + if (ret) { + ret = __LINE__; + goto drop; + } + + ret = xradio_tx_h_skb_pad(hw_priv, wsm, skb); + if (ret) { + ret = __LINE__; + goto drop; + } + + rcu_read_lock(); + sta = rcu_dereference(t.tx_info->control.sta); + + xradio_tx_h_ba_stat(priv, &t); + spin_lock_bh(&priv->ps_state_lock); + { + tid_update = xradio_tx_h_pm_state(priv, &t); + SYS_BUG(xradio_queue_put(&hw_priv->tx_queue[t.queue], + t.skb, &t.txpriv)); +#ifdef ROC_DEBUG + txrx_printk(XRADIO_DBG_ERROR, "QPUT %x, %pM, if_id - %d\n", + t.hdr->frame_control, t.da, priv->if_id); +#endif + } + spin_unlock_bh(&priv->ps_state_lock); + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + if (tid_update && sta) + ieee80211_sta_set_buffered(sta, + t.txpriv.tid, true); +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ + + rcu_read_unlock(); + + xradio_bh_wakeup(hw_priv); + + return; + +drop: + txrx_printk(XRADIO_DBG_WARN, "drop=%d, fctl=0x%04x.\n", ret, frame->frame_control); + xradio_skb_dtor(hw_priv, skb, &t.txpriv); + return; +} + +/* ******************************************************************** */ + +static int xradio_handle_pspoll(struct xradio_vif *priv, + struct sk_buff *skb) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct ieee80211_sta *sta; + struct ieee80211_pspoll *pspoll = + (struct ieee80211_pspoll *) skb->data; + int link_id = 0; + u32 pspoll_mask = 0; + int drop = 1; + int i; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (priv->join_status != XRADIO_JOIN_STATUS_AP) + goto done; + if (memcmp(priv->vif->addr, pspoll->bssid, ETH_ALEN)) + goto done; + + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, pspoll->ta); + if (sta) { + struct xradio_sta_priv *sta_priv; + sta_priv = (struct xradio_sta_priv *)&sta->drv_priv; + link_id = sta_priv->link_id; + pspoll_mask = BIT(sta_priv->link_id); + } + rcu_read_unlock(); + if (!link_id) + goto done; + + priv->pspoll_mask |= pspoll_mask; + drop = 0; + + /* Do not report pspols if data for given link id is + * queued already. */ + for (i = 0; i < 4; ++i) { + if (xradio_queue_get_num_queued(priv, + &hw_priv->tx_queue[i], + pspoll_mask)) { + xradio_bh_wakeup(hw_priv); + drop = 1; + break; + } + } + txrx_printk(XRADIO_DBG_NIY, "[RX] PSPOLL: %s\n", drop ? "local" : "fwd"); +done: + return drop; +} + +/* ******************************************************************** */ +//for test yangfh +extern u32 tx_retrylimit; +extern u32 tx_over_limit; +extern u32 tx_lower_limit; +extern int retry_mis; + +void xradio_tx_confirm_cb(struct xradio_common *hw_priv, + struct wsm_tx_confirm *arg) +{ + u8 queue_id = xradio_queue_get_queue_id(arg->packetID); + struct xradio_queue *queue = &hw_priv->tx_queue[queue_id]; + struct sk_buff *skb; + const struct xradio_txpriv *txpriv; + struct xradio_vif *priv; + u32 feedback_retry = 0; + + if(arg->status) { + txrx_printk(XRADIO_DBG_WARN,"status=%d, retry=%d, lastRate=%d\n", + arg->status, arg->ackFailures, arg->txedRate); + } else { + txrx_printk(XRADIO_DBG_MSG,"status=%d, retry=%d, lastRate=%d\n", + arg->status, arg->ackFailures, arg->txedRate); + } + +#ifdef TES_P2P_0002_ROC_RESTART + if( (TES_P2P_0002_state == TES_P2P_0002_STATE_GET_PKTID) && + (arg->packetID == TES_P2P_0002_packet_id) ) { + if (arg->status == 0x00) { + + struct timeval TES_P2P_0002_tmval; + s32 TES_P2P_0002_roc_time; + s32 TES_P2P_0002_now_sec; + s32 TES_P2P_0002_now_usec; + bool TES_P2P_0002_roc_rst_need; + + do_gettimeofday(&TES_P2P_0002_tmval); + TES_P2P_0002_roc_rst_need = false; + TES_P2P_0002_now_sec = (s32)(TES_P2P_0002_tmval.tv_sec); + TES_P2P_0002_now_usec = (s32)(TES_P2P_0002_tmval.tv_usec); + TES_P2P_0002_roc_time = TES_P2P_0002_roc_dur - + (((TES_P2P_0002_now_sec - TES_P2P_0002_roc_sec) * 1000) + + (( TES_P2P_0002_now_usec - TES_P2P_0002_roc_usec ) / 1000) ); + + ///w, tx rsp to rx cfm will need more than 60ms + if( TES_P2P_0002_roc_time < 100 ) { + TES_P2P_0002_roc_time = 100; + TES_P2P_0002_roc_rst_need = true; + } + + if( TES_P2P_0002_roc_rst_need == true ) { + txrx_printk(XRADIO_DBG_WARN, "[ROC RESTART ACTIVE ON][Confirm CallBack]"); + cancel_delayed_work_sync(&hw_priv->rem_chan_timeout); + if(atomic_read(&hw_priv->remain_on_channel)) { + queue_delayed_work(hw_priv->workqueue, &hw_priv->rem_chan_timeout, + (TES_P2P_0002_roc_time) * HZ / 1000); + } + } + } + TES_P2P_0002_state = TES_P2P_0002_STATE_IDLE; + txrx_printk(XRADIO_DBG_WARN, "[ROC_RESTART_STATE_IDLE][Confirm CallBack]"); + } +#endif + + if (unlikely(xradio_itp_tx_running(hw_priv))) + return; + + priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg->if_id); + if (unlikely(!priv)) + return; + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + spin_unlock(&priv->vif_lock); + return; + } + + if (SYS_WARN(queue_id >= 4)) { + spin_unlock(&priv->vif_lock); + return; + } + +#ifdef CONFIG_XRADIO_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if ((arg->status == WSM_STATUS_RETRY_EXCEEDED) || + (arg->status == WSM_STATUS_TX_LIFETIME_EXCEEDED)) { + hw_priv->tsm_stats.msdu_discarded_count++; + } else if ((hw_priv->start_stop_tsm.start) && + (arg->status == WSM_STATUS_SUCCESS)) { + if (queue_id == hw_priv->tsm_info.ac) { + struct timeval tmval; + do_gettimeofday(&tmval); + u16 pkt_delay = + hw_priv->start_stop_tsm.packetization_delay; + if (hw_priv->tsm_info.sta_roamed && + !hw_priv->tsm_info.use_rx_roaming) { + hw_priv->tsm_info.roam_delay = tmval.tv_usec - + hw_priv->tsm_info.txconf_timestamp_vo; + if (hw_priv->tsm_info.roam_delay > pkt_delay) + hw_priv->tsm_info.roam_delay -= pkt_delay; + txrx_printk(XRADIO_DBG_MSG, "[TX] txConf" + "Roaming: roam_delay = %u\n", + hw_priv->tsm_info.roam_delay); + hw_priv->tsm_info.sta_roamed = 0; + } + hw_priv->tsm_info.txconf_timestamp_vo = tmval.tv_usec; + } + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_XRADIO_TESTMODE*/ + if ((arg->status == WSM_REQUEUE) && + (arg->flags & WSM_TX_STATUS_REQUEUE)) { + /* "Requeue" means "implicit suspend" */ + struct wsm_suspend_resume suspend = { + .link_id = arg->link_id, + .stop = 1, + .multicast = !arg->link_id, + .if_id = arg->if_id, + }; + xradio_suspend_resume(priv, &suspend); + txrx_printk(XRADIO_DBG_WARN, "Requeue for link_id %d (try %d)." + " STAs asleep: 0x%.8X\n", + arg->link_id, + xradio_queue_get_generation(arg->packetID) + 1, + priv->sta_asleep_mask); +#ifdef CONFIG_XRADIO_TESTMODE + SYS_WARN(xradio_queue_requeue(hw_priv, queue, + arg->packetID, true)); +#else + SYS_WARN(xradio_queue_requeue(queue, + arg->packetID, true)); +#endif + spin_lock_bh(&priv->ps_state_lock); + if (!arg->link_id) { + priv->buffered_multicasts = true; + if (priv->sta_asleep_mask) { + queue_work(hw_priv->workqueue, + &priv->multicast_start_work); + } + } + spin_unlock_bh(&priv->ps_state_lock); + spin_unlock(&priv->vif_lock); + } else if (!SYS_WARN(xradio_queue_get_skb( + queue, arg->packetID, &skb, &txpriv))) { + struct ieee80211_tx_info *tx = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset]; + int tx_count = arg->ackFailures; + u8 ht_flags = 0; + int i; + + //yangfh add to reset if_0 in firmware when STA-unjoined, + //fix the errors when switch APs in combo mode. + if (unlikely(ieee80211_is_disassoc(frame->frame_control) || + ieee80211_is_deauth(frame->frame_control))) { + if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + wms_send_deauth_to_self(hw_priv, priv); + /* Shedule unjoin work */ + txrx_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX) by self.\n"); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } + +#ifdef ROC_DEBUG +#ifndef P2P_MULTIVIF + if (txpriv->offchannel_if_id) + txrx_printk(XRADIO_DBG_ERROR, "TX CONFIRM %x - %d - %d\n", + skb->data[txpriv->offset], + txpriv->offchannel_if_id, arg->status); +#else + if (txpriv->if_id) + txrx_printk(XRADIO_DBG_ERROR, "TX CONFIRM %x - %d - %d\n", + skb->data[txpriv->offset], + txpriv->raw_if_id, arg->status); +#endif +#endif + if (priv->association_mode.greenfieldMode) + ht_flags |= IEEE80211_TX_RC_GREEN_FIELD; + + //bss loss confirm. + if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CONFIRMING && + priv->bss_loss_confirm_id == arg->packetID)) { + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_status = arg->status? + XRADIO_BSS_LOSS_CONFIRMED : XRADIO_BSS_LOSS_NONE; + spin_unlock(&priv->bss_loss_lock); + } + + if (likely(!arg->status)) { + tx->flags |= IEEE80211_TX_STAT_ACK; + priv->cqm_tx_failure_count = 0; + ++tx_count; + if (arg->txedRate<24) + TxedRateIdx_Map[arg->txedRate]++; + else + SYS_WARN(1); + xradio_debug_txed(priv); + if (arg->flags & WSM_TX_STATUS_AGGREGATION) { + /* Do not report aggregation to mac80211: + * it confuses minstrel a lot. */ + /* tx->flags |= IEEE80211_TX_STAT_AMPDU; */ + xradio_debug_txed_agg(priv); + } + } else { + /* TODO: Update TX failure counters */ + if (unlikely(priv->cqm_tx_failure_thold && + (++priv->cqm_tx_failure_count > + priv->cqm_tx_failure_thold))) { + priv->cqm_tx_failure_thold = 0; + queue_work(hw_priv->workqueue, + &priv->tx_failure_work); + } + if (tx_count) + ++tx_count; + } + spin_unlock(&priv->vif_lock); + + tx->status.ampdu_len = 1; + tx->status.ampdu_ack_len = 1; + +#if 0 + tx_count = arg->ackFailures+1; + for (i = 0; i < IEEE80211_TX_MAX_RATES; ++i) { + if (tx->status.rates[i].count >= tx_count) { + tx->status.rates[i].count = tx_count; + if(likely(!arg->status)) { + s8 txed_idx = xradio_get_rate_idx(hw_priv, tx->status.rates[i].flags, + arg->txedRate); + //if(txed_idx != tx->status.rates[i].idx) { + // tx_policy_err++; + //} + //tx->status.rates[i].idx = (txed_idx >=0)? + // txed_idx:tx->status.rates[i].idx; + if(tx->status.rates[i].idx != txed_idx) { + if(i<(IEEE80211_TX_MAX_RATES-1)) { + i++; + tx->status.rates[i].idx = txed_idx; + tx->status.rates[i].count = 1; + } else if(txed_idx >=0) { + tx->status.rates[i].idx = txed_idx; + tx->status.rates[i].count = 1; + } + } + } + break; + } + tx_count -= tx->status.rates[i].count; + if (tx->status.rates[i].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[i].flags |= ht_flags; + } + + for (++i; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + +#else + txrx_printk(XRADIO_DBG_NIY,"feedback:%08x, %08x, %08x.\n", + arg->rate_try[2], arg->rate_try[1], arg->rate_try[0]); + if(txpriv->use_bg_rate) { //bg rates + tx->status.rates[0].count = arg->ackFailures+1; + tx->status.rates[0].idx = 0; + tx->status.rates[1].idx = -1; + tx->status.rates[2].idx = -1; + tx->status.rates[3].idx = -1; + tx->status.rates[4].idx = -1; + } else { + int j; + s8 txed_idx; + register u8 rate_num=0, shift=0, retries=0; + u8 flag = tx->status.rates[0].flags; + + //get retry rate idx. + for(i=2; i>=0;i--) { + if(arg->rate_try[i]) { + for(j=7; j>=0;j--) { + shift = j<<2; + retries = (arg->rate_try[i]>>shift)&0xf; + if(retries) { + feedback_retry += retries; + txed_idx = xradio_get_rate_idx(hw_priv,flag,((i<<3)+j)); + txrx_printk(XRADIO_DBG_NIY, "rate_num=%d, hw=%d, idx=%d, " + "retries=%d, flag=%d", rate_num, ((i<<3)+j), + txed_idx, retries, flag); + if(likely(txed_idx>=0)) { + tx->status.rates[rate_num].idx = txed_idx; + tx->status.rates[rate_num].count = retries; + if (tx->status.rates[rate_num].flags & IEEE80211_TX_RC_MCS) + tx->status.rates[rate_num].flags |= ht_flags; + rate_num++; + if(rate_num>=IEEE80211_TX_MAX_RATES) { + i = -1; + break; + } + } + } + } + } + } + //clear other rate. + for (i=rate_num; i < IEEE80211_TX_MAX_RATES; ++i) { + tx->status.rates[i].count = 0; + tx->status.rates[i].idx = -1; + } + //get successful rate idx. + if(!arg->status) { + txed_idx = xradio_get_rate_idx(hw_priv, flag, arg->txedRate); + if(rate_num == 0) { + tx->status.rates[0].idx = txed_idx; + tx->status.rates[0].count = 1; + } else if(rate_num <= IEEE80211_TX_MAX_RATES){ + --rate_num; + if(txed_idx == tx->status.rates[rate_num].idx) { + tx->status.rates[rate_num].count += 1; + } else if(rate_num<(IEEE80211_TX_MAX_RATES-1)){ + ++rate_num; + tx->status.rates[rate_num].idx = txed_idx; + tx->status.rates[rate_num].count = 1; + } else if(txed_idx >=0) { + tx->status.rates[rate_num].idx = txed_idx; + tx->status.rates[rate_num].count = 1; + } + } + } + } +#endif + +#ifdef CONFIG_XRADIO_DEBUGFS + if (arg->status == WSM_STATUS_RETRY_EXCEEDED) { + tx_retrylimit++; + retry_mis += ((s32)hw_priv->short_frame_max_tx_count-arg->ackFailures-1); + if(arg->ackFailures != (hw_priv->short_frame_max_tx_count-1)) { + if(arg->ackFailures < (hw_priv->short_frame_max_tx_count-1)) + tx_lower_limit++; + else + tx_over_limit++; + txrx_printk(XRADIO_DBG_WARN, "retry_err, ackFailures=%d, feedbk_retry=%d.\n", + arg->ackFailures, feedback_retry); + } + } else if (feedback_retry > hw_priv->short_frame_max_tx_count-1) { + txrx_printk(XRADIO_DBG_WARN, "status=%d, ackFailures=%d, feedbk_retry=%d.\n", + arg->status, arg->ackFailures, feedback_retry); + } +#endif + + txrx_printk(XRADIO_DBG_MSG, "[TX policy] Ack: " \ + "%d:%d, %d:%d, %d:%d, %d:%d, %d:%d\n", + tx->status.rates[0].idx, tx->status.rates[0].count, + tx->status.rates[1].idx, tx->status.rates[1].count, + tx->status.rates[2].idx, tx->status.rates[2].count, + tx->status.rates[3].idx, tx->status.rates[3].count, + tx->status.rates[4].idx, tx->status.rates[4].count); + +#ifdef CONFIG_XRADIO_TESTMODE + xradio_queue_remove(hw_priv, queue, arg->packetID); +#else + xradio_queue_remove(queue, arg->packetID); +#endif /*CONFIG_XRADIO_TESTMODE*/ + } +} + +static void xradio_notify_buffered_tx(struct xradio_vif *priv, + struct sk_buff *skb, int link_id, int tid) +{ +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + u8 *buffered; + u8 still_buffered = 0; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (link_id && tid < XRADIO_MAX_TID) { + buffered = priv->link_id_db + [link_id - 1].buffered; + + spin_lock_bh(&priv->ps_state_lock); + if (!SYS_WARN(!buffered[tid])) + still_buffered = --buffered[tid]; + spin_unlock_bh(&priv->ps_state_lock); + + if (!still_buffered && tid < XRADIO_MAX_TID) { + hdr = (struct ieee80211_hdr *) skb->data; + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, hdr->addr1); + if (sta) + ieee80211_sta_set_buffered(sta, tid, false); + rcu_read_unlock(); + } + } +#endif /* CONFIG_XRADIO_USE_EXTENSIONS */ +} + +void xradio_skb_dtor(struct xradio_common *hw_priv, + struct sk_buff *skb, + const struct xradio_txpriv *txpriv) +{ + struct xradio_vif *priv = + __xrwl_hwpriv_to_vifpriv(hw_priv, txpriv->if_id); + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + skb_pull(skb, txpriv->offset); + if (priv && txpriv->rate_id != XRADIO_INVALID_RATE_ID) { + xradio_notify_buffered_tx(priv, skb, + txpriv->raw_link_id, txpriv->tid); + tx_policy_put(hw_priv, txpriv->rate_id); + } + if (likely(!xradio_is_itp(hw_priv))) + ieee80211_tx_status(hw_priv->hw, skb); + +} +#ifdef CONFIG_XRADIO_TESTMODE +/* TODO It should be removed before official delivery */ +static void frame_hexdump(char *prefix, u8 *data, int len) +{ + int i; + + txrx_printk(XRADIO_DBG_MSG, "%s hexdump:\n", prefix); + for (i = 0; i < len; i++) { + if (i + 10 < len) { + txrx_printk(XRADIO_DBG_MSG, "%.1X %.1X %.1X %.1X" \ + "%.1X %.1X %.1X %.1X %.1X %.1X", + data[i], data[i+1], data[i+2], + data[i+3], data[i+4], data[i+5], + data[i+6], data[i+7], data[i+8], + data[i+9]); + i += 9; + } else { + txrx_printk(XRADIO_DBG_MSG, "%.1X ", data[i]); + } + } +} +/** + * c1200_tunnel_send_testmode_data - Send test frame to the driver + * + * @priv: pointer to xradio private structure + * @skb: skb with frame + * + * Returns: 0 on success or non zero value on failure + */ +static int xradio_tunnel_send_testmode_data(struct xradio_common *hw_priv, + struct sk_buff *skb) +{ + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + if (xradio_tesmode_event(hw_priv->hw->wiphy, AW_MSG_EVENT_FRAME_DATA, + skb->data, skb->len, GFP_ATOMIC)) + return -EINVAL; + + return 0; +} + +/** + * xradio_frame_test_detection - Detection frame_test + * + * @priv: pointer to xradio vif structure + * @frame: ieee80211 header + * @skb: skb with frame + * + * Returns: 1 - frame test detected, 0 - not detected + */ +static int xradio_frame_test_detection(struct xradio_vif *priv, + struct ieee80211_hdr *frame, + struct sk_buff *skb) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + int hdrlen = ieee80211_hdrlen(frame->frame_control); + int detected = 0; + int ret; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (hdrlen + hw_priv->test_frame.len <= skb->len && + memcmp(skb->data + hdrlen, hw_priv->test_frame.data, + hw_priv->test_frame.len) == 0) { + detected = 1; + txrx_printk(XRADIO_DBG_MSG, "TEST FRAME detected"); + frame_hexdump("TEST FRAME original:", skb->data, skb->len); + ret = ieee80211_data_to_8023(skb, hw_priv->mac_addr, + priv->mode); + if (!ret) { + frame_hexdump("FRAME 802.3:", skb->data, skb->len); + ret = xradio_tunnel_send_testmode_data(hw_priv, skb); + } + if (ret) + txrx_printk(XRADIO_DBG_ERROR, "Send TESTFRAME failed(%d)", ret); + } + return detected; +} +#endif /* CONFIG_XRADIO_TESTMODE */ + + +static void +xradio_rx_h_ba_stat(struct xradio_vif *priv, + size_t hdrlen, size_t skb_len ) +{ + struct xradio_common *hw_priv = priv->hw_priv; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (priv->join_status != XRADIO_JOIN_STATUS_STA) + return; + if (!xradio_is_ht(&hw_priv->ht_info)) + return; + if (!priv->setbssparams_done) + return; + + spin_lock_bh(&hw_priv->ba_lock); + hw_priv->ba_acc_rx += skb_len - hdrlen; + if (!(hw_priv->ba_cnt_rx || hw_priv->ba_cnt)) { + mod_timer(&hw_priv->ba_timer, + jiffies + XRADIO_BLOCK_ACK_INTERVAL); + } + hw_priv->ba_cnt_rx++; + spin_unlock_bh(&hw_priv->ba_lock); +} + +#if 0 +u8 nettest_bssid[] = {0x00,0x02,0x03,0x04,0x05,0x06}; +u8 save_rate_ie; +#endif + +void xradio_rx_cb(struct xradio_vif *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + struct sk_buff *skb = *skb_p; + struct ieee80211_rx_status *hdr = IEEE80211_SKB_RXCB(skb); + struct ieee80211_hdr *frame = (struct ieee80211_hdr *)skb->data; +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; +#endif + struct xradio_link_entry *entry = NULL; + unsigned long grace_period; + bool early_data = false; + size_t hdrlen = 0; + u8 parse_iv_len = 0; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + hdr->flag = 0; + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + goto drop; + } + +#ifdef TES_P2P_0002_ROC_RESTART + xradio_frame_monitor(hw_priv,skb,false); +#endif + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + if ((ieee80211_is_action(frame->frame_control)) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + u8 *action = (u8*)&mgmt->u.action.category; + xradio_check_go_neg_conf_success(hw_priv, action); + } +#endif + +#ifdef CONFIG_XRADIO_TESTMODE + spin_lock_bh(&hw_priv->tsm_lock); + if (hw_priv->start_stop_tsm.start) { + unsigned queue_id = skb_get_queue_mapping(skb); + if (queue_id == 0) { + struct timeval tmval; + do_gettimeofday(&tmval); + if (hw_priv->tsm_info.sta_roamed && + hw_priv->tsm_info.use_rx_roaming) { + hw_priv->tsm_info.roam_delay = tmval.tv_usec - + hw_priv->tsm_info.rx_timestamp_vo; + txrx_printk(XRADIO_DBG_NIY, "[RX] RxInd Roaming:" + "roam_delay = %u\n", hw_priv->tsm_info.roam_delay); + hw_priv->tsm_info.sta_roamed = 0; + } + hw_priv->tsm_info.rx_timestamp_vo = tmval.tv_usec; + } + } + spin_unlock_bh(&hw_priv->tsm_lock); +#endif /*CONFIG_XRADIO_TESTMODE*/ + if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED) + && (arg->link_id <= XRADIO_MAX_STA_IN_AP_MODE)) { + entry = &priv->link_id_db[arg->link_id - 1]; + if (entry->status == XRADIO_LINK_SOFT && + ieee80211_is_data(frame->frame_control)) + early_data = true; + entry->timestamp = jiffies; + } +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + else if ((arg->link_id == XRADIO_LINK_ID_UNMAPPED) + && (priv->vif->p2p == WSM_START_MODE_P2P_GO) + && ieee80211_is_action(frame->frame_control) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + txrx_printk(XRADIO_DBG_NIY, "[RX] Going to MAP&RESET link ID\n"); + + if (work_pending(&priv->linkid_reset_work)) + SYS_WARN(1); + + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = 0; + schedule_work(&priv->linkid_reset_work); + } + + if (arg->link_id && (arg->link_id != XRADIO_LINK_ID_UNMAPPED) + && (priv->vif->p2p == WSM_START_MODE_P2P_GO) + && ieee80211_is_action(frame->frame_control) + && (mgmt->u.action.category == WLAN_CATEGORY_PUBLIC)) { + /* Link ID already exists for the ACTION frame. + * Reset and Remap */ + if (work_pending(&priv->linkid_reset_work)) + SYS_WARN(1); + memcpy(&priv->action_frame_sa[0], + ieee80211_get_SA(frame), ETH_ALEN); + priv->action_linkid = arg->link_id; + schedule_work(&priv->linkid_reset_work); + } +#endif + if (unlikely(arg->status)) { + if (arg->status == WSM_STATUS_MICFAILURE) { + txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, MIC failure.\n", + priv->if_id); + hdr->flag |= RX_FLAG_MMIC_ERROR; + } else if (arg->status == WSM_STATUS_NO_KEY_FOUND) { + txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, No key found.\n", + priv->if_id); + goto drop; + } else { + txrx_printk(XRADIO_DBG_WARN, "[RX] IF=%d, Receive failure: %d.\n", + priv->if_id, arg->status); + goto drop; + } + } + + if (skb->len < sizeof(struct ieee80211_pspoll)) { + txrx_printk(XRADIO_DBG_WARN, "Mailformed SDU rx'ed. " + "Size is lesser than IEEE header.\n"); + goto drop; + } + + if (unlikely(ieee80211_is_pspoll(frame->frame_control))) + if (xradio_handle_pspoll(priv, skb)) + goto drop; + + hdr->mactime = 0; /* Not supported by WSM */ + hdr->band = (arg->channelNumber > 14) ? + IEEE80211_BAND_5GHZ : IEEE80211_BAND_2GHZ; + hdr->freq = ieee80211_channel_to_frequency( + arg->channelNumber, + hdr->band); + +#ifdef AP_HT_COMPAT_FIX + if (!priv->ht_compat_det && priv->htcap && + ieee80211_is_data_qos(frame->frame_control)) { + if(xradio_apcompat_detect(priv, arg->rxedRate)) + goto drop; + } +#endif + + if (arg->rxedRate<24) + RxedRateIdx_Map[arg->rxedRate]++; + else + SYS_WARN(1); + + if (arg->rxedRate >= 14) { + hdr->flag |= RX_FLAG_HT; + hdr->rate_idx = arg->rxedRate - 14; + } else if (arg->rxedRate >= 4) { + if (hdr->band == IEEE80211_BAND_5GHZ) + hdr->rate_idx = arg->rxedRate - 6; + else + hdr->rate_idx = arg->rxedRate - 2; + } else { + hdr->rate_idx = arg->rxedRate; + } + + hdr->signal = (s8)arg->rcpiRssi; + hdr->antenna = 0; + + hdrlen = ieee80211_hdrlen(frame->frame_control); + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + size_t iv_len = 0, icv_len = 0; + + hdr->flag |= RX_FLAG_DECRYPTED; + + /* Oops... There is no fast way to ask mac80211 about + * IV/ICV lengths. Even defineas are not exposed.*/ + switch (WSM_RX_STATUS_ENCRYPTION(arg->flags)) { + case WSM_RX_STATUS_WEP: + iv_len = 4 /* WEP_IV_LEN */; + icv_len = 4 /* WEP_ICV_LEN */; + break; + case WSM_RX_STATUS_TKIP: + iv_len = 8 /* TKIP_IV_LEN */; + icv_len = 4 /* TKIP_ICV_LEN */ + + 8 /*MICHAEL_MIC_LEN*/; + break; + case WSM_RX_STATUS_AES: + iv_len = 8 /* CCMP_HDR_LEN */; + icv_len = 8 /* CCMP_MIC_LEN */; + break; + case WSM_RX_STATUS_WAPI: + iv_len = 18 /* WAPI_HDR_LEN */; + icv_len = 16 /* WAPI_MIC_LEN */; + hdr->flag |= RX_FLAG_IV_STRIPPED; + break; + default: + SYS_WARN("Unknown encryption type"); + goto drop; + } + + /* Firmware strips ICV in case of MIC failure. */ + if (arg->status == WSM_STATUS_MICFAILURE) { + icv_len = 0; + hdr->flag |= RX_FLAG_IV_STRIPPED; + } + + if (skb->len < hdrlen + iv_len + icv_len) { + txrx_printk(XRADIO_DBG_WARN, "Mailformed SDU rx'ed. " + "Size is lesser than crypto headers.\n"); + goto drop; + } + + if (WSM_RX_STATUS_ENCRYPTION(arg->flags) == + WSM_RX_STATUS_TKIP) { + /* Remove TKIP MIC 8 bytes*/ + memmove(skb->data + skb->len-icv_len, + skb->data + skb->len-icv_len+8, 4); + skb_trim(skb, skb->len - 8); + hdr->flag |= RX_FLAG_MMIC_STRIPPED; + } else if (unlikely(WSM_RX_STATUS_ENCRYPTION(arg->flags) == + WSM_RX_STATUS_WAPI)) { + /* Protocols not defined in mac80211 should be + stripped/crypted in driver/firmware */ + /* Remove IV, ICV and MIC */ + skb_trim(skb, skb->len - icv_len); + memmove(skb->data + iv_len, skb->data, hdrlen); + skb_pull(skb, iv_len); + } + parse_iv_len = iv_len; + } + + xradio_debug_rxed(priv); + if (arg->flags & WSM_RX_STATUS_AGGREGATE) + xradio_debug_rxed_agg(priv); + +#if 0 + /*huanglu, for nettest*/ + if (ieee80211_is_probe_resp(frame->frame_control) && + !arg->status && + !memcmp(ieee80211_get_SA(frame), nettest_bssid, ETH_ALEN)) { + const u8 *supp_rate_ie; + u8 *ies = ((struct ieee80211_mgmt *) + (skb->data))->u.probe_resp.variable; + size_t ies_len = skb->len - (ies - (u8 *)(skb->data)); + + supp_rate_ie = xradio_get_ie(ies, ies_len, WLAN_EID_SUPP_RATES); + save_rate_ie=supp_rate_ie[2]; + txrx_printk(XRADIO_DBG_WARN, "[netest]: save_rate_ie=%2x\n", + save_rate_ie); + } + + if (ieee80211_is_assoc_resp(frame->frame_control) && + !arg->status && + !memcmp(ieee80211_get_SA(frame), nettest_bssid, ETH_ALEN)) { + u8 *supp_rate_ie2; + size_t ies_len; + u8 *ies = ((struct ieee80211_mgmt *) + (skb->data))->u.assoc_resp.variable; + ies_len = skb->len - (ies - (u8 *)(skb->data)); + supp_rate_ie2 = xradio_get_ie(ies, ies_len, WLAN_EID_SUPP_RATES); + + if ((supp_rate_ie2[1]==1) && (supp_rate_ie2[2]==0x80)) { + supp_rate_ie2[2] = save_rate_ie; + txrx_printk(XRADIO_DBG_WARN, "[netest]: rate_ie modified=%2x\n", supp_rate_ie2[2]); + } + } + /*huanglu, for test*/ +#endif + + if (ieee80211_is_beacon(frame->frame_control) && + !arg->status && + !memcmp(ieee80211_get_SA(frame), priv->join_bssid,ETH_ALEN)) { + const u8 *tim_ie; + u8 *ies; + size_t ies_len; + priv->disable_beacon_filter = false; + queue_work(hw_priv->workqueue, &priv->update_filtering_work); + ies = ((struct ieee80211_mgmt *) + (skb->data))->u.beacon.variable; + ies_len = skb->len - (ies - (u8 *)(skb->data)); + + tim_ie = xradio_get_ie(ies, ies_len, WLAN_EID_TIM); + if (tim_ie) { + struct ieee80211_tim_ie *tim = + (struct ieee80211_tim_ie *)&tim_ie[2]; + + if (priv->join_dtim_period != tim->dtim_period) { + priv->join_dtim_period = tim->dtim_period; + queue_work(hw_priv->workqueue, + &priv->set_beacon_wakeup_period_work); + } + } + if (unlikely(priv->disable_beacon_filter)) { + priv->disable_beacon_filter = false; + queue_work(hw_priv->workqueue, + &priv->update_filtering_work); + } + } +#ifdef AP_HT_CAP_UPDATE + if (priv->mode == NL80211_IFTYPE_AP && + ieee80211_is_beacon(frame->frame_control) && + ((priv->ht_info&HT_INFO_MASK) != 0x0011) && + !arg->status){ + u8 *ies; + size_t ies_len; + const u8 *ht_cap; + ies = ((struct ieee80211_mgmt *)(skb->data))->u.beacon.variable; + ies_len = skb->len - (ies - (u8 *)(skb->data)); + ht_cap = xradio_get_ie(ies, ies_len, WLAN_EID_HT_CAPABILITY); + if(!ht_cap) { + priv->ht_info |= 0x0011; + queue_work(hw_priv->workqueue, &priv->ht_info_update_work); + } + } +#endif + +#ifdef AP_HT_COMPAT_FIX + if (ieee80211_is_mgmt(frame->frame_control) && + priv->if_id == 0 && !(priv->ht_compat_det & 0x10)) { + xradio_remove_ht_ie(priv, skb); + } +#endif + +#ifdef ROAM_OFFLOAD + if ((ieee80211_is_beacon(frame->frame_control)||ieee80211_is_probe_resp(frame->frame_control)) && + !arg->status ) { + if (hw_priv->auto_scanning && !atomic_read(&hw_priv->scan.in_progress)) + hw_priv->frame_rcvd = 1; + + if (!memcmp(ieee80211_get_SA(frame), priv->join_bssid, ETH_ALEN)) { + if (hw_priv->beacon) + dev_kfree_skb(hw_priv->beacon); + hw_priv->beacon = skb_copy(skb, GFP_ATOMIC); + if (!hw_priv->beacon) + txrx_printk(XRADIO_DBG_ERROR, "sched_scan: own beacon storing failed\n"); + } + } +#endif /*ROAM_OFFLOAD*/ + + //don't delay scan before next connect, yangfh. + if (ieee80211_is_deauth(frame->frame_control) || + ieee80211_is_disassoc(frame->frame_control)) + hw_priv->connet_time[priv->if_id] = 0; + + /* Stay awake for 1sec. after frame is received to give + * userspace chance to react and acquire appropriate + * wakelock. */ + if (ieee80211_is_auth(frame->frame_control)) + grace_period = 5 * HZ; + else if (ieee80211_is_deauth(frame->frame_control)) + grace_period = 5 * HZ; + else + grace_period = HZ; + + if (ieee80211_is_data(frame->frame_control)) + xradio_rx_h_ba_stat(priv, hdrlen, skb->len); + + xradio_pm_stay_awake(&hw_priv->pm_state, grace_period); + +#ifdef CONFIG_XRADIO_TESTMODE + if (hw_priv->test_frame.len > 0 && + priv->mode == NL80211_IFTYPE_STATION) { + if (xradio_frame_test_detection(priv, frame, skb) == 1) { + consume_skb(skb); + *skb_p = NULL; + return; + } + } +#endif /* CONFIG_XRADIO_TESTMODE */ + +#if (defined(CONFIG_XRADIO_DEBUG)) + //parsse frame here for debug. + if(rxparse_flags) + xradio_parse_frame(skb->data, parse_iv_len, rxparse_flags|PF_RX, priv->if_id); +#endif + + if(xradio_realloc_resv_skb(hw_priv, *skb_p)) { + *skb_p = NULL; + return; + } + /* Try to a packet for the case dev_alloc_skb failed in bh.*/ + if (unlikely(xradio_itp_rxed(hw_priv, skb))) + consume_skb(skb); + else if (unlikely(early_data)) { + spin_lock_bh(&priv->ps_state_lock); + /* Double-check status with lock held */ + if (entry->status == XRADIO_LINK_SOFT) { + skb_queue_tail(&entry->rx_queue, skb); + txrx_printk(XRADIO_DBG_WARN, "***skb_queue_tail\n"); + } else + ieee80211_rx_irqsafe(priv->hw, skb); + spin_unlock_bh(&priv->ps_state_lock); + } else { + ieee80211_rx_irqsafe(priv->hw, skb); + } + *skb_p = NULL; + + return; + +drop: + /* TODO: update failure counters */ + return; +} + +/* ******************************************************************** */ +/* Security */ + +int xradio_alloc_key(struct xradio_common *hw_priv) +{ + int idx; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + idx = ffs(~hw_priv->key_map) - 1; + if (idx < 0 || idx > WSM_KEY_MAX_INDEX) + return -1; + + hw_priv->key_map |= BIT(idx); + hw_priv->keys[idx].entryIndex = idx; + txrx_printk(XRADIO_DBG_NIY,"%s, idx=%d\n", __func__, idx); + return idx; +} + +void xradio_free_key(struct xradio_common *hw_priv, int idx) +{ + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + SYS_BUG(!(hw_priv->key_map & BIT(idx))); + memset(&hw_priv->keys[idx], 0, sizeof(hw_priv->keys[idx])); + hw_priv->key_map &= ~BIT(idx); + txrx_printk(XRADIO_DBG_NIY,"%s, idx=%d\n", __func__, idx); +} + +void xradio_free_keys(struct xradio_common *hw_priv) +{ + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + memset(&hw_priv->keys, 0, sizeof(hw_priv->keys)); + hw_priv->key_map = 0; +} + +int xradio_upload_keys(struct xradio_vif *priv) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + int idx, ret = 0; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + for (idx = 0; idx <= WSM_KEY_MAX_IDX; ++idx) + if (hw_priv->key_map & BIT(idx)) { + ret = wsm_add_key(hw_priv, &hw_priv->keys[idx], priv->if_id); + if (ret < 0) + break; + } + return ret; +} +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) +/* Workaround for WFD test case 6.1.10 */ +void xradio_link_id_reset(struct work_struct *work) +{ + struct xradio_vif *priv = + container_of(work, struct xradio_vif, linkid_reset_work); + struct xradio_common *hw_priv = priv->hw_priv; + int temp_linkid; + txrx_printk(XRADIO_DBG_TRC,"%s\n", __func__); + + if (!priv->action_linkid) { + /* In GO mode we can receive ACTION frames without a linkID */ + temp_linkid = xradio_alloc_link_id(priv, + &priv->action_frame_sa[0]); + SYS_WARN(!temp_linkid); + if (temp_linkid) { + /* Make sure we execute the WQ */ + flush_workqueue(hw_priv->workqueue); + /* Release the link ID */ + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[temp_linkid - 1].prev_status = + priv->link_id_db[temp_linkid - 1].status; + priv->link_id_db[temp_linkid - 1].status = + XRADIO_LINK_RESET; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, + &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } else { + spin_lock_bh(&priv->ps_state_lock); + priv->link_id_db[priv->action_linkid - 1].prev_status = + priv->link_id_db[priv->action_linkid - 1].status; + priv->link_id_db[priv->action_linkid - 1].status = + XRADIO_LINK_RESET_REMAP; + spin_unlock_bh(&priv->ps_state_lock); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->link_id_work) <= 0) + wsm_unlock_tx(hw_priv); + flush_workqueue(hw_priv->workqueue); + } +} +#endif diff --git a/drivers/net/wireless/xradio/txrx.h b/drivers/net/wireless/xradio/txrx.h new file mode 100644 index 0000000..7bec1fc --- /dev/null +++ b/drivers/net/wireless/xradio/txrx.h @@ -0,0 +1,96 @@ +/* + * txrx interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_TXRX_H +#define XRADIO_TXRX_H + +#include + +/* extern */ struct ieee80211_hw; +/* extern */ struct sk_buff; +/* extern */ struct wsm_tx; +/* extern */ struct wsm_rx; +/* extern */ struct wsm_tx_confirm; +/* extern */ struct xradio_txpriv; +/* extern */ struct xradio_vif; + +struct tx_policy { + union { + __le32 tbl[3]; + u8 raw[12]; + }; + u8 defined; /* TODO: u32 or u8, profile and select best */ + u8 usage_count; /* --// -- */ + u8 retry_count; /* --// -- */ + u8 uploaded; +}; + +struct tx_policy_cache_entry { + struct tx_policy policy; + struct list_head link; +}; + +#define TX_POLICY_CACHE_SIZE (8) +struct tx_policy_cache { + struct tx_policy_cache_entry cache[TX_POLICY_CACHE_SIZE]; + struct list_head used; + struct list_head free; + spinlock_t lock; +}; + +/* ******************************************************************** */ +/* TX policy cache */ +/* Intention of TX policy cache is an overcomplicated WSM API. + * Device does not accept per-PDU tx retry sequence. + * It uses "tx retry policy id" instead, so driver code has to sync + * linux tx retry sequences with a retry policy table in the device. + */ +void tx_policy_init(struct xradio_common *hw_priv); +void tx_policy_upload_work(struct work_struct *work); + +/* ******************************************************************** */ +/* TX implementation */ + +u32 xradio_rate_mask_to_wsm(struct xradio_common *hw_priv, + u32 rates); +void xradio_tx(struct ieee80211_hw *dev, struct sk_buff *skb); +void xradio_skb_dtor(struct xradio_common *hw_priv, + struct sk_buff *skb, + const struct xradio_txpriv *txpriv); + +/* ******************************************************************** */ +/* WSM callbacks */ + +void xradio_tx_confirm_cb(struct xradio_common *hw_priv, + struct wsm_tx_confirm *arg); +void xradio_rx_cb(struct xradio_vif *priv, + struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* ******************************************************************** */ +/* Timeout */ + +void xradio_tx_timeout(struct work_struct *work); + +/* ******************************************************************** */ +/* Security */ +int xradio_alloc_key(struct xradio_common *hw_priv); +void xradio_free_key(struct xradio_common *hw_priv, int idx); +void xradio_free_keys(struct xradio_common *hw_priv); +int xradio_upload_keys(struct xradio_vif *priv); + +/* ******************************************************************** */ +/* Workaround for WFD test case 6.1.10 */ +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) +void xradio_link_id_reset(struct work_struct *work); +#endif + +#endif /* XRADIO_TXRX_H */ diff --git a/drivers/net/wireless/xradio/wsm.c b/drivers/net/wireless/xradio/wsm.c new file mode 100644 index 0000000..1acc259 --- /dev/null +++ b/drivers/net/wireless/xradio/wsm.c @@ -0,0 +1,3274 @@ +/* + * WSM host interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include "xradio.h" +#include "wsm.h" +#include "bh.h" +#include "itp.h" +#ifdef ROAM_OFFLOAD +#include "sta.h" +#endif /*ROAM_OFFLOAD*/ + + +#define WSM_CMD_TIMEOUT (2 * HZ) /* With respect to interrupt loss */ +#define WSM_CMD_JOIN_TIMEOUT (7 * HZ) /* Join timeout is 5 sec. in FW */ +#define WSM_CMD_START_TIMEOUT (7 * HZ) +#define WSM_CMD_RESET_TIMEOUT (3 * HZ) /* 2 sec. timeout was observed. */ +#define WSM_CMD_DEFAULT_TIMEOUT (3 * HZ) +#define WSM_SKIP(buf, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + goto underflow; \ + (buf)->data += size; \ + } while (0) + +#define WSM_GET(buf, ptr, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + goto underflow; \ + memcpy(ptr, (buf)->data, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_GET(buf, type, cvt) \ + ({ \ + type val; \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + goto underflow; \ + val = cvt(*(type *)(buf)->data); \ + (buf)->data += sizeof(type); \ + val; \ + }) + +#define WSM_GET8(buf) __WSM_GET(buf, u8, (u8)) +#define WSM_GET16(buf) __WSM_GET(buf, u16, __le16_to_cpu) +#define WSM_GET32(buf) __WSM_GET(buf, u32, __le32_to_cpu) + +#define WSM_PUT(buf, ptr, size) \ + do { \ + if (unlikely((buf)->data + size > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), size))) \ + goto nomem; \ + memcpy((buf)->data, ptr, size); \ + (buf)->data += size; \ + } while (0) + +#define __WSM_PUT(buf, val, type, cvt) \ + do { \ + if (unlikely((buf)->data + sizeof(type) > (buf)->end)) \ + if (unlikely(wsm_buf_reserve((buf), sizeof(type)))) \ + goto nomem; \ + *(type *)(buf)->data = cvt(val); \ + (buf)->data += sizeof(type); \ + } while (0) + +#define WSM_PUT8(buf, val) __WSM_PUT(buf, val, u8, (u8)) +#define WSM_PUT16(buf, val) __WSM_PUT(buf, val, u16, __cpu_to_le16) +#define WSM_PUT32(buf, val) __WSM_PUT(buf, val, u32, __cpu_to_le32) + +static void wsm_buf_reset(struct wsm_buf *buf); +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size); +static int get_interface_id_scanning(struct xradio_common *hw_priv); + +static int wsm_cmd_send(struct xradio_common *hw_priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo, int if_id); + +static struct xradio_vif + *wsm_get_interface_for_tx(struct xradio_common *hw_priv); + +static inline void wsm_cmd_lock(struct xradio_common *hw_priv) +{ + mutex_lock(&hw_priv->wsm_cmd_mux); +} + +static inline void wsm_cmd_unlock(struct xradio_common *hw_priv) +{ + mutex_unlock(&hw_priv->wsm_cmd_mux); +} + +static inline void wsm_oper_lock(struct xradio_common *hw_priv) +{ + mutex_lock(&hw_priv->wsm_oper_lock); +} + +static inline void wsm_oper_unlock(struct xradio_common *hw_priv) +{ + mutex_unlock(&hw_priv->wsm_oper_lock); +} + +/* ******************************************************************** */ +/* WSM API implementation */ + +static int wsm_generic_confirm(struct xradio_common *hw_priv, + void *arg, + struct wsm_buf *buf) +{ + u32 status = WSM_GET32(buf); + if (status != WSM_STATUS_SUCCESS) + return -EINVAL; + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +#if defined(DGB_XRADIO_HWT) +int wsm_hwt_cmd(struct xradio_common *hw_priv, void *arg, size_t arg_size) +{ + int ret = 0; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + WSM_PUT(buf, arg, arg_size); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0024, WSM_CMD_TIMEOUT, -1); + wsm_cmd_unlock(hw_priv); + + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} +#endif + +#ifdef XR_RRM//RadioResourceMeasurement +static int wsm_start_measure_requset(struct xradio_common *hw_priv, + MEASUREMENT_PARAMETERS *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + ret = wsm_cmd_send(hw_priv, buf, arg, 0x000E, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + + nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; + +} + +int wsm_11k_measure_requset(struct xradio_common *hw_priv, + u8 measure_type, + u16 ChannelNum, + u16 Duration) +{ + int ret; + u8 type, sub_type; + MEASUREMENT_PARAMETERS rrm_paras; + LMAC_MEAS_REQUEST *rrm_req = &rrm_paras.MeasurementRequest; +// LMAC_MEAS_CHANNEL_LOAD_PARAMS *rrm_req = &rrm_paras.MeasurementRequest; + rrm_paras.TxPowerLevel = 0x11; + rrm_paras.DurationMandatory = 0x22; + rrm_paras.MeasurementRequestLength = 0x33; + + type = (measure_type&0xf0)>>4; + sub_type = measure_type&0xf; + rrm_paras.MeasurementType = type; +// if (measure_type == ChannelLoadMeasurement) { + if (type == ChannelLoadMeasurement) { + rrm_req->ChannelLoadParams.Reserved = 0; + rrm_req->ChannelLoadParams.ChannelLoadCCA = sub_type; + rrm_req->ChannelLoadParams.ChannelNum = ChannelNum; + //valid when channelload measure, interval bettween request&start + rrm_req->ChannelLoadParams.RandomInterval = 0; + //unit:1TU=1024us + rrm_req->ChannelLoadParams.MeasurementDuration = Duration; + rrm_req->ChannelLoadParams.MeasurementStartTimel = 0; + rrm_req->ChannelLoadParams.MeasurementStartTimeh = 0; + } else if (type == NoiseHistrogramMeasurement) { + rrm_req->NoisHistogramParams.Reserved = 0; + rrm_req->NoisHistogramParams.IpiRpi = sub_type; + rrm_req->NoisHistogramParams.ChannelNum = ChannelNum; + rrm_req->NoisHistogramParams.RandomInterval = 0; + rrm_req->NoisHistogramParams.MeasurementDuration = Duration; + rrm_req->NoisHistogramParams.MeasurementStartTimel = 0; + rrm_req->NoisHistogramParams.MeasurementStartTimeh = 0; + } + ret = wsm_start_measure_requset(hw_priv, &rrm_paras, 0); + + return ret; +} + + +#endif//RadioResourceMeasurement +int wsm_configuration(struct xradio_common *hw_priv, + struct wsm_configuration *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT32(buf, arg->dot11MaxTransmitMsduLifeTime); + WSM_PUT32(buf, arg->dot11MaxReceiveLifeTime); + WSM_PUT32(buf, arg->dot11RtsThreshold); + + /* DPD block. */ + WSM_PUT16(buf, arg->dpdData_size + 12); + WSM_PUT16(buf, 1); /* DPD version */ + WSM_PUT(buf, arg->dot11StationId, ETH_ALEN); + WSM_PUT16(buf, 5); /* DPD flags */ + WSM_PUT(buf, arg->dpdData, arg->dpdData_size); + + ret = wsm_cmd_send(hw_priv, buf, arg, 0x0009, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +static int wsm_configuration_confirm(struct xradio_common *hw_priv, + struct wsm_configuration *arg, + struct wsm_buf *buf) +{ + int i; + int status; + + status = WSM_GET32(buf); + if (SYS_WARN(status != WSM_STATUS_SUCCESS)) + return -EINVAL; + + WSM_GET(buf, arg->dot11StationId, ETH_ALEN); + arg->dot11FrequencyBandsSupported = WSM_GET8(buf); + WSM_SKIP(buf, 1); + arg->supportedRateMask = WSM_GET32(buf); + for (i = 0; i < 2; ++i) { + arg->txPowerRange[i].min_power_level = WSM_GET32(buf); + arg->txPowerRange[i].max_power_level = WSM_GET32(buf); + arg->txPowerRange[i].stepping = WSM_GET32(buf); + } + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +/* ******************************************************************** */ +/*forcing upper layer to restart wifi.*/ +void wsm_upper_restart(struct xradio_common *hw_priv) +{ + int i = 0; + struct xradio_vif *priv = NULL; + wsm_printk(XRADIO_DBG_ERROR, "%s\n", __func__); +#ifdef CONFIG_PM + xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ); +#endif + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) && 0 + spin_lock(&hw_priv->vif_list_lock); + xradio_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + wsm_printk(XRADIO_DBG_WARN, "%s driver_hang_notify\n", __func__); + } + spin_unlock(&hw_priv->vif_list_lock); +#elif defined(HW_RESTART) + if (!work_pending(&hw_priv->hw_restart_work)) { + bool reset = false; + spin_lock(&hw_priv->vif_list_lock); + xradio_for_each_vif(hw_priv, priv, i) { + if (priv) { + reset = true; + break; + } + } + spin_unlock(&hw_priv->vif_list_lock); + if (reset) { + wsm_cmd_lock(hw_priv); + hw_priv->hw_restart = true; + wsm_cmd_unlock(hw_priv); + /* wait for scan complete.*/ + wsm_printk(XRADIO_DBG_WARN, "Wait for scan complete!\n"); + down(&hw_priv->scan.lock); + mutex_lock(&hw_priv->conf_mutex); + /* Unlock wsm_oper_lock since no confirms of wsm_oper_locks.*/ + if (!mutex_trylock(&hw_priv->wsm_oper_lock)) + wsm_printk(XRADIO_DBG_WARN, "oper_lock may be locked!\n"); + mutex_unlock(&hw_priv->wsm_oper_lock); + mutex_unlock(&hw_priv->conf_mutex); + up(&hw_priv->scan.lock); + msleep(200); + schedule_work(&hw_priv->hw_restart_work); + wsm_printk(XRADIO_DBG_WARN, "%s schedule restart_work!\n", __func__); + } + } +#endif +} + +void wsm_query_work(struct work_struct *work) +{ + struct xradio_common *hw_priv = + container_of(work, struct xradio_common, query_work); + u8 ret[100] = {0}; + wsm_printk(XRADIO_DBG_ERROR, "%s\n", __func__); + + *(u32*)&ret[0] = hw_priv->query_packetID; + wsm_read_mib(hw_priv, WSM_MIB_ID_REQ_PKT_STATUS, (void*)&ret[0], sizeof(ret), 4); + if(!ret[4]) { + wsm_printk(XRADIO_DBG_ERROR,"QuerypktID=0x%08x, status=0x%x, retry=%d, flags=0x%x, PktDebug=0x%x\n" \ + "pktqueue=0x%x, ext1=%d, ext2=%d, ext3=%d, ext4=0x%x, ext5=0x%x\n", + *(u32*)&ret[0], ret[6], ret[7], *(u32*)&ret[8], *(u32*)&ret[12], + ret[44], ret[45], ret[46], ret[47], ret[48], ret[49]); + wsm_printk(XRADIO_DBG_ERROR,"interdebug=0x%x, 0x%x, 0x%x, Soure=0x%x, 0x%x, 0x%x\n" \ + "interuse=%d, external=%d, TxOutstanding=%d, QueueStatus=0x%x, BA0=0x%x, BA1=0x%x\n" \ + "ScanStatus=0x%x, scanNULL=0x%x, wr_state=0x%x,0x%x,0x%x,0x%x," \ + "wr_cnt=%d, %d, %d, %d\n", + *(u32*)&ret[16], *(u32*)&ret[20], *(u32*)&ret[24], ret[28], ret[29], ret[30], + ret[32], ret[33], ret[34], ret[35], *(u32*)&ret[36], *(u32*)&ret[40], + ret[50], ret[51], ret[52], ret[53], ret[54], ret[55], + *(u16*)&ret[56], *(u16*)&ret[58], *(u16*)&ret[60], *(u16*)&ret[62]); + } else { + ret[5] = 0; + wsm_printk(XRADIO_DBG_ERROR,"No req packid=0x%08x!\n", *(u32*)&ret[0]); + } + //hardware error occurs, try to restart wifi. + if(ret[5] & 0x4) { + wsm_printk(XRADIO_DBG_ERROR,"Hardware need to reset 0x%x.\n", ret[5]); + hw_priv->bh_error = 1; +#ifdef BH_USE_SEMAPHORE + up(&hw_priv->bh_sem); +#else + wake_up(&hw_priv->bh_wq); +#endif + } + hw_priv->query_packetID = 0; +} + +/* ******************************************************************** */ + +int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + u16 cmd = 0x000A | WSM_TX_LINK_ID(arg->link_id); + + wsm_cmd_lock(hw_priv); + + WSM_PUT32(buf, arg->reset_statistics ? 0 : 1); + ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_RESET_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +struct wsm_mib { + u16 mibId; + void *buf; + size_t buf_size; +}; + +int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf, + size_t buf_size, size_t arg_size) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + wsm_cmd_lock(hw_priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, arg_size); + WSM_PUT(buf, _buf, arg_size); + + ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0005, WSM_CMD_TIMEOUT, -1); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +static int wsm_read_mib_confirm(struct xradio_common *hw_priv, + struct wsm_mib *arg, + struct wsm_buf *buf) +{ + u16 size; + if (SYS_WARN(WSM_GET32(buf) != WSM_STATUS_SUCCESS)) + return -EINVAL; + + if (SYS_WARN(WSM_GET16(buf) != arg->mibId)) + return -EINVAL; + + size = WSM_GET16(buf); + if (size > arg->buf_size) + size = arg->buf_size; + + WSM_GET(buf, arg->buf, size); + arg->buf_size = size; + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *_buf, + size_t buf_size, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + struct wsm_mib mib_buf = { + .mibId = mibId, + .buf = _buf, + .buf_size = buf_size, + }; + + wsm_cmd_lock(hw_priv); + + WSM_PUT16(buf, mibId); + WSM_PUT16(buf, buf_size); + WSM_PUT(buf, _buf, buf_size); + + ret = wsm_cmd_send(hw_priv, buf, &mib_buf, 0x0006, WSM_CMD_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +static int wsm_write_mib_confirm(struct xradio_common *hw_priv, + struct wsm_mib *arg, + struct wsm_buf *buf, + int interface_link_id) +{ + int ret; + int i; + struct xradio_vif *priv; + ret = wsm_generic_confirm(hw_priv, arg, buf); + if (ret) + return ret; + + /*wsm_set_operational_mode confirm.*/ + if (arg->mibId == 0x1006) { + const char *p = arg->buf; + bool powersave_enabled = (p[0] & 0x0F) ? true : false; + + /* update vif PM status. */ + priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); + if (priv) { + xradio_enable_powersave(priv, powersave_enabled); + spin_unlock(&priv->vif_lock); + } + + /* HW powersave base on vif except for generic vif. */ + spin_lock(&hw_priv->vif_list_lock); + xradio_for_each_vif(hw_priv, priv, i) { +#ifdef P2P_MULTIVIF + if ((i == (XRWL_MAX_VIFS - 1)) || !priv) +#else + if (!priv) +#endif + continue; + powersave_enabled &= !!priv->powersave_enabled; + } + hw_priv->powersave_enabled = powersave_enabled; + spin_unlock(&hw_priv->vif_list_lock); + + } + return 0; +} + +/* ******************************************************************** */ + +int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg, + int if_id) +{ + int i; + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + if (unlikely(arg->numOfChannels > 48)) + return -EINVAL; + + if (unlikely(arg->numOfSSIDs > WSM_SCAN_MAX_NUM_OF_SSIDS)) + return -EINVAL; + + if (unlikely(arg->band > 1)) + return -EINVAL; + + wsm_oper_lock(hw_priv); + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->band); + WSM_PUT8(buf, arg->scanType); + WSM_PUT8(buf, arg->scanFlags); + WSM_PUT8(buf, arg->maxTransmitRate); + WSM_PUT32(buf, arg->autoScanInterval); + WSM_PUT8(buf, arg->numOfProbeRequests); + WSM_PUT8(buf, arg->numOfChannels); + WSM_PUT8(buf, arg->numOfSSIDs); + WSM_PUT8(buf, arg->probeDelay); + + for (i = 0; i < arg->numOfChannels; ++i) { + WSM_PUT16(buf, arg->ch[i].number); + WSM_PUT16(buf, 0); + WSM_PUT32(buf, arg->ch[i].minChannelTime); + WSM_PUT32(buf, arg->ch[i].maxChannelTime); + WSM_PUT32(buf, 0); + } + + for (i = 0; i < arg->numOfSSIDs; ++i) { + WSM_PUT32(buf, arg->ssids[i].length); + WSM_PUT(buf, &arg->ssids[i].ssid[0], + sizeof(arg->ssids[i].ssid)); + } + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0007, WSM_CMD_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + if (ret) + wsm_oper_unlock(hw_priv); +#ifdef HW_RESTART + else if (hw_priv->hw_restart) + wsm_oper_unlock(hw_priv); +#endif + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_oper_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_stop_scan(struct xradio_common *hw_priv, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + wsm_cmd_lock(hw_priv); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0008, WSM_CMD_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + return ret; +} + + +static int wsm_tx_confirm(struct xradio_common *hw_priv, + struct wsm_buf *buf, + int interface_link_id) +{ + struct wsm_tx_confirm tx_confirm; + + tx_confirm.packetID = WSM_GET32(buf); + tx_confirm.status = WSM_GET32(buf); + tx_confirm.txedRate = WSM_GET8(buf); + tx_confirm.ackFailures = WSM_GET8(buf); + tx_confirm.flags = WSM_GET16(buf); + tx_confirm.rate_try[0] = WSM_GET32(buf); + tx_confirm.rate_try[1] = WSM_GET32(buf); + tx_confirm.rate_try[2] = WSM_GET32(buf); + tx_confirm.mediaDelay = WSM_GET32(buf); + tx_confirm.txQueueDelay = WSM_GET32(buf); + + if (is_hardware_xradio(hw_priv)) { + /* TODO:COMBO:linkID will be stored in packetID*/ + /* TODO:COMBO: Extract traffic resumption map */ + tx_confirm.if_id = xradio_queue_get_if_id(tx_confirm.packetID); + tx_confirm.link_id = xradio_queue_get_link_id( + tx_confirm.packetID); + } else { + tx_confirm.link_id = interface_link_id; + tx_confirm.if_id = 0; + } + + wsm_release_vif_tx_buffer(hw_priv, tx_confirm.if_id, 1); + + if (hw_priv->wsm_cbc.tx_confirm) + hw_priv->wsm_cbc.tx_confirm(hw_priv, &tx_confirm); + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +static int wsm_multi_tx_confirm(struct xradio_common *hw_priv, + struct wsm_buf *buf, int interface_link_id) +{ + struct xradio_vif *priv; + int ret; + int count; + int i; + + count = WSM_GET32(buf); + if (SYS_WARN(count <= 0)) + return -EINVAL; + else if (count > 1) { + ret = wsm_release_tx_buffer(hw_priv, count - 1); + if (ret < 0) + return ret; + else if (ret > 0) + xradio_bh_wakeup(hw_priv); + } + priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); + if (priv) { + xradio_debug_txed_multi(priv, count); + spin_unlock(&priv->vif_lock); + } + for (i = 0; i < count; ++i) { + ret = wsm_tx_confirm(hw_priv, buf, interface_link_id); + if (ret) + return ret; + } + return ret; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +/* ******************************************************************** */ + +static int wsm_join_confirm(struct xradio_common *hw_priv, + struct wsm_join *arg, + struct wsm_buf *buf) +{ + if (WSM_GET32(buf) != WSM_STATUS_SUCCESS) + return -EINVAL; + arg->minPowerLevel = WSM_GET32(buf); + arg->maxPowerLevel = WSM_GET32(buf); + + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg, + int if_id) +/*TODO: combo: make it work per vif.*/ +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_oper_lock(hw_priv); + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT(buf, &arg->bssid[0], sizeof(arg->bssid)); + WSM_PUT16(buf, arg->atimWindow); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeForJoin); + WSM_PUT8(buf, arg->dtimPeriod); + WSM_PUT8(buf, arg->flags); + WSM_PUT32(buf, arg->ssidLength); + WSM_PUT(buf, &arg->ssid[0], sizeof(arg->ssid)); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT32(buf, arg->basicRateSet); + + hw_priv->tx_burst_idx = -1; + ret = wsm_cmd_send(hw_priv, buf, arg, 0x000B, WSM_CMD_JOIN_TIMEOUT, + if_id); + wsm_cmd_unlock(hw_priv); + wsm_oper_unlock(hw_priv); /*confirm, not indcation.*/ + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_oper_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_bss_params(struct xradio_common *hw_priv, + const struct wsm_set_bss_params *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->beaconLostCount); + WSM_PUT16(buf, arg->aid); + WSM_PUT32(buf, arg->operationalRateSet); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0011, WSM_CMD_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT(buf, arg, sizeof(*arg)); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000C, WSM_CMD_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_remove_key(struct xradio_common *hw_priv, + const struct wsm_remove_key *arg, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->entryIndex); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x000D, WSM_CMD_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_tx_queue_params(struct xradio_common *hw_priv, + const struct wsm_set_tx_queue_params *arg, + u8 id, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + u8 queue_id_to_wmm_aci[] = {3, 2, 0, 1}; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, queue_id_to_wmm_aci[id]); + WSM_PUT8(buf, 0); + WSM_PUT8(buf, arg->ackPolicy); + WSM_PUT8(buf, 0); + WSM_PUT32(buf, arg->maxTransmitLifetime); + WSM_PUT16(buf, arg->allowedMediumTime); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0012, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_edca_params(struct xradio_common *hw_priv, + const struct wsm_edca_params *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + /* Implemented according to specification. */ + + WSM_PUT16(buf, arg->params[3].cwMin); + WSM_PUT16(buf, arg->params[2].cwMin); + WSM_PUT16(buf, arg->params[1].cwMin); + WSM_PUT16(buf, arg->params[0].cwMin); + + WSM_PUT16(buf, arg->params[3].cwMax); + WSM_PUT16(buf, arg->params[2].cwMax); + WSM_PUT16(buf, arg->params[1].cwMax); + WSM_PUT16(buf, arg->params[0].cwMax); + + WSM_PUT8(buf, arg->params[3].aifns); + WSM_PUT8(buf, arg->params[2].aifns); + WSM_PUT8(buf, arg->params[1].aifns); + WSM_PUT8(buf, arg->params[0].aifns); + + WSM_PUT16(buf, arg->params[3].txOpLimit); + WSM_PUT16(buf, arg->params[2].txOpLimit); + WSM_PUT16(buf, arg->params[1].txOpLimit); + WSM_PUT16(buf, arg->params[0].txOpLimit); + + WSM_PUT32(buf, arg->params[3].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[2].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[1].maxReceiveLifetime); + WSM_PUT32(buf, arg->params[0].maxReceiveLifetime); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0013, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_switch_channel(struct xradio_common *hw_priv, + const struct wsm_switch_channel *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_lock_tx(hw_priv); + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->channelMode); + WSM_PUT8(buf, arg->channelSwitchCount); + WSM_PUT16(buf, arg->newChannelNumber); + + hw_priv->channel_switch_in_progress = 1; + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0016, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + if (ret) { + wsm_unlock_tx(hw_priv); + hw_priv->channel_switch_in_progress = 0; + } + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_unlock_tx(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_oper_lock(hw_priv); + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->pmMode); + WSM_PUT8(buf, arg->fastPsmIdlePeriod); + WSM_PUT8(buf, arg->apPsmChangePeriod); + WSM_PUT8(buf, arg->minAutoPsPollPeriod); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0010, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + if (ret) + wsm_oper_unlock(hw_priv); +#ifdef HW_RESTART + else if (hw_priv->hw_restart) + wsm_oper_unlock(hw_priv); +#endif + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + wsm_oper_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT8(buf, arg->mode); + WSM_PUT8(buf, arg->band); + WSM_PUT16(buf, arg->channelNumber); + WSM_PUT32(buf, arg->CTWindow); + WSM_PUT32(buf, arg->beaconInterval); + WSM_PUT8(buf, arg->DTIMPeriod); + WSM_PUT8(buf, arg->preambleType); + WSM_PUT8(buf, arg->probeDelay); + WSM_PUT8(buf, arg->ssidLength); + WSM_PUT(buf, arg->ssid, sizeof(arg->ssid)); + WSM_PUT32(buf, arg->basicRateSet); + + hw_priv->tx_burst_idx = -1; + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0017, WSM_CMD_START_TIMEOUT, + if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +#if 0 +/* This API is no longer present in WSC */ +/* ******************************************************************** */ + +int wsm_beacon_transmit(struct xradio_common *hw_priv, + const struct wsm_beacon_transmit *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT32(buf, arg->enableBeaconing ? 1 : 0); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0018, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} +#endif + +/* ******************************************************************** */ + +int wsm_start_find(struct xradio_common *hw_priv, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x0019, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_stop_find(struct xradio_common *hw_priv, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001A, WSM_CMD_TIMEOUT, if_id); + wsm_cmd_unlock(hw_priv); + return ret; +} + +/* ******************************************************************** */ + +int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg, + int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + u16 cmd = 0x001C; + + wsm_cmd_lock(hw_priv); + + WSM_PUT(buf, &arg->mac_addr[0], sizeof(arg->mac_addr)); + + if (is_hardware_xradio(hw_priv)) { + WSM_PUT8(buf, arg->unmap); + WSM_PUT8(buf, arg->link_id); + } else { + cmd |= WSM_TX_LINK_ID(arg->link_id); + WSM_PUT16(buf, 0); + } + + ret = wsm_cmd_send(hw_priv, buf, NULL, cmd, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; +} + +/* ******************************************************************** */ + +int wsm_update_ie(struct xradio_common *hw_priv, + const struct wsm_update_ie *arg, int if_id) +{ + int ret; + struct wsm_buf *buf = &hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(hw_priv); + + WSM_PUT16(buf, arg->what); + WSM_PUT16(buf, arg->count); + WSM_PUT(buf, arg->ies, arg->length); + + ret = wsm_cmd_send(hw_priv, buf, NULL, 0x001B, WSM_CMD_TIMEOUT, if_id); + + wsm_cmd_unlock(hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(hw_priv); + return -ENOMEM; + +} +/* ******************************************************************** */ +#ifdef MCAST_FWDING +/* 3.66 */ +static int wsm_give_buffer_confirm(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + wsm_printk(XRADIO_DBG_MSG, "HW Buf count %d\n", hw_priv->hw_bufs_used); + if (!hw_priv->hw_bufs_used) + wake_up(&hw_priv->bh_evt_wq); + + return 0; +} + +/* 3.65 */ +int wsm_init_release_buffer_request(struct xradio_common *hw_priv, u8 index) +{ + struct wsm_buf *buf = &hw_priv->wsm_release_buf[index]; + u16 cmd = 0x0022; /* Buffer Request */ + u8 flags; + size_t buf_len; + + wsm_buf_init(buf); + + flags = index ? 0: 0x1; + + WSM_PUT8(buf, flags); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + buf_len = buf->data - buf->begin; + + /* Fill HI message header */ + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd); + + return 0; +nomem: + return -ENOMEM; +} + +/* 3.65 fixed memory leakage by yangfh*/ +int wsm_deinit_release_buffer(struct xradio_common *hw_priv) +{ + struct wsm_buf *buf = NULL; + int i, err = 0; + + for (i = 0; i < WSM_MAX_BUF; i++) { + buf = &hw_priv->wsm_release_buf[i]; + if(likely(buf)) { + if(likely(buf->begin)) + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; + } else { + err++; + } + } + if(err) wsm_printk(XRADIO_DBG_ERROR, "%s, NULL buf=%d!\n", __func__, err); + return 0; +} + +/* 3.68 */ +static int wsm_request_buffer_confirm(struct xradio_vif *priv, + u8 *arg, + struct wsm_buf *buf) +{ + u8 count; + u32 sta_asleep_mask = 0; + int i; + u32 mask = 0; + u32 change_mask = 0; + struct xradio_common *hw_priv = priv->hw_priv; + + /* There is no status field in this message */ + sta_asleep_mask = WSM_GET32(buf); + count = WSM_GET8(buf); + count -= 1; /* Current workaround for FW issue */ + + spin_lock_bh(&priv->ps_state_lock); + change_mask = (priv->sta_asleep_mask ^ sta_asleep_mask); + wsm_printk(XRADIO_DBG_MSG, "CM %x, HM %x, FWM %x\n", change_mask,priv->sta_asleep_mask, sta_asleep_mask); + spin_unlock_bh(&priv->ps_state_lock); + + if (change_mask) { + struct ieee80211_sta *sta; + int ret = 0; + + + for (i = 0; i < MAX_STA_IN_AP_MODE ; ++i) { + + if(XRADIO_LINK_HARD != priv->link_id_db[i].status) + continue; + + mask = BIT(i + 1); + + /* If FW state and host state for this link are different then notify OMAC */ + if(change_mask & mask) { + wsm_printk(XRADIO_DBG_MSG, "PS State Changed %d for sta %pM\n", (sta_asleep_mask & mask) ? 1:0, priv->link_id_db[i].mac); + rcu_read_lock(); + sta = ieee80211_find_sta(priv->vif, priv->link_id_db[i].mac); + if (!sta) { + wsm_printk(XRADIO_DBG_MSG, "WRBC - could not find sta %pM\n", + priv->link_id_db[i].mac); + } else { + ret = ieee80211_sta_ps_transition_ni(sta, (sta_asleep_mask & mask) ? true: false); + wsm_printk(XRADIO_DBG_MSG, "PS State NOTIFIED %d\n", ret); + SYS_WARN(ret); + } + rcu_read_unlock(); + } + } + /* Replace STA mask with one reported by FW */ + spin_lock_bh(&priv->ps_state_lock); + priv->sta_asleep_mask = sta_asleep_mask; + spin_unlock_bh(&priv->ps_state_lock); + } + + wsm_printk(XRADIO_DBG_MSG, "WRBC - HW Buf count %d SleepMask %d\n", + hw_priv->hw_bufs_used, sta_asleep_mask); + hw_priv->buf_released = 0; + SYS_WARN(count != (hw_priv->wsm_caps.numInpChBufs - 1)); + + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +/* 3.67 */ +int wsm_request_buffer_request(struct xradio_vif *priv, + u8 *arg) +{ + int ret; + struct wsm_buf *buf = &priv->hw_priv->wsm_cmd_buf; + + wsm_cmd_lock(priv->hw_priv); + + WSM_PUT8(buf, (*arg)); + WSM_PUT8(buf, 0); + WSM_PUT16(buf, 0); + + ret = wsm_cmd_send(priv->hw_priv, buf, arg, 0x0023, WSM_CMD_JOIN_TIMEOUT,priv->if_id); + + wsm_cmd_unlock(priv->hw_priv); + return ret; + +nomem: + wsm_cmd_unlock(priv->hw_priv); + return -ENOMEM; +} + +#endif + +int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + + priv->rx_filter.keepalive = enable; + return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); +} + +int wsm_set_probe_responder(struct xradio_vif *priv, bool enable) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + + priv->rx_filter.probeResponder = enable; + return wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id); +} +/* ******************************************************************** */ +/* WSM indication events implementation */ + +static int wsm_startup_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + u16 status; +#ifdef CONFIG_XRADIO_DEBUG + static const char * const fw_types[] = { + "ETF", + "WFM", + "WSM", + "HI test", + "Platform test" + }; +#endif + + hw_priv->wsm_caps.numInpChBufs = WSM_GET16(buf); + hw_priv->wsm_caps.sizeInpChBuf = WSM_GET16(buf); + hw_priv->wsm_caps.hardwareId = WSM_GET16(buf); + hw_priv->wsm_caps.hardwareSubId = WSM_GET16(buf); + status = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareCap = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareType = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareApiVer = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareBuildNumber = WSM_GET16(buf); + hw_priv->wsm_caps.firmwareVersion = WSM_GET16(buf); + WSM_GET(buf, &hw_priv->wsm_caps.fw_label[0], WSM_FW_LABEL); + hw_priv->wsm_caps.fw_label[WSM_FW_LABEL+1] = 0; /* Do not trust FW too much. */ + + if (SYS_WARN(status)) + return -EINVAL; + + if (SYS_WARN(hw_priv->wsm_caps.firmwareType > 4)) + return -EINVAL; + + wsm_printk(XRADIO_DBG_NIY, "%s\n" + " Input buffers: %d x %d bytes\n" + " Hardware: %d.%d\n" + " %s firmware ver: %d, build: %d," + " api: %d, cap: 0x%.4X\n", + __func__, + hw_priv->wsm_caps.numInpChBufs, + hw_priv->wsm_caps.sizeInpChBuf, + hw_priv->wsm_caps.hardwareId, + hw_priv->wsm_caps.hardwareSubId, + fw_types[hw_priv->wsm_caps.firmwareType], + hw_priv->wsm_caps.firmwareVersion, + hw_priv->wsm_caps.firmwareBuildNumber, + hw_priv->wsm_caps.firmwareApiVer, + hw_priv->wsm_caps.firmwareCap); + + wsm_printk(XRADIO_DBG_ALWY, "Firmware Label:%s\n", &hw_priv->wsm_caps.fw_label[0]); + + hw_priv->wsm_caps.firmwareReady = 1; + + wake_up(&hw_priv->wsm_startup_done); + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +//add by yangfh 2014-10-31 16:58:53 +void wms_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv) +{ + struct sk_buff *skb = NULL; + struct ieee80211_mgmt *deauth = NULL; + + if (priv->join_status == XRADIO_JOIN_STATUS_AP) { + int i = 0; + wsm_printk(XRADIO_DBG_WARN, "AP mode, send_deauth_to_self\n"); + for (i = 0; ilink_id_db[i].status == XRADIO_LINK_HARD) { + skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); + if (!skb) + return; + skb_reserve(skb, 64); + deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); + if(!deauth) { + SYS_WARN(1); + return; + } + deauth->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); + deauth->duration = 0; + memcpy(deauth->da, priv->vif->addr, ETH_ALEN); + memcpy(deauth->sa, priv->link_id_db[i].mac, ETH_ALEN); + memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN); + deauth->seq_ctrl = 0; + deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; + ieee80211_rx_irqsafe(priv->hw, skb); + } + } + } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + wsm_printk(XRADIO_DBG_WARN, "STA mode, send_deauth_to_self\n"); + skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); + if (!skb) + return; + skb_reserve(skb, 64); + deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); + if(!deauth) { + SYS_WARN(1); + return; + } + deauth->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DEAUTH); + deauth->duration = 0; + memcpy(deauth->da, priv->vif->addr, ETH_ALEN); + memcpy(deauth->sa, priv->join_bssid, ETH_ALEN); + memcpy(deauth->bssid, priv->join_bssid, ETH_ALEN); + deauth->seq_ctrl = 0; + deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING; + ieee80211_rx_irqsafe(priv->hw, skb); + } +} + +void wms_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv) +{ + struct sk_buff *skb = NULL; + struct ieee80211_mgmt *disassoc = NULL; + if (priv->join_status == XRADIO_JOIN_STATUS_AP) { + int i = 0; + wsm_printk(XRADIO_DBG_WARN, "AP mode, wms_send_disassoc_to_self\n"); + for (i = 0; ilink_id_db[i].status == XRADIO_LINK_HARD) { + skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); + if (!skb) + return; + skb_reserve(skb, 64); + disassoc = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); + if(!disassoc) { + SYS_WARN(1); + return; + } + disassoc->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); + disassoc->duration = 0; + memcpy(disassoc->da, priv->vif->addr, ETH_ALEN); + memcpy(disassoc->sa, priv->link_id_db[i].mac, ETH_ALEN); + memcpy(disassoc->bssid, priv->vif->addr, ETH_ALEN); + disassoc->seq_ctrl = 0; + disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_STA_HAS_LEFT; + ieee80211_rx_irqsafe(priv->hw, skb); + } + } + } else if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + wsm_printk(XRADIO_DBG_WARN, "STA mode, wms_send_disassoc_to_self\n"); + skb = xr_alloc_skb(sizeof(struct ieee80211_mgmt) + 64); + if (!skb) + return; + skb_reserve(skb, 64); + disassoc = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt)); + if(!disassoc) { + SYS_WARN(1); + return; + } + disassoc->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_DISASSOC); + disassoc->duration = 0; + memcpy(disassoc->da, priv->vif->addr, ETH_ALEN); + memcpy(disassoc->sa, priv->join_bssid, ETH_ALEN); + memcpy(disassoc->bssid, priv->join_bssid, ETH_ALEN); + disassoc->seq_ctrl = 0; + disassoc->u.disassoc.reason_code = WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY; + ieee80211_rx_irqsafe(priv->hw, skb); + } +} + +static int wsm_receive_indication(struct xradio_common *hw_priv, + int interface_link_id, + struct wsm_buf *buf, + struct sk_buff **skb_p) +{ + struct xradio_vif *priv; + + hw_priv->rx_timestamp = jiffies; + if (hw_priv->wsm_cbc.rx) { + struct wsm_rx rx; + struct ieee80211_hdr *hdr; + size_t hdr_len; + + rx.status = WSM_GET32(buf); + rx.channelNumber = WSM_GET16(buf); + rx.rxedRate = WSM_GET8(buf); + rx.rcpiRssi = WSM_GET8(buf); + rx.flags = WSM_GET32(buf); + + /* TODO:COMBO: Frames received from scanning are received + * with interface ID == 2 */ + if (is_hardware_xradio(hw_priv)) { + if (interface_link_id == XRWL_GENERIC_IF_ID) { + /* Frames received in response to SCAN + * Request */ + interface_link_id = + get_interface_id_scanning(hw_priv); + if (interface_link_id == -1) { + interface_link_id = hw_priv->roc_if_id; + } +#ifdef ROAM_OFFLOAD + if (hw_priv->auto_scanning) { + interface_link_id = hw_priv->scan.if_id; + } +#endif/*ROAM_OFFLOAD*/ + } + /* linkid (peer sta id is encoded in bit 25-28 of + flags field */ + rx.link_id = ((rx.flags & (0xf << 25)) >> 25); + rx.if_id = interface_link_id; + } else { + rx.link_id = interface_link_id; + rx.if_id = 0; + } + priv = xrwl_hwpriv_to_vifpriv(hw_priv, rx.if_id); + if (!priv) { + wsm_printk(XRADIO_DBG_ERROR, "wsm_receive_indication: NULL priv drop frame\n"); + return 0; + } + //remove wsm hdr of skb + hdr_len = buf->data - buf->begin; + skb_pull(*skb_p, hdr_len); + + /* FW Workaround: Drop probe resp or + beacon when RSSI is 0 */ + hdr = (struct ieee80211_hdr *) (*skb_p)->data; + + if (!rx.rcpiRssi && + (ieee80211_is_probe_resp(hdr->frame_control) || + ieee80211_is_beacon(hdr->frame_control))) { + spin_unlock(&priv->vif_lock); + return 0; + } + + /* If no RSSI subscription has been made, + * convert RCPI to RSSI here */ + if (!priv->cqm_use_rssi) + rx.rcpiRssi = rx.rcpiRssi / 2 - 110; + + if (!rx.status && unlikely(ieee80211_is_deauth(hdr->frame_control))) { + if (priv->join_status == XRADIO_JOIN_STATUS_STA) { + /* Shedule unjoin work */ + wsm_printk(XRADIO_DBG_WARN, \ + "Issue unjoin command (RX).\n"); + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, + &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } + hw_priv->wsm_cbc.rx(priv, &rx, skb_p); + if (*skb_p) + skb_push(*skb_p, hdr_len); + spin_unlock(&priv->vif_lock); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_event_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf, + int interface_link_id) +{ + int first; + struct xradio_wsm_event *event = NULL; + struct xradio_vif *priv; + + if (!is_hardware_xradio(hw_priv)) + interface_link_id = 0; + + priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); + + if (unlikely(!priv)) { + wsm_printk(XRADIO_DBG_WARN, "Event: %d(%d) for removed " + "interface, ignoring\n", __le32_to_cpu(WSM_GET32(buf)), + __le32_to_cpu(WSM_GET32(buf))); + return 0; + } + + if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) { + /* STA is stopped. */ + return 0; + } + spin_unlock(&priv->vif_lock); + + event = xr_kzalloc(sizeof(struct xradio_wsm_event), false); + if (event == NULL) { + wsm_printk(XRADIO_DBG_ERROR, "%s:xr_kzalloc failed!", __func__); + return -EINVAL; + } + + event->evt.eventId = __le32_to_cpu(WSM_GET32(buf)); + event->evt.eventData = __le32_to_cpu(WSM_GET32(buf)); + event->if_id = interface_link_id; + + wsm_printk(XRADIO_DBG_MSG, "Event: %d(%d)\n", + event->evt.eventId, event->evt.eventData); + + spin_lock(&hw_priv->event_queue_lock); + first = list_empty(&hw_priv->event_queue); + list_add_tail(&event->link, &hw_priv->event_queue); + spin_unlock(&hw_priv->event_queue_lock); + + if (first) + queue_work(hw_priv->workqueue, &hw_priv->event_handler); + + return 0; + +underflow: + kfree(event); + return -EINVAL; +} + +#define PRINT_11K_MEASRURE 1 +static int wsm_measure_cmpl_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + MEASUREMENT_COMPLETE measure_cmpl; + u8 cca_chanload; + u32 buf_len = 0; + u32 *data; + + LMAC_MEAS_CHANNEL_LOAD_RESULTS *chanload_res; + LMAC_MEAS_NOISE_HISTOGRAM_RESULTS *noise_res; + WSM_GET(buf, &measure_cmpl, 12); + + switch (measure_cmpl.MeasurementType) { + case ChannelLoadMeasurement: + buf_len = sizeof(LMAC_MEAS_CHANNEL_LOAD_RESULTS); + break; + case NoiseHistrogramMeasurement: + buf_len = sizeof(LMAC_MEAS_NOISE_HISTOGRAM_RESULTS); + break; + case BeaconReport: + buf_len = sizeof(LMAC_MEAS_BEACON_RESULTS); + break; + case STAstatisticsReport: + buf_len = sizeof(LMAC_MEAS_STA_STATS_RESULTS); + break; + case LinkMeasurement: + buf_len = sizeof(LMAC_MEAS_LINK_MEASUREMENT_RESULTS); + break; + } + wsm_printk(XRADIO_DBG_ERROR, "[11K]buf_len = %d\n", buf_len); + WSM_GET(buf, &measure_cmpl.MeasurementReport, buf_len); + + data = (u32 *)(&measure_cmpl); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[0]=%08x\n", data[0]); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[1]=%08x\n", data[1]); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[2]=%08x\n", data[2]); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[3]=%08x\n", data[3]); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[4]=%08x\n", data[4]); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[5]=%08x\n", data[5]); +// wsm_printk(XRADIO_DBG_ERROR, "[***HL***]data[6]=%08x\n", data[6]); + wsm_printk(XRADIO_DBG_ERROR, "[***HL***]MeasurementType=%0d\n", measure_cmpl.MeasurementType); + + if (measure_cmpl.Status == WSM_STATUS_SUCCESS){ + switch (measure_cmpl.MeasurementType) { + case ChannelLoadMeasurement: + chanload_res = &measure_cmpl.MeasurementReport.ChannelLoadResults; + cca_chanload = (chanload_res->ChannelLoadCCA == MEAS_CCA) ? + chanload_res->CCAbusyFraction : + chanload_res->ChannelLoad; + #ifdef PRINT_11K_MEASRURE + wsm_printk(XRADIO_DBG_ERROR, "[11K] ChannelLoadMeasurement Result:\n"\ + "ChannelLoadCCA = %d\n"\ + "ChannelNum = %d\n"\ + "Duration = %d\n"\ + "Fraction = %d\n", \ + chanload_res->ChannelLoadCCA,\ + chanload_res->ChannelNum,\ + chanload_res->MeasurementDuration,\ + cca_chanload + ); + #endif + break; + case NoiseHistrogramMeasurement: + noise_res = &measure_cmpl.MeasurementReport.NoiseHistogramResults; +// IpiRpi = (noise_res->IpiRpi == MEAS_RPI) ? +// chanload_res->CCAbusyFraction : +// chanload_res->ChannelLoad; + #ifdef PRINT_11K_MEASRURE + wsm_printk(XRADIO_DBG_ERROR, "[11K] NoiseHistogramResults:\n"\ + "IpiRpi = %d\n"\ + "ChannelNum = %d\n"\ + "PI_0__Density = %d\n"\ + "PI_1__Density = %d\n"\ + "PI_2__Density = %d\n"\ + "PI_3__Density = %d\n"\ + "PI_4__Density = %d\n"\ + "PI_5__Density = %d\n"\ + "PI_6__Density = %d\n"\ + "PI_7__Density = %d\n"\ + "PI_8__Density = %d\n"\ + "PI_9__Density = %d\n"\ + "PI_10_Density = %d\n", \ + noise_res->IpiRpi,\ + noise_res->ChannelNum,\ + noise_res->PI_0_Density,\ + noise_res->PI_1_Density,\ + noise_res->PI_2_Density,\ + noise_res->PI_3_Density,\ + noise_res->PI_4_Density,\ + noise_res->PI_5_Density,\ + noise_res->PI_6_Density,\ + noise_res->PI_7_Density,\ + noise_res->PI_8_Density,\ + noise_res->PI_9_Density,\ + noise_res->PI_10_Density + ); + #endif + break; + case BeaconReport: + break; + case STAstatisticsReport: + break; + case LinkMeasurement: + break; + } + } else { + wsm_printk(XRADIO_DBG_ERROR, "11K Measure(type=%d) Fail\n", measure_cmpl.MeasurementType); + } + + return 0; + +underflow: + return -EINVAL; +} +/* TODO:COMBO:Make this perVIFF once mac80211 support is available */ +static int wsm_channel_switch_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + wsm_unlock_tx(hw_priv); /* Re-enable datapath */ + SYS_WARN(WSM_GET32(buf)); + + hw_priv->channel_switch_in_progress = 0; + wake_up(&hw_priv->channel_switch_done); + + if (hw_priv->wsm_cbc.channel_switch) + hw_priv->wsm_cbc.channel_switch(hw_priv); + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_set_pm_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + wsm_oper_unlock(hw_priv); + return 0; +} + +static int wsm_scan_complete_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ +#ifdef ROAM_OFFLOAD + if(hw_priv->auto_scanning == 0) + wsm_oper_unlock(hw_priv); +#else + wsm_oper_unlock(hw_priv); +#endif /*ROAM_OFFLOAD*/ + + if (hw_priv->wsm_cbc.scan_complete) { + struct wsm_scan_complete arg; + arg.status = WSM_GET32(buf); + arg.psm = WSM_GET8(buf); + arg.numChannels = WSM_GET8(buf); + hw_priv->wsm_cbc.scan_complete(hw_priv, &arg); + } + return 0; + +underflow: + return -EINVAL; +} + +static int wsm_find_complete_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + /* TODO: Implement me. */ + //STUB(); + return 0; +} + +static int wsm_suspend_resume_indication(struct xradio_common *hw_priv, + int interface_link_id, + struct wsm_buf *buf) +{ + if (hw_priv->wsm_cbc.suspend_resume) { + u32 flags; + struct wsm_suspend_resume arg; + struct xradio_vif *priv; + + if (is_hardware_xradio(hw_priv)) { + int i; + arg.if_id = interface_link_id; + /* TODO:COMBO: Extract bitmap from suspend-resume + * TX indication */ + xradio_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + if (priv->join_status == + XRADIO_JOIN_STATUS_AP) { + arg.if_id = priv->if_id; + break; + } + arg.link_id = 0; + } + } else { + arg.if_id = 0; + arg.link_id = interface_link_id; + } + + flags = WSM_GET32(buf); + arg.stop = !(flags & 1); + arg.multicast = !!(flags & 8); + arg.queue = (flags >> 1) & 3; + + priv = xrwl_hwpriv_to_vifpriv(hw_priv, arg.if_id); + if (unlikely(!priv)) { + wsm_printk(XRADIO_DBG_MSG, "suspend-resume indication" + " for removed interface!\n"); + return 0; + } + hw_priv->wsm_cbc.suspend_resume(priv, &arg); + spin_unlock(&priv->vif_lock); + } + return 0; + +underflow: + return -EINVAL; +} + + +/* ******************************************************************** */ +/* WSM TX */ + +int wsm_cmd_send(struct xradio_common *hw_priv, + struct wsm_buf *buf, + void *arg, u16 cmd, long tmo, int if_id) +{ + size_t buf_len = buf->data - buf->begin; + int ret; + + if (cmd == 0x0006 || cmd == 0x0005) /* Write/Read MIB */ + wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X [MIB: 0x%.4X] (%d)\n", + cmd, __le16_to_cpu(((__le16 *)buf->begin)[2]), + buf_len); + else + wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%d)\n", cmd, buf_len); + +#ifdef HW_RESTART + if (hw_priv->hw_restart) { + wsm_printk(XRADIO_DBG_NIY, "hw reset!>>> 0x%.4X (%d)\n", cmd, buf_len); + wsm_buf_reset(buf); + return 0; /*return success, don't process cmd in power off.*/ + } +#endif + + if (unlikely(hw_priv->bh_error)) { + wsm_buf_reset(buf); + wsm_printk(XRADIO_DBG_ERROR, "bh error!>>> 0x%.4X (%d)\n", cmd, buf_len); + return -ETIMEDOUT; + } + + /* Fill HI message header */ + /* BH will add sequence number */ + + /* TODO:COMBO: Add if_id from to the WSM header */ + /* if_id == -1 indicates that command is HW specific, + * eg. wsm_configuration which is called during driver initialzation + * (mac80211 .start callback called when first ifce is created. )*/ + + /* send hw specific commands on if 0 */ + if (if_id == -1) + if_id = 0; + + ((__le16 *)buf->begin)[0] = __cpu_to_le16(buf_len); + ((__le16 *)buf->begin)[1] = __cpu_to_le16(cmd | + ((is_hardware_xradio(hw_priv)) ? (if_id << 6) : 0)); + + spin_lock(&hw_priv->wsm_cmd.lock); + SYS_BUG(hw_priv->wsm_cmd.ptr); + hw_priv->wsm_cmd.done = 0; + hw_priv->wsm_cmd.ptr = buf->begin; + hw_priv->wsm_cmd.len = buf_len; + hw_priv->wsm_cmd.arg = arg; + hw_priv->wsm_cmd.cmd = cmd; + spin_unlock(&hw_priv->wsm_cmd.lock); + + xradio_bh_wakeup(hw_priv); + + if (unlikely(hw_priv->bh_error)) { + /* Do not wait for timeout if BH is dead. Exit immediately. */ + ret = 0; + } else { + unsigned long wsm_cmd_max_tmo; + + /* Give start cmd a little more time */ + if (unlikely(tmo == WSM_CMD_START_TIMEOUT)) + wsm_cmd_max_tmo = WSM_CMD_START_TIMEOUT; + else + wsm_cmd_max_tmo = WSM_CMD_DEFAULT_TIMEOUT; + + /*Set max timeout.*/ + wsm_cmd_max_tmo = jiffies + wsm_cmd_max_tmo; + + /* Firmware prioritizes data traffic over control confirm. + * Loop below checks if data was RXed and increases timeout + * accordingly. */ + do { + /* It's safe to use unprotected access to wsm_cmd.done here */ + ret = wait_event_timeout(hw_priv->wsm_cmd_wq, hw_priv->wsm_cmd.done, tmo); + + /* check time since last rxed and max timeout.*/ + } while (!ret && + time_before_eq(jiffies, hw_priv->rx_timestamp+tmo) && + time_before(jiffies, wsm_cmd_max_tmo)); + + } + + if (unlikely(ret == 0)) { + u16 raceCheck; + + spin_lock(&hw_priv->wsm_cmd.lock); + raceCheck = hw_priv->wsm_cmd.cmd; + hw_priv->wsm_cmd.arg = NULL; + hw_priv->wsm_cmd.ptr = NULL; + spin_unlock(&hw_priv->wsm_cmd.lock); + + wsm_printk(XRADIO_DBG_ERROR, + "***CMD timeout!>>> 0x%.4X (%d), buf_use=%d, bh_state=%d\n", + cmd, buf_len, hw_priv->hw_bufs_used, hw_priv->bh_error); + /* Race condition check to make sure _confirm is not called + * after exit of _send */ + if (raceCheck == 0xFFFF) { + /* If wsm_handle_rx got stuck in _confirm we will hang + * system there. It's better than silently currupt + * stack or heap, isn't it? */ + SYS_BUG(wait_event_timeout( + hw_priv->wsm_cmd_wq, + hw_priv->wsm_cmd.done, + WSM_CMD_LAST_CHANCE_TIMEOUT) <= 0); + } + + /* Kill BH thread to report the error to the top layer. */ + hw_priv->bh_error = 1; +#ifdef BH_USE_SEMAPHORE + up(&hw_priv->bh_sem); +#else + wake_up(&hw_priv->bh_wq); +#endif + ret = -ETIMEDOUT; + } else { + spin_lock(&hw_priv->wsm_cmd.lock); + SYS_BUG(!hw_priv->wsm_cmd.done); + ret = hw_priv->wsm_cmd.ret; + spin_unlock(&hw_priv->wsm_cmd.lock); + } + wsm_buf_reset(buf); + return ret; +} + +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct xradio_common *hw_priv) +{ + down(&hw_priv->tx_lock_sem); + atomic_add(1, &hw_priv->tx_lock); + /* always check event if wsm_vif_lock_tx.*/ + if (wsm_flush_tx(hw_priv)) + wsm_printk(XRADIO_DBG_MSG, "TX is locked.\n"); + up(&hw_priv->tx_lock_sem); +} + +void wsm_vif_lock_tx(struct xradio_vif *priv) +{ + struct xradio_common *hw_priv = priv->hw_priv; + down(&hw_priv->tx_lock_sem); + if (atomic_add_return(1, &hw_priv->tx_lock) == 1) { + if (wsm_vif_flush_tx(priv)) + wsm_printk(XRADIO_DBG_MSG, "TX is locked for" + " if_id %d.\n", priv->if_id); + } + up(&hw_priv->tx_lock_sem); +} + +void wsm_lock_tx_async(struct xradio_common *hw_priv) +{ + if (atomic_add_return(1, &hw_priv->tx_lock) == 1) + wsm_printk(XRADIO_DBG_MSG, "TX is locked (async).\n"); +} + +bool wsm_flush_tx(struct xradio_common *hw_priv) +{ + long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT; + + /* Flush must be called with TX lock held. */ + SYS_BUG(!atomic_read(&hw_priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. */ + if (!hw_priv->hw_bufs_used) + return true; + + if (hw_priv->bh_error) { + /* In case of failure do not wait for magic. */ + wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, " + "will not flush TX.\n"); + return false; + } else { + /* Get "oldest" frame, if any frames stuck in firmware, + query all of them until max timeout. */ + int num = hw_priv->hw_bufs_used + 1; + while (xradio_query_txpkt_timeout(hw_priv, XRWL_ALL_IFS, + 0xffffffff, &timeout)) { + if (timeout < 0 || !num) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + wsm_printk(XRADIO_DBG_ERROR, + "%s:hw_bufs_used=%d, num=%d, timeout=%ld\n", + __func__, hw_priv->hw_bufs_used, num, timeout); + hw_priv->bh_error = 1; +#ifdef BH_USE_SEMAPHORE + up(&hw_priv->bh_sem); +#else + wake_up(&hw_priv->bh_wq); +#endif + return false; + } else if (wait_event_timeout(hw_priv->bh_evt_wq, + !hw_priv->hw_bufs_used, timeout) > 0) { + return true; + } + --num; + } + if (hw_priv->hw_bufs_used) + wsm_printk(XRADIO_DBG_ERROR, "%s:No pengding, but hw_bufs_used=%d\n", + __func__, hw_priv->hw_bufs_used); + /* Ok, everything is flushed. */ + return true; + } +} + +bool wsm_vif_flush_tx(struct xradio_vif *priv) +{ + struct xradio_common *hw_priv = priv->hw_priv; + long timeout = WSM_CMD_LAST_CHANCE_TIMEOUT; + int if_id = priv->if_id; + + /* Flush must be called with TX lock held. */ + SYS_BUG(!atomic_read(&hw_priv->tx_lock)); + + /* First check if we really need to do something. + * It is safe to use unprotected access, as hw_bufs_used + * can only decrements. */ + if (!hw_priv->hw_bufs_used_vif[if_id]) + return true; + + if (hw_priv->bh_error) { + /* In case of failure do not wait for magic. */ + wsm_printk(XRADIO_DBG_ERROR, "Fatal error occured, " + "will not flush TX.\n"); + return false; + } else { + /* Get "oldest" frame, if any frames stuck in firmware, + query all of them until max timeout. */ + int num = hw_priv->hw_bufs_used_vif[if_id] + 1; + while (xradio_query_txpkt_timeout(hw_priv, if_id, 0xffffffff, &timeout)) { + if (timeout < 0 || !num) { + /* Hmmm... Not good. Frame had stuck in firmware. */ + wsm_printk(XRADIO_DBG_ERROR, "%s: if_id=%d, hw_bufs_used_vif=%d, num=%d\n", + __func__, if_id, hw_priv->hw_bufs_used_vif[priv->if_id], + num); + hw_priv->bh_error = 1; + #ifdef BH_USE_SEMAPHORE + up(&hw_priv->bh_sem); + #else + wake_up(&hw_priv->bh_wq); + #endif + return false; + } else if (wait_event_timeout(hw_priv->bh_evt_wq, + !hw_priv->hw_bufs_used_vif[if_id], timeout) > 0) { + return true; + } + --num; + } + if (hw_priv->hw_bufs_used_vif[if_id]) + wsm_printk(XRADIO_DBG_ERROR, "%s:No pengding, but hw_bufs_used_vif=%d\n", + __func__, hw_priv->hw_bufs_used_vif[priv->if_id]); + /* Ok, everything is flushed. */ + return true; + } +} + + +void wsm_unlock_tx(struct xradio_common *hw_priv) +{ + int tx_lock; + if (hw_priv->bh_error) + wsm_printk(XRADIO_DBG_ERROR, "bh_error=%d, wsm_unlock_tx is unsafe\n", + hw_priv->bh_error); + else { + tx_lock = atomic_sub_return(1, &hw_priv->tx_lock); + if (tx_lock < 0) { + SYS_BUG(1); + } else if (tx_lock == 0) { + xradio_bh_wakeup(hw_priv); + wsm_printk(XRADIO_DBG_MSG, "TX is unlocked.\n"); + } + } +} + +/* ******************************************************************** */ +/* WSM RX */ + +int wsm_handle_exception(struct xradio_common *hw_priv, u8 *data, size_t len) +{ + struct wsm_buf buf; + u32 reason; + u32 reg[18]; + char fname[48]; + int i = 0; +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + struct xradio_vif *priv = NULL; +#endif + +#ifdef CONFIG_XRADIO_DEBUG + static const char * const reason_str[] = { + "undefined instruction", + "prefetch abort", + "data abort", + "unknown error", + }; +#endif + +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + /* Send the event upwards on the FW exception */ + xradio_pm_stay_awake(&hw_priv->pm_state, 3*HZ); + + spin_lock(&hw_priv->vif_list_lock); + xradio_for_each_vif(hw_priv, priv, i) { + if (!priv) + continue; + //ieee80211_driver_hang_notify(priv->vif, GFP_KERNEL); + } + spin_unlock(&hw_priv->vif_list_lock); +#endif + + buf.begin = buf.data = data; + buf.end = &buf.begin[len]; + + reason = WSM_GET32(&buf); + for (i = 0; i < ARRAY_SIZE(reg); ++i) + reg[i] = WSM_GET32(&buf); + WSM_GET(&buf, fname, sizeof(fname)); + + if (reason < 4) { + wsm_printk(XRADIO_DBG_ERROR, "Firmware exception: %s.\n", + reason_str[reason]); + } else { + wsm_printk(XRADIO_DBG_ERROR, "Firmware assert at %.*s, line %d, reason=0x%x\n", + sizeof(fname), fname, reg[1], reg[2]); + } + + for (i = 0; i < 12; i += 4) { + wsm_printk(XRADIO_DBG_ERROR, "Firmware:" \ + "R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X, R%d: 0x%.8X,\n", + i + 0, reg[i + 0], i + 1, reg[i + 1], + i + 2, reg[i + 2], i + 3, reg[i + 3]); + } + wsm_printk(XRADIO_DBG_ERROR, "Firmware:" \ + "R12: 0x%.8X, SP: 0x%.8X, LR: 0x%.8X, PC: 0x%.8X,\n", + reg[i + 0], reg[i + 1], reg[i + 2], reg[i + 3]); + i += 4; + wsm_printk(XRADIO_DBG_ERROR, "Firmware:CPSR: 0x%.8X, SPSR: 0x%.8X\n", + reg[i + 0], reg[i + 1]); + + return 0; + +underflow: + wiphy_err(hw_priv->hw->wiphy, "Firmware exception.\n"); + print_hex_dump_bytes("Exception: ", DUMP_PREFIX_NONE, data, len); + return -EINVAL; +} + +static int wsm_debug_indication(struct xradio_common *hw_priv, + struct wsm_buf *buf) +{ + //for only one debug item. + u32 dbg_id, buf_data=0; + u16 dbg_buf_len; + u8 dbg_len; + u8 *dbg_buf; + dbg_id = WSM_GET32(buf); + + dbg_buf_len = buf->end - buf->data; + + if (dbg_id == 5) { + do { + dbg_buf_len = buf->end - buf->data; + dbg_len = WSM_GET8(buf); + if (dbg_len > dbg_buf_len - sizeof(dbg_len)) { + wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_len = %d\n", dbg_len); + wsm_printk(XRADIO_DBG_ERROR, "[FW]dbg_buf_len = %d\n", dbg_buf_len); + wsm_printk(XRADIO_DBG_ERROR, "[FW]debug ind err\n"); + //ret = -EINVAL; + //return ret; + + + break; + } + + dbg_buf = buf->data; + //print it; + wsm_printk(XRADIO_DBG_ALWY, "[FW-LOG] %s", dbg_buf); + // + buf->data += dbg_len; + + } while (buf->data < buf->end); + } else { + if (dbg_buf_len >= 4) { + buf_data = WSM_GET32(buf); + wsm_printk(XRADIO_DBG_ALWY, "[FW-DEBUG] DbgId = %d, data = %d", dbg_id, buf_data); + } else { + wsm_printk(XRADIO_DBG_ALWY, "[FW-DEBUG] DbgId = %d", dbg_id); + } + } + + return 0; + +underflow: + SYS_WARN(1); + return -EINVAL; +} + +#if defined(DGB_XRADIO_HWT) +extern u8 hwt_testing; +extern u16 hwt_tx_len; +extern u16 hwt_tx_num; +extern int sent_num; +extern struct timeval hwt_start_time; +extern struct timeval hwt_end_time; +int wsm_hwt_tx_confirm(struct xradio_common *hw_priv, struct wsm_buf *buf) +{ + u32 *packetID = (u32 *)(buf->data+8); + u8 num = *(buf->data + 6); + + wsm_printk(XRADIO_DBG_NIY, "%s, num=%d, hw_bufs_used=%d\n", + __func__, num, hw_priv->hw_bufs_used); + + wsm_release_vif_tx_buffer(hw_priv, 0, num); + wsm_release_tx_buffer(hw_priv, num-1); //one release is in bh. + + //confirm of last packet, so report the test results. + if (*(buf->data+7) & 0x01) { //last packet + u32 time_int = 0; + u32 total = hwt_tx_num*hwt_tx_len*8; + do_gettimeofday(&hwt_end_time); + time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \ + (hwt_end_time.tv_usec-hwt_start_time.tv_usec); + wsm_printk(XRADIO_DBG_ALWY, "%s, HIF TX: time=%dms, throughput=%d.%dMbps, SW time=%dus\n", + __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int, + (*(u32 *)(buf->data+8))); + hwt_tx_len = 0; + hwt_tx_num = 0; + sent_num = 0; //reset the sent_num + hwt_testing = 0; + } + return 0; +} + +u16 recv_num = 0; +extern u8 hwt_rx_en; +extern u16 hwt_rx_len; +extern u16 hwt_rx_num; +int wsm_hwt_rx_frames(struct xradio_common *hw_priv, struct wsm_buf *buf) +{ + + wsm_printk(XRADIO_DBG_NIY, "%s, status=%d, len=%d\n", __func__, + *(u16 *)(buf->data+2), *(u16 *)(buf->data+4)); + recv_num++; + if (recv_num >= hwt_rx_num) { //last packet + u32 time_int = 0; + u32 total = recv_num*hwt_rx_len*8; + do_gettimeofday(&hwt_end_time); + time_int = (hwt_end_time.tv_sec-hwt_start_time.tv_sec)*1000000 + \ + (hwt_end_time.tv_usec-hwt_start_time.tv_usec); + wsm_printk(XRADIO_DBG_ALWY, "%s, HIF RX: time=%dms, throughput=%d.%dMbps\n", + __func__, time_int/1000, total/time_int, (total%time_int)*10/time_int); + hwt_rx_en = 0; + hwt_rx_num = 0; + recv_num = 0; //reset the recv_num + hwt_testing = 0; + } + + return 0; +} + +int wsm_hwt_enc_results(struct xradio_common *hw_priv, struct wsm_buf *buf) +{ + wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, enc throughput=%d.%02dMbps\n", __func__, + *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12)); + hwt_testing = 0; + return 0; +} + +int wsm_hwt_mic_results(struct xradio_common *hw_priv, struct wsm_buf *buf) +{ + wsm_printk(XRADIO_DBG_ALWY, "%s, status=%d, mic throughput=%d.%02dMbps\n", __func__, + *(u16 *)(buf->data+2), *(u32 *)(buf->data+8), *(u32 *)(buf->data+12)); + hwt_testing = 0; + return 0; +} +#endif //DGB_XRADIO_HWT + + +int wsm_handle_rx(struct xradio_common *hw_priv, int id, + struct wsm_hdr *wsm, struct sk_buff **skb_p) +{ + int ret = 0; + struct wsm_buf wsm_buf; + struct xradio_vif *priv = NULL; + int i = 0; + int interface_link_id = (id >> 6) & 0x0F; +#ifdef ROAM_OFFLOAD +#if 0 + struct xradio_vif *priv; + priv = xrwl_hwpriv_to_vifpriv(hw_priv, interface_link_id); + if (unlikely(!priv)) { + SYS_WARN(1); + return 0; + } + spin_unlock(&priv->vif_lock); +#endif +#endif/*ROAM_OFFLOAD*/ + + /* Strip link id. */ + id &= ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + + wsm_buf.begin = (u8 *)&wsm[0]; + wsm_buf.data = (u8 *)&wsm[1]; + wsm_buf.end = &wsm_buf.begin[__le32_to_cpu(wsm->len)]; + + wsm_printk(XRADIO_DBG_MSG, "<<< 0x%.4X (%d)\n", id, + wsm_buf.end - wsm_buf.begin); + +#if defined(DGB_XRADIO_HWT) +/***************************for HWT ********************************/ + if (id == 0x0424) { + u16 TestID = *(u16 *)(wsm_buf.data); + if (TestID == 1) //test frame confirm. + wsm_hwt_tx_confirm(hw_priv, &wsm_buf); + else { + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.ret = *((u16 *)(wsm_buf.data) + 1); + hw_priv->wsm_cmd.done = 1; + spin_unlock(&hw_priv->wsm_cmd.lock); + wake_up(&hw_priv->wsm_cmd_wq); + wsm_printk(XRADIO_DBG_ALWY, "HWT TestID=0x%x Confirm ret=%d\n", + *(u16 *)(wsm_buf.data), hw_priv->wsm_cmd.ret); + } + return 0; + } else if (id == 0x0824) { + u16 TestID = *(u16 *)(wsm_buf.data); + switch (TestID) { + case 2: //recieve a test frame. + wsm_hwt_rx_frames(hw_priv, &wsm_buf); + break; + case 3: //enc test result. + wsm_hwt_enc_results(hw_priv, &wsm_buf); + break; + case 4: //mic test result. + wsm_hwt_mic_results(hw_priv, &wsm_buf); + break; + default: + wsm_printk(XRADIO_DBG_ERROR, "HWT ERROR Indication TestID=0x%x\n", TestID); + break; + } + return 0; + } +/***************************for HWT ********************************/ +#endif //DGB_XRADIO_HWT + + if (id == 0x404) { + ret = wsm_tx_confirm(hw_priv, &wsm_buf, interface_link_id); +#ifdef MCAST_FWDING +#if 1 + } else if (id == 0x422) { + ret = wsm_give_buffer_confirm(hw_priv, &wsm_buf); +#endif +#endif + + } else if (id == 0x41E) { + ret = wsm_multi_tx_confirm(hw_priv, &wsm_buf, + interface_link_id); + } else if (id & 0x0400) { + void *wsm_arg; + u16 wsm_cmd; + + /* Do not trust FW too much. Protection against repeated + * response and race condition removal (see above). */ + spin_lock(&hw_priv->wsm_cmd.lock); + wsm_arg = hw_priv->wsm_cmd.arg; + wsm_cmd = hw_priv->wsm_cmd.cmd & + ~WSM_TX_LINK_ID(WSM_TX_LINK_ID_MAX); + hw_priv->wsm_cmd.cmd = 0xFFFF; + spin_unlock(&hw_priv->wsm_cmd.lock); + + if (SYS_WARN((id & ~0x0400) != wsm_cmd)) { + /* Note that any non-zero is a fatal retcode. */ + ret = -EINVAL; + goto out; + } + + switch (id) { + case 0x0409: + /* Note that wsm_arg can be NULL in case of timeout in + * wsm_cmd_send(). */ + if (likely(wsm_arg)) + ret = wsm_configuration_confirm(hw_priv, + wsm_arg, + &wsm_buf); + break; + case 0x0405: + if (likely(wsm_arg)) + ret = wsm_read_mib_confirm(hw_priv, wsm_arg, + &wsm_buf); + break; + case 0x0406: + if (likely(wsm_arg)) + ret = wsm_write_mib_confirm(hw_priv, wsm_arg, + &wsm_buf, + interface_link_id); + break; + case 0x040B: + if (likely(wsm_arg)) + ret = wsm_join_confirm(hw_priv, wsm_arg, &wsm_buf); + if (ret) + wsm_printk(XRADIO_DBG_WARN, "Join confirm Failed!\n"); + break; + case 0x040E: /* 11K measure*/ + if (likely(wsm_arg)) + ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); + if (ret) + wsm_printk(XRADIO_DBG_ERROR, "[***HL***]11K Confirm Error\n"); + + break; + +#ifdef MCAST_FWDING + case 0x0423: /* req buffer cfm*/ + if (likely(wsm_arg)){ + xradio_for_each_vif(hw_priv, priv, i) { + if (priv && (priv->join_status == XRADIO_JOIN_STATUS_AP)) + ret = wsm_request_buffer_confirm(priv, + wsm_arg, &wsm_buf); + } + } + break; +#endif + case 0x0407: /* start-scan */ +#ifdef ROAM_OFFLOAD + if (hw_priv->auto_scanning) { + if (atomic_read(&hw_priv->scan.in_progress)) { + hw_priv->auto_scanning = 0; + } + else { + wsm_oper_unlock(hw_priv); + up(&hw_priv->scan.lock); + } + } +#endif /*ROAM_OFFLOAD*/ + case 0x0408: /* stop-scan */ + case 0x040A: /* wsm_reset */ + case 0x040C: /* add_key */ + case 0x040D: /* remove_key */ + case 0x0410: /* wsm_set_pm */ + case 0x0411: /* set_bss_params */ + case 0x0412: /* set_tx_queue_params */ + case 0x0413: /* set_edca_params */ + case 0x0416: /* switch_channel */ + case 0x0417: /* start */ + case 0x0418: /* beacon_transmit */ + case 0x0419: /* start_find */ + case 0x041A: /* stop_find */ + case 0x041B: /* update_ie */ + case 0x041C: /* map_link */ + SYS_WARN(wsm_arg != NULL); + ret = wsm_generic_confirm(hw_priv, wsm_arg, &wsm_buf); + if (ret) + wsm_printk(XRADIO_DBG_ERROR, + "wsm_generic_confirm " + "failed for request 0x%.4X.\n", + id & ~0x0400); + break; + default: + SYS_BUG(1); + } + + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.ret = ret; + hw_priv->wsm_cmd.done = 1; + spin_unlock(&hw_priv->wsm_cmd.lock); + ret = 0; /* Error response from device should ne stop BH. */ + + wake_up(&hw_priv->wsm_cmd_wq); + } else if (id & 0x0800) { + switch (id) { + case 0x0801: + ret = wsm_startup_indication(hw_priv, &wsm_buf); + break; + case 0x0804: + ret = wsm_receive_indication(hw_priv, interface_link_id, + &wsm_buf, skb_p); + break; + case 0x0805: + ret = wsm_event_indication(hw_priv, &wsm_buf, + interface_link_id); + break; + case 0x0807: + wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm_measure_cmpl_indication\n"); +// wsm_printk(XRADIO_DBG_ERROR, "[11K]wsm->len = %d\n",wsm->len); + + ret = wsm_measure_cmpl_indication(hw_priv, &wsm_buf); + break; + case 0x080A: + ret = wsm_channel_switch_indication(hw_priv, &wsm_buf); + break; + case 0x0809: + ret = wsm_set_pm_indication(hw_priv, &wsm_buf); + break; + case 0x0806: +#ifdef ROAM_OFFLOAD + if(hw_priv->auto_scanning && hw_priv->frame_rcvd) { + struct xradio_vif *priv; + hw_priv->frame_rcvd = 0; + priv = xrwl_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id); + if (unlikely(!priv)) { + SYS_WARN(1); + return 0; + } + spin_unlock(&priv->vif_lock); + if (hw_priv->beacon) { + struct wsm_scan_complete *scan_cmpl = \ + (struct wsm_scan_complete *) \ + ((u8 *)wsm + sizeof(struct wsm_hdr)); + struct ieee80211_rx_status *rhdr = \ + IEEE80211_SKB_RXCB(hw_priv->beacon); + rhdr->signal = (s8)scan_cmpl->reserved; + if (!priv->cqm_use_rssi) { + rhdr->signal = rhdr->signal / 2 - 110; + } + if (!hw_priv->beacon_bkp) + hw_priv->beacon_bkp = \ + skb_copy(hw_priv->beacon, GFP_ATOMIC); + ieee80211_rx_irqsafe(hw_priv->hw, hw_priv->beacon); + hw_priv->beacon = hw_priv->beacon_bkp; + + hw_priv->beacon_bkp = NULL; + } + wsm_printk(XRADIO_DBG_MSG, \ + "Send Testmode Event.\n"); + xradio_testmode_event(priv->hw->wiphy, + NL80211_CMD_NEW_SCAN_RESULTS, 0, + 0, GFP_KERNEL); + + } +#endif /*ROAM_OFFLOAD*/ + ret = wsm_scan_complete_indication(hw_priv, &wsm_buf); + break; + case 0x080B: + ret = wsm_find_complete_indication(hw_priv, &wsm_buf); + break; + case 0x080C: + ret = wsm_suspend_resume_indication(hw_priv, + interface_link_id, &wsm_buf); + break; + case 0x080E: + wsm_printk(XRADIO_DBG_MSG, "wsm_debug_indication"); + ret = wsm_debug_indication(hw_priv, &wsm_buf); + break; + + default: + wsm_printk(XRADIO_DBG_ERROR, "unknown Indmsg ID=0x%04x,len=%d\n", + wsm->id, wsm->len); + break; + } + } else { + SYS_WARN(1); + ret = -EINVAL; + } +out: + return ret; +} + +static bool wsm_handle_tx_data(struct xradio_vif *priv, + const struct wsm_tx *wsm, + const struct ieee80211_tx_info *tx_info, + struct xradio_txpriv *txpriv, + struct xradio_queue *queue) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); +#ifdef P2P_MULTIVIF + struct xradio_vif *p2p_if_vif = NULL; +#endif + bool handled = false; + const struct ieee80211_hdr *frame = + (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; + __le16 fctl = frame->frame_control; + enum { + doProbe, + doDrop, + doJoin, + doOffchannel, + doWep, + doTx, + } action = doTx; + + hw_priv = xrwl_vifpriv_to_hwpriv(priv); +#ifdef P2P_MULTIVIF + if (priv->if_id == XRWL_GENERIC_IF_ID) + p2p_if_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1); +#endif + frame = (struct ieee80211_hdr *) &((u8 *)wsm)[txpriv->offset]; + fctl = frame->frame_control; + + switch (priv->mode) { + case NL80211_IFTYPE_STATION: + if (unlikely(priv->bss_loss_status == XRADIO_BSS_LOSS_CHECKING && + priv->join_status == XRADIO_JOIN_STATUS_STA) && + ieee80211_is_data(fctl)) { + spin_lock(&priv->bss_loss_lock); + priv->bss_loss_confirm_id = wsm->packetID; + priv->bss_loss_status = XRADIO_BSS_LOSS_CONFIRMING; + spin_unlock(&priv->bss_loss_lock); + } else if (unlikely((priv->join_status <= XRADIO_JOIN_STATUS_MONITOR) || + memcmp(frame->addr1, priv->join_bssid,sizeof(priv->join_bssid)))) { +#ifdef P2P_MULTIVIF + if (p2p_if_vif && (p2p_if_vif->join_status > XRADIO_JOIN_STATUS_MONITOR) && + (priv->join_status < XRADIO_JOIN_STATUS_MONITOR)) { + + /* Post group formation, frame transmission on p2p0 + * interafce should not use offchannel/generic channel. + * Instead, the frame should be transmitted on interafce + * 1. This is needed by wsc fw. + */ + action = doTx; + txpriv->raw_if_id = 1; + } else +#endif + if (ieee80211_is_auth(fctl)) + action = doJoin; + else if ((ieee80211_is_deauth(fctl) || ieee80211_is_disassoc(fctl))&& + priv->join_status < XRADIO_JOIN_STATUS_MONITOR) + action = doDrop; //no need to send deauth when STA-unjoined, yangfh 2014-10-31 16:32:16. + else if (ieee80211_is_probe_req(fctl)) + action = doTx; + else if (memcmp(frame->addr1, priv->join_bssid, + sizeof(priv->join_bssid)) && + (priv->join_status == + XRADIO_JOIN_STATUS_STA) && + (ieee80211_is_data(fctl))) { + action = doDrop; + } + else if (priv->join_status >= + XRADIO_JOIN_STATUS_MONITOR) + action = doTx; + else if (get_interface_id_scanning(hw_priv) != -1) { + wsm_printk(XRADIO_DBG_WARN, "Scan ONGOING dropping" + " offchannel eligible frame.\n"); + action = doDrop; + } else { + if (ieee80211_is_probe_resp(fctl)) + action = doDrop; + else + action = doOffchannel; + wsm_printk(XRADIO_DBG_WARN, "Offchannel fctl=0x%04x", fctl); + } + } + break; + case NL80211_IFTYPE_AP: + if (unlikely(!priv->join_status)) + action = doDrop; + else if (unlikely(!(BIT(txpriv->raw_link_id) & + (BIT(0) | priv->link_id_map)))) { + wsm_printk(XRADIO_DBG_WARN, + "A frame with expired link id " + "is dropped.\n"); + action = doDrop; + } + if (xradio_queue_get_generation(wsm->packetID) > + XRADIO_MAX_REQUEUE_ATTEMPTS) { + /* HACK!!! WSM324 firmware has tendency to requeue + * multicast frames in a loop, causing performance + * drop and high power consumption of the driver. + * In this situation it is better just to drop + * the problematic frame. */ + wsm_printk(XRADIO_DBG_WARN, + "Too many attempts " + "to requeue a frame. " + "Frame is dropped, fctl=0x%04x.\n", fctl); + action = doDrop; + } + break; + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_MESH_POINT: + //STUB(); + case NL80211_IFTYPE_MONITOR: + default: + action = doDrop; + break; + } + + if (action == doTx) { + if (unlikely(ieee80211_is_probe_req(fctl))) { +#ifdef CONFIG_XRADIO_TESTMODE + if (hw_priv->enable_advance_scan && + (priv->join_status == XRADIO_JOIN_STATUS_STA) && + (hw_priv->advanceScanElems.scanMode == + XRADIO_SCAN_MEASUREMENT_ACTIVE)) + /* If Advance Scan is Requested on Active Scan + * then transmit the Probe Request */ + action = doTx; + else +#endif + action = doProbe; + } else if ((fctl & __cpu_to_le32(IEEE80211_FCTL_PROTECTED)) && + tx_info->control.hw_key && + unlikely(tx_info->control.hw_key->keyidx != + priv->wep_default_key_id) && + (tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP40 || + tx_info->control.hw_key->cipher == + WLAN_CIPHER_SUITE_WEP104)) { + action = doWep; + } + } + + switch (action) { + case doProbe: + { + /* An interesting FW "feature". Device filters + * probe responses. + * The easiest way to get it back is to convert + * probe request into WSM start_scan command. */ + wsm_printk(XRADIO_DBG_MSG, \ + "Convert probe request to scan.\n"); + wsm_lock_tx_async(hw_priv); + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + queue_delayed_work(hw_priv->workqueue, + &hw_priv->scan.probe_work, 0); + handled = true; + } + break; + case doDrop: + { + /* See detailed description of "join" below. + * We are dropping everything except AUTH in non-joined mode. */ + wsm_printk(XRADIO_DBG_MSG, "Drop frame (0x%.4X).\n", fctl); +#ifdef CONFIG_XRADIO_TESTMODE + SYS_BUG(xradio_queue_remove(hw_priv, queue, + __le32_to_cpu(wsm->packetID))); +#else + SYS_BUG(xradio_queue_remove(queue, + __le32_to_cpu(wsm->packetID))); +#endif /*CONFIG_XRADIO_TESTMODE*/ + handled = true; + } + break; + case doJoin: + { + /* p2p should disconnect when sta try to join a different channel AP, + * because no good performance in this case. + */ + struct xradio_vif *p2p_tmp_vif = __xrwl_hwpriv_to_vifpriv(hw_priv, 1); + if (priv->if_id == 0 && p2p_tmp_vif) { + if (p2p_tmp_vif->join_status >= XRADIO_JOIN_STATUS_STA && + hw_priv->channel_changed) { + wsm_printk(XRADIO_DBG_WARN, "combo with different channels, p2p disconnect.\n"); + wms_send_disassoc_to_self(hw_priv, p2p_tmp_vif); + } + } + + /* There is one more interesting "feature" + * in FW: it can't do RX/TX before "join". + * "Join" here is not an association, + * but just a syncronization between AP and STA. + * priv->join_status is used only in bh thread and does + * not require protection */ + wsm_printk(XRADIO_DBG_NIY, "Issue join command.\n"); + wsm_lock_tx_async(hw_priv); + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(hw_priv->workqueue, &priv->join_work) <= 0) + wsm_unlock_tx(hw_priv); + handled = true; + } + break; + case doOffchannel: + { + wsm_printk(XRADIO_DBG_MSG, "Offchannel TX request.\n"); + wsm_lock_tx_async(hw_priv); + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(hw_priv->workqueue, &priv->offchannel_work) <= 0) + wsm_unlock_tx(hw_priv); + handled = true; + } + break; + case doWep: + { + wsm_printk(XRADIO_DBG_MSG, "Issue set_default_wep_key.\n"); + wsm_lock_tx_async(hw_priv); + priv->wep_default_key_id = tx_info->control.hw_key->keyidx; + hw_priv->pending_frame_id = __le32_to_cpu(wsm->packetID); + if (queue_work(hw_priv->workqueue, &priv->wep_key_work) <= 0) + wsm_unlock_tx(hw_priv); + handled = true; + } + break; + case doTx: + { +#if 0 + /* Kept for history. If you want to implement wsm->more, + * make sure you are able to send a frame after that. */ + wsm->more = (count > 1) ? 1 : 0; + if (wsm->more) { + /* HACK!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + * It's undocumented in WSM spec, but XRADIO hangs + * if 'more' is set and no TX is performed due to TX + * buffers limitation. */ + if (priv->hw_bufs_used + 1 == + priv->wsm_caps.numInpChBufs) + wsm->more = 0; + } + + /* BUG!!! FIXME: we can't use 'more' at all: we don't know + * future. It could be a request from upper layer with TX lock + * requirements (scan, for example). If "more" is set device + * will not send data and wsm_tx_lock() will fail... + * It's not obvious how to fix this deadlock. Any ideas? + * As a workaround more is set to 0. */ + wsm->more = 0; +#endif /* 0 */ + + if (ieee80211_is_deauth(fctl) && + priv->mode != NL80211_IFTYPE_AP) { + /* Shedule unjoin work */ + wsm_printk(XRADIO_DBG_WARN, "Issue unjoin command(TX).\n"); +#if 0 + wsm->more = 0; +#endif /* 0 */ + wsm_lock_tx_async(hw_priv); + if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0) + wsm_unlock_tx(hw_priv); + } + } + break; + } + return handled; +} + +static int xradio_get_prio_queue(struct xradio_vif *priv, + u32 link_id_map, int *total) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + static u32 urgent; + struct wsm_edca_queue_params *edca; + unsigned score, best = -1; + int winner = -1; + int queued; + int i; + urgent = BIT(priv->link_id_after_dtim) | BIT(priv->link_id_uapsd); + + /* search for a winner using edca params */ + for (i = 0; i < 4; ++i) { + queued = xradio_queue_get_num_queued(priv, + &hw_priv->tx_queue[i], + link_id_map); + if (!queued) + continue; + *total += queued; + edca = &priv->edca.params[i]; + score = ((edca->aifns + edca->cwMin) << 16) + + (edca->cwMax - edca->cwMin) * + (random32() & 0xFFFF); + if (score < best && (winner < 0 || i != 3)) { + best = score; + winner = i; + } + } + + /* override winner if bursting */ + if (winner >= 0 && hw_priv->tx_burst_idx >= 0 && + winner != hw_priv->tx_burst_idx && + !xradio_queue_get_num_queued(priv, + &hw_priv->tx_queue[winner], + link_id_map & urgent) && + xradio_queue_get_num_queued(priv, + &hw_priv->tx_queue[hw_priv->tx_burst_idx], + link_id_map)) + winner = hw_priv->tx_burst_idx; + + return winner; +} + +static int wsm_get_tx_queue_and_mask(struct xradio_vif *priv, + struct xradio_queue **queue_p, + u32 *tx_allowed_mask_p, + bool *more) +{ + struct xradio_common *hw_priv = xrwl_vifpriv_to_hwpriv(priv); + int idx; + u32 tx_allowed_mask; + int total = 0; + + /* Search for a queue with multicast frames buffered */ + if (priv->tx_multicast) { + tx_allowed_mask = BIT(priv->link_id_after_dtim); + idx = xradio_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx >= 0) { + *more = total > 1; + goto found; + } + } + + /* Search for unicast traffic */ + tx_allowed_mask = ~priv->sta_asleep_mask; + tx_allowed_mask |= BIT(priv->link_id_uapsd); + if (priv->sta_asleep_mask) { + tx_allowed_mask |= priv->pspoll_mask; + tx_allowed_mask &= ~BIT(priv->link_id_after_dtim); + } else { + tx_allowed_mask |= BIT(priv->link_id_after_dtim); + } + idx = xradio_get_prio_queue(priv, + tx_allowed_mask, &total); + if (idx < 0) + return -ENOENT; + +found: + *queue_p = &hw_priv->tx_queue[idx]; + *tx_allowed_mask_p = tx_allowed_mask; + return 0; +} + +int wsm_get_tx(struct xradio_common *hw_priv, u8 **data, + size_t *tx_len, int *burst, int *vif_selected) +{ + struct wsm_tx *wsm = NULL; + struct ieee80211_tx_info *tx_info; + struct xradio_queue *queue = NULL; + int queue_num; + u32 tx_allowed_mask = 0; + struct xradio_txpriv *txpriv = NULL; +#ifdef P2P_MULTIVIF + int first = 1; + int tmp_if_id = -1; +#endif + /* + * Count was intended as an input for wsm->more flag. + * During implementation it was found that wsm->more + * is not usable, see details above. It is kept just + * in case you would like to try to implement it again. + */ + int count = 0; +#ifdef P2P_MULTIVIF + int if_pending = XRWL_MAX_VIFS - 1; +#else + int if_pending = 1; +#endif + + /* More is used only for broadcasts. */ + bool more = false; + + count = xradio_itp_get_tx(hw_priv, data, tx_len, burst); + if (count) + return count; + + if (hw_priv->wsm_cmd.ptr) { + ++count; + spin_lock(&hw_priv->wsm_cmd.lock); + SYS_BUG(!hw_priv->wsm_cmd.ptr); + *data = hw_priv->wsm_cmd.ptr; + *tx_len = hw_priv->wsm_cmd.len; + *burst = 1; + *vif_selected = -1; + spin_unlock(&hw_priv->wsm_cmd.lock); + } else { + for (;;) { + int ret; + struct xradio_vif *priv; +#if 0 + int num_pending_vif0, num_pending_vif1; +#endif + if (atomic_add_return(0, &hw_priv->tx_lock)) + break; + /* Keep one buffer reserved for commands. Note + that, hw_bufs_used has already been incremented + before reaching here. */ + if (hw_priv->hw_bufs_used >= + hw_priv->wsm_caps.numInpChBufs) + break; +#ifdef P2P_MULTIVIF + if (first) { + tmp_if_id = hw_priv->if_id_selected; + hw_priv->if_id_selected = 2; + } +#endif + priv = wsm_get_interface_for_tx(hw_priv); + /* go to next interface ID to select next packet */ +#ifdef P2P_MULTIVIF + if (first) { + hw_priv->if_id_selected = tmp_if_id; + first = 0; + } else +#endif + hw_priv->if_id_selected ^= 1; + + /* There might be no interface before add_interface + * call */ + if (!priv) { + if (if_pending) { +#ifdef P2P_MULTIVIF + if_pending--; +#else + if_pending = 0; +#endif + continue; + } + break; + } + +#if 0 + if (((priv->if_id == 0) && + (hw_priv->hw_bufs_used_vif[0] >= + XRWL_FW_VIF0_THROTTLE)) || + ((priv->if_id == 1) && + (hw_priv->hw_bufs_used_vif[1] >= + XRWL_FW_VIF1_THROTTLE))) { + spin_unlock(&priv->vif_lock); + if (if_pending) { + if_pending = 0; + continue; + } + break; + } +#endif + + /* This can be removed probably: xradio_vif will not + * be in hw_priv->vif_list (as returned from + * wsm_get_interface_for_tx) until it's fully + * enabled, so statement above will take case of that*/ + if (!atomic_read(&priv->enabled)) { + spin_unlock(&priv->vif_lock); + break; + } + + /* TODO:COMBO: Find the next interface for which + * packet needs to be found */ + spin_lock_bh(&priv->ps_state_lock); + ret = wsm_get_tx_queue_and_mask(priv, &queue, + &tx_allowed_mask, &more); + queue_num = queue - hw_priv->tx_queue; + + if (priv->buffered_multicasts && + (ret || !more) && + (priv->tx_multicast || + !priv->sta_asleep_mask)) { + priv->buffered_multicasts = false; + if (priv->tx_multicast) { + priv->tx_multicast = false; + queue_work(hw_priv->workqueue, + &priv->multicast_stop_work); + } + } + + spin_unlock_bh(&priv->ps_state_lock); + + if (ret) { + spin_unlock(&priv->vif_lock); +#ifdef P2P_MULTIVIF + if (if_pending) { +#else + if (if_pending == 1) { +#endif +#ifdef P2P_MULTIVIF + if_pending--; +#else + if_pending = 0; +#endif + continue; + } + break; + } + + if (xradio_queue_get(queue, + priv->if_id, + tx_allowed_mask, + &wsm, &tx_info, &txpriv)) { + spin_unlock(&priv->vif_lock); + if_pending = 0; + continue; + } + +#ifdef ROC_DEBUG +#ifndef P2P_MULTIVIF + { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + + wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d," + " if_id %d\n", + hdr->frame_control, + txpriv->offchannel_if_id, + priv->if_id); + } +#else + { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + + wsm_printk(XRADIO_DBG_ERROR, "QGET-1 %x, off_id %d," + " if_id %d\n", + hdr->frame_control, + txpriv->raw_if_id, + priv->if_id); + } +#endif +#endif + + if (wsm_handle_tx_data(priv, wsm, + tx_info, txpriv, queue)) { + spin_unlock(&priv->vif_lock); + if_pending = 0; + continue; /* Handled by WSM */ + } + + wsm->hdr.id &= __cpu_to_le16( + ~WSM_TX_IF_ID(WSM_TX_IF_ID_MAX)); +#ifdef P2P_MULTIVIF + if (txpriv->raw_if_id) + wsm->hdr.id |= cpu_to_le16( + WSM_TX_IF_ID(txpriv->raw_if_id)); +#else + if (txpriv->offchannel_if_id) + wsm->hdr.id |= cpu_to_le16( + WSM_TX_IF_ID(txpriv->offchannel_if_id)); +#endif + else + wsm->hdr.id |= cpu_to_le16( + WSM_TX_IF_ID(priv->if_id)); + + *vif_selected = priv->if_id; +#ifdef ROC_DEBUG +/* remand the roc debug. */ +#ifndef P2P_MULTIVIF + + { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + + wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d," + " if_id %d\n", + hdr->frame_control, + txpriv->offchannel_if_id, + priv->if_id); + } +#else + { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + + wsm_printk(XRADIO_DBG_ERROR, "QGET-2 %x, off_id %d," + " if_id %d\n", + hdr->frame_control, + txpriv->raw_if_id, + priv->if_id); + } +#endif +#endif + + priv->pspoll_mask &= ~BIT(txpriv->raw_link_id); + + *data = (u8 *)wsm; + *tx_len = __le16_to_cpu(wsm->hdr.len); + + /* allow bursting if txop is set */ + if (priv->edca.params[queue_num].txOpLimit) + *burst = min(*burst, + (int)xradio_queue_get_num_queued(priv, + queue, tx_allowed_mask) + 1); + else + *burst = 1; + + /* store index of bursting queue */ + if (*burst > 1) + hw_priv->tx_burst_idx = queue_num; + else + hw_priv->tx_burst_idx = -1; + + if (more) { + struct ieee80211_hdr *hdr = + (struct ieee80211_hdr *) + &((u8 *)wsm)[txpriv->offset]; + if(strstr(&priv->ssid[0], "6.1.12")) { + if(hdr->addr1[0] & 0x01 ) { + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + } + else { + /* more buffered multicast/broadcast frames + * ==> set MoreData flag in IEEE 802.11 header + * to inform PS STAs */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } + } + wsm_printk(XRADIO_DBG_MSG, ">>> 0x%.4X (%d) %p %c\n", + 0x0004, *tx_len, *data, + wsm->more ? 'M' : ' '); + ++count; + spin_unlock(&priv->vif_lock); + break; + } + } + + return count; +} + +void wsm_txed(struct xradio_common *hw_priv, u8 *data) +{ + if (data == hw_priv->wsm_cmd.ptr) { + spin_lock(&hw_priv->wsm_cmd.lock); + hw_priv->wsm_cmd.ptr = NULL; + spin_unlock(&hw_priv->wsm_cmd.lock); + } +} + +/* ******************************************************************** */ +/* WSM buffer */ + +void wsm_buf_init(struct wsm_buf *buf) +{ + int size = (SDIO_BLOCK_SIZE<<1); //for sdd file big than SDIO_BLOCK_SIZE + SYS_BUG(buf->begin); + buf->begin = xr_kmalloc(size, true); + buf->end = buf->begin ? &buf->begin[size] : buf->begin; + wsm_buf_reset(buf); +} + +void wsm_buf_deinit(struct wsm_buf *buf) +{ + if(buf->begin) + kfree(buf->begin); + buf->begin = buf->data = buf->end = NULL; +} + +static void wsm_buf_reset(struct wsm_buf *buf) +{ + if (buf->begin) { + buf->data = &buf->begin[4]; + *(u32 *)buf->begin = 0; + } else + buf->data = buf->begin; +} + +static int wsm_buf_reserve(struct wsm_buf *buf, size_t extra_size) +{ + size_t pos = buf->data - buf->begin; + size_t size = pos + extra_size; + + + if (size & (SDIO_BLOCK_SIZE - 1)) { + size &= SDIO_BLOCK_SIZE; + size += SDIO_BLOCK_SIZE; + } + + buf->begin = xr_krealloc(buf->begin, size, true); + if (buf->begin) { + buf->data = &buf->begin[pos]; + buf->end = &buf->begin[size]; + return 0; + } else { + buf->end = buf->data = buf->begin; + return -ENOMEM; + } +} + +static struct xradio_vif + *wsm_get_interface_for_tx(struct xradio_common *hw_priv) +{ + struct xradio_vif *priv = NULL, *i_priv; + int i = hw_priv->if_id_selected; + + if ( 1 /*TODO:COMBO*/) { + spin_lock(&hw_priv->vif_list_lock); + i_priv = hw_priv->vif_list[i] ? + xrwl_get_vif_from_ieee80211(hw_priv->vif_list[i]) : NULL; + if (i_priv) { + priv = i_priv; + spin_lock(&priv->vif_lock); + } + /* TODO:COMBO: + * Find next interface based on TX bitmap announced by the FW + * Find next interface based on load balancing */ + spin_unlock(&hw_priv->vif_list_lock); + } else { + priv = xrwl_hwpriv_to_vifpriv(hw_priv, 0); + } + + return priv; +} + +static inline int get_interface_id_scanning(struct xradio_common *hw_priv) +{ + if (hw_priv->scan.req || hw_priv->scan.direct_probe) + return hw_priv->scan.if_id; + else + return -1; +} diff --git a/drivers/net/wireless/xradio/wsm.h b/drivers/net/wireless/xradio/wsm.h new file mode 100644 index 0000000..e2015c7 --- /dev/null +++ b/drivers/net/wireless/xradio/wsm.h @@ -0,0 +1,2441 @@ +/* + * wsm interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_WSM_H_INCLUDED +#define XRADIO_WSM_H_INCLUDED + +#include + +struct xradio_common; + +/* Bands */ +/* Radio band 2.412 -2.484 GHz. */ +#define WSM_PHY_BAND_2_4G (0) + +/* Radio band 4.9375-5.8250 GHz. */ +#define WSM_PHY_BAND_5G (1) + +/* Transmit rates */ +/* 1 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_1 (0) + +/* 2 Mbps ERP-DSSS */ +#define WSM_TRANSMIT_RATE_2 (1) + +/* 5.5 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_5 (2) */ + +/* 11 Mbps ERP-CCK, ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_11 (3) */ + +/* 22 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_22 (4) */ + +/* 33 Mbps ERP-PBCC (Not supported) */ +/* #define WSM_TRANSMIT_RATE_33 (5) */ + +/* 6 Mbps (3 Mbps) ERP-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_6 (6) + +/* 9 Mbps (4.5 Mbps) ERP-OFDM, BPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_9 (7) + +/* 12 Mbps (6 Mbps) ERP-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_12 (8) + +/* 18 Mbps (9 Mbps) ERP-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_18 (9) + +/* 24 Mbps (12 Mbps) ERP-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_24 (10) + +/* 36 Mbps (18 Mbps) ERP-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_36 (11) + +/* 48 Mbps (24 Mbps) ERP-OFDM, 64QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_48 (12) + +/* 54 Mbps (27 Mbps) ERP-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_54 (13) + +/* 6.5 Mbps HT-OFDM, BPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_6 (14) + +/* 13 Mbps HT-OFDM, QPSK coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_13 (15) + +/* 19.5 Mbps HT-OFDM, QPSK coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_19 (16) + +/* 26 Mbps HT-OFDM, 16QAM coding rate 1/2 */ +#define WSM_TRANSMIT_RATE_HT_26 (17) + +/* 39 Mbps HT-OFDM, 16QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_39 (18) + +/* 52 Mbps HT-OFDM, 64QAM coding rate 2/3 */ +#define WSM_TRANSMIT_RATE_HT_52 (19) + +/* 58.5 Mbps HT-OFDM, 64QAM coding rate 3/4 */ +#define WSM_TRANSMIT_RATE_HT_58 (20) + +/* 65 Mbps HT-OFDM, 64QAM coding rate 5/6 */ +#define WSM_TRANSMIT_RATE_HT_65 (21) + +/* Scan types */ +/* Foreground scan */ +#define WSM_SCAN_TYPE_FOREGROUND (0) + +/* Background scan */ +#define WSM_SCAN_TYPE_BACKGROUND (1) + +/* Auto scan */ +#define WSM_SCAN_TYPE_AUTO (2) + +/* Scan flags */ +/* Forced background scan means if the station cannot */ +/* enter the power-save mode, it shall force to perform a */ +/* background scan. Only valid when ScanType is */ +/* background scan. */ +#define WSM_SCAN_FLAG_FORCE_BACKGROUND (BIT(0)) + +/* The WLAN device scans one channel at a time so */ +/* that disturbance to the data traffic is minimized. */ +#define WSM_SCAN_FLAG_SPLIT_METHOD (BIT(1)) + +/* Preamble Type. Long if not set. */ +#define WSM_SCAN_FLAG_SHORT_PREAMBLE (BIT(2)) + +/* 11n Tx Mode. Mixed if not set. */ +#define WSM_SCAN_FLAG_11N_GREENFIELD (BIT(3)) + +#define WSM_FLAG_MAC_INSTANCE_1 (BIT(4)) + +#define WSM_FLAG_MAC_INSTANCE_0 (~(BIT(4))) + +/* Scan constraints */ +/* Maximum number of channels to be scanned. */ +#define WSM_SCAN_MAX_NUM_OF_CHANNELS (48) + +/* The maximum number of SSIDs that the device can scan for. */ +#define WSM_SCAN_MAX_NUM_OF_SSIDS (2) +#ifdef CONFIG_XRADIO_TESTMODE +/* Transmit flags */ +/* Start Expiry time from the receipt of tx request */ +#define WSM_TX_FLAG_EXPIRY_TIME (BIT(0)) +#endif /*CONFIG_XRADIO_TESTMODE*/ + +/* Power management modes */ +/* 802.11 Active mode */ +#define WSM_PSM_ACTIVE (0) + +/* 802.11 PS mode */ +#define WSM_PSM_PS BIT(0) + +/* Fast Power Save bit */ +#define WSM_PSM_FAST_PS_FLAG BIT(7) + +/* Dynamic aka Fast power save */ +#define WSM_PSM_FAST_PS (BIT(0) | BIT(7)) + +/* Undetermined */ +/* Note : Undetermined status is reported when the */ +/* NULL data frame used to advertise the PM mode to */ +/* the AP at Pre or Post Background Scan is not Acknowledged */ +#define WSM_PSM_UNKNOWN BIT(1) + +/* Queue IDs */ +/* best effort/legacy */ +#define WSM_QUEUE_BEST_EFFORT (0) + +/* background */ +#define WSM_QUEUE_BACKGROUND (1) + +/* video */ +#define WSM_QUEUE_VIDEO (2) + +/* voice */ +#define WSM_QUEUE_VOICE (3) + +/* HT TX parameters */ +/* Non-HT */ +#define WSM_HT_TX_NON_HT (0) + +/* Mixed format */ +#define WSM_HT_TX_MIXED (1) + +/* Greenfield format */ +#define WSM_HT_TX_GREENFIELD (2) + +/* STBC allowed */ +#define WSM_HT_TX_STBC (BIT(7)) + +/* EPTA prioirty flags for BT Coex */ +/* default epta priority */ +#define WSM_EPTA_PRIORITY_DEFAULT 4 +/* use for normal data */ +#define WSM_EPTA_PRIORITY_DATA 4 +/* use for connect/disconnect/roaming*/ +#define WSM_EPTA_PRIORITY_MGT 5 +/* use for action frames */ +#define WSM_EPTA_PRIORITY_ACTION 5 +/* use for AC_VI data */ +#define WSM_EPTA_PRIORITY_VIDEO 5 +/* use for AC_VO data */ +#define WSM_EPTA_PRIORITY_VOICE 6 +/* use for EAPOL exchange */ +#define WSM_EPTA_PRIORITY_EAPOL 7 + +/* TX status */ +/* Frame was sent aggregated */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_AGGREGATION (BIT(0)) + +/* Host should requeue this frame later. */ +/* Valid only when status is WSM_REQUEUE. */ +#define WSM_TX_STATUS_REQUEUE (BIT(1)) + +/* Normal Ack */ +#define WSM_TX_STATUS_NORMAL_ACK (0<<2) + +/* No Ack */ +#define WSM_TX_STATUS_NO_ACK (1<<2) + +/* No explicit acknowledgement */ +#define WSM_TX_STATUS_NO_EXPLICIT_ACK (2<<2) + +/* Block Ack */ +/* Only valid for WSM_SUCCESS status. */ +#define WSM_TX_STATUS_BLOCK_ACK (3<<2) + +/* RX status */ +/* Unencrypted */ +#define WSM_RX_STATUS_UNENCRYPTED (0<<0) + +/* WEP */ +#define WSM_RX_STATUS_WEP (1<<0) + +/* TKIP */ +#define WSM_RX_STATUS_TKIP (2<<0) + +/* AES */ +#define WSM_RX_STATUS_AES (3<<0) + +/* WAPI */ +#define WSM_RX_STATUS_WAPI (4<<0) + +/* Macro to fetch encryption subfield. */ +#define WSM_RX_STATUS_ENCRYPTION(status) ((status) & 0x07) + +/* Frame was part of an aggregation */ +#define WSM_RX_STATUS_AGGREGATE (BIT(3)) + +/* Frame was first in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_FIRST (BIT(4)) + +/* Frame was last in the aggregation */ +#define WSM_RX_STATUS_AGGREGATE_LAST (BIT(5)) + +/* Indicates a defragmented frame */ +#define WSM_RX_STATUS_DEFRAGMENTED (BIT(6)) + +/* Indicates a Beacon frame */ +#define WSM_RX_STATUS_BEACON (BIT(7)) + +/* Indicates STA bit beacon TIM field */ +#define WSM_RX_STATUS_TIM (BIT(8)) + +/* Indicates Beacon frame's virtual bitmap contains multicast bit */ +#define WSM_RX_STATUS_MULTICAST (BIT(9)) + +/* Indicates frame contains a matching SSID */ +#define WSM_RX_STATUS_MATCHING_SSID (BIT(10)) + +/* Indicates frame contains a matching BSSI */ +#define WSM_RX_STATUS_MATCHING_BSSI (BIT(11)) + +/* Indicates More bit set in Framectl field */ +#define WSM_RX_STATUS_MORE_DATA (BIT(12)) + +/* Indicates frame received during a measurement process */ +#define WSM_RX_STATUS_MEASUREMENT (BIT(13)) + +/* Indicates frame received as an HT packet */ +#define WSM_RX_STATUS_HT (BIT(14)) + +/* Indicates frame received with STBC */ +#define WSM_RX_STATUS_STBC (BIT(15)) + +/* Indicates Address 1 field matches dot11StationId */ +#define WSM_RX_STATUS_ADDRESS1 (BIT(16)) + +/* Indicates Group address present in the Address 1 field */ +#define WSM_RX_STATUS_GROUP (BIT(17)) + +/* Indicates Broadcast address present in the Address 1 field */ +#define WSM_RX_STATUS_BROADCAST (BIT(18)) + +/* Indicates group key used with encrypted frames */ +#define WSM_RX_STATUS_GROUP_KEY (BIT(19)) + +/* Macro to fetch encryption key index. */ +#define WSM_RX_STATUS_KEY_IDX(status) (((status >> 20)) & 0x0F) + +/* Frame Control field starts at Frame offset + 2 */ +#define WSM_TX_2BYTES_SHIFT (BIT(7)) + +/* Join mode */ +/* IBSS */ +#define WSM_JOIN_MODE_IBSS (0) + +/* BSS */ +#define WSM_JOIN_MODE_BSS (1) + +/* PLCP preamble type */ +/* For long preamble */ +#define WSM_JOIN_PREAMBLE_LONG (0) + +/* For short preamble (Long for 1Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT (1) + +/* For short preamble (Long for 1 and 2Mbps) */ +#define WSM_JOIN_PREAMBLE_SHORT_2 (2) + +/* Join flags */ +/* Unsynchronized */ +#define WSM_JOIN_FLAGS_UNSYNCRONIZED BIT(0) +/* The BSS owner is a P2P GO */ +#define WSM_JOIN_FLAGS_P2P_GO BIT(1) +/* Force to join BSS with the BSSID and the + * SSID specified without waiting for beacons. The + * ProbeForJoin parameter is ignored. */ +#define WSM_JOIN_FLAGS_FORCE BIT(2) +/* Give probe request/response higher + * priority over the BT traffic */ +#define WSM_JOIN_FLAGS_PRIO BIT(3) + +/* Key types */ +#define WSM_KEY_TYPE_WEP_DEFAULT (0) +#define WSM_KEY_TYPE_WEP_PAIRWISE (1) +#define WSM_KEY_TYPE_TKIP_GROUP (2) +#define WSM_KEY_TYPE_TKIP_PAIRWISE (3) +#define WSM_KEY_TYPE_AES_GROUP (4) +#define WSM_KEY_TYPE_AES_PAIRWISE (5) +#define WSM_KEY_TYPE_WAPI_GROUP (6) +#define WSM_KEY_TYPE_WAPI_PAIRWISE (7) + +/* Key indexes */ +#define WSM_KEY_MAX_INDEX (10) + +/* ACK policy */ +#define WSM_ACK_POLICY_NORMAL (0) +#define WSM_ACK_POLICY_NO_ACK (1) + +/* Start modes */ +#define WSM_START_MODE_AP (0) /* Mini AP */ +#define WSM_START_MODE_P2P_GO (1) /* P2P GO */ +#define WSM_START_MODE_P2P_DEV (2) /* P2P device */ + +/* SetAssociationMode MIB flags */ +#define WSM_ASSOCIATION_MODE_USE_PREAMBLE_TYPE (BIT(0)) +#define WSM_ASSOCIATION_MODE_USE_HT_MODE (BIT(1)) +#define WSM_ASSOCIATION_MODE_USE_BASIC_RATE_SET (BIT(2)) +#define WSM_ASSOCIATION_MODE_USE_MPDU_START_SPACING (BIT(3)) +#define WSM_ASSOCIATION_MODE_SNOOP_ASSOC_FRAMES (BIT(4)) + +/* RcpiRssiThreshold MIB flags */ +#define WSM_RCPI_RSSI_THRESHOLD_ENABLE (BIT(0)) +#define WSM_RCPI_RSSI_USE_RSSI (BIT(1)) +#define WSM_RCPI_RSSI_DONT_USE_UPPER (BIT(2)) +#define WSM_RCPI_RSSI_DONT_USE_LOWER (BIT(3)) + +/* Update-ie constants */ +#define WSM_UPDATE_IE_BEACON (BIT(0)) +#define WSM_UPDATE_IE_PROBE_RESP (BIT(1)) +#define WSM_UPDATE_IE_PROBE_REQ (BIT(2)) + +/* WSM events */ +/* Error */ +#define WSM_EVENT_ERROR (0) + +/* BSS lost */ +#define WSM_EVENT_BSS_LOST (1) + +/* BSS regained */ +#define WSM_EVENT_BSS_REGAINED (2) + +/* Radar detected */ +#define WSM_EVENT_RADAR_DETECTED (3) + +/* RCPI or RSSI threshold triggered */ +#define WSM_EVENT_RCPI_RSSI (4) + +/* BT inactive */ +#define WSM_EVENT_BT_INACTIVE (5) + +/* BT active */ +#define WSM_EVENT_BT_ACTIVE (6) + +#define WSM_EVENT_PS_MODE_ERROR (7) + +#define WSM_EVENT_INACTIVITY (9) + +/* MAC Addr Filter */ +#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030 + +/* MIB IDs */ +/* 4.1 dot11StationId */ +#define WSM_MIB_ID_DOT11_STATION_ID 0x0000 + +/* 4.2 dot11MaxtransmitMsduLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_TRANSMIT_LIFTIME 0x0001 + +/* 4.3 dot11MaxReceiveLifeTime */ +#define WSM_MIB_ID_DOT11_MAX_RECEIVE_LIFETIME 0x0002 + +/* 4.4 dot11SlotTime */ +#define WSM_MIB_ID_DOT11_SLOT_TIME 0x0003 + +/* 4.5 dot11GroupAddressesTable */ +#define WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE 0x0004 +#define WSM_MAX_GRP_ADDRTABLE_ENTRIES 8 + +/* 4.6 dot11WepDefaultKeyId */ +#define WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID 0x0005 + +/* 4.7 dot11CurrentTxPowerLevel */ +#define WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL 0x0006 + +/* 4.8 dot11RTSThreshold */ +#define WSM_MIB_ID_DOT11_RTS_THRESHOLD 0x0007 + +/* Huanglu add for firmware debug control */ +#define WSM_MIB_ID_FW_DEBUG_CONTROL 0x0008 + +/* yangfh add for read/write registers from firmware*/ +#define WSM_MIB_ID_RW_FW_REG 0x0009 + +/* yangfh add for Set max number of mpdus in a-mpdu*/ +#define WSM_MIB_ID_SET_AMPDU_NUM 0x000a + +/* Huanglu add for tx-ampdu-len-adaption */ +#define WSM_MIB_ID_SET_TALA_PARA 0x000b + +/* yangfh add for set TPA param */ +#define WSM_MIB_ID_SET_TPA_PARAM 0x000c + +/* 4.9 NonErpProtection */ +#define WSM_MIB_ID_NON_ERP_PROTECTION 0x1000 + +/* 4.10 ArpIpAddressesTable */ +#define WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE 0x1001 +#define WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES 1 + +/* 4.11 TemplateFrame */ +#define WSM_MIB_ID_TEMPLATE_FRAME 0x1002 + +/* 4.12 RxFilter */ +#define WSM_MIB_ID_RX_FILTER 0x1003 + +/* 4.13 BeaconFilterTable */ +#define WSM_MIB_ID_BEACON_FILTER_TABLE 0x1004 + +/* 4.14 BeaconFilterEnable */ +#define WSM_MIB_ID_BEACON_FILTER_ENABLE 0x1005 + +/* 4.15 OperationalPowerMode */ +#define WSM_MIB_ID_OPERATIONAL_POWER_MODE 0x1006 + +/* 4.16 BeaconWakeUpPeriod */ +#define WSM_MIB_ID_BEACON_WAKEUP_PERIOD 0x1007 + +/* 4.17 RcpiRssiThreshold */ +#define WSM_MIB_ID_RCPI_RSSI_THRESHOLD 0x1009 + +/* 4.18 StatisticsTable */ +#define WSM_MIB_ID_STATISTICS_TABLE 0x100A + +/* 4.19 IbssPsConfig */ +#define WSM_MIB_ID_IBSS_PS_CONFIG 0x100B + +/* 4.20 CountersTable */ +#define WSM_MIB_ID_COUNTERS_TABLE 0x100C +#define WSM_MIB_ID_AMPDUCOUNTERS_TABLE 0x1036 +#define WSM_MIB_ID_TXPIPE_TABLE 0x1037 +#define WSM_MIB_ID_BACKOFF_DBG 0x1038 +#define WSM_MIB_ID_BACKOFF_CTRL 0x1039 + +//add yangfh for requery packet status +#define WSM_MIB_ID_REQ_PKT_STATUS 0x1040 + +//add yangfh for TPA debug informations +#define WSM_MIB_ID_TPA_DEBUG_INFO 0x1041 + +//add yangfh for tx power informations +#define WSM_MIB_ID_TX_POWER_INFO 0x1042 + +//add yangfh for some hardware information +#define WSM_MIB_ID_HW_INFO 0x1043 + +/* 4.21 BlockAckPolicy */ +#define WSM_MIB_ID_BLOCK_ACK_POLICY 0x100E + +/* 4.22 OverrideInternalTxRate */ +#define WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE 0x100F + +/* 4.23 SetAssociationMode */ +#define WSM_MIB_ID_SET_ASSOCIATION_MODE 0x1010 + +/* 4.24 UpdateEptaConfigData */ +#define WSM_MIB_ID_UPDATE_EPTA_CONFIG_DATA 0x1011 + +/* 4.25 SelectCcaMethod */ +#define WSM_MIB_ID_SELECT_CCA_METHOD 0x1012 + +/* 4.26 SetUpasdInformation */ +#define WSM_MIB_ID_SET_UAPSD_INFORMATION 0x1013 + +/* 4.27 SetAutoCalibrationMode WBF00004073 */ +#define WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE 0x1015 + +/* 4.28 SetTxRateRetryPolicy */ +#define WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY 0x1016 + +/* 4.29 SetHostMessageTypeFilter */ +#define WSM_MIB_ID_SET_HOST_MSG_TYPE_FILTER 0x1017 + +/* 4.30 P2PFindInfo */ +#define WSM_MIB_ID_P2P_FIND_INFO 0x1018 + +/* 4.31 P2PPsModeInfo */ +#define WSM_MIB_ID_P2P_PS_MODE_INFO 0x1019 + +/* 4.32 SetEtherTypeDataFrameFilter */ +#define WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER 0x101A + +/* 4.33 SetUDPPortDataFrameFilter */ +#define WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER 0x101B + +/* 4.34 SetMagicDataFrameFilter */ +#define WSM_MIB_ID_SET_MAGIC_DATAFRAME_FILTER 0x101C +#define WSM_MIB_ID_SET_HOST_SLEEP 0x1050 + +/* This is the end of specification. */ + +/* 4.35 P2PDeviceInfo */ +#define WSM_MIB_ID_P2P_DEVICE_INFO 0x101D + +/* 4.36 SetWCDMABand */ +#define WSM_MIB_ID_SET_WCDMA_BAND 0x101E + +/* 4.37 GroupTxSequenceCounter */ +#define WSM_MIB_ID_GRP_SEQ_COUNTER 0x101F + +/* 4.38 ProtectedMgmtPolicy */ +#define WSM_MIB_ID_PROTECTED_MGMT_POLICY 0x1020 + +/* 4.39 SetHtProtection */ +#define WSM_MID_ID_SET_HT_PROTECTION 0x1021 + +/* 4.40 GPIO Command */ +#define WSM_MIB_ID_GPIO_COMMAND 0x1022 + +/* 4.41 TSF Counter Value */ +#define WSM_MIB_ID_TSF_COUNTER 0x1023 + +/* Test Purposes Only */ +#define WSM_MIB_ID_BLOCK_ACK_INFO 0x100D + +/* 4.42 UseMultiTxConfMessage */ +#define WSM_MIB_USE_MULTI_TX_CONF 0x1024 + +/* 4.43 Keep-alive period */ +#define WSM_MIB_ID_KEEP_ALIVE_PERIOD 0x1025 + +/* 4.44 Disable BSSID filter */ +#define WSM_MIB_ID_DISABLE_BSSID_FILTER 0x1026 + +/* Inactivity */ +#define WSM_MIB_ID_SET_INACTIVITY 0x1035 + +/* MAC Addr Filter */ +#define WSM_MIB_ID_MAC_ADDR_FILTER 0x1030 + +#ifdef MCAST_FWDING +/* 4.51 Set Forwarding Offload */ +#define WSM_MIB_ID_FORWARDING_OFFLOAD 0x1033 +#endif + +#ifdef IPV6_FILTERING +/* IpV6 Addr Filter */ +/* 4.52 Neighbor solicitation IPv6 address table */ +#define WSM_MIB_IP_IPV6_ADDR_FILTER 0x1032 +#define WSM_MIB_ID_NS_IP_ADDRESSES_TABLE 0x1034 +#define WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES 1 +#endif /*IPV6_FILTERING*/ + +/* Frame template types */ +#define WSM_FRAME_TYPE_PROBE_REQUEST (0) +#define WSM_FRAME_TYPE_BEACON (1) +#define WSM_FRAME_TYPE_NULL (2) +#define WSM_FRAME_TYPE_QOS_NULL (3) +#define WSM_FRAME_TYPE_PS_POLL (4) +#define WSM_FRAME_TYPE_PROBE_RESPONSE (5) +#define WSM_FRAME_TYPE_ARP_REPLY (6) + +#ifdef IPV6_FILTERING +#define WSM_FRAME_TYPE_NA (7) +#endif /*IPV6_FILTERING*/ + +#define WSM_FRAME_GREENFIELD (0x80) /* See 4.11 */ + +/* Status */ +/* The WSM firmware has completed a request */ +/* successfully. */ +#define WSM_STATUS_SUCCESS (0) + +/* This is a generic failure code if other error codes do */ +/* not apply. */ +#define WSM_STATUS_FAILURE (1) + +/* A request contains one or more invalid parameters. */ +#define WSM_INVALID_PARAMETER (2) + +/* The request cannot perform because the device is in */ +/* an inappropriate mode. */ +#define WSM_ACCESS_DENIED (3) + +/* The frame received includes a decryption error. */ +#define WSM_STATUS_DECRYPTFAILURE (4) + +/* A MIC failure is detected in the received packets. */ +#define WSM_STATUS_MICFAILURE (5) + +/* The transmit request failed due to retry limit being */ +/* exceeded. */ +#define WSM_STATUS_RETRY_EXCEEDED (6) + +/* The transmit request failed due to MSDU life time */ +/* being exceeded. */ +#define WSM_STATUS_TX_LIFETIME_EXCEEDED (7) + +/* The link to the AP is lost. */ +#define WSM_STATUS_LINK_LOST (8) + +/* No key was found for the encrypted frame */ +#define WSM_STATUS_NO_KEY_FOUND (9) + +/* Jammer was detected when transmitting this frame */ +#define WSM_STATUS_JAMMER_DETECTED (10) + +/* The message should be requeued later. */ +/* This is applicable only to Transmit */ +#define WSM_REQUEUE (11) + +/* Advanced filtering options */ +#define WSM_MAX_FILTER_ELEMENTS (4) + +#define WSM_FILTER_ACTION_IGNORE (0) +#define WSM_FILTER_ACTION_FILTER_IN (1) +#define WSM_FILTER_ACTION_FILTER_OUT (2) + +#define WSM_FILTER_PORT_TYPE_DST (0) +#define WSM_FILTER_PORT_TYPE_SRC (1) + + + +struct wsm_hdr { + __le16 len; + __le16 id; +}; + +#define WSM_TX_SEQ_MAX (7) +#define WSM_TX_SEQ(seq) \ + ((seq & WSM_TX_SEQ_MAX) << 13) +#define WSM_TX_LINK_ID_MAX (0x0F) +#define WSM_TX_LINK_ID(link_id) \ + ((link_id & WSM_TX_LINK_ID_MAX) << 6) + +#define WSM_TX_IF_ID_MAX (0x0F) +#define WSM_TX_IF_ID(if_id) \ + ((if_id & WSM_TX_IF_ID_MAX) << 6) + +#define MAX_BEACON_SKIP_TIME_MS 1000 + +#ifdef FPGA_SETUP +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 9 / 2) +#else +#define WSM_CMD_LAST_CHANCE_TIMEOUT (HZ * 20 / 2) +#endif +#define WSM_CMD_EXTENDED_TIMEOUT (HZ * 20 / 2) + +#define WSM_RI_GET_PEER_ID_FROM_FLAGS(_f) (((_f)&(0xF<<25)>>25)) + + +/* ******************************************************************** */ +/* WSM capcbility */ +#define WSM_FW_LABEL 128 +struct wsm_caps { + u16 numInpChBufs; + u16 sizeInpChBuf; + u16 hardwareId; + u16 hardwareSubId; + u16 firmwareCap; + u16 firmwareType; + u16 firmwareApiVer; + u16 firmwareBuildNumber; + u16 firmwareVersion; + char fw_label[WSM_FW_LABEL+2]; + int firmwareReady; +}; + +/* ******************************************************************** */ +/* WSM commands */ + +struct wsm_tx_power_range { + int min_power_level; + int max_power_level; + u32 stepping; +}; + +/* 3.1 */ +struct wsm_configuration { + /* [in] */ u32 dot11MaxTransmitMsduLifeTime; + /* [in] */ u32 dot11MaxReceiveLifeTime; + /* [in] */ u32 dot11RtsThreshold; + /* [in, out] */ u8 *dot11StationId; + /* [in] */ const void *dpdData; + /* [in] */ size_t dpdData_size; + /* [out] */ u8 dot11FrequencyBandsSupported; + /* [out] */ u32 supportedRateMask; + /* [out] */ struct wsm_tx_power_range txPowerRange[2]; +}; + +int wsm_configuration(struct xradio_common *hw_priv, + struct wsm_configuration *arg, + int if_id); + +/* 3.3 */ +struct wsm_reset { + /* [in] */ int link_id; + /* [in] */ bool reset_statistics; +}; + +int wsm_reset(struct xradio_common *hw_priv, const struct wsm_reset *arg, + int if_id); + +//add by yangfh +void wsm_upper_restart(struct xradio_common *hw_priv); + +//add by yangfh +void wsm_query_work(struct work_struct *work); + +/* 3.5 */ +int wsm_read_mib(struct xradio_common *hw_priv, u16 mibId, void *buf, + size_t buf_size, size_t arg_size); + +/* 3.7 */ +int wsm_write_mib(struct xradio_common *hw_priv, u16 mibId, void *buf, + size_t buf_size, int if_id); + +/* 3.9 */ +struct wsm_ssid { + u8 ssid[32]; + u32 length; +}; + +struct wsm_scan_ch { + u16 number; + u32 minChannelTime; + u32 maxChannelTime; + u32 txPowerLevel; +}; + +/* 3.13 */ +struct wsm_scan_complete { + /* WSM_STATUS_... */ + u32 status; + + /* WSM_PSM_... */ + u8 psm; + + /* Number of channels that the scan operation completed. */ + u8 numChannels; +#ifdef ROAM_OFFLOAD + u16 reserved; +#endif /*ROAM_OFFLOAD*/ +}; + +typedef void (*wsm_scan_complete_cb) (struct xradio_common *hw_priv, + struct wsm_scan_complete *arg); + +/* 3.9 */ +struct wsm_scan { + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* WSM_SCAN_TYPE_... */ + /* [in] */ u8 scanType; + + /* WSM_SCAN_FLAG_... */ + /* [in] */ u8 scanFlags; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTransmitRate; + + /* Interval period in TUs that the device shall the re- */ + /* execute the requested scan. Max value supported by the device */ + /* is 256s. */ + /* [in] */ u32 autoScanInterval; + + /* Number of probe requests (per SSID) sent to one (1) */ + /* channel. Zero (0) means that none is send, which */ + /* means that a passive scan is to be done. Value */ + /* greater than zero (0) means that an active scan is to */ + /* be done. */ + /* [in] */ u32 numOfProbeRequests; + + /* Number of channels to be scanned. */ + /* Maximum value is WSM_SCAN_MAX_NUM_OF_CHANNELS. */ + /* [in] */ u8 numOfChannels; + + /* Number of SSID provided in the scan command (this */ + /* is zero (0) in broadcast scan) */ + /* The maximum number of SSIDs is WSM_SCAN_MAX_NUM_OF_SSIDS. */ + /* [in] */ u8 numOfSSIDs; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* SSIDs to be scanned [numOfSSIDs]; */ + /* [in] */ struct wsm_ssid *ssids; + + /* Channels to be scanned [numOfChannels]; */ + /* [in] */ struct wsm_scan_ch *ch; +}; + +int wsm_scan(struct xradio_common *hw_priv, const struct wsm_scan *arg, + int if_id); + +/* 3.11 */ +int wsm_stop_scan(struct xradio_common *hw_priv, int if_id); + +/* 3.14 */ +struct wsm_tx_confirm { + /* Packet identifier used in wsm_tx. */ + /* [out] */ u32 packetID; + + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 txedRate; + + /* The number of times the frame was transmitted */ + /* without receiving an acknowledgement. */ + /* [out] */ u8 ackFailures; + + /* WSM_TX_STATUS_... */ + /* [out] */ u16 flags; + + //rate feed back, add by yangfh + /* [out] */ u32 rate_try[3]; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission as completed. */ + /* [out] */ u32 mediaDelay; + + /* The total time in microseconds that the frame spent in */ + /* the WLAN device before transmission was started. */ + /* [out] */ u32 txQueueDelay; + + /* [out]*/ u32 link_id; + + /*[out]*/ int if_id; +}; + +/* 3.15 */ +typedef void (*wsm_tx_confirm_cb) (struct xradio_common *hw_priv, + struct wsm_tx_confirm *arg); + +/* Note that ideology of wsm_tx struct is different against the rest of + * WSM API. wsm_hdr is /not/ a caller-adapted struct to be used as an input + * argument for WSM call, but a prepared bytestream to be sent to firmware. + * It is filled partly in xradio_tx, partly in low-level WSM code. + * Please pay attention once again: ideology is different. + * + * Legend: + * - [in]: xradio_tx must fill this field. + * - [wsm]: the field is filled by low-level WSM. + */ +struct wsm_tx { + /* common WSM header */ + /* [in/wsm] */ struct wsm_hdr hdr; + + /* Packet identifier that meant to be used in completion. */ + /* [in] */ __le32 packetID; + + /* WSM_TRANSMIT_RATE_... */ + /* [in] */ u8 maxTxRate; + + /* WSM_QUEUE_... */ + /* [in] */ u8 queueId; + + /* True: another packet is pending on the host for transmission. */ + /* [wsm] */ u8 more; + + /* Bit 0 = 0 - Start expiry time from first Tx attempt (default) */ + /* Bit 0 = 1 - Start expiry time from receipt of Tx Request */ + /* Bits 3:1 - PTA Priority */ + /* Bits 6:4 - Tx Rate Retry Policy */ + /* Bit 7 - Reserved */ + /* [in] */ u8 flags; + + /* Should be 0. */ + /* [in] */ __le32 reserved; + + /* The elapsed time in TUs, after the initial transmission */ + /* of an MSDU, after which further attempts to transmit */ + /* the MSDU shall be terminated. Overrides the global */ + /* dot11MaxTransmitMsduLifeTime setting [optional] */ + /* Device will set the default value if this is 0. */ + /* [wsm] */ __le32 expireTime; + + /* WSM_HT_TX_... */ + /* [in] */ __le32 htTxParameters; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) + sizeof(alignment) */ +#define WSM_TX_EXTRA_HEADROOM (28) + +/* 3.16 */ +struct wsm_rx { + /* WSM_STATUS_... */ + /* [out] */ u32 status; + + /* Specifies the channel of the received packet. */ + /* [out] */ u16 channelNumber; + + /* WSM_TRANSMIT_RATE_... */ + /* [out] */ u8 rxedRate; + + /* This value is expressed in signed Q8.0 format for */ + /* RSSI and unsigned Q7.1 format for RCPI. */ + /* [out] */ u8 rcpiRssi; + + /* WSM_RX_STATUS_... */ + /* [out] */ u32 flags; + + /* An 802.11 frame. */ + /* [out] */ void *frame; + + /* Size of the frame */ + /* [out] */ size_t frame_size; + + /* Link ID */ + /* [out] */ int link_id; + /* [out] */ int if_id; +}; + +/* = sizeof(generic hi hdr) + sizeof(wsm hdr) */ +#define WSM_RX_EXTRA_HEADROOM (16) + +typedef void (*wsm_rx_cb) (struct xradio_vif *priv, struct wsm_rx *arg, + struct sk_buff **skb_p); + +/* 3.17 */ +struct wsm_event { + /* WSM_STATUS_... */ + /* [out] */ u32 eventId; + + /* Indication parameters. */ + /* For error indication, this shall be a 32-bit WSM status. */ + /* For RCPI or RSSI indication, this should be an 8-bit */ + /* RCPI or RSSI value. */ + /* [out] */ u32 eventData; +}; + +struct xradio_wsm_event { + struct list_head link; + struct wsm_event evt; + u8 if_id; +}; + +/* 3.18 - 3.22 */ +/* Measurement. Skipped for now. Irrelevent. */ + +typedef void (*wsm_event_cb) (struct xradio_common *hw_priv, + struct wsm_event *arg); + +/* 3.23 */ +struct wsm_join { + /* WSM_JOIN_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Specifies the channel number to join. The channel */ + /* number will be mapped to an actual frequency */ + /* according to the band */ + /* [in] */ u16 channelNumber; + + /* Specifies the BSSID of the BSS or IBSS to be joined */ + /* or the IBSS to be started. */ + /* [in] */ u8 bssid[6]; + + /* ATIM window of IBSS */ + /* When ATIM window is zero the initiated IBSS does */ + /* not support power saving. */ + /* [in] */ u16 atimWindow; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* Specifies if a probe request should be send with the */ + /* specified SSID when joining to the network. */ + /* [in] */ u8 probeForJoin; + + /* DTIM Period (In multiples of beacon interval) */ + /* [in] */ u8 dtimPeriod; + + /* WSM_JOIN_FLAGS_... */ + /* [in] */ u8 flags; + + /* Length of the SSID */ + /* [in] */ u32 ssidLength; + + /* Specifies the SSID of the IBSS to join or start */ + /* [in] */ u8 ssid[32]; + + /* Specifies the time between TBTTs in TUs */ + /* [in] */ u32 beaconInterval; + + /* A bit mask that defines the BSS basic rate set. */ + /* [in] */ u32 basicRateSet; + + /* Minimum transmission power level in units of 0.1dBm */ + /* [out] */ int minPowerLevel; + + /* Maximum transmission power level in units of 0.1dBm */ + /* [out] */ int maxPowerLevel; +}; + +int wsm_join(struct xradio_common *hw_priv, struct wsm_join *arg, int if_id); + +/* 3.25 */ +struct wsm_set_pm { + /* WSM_PSM_... */ + /* [in] */ u8 pmMode; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 fastPsmIdlePeriod; + + /* in unit of 500us; 0 to use default */ + /* [in] */ u8 apPsmChangePeriod; + + /* in unit of 500us; 0 to disable auto-pspoll */ + /* [in] */ u8 minAutoPsPollPeriod; +}; + +int wsm_set_pm(struct xradio_common *hw_priv, const struct wsm_set_pm *arg, + int if_id); + +/* 3.27 */ +struct wsm_set_pm_complete { + u8 psm; /* WSM_PSM_... */ +}; + +typedef void (*wsm_set_pm_complete_cb) (struct xradio_common *hw_priv, + struct wsm_set_pm_complete *arg); + +/* 3.28 */ +struct wsm_set_bss_params { + /* The number of lost consecutive beacons after which */ + /* the WLAN device should indicate the BSS-Lost event */ + /* to the WLAN host driver. */ + u8 beaconLostCount; + + /* The AID received during the association process. */ + u16 aid; + + /* The operational rate set mask */ + u32 operationalRateSet; +}; + +int wsm_set_bss_params(struct xradio_common *hw_priv, + const struct wsm_set_bss_params *arg, int if_id); + +/* 3.30 */ +struct wsm_add_key { + u8 type; /* WSM_KEY_TYPE_... */ + u8 entryIndex; /* Key entry index: 0 -- WSM_KEY_MAX_INDEX */ + u16 reserved; + union { + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved; + u8 keyLength; /* Key length in bytes */ + u8 keyData[16]; /* Key data */ + } __packed wepPairwiseKey; + struct { + u8 keyId; /* Unique per key identifier + * (0..3) */ + u8 keyLength; /* Key length in bytes */ + u16 reserved; + u8 keyData[16]; /* Key data */ + } __packed wepGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 reserved[2]; + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 txMicKey[8]; /* Tx MIC key */ + } __packed tkipPairwiseKey; + struct { + u8 tkipKeyData[16]; /* TKIP key data */ + u8 rxMicKey[8]; /* Rx MIC key */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed tkipGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u16 reserved; + u8 aesKeyData[16]; /* AES key data */ + } __packed aesPairwiseKey; + struct { + u8 aesKeyData[16]; /* AES key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + u8 rxSeqCounter[8]; /* Receive Sequence Counter */ + } __packed aesGroupKey; + struct { + u8 peerAddress[6]; /* MAC address of the + * peer station */ + u8 keyId; /* Key ID */ + u8 reserved; + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + } __packed wapiPairwiseKey; + struct { + u8 wapiKeyData[16]; /* WAPI key data */ + u8 micKeyData[16]; /* MIC key data */ + u8 keyId; /* Key ID */ + u8 reserved[3]; + } __packed wapiGroupKey; + } __packed; +} __packed; + +int wsm_add_key(struct xradio_common *hw_priv, const struct wsm_add_key *arg, + int if_id); + +/* 3.32 */ +struct wsm_remove_key { + /* Key entry index : 0-10 */ + u8 entryIndex; +}; + +int wsm_remove_key(struct xradio_common *hw_priv, + const struct wsm_remove_key *arg, int if_id); + +/* 3.34 */ +struct wsm_set_tx_queue_params { + /* WSM_ACK_POLICY_... */ + u8 ackPolicy; + + /* Medium Time of TSPEC (in 32us units) allowed per */ + /* One Second Averaging Period for this queue. */ + u16 allowedMediumTime; + + /* dot11MaxTransmitMsduLifetime to be used for the */ + /* specified queue. */ + u32 maxTransmitLifetime; +}; + +struct wsm_tx_queue_params { + /* NOTE: index is a linux queue id. */ + struct wsm_set_tx_queue_params params[4]; +}; + +#define WSM_TX_QUEUE_SET(queue_params, queue, ack_policy, allowed_time, \ + max_life_time) \ +do { \ + struct wsm_set_tx_queue_params *p = &(queue_params)->params[queue]; \ + p->ackPolicy = (ack_policy); \ + p->allowedMediumTime = (allowed_time); \ + p->maxTransmitLifetime = (max_life_time); \ +} while (0) + +int wsm_set_tx_queue_params(struct xradio_common *hw_priv, + const struct wsm_set_tx_queue_params *arg, + u8 id, int if_id); + +/* 3.36 */ +struct wsm_edca_queue_params { + /* CWmin (in slots) for the access class. */ + /* [in] */ u16 cwMin; + + /* CWmax (in slots) for the access class. */ + /* [in] */ u16 cwMax; + + /* AIFS (in slots) for the access class. */ + /* [in] */ u8 aifns; + + /* TX OP Limit (in microseconds) for the access class. */ + /* [in] */ u16 txOpLimit; + + /* dot11MaxReceiveLifetime to be used for the specified */ + /* the access class. Overrides the global */ + /* dot11MaxReceiveLifetime value */ + /* [in] */ u32 maxReceiveLifetime; + + /* UAPSD trigger support for the access class. */ + /* [in] */ bool uapsdEnable; +}; + +struct wsm_edca_params { + /* NOTE: index is a linux queue id. */ + struct wsm_edca_queue_params params[4]; +}; + +#define TXOP_UNIT 32 +#define WSM_EDCA_SET(edca, queue, aifs, cw_min, cw_max, txop, life_time,\ + uapsd) \ + do { \ + struct wsm_edca_queue_params *p = &(edca)->params[queue]; \ + p->cwMin = (cw_min); \ + p->cwMax = (cw_max); \ + p->aifns = (aifs); \ + p->txOpLimit = ((txop) * TXOP_UNIT); \ + p->maxReceiveLifetime = (life_time); \ + p->uapsdEnable = (uapsd); \ + } while (0) + +int wsm_set_edca_params(struct xradio_common *hw_priv, + const struct wsm_edca_params *arg, int if_id); + +int wsm_set_uapsd_param(struct xradio_common *hw_priv, + const struct wsm_edca_params *arg); + +/* 3.38 */ +/* Set-System info. Skipped for now. Irrelevent. */ + +/* 3.40 */ +struct wsm_switch_channel { + /* 1 - means the STA shall not transmit any further */ + /* frames until the channel switch has completed */ + /* [in] */ u8 channelMode; + + /* Number of TBTTs until channel switch occurs. */ + /* 0 - indicates switch shall occur at any time */ + /* 1 - occurs immediately before the next TBTT */ + /* [in] */ u8 channelSwitchCount; + + /* The new channel number to switch to. */ + /* Note this is defined as per section 2.7. */ + /* [in] */ u16 newChannelNumber; +}; + +int wsm_switch_channel(struct xradio_common *hw_priv, + const struct wsm_switch_channel *arg, int if_id); + +typedef void (*wsm_channel_switch_cb) (struct xradio_common *hw_priv); + +struct wsm_start { + /* WSM_START_MODE_... */ + /* [in] */ u8 mode; + + /* WSM_PHY_BAND_... */ + /* [in] */ u8 band; + + /* Channel number */ + /* [in] */ u16 channelNumber; + + /* Client Traffic window in units of TU */ + /* Valid only when mode == ..._P2P */ + /* [in] */ u32 CTWindow; + + /* Interval between two consecutive */ + /* beacon transmissions in TU. */ + /* [in] */ u32 beaconInterval; + + /* DTIM period in terms of beacon intervals */ + /* [in] */ u8 DTIMPeriod; + + /* WSM_JOIN_PREAMBLE_... */ + /* [in] */ u8 preambleType; + + /* The delay time (in microseconds) period */ + /* before sending a probe-request. */ + /* [in] */ u8 probeDelay; + + /* Length of the SSID */ + /* [in] */ u8 ssidLength; + + /* SSID of the BSS or P2P_GO to be started now. */ + /* [in] */ u8 ssid[32]; + + /* The basic supported rates for the MiniAP. */ + /* [in] */ u32 basicRateSet; +}; + +int wsm_start(struct xradio_common *hw_priv, const struct wsm_start *arg, + int if_id); + +#if 0 +struct wsm_beacon_transmit { + /* 1: enable; 0: disable */ + /* [in] */ u8 enableBeaconing; +}; + +int wsm_beacon_transmit(struct xradio_common *hw_priv, + const struct wsm_beacon_transmit *arg, + int if_id); +#endif + +int wsm_start_find(struct xradio_common *hw_priv, int if_id); + +int wsm_stop_find(struct xradio_common *hw_priv, int if_id); + +typedef void (*wsm_find_complete_cb) (struct xradio_common *hw_priv, + u32 status); + +struct wsm_suspend_resume { + /* See 3.52 */ + /* Link ID */ + /* [out] */ int link_id; + /* Stop sending further Tx requests down to device for this link */ + /* [out] */ bool stop; + /* Transmit multicast Frames */ + /* [out] */ bool multicast; + /* The AC on which Tx to be suspended /resumed. */ + /* This is applicable only for U-APSD */ + /* WSM_QUEUE_... */ + /* [out] */ int queue; + /* [out] */ int if_id; +}; + +typedef void (*wsm_suspend_resume_cb) (struct xradio_vif *priv, + struct wsm_suspend_resume *arg); + +/* 3.54 Update-IE request. */ +struct wsm_update_ie { + /* WSM_UPDATE_IE_... */ + /* [in] */ u16 what; + /* [in] */ u16 count; + /* [in] */ u8 *ies; + /* [in] */ size_t length; +}; + +int wsm_update_ie(struct xradio_common *hw_priv, + const struct wsm_update_ie *arg, int if_id); + +/* 3.56 */ +struct wsm_map_link { + /* MAC address of the remote device */ + /* [in] */ u8 mac_addr[6]; + /* [in] */ u8 unmap; + /* [in] */ u8 link_id; +}; + +int wsm_map_link(struct xradio_common *hw_priv, const struct wsm_map_link *arg, + int if_id); + +struct wsm_cbc { + wsm_scan_complete_cb scan_complete; + wsm_tx_confirm_cb tx_confirm; + wsm_rx_cb rx; + wsm_event_cb event; + wsm_set_pm_complete_cb set_pm_complete; + wsm_channel_switch_cb channel_switch; + wsm_find_complete_cb find_complete; + wsm_suspend_resume_cb suspend_resume; +}; +#ifdef MCAST_FWDING + +/* 3.65 Give Buffer Request */ +int wsm_init_release_buffer_request(struct xradio_common *priv, u8 index); + +/* 3.65 fixed memory leakage by yangfh*/ +int wsm_deinit_release_buffer(struct xradio_common *hw_priv); + +/* 3.67 Request Buffer Request */ +int wsm_request_buffer_request(struct xradio_vif *priv, + u8 *arg); +#endif +/* ******************************************************************** */ +/* MIB shortcats */ +#define XR_RRM 1 +#ifdef XR_RRM//RadioResourceMeasurement +/* RadioResourceMeasurement Request*/ +#define MEAS_CCA 0 +#define MEAS_CHANNELLOAD 1 +typedef struct LMAC_MEAS_CHANNEL_LOAD_PARAMS_S +{ + u8 Reserved; + u8 ChannelLoadCCA; + u16 ChannelNum; + u16 RandomInterval; + u16 MeasurementDuration; + u32 MeasurementStartTimel; + u32 MeasurementStartTimeh; +}LMAC_MEAS_CHANNEL_LOAD_PARAMS; + +#define MEAS_RPI 0 +#define MEAS_IPI 1 + +typedef struct LMAC_MEAS_NOISE_HISTOGRAM_PARAMS_S +{ + u8 Reserved; + u8 IpiRpi; + u16 ChannelNum; + u16 RandomInterval; + u16 MeasurementDuration; + u32 MeasurementStartTimel; + u32 MeasurementStartTimeh; +}LMAC_MEAS_NOISE_HISTOGRAM_PARAMS; + +#define LMAC_MAX_SSIDS 16 +#define LMAC_MAX_SSID_LENGTH 32 +typedef struct LMAC_CHANNELS_S +{ + u32 ChannelNum; + u32 MinChannelTime; + u32 MaxChannelTime; + s32 TxPowerLevel; +}LMAC_CHANNELS; + +typedef struct LMAC_SSIDS_S +{ + u32 SSIDLength; + u8 SSID[LMAC_MAX_SSID_LENGTH]; +}LMAC_SSIDS; + +typedef struct LMAC_MEAS_BEACON_PARAMS_S +{ + //u8 RegulatoryClass; + //u8 MeasurementMode; + //u16 ChannelNum; + u16 RandomInterval; + //u16 MeasurementDuration; + //u8 Bssid[6]; + u16 Reserved; + //SCAN_PARAMETERS ScanParameters; + u8 Band; + u8 ScanType; + u8 ScanFlags; + u8 MaxTransmitRate; + u32 AutoScanInterval; + u8 NumOfProbeRequests; + u8 NumOfChannels; + u8 NumOfSSIDs; + u8 ProbeDelay; + LMAC_CHANNELS Channels; + LMAC_SSIDS Ssids; // here for SCAN_PARAMETER sizing purposes +}LMAC_MEAS_BEACON_PARAMS; + +typedef struct LMAC_MEAS_STA_STATS_PARAMS_S +{ + u8 PeerMacAddress[6]; + u16 RandomInterval; + u16 MeasurementDuration; + u8 GroupId; + u8 Reserved; +}LMAC_MEAS_STA_STATS_PARAMS; + +typedef struct LMAC_MEAS_LINK_MEASUREMENT_PARAMS_S +{ + u8 Reserved[4]; +}LMAC_MEAS_LINK_MEASUREMENT_PARAMS; + +typedef union LMAC_MEAS_REQUEST_U +{ + LMAC_MEAS_CHANNEL_LOAD_PARAMS ChannelLoadParams; + LMAC_MEAS_NOISE_HISTOGRAM_PARAMS NoisHistogramParams; + LMAC_MEAS_BEACON_PARAMS BeaconParams; + LMAC_MEAS_STA_STATS_PARAMS StaStatsParams; + LMAC_MEAS_LINK_MEASUREMENT_PARAMS LinkMeasurementParams; +} LMAC_MEAS_REQUEST; + +// This struct is a copy of WSM_HI_START_MEASUREMENT_REQ, except that MsgLen and MsgId is not included +typedef struct MEASUREMENT_PARAMETERS_S +{ +// u16 MsgLen; +// u16 MsgId; + s32 TxPowerLevel; + u8 DurationMandatory; + u8 MeasurementType; + u8 MeasurementRequestLength; + u8 Reserved[5]; + LMAC_MEAS_REQUEST MeasurementRequest; +}MEASUREMENT_PARAMETERS; + +/* RadioResourceMeasurement Result*/ + typedef struct LMAC_MEAS_CHANNEL_LOAD_RESULTS_S +{ + u8 Reserved; + u8 ChannelLoadCCA; + u16 ChannelNum; + u32 ActualMeasurementStartTimel; + u32 ActualMeasurementStartTimeh; + u16 MeasurementDuration; + u8 CCAbusyFraction; + u8 ChannelLoad; +}LMAC_MEAS_CHANNEL_LOAD_RESULTS; + +typedef struct LMAC_MEAS_NOISE_HISTOGRAM_RESULTS_S +{ + u16 Reserved; + u16 ChannelNum; + u32 ActualMeasurementStartTimel; + u32 ActualMeasurementStartTimeh; + u16 MeasurementDuration; + u8 AntennaID; + u8 IpiRpi; + u8 PI_0_Density; + u8 PI_1_Density; + u8 PI_2_Density; + u8 PI_3_Density; + u8 PI_4_Density; + u8 PI_5_Density; + u8 PI_6_Density; + u8 PI_7_Density; + u8 PI_8_Density; + u8 PI_9_Density; + u8 PI_10_Density; + u8 Reserved2; +}LMAC_MEAS_NOISE_HISTOGRAM_RESULTS; + +typedef struct LMAC_MEAS_BEACON_RESULTS_S +{ + u16 MeasurementDuration; + u16 Reserved; + u32 StartTsfl; + u32 StartTsfh; + u32 Durationl; + u32 Durationh; + //SCAN_PARAMETERS ScanParameters; + u8 Band; + u8 ScanType; + u8 ScanFlags; + u8 MaxTransmitRate; + u32 AutoScanInterval; + u8 NumOfProbeRequests; + u8 NumOfChannels; + u8 NumOfSSIDs; + u8 ProbeDelay; + LMAC_CHANNELS Channels; + LMAC_SSIDS Ssids; +}LMAC_MEAS_BEACON_RESULTS; + +typedef struct LMAC_MEAS_STA_STATS_RESULTS_S +{ + u16 MeasurementDuration; + u8 GroupId; + u8 StatisticsGroupDataLength; + u8 StatisticsGroupData[52]; +}LMAC_MEAS_STA_STATS_RESULTS; + +typedef struct LMAC_MEAS_LINK_MEASUREMENT_RESULTS_S +{ + s16 TransmitPower; + u8 RxAntennaID; + u8 TxAntennaID; + s32 NoiseLeveldBm; + s8 LatestRssi; + u8 Reserved1; + u8 Reserved2; + u8 Reserved3; +}LMAC_MEAS_LINK_MEASUREMENT_RESULTS; + +typedef union LMAC_MEAS_REPORT_U +{ + LMAC_MEAS_CHANNEL_LOAD_RESULTS ChannelLoadResults; + LMAC_MEAS_NOISE_HISTOGRAM_RESULTS NoiseHistogramResults; + LMAC_MEAS_BEACON_RESULTS BeaconResults; + LMAC_MEAS_STA_STATS_RESULTS StaStatsResults; + LMAC_MEAS_LINK_MEASUREMENT_RESULTS LinkMeasurementResults; +}LMAC_MEAS_REPORT; + +// Note: eMeasurementTypes MUST match the #define WSM_MEASURE_TYPE_XXX from wsm_api.h +typedef enum { + ChannelLoadMeasurement=0, + NoiseHistrogramMeasurement, + BeaconReport, + STAstatisticsReport, + LinkMeasurement +}eMeasurementTypes; + +typedef struct MEASUREMENT_COMPLETE_S +{ +// u16 RandomInterval; +// u16 Reserved0; + u8 Dot11PowerMgmtMode; // From here WSM_HI_MEASURE_CMPL_IND and MEASUREMENT_COMPLETE_S must be identical + u8 MeasurementType; + u16 MoreInd; // Set to 1 if more indications are to follow for this measurement, otherwise 0; + u32 Status; + u8 MeasurementReportLength; + u8 Reserved2[3]; + LMAC_MEAS_REPORT MeasurementReport; +}MEASUREMENT_COMPLETE; // Note: must be 32 bit aligned + +#endif +int wsm_11k_measure_requset(struct xradio_common *hw_priv, + u8 measure_type, + u16 ChannelNum, + u16 Duration); + + +static inline int wsm_set_fw_debug_control(struct xradio_common *hw_priv, + int debug_control, int if_id) +{ + __le32 val = __cpu_to_le32(debug_control); + return wsm_write_mib(hw_priv, WSM_MIB_ID_FW_DEBUG_CONTROL, + &val, sizeof(val), if_id); +} + +static inline int wsm_set_host_sleep(struct xradio_common *hw_priv, + u8 host_sleep, int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_HOST_SLEEP, + &host_sleep, sizeof(host_sleep), if_id); +} + +static inline int wsm_set_output_power(struct xradio_common *hw_priv, + int power_level, int if_id) +{ + __le32 val = __cpu_to_le32(power_level); + return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_CURRENT_TX_POWER_LEVEL, + &val, sizeof(val), if_id); +} + +static inline int wsm_set_beacon_wakeup_period(struct xradio_common *hw_priv, + unsigned dtim_interval, + unsigned listen_interval, + int if_id) +{ + struct { + u8 numBeaconPeriods; + u8 reserved; + __le16 listenInterval; + } val = { + dtim_interval, 0, __cpu_to_le16(listen_interval)}; + if (dtim_interval > 0xFF || listen_interval > 0xFFFF) + return -EINVAL; + else + return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_WAKEUP_PERIOD, + &val, sizeof(val), if_id); +} + +struct wsm_rcpi_rssi_threshold { + u8 rssiRcpiMode; /* WSM_RCPI_RSSI_... */ + u8 lowerThreshold; + u8 upperThreshold; + u8 rollingAverageCount; +}; + +static inline int wsm_set_rcpi_rssi_threshold(struct xradio_common *hw_priv, + struct wsm_rcpi_rssi_threshold *arg, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_RCPI_RSSI_THRESHOLD, arg, + sizeof(*arg), if_id); +} + +struct wsm_counters_table { + __le32 countPlcpErrors; + __le32 countFcsErrors; + __le32 countTxPackets; + __le32 countRxPackets; + __le32 countRxPacketErrors; + __le32 countRtsSuccess; + __le32 countRtsFailures; + __le32 countRxFramesSuccess; + __le32 countRxDecryptionFailures; + __le32 countRxMicFailures; + __le32 countRxNoKeyFailures; + __le32 countTxMulticastFrames; + __le32 countTxFramesSuccess; + __le32 countTxFrameFailures; + __le32 countTxFramesRetried; + __le32 countTxFramesMultiRetried; + __le32 countRxFrameDuplicates; + __le32 countAckFailures; + __le32 countRxMulticastFrames; + __le32 countRxCMACICVErrors; + __le32 countRxCMACReplays; + __le32 countRxMgmtCCMPReplays; + __le32 countRxBIPMICErrors; +}; + + +struct wsm_ampducounters_table { + u32 countTxAMPDUs; + u32 countTxMPDUsInAMPDUs; + u32 countTxOctetsInAMPDUs_l32; + u32 countTxOctetsInAMPDUs_h32; + u32 countRxAMPDUs; + u32 countRxMPDUsInAMPDUs; + u32 countRxOctetsInAMPDUs_l32; + u32 countRxOctetsInAMPDUs_h32; + u32 countRxDelimeterCRCErrorCount; + u32 countImplictBARFailures; + u32 countExplictBARFailures; +}; + +struct wsm_txpipe_counter { + u32 count1; + u32 count2; + u32 count3; + u32 count4; + u32 count5; + u32 count6; + u32 count7; + u32 count8; + u32 count9; + u32 counta; +}; + +struct wsm_backoff_counter { + u32 count0; + u32 count1; + u32 count2; + u32 count3; + u32 count4; + u32 count5; + u32 count6; + u32 count7; + u32 count8; + u32 count9; +}; +//add by yangfh for read/write fw registers +#define WSM_REG_RW_F BIT(0) //0:read, 1:write +#define WSM_REG_RET_F BIT(1) //results is valid. +#define WSM_REG_BK_F BIT(4) //operate in block mode. + +struct reg_data { + u32 reg_addr; + u32 reg_val; +}; + +typedef struct tag_wsm_reg_w { + u16 flag; + u16 data_size; + struct reg_data arg[16]; +} WSM_REG_W; + +typedef struct tag_wsm_reg_r { + u16 flag; + u16 data_size; + u32 arg[16]; +} WSM_REG_R; + +struct wsm_backoff_ctrl { + u32 enable; + u32 min; + u32 max; +}; +struct wsm_tala_para { + u32 para; + u32 thresh; +}; +static inline int wsm_get_counters_table(struct xradio_common *hw_priv, + struct wsm_counters_table *arg) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_COUNTERS_TABLE, + arg, sizeof(*arg), 0); +} + +static inline int wsm_get_ampducounters_table(struct xradio_common *hw_priv, + struct wsm_ampducounters_table *arg) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_AMPDUCOUNTERS_TABLE, + arg, sizeof(*arg), 0); +} + +static inline int wsm_get_txpipe_table(struct xradio_common *hw_priv, + struct wsm_txpipe_counter *arg) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_TXPIPE_TABLE, + arg, sizeof(*arg), 0); +} + +static inline int wsm_get_backoff_dbg(struct xradio_common *hw_priv, + struct wsm_backoff_counter *arg) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_BACKOFF_DBG, + arg, sizeof(*arg), 0); +} + +static inline int wsm_set_backoff_ctrl(struct xradio_common *hw_priv, + struct wsm_backoff_ctrl *arg) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_BACKOFF_CTRL, + arg, sizeof(*arg), 0); +} + +static inline int wsm_set_tala(struct xradio_common *hw_priv, + struct wsm_tala_para *arg) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TALA_PARA, + arg, sizeof(*arg), 0); +} +static inline int wsm_get_station_id(struct xradio_common *hw_priv, u8 *mac) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_DOT11_STATION_ID, mac, + ETH_ALEN, 0); +} + +struct wsm_rx_filter { + bool promiscuous; + bool bssid; + bool fcs; + bool probeResponder; + bool keepalive; +}; + +static inline int wsm_set_rx_filter(struct xradio_common *hw_priv, + const struct wsm_rx_filter *arg, + int if_id) +{ + __le32 val = 0; + if (arg->promiscuous) + val |= __cpu_to_le32(BIT(0)); + if (arg->bssid) + val |= __cpu_to_le32(BIT(1)); + if (arg->fcs) + val |= __cpu_to_le32(BIT(2)); + if (arg->probeResponder) + val |= __cpu_to_le32(BIT(3)); + if (arg->keepalive) + val |= __cpu_to_le32(BIT(4)); + return wsm_write_mib(hw_priv, WSM_MIB_ID_RX_FILTER, &val, sizeof(val), + if_id); +} + +int wsm_set_probe_responder(struct xradio_vif *priv, bool enable); +int wsm_set_keepalive_filter(struct xradio_vif *priv, bool enable); + +#define WSM_BEACON_FILTER_IE_HAS_CHANGED BIT(0) +#define WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT BIT(1) +#define WSM_BEACON_FILTER_IE_HAS_APPEARED BIT(2) + +struct wsm_beacon_filter_table_entry { + u8 ieId; + u8 actionFlags; + u8 oui[3]; + u8 matchData[3]; +} __packed; + +struct wsm_beacon_filter_table { + __le32 numOfIEs; + struct wsm_beacon_filter_table_entry entry[10]; +} __packed; + +static inline int wsm_set_beacon_filter_table(struct xradio_common *hw_priv, + struct wsm_beacon_filter_table *ft, + int if_id) +{ + size_t size = __le32_to_cpu(ft->numOfIEs) * + sizeof(struct wsm_beacon_filter_table_entry) + + sizeof(__le32); + + return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_TABLE, ft, size, + if_id); +} + +#define WSM_BEACON_FILTER_ENABLE BIT(0) /* Enable/disable beacon filtering */ +#define WSM_BEACON_FILTER_AUTO_ERP BIT(1) /* If 1 FW will handle ERP IE changes internally */ + +struct wsm_beacon_filter_control { + int enabled; + int bcn_count; +}; + +static inline int wsm_beacon_filter_control(struct xradio_common *hw_priv, + struct wsm_beacon_filter_control *arg, + int if_id) +{ + struct { + __le32 enabled; + __le32 bcn_count; + } val; + val.enabled = __cpu_to_le32(arg->enabled); + val.bcn_count = __cpu_to_le32(arg->bcn_count); + return wsm_write_mib(hw_priv, WSM_MIB_ID_BEACON_FILTER_ENABLE, &val, + sizeof(val), if_id); +} + +enum wsm_power_mode { + wsm_power_mode_active = 0, + wsm_power_mode_doze = 1, + wsm_power_mode_quiescent = 2, +}; + +struct wsm_operational_mode { + enum wsm_power_mode power_mode; + int disableMoreFlagUsage; + int performAntDiversity; +}; + +#ifdef CONFIG_XRADIO_DEBUGFS +extern u8 low_pwr_disable; +#endif + +static inline int wsm_set_operational_mode(struct xradio_common *hw_priv, + const struct wsm_operational_mode *arg, + int if_id) +{ + u32 val = arg->power_mode; + +#ifdef CONFIG_XRADIO_DEBUGFS //add by yangfh for disable low_power mode. + if(low_pwr_disable) + val = wsm_power_mode_active; +#endif + + if (arg->disableMoreFlagUsage) + val |= BIT(4); + if (arg->performAntDiversity) + val |= BIT(5); + return wsm_write_mib(hw_priv, WSM_MIB_ID_OPERATIONAL_POWER_MODE, &val, + sizeof(val), if_id); +} + +struct wsm_inactivity { + u8 max_inactivity; + u8 min_inactivity; +}; + +static inline int wsm_set_inactivity(struct xradio_common *hw_priv, + const struct wsm_inactivity *arg, + int if_id) +{ + struct { + u8 min_inactive; + u8 max_inactive; + u16 reserved; + } val; + + val.max_inactive = arg->max_inactivity; + val.min_inactive = arg->min_inactivity; + val.reserved = 0; + + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_INACTIVITY, &val, + sizeof(val), if_id); +} + +struct wsm_template_frame { + u8 frame_type; + u8 rate; + bool disable; + struct sk_buff *skb; +}; + +static inline int wsm_set_template_frame(struct xradio_common *hw_priv, + struct wsm_template_frame *arg, + int if_id) +{ + int ret; + u8 *p = skb_push(arg->skb, 4); + p[0] = arg->frame_type; + p[1] = arg->rate; + if (arg->disable) + ((u16 *) p)[1] = 0; + else + ((u16 *) p)[1] = __cpu_to_le16(arg->skb->len - 4); + ret = wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, p, + arg->skb->len, if_id); + skb_pull(arg->skb, 4); + return ret; +} + + +struct wsm_protected_mgmt_policy { + bool protectedMgmtEnable; + bool unprotectedMgmtFramesAllowed; + bool encryptionForAuthFrame; +}; + +static inline int +wsm_set_protected_mgmt_policy(struct xradio_common *hw_priv, + struct wsm_protected_mgmt_policy *arg, + int if_id) +{ + __le32 val = 0; + int ret; + if (arg->protectedMgmtEnable) + val |= __cpu_to_le32(BIT(0)); + if (arg->unprotectedMgmtFramesAllowed) + val |= __cpu_to_le32(BIT(1)); + if (arg->encryptionForAuthFrame) + val |= __cpu_to_le32(BIT(2)); + ret = wsm_write_mib(hw_priv, WSM_MIB_ID_PROTECTED_MGMT_POLICY, &val, + sizeof(val), if_id); + return ret; +} + +static inline int wsm_set_block_ack_policy(struct xradio_common *hw_priv, + u8 blockAckTxTidPolicy, + u8 blockAckRxTidPolicy, + int if_id) +{ + struct { + u8 blockAckTxTidPolicy; + u8 reserved1; + u8 blockAckRxTidPolicy; + u8 reserved2; + } val = { + .blockAckTxTidPolicy = blockAckTxTidPolicy, + .blockAckRxTidPolicy = blockAckRxTidPolicy, + }; + return wsm_write_mib(hw_priv, WSM_MIB_ID_BLOCK_ACK_POLICY, &val, + sizeof(val), if_id); +} + +struct wsm_association_mode { + u8 flags; /* WSM_ASSOCIATION_MODE_... */ + u8 preambleType; /* WSM_JOIN_PREAMBLE_... */ + u8 greenfieldMode; /* 1 for greenfield */ + u8 mpduStartSpacing; + __le32 basicRateSet; +}; + +static inline int wsm_set_association_mode(struct xradio_common *hw_priv, + struct wsm_association_mode *arg, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ASSOCIATION_MODE, arg, + sizeof(*arg), if_id); +} + +struct wsm_set_tx_rate_retry_policy_header { + u8 numTxRatePolicies; + u8 reserved[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy_policy { + u8 policyIndex; + u8 shortRetryCount; + u8 longRetryCount; + u8 policyFlags; + u8 rateRecoveryCount; + u8 reserved[3]; + __le32 rateCountIndices[3]; +} __packed; + +struct wsm_set_tx_rate_retry_policy { + struct wsm_set_tx_rate_retry_policy_header hdr; + struct wsm_set_tx_rate_retry_policy_policy tbl[8]; +} __packed; + +static inline int wsm_set_tx_rate_retry_policy(struct xradio_common *hw_priv, + struct wsm_set_tx_rate_retry_policy *arg, + int if_id) +{ + size_t size = sizeof(struct wsm_set_tx_rate_retry_policy_header) + + arg->hdr.numTxRatePolicies * + sizeof(struct wsm_set_tx_rate_retry_policy_policy); + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_TX_RATE_RETRY_POLICY, arg, + size, if_id); +} + +/* 4.32 SetEtherTypeDataFrameFilter */ +struct wsm_ether_type_filter_hdr { + u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_ether_type_filter { + u8 filterAction; /* WSM_FILTER_ACTION_XXX */ + u8 reserved; + __le16 etherType; /* Type of ethernet frame */ +} __packed; + +static inline int wsm_set_ether_type_filter(struct xradio_common *hw_priv, + struct wsm_ether_type_filter_hdr *arg, + int if_id) +{ + size_t size = sizeof(struct wsm_ether_type_filter_hdr) + + arg->nrFilters * sizeof(struct wsm_ether_type_filter); + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_ETHERTYPE_DATAFRAME_FILTER, + arg, size, if_id); +} + + +/* 4.33 SetUDPPortDataFrameFilter */ +struct wsm_udp_port_filter_hdr { + u8 nrFilters; /* Up to WSM_MAX_FILTER_ELEMENTS */ + u8 reserved[3]; +} __packed; + +struct wsm_udp_port_filter { + u8 filterAction; /* WSM_FILTER_ACTION_XXX */ + u8 portType; /* WSM_FILTER_PORT_TYPE_XXX */ + __le16 udpPort; /* Port number */ +} __packed; + +static inline int wsm_set_udp_port_filter(struct xradio_common *hw_priv, + struct wsm_udp_port_filter_hdr *arg, + int if_id) +{ + size_t size = sizeof(struct wsm_udp_port_filter_hdr) + + arg->nrFilters * sizeof(struct wsm_udp_port_filter); + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UDPPORT_DATAFRAME_FILTER, + arg, size, if_id); +} + +/* Undocumented MIBs: */ +/* 4.35 P2PDeviceInfo */ +#define D11_MAX_SSID_LEN (32) + +struct wsm_p2p_device_type { + __le16 categoryId; + u8 oui[4]; + __le16 subCategoryId; +} __packed; + +struct wsm_p2p_device_info { + struct wsm_p2p_device_type primaryDevice; + u8 reserved1[3]; + u8 devNameSize; + u8 localDevName[D11_MAX_SSID_LEN]; + u8 reserved2[3]; + u8 numSecDevSupported; + struct wsm_p2p_device_type secondaryDevices[0]; +} __packed; + +/* 4.36 SetWCDMABand - WO */ +struct wsm_cdma_band { + u8 WCDMA_Band; + u8 reserved[3]; +} __packed; + +/* 4.37 GroupTxSequenceCounter - RO */ +struct wsm_group_tx_seq { + __le32 bits_47_16; + __le16 bits_15_00; + __le16 reserved; +} __packed; + +/* 4.39 SetHtProtection - WO */ +#define WSM_DUAL_CTS_PROT_ENB (1 << 0) +#define WSM_NON_GREENFIELD_STA PRESENT(1 << 1) +#define WSM_HT_PROT_MODE__NO_PROT (0 << 2) +#define WSM_HT_PROT_MODE__NON_MEMBER (1 << 2) +#define WSM_HT_PROT_MODE__20_MHZ (2 << 2) +#define WSM_HT_PROT_MODE__NON_HT_MIXED (3 << 2) +#define WSM_LSIG_TXOP_PROT_FULL (1 << 4) +#define WSM_LARGE_L_LENGTH_PROT (1 << 5) + +struct wsm_ht_protection { + __le32 flags; +} __packed; + +/* 4.40 GPIO Command - R/W */ +#define WSM_GPIO_COMMAND_SETUP 0 +#define WSM_GPIO_COMMAND_READ 1 +#define WSM_GPIO_COMMAND_WRITE 2 +#define WSM_GPIO_COMMAND_RESET 3 +#define WSM_GPIO_ALL_PINS 0xFF + +struct wsm_gpio_command { + u8 GPIO_Command; + u8 pin; + __le16 config; +} __packed; + +/* 4.41 TSFCounter - RO */ +struct wsm_tsf_counter { + __le64 TSF_Counter; +} __packed; + +/* 4.43 Keep alive period */ +struct wsm_keep_alive_period { + __le16 keepAlivePeriod; + u8 reserved[2]; +} __packed; + +static inline int wsm_keep_alive_period(struct xradio_common *hw_priv, + int period, int if_id) +{ + struct wsm_keep_alive_period arg = { + .keepAlivePeriod = __cpu_to_le16(period), + }; + return wsm_write_mib(hw_priv, WSM_MIB_ID_KEEP_ALIVE_PERIOD, + &arg, sizeof(arg), if_id); +}; + +/* BSSID filtering */ +struct wsm_set_bssid_filtering { + u8 filter; + u8 reserved[3]; +} __packed; + +static inline int wsm_set_bssid_filtering(struct xradio_common *hw_priv, + bool enabled, int if_id) +{ + struct wsm_set_bssid_filtering arg = { + .filter = !enabled, + }; + return wsm_write_mib(hw_priv, WSM_MIB_ID_DISABLE_BSSID_FILTER, + &arg, sizeof(arg), if_id); +} + +/* Multicat filtering - 4.5 */ +struct wsm_multicast_filter { + __le32 enable; + __le32 numOfAddresses; + u8 macAddress[WSM_MAX_GRP_ADDRTABLE_ENTRIES][ETH_ALEN]; +} __packed; + +/* Mac Addr Filter Info */ +struct wsm_mac_addr_info { + u8 filter_mode; + u8 address_mode; + u8 MacAddr[6]; +} __packed; + +/* Mac Addr Filter */ +struct wsm_mac_addr_filter { + u8 numfilter; + u8 action_mode; + u8 Reserved[2]; + struct wsm_mac_addr_info macaddrfilter[0]; +} __packed; + +/* Broadcast Addr Filter */ +struct wsm_broadcast_addr_filter { + u8 action_mode; + u8 nummacaddr; + u8 filter_mode; + u8 address_mode; + u8 MacAddr[6]; +} __packed; + +static inline int wsm_set_multicast_filter(struct xradio_common *hw_priv, + struct wsm_multicast_filter *fp, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, + fp, sizeof(*fp), if_id); +} + +/* ARP IPv4 filtering - 4.10 */ +struct wsm_arp_ipv4_filter { + __le32 enable; + __be32 ipv4Address[WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES]; +} __packed; + +#ifdef IPV6_FILTERING +/* NDP IPv6 filtering */ +struct wsm_ndp_ipv6_filter { + __le32 enable; + struct in6_addr ipv6Address[WSM_MAX_NDP_IP_ADDRTABLE_ENTRIES]; +} __packed; +/* IPV6 Addr Filter Info */ +struct wsm_ip6_addr_info { + u8 filter_mode; + u8 address_mode; + u8 Reserved[2]; + u8 ipv6[16]; +}; + +/* IPV6 Addr Filter */ +struct wsm_ipv6_filter { + u8 numfilter; + u8 action_mode; + u8 Reserved[2]; + struct wsm_ip6_addr_info ipv6filter[0]; +} __packed; +#endif /*IPV6_FILTERING*/ + +static inline int wsm_set_arp_ipv4_filter(struct xradio_common *hw_priv, + struct wsm_arp_ipv4_filter *fp, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_ARP_IP_ADDRESSES_TABLE, + fp, sizeof(*fp), if_id); +} + +#ifdef IPV6_FILTERING +static inline int wsm_set_ndp_ipv6_filter(struct xradio_common *priv, + struct wsm_ndp_ipv6_filter *fp, + int if_id) +{ + return wsm_write_mib(priv, WSM_MIB_ID_NS_IP_ADDRESSES_TABLE, + fp, sizeof(*fp), if_id); +} +#endif /*IPV6_FILTERING*/ + +/* P2P Power Save Mode Info - 4.31 */ +struct wsm_p2p_ps_modeinfo { + u8 oppPsCTWindow; + u8 count; + u8 reserved; + u8 dtimCount; + __le32 duration; + __le32 interval; + __le32 startTime; +} __packed; + +static inline int wsm_set_p2p_ps_modeinfo(struct xradio_common *hw_priv, + struct wsm_p2p_ps_modeinfo *mi, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi), if_id); +} + +static inline int wsm_get_p2p_ps_modeinfo(struct xradio_common *hw_priv, + struct wsm_p2p_ps_modeinfo *mi) +{ + return wsm_read_mib(hw_priv, WSM_MIB_ID_P2P_PS_MODE_INFO, + mi, sizeof(*mi), 0); +} + +/* UseMultiTxConfMessage */ + +static inline int wsm_use_multi_tx_conf(struct xradio_common *hw_priv, + bool enabled, int if_id) +{ + __le32 arg = enabled ? __cpu_to_le32(1) : 0; + + return wsm_write_mib(hw_priv, WSM_MIB_USE_MULTI_TX_CONF, + &arg, sizeof(arg), if_id); +} + + +/* 4.26 SetUpasdInformation */ +struct wsm_uapsd_info { + __le16 uapsdFlags; + __le16 minAutoTriggerInterval; + __le16 maxAutoTriggerInterval; + __le16 autoTriggerStep; +}; + +static inline int wsm_set_uapsd_info(struct xradio_common *hw_priv, + struct wsm_uapsd_info *arg, + int if_id) +{ + /* TODO:COMBO:UAPSD will be supported only on one interface */ + return wsm_write_mib(hw_priv, WSM_MIB_ID_SET_UAPSD_INFORMATION, + arg, sizeof(*arg), if_id); +} + +/* 4.22 OverrideInternalTxRate */ +struct wsm_override_internal_txrate { + u8 internalTxRate; + u8 nonErpInternalTxRate; + u8 reserved[2]; +} __packed; + +static inline int +wsm_set_override_internal_txrate(struct xradio_common *hw_priv, + struct wsm_override_internal_txrate *arg, + int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE, + arg, sizeof(*arg), if_id); +} +#ifdef MCAST_FWDING +/* 4.51 SetForwardingOffload */ +struct wsm_forwarding_offload { + u8 fwenable; + u8 flags; + u8 reserved[2]; +} __packed; + +static inline int wsm_set_forwarding_offlad(struct xradio_common *hw_priv, + struct wsm_forwarding_offload *arg,int if_id) +{ + return wsm_write_mib(hw_priv, WSM_MIB_ID_FORWARDING_OFFLOAD, + arg, sizeof(*arg),if_id); +} + +#endif +/* ******************************************************************** */ +/* WSM TX port control */ + +void wsm_lock_tx(struct xradio_common *hw_priv); +void wsm_vif_lock_tx(struct xradio_vif *priv); +void wsm_lock_tx_async(struct xradio_common *hw_priv); +bool wsm_flush_tx(struct xradio_common *hw_priv); +bool wsm_vif_flush_tx(struct xradio_vif *priv); +void wsm_unlock_tx(struct xradio_common *hw_priv); + +/* ******************************************************************** */ +/* WSM / BH API */ + +int wsm_handle_exception(struct xradio_common *hw_priv, u8 * data, size_t len); +int wsm_handle_rx(struct xradio_common *hw_priv, int id, struct wsm_hdr *wsm, + struct sk_buff **skb_p); +void wms_send_deauth_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv); +void wms_send_disassoc_to_self(struct xradio_common *hw_priv, struct xradio_vif *priv); + +/* ******************************************************************** */ +/* wsm_buf API */ + +struct wsm_buf { + u8 *begin; + u8 *data; + u8 *end; +}; + +void wsm_buf_init(struct wsm_buf *buf); +void wsm_buf_deinit(struct wsm_buf *buf); + +/* ******************************************************************** */ +/* wsm_cmd API */ + +struct wsm_cmd { + spinlock_t lock; + int done; + u8 *ptr; + size_t len; + void *arg; + int ret; + u16 cmd; +}; + +/* ******************************************************************** */ +/* WSM TX buffer access */ + +int wsm_get_tx(struct xradio_common *hw_priv, u8 **data, + size_t *tx_len, int *burst, int *vif_selected); +void wsm_txed(struct xradio_common *hw_priv, u8 *data); + +/* ******************************************************************** */ +/* Queue mapping: WSM <---> linux */ +/* Linux: VO VI BE BK */ +/* WSM: BE BK VI VO */ + +static inline u8 wsm_queue_id_to_linux(u8 queueId) +{ + static const u8 queue_mapping[] = { + 2, 3, 1, 0 + }; + return queue_mapping[queueId]; +} + +static inline u8 wsm_queue_id_to_wsm(u8 queueId) +{ + static const u8 queue_mapping[] = { + 3, 2, 0, 1 + }; + return queue_mapping[queueId]; +} + +#endif /* XRADIO_HWIO_H_INCLUDED */ diff --git a/drivers/net/wireless/xradio/xr_version.h b/drivers/net/wireless/xradio/xr_version.h new file mode 100644 index 0000000..9e99992 --- /dev/null +++ b/drivers/net/wireless/xradio/xr_version.h @@ -0,0 +1,18 @@ +/* + * sta interfaces for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef XR_VERSION_H +#define XR_VERSION_H + +#define XRADIO_VERSION "L34M.01.08.0002" +#define XRADIO_MAIN_VER 1 +#define XRADIO_SUB_VER 8 + +#endif diff --git a/drivers/net/wireless/xradio/xradio.h b/drivers/net/wireless/xradio/xradio.h new file mode 100644 index 0000000..32632a6 --- /dev/null +++ b/drivers/net/wireless/xradio/xradio.h @@ -0,0 +1,688 @@ +/* + * Common define of private data for XRadio drivers + * + * Copyright (c) 2013, XRadio + * Author: XRadio + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef XRADIO_H +#define XRADIO_H + +#include +#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 "common.h" +#include "queue.h" +#include "wsm.h" +#include "scan.h" +#include "txrx.h" +#include "ht.h" +#include "pm.h" +#include "fwio.h" +#ifdef CONFIG_XRADIO_TESTMODE +#include "nl80211_testmode_msg_copy.h" +#endif /*CONFIG_XRADIO_TESTMODE*/ + +/* #define ROC_DEBUG */ +/* hidden ssid is only supported when separate probe resp IE + configuration is supported */ +#ifdef PROBE_RESP_EXTRA_IE +#define HIDDEN_SSID 1 +#endif + +#define XRADIO_MAX_CTRL_FRAME_LEN (0x1000) + +#define MAX_STA_IN_AP_MODE (14) +#define WLAN_LINK_ID_MAX (MAX_STA_IN_AP_MODE + 3) + +#define XRADIO_MAX_STA_IN_AP_MODE (5) +#define XRADIO_MAX_REQUEUE_ATTEMPTS (5) +#define XRADIO_LINK_ID_UNMAPPED (15) +#define XRADIO_MAX_TID (8) + +#define XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F) +#define XRADIO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID (0x3F) +#define XRADIO_RX_BLOCK_ACK_ENABLED_FOR_BE_TID \ + (XRADIO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID & 0x01) +#define XRADIO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0) +#define XRADIO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID (0) + +#define XRADIO_BLOCK_ACK_CNT (30) +#define XRADIO_BLOCK_ACK_THLD (800) +#define XRADIO_BLOCK_ACK_HIST (3) +#define XRADIO_BLOCK_ACK_INTERVAL (1 * HZ / XRADIO_BLOCK_ACK_HIST) +#define XRWL_ALL_IFS (-1) + +#ifdef ROAM_OFFLOAD +#define XRADIO_SCAN_TYPE_ACTIVE 0x1000 +#define XRADIO_SCAN_BAND_5G 0x2000 +#endif /*ROAM_OFFLOAD*/ + +#define IEEE80211_FCTL_WEP 0x4000 +#define IEEE80211_QOS_DATAGRP 0x0080 +#ifdef CONFIG_XRADIO_TESTMODE +#define XRADIO_SCAN_MEASUREMENT_PASSIVE (0) +#define XRADIO_SCAN_MEASUREMENT_ACTIVE (1) +#endif + +#ifdef MCAST_FWDING +#define WSM_MAX_BUF 30 +#endif + +#define MAX_RATES_STAGE 8 // +#define MAX_RATES_RETRY 15 + +#define XRADIO_WORKQUEUE "xradio_wq" +#define WIFI_CONF_PATH "/data/xr_wifi.conf" + +// +extern char *drv_version; +extern char *drv_buildtime; +#define DRV_VERSION drv_version +#define DRV_BUILDTIME drv_buildtime + +/* extern */ struct sbus_ops; +/* extern */ struct task_struct; +/* extern */ struct xradio_debug_priv; +/* extern */ struct xradio_debug_common; +/* extern */ struct firmware; + +/* Please keep order */ +enum xradio_join_status { + XRADIO_JOIN_STATUS_PASSIVE = 0, + XRADIO_JOIN_STATUS_MONITOR, + XRADIO_JOIN_STATUS_STA, + XRADIO_JOIN_STATUS_AP, +}; + +enum xradio_link_status { + XRADIO_LINK_OFF, + XRADIO_LINK_RESERVE, + XRADIO_LINK_SOFT, + XRADIO_LINK_HARD, +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + XRADIO_LINK_RESET, + XRADIO_LINK_RESET_REMAP, +#endif +}; + +enum xradio_bss_loss_status { + XRADIO_BSS_LOSS_NONE, + XRADIO_BSS_LOSS_CHECKING, + XRADIO_BSS_LOSS_CONFIRMING, + XRADIO_BSS_LOSS_CONFIRMED, +}; + +struct xradio_link_entry { + unsigned long timestamp; + enum xradio_link_status status; +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + enum xradio_link_status prev_status; +#endif + u8 mac[ETH_ALEN]; + u8 buffered[XRADIO_MAX_TID]; + struct sk_buff_head rx_queue; +}; + +#if defined(ROAM_OFFLOAD) || defined(CONFIG_XRADIO_TESTMODE) +struct xradio_testframe { + u8 len; + u8 *data; +}; +#endif +#ifdef CONFIG_XRADIO_TESTMODE +struct advance_scan_elems { + u8 scanMode; + u16 duration; +}; +/** + * xradio_tsm_info - Keeps information about ongoing TSM collection + * @ac: Access category for which metrics to be collected + * @use_rx_roaming: Use received voice packets to compute roam delay + * @sta_associated: Set to 1 after association + * @sta_roamed: Set to 1 after successful roaming + * @roam_delay: Roam delay + * @rx_timestamp_vo: Timestamp of received voice packet + * @txconf_timestamp_vo: Timestamp of received tx confirmation for + * successfully transmitted VO packet + * @sum_pkt_q_delay: Sum of packet queue delay + * @sum_media_delay: Sum of media delay + * + */ +struct xradio_tsm_info { + u8 ac; + u8 use_rx_roaming; + u8 sta_associated; + u8 sta_roamed; + u16 roam_delay; + u32 rx_timestamp_vo; + u32 txconf_timestamp_vo; + u32 sum_pkt_q_delay; + u32 sum_media_delay; +}; + +/** + * xradio_start_stop_tsm - To start or stop collecting TSM metrics in + * xradio driver + * @start: To start or stop collecting TSM metrics + * @up: up for which metrics to be collected + * @packetization_delay: Packetization delay for this TID + * + */ +struct xradio_start_stop_tsm { + u8 start; /*1: To start, 0: To stop*/ + u8 up; + u16 packetization_delay; +}; + +#endif /* CONFIG_XRADIO_TESTMODE */ +struct xradio_common { + struct xradio_debug_common *debug; + struct xradio_queue tx_queue[AC_QUEUE_NUM]; + struct xradio_queue_stats tx_queue_stats; + + struct ieee80211_hw *hw; + struct mac_address addresses[XRWL_MAX_VIFS]; + + /*Will be a pointer to a list of VIFs - Dynamically allocated */ + struct ieee80211_vif *vif_list[XRWL_MAX_VIFS]; + atomic_t num_vifs; + spinlock_t vif_list_lock; + u32 if_id_slot; + struct device *pdev; + struct workqueue_struct *workqueue; + + struct mutex conf_mutex; + + const struct sbus_ops *sbus_ops; + struct sbus_priv *sbus_priv; + int driver_ready; + + /* HW/FW type (HIF_...) */ + int hw_type; + int hw_revision; + int fw_revision; + + /* firmware/hardware info */ + unsigned int tx_hdr_len; + + /* Radio data */ + int output_power; + int noise; + + /* calibration, output power limit and rssi<->dBm conversation data */ + + /* BBP/MAC state */ +#ifdef USE_VFS_FIRMWARE + const struct xr_file *sdd; +#else + const struct firmware *sdd; +#endif + struct ieee80211_rate *rates; + struct ieee80211_rate *mcs_rates; + u8 mac_addr[ETH_ALEN]; + /*TODO:COMBO: To be made per VIFF after mac80211 support */ + struct ieee80211_channel *channel; + int channel_switch_in_progress; + wait_queue_head_t channel_switch_done; + u8 channel_changed; //add by yangfh 2015-5-15 16:57:38. + u8 long_frame_max_tx_count; + u8 short_frame_max_tx_count; + /* TODO:COMBO: According to Hong aggregation will happen per VIFF. + * Keeping in common structure for the time being. Will be moved to VIFF + * after the mechanism is clear */ + u8 ba_tid_mask; + int ba_acc; /*TODO: Same as above */ + int ba_cnt; /*TODO: Same as above */ + int ba_cnt_rx; /*TODO: Same as above */ + int ba_acc_rx; /*TODO: Same as above */ + int ba_hist; /*TODO: Same as above */ + struct timer_list ba_timer;/*TODO: Same as above */ + spinlock_t ba_lock; /*TODO: Same as above */ + bool ba_ena; /*TODO: Same as above */ + struct work_struct ba_work; /*TODO: Same as above */ + struct xradio_pm_state pm_state; + bool is_BT_Present; + bool is_go_thru_go_neg; + u8 conf_listen_interval; + + /* BH */ + atomic_t bh_rx; + atomic_t bh_tx; + atomic_t bh_term; + atomic_t bh_suspend; + struct task_struct *bh_thread; + int bh_error; +#ifdef BH_USE_SEMAPHORE + struct semaphore bh_sem; + atomic_t bh_wk; +#else + wait_queue_head_t bh_wq; +#endif + wait_queue_head_t bh_evt_wq; + + + int buf_id_tx; /* byte */ + int buf_id_rx; /* byte */ + int wsm_rx_seq; /* byte */ + int wsm_tx_seq; /* byte */ + int hw_bufs_used; + int hw_bufs_used_vif[XRWL_MAX_VIFS]; + struct sk_buff *skb_cache; + struct sk_buff *skb_reserved; + int skb_resv_len; + bool powersave_enabled; + bool device_can_sleep; + /* Keep xradio awake (WUP = 1) 1 second after each scan to avoid + * FW issue with sleeping/waking up. */ + atomic_t recent_scan; + long connet_time[XRWL_MAX_VIFS]; +#ifdef CONFIG_XRADIO_SUSPEND_POWER_OFF + atomic_t suspend_state; +#endif +#ifdef HW_RESTART + bool hw_restart; + struct work_struct hw_restart_work; +#endif + + /* WSM */ + struct wsm_caps wsm_caps; + struct mutex wsm_cmd_mux; + struct wsm_buf wsm_cmd_buf; + struct wsm_cmd wsm_cmd; + wait_queue_head_t wsm_cmd_wq; + wait_queue_head_t wsm_startup_done; + struct wsm_cbc wsm_cbc; + struct semaphore tx_lock_sem; + atomic_t tx_lock; + u32 pending_frame_id; +#ifdef CONFIG_XRADIO_TESTMODE + /* Device Power Range */ + struct wsm_tx_power_range txPowerRange[2]; + /* Advance Scan */ + struct advance_scan_elems advanceScanElems; + bool enable_advance_scan; + struct delayed_work advance_scan_timeout; +#endif /* CONFIG_XRADIO_TESTMODE */ + + /* WSM debug */ + int wsm_enable_wsm_dumps; + u32 wsm_dump_max_size; + u32 query_packetID; + atomic_t query_cnt; + struct work_struct query_work; /* for query packet */ + + /* Scan status */ + struct xradio_scan scan; + + /* TX/RX */ + unsigned long rx_timestamp; + + /* WSM events */ + spinlock_t event_queue_lock; + struct list_head event_queue; + struct work_struct event_handler; + + /* TX rate policy cache */ + struct tx_policy_cache tx_policy_cache; + struct work_struct tx_policy_upload_work; + atomic_t upload_count; + + /* cryptographic engine information */ + + /* bit field of glowing LEDs */ + u16 softled_state; + + /* statistics */ + struct ieee80211_low_level_stats stats; + + struct xradio_ht_info ht_info; + int tx_burst_idx; + + struct ieee80211_iface_limit if_limits1[2]; + struct ieee80211_iface_limit if_limits2[2]; + struct ieee80211_iface_limit if_limits3[2]; + struct ieee80211_iface_combination if_combs[3]; + + struct mutex wsm_oper_lock; + struct delayed_work rem_chan_timeout; + atomic_t remain_on_channel; + int roc_if_id; + u64 roc_cookie; + wait_queue_head_t offchannel_wq; + u16 offchannel_done; + u16 prev_channel; + int if_id_selected; + u32 key_map; + struct wsm_add_key keys[WSM_KEY_MAX_INDEX + 1]; +#ifdef MCAST_FWDING + struct wsm_buf wsm_release_buf[WSM_MAX_BUF]; + u8 buf_released; +#endif +#ifdef ROAM_OFFLOAD + u8 auto_scanning; + u8 frame_rcvd; + u8 num_scanchannels; + u8 num_2g_channels; + u8 num_5g_channels; + struct wsm_scan_ch scan_channels[48]; + struct sk_buff *beacon; + struct sk_buff *beacon_bkp; + struct xradio_testframe testframe; +#endif /*ROAM_OFFLOAD*/ +#ifdef CONFIG_XRADIO_TESTMODE + struct xradio_testframe test_frame; + struct xr_tsm_stats tsm_stats; + struct xradio_tsm_info tsm_info; + spinlock_t tsm_lock; + struct xradio_start_stop_tsm start_stop_tsm; +#endif /* CONFIG_XRADIO_TESTMODE */ + u8 connected_sta_cnt; + u16 vif0_throttle; + u16 vif1_throttle; +}; + +/* Virtual Interface State. One copy per VIF */ +struct xradio_vif { + atomic_t enabled; + spinlock_t vif_lock; + int if_id; + /*TODO: Split into Common and VIF parts */ + struct xradio_debug_priv *debug; + /* BBP/MAC state */ + u8 bssid[ETH_ALEN]; + struct wsm_edca_params edca; + struct wsm_tx_queue_params tx_queue_params; + struct wsm_association_mode association_mode; + struct wsm_set_bss_params bss_params; + struct wsm_set_pm powersave_mode; + struct wsm_set_pm firmware_ps_mode; + int power_set_true; + int user_power_set_true; + u8 user_pm_mode; + int cqm_rssi_thold; + unsigned cqm_rssi_hyst; + unsigned cqm_tx_failure_thold; + unsigned cqm_tx_failure_count; + bool cqm_use_rssi; + int cqm_link_loss_count; + int cqm_beacon_loss_count; + int mode; + bool enable_beacon; + int beacon_int; + size_t ssid_length; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +#ifdef HIDDEN_SSID + bool hidden_ssid; +#endif + bool listening; + struct wsm_rx_filter rx_filter; + struct wsm_beacon_filter_table bf_table; + struct wsm_beacon_filter_control bf_control; + struct wsm_multicast_filter multicast_filter; + bool has_multicast_subscription; + struct wsm_broadcast_addr_filter broadcast_filter; + bool disable_beacon_filter; + struct wsm_arp_ipv4_filter filter4; +#ifdef IPV6_FILTERING + struct wsm_ndp_ipv6_filter filter6; +#endif /*IPV6_FILTERING*/ + struct work_struct update_filtering_work; + struct work_struct set_beacon_wakeup_period_work; + struct xradio_pm_state_vif pm_state_vif; + /*TODO: Add support in mac80211 for psmode info per VIF */ + struct wsm_p2p_ps_modeinfo p2p_ps_modeinfo; + struct wsm_uapsd_info uapsd_info; + bool setbssparams_done; + u32 listen_interval; + u32 erp_info; + bool powersave_enabled; + + /* WSM Join */ + enum xradio_join_status join_status; + u8 join_bssid[ETH_ALEN]; + struct work_struct join_work; + struct delayed_work join_timeout; + struct work_struct unjoin_work; + struct work_struct offchannel_work; + int join_dtim_period; + atomic_t delayed_unjoin; + + /* Security */ + s8 wep_default_key_id; + struct work_struct wep_key_work; + unsigned long rx_timestamp; + u32 cipherType; + + + /* AP powersave */ + u32 link_id_map; + u32 max_sta_ap_mode; + u32 link_id_after_dtim; + u32 link_id_uapsd; + u32 link_id_max; + u32 wsm_key_max_idx; + struct xradio_link_entry link_id_db[MAX_STA_IN_AP_MODE]; + struct work_struct link_id_work; + struct delayed_work link_id_gc_work; + u32 sta_asleep_mask; + u32 pspoll_mask; + bool aid0_bit_set; + spinlock_t ps_state_lock; + bool buffered_multicasts; + bool tx_multicast; + u8 last_tim[8]; //for softap dtim, add by yangfh + struct work_struct set_tim_work; + struct delayed_work set_cts_work; + struct work_struct multicast_start_work; + struct work_struct multicast_stop_work; + struct timer_list mcast_timeout; + + /* CQM Implementation */ + struct delayed_work bss_loss_work; + struct delayed_work connection_loss_work; + struct work_struct tx_failure_work; + int delayed_link_loss; + spinlock_t bss_loss_lock; + int bss_loss_status; + int bss_loss_confirm_id; + + struct ieee80211_vif *vif; + struct xradio_common *hw_priv; + struct ieee80211_hw *hw; + + /* ROC implementation */ + struct delayed_work pending_offchanneltx_work; +#if defined(CONFIG_XRADIO_USE_EXTENSIONS) + /* Workaround for WFD testcase 6.1.10*/ + struct work_struct linkid_reset_work; + u8 action_frame_sa[ETH_ALEN]; + u8 action_linkid; +#endif + bool htcap; +#ifdef AP_HT_CAP_UPDATE + u16 ht_info; + struct work_struct ht_info_update_work; +#endif + +#ifdef AP_HT_COMPAT_FIX + u16 ht_compat_cnt; + u16 ht_compat_det; +#endif +}; +struct xradio_sta_priv { + int link_id; + struct xradio_vif *priv; +}; +enum xradio_data_filterid { + IPV4ADDR_FILTER_ID = 0, +#ifdef IPV6_FILTERING + IPV6ADDR_FILTER_ID, +#endif /*IPV6_FILTERING*/ +}; + +#ifdef IPV6_FILTERING +/* IPV6 host addr info */ +struct ipv6_addr_info { + u8 filter_mode; + u8 address_mode; + u16 ipv6[8]; +}; +#endif /*IPV6_FILTERING*/ + +/* Datastructure for LLC-SNAP HDR */ +#define P80211_OUI_LEN 3 +struct ieee80211_snap_hdr { + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ +} __packed; + + +#ifdef TES_P2P_0002_ROC_RESTART +extern s32 TES_P2P_0002_roc_dur; +extern s32 TES_P2P_0002_roc_sec; +extern s32 TES_P2P_0002_roc_usec; +extern u32 TES_P2P_0002_packet_id; +extern u32 TES_P2P_0002_state; + +#define TES_P2P_0002_STATE_IDLE 0x00 +#define TES_P2P_0002_STATE_SEND_RESP 0x01 +#define TES_P2P_0002_STATE_GET_PKTID 0x02 +#endif + +/* debug.h must be here because refer to struct xradio_vif and + struct xradio_common.*/ +#include "debug.h" + +/******************************************************* + interfaces for operations of vif. +********************************************************/ +static inline +struct xradio_common *xrwl_vifpriv_to_hwpriv(struct xradio_vif *priv) +{ + return priv->hw_priv; +} +static inline +struct xradio_vif *xrwl_get_vif_from_ieee80211(struct ieee80211_vif *vif) +{ + return (struct xradio_vif *)vif->drv_priv; +} + +static inline +struct xradio_vif *xrwl_hwpriv_to_vifpriv(struct xradio_common *hw_priv, + int if_id) +{ + struct xradio_vif *vif; + + if (SYS_WARN((-1 == if_id) || (if_id > XRWL_MAX_VIFS))) + return NULL; + /* TODO:COMBO: During scanning frames can be received + * on interface ID 3 */ + spin_lock(&hw_priv->vif_list_lock); + if (!hw_priv->vif_list[if_id]) { + spin_unlock(&hw_priv->vif_list_lock); + return NULL; + } + + vif = xrwl_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); + SYS_WARN(!vif); + if (vif) + spin_lock(&vif->vif_lock); + spin_unlock(&hw_priv->vif_list_lock); + return vif; +} + +static inline +struct xradio_vif *__xrwl_hwpriv_to_vifpriv(struct xradio_common *hw_priv, + int if_id) +{ + SYS_WARN((-1 == if_id) || (if_id > XRWL_MAX_VIFS)); + /* TODO:COMBO: During scanning frames can be received + * on interface ID 3 */ + if (!hw_priv->vif_list[if_id]) { + return NULL; + } + + return xrwl_get_vif_from_ieee80211(hw_priv->vif_list[if_id]); +} + +static inline +struct xradio_vif *xrwl_get_activevif(struct xradio_common *hw_priv) +{ + return xrwl_hwpriv_to_vifpriv(hw_priv, ffs(hw_priv->if_id_slot)-1); +} + +static inline bool is_hardware_xradio(struct xradio_common *hw_priv) +{ + return (hw_priv->hw_revision == XR819_HW_REV0); +} + +static inline int xrwl_get_nr_hw_ifaces(struct xradio_common *hw_priv) +{ + switch(hw_priv->hw_revision) { + case XR819_HW_REV0: + default: + return 1; + } +} + +#define xradio_for_each_vif(_hw_priv, _priv, _i) \ + for( \ + _i = 0; \ + (_i < XRWL_MAX_VIFS) \ + && ((_priv = _hw_priv->vif_list[_i] ? \ + xrwl_get_vif_from_ieee80211(_hw_priv->vif_list[_i]) : NULL),1); \ + _i++ \ + ) + +/******************************************************* + interfaces for operations of queue. +********************************************************/ +static inline void xradio_tx_queues_lock(struct xradio_common *hw_priv) +{ + int i; + for (i = 0; i < 4; ++i) + xradio_queue_lock(&hw_priv->tx_queue[i]); +} + +static inline void xradio_tx_queues_unlock(struct xradio_common *hw_priv) +{ + int i; + for (i = 0; i < 4; ++i) + xradio_queue_unlock(&hw_priv->tx_queue[i]); +} + +#endif /* XRADIO_H */