project:cfg:BoardConfig_IPC: Added fastboot BoardConfig file and firmware post-scripts, distinguishing between the BoardConfigs for Luckfox Pico Pro and Luckfox Pico Max. project:app: Added fastboot_client and rk_smart_door for quick boot applications; updated rkipc app to adapt to the latest media library. media:samples: Added more usage examples. media:rockit: Fixed bugs; removed support for retrieving data frames from VPSS. media:isp: Updated rkaiq library and related tools to support connection to RKISP_Tuner. sysdrv:Makefile: Added support for compiling drv_ko on Luckfox Pico Ultra W using Ubuntu; added support for custom root filesystem. sysdrv:tools:board: Updated Buildroot optional mirror sources, updated some software versions, and stored device tree files and configuration files that undergo multiple modifications for U-Boot and kernel separately. sysdrv:source:mcu: Used RISC-V MCU SDK with RT-Thread system, mainly for initializing camera AE during quick boot. sysdrv:source:uboot: Added support for fastboot; added high baud rate DDR bin for serial firmware upgrades. sysdrv:source:kernel: Upgraded to version 5.10.160; increased NPU frequency for RV1106G3; added support for fastboot. Signed-off-by: luckfox-eng29 <eng29@luckfox.com>
2466 lines
71 KiB
C
2466 lines
71 KiB
C
/*
|
|
* mac80211 STA and AP API for mac80211 altobeam APOLLO drivers
|
|
*
|
|
*
|
|
* Copyright (c) 2016, altobeam
|
|
* Author:
|
|
*
|
|
* Based on 2010, ST-Ericsson
|
|
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>
|
|
*
|
|
* 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 "apollo.h"
|
|
#include "sta.h"
|
|
#include "ap.h"
|
|
#include "bh.h"
|
|
#include "net/atbm_mac80211.h"
|
|
#include "mac80211/ieee80211_i.h"
|
|
|
|
|
|
#if defined(CONFIG_ATBM_APOLLO_STA_DEBUG)
|
|
#define ap_printk(...) atbm_printk_always(__VA_ARGS__)
|
|
#else
|
|
#define ap_printk(...)
|
|
#endif
|
|
extern int start_choff;
|
|
#define HT_INFO_OFFSET 4
|
|
#define HT_INFO_MASK 0x0011
|
|
#define HT_INFO_IE_LEN 22
|
|
#define ATBM_APOLLO_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
|
|
|
|
#define ATBM_APOLLO_ENABLE_ARP_FILTER_OFFLOAD 3
|
|
/*For Samsung, it is defined as 4*/
|
|
#define ATBM_APOLLO_KEEP_ALIVE_PERIOD (4)
|
|
|
|
#ifndef ERP_INFO_BYTE_OFFSET
|
|
#define ERP_INFO_BYTE_OFFSET 2
|
|
#endif
|
|
|
|
#ifdef IPV6_FILTERING
|
|
#define ATBM_APOLLO_ENABLE_NDP_FILTER_OFFLOAD 3
|
|
#endif /*IPV6_FILTERING*/
|
|
|
|
static int atbm_upload_beacon(struct atbm_vif *priv);
|
|
#ifdef ATBM_PROBE_RESP_EXTRA_IE
|
|
static int atbm_upload_proberesp(struct atbm_vif *priv);
|
|
#endif
|
|
#ifdef CONFIG_ATBM_BT_COMB
|
|
static int atbm_upload_pspoll(struct atbm_vif *priv);
|
|
static int atbm_upload_null(struct atbm_vif *priv);
|
|
#endif
|
|
static int atbm_upload_qosnull(struct atbm_vif *priv);
|
|
static int atbm_start_ap(struct atbm_vif *priv);
|
|
static int atbm_stop_ap(struct atbm_vif *priv);
|
|
|
|
static int atbm_update_beaconing(struct atbm_vif *priv);
|
|
/*
|
|
static int atbm_enable_beaconing(struct atbm_vif *priv,
|
|
bool enable);
|
|
*/
|
|
static void __atbm_sta_notify(struct atbm_vif *priv,
|
|
enum sta_notify_cmd notify_cmd,
|
|
int link_id);
|
|
|
|
/* ******************************************************************** */
|
|
/* AP API */
|
|
static int __atbm_find_link_id(struct atbm_vif *priv, const u8 *mac)
|
|
{
|
|
int i, ret = 0;
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
|
|
for (i = 0; i < ATBMWIFI_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 atbm_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta)
|
|
{
|
|
struct atbm_sta_priv *sta_priv =
|
|
(struct atbm_sta_priv *)&sta->drv_priv;
|
|
struct atbm_vif *priv = ABwifi_get_vif_from_ieee80211(vif);
|
|
struct atbm_link_entry *entry;
|
|
struct sk_buff *skb;
|
|
struct atbm_common *hw_priv = hw->priv;
|
|
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return -ENOENT;
|
|
}
|
|
#ifdef P2P_MULTIVIF
|
|
WARN_ON(priv->if_id == ATBM_WIFI_GENERIC_IF_ID);
|
|
#endif
|
|
|
|
if (priv->mode != NL80211_IFTYPE_AP)
|
|
return 0;
|
|
#ifdef AP_MODE_SUPPORT_SLEEP
|
|
if(hw_priv->connected_sta_cnt == 0){
|
|
atbm_printk_err("[STA] Setting p2p powersave "
|
|
"close.\n");
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
priv->p2p_ps_modeinfo.oppPsCTWindow &= 0x7F;
|
|
|
|
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
|
|
&priv->p2p_ps_modeinfo, priv->if_id));
|
|
atbm_notify_noa(priv, ATBM_APOLLO_NOA_NOTIFICATION_DELAY);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
atbm_printk_err("sta(%pM) add\n",sta->addr);
|
|
sta_priv->priv = priv;
|
|
sta_priv->link_id = __atbm_find_link_id(priv, sta->addr);
|
|
if (!sta_priv->link_id) {
|
|
/* Impossible error */
|
|
sta_priv->link_id = atbm_alloc_link_id(priv,sta->addr);
|
|
if(sta_priv->link_id){
|
|
atbm_printk_err("realloc link id(%d) mac[%pM]\n",sta_priv->link_id,sta->addr);
|
|
atbm_flush_workqueue(hw_priv->workqueue);
|
|
}else {
|
|
WARN_ON(1);
|
|
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 = ATBM_APOLLO_LINK_HARD;
|
|
entry->timestamp = jiffies;
|
|
while ((skb = atbm_skb_dequeue(&entry->rx_queue)))
|
|
atbm_ieee80211_rx(priv->hw, skb);
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
hw_priv->connected_sta_cnt++;
|
|
#ifdef CONFIG_ATBM_AP_SUPPORT_ONE_AMPDU
|
|
if(hw_priv->connected_sta_cnt>1) {
|
|
wsm_lock_tx(hw_priv);
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
ATBM_APOLLO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
|
|
ATBM_APOLLO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
|
|
priv->if_id));
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int atbm_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
struct ieee80211_sta *sta)
|
|
{
|
|
struct atbm_common *hw_priv = hw->priv;
|
|
struct atbm_sta_priv *sta_priv =
|
|
(struct atbm_sta_priv *)&sta->drv_priv;
|
|
struct atbm_vif *priv = ABwifi_get_vif_from_ieee80211(vif);
|
|
struct atbm_link_entry *entry;
|
|
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return 0;
|
|
}
|
|
#ifdef P2P_MULTIVIF
|
|
WARN_ON(priv->if_id == ATBM_WIFI_GENERIC_IF_ID);
|
|
#endif
|
|
|
|
if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
|
|
return 0;
|
|
atbm_printk_err("sta(%pM) remove\n",sta->addr);
|
|
entry = &priv->link_id_db[sta_priv->link_id - 1];
|
|
#ifdef ATBM_PKG_REORDER
|
|
atbm_reorder_func_reset(priv,sta_priv->link_id - 1);
|
|
#endif
|
|
/*
|
|
*wait tx path and rx path finished .after that the data frame can not
|
|
*send to low path
|
|
*/
|
|
synchronize_rcu();
|
|
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
// if(priv->vif->p2p == false){
|
|
entry->status = ATBM_APOLLO_LINK_SOFT;
|
|
smp_mb();
|
|
entry->timestamp = jiffies - ATBM_APOLLO_LINK_ID_GC_TIMEOUT - msecs_to_jiffies(10);
|
|
//}else {
|
|
// entry->status = ATBM_APOLLO_LINK_RESERVE;
|
|
// smp_mb();
|
|
// entry->timestamp = jiffies;
|
|
// }
|
|
wsm_lock_tx_async(hw_priv);
|
|
if (atbm_hw_priv_queue_work(hw_priv, &priv->link_id_work) <= 0)
|
|
wsm_unlock_tx(hw_priv);
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
atbm_flush_workqueue(hw_priv->workqueue);
|
|
hw_priv->connected_sta_cnt--;
|
|
#ifdef CONFIG_ATBM_AP_SUPPORT_ONE_AMPDU
|
|
if(hw_priv->connected_sta_cnt <= 1) {
|
|
if ((priv->if_id != 1) ||
|
|
((priv->if_id == 1)
|
|
#ifdef P2P_MULTIVIF
|
|
&& hw_priv->is_go_thru_go_neg
|
|
#endif
|
|
)
|
|
) {
|
|
wsm_lock_tx(hw_priv);
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
ATBM_APOLLO_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
|
|
ATBM_APOLLO_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
|
|
priv->if_id));
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef AP_MODE_SUPPORT_SLEEP
|
|
if(hw_priv->connected_sta_cnt == 0){
|
|
atbm_printk_err("[STA] Setting p2p powersave "
|
|
"open.\n");
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
priv->p2p_ps_modeinfo.oppPsCTWindow |= BIT(7);
|
|
|
|
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
|
|
&priv->p2p_ps_modeinfo, priv->if_id));
|
|
atbm_notify_noa(priv, ATBM_APOLLO_NOA_NOTIFICATION_DELAY);
|
|
#endif
|
|
}
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static void __atbm_sta_notify(struct atbm_vif *priv,
|
|
enum sta_notify_cmd notify_cmd,
|
|
int link_id)
|
|
{
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
u32 bit, prev;
|
|
|
|
/* Zero link id means "for all link IDs" */
|
|
if (link_id)
|
|
bit = BIT(link_id);
|
|
else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
|
|
bit = 0;
|
|
else
|
|
bit = priv->link_id_map;
|
|
prev = priv->sta_asleep_mask & bit;
|
|
|
|
switch (notify_cmd) {
|
|
case STA_NOTIFY_SLEEP:
|
|
if (!prev) {
|
|
if (priv->buffered_multicasts &&
|
|
!priv->sta_asleep_mask)
|
|
atbm_hw_priv_queue_work(hw_priv,
|
|
&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)
|
|
atbm_hw_priv_queue_work(hw_priv,
|
|
&priv->multicast_stop_work);
|
|
atbm_bh_wakeup(hw_priv);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void atbm_sta_notify(struct ieee80211_hw *dev,
|
|
struct ieee80211_vif *vif,
|
|
enum sta_notify_cmd notify_cmd,
|
|
struct ieee80211_sta *sta)
|
|
{
|
|
struct atbm_vif *priv = ABwifi_get_vif_from_ieee80211(vif);
|
|
struct atbm_sta_priv *sta_priv =
|
|
(struct atbm_sta_priv *)&sta->drv_priv;
|
|
|
|
if(atomic_read(&priv->enabled)==0){
|
|
return;
|
|
}
|
|
if(atbm_bh_is_term(priv->hw_priv)){
|
|
return;
|
|
}
|
|
#ifdef P2P_MULTIVIF
|
|
WARN_ON(priv->if_id == ATBM_WIFI_GENERIC_IF_ID);
|
|
#endif
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
__atbm_sta_notify(priv, notify_cmd, sta_priv->link_id);
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
}
|
|
|
|
static void atbm_ps_notify(struct atbm_vif *priv,
|
|
int link_id, bool ps)
|
|
{
|
|
if (link_id > ATBMWIFI_MAX_STA_IN_AP_MODE)
|
|
return;
|
|
|
|
txrx_printk("%s for LinkId: %d. STAs asleep: %.8X\n",
|
|
ps ? "Stop" : "Start",
|
|
link_id, priv->sta_asleep_mask);
|
|
|
|
/* TODO:COMBO: __atbm_sta_notify changed. */
|
|
__atbm_sta_notify(priv,
|
|
ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
|
|
}
|
|
|
|
static int atbm_set_tim_impl(struct atbm_vif *priv, bool aid0_bit_set)
|
|
{
|
|
struct atbm_common *hw_priv = ABwifi_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;
|
|
|
|
#if 0
|
|
ap_printk( "[AP] %s mcast: %s.\n",
|
|
__func__, aid0_bit_set ? "ena" : "dis");
|
|
#endif
|
|
skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
|
|
&tim_offset, &tim_length);
|
|
if (!skb) {
|
|
atbm_printk_err("%s : if_id[%d],if_name[%s] alloc skb err \n",__func__,priv->if_id,vif_to_sdata(priv->vif)->name);
|
|
#if 0
|
|
if (!__atbm_flush(hw_priv, false, priv->if_id))
|
|
wsm_unlock_tx(hw_priv);
|
|
#endif
|
|
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;
|
|
WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
|
|
|
|
atbm_dev_kfree_skb(skb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void atbm_set_tim_work(struct atbm_work_struct *work)
|
|
{
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, set_tim_work);
|
|
if(atbm_bh_is_term(priv->hw_priv)){
|
|
return;
|
|
}
|
|
(void)atbm_set_tim_impl(priv, priv->aid0_bit_set);
|
|
}
|
|
|
|
int atbm_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
|
|
bool set)
|
|
{
|
|
struct atbm_sta_priv *sta_priv =
|
|
(struct atbm_sta_priv *)&sta->drv_priv;
|
|
struct atbm_vif *priv = sta_priv->priv;
|
|
|
|
if(atomic_read(&priv->enabled)==0){
|
|
return 0;
|
|
}
|
|
if(atbm_bh_is_term(priv->hw_priv)){
|
|
return 0;
|
|
}
|
|
#ifdef P2P_MULTIVIF
|
|
WARN_ON(priv->if_id == ATBM_WIFI_GENERIC_IF_ID);
|
|
#endif
|
|
WARN_ON(priv->mode != NL80211_IFTYPE_AP);
|
|
atbm_hw_priv_queue_work(priv->hw_priv, &priv->set_tim_work);
|
|
return 0;
|
|
}
|
|
#if 0
|
|
void atbm_set_cts_work(struct atbm_work_struct *work)
|
|
{
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, set_cts_work.work);
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
|
|
u8 erp_ie[3] = {ATBM_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;
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return;
|
|
}
|
|
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("[STA] ERP information 0x%x\n", erp_info);
|
|
|
|
/* TODO:COMBO: If 2 interfaces are on the same channel they share
|
|
the same ERP values */
|
|
WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION,
|
|
&use_cts_prot, sizeof(use_cts_prot),
|
|
priv->if_id));
|
|
#if defined(CONFIG_NL80211_TESTMODE) || defined(CONFIG_ATBM_IOCTRL)
|
|
{
|
|
extern int atbm_tool_use_cts_prot;
|
|
atbm_tool_use_cts_prot = use_cts_prot;
|
|
}
|
|
#endif
|
|
/* If STA Mode update_ie is not required */
|
|
if (priv->mode != NL80211_IFTYPE_STATION) {
|
|
WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_ATBM_BT_COMB
|
|
static int atbm_set_btcoexinfo(struct atbm_vif *priv)
|
|
{
|
|
struct wsm_override_internal_txrate arg;
|
|
int ret = 0;
|
|
|
|
if (priv->mode == NL80211_IFTYPE_STATION) {
|
|
/* Plumb PSPOLL and NULL template */
|
|
WARN_ON(atbm_upload_pspoll(priv));
|
|
WARN_ON(atbm_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("[STA] STA has ERP rates\n");
|
|
/* G or BG mode */
|
|
arg.internalTxRate = (__ffs(
|
|
priv->bss_params.operationalRateSet & ~0xF));
|
|
} else {
|
|
ap_printk("[STA] 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( "[STA] BTCOEX_INFO"
|
|
"MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
|
|
priv->mode,
|
|
arg.internalTxRate,
|
|
arg.nonErpInternalTxRate);
|
|
|
|
ret = WARN_ON(wsm_write_mib(ABwifi_vifpriv_to_hwpriv(priv),
|
|
WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
|
|
&arg, sizeof(arg), priv->if_id));
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|
|
void atbm_ibss_join_work(struct atbm_vif *priv)
|
|
{
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
const u8 *bssid;
|
|
struct cfg80211_bss *bss;
|
|
struct wsm_protected_mgmt_policy mgmt_policy;
|
|
struct wsm_operational_mode mode = {
|
|
.power_mode = wsm_power_mode_quiescent,
|
|
.disableMoreFlagUsage = true,
|
|
};
|
|
struct wsm_join join = {
|
|
.mode = WSM_JOIN_MODE_IBSS,
|
|
.preambleType = WSM_JOIN_PREAMBLE_SHORT,
|
|
.probeForJoin = 1,
|
|
/* dtimPeriod will be updated after association */
|
|
.dtimPeriod = 1,
|
|
// .beaconInterval = bss->beacon_interval,
|
|
};
|
|
|
|
|
|
|
|
if (unlikely(priv->join_status)) {
|
|
wsm_lock_tx(hw_priv);
|
|
mutex_unlock(&hw_priv->conf_mutex);
|
|
atbm_unjoin_work(&priv->unjoin_work);
|
|
mutex_lock(&hw_priv->conf_mutex);
|
|
}
|
|
|
|
bssid = priv->vif->bss_conf.bssid;
|
|
bss = ieee80211_atbm_get_bss(hw_priv->hw->wiphy, hw_priv->channel,
|
|
bssid, NULL, 0, 0, 0);
|
|
if (!bss) {
|
|
wsm_unlock_tx(hw_priv);
|
|
return;
|
|
}
|
|
join.beaconInterval = bss->beacon_interval;
|
|
if (priv->if_id)
|
|
join.flags |= WSM_FLAG_MAC_INSTANCE_1;
|
|
else
|
|
join.flags &= ~WSM_FLAG_MAC_INSTANCE_1;
|
|
|
|
if(priv->tmpframe_probereq_set==0){
|
|
join.probeForJoin = 0;
|
|
//hw_priv->channel->hw_value = 6;
|
|
//join.channelNumber = 0;
|
|
atbm_printk_ap("<atbm_WIFI>because not scan before join, probeForJoin =0 hw_value %d\n",hw_priv->channel->hw_value);
|
|
}
|
|
#ifdef CONFIG_ATBM_BT_COMB
|
|
/* 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);
|
|
}
|
|
#endif
|
|
|
|
if (priv->hw->conf.ps_dtim_period) {
|
|
priv->join_dtim_period = priv->hw->conf.ps_dtim_period;
|
|
}
|
|
join.dtimPeriod = priv->join_dtim_period;
|
|
priv->beacon_int = bss->beacon_interval;
|
|
ap_printk( "[STA] Join DTIM: %d, interval: %d\n",
|
|
join.dtimPeriod, priv->beacon_int);
|
|
|
|
hw_priv->is_go_thru_go_neg = false;
|
|
join.channelNumber = channel_hw_value(hw_priv->channel);
|
|
|
|
/* 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));
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
if (priv->vif->p2p) {
|
|
join.flags |= WSM_JOIN_FLAGS_P2P_GO;
|
|
#ifdef P2P_MULTIVIF
|
|
join.flags |= (1 << 6);
|
|
#endif
|
|
join.basicRateSet =
|
|
atbm_rate_mask_to_wsm(hw_priv, 0xFF0);
|
|
}
|
|
#endif
|
|
#ifdef ATBM_SUPPORT_WIDTH_40M
|
|
if(priv->if_id&&(priv->vif->p2p==true))
|
|
{
|
|
join.channel_type = (u32)(hw_priv->channel_type>NL80211_CHAN_NO_HT ?
|
|
NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT);
|
|
}
|
|
else
|
|
{
|
|
spin_lock_bh(&hw_priv->spinlock_channel_type);
|
|
join.channel_type = (u32)hw_priv->channel_type;
|
|
spin_unlock_bh(&hw_priv->spinlock_channel_type);
|
|
}
|
|
#endif
|
|
wsm_flush_tx(hw_priv);
|
|
|
|
/* Queue unjoin if not associated in 3 sec. */
|
|
atbm_hw_priv_queue_delayed_work(hw_priv,
|
|
&priv->join_timeout, 3 * HZ);
|
|
#ifdef CONFIG_PM
|
|
/*Stay Awake for Join Timeout*/
|
|
atbm_pm_stay_awake(&hw_priv->pm_state, 3 * HZ);
|
|
#endif
|
|
#if define(CONFIG_ATBM_STA_LISTEN) || defined(CONFIG_ATBM_SUPPORT_P2P)
|
|
atbm_disable_listening(priv);
|
|
#endif
|
|
WARN_ON(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
/*hw_priv->ba_tid_mask*/0, hw_priv->ba_tid_rx_mask, priv->if_id));
|
|
#ifdef CONFIG_ATBM_BA_STATUS
|
|
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);
|
|
#endif
|
|
|
|
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));
|
|
|
|
atbm_hw_cancel_delayed_work(&priv->join_timeout,true);
|
|
} else {
|
|
/* Upload keys */
|
|
priv->join_status = ATBM_APOLLO_JOIN_STATUS_IBSS;
|
|
atbm_upload_keys(priv);
|
|
|
|
/* 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;
|
|
|
|
}
|
|
atbm_update_filtering(priv);
|
|
ieee80211_atbm_put_bss(hw_priv->hw->wiphy, bss);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
#endif
|
|
void atbm_bss_info_changed(struct ieee80211_hw *dev,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_bss_conf *info,
|
|
u32 changed)
|
|
{
|
|
struct atbm_common *hw_priv = dev->priv;
|
|
struct atbm_vif *priv = ABwifi_get_vif_from_ieee80211(vif);
|
|
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|
|
bool do_ibss_join = false;
|
|
#endif
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
if (priv->if_id == ATBM_WIFI_GENERIC_IF_ID)
|
|
return;
|
|
#endif
|
|
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return;
|
|
}
|
|
mutex_lock(&hw_priv->conf_mutex);
|
|
if (changed & BSS_CHANGED_STA_RESTART){
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
|
|
if(sdata->u.mgd.associated){
|
|
atbm_restart_join_bss(priv,sdata->u.mgd.associated);
|
|
}
|
|
}
|
|
if (changed & BSS_CHANGED_BSSID) {
|
|
#ifdef CONFIG_ATBM_APOLLO_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_ATBM_APOLLO_TESTMODE*/
|
|
memcpy(priv->bssid, info->bssid, ETH_ALEN);
|
|
atbm_setup_mac_pvif(priv);
|
|
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|
|
if (info->ibss_joined)
|
|
do_ibss_join = true;
|
|
#endif
|
|
}
|
|
|
|
/* TODO: BSS_CHANGED_IBSS */
|
|
|
|
if (changed & BSS_CHANGED_ARP_FILTER) {
|
|
struct wsm_arp_ipv4_filter filter = {0};
|
|
int i;
|
|
ap_printk( "[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)ATBM_APOLLO_ENABLE_ARP_FILTER_OFFLOAD;
|
|
else if (priv->join_status == ATBM_APOLLO_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( "[STA] addr[%d]: 0x%X\n",
|
|
i, filter.ipv4Address[i]);
|
|
}
|
|
} else
|
|
filter.enable = 0;
|
|
ap_printk( "[STA] arp ip filter enable: %d\n",
|
|
__le32_to_cpu(filter.enable));
|
|
atbm_printk_ap("[STA] arp ip filter enable: %d\n",
|
|
__le32_to_cpu(filter.enable));
|
|
#ifdef CONFIG_ATBM_LMAC_FILTER_IP_FRAME
|
|
if (filter.enable){
|
|
atbm_set_arpreply(dev, vif);
|
|
}
|
|
if (wsm_set_arp_ipv4_filter(hw_priv, &filter, priv->if_id))
|
|
WARN_ON(1);
|
|
#endif
|
|
|
|
priv->filter4.enable = filter.enable;
|
|
#ifdef ATBM_PKG_REORDER
|
|
priv->filter4.enable = 0;
|
|
#endif
|
|
if (priv->filter4.enable &&
|
|
(priv->join_status == ATBM_APOLLO_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. */
|
|
LOCAL_SET_CONNECT_STOP(hw_to_local(hw_priv->hw));
|
|
if (info->dynamic_ps_timeout >= 0x80)
|
|
priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
|
|
else
|
|
priv->powersave_mode.fastPsmIdlePeriod =
|
|
info->dynamic_ps_timeout << 1;
|
|
|
|
if (priv->setbssparams_done) {
|
|
struct wsm_set_pm pm = priv->powersave_mode;
|
|
int ret = 0;
|
|
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;
|
|
}
|
|
|
|
#ifdef ATBM_WIFI_NO_P2P_PS
|
|
if(priv->vif->p2p)
|
|
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
#endif
|
|
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
atbm_printk_err("%s %d ,pmMode = %d \n",__func__,__LINE__,priv->powersave_mode.pmMode);
|
|
ret = atbm_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;
|
|
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
atbm_printk_err("%s %d : pmMode(%d) \n",__func__,__LINE__,priv->powersave_mode.pmMode);
|
|
}
|
|
|
|
}
|
|
#ifdef CONFIG_ATBM_LMAC_FILTER_IP_FRAME
|
|
#ifdef IPV6_FILTERING
|
|
if (changed & BSS_CHANGED_NDP_FILTER) {
|
|
struct wsm_ndp_ipv6_filter filter = {0};
|
|
int i;
|
|
u16 *ipv6addr = NULL;
|
|
|
|
ap_printk( "[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)ATBM_APOLLO_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( "[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( "[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( "[STA] ndp ip filter enable: %d\n",
|
|
__le32_to_cpu(filter.enable));
|
|
|
|
if (filter.enable)
|
|
atbm_set_na(dev, vif);
|
|
|
|
priv->filter6.enable = filter.enable;
|
|
|
|
if (wsm_set_ndp_ipv6_filter(hw_priv, &filter, priv->if_id))
|
|
WARN_ON(1);
|
|
#if 0 /*Commented out to disable Power Save in IPv6*/
|
|
if (filter.enable && (priv->join_status == ATBM_APOLLO_JOIN_STATUS_STA) && (priv->vif->p2p) &&
|
|
!(priv->firmware_ps_mode.pmMode & WSM_PSM_FAST_PS)) {
|
|
if(priv->setbssparams_done) {
|
|
struct wsm_set_pm pm = priv->powersave_mode;
|
|
int ret = 0;
|
|
|
|
priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
|
|
ret = atbm_set_pm(priv, &priv->powersave_mode);
|
|
if(ret) {
|
|
priv->powersave_mode = pm;
|
|
}
|
|
} else {
|
|
priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
#endif /*IPV6_FILTERING*/
|
|
#endif
|
|
if (changed & BSS_CHANGED_BEACON) {
|
|
ap_printk( "BSS_CHANGED_BEACON\n");
|
|
#ifdef HIDDEN_SSID
|
|
if(priv->join_status != ATBM_APOLLO_JOIN_STATUS_AP)
|
|
{
|
|
// printk("%s , priv->hidden_ssid=%d, info->hidden_ssid=%d \n",__func__,priv->hidden_ssid ,info->hidden_ssid);
|
|
// if((priv->hidden_ssid != info->hidden_ssid) && (priv->hidden_ssid || info->hidden_ssid)){
|
|
priv->hidden_ssid = info->hidden_ssid;
|
|
priv->ssid_length = info->ssid_len;
|
|
memcpy(priv->ssid, info->ssid, info->ssid_len);
|
|
// }
|
|
}
|
|
else
|
|
ap_printk( "priv->join_status=%d\n",priv->join_status);
|
|
#endif
|
|
WARN_ON(atbm_upload_beacon(priv));
|
|
WARN_ON(atbm_update_beaconing(priv));
|
|
}
|
|
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|
|
if (changed & BSS_CHANGED_IBSS)
|
|
{
|
|
priv->beacon_int = info->beacon_int;
|
|
atbm_upload_beacon(priv);
|
|
WARN_ON(atbm_update_beaconing(priv));
|
|
}
|
|
#endif
|
|
if (changed & BSS_CHANGED_BEACON_ENABLED) {
|
|
ap_printk( "BSS_CHANGED_BEACON_ENABLED dummy\n");
|
|
if (priv->enable_beacon != info->enable_beacon) {
|
|
priv->enable_beacon = info->enable_beacon;
|
|
}
|
|
if(priv->enable_beacon == false){
|
|
atbm_stop_ap(priv);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (changed & BSS_CHANGED_BEACON_INT) {
|
|
ap_printk( "CHANGED_BEACON_INT\n");
|
|
/* Restart AP only when connected */
|
|
if (priv->join_status == ATBM_APOLLO_JOIN_STATUS_AP)
|
|
{
|
|
WARN_ON(atbm_update_beaconing(priv));
|
|
}
|
|
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|
|
else if (info->ibss_joined)
|
|
{
|
|
do_ibss_join = true;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
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 = 40;
|
|
priv->cqm_beacon_loss_count = 20;
|
|
priv->cqm_tx_failure_thold = 0;
|
|
}
|
|
priv->cqm_tx_failure_count = 0;
|
|
}
|
|
#ifdef CONFIG_ATBM_SUPPORT_IBSS
|
|
/* TODO: BSS_CHANGED_IBSS */
|
|
if (do_ibss_join)
|
|
{
|
|
wsm_lock_tx(hw_priv);
|
|
atbm_ibss_join_work(priv);
|
|
}
|
|
if (changed & BSS_CHANGED_IBSS)
|
|
{
|
|
if (!info->assoc) {
|
|
wsm_lock_tx(hw_priv);
|
|
atbm_ibss_join_work(priv);
|
|
}
|
|
}
|
|
#endif
|
|
if (changed &
|
|
(BSS_CHANGED_ASSOC |
|
|
BSS_CHANGED_BASIC_RATES |
|
|
BSS_CHANGED_ERP_PREAMBLE |
|
|
BSS_CHANGED_HT |
|
|
BSS_CHANGED_ERP_SLOT |
|
|
BSS_CHANGED_IBSS)) {
|
|
int is_combo = 0;
|
|
int i;
|
|
struct atbm_vif *tmp_priv;
|
|
atbm_printk_ap("BSS_CHANGED_ASSOC.changed(%x)\n",changed);
|
|
if (info->assoc || info->ibss_joined) { /* 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 */
|
|
mutex_unlock(&hw_priv->conf_mutex);
|
|
atbm_hw_cancel_delayed_work(&priv->join_timeout,false);
|
|
mutex_lock(&hw_priv->conf_mutex);
|
|
|
|
rcu_read_lock();
|
|
if (info->bssid && !info->ibss_joined)
|
|
sta = ieee80211_find_sta(vif, info->bssid);
|
|
if (sta) {
|
|
/* TODO:COMBO:Change this once
|
|
* mac80211 changes are available */
|
|
//printk("%s:channel_type(%d),changed(%x)\n",__func__,info->channel_type,changed);
|
|
// BUG_ON(!hw_priv->channel);
|
|
if(!hw_priv->channel){
|
|
atbm_printk_err("%s %d : hw_priv->channel NULL \n",__func__,__LINE__);
|
|
return;
|
|
}
|
|
hw_priv->ht_info.ht_cap = sta->ht_cap;
|
|
priv->bss_params.operationalRateSet =
|
|
__cpu_to_le32(
|
|
atbm_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 && atbm_is_ht(&hw_priv->ht_info));
|
|
atbm_for_each_vif(hw_priv, tmp_priv, i) {
|
|
#ifdef P2P_MULTIVIF
|
|
if ((i == (ATBM_WIFI_MAX_VIFS - 1)) || !tmp_priv)
|
|
#else
|
|
if (!tmp_priv)
|
|
#endif
|
|
continue;
|
|
if (tmp_priv->join_status >= ATBM_APOLLO_JOIN_STATUS_STA)
|
|
is_combo++;
|
|
}
|
|
|
|
if (is_combo > 1) {
|
|
hw_priv->vif0_throttle = ATBM_WIFI_HOST_VIF0_11BG_THROTTLE;
|
|
hw_priv->vif1_throttle = ATBM_WIFI_HOST_VIF1_11BG_THROTTLE;
|
|
atbm_printk_ap("ASSOC is_combo %d\n",hw_priv->vif0_throttle);
|
|
} else if ((priv->join_status == ATBM_APOLLO_JOIN_STATUS_STA) &&
|
|
priv->htcap) {
|
|
hw_priv->vif0_throttle = ATBM_WIFI_HOST_VIF0_11N_THROTTLE;
|
|
hw_priv->vif1_throttle = ATBM_WIFI_HOST_VIF1_11N_THROTTLE;
|
|
//printk(KERN_ERR "ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle);
|
|
} else {
|
|
hw_priv->vif0_throttle = ATBM_WIFI_HOST_VIF0_11BG_THROTTLE;
|
|
hw_priv->vif1_throttle = ATBM_WIFI_HOST_VIF1_11BG_THROTTLE;
|
|
atbm_printk_ap("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("[STA]"
|
|
" Non-GF STA present\n");
|
|
/* Non Green field capable STA */
|
|
val = __cpu_to_le32(BIT(1));
|
|
val = val;
|
|
}
|
|
#if 0
|
|
wsm_write_mib(hw_priv,WSM_MIB_ID_SET_HT_PROTECTION,
|
|
&val, sizeof(val), priv->if_id);
|
|
#endif
|
|
}
|
|
|
|
priv->association_mode.greenfieldMode =
|
|
atbm_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;
|
|
#if defined(CONFIG_NL80211_TESTMODE) || defined(CONFIG_ATBM_IOCTRL)
|
|
{
|
|
extern int atbm_tool_use_short_preamble;
|
|
atbm_tool_use_short_preamble = priv->association_mode.preambleType;
|
|
}
|
|
#endif
|
|
priv->association_mode.basicRateSet = __cpu_to_le32(
|
|
atbm_rate_mask_to_wsm(hw_priv,
|
|
info->basic_rates));
|
|
priv->association_mode.mpduStartSpacing =
|
|
atbm_ht_ampdu_density(&hw_priv->ht_info);
|
|
|
|
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;
|
|
#ifndef CONFIG_TX_NO_CONFIRM
|
|
atbm_hw_cancel_delayed_work(&priv->bss_loss_work,false);
|
|
atbm_hw_cancel_delayed_work(&priv->connection_loss_work,false);
|
|
#endif
|
|
atbm_hw_cancel_delayed_work(&priv->dhcp_retry_work,false);
|
|
|
|
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( "[STA] DTIM %d, interval: %d\n",
|
|
priv->join_dtim_period, priv->beacon_int);
|
|
ap_printk("[STA] Preamble: %d, " \
|
|
"Greenfield: %d, Aid: %d, " \
|
|
"Rates: 0x%.8X, Basic: 0x%.8X,mpduStartSpacing %d\n",
|
|
priv->association_mode.preambleType,
|
|
priv->association_mode.greenfieldMode,
|
|
priv->bss_params.aid,
|
|
priv->bss_params.operationalRateSet,
|
|
priv->association_mode.basicRateSet,
|
|
priv->association_mode.mpduStartSpacing);
|
|
WARN_ON(wsm_set_association_mode(hw_priv,
|
|
&priv->association_mode, priv->if_id));
|
|
if (!info->ibss_joined)
|
|
{
|
|
WARN_ON(wsm_keep_alive_period(hw_priv,
|
|
ATBM_APOLLO_KEEP_ALIVE_PERIOD /* sec */,
|
|
priv->if_id));
|
|
WARN_ON(wsm_set_bss_params(hw_priv, &priv->bss_params,
|
|
priv->if_id));
|
|
priv->setbssparams_done = true;
|
|
WARN_ON(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));
|
|
atbm_printk_ap("priv->htcap(%d)\n",priv->htcap);
|
|
if (priv->htcap) {
|
|
wsm_lock_tx(hw_priv);
|
|
/* Statically enabling block ack for TX/RX */
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
hw_priv->ba_tid_tx_mask, hw_priv->ba_tid_rx_mask,
|
|
priv->if_id));
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
atbm_set_pm(priv, &priv->powersave_mode);
|
|
}
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
if (priv->vif->p2p) {
|
|
ap_printk(
|
|
"[STA] Setting p2p powersave "
|
|
"configuration.\n");
|
|
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
|
|
&priv->p2p_ps_modeinfo, priv->if_id));
|
|
atbm_notify_noa(priv, ATBM_APOLLO_NOA_NOTIFICATION_DELAY);
|
|
}
|
|
#endif
|
|
if (priv->mode == NL80211_IFTYPE_STATION)
|
|
WARN_ON(atbm_upload_qosnull(priv));
|
|
#ifdef CONFIG_ATBM_BT_COMB
|
|
if (hw_priv->is_BT_Present)
|
|
WARN_ON(atbm_set_btcoexinfo(priv));
|
|
#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 == ATBM_APOLLO_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){
|
|
#if 0
|
|
atbm_hw_priv_queue_delayed_work(hw_priv,
|
|
&priv->set_cts_work, 0*HZ);
|
|
#endif
|
|
u8 erp_ie[3] = {ATBM_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;
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return;
|
|
}
|
|
erp_info = priv->erp_info;
|
|
use_cts_prot =
|
|
erp_info & WLAN_ERP_USE_PROTECTION ?
|
|
__cpu_to_le32(1) : 0;
|
|
|
|
erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
|
|
|
|
printk("[STA] ERP information,erp_ie[0]=%d,erp_ie[1]=%d, 0x%x\n",erp_ie[0],erp_ie[1], erp_info);
|
|
|
|
/* TODO:COMBO: If 2 interfaces are on the same channel they share
|
|
the same ERP values */
|
|
WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_NON_ERP_PROTECTION,
|
|
&use_cts_prot, sizeof(use_cts_prot),
|
|
priv->if_id));
|
|
#if defined(CONFIG_NL80211_TESTMODE) || defined(CONFIG_ATBM_IOCTRL)
|
|
{
|
|
extern int atbm_tool_use_cts_prot;
|
|
atbm_tool_use_cts_prot = use_cts_prot;
|
|
}
|
|
#endif
|
|
printk("[STA] ERP information,erp_ie[0]=%d,erp_ie[1]=%d, 0x%x\n",update_ie.ies[0],update_ie.ies[1], erp_info);
|
|
WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
|
|
}
|
|
}
|
|
}
|
|
|
|
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( "[STA] Slot time :%d us.\n",
|
|
__le32_to_cpu(slot_time));
|
|
|
|
//add by wp fix PHICOMM AP4 long slot bug
|
|
if(priv->htcap){
|
|
slot_time = __cpu_to_le32(9);
|
|
}
|
|
atbm_printk_ap( "[STA] Slot time :%d us.\n",__le32_to_cpu(slot_time));
|
|
WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_SLOT_TIME,
|
|
&slot_time, sizeof(slot_time), priv->if_id));
|
|
|
|
#if defined(CONFIG_NL80211_TESTMODE) || defined(CONFIG_ATBM_IOCTRL)
|
|
{
|
|
extern int atbm_tool_use_short_slot;
|
|
atbm_tool_use_short_slot = (slot_time == __cpu_to_le32(9))? 1:0;
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef CONFIG_ATBM_SUPPORT_RCPI_RSSI
|
|
if (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_CQM)) {
|
|
struct wsm_rcpi_rssi_threshold threshold = {
|
|
.rollingAverageCount = 8,
|
|
};
|
|
|
|
ap_printk( "[CQM] RSSI threshold "
|
|
"subscribe: %d +- %d\n",
|
|
info->cqm_rssi_thold, info->cqm_rssi_hyst);
|
|
ap_printk( "[CQM] Beacon loss subscribe: %d\n",
|
|
info->cqm_beacon_miss_thold);
|
|
ap_printk( "[CQM] TX failure subscribe: %d\n",
|
|
info->cqm_tx_fail_thold);
|
|
priv->cqm_rssi_thold = info->cqm_rssi_thold;
|
|
priv->cqm_rssi_hyst = info->cqm_rssi_hyst;
|
|
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) * 2;
|
|
threshold.lowerThreshold =
|
|
(info->cqm_rssi_thold + 110) * 2;
|
|
}
|
|
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;
|
|
}
|
|
WARN_ON(wsm_set_rcpi_rssi_threshold(hw_priv, &threshold,
|
|
priv->if_id));
|
|
|
|
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) {
|
|
WARN_ON(wsm_set_bss_params(hw_priv,
|
|
&priv->bss_params,
|
|
priv->if_id));
|
|
priv->setbssparams_done = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
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( "[STA] Aid: %d, Joined: %s, Powersave: %s\n",
|
|
priv->bss_params.aid,
|
|
priv->join_status == ATBM_APOLLO_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;
|
|
|
|
#ifdef ATBM_WIFI_NO_P2P_PS
|
|
if(priv->vif->p2p)
|
|
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
#endif
|
|
|
|
if (priv->join_status == ATBM_APOLLO_JOIN_STATUS_STA &&
|
|
priv->bss_params.aid &&
|
|
priv->setbssparams_done &&
|
|
priv->filter4.enable){
|
|
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
atbm_printk_err("%s %d:pmMode = %d \n",__func__,__LINE__,priv->powersave_mode.pmMode);
|
|
atbm_set_pm(priv, &priv->powersave_mode);
|
|
}else
|
|
priv->power_set_true = 1;
|
|
|
|
}
|
|
#ifndef CONFIG_RATE_HW_CONTROL
|
|
if (changed & BSS_CHANGED_RETRY_LIMITS) {
|
|
ap_printk( "[AP] 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 atbm_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. */
|
|
/* WARN_ON(tx_policy_force_upload(priv)); */
|
|
}
|
|
#endif
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
if (changed & BSS_CHANGED_P2P_PS) {
|
|
struct wsm_p2p_ps_modeinfo *modeinfo;
|
|
modeinfo = &priv->p2p_ps_modeinfo;
|
|
ap_printk( "[AP] BSS_CHANGED_P2P_PS\n");
|
|
ap_printk( "[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 == ATBM_APOLLO_JOIN_STATUS_STA){
|
|
atbm_printk_err("%s %d:pmMode = %d \n",__func__,__LINE__,priv->powersave_mode.pmMode);
|
|
|
|
atbm_set_pm(priv, &priv->powersave_mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
atbm_printk_ap("[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;
|
|
|
|
atbm_printk_ap("[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;
|
|
}
|
|
|
|
atbm_printk_ap("[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 == ATBM_APOLLO_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 (priv->join_status == ATBM_APOLLO_JOIN_STATUS_STA ||
|
|
priv->join_status == ATBM_APOLLO_JOIN_STATUS_AP) {
|
|
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv, modeinfo,
|
|
priv->if_id));
|
|
}
|
|
|
|
/* Temporary solution while firmware don't support NOA change
|
|
* notification yet */
|
|
atbm_notify_noa(priv, 10);
|
|
}
|
|
#endif
|
|
mutex_unlock(&hw_priv->conf_mutex);
|
|
|
|
}
|
|
|
|
void atbm_multicast_start_work(struct atbm_work_struct *work)
|
|
{
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, multicast_start_work);
|
|
long tmo = priv->join_dtim_period *
|
|
(priv->beacon_int + 20) * HZ / 1024;
|
|
|
|
atbm_hw_cancel_queue_work(&priv->multicast_stop_work,true);
|
|
if(atbm_bh_is_term(priv->hw_priv)){
|
|
return;
|
|
}
|
|
if (!priv->aid0_bit_set) {
|
|
wsm_lock_tx(priv->hw_priv);
|
|
atbm_set_tim_impl(priv, true);
|
|
priv->aid0_bit_set = true;
|
|
atbm_mod_timer(&priv->mcast_timeout, jiffies + tmo);
|
|
wsm_unlock_tx(priv->hw_priv);
|
|
}
|
|
}
|
|
|
|
void atbm_multicast_stop_work(struct atbm_work_struct *work)
|
|
{
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, multicast_stop_work);
|
|
if(atbm_bh_is_term(priv->hw_priv)){
|
|
return;
|
|
}
|
|
if (priv->aid0_bit_set) {
|
|
atbm_del_timer_sync(&priv->mcast_timeout);
|
|
wsm_lock_tx(priv->hw_priv);
|
|
priv->aid0_bit_set = false;
|
|
atbm_set_tim_impl(priv, false);
|
|
wsm_unlock_tx(priv->hw_priv);
|
|
}
|
|
}
|
|
|
|
void atbm_mcast_timeout(unsigned long arg)
|
|
{
|
|
struct atbm_vif *priv =
|
|
(struct atbm_vif *)arg;
|
|
|
|
atbm_printk_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)
|
|
atbm_bh_wakeup(ABwifi_vifpriv_to_hwpriv(priv));
|
|
else if(priv->aid0_bit_set == true){
|
|
/*
|
|
*Maybe all Sta have been in wake state,and bh has been send the Multicast
|
|
*to all stations.so here need to clear the TIM
|
|
*/
|
|
atbm_printk_err("clear TIM\n");
|
|
atbm_hw_priv_queue_work(priv->hw_priv,&priv->multicast_stop_work);
|
|
}
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
}
|
|
|
|
int atbm_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;
|
|
|
|
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 atbm_suspend_resume(struct atbm_vif *priv,
|
|
struct wsm_suspend_resume *arg)
|
|
{
|
|
struct atbm_common *hw_priv =
|
|
ABwifi_vifpriv_to_hwpriv(priv);
|
|
#if 0
|
|
ap_printk( "[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. */
|
|
#ifdef CONFIG_PM
|
|
atbm_pm_stay_awake(&hw_priv->pm_state,
|
|
priv->join_dtim_period *
|
|
(priv->beacon_int + 20) * HZ / 1024);
|
|
#endif
|
|
priv->tx_multicast = priv->aid0_bit_set &&
|
|
priv->buffered_multicasts;
|
|
if (priv->tx_multicast) {
|
|
cancel_tmo = true;
|
|
atbm_bh_wakeup(hw_priv);
|
|
}
|
|
}
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
if (cancel_tmo)
|
|
atbm_del_timer_sync(&priv->mcast_timeout);
|
|
} else {
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
atbm_ps_notify(priv, arg->link_id, arg->stop);
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
if (!arg->stop)
|
|
atbm_bh_wakeup(hw_priv);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* ******************************************************************** */
|
|
/* AP privates */
|
|
|
|
static int atbm_upload_beacon(struct atbm_vif *priv)
|
|
{
|
|
int ret = 0;
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct wsm_template_frame frame = {
|
|
.frame_type = WSM_FRAME_TYPE_BEACON,
|
|
};
|
|
struct atbm_ieee80211_mgmt *mgmt;
|
|
u8 *erp_inf, *ies, *ht_info;
|
|
#ifdef CONFIG_ATBM_AP_CHANNEL_CHANGE_EVENT
|
|
u8 *ds_params;
|
|
#endif
|
|
u32 ies_len;
|
|
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 (!frame.skb)
|
|
{
|
|
atbm_printk_err("upload_beacon NULL\n");
|
|
return -ENOMEM;
|
|
}
|
|
mgmt = (void *)frame.skb->data;
|
|
ies = mgmt->u.beacon.variable;
|
|
ies_len = frame.skb->len - (u32)(ies - (u8 *)mgmt);
|
|
#ifdef ATBM_SUPPORT_WIDTH_40M
|
|
//#ifdef P2P_MULTIVIF
|
|
atbm_printk_ap("atbm_clear_wpas_p2p_40M_ie\n");
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
if(priv->if_id&&(priv->vif->p2p==true))
|
|
atbm_clear_wpas_p2p_40M_ie(mgmt,frame.skb->len);
|
|
#endif
|
|
//#endif
|
|
#endif
|
|
ht_info = (u8 *)atbm_ieee80211_find_ie( ATBM_WLAN_EID_HT_INFORMATION, ies, ies_len);
|
|
if (ht_info) {
|
|
/* Enable RIFS*/
|
|
ht_info[3] |= 8;
|
|
}
|
|
erp_inf = (u8 *)atbm_ieee80211_find_ie(ATBM_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 CONFIG_ATBM_AP_CHANNEL_CHANGE_EVENT
|
|
//change AP dsparam channel when ap & sta not at the same channel
|
|
ds_params = (u8 *)atbm_ieee80211_find_ie( ATBM_WLAN_EID_DS_PARAMS, ies, ies_len);
|
|
if (ds_params) {
|
|
ds_params[2]= channel_hw_value(hw_priv->channel);
|
|
atbm_printk_err("change ds_params channel %d \n",channel_hw_value(hw_priv->channel));
|
|
}
|
|
#endif //#ifdef CONFIG_ATBM_AP_CHANNEL_CHANGE_EVENT
|
|
|
|
#ifdef HIDDEN_SSID
|
|
if (priv->hidden_ssid) {
|
|
u8 *ssid_ie;
|
|
u8 ssid_len;
|
|
|
|
atbm_printk_ap("hidden_ssid set\n");
|
|
ssid_ie = (u8 *)atbm_ieee80211_find_ie(ATBM_WLAN_EID_SSID, ies, ies_len);
|
|
WARN_ON(!ssid_ie);
|
|
ssid_len = ssid_ie[1];
|
|
if (ssid_len) {
|
|
atbm_printk_ap("%s: hidden_ssid with zero content "
|
|
"ssid\n", __func__);
|
|
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 {
|
|
atbm_printk_ap("%s: hidden ssid with ssid len 0"
|
|
" not supported", __func__);
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
|
|
if (!ret) {
|
|
#ifdef ATBM_PROBE_RESP_EXTRA_IE
|
|
if(priv->mode == NL80211_IFTYPE_AP) {
|
|
ret = atbm_upload_proberesp(priv);
|
|
goto __exit_up_beacon;
|
|
}
|
|
#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
|
|
*/
|
|
ret = wsm_set_template_frame(hw_priv, &frame,
|
|
priv->if_id);
|
|
WARN_ON(wsm_set_probe_responder(priv, false));
|
|
#endif
|
|
}
|
|
__exit_up_beacon:
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef ATBM_PROBE_RESP_EXTRA_IE
|
|
static int atbm_upload_proberesp(struct atbm_vif *priv)
|
|
{
|
|
int ret = 0;
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct wsm_template_frame frame = {
|
|
.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE,
|
|
};
|
|
#ifdef HIDDEN_SSID
|
|
u8 *ssid_ie;
|
|
#endif
|
|
if (priv->vif->p2p || hw_priv->channel->band == IEEE80211_BAND_5GHZ)
|
|
frame.rate = WSM_TRANSMIT_RATE_6;
|
|
|
|
frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif);
|
|
if (WARN_ON(!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 atbm_ieee80211_mgmt, u.probe_resp.variable);
|
|
ssid_ie = (u8*)atbm_ieee80211_find_ie(ATBM_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 {
|
|
atbm_printk_err("hidden ssid with mismatched ssid_len %d\n",ssid_len);
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
return -1;
|
|
}
|
|
}
|
|
#endif
|
|
ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
|
|
WARN_ON(wsm_set_probe_responder(priv, false));
|
|
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int atbm_upload_beacon_private(struct atbm_vif *priv)
|
|
{
|
|
return atbm_upload_beacon(priv);
|
|
}
|
|
int atbm_upload_proberesp_private(struct atbm_vif *priv)
|
|
{
|
|
#ifdef ATBM_PROBE_RESP_EXTRA_IE
|
|
return atbm_upload_proberesp(priv);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_ATBM_BT_COMB
|
|
static int atbm_upload_pspoll(struct atbm_vif *priv)
|
|
{
|
|
int ret = 0;
|
|
struct wsm_template_frame frame = {
|
|
.frame_type = WSM_FRAME_TYPE_PS_POLL,
|
|
.rate = 0xFF,
|
|
};
|
|
|
|
|
|
frame.skb = ieee80211_pspoll_get(priv->hw, priv->vif);
|
|
if (WARN_ON(!frame.skb))
|
|
return -ENOMEM;
|
|
|
|
ret = wsm_set_template_frame(ABwifi_vifpriv_to_hwpriv(priv), &frame,
|
|
priv->if_id);
|
|
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int atbm_upload_null(struct atbm_vif *priv)
|
|
{
|
|
int ret = 0;
|
|
struct wsm_template_frame frame = {
|
|
.frame_type = WSM_FRAME_TYPE_NULL,
|
|
.rate = 0xFF,
|
|
};
|
|
|
|
|
|
frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif);
|
|
if (WARN_ON(!frame.skb))
|
|
return -ENOMEM;
|
|
|
|
ret = wsm_set_template_frame(ABwifi_vifpriv_to_hwpriv(priv),
|
|
&frame, priv->if_id);
|
|
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
static int atbm_upload_qosnull(struct atbm_vif *priv)
|
|
{
|
|
int ret = 0;
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct wsm_template_frame frame = {
|
|
.frame_type = WSM_FRAME_TYPE_QOS_NULL,
|
|
.rate = 0xFF,
|
|
};
|
|
|
|
|
|
frame.skb = ieee80211_qosnullfunc_get(priv->hw, priv->vif);
|
|
if (WARN_ON(!frame.skb))
|
|
return -ENOMEM;
|
|
|
|
ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
|
|
|
|
atbm_dev_kfree_skb(frame.skb);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int atbm_stop_ap(struct atbm_vif *priv)
|
|
{
|
|
atbm_printk_err("atbm_stop_ap !! \n");
|
|
#if 0
|
|
#ifdef CONFIG_IEEE80211_SPECIAL_FILTER
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(priv->vif);
|
|
|
|
ieee80211_special_filter_clear(sdata);
|
|
atbm_printk_err("atbm_stop_ap : ieee80211_special_filter_clear \n");
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
static int atbm_start_ap(struct atbm_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 atbm_common *hw_priv = ABwifi_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 = channel_hw_value(hw_priv->channel),
|
|
.beaconInterval = conf->beacon_int,
|
|
.DTIMPeriod = conf->dtim_period,
|
|
.preambleType = conf->use_short_preamble ?
|
|
WSM_JOIN_PREAMBLE_SHORT :
|
|
WSM_JOIN_PREAMBLE_LONG,
|
|
.probeDelay = 100,
|
|
.basicRateSet = atbm_rate_mask_to_wsm(hw_priv,
|
|
conf->basic_rates),
|
|
#ifdef P2P_MULTIVIF
|
|
.CTWindow = priv->vif->p2p ? 0xFFFFFFFF : 0,
|
|
#else
|
|
.CTWindow = 0,
|
|
#endif
|
|
};
|
|
|
|
struct wsm_inactivity inactivity = {
|
|
.min_inactivity = 10,//50,
|
|
.max_inactivity = 5,//10,
|
|
};
|
|
|
|
struct wsm_operational_mode mode = {
|
|
.power_mode = wsm_power_mode_quiescent,
|
|
.disableMoreFlagUsage = true,
|
|
};
|
|
|
|
if (priv->if_id)
|
|
start.mode |= WSM_FLAG_MAC_INSTANCE_1;
|
|
else
|
|
start.mode &= ~WSM_FLAG_MAC_INSTANCE_1;
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
if(priv->vif->p2p){
|
|
inactivity.min_inactivity = 15;
|
|
inactivity.max_inactivity = 5;
|
|
}
|
|
#endif
|
|
hw_priv->connected_sta_cnt = 0;
|
|
|
|
#ifndef HIDDEN_SSID
|
|
/* Get SSID */
|
|
skb = ieee80211_beacon_get(priv->hw, priv->vif);
|
|
if (WARN_ON(!skb))
|
|
return -ENOMEM;
|
|
|
|
offset = offsetof(struct atbm_ieee80211_mgmt, u.beacon.variable);
|
|
ssidie = atbm_ieee80211_find_ie(ATBM_WLAN_EID_SSID, skb->data + offset,
|
|
skb->len - offset);
|
|
|
|
memset(priv->ssid, 0, sizeof(priv->ssid));
|
|
if (ssidie) {
|
|
priv->ssid_length = ssidie[1];
|
|
if (WARN_ON(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;
|
|
}
|
|
atbm_dev_kfree_skb(skb);
|
|
#endif
|
|
|
|
priv->beacon_int = conf->beacon_int;
|
|
priv->join_dtim_period = conf->dtim_period;
|
|
|
|
start.ssidLength = priv->ssid_length;
|
|
memcpy(&start.ssid[0], priv->ssid, start.ssidLength);
|
|
|
|
memset(&priv->link_id_db, 0, sizeof(priv->link_id_db));
|
|
|
|
#ifdef ATBM_SUPPORT_WIDTH_40M
|
|
|
|
start.channel_type = (u32)(vif_chw(priv->vif));
|
|
if(start_choff<=NL80211_CHAN_HT40PLUS)
|
|
{
|
|
atbm_printk_ap("%s:fix chatype(%d)\n",__func__,start_choff);
|
|
start.channel_type = start_choff;
|
|
}
|
|
|
|
if(priv->if_id&&(priv->vif->p2p==true))
|
|
start.channel_type = NL80211_CHAN_HT20;
|
|
|
|
atbm_printk_ap("%s:start.channel_type,(%d),channelNumber(%d)\n",__func__,start.channel_type,start.channelNumber);
|
|
#endif
|
|
|
|
atbm_printk_ap("[AP] ch: %d(%d), bcn: %d(%d), "
|
|
"brt: 0x%.8X, ssid: %.*s.\n",
|
|
start.channelNumber, start.band,
|
|
start.beaconInterval, start.DTIMPeriod,
|
|
start.basicRateSet,
|
|
start.ssidLength, start.ssid);
|
|
ret = WARN_ON(wsm_start(hw_priv, &start, priv->if_id));
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
#ifdef AP_MODE_SUPPORT_SLEEP
|
|
if (!ret)
|
|
#else
|
|
|
|
if (!ret && priv->vif->p2p)
|
|
#endif
|
|
{
|
|
ap_printk(
|
|
"[AP] Setting p2p powersave "
|
|
"configuration.\n");
|
|
#ifdef AP_MODE_SUPPORT_SLEEP
|
|
priv->p2p_ps_modeinfo.oppPsCTWindow = 20;
|
|
priv->p2p_ps_modeinfo.oppPsCTWindow |= BIT(7);
|
|
#endif
|
|
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
|
|
&priv->p2p_ps_modeinfo, priv->if_id));
|
|
atbm_notify_noa(priv, ATBM_APOLLO_NOA_NOTIFICATION_DELAY);
|
|
}
|
|
#ifdef ATBM_P2P_CHANGE
|
|
if(priv->if_id == 0){
|
|
atomic_set(&hw_priv->combination,1);
|
|
}
|
|
#endif
|
|
#endif
|
|
/*Set Inactivity time*/
|
|
if(!(strstr(&start.ssid[0], "6.1.12"))) {
|
|
wsm_set_inactivity(hw_priv, &inactivity, priv->if_id);
|
|
}
|
|
if (!ret) {
|
|
#ifdef P2P_MULTIVIF
|
|
if ((priv->if_id ==1) && !hw_priv->is_go_thru_go_neg)
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
ATBM_APOLLO_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
|
|
ATBM_APOLLO_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
|
|
priv->if_id));
|
|
else
|
|
#endif
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
hw_priv->ba_tid_tx_mask,
|
|
hw_priv->ba_tid_rx_mask,
|
|
priv->if_id));
|
|
|
|
priv->join_status = ATBM_APOLLO_JOIN_STATUS_AP;
|
|
/* atbm_update_filtering(priv); */
|
|
}
|
|
WARN_ON(wsm_set_operational_mode(hw_priv, &mode, priv->if_id));
|
|
hw_priv->vif0_throttle = ATBM_WIFI_HOST_VIF0_11N_THROTTLE;
|
|
hw_priv->vif1_throttle = ATBM_WIFI_HOST_VIF0_11N_THROTTLE;
|
|
//use when wep share key,need firmware not enc auth frame
|
|
if((priv->cipherType == WLAN_CIPHER_SUITE_WEP40)
|
|
|| (priv->cipherType == WLAN_CIPHER_SUITE_WEP104)){
|
|
struct wsm_protected_mgmt_policy mgmt_policy;
|
|
mgmt_policy.protectedMgmtEnable = 0;
|
|
mgmt_policy.unprotectedMgmtFramesAllowed = 1;
|
|
mgmt_policy.encryptionForAuthFrame = 1;//not enc for hw
|
|
wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy,
|
|
priv->if_id);
|
|
}
|
|
#ifdef ATBM_WIFI_QUEUE_LOCK_BUG
|
|
atbm_set_priv_queue_cap(priv);
|
|
#endif
|
|
#if 0
|
|
|
|
#ifdef CONFIG_IEEE80211_SPECIAL_FILTER
|
|
struct ieee80211_sub_if_data *sdata = vif_to_sdata(priv->vif);
|
|
struct ieee80211_special_filter filter;
|
|
filter.filter_action = 0x40;
|
|
filter.flags = SPECIAL_F_FLAGS_FRAME_TYPE;
|
|
ieee80211_special_filter_clear(sdata);
|
|
if(ieee80211_special_filter_register(sdata,&filter) == false)
|
|
atbm_printk_err("%s() ieee80211_special_filter_register() ## filter probe req set fail! \n",__func__);
|
|
#endif
|
|
#endif
|
|
|
|
atbm_printk_ap("AP/GO mode BG THROTTLE %d,cipherType %x\n", hw_priv->vif0_throttle,priv->cipherType);
|
|
return ret;
|
|
}
|
|
|
|
static int atbm_update_beaconing(struct atbm_vif *priv)
|
|
{
|
|
struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct wsm_reset reset = {
|
|
.link_id = 0,
|
|
.reset_statistics = true,
|
|
};
|
|
|
|
if (priv->mode == NL80211_IFTYPE_AP) {
|
|
/* TODO: check if changed channel, band */
|
|
if (priv->join_status != ATBM_APOLLO_JOIN_STATUS_AP ||
|
|
priv->beacon_int != conf->beacon_int) {
|
|
ap_printk( "ap restarting\n");
|
|
wsm_lock_tx(hw_priv);
|
|
if (priv->join_status != ATBM_APOLLO_JOIN_STATUS_PASSIVE)
|
|
WARN_ON(wsm_reset(hw_priv, &reset,
|
|
priv->if_id));
|
|
priv->join_status = ATBM_APOLLO_JOIN_STATUS_PASSIVE;
|
|
WARN_ON(atbm_start_ap(priv));
|
|
wsm_unlock_tx(hw_priv);
|
|
} else
|
|
ap_printk( "ap started join_status: %d\n",
|
|
priv->join_status);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int atbm_find_link_id(struct atbm_vif *priv, const u8 *mac)
|
|
{
|
|
int i, ret = 0;
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
|
|
for (i = 0; i < ATBMWIFI_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 atbm_alloc_link_id(struct atbm_vif *priv, const u8 *mac)
|
|
{
|
|
int i, ret = 0;
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
// BUG_ON(hw_priv->wsm_caps.NumOfStations == 0);
|
|
// BUG_ON(hw_priv->wsm_caps.NumOfStations > ATBMWIFI_MAX_STA_IN_AP_MODE);
|
|
if((hw_priv->wsm_caps.NumOfStations == 0) ||
|
|
(hw_priv->wsm_caps.NumOfStations > ATBMWIFI_MAX_STA_IN_AP_MODE)){
|
|
atbm_printk_err("%s %d :%s ,%s",__func__,__LINE__,
|
|
hw_priv->wsm_caps.NumOfStations == 0?"hw_priv->wsm_caps.NumOfStations == 0":" ",
|
|
hw_priv->wsm_caps.NumOfStations > ATBMWIFI_MAX_STA_IN_AP_MODE?"hw_priv->wsm_caps.NumOfStations > ATBMWIFI_MAX_STA_IN_AP_MODE":" ");
|
|
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
for (i = 0; i < hw_priv->wsm_caps.NumOfStations/*ATBMWIFI_MAX_STA_IN_AP_MODE*/; ++i) {
|
|
if (!priv->link_id_db[i].status) {
|
|
ret = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
if (ret) {
|
|
struct atbm_link_entry *entry = &priv->link_id_db[ret - 1];
|
|
ap_printk( "[AP] STA added, link_id: %d\n",
|
|
ret);
|
|
atbm_printk_err("alloc mac[%pM],link_id(%d)\n",mac,ret);
|
|
entry->timestamp = jiffies;
|
|
entry->status = ATBM_APOLLO_LINK_RESERVE;
|
|
memcpy(&entry->mac, mac, ETH_ALEN);
|
|
memset(&entry->buffered, 0, ATBM_APOLLO_MAX_TID);
|
|
atbm_skb_queue_head_init(&entry->rx_queue);
|
|
wsm_lock_tx_async(hw_priv);
|
|
if (atbm_hw_priv_queue_work(hw_priv, &priv->link_id_work) <= 0)
|
|
wsm_unlock_tx(hw_priv);
|
|
} else {
|
|
for(i=0;i<hw_priv->wsm_caps.NumOfStations;i++){
|
|
atbm_printk_err("dammp mac[%pM],status[%d]\n",priv->link_id_db[i].mac,priv->link_id_db[i].status);
|
|
}
|
|
}
|
|
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
return ret;
|
|
}
|
|
|
|
void atbm_link_id_work(struct atbm_work_struct *work)
|
|
{
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, link_id_work);
|
|
struct atbm_common *hw_priv = priv->hw_priv;
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
wsm_unlock_tx(hw_priv);
|
|
return;
|
|
}
|
|
wsm_flush_tx(hw_priv);
|
|
atbm_link_id_gc_work(&priv->link_id_gc_work.work);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
void atbm_link_id_gc_work(struct atbm_work_struct *work)
|
|
{
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, link_id_gc_work.work);
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct wsm_map_link map_link = {
|
|
.link_id = 0,
|
|
};
|
|
struct wsm_map_link unmap_link = {
|
|
.link_id = 0,
|
|
.unmap = 1,
|
|
};
|
|
unsigned long now = jiffies;
|
|
unsigned long next_gc = -1;
|
|
long ttl;
|
|
bool need_reset;
|
|
u32 mask;
|
|
int i;
|
|
|
|
if (priv->join_status != ATBM_APOLLO_JOIN_STATUS_AP)
|
|
return;
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return;
|
|
}
|
|
wsm_lock_tx(hw_priv);
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
for (i = 0; i < ATBMWIFI_MAX_STA_IN_AP_MODE; ++i) {
|
|
need_reset = false;
|
|
mask = BIT(i + 1);
|
|
if (priv->link_id_db[i].status == ATBM_APOLLO_LINK_RESERVE ||
|
|
(priv->link_id_db[i].status == ATBM_APOLLO_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 != ATBM_APOLLO_LINK_HARD)
|
|
priv->link_id_db[i].status = ATBM_APOLLO_LINK_SOFT;
|
|
memcpy(map_link.mac_addr, priv->link_id_db[i].mac,
|
|
ETH_ALEN);
|
|
if(need_reset == true){
|
|
memcpy(unmap_link.mac_addr,priv->link_id_db[i].mac,ETH_ALEN);
|
|
unmap_link.link_id = i+1;
|
|
unmap_link.unmap = 1;
|
|
}
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
if (need_reset) {
|
|
WARN_ON(wsm_map_link(hw_priv,&unmap_link,priv->if_id));
|
|
}
|
|
map_link.link_id = i + 1;
|
|
WARN_ON(wsm_map_link(hw_priv, &map_link, priv->if_id));
|
|
next_gc = min(next_gc, ATBM_APOLLO_LINK_ID_GC_TIMEOUT);
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
} else if (priv->link_id_db[i].status == ATBM_APOLLO_LINK_SOFT) {
|
|
ttl = priv->link_id_db[i].timestamp - now +
|
|
ATBM_APOLLO_LINK_ID_GC_TIMEOUT;
|
|
if (ttl <= 0) {
|
|
need_reset = true;
|
|
priv->link_id_db[i].status = ATBM_APOLLO_LINK_OFF;
|
|
priv->link_id_map &= ~mask;
|
|
priv->sta_asleep_mask &= ~mask;
|
|
priv->pspoll_mask &= ~mask;
|
|
memset(map_link.mac_addr, 0, ETH_ALEN);
|
|
/*
|
|
*unmap the current device
|
|
*/
|
|
memcpy(unmap_link.mac_addr,priv->link_id_db[i].mac,ETH_ALEN);
|
|
unmap_link.link_id = i+1;
|
|
unmap_link.unmap = 1;
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
WARN_ON(wsm_map_link(hw_priv, &unmap_link, priv->if_id));
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
} else {
|
|
next_gc = min_t(unsigned long, next_gc, ttl);
|
|
}
|
|
}
|
|
#if 0
|
|
else if (priv->link_id_db[i].status == ATBM_APOLLO_LINK_RESET ||
|
|
priv->link_id_db[i].status ==
|
|
ATBM_APOLLO_LINK_RESET_REMAP) {
|
|
int status = priv->link_id_db[i].status;
|
|
if(priv->link_id_db[i].status == ATBM_APOLLO_LINK_RESET){
|
|
priv->link_id_db[i].status = ATBM_APOLLO_LINK_OFF;
|
|
priv->link_id_map &= ~mask;
|
|
priv->sta_asleep_mask &= ~mask;
|
|
priv->pspoll_mask &= ~mask;
|
|
}else {
|
|
priv->link_id_db[i].status = priv->link_id_db[i].prev_status;
|
|
priv->link_id_db[i].timestamp = now;
|
|
}
|
|
memcpy(unmap_link.mac_addr,priv->link_id_db[i].mac,ETH_ALEN);
|
|
unmap_link.link_id = i+1;
|
|
unmap_link.unmap = 1;
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
WARN_ON(wsm_map_link(hw_priv, &unmap_link, priv->if_id));
|
|
if (status == ATBM_APOLLO_LINK_RESET_REMAP) {
|
|
memcpy(map_link.mac_addr,
|
|
priv->link_id_db[i].mac,
|
|
ETH_ALEN);
|
|
map_link.link_id = i + 1;
|
|
WARN_ON(wsm_map_link(hw_priv, &map_link,
|
|
priv->if_id));
|
|
next_gc = min(next_gc,
|
|
ATBM_APOLLO_LINK_ID_GC_TIMEOUT);
|
|
|
|
}
|
|
spin_lock_bh(&priv->ps_state_lock);
|
|
}
|
|
#endif
|
|
if (need_reset) {
|
|
atbm_skb_queue_purge(&priv->link_id_db[i].rx_queue);
|
|
ap_printk( "[AP] STA removed, link_id: %d\n",
|
|
i + 1);
|
|
}
|
|
}
|
|
spin_unlock_bh(&priv->ps_state_lock);
|
|
if (next_gc != -1)
|
|
atbm_hw_priv_queue_delayed_work(hw_priv,
|
|
&priv->link_id_gc_work, next_gc);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
#ifdef CONFIG_ATBM_SUPPORT_P2P
|
|
void atbm_notify_noa(struct atbm_vif *priv, int delay)
|
|
{
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
// struct cfg80211_p2p_ps p2p_ps = {0};
|
|
struct wsm_p2p_ps_modeinfo *modeinfo;
|
|
modeinfo = &priv->p2p_ps_modeinfo;
|
|
|
|
ap_printk( "[AP]: %s called\n", __func__);
|
|
/*
|
|
*if ctwindows and disable is disabled,we should keep
|
|
*the all of the interface awake;
|
|
*/
|
|
if((!!(modeinfo->oppPsCTWindow & BIT(7)) == 0)&&
|
|
( (modeinfo->oppPsCTWindow & (~BIT(7))) == 0))
|
|
{
|
|
struct atbm_vif *priv_wlan;
|
|
/*
|
|
*BUG!!!!!! at go mode, the firmware can enter ps mode if the wlan0 interface
|
|
*has connect ap.
|
|
*so to fixed that bug, i wanted to change wlan0 ps mode to active mode.
|
|
*!!!!!!!!due to that wsm_set_pm function must lock wsm_oper_lock and unlock wsm_oper_lock
|
|
*must wait until the firmware ps mode has change at ap,so there is some dangerous
|
|
*that the hmac cannot receive the ps commplete indication,that make the wsm_oper_lock
|
|
*unlock.
|
|
*/
|
|
if((priv_wlan=__ABwifi_hwpriv_to_vifpriv(hw_priv,0))!= NULL)
|
|
{
|
|
if(priv_wlan->powersave_mode.pmMode != WSM_PSM_ACTIVE)
|
|
memcpy(&hw_priv->roc_wlan_pm,&priv_wlan->powersave_mode,sizeof(struct wsm_set_pm));
|
|
|
|
priv_wlan->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
atbm_set_pm(priv_wlan,&priv_wlan->powersave_mode);
|
|
}
|
|
}
|
|
if (priv->join_status != ATBM_APOLLO_JOIN_STATUS_AP)
|
|
return;
|
|
|
|
if (delay)
|
|
msleep(delay);
|
|
|
|
if (!WARN_ON(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo,priv->if_id))) {
|
|
#if defined(CONFIG_ATBM_APOLLO_STA_DEBUG)
|
|
print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ",
|
|
DUMP_PREFIX_NONE,
|
|
(u8 *)modeinfo,
|
|
sizeof(*modeinfo));
|
|
#endif /* CONFIG_ATBM_APOLLO_STA_DEBUG */
|
|
#if 0
|
|
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 ABwifi_unmap_link(struct atbm_vif *priv, int link_id)
|
|
{
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
//int ret = 0;
|
|
//struct wsm_operational_mode mode = {
|
|
// .power_mode = wsm_power_mode_quiescent,
|
|
// .disableMoreFlagUsage = true,
|
|
//};
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,3,0))
|
|
void atbm_ht_info_update_work(struct atbm_work_struct *work)
|
|
{
|
|
struct sk_buff *skb;
|
|
struct atbm_ieee80211_mgmt *mgmt;
|
|
u8 *ht_info, *ies;
|
|
u32 ies_len;
|
|
struct atbm_vif *priv =
|
|
container_of(work, struct atbm_vif, ht_info_update_work);
|
|
struct atbm_common *hw_priv = ABwifi_vifpriv_to_hwpriv(priv);
|
|
struct wsm_update_ie update_ie = {
|
|
.what = WSM_UPDATE_IE_BEACON,
|
|
.count = 1,
|
|
};
|
|
if(atbm_bh_is_term(hw_priv)){
|
|
return;
|
|
}
|
|
skb = ieee80211_beacon_get(priv->hw, priv->vif);
|
|
if (WARN_ON(!skb))
|
|
return;
|
|
|
|
mgmt = (void *)skb->data;
|
|
ies = mgmt->u.beacon.variable;
|
|
ies_len = skb->len - (u32)(ies - (u8 *)mgmt);
|
|
ht_info= (u8 *)atbm_ieee80211_find_ie( ATBM_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;
|
|
|
|
WARN_ON(wsm_update_ie(hw_priv, &update_ie, priv->if_id));
|
|
|
|
|
|
}
|
|
atbm_dev_kfree_skb(skb);
|
|
}
|
|
#endif
|
|
int atbm_start_monitor_mode(struct atbm_vif *priv,
|
|
struct ieee80211_channel *chan)
|
|
{
|
|
struct atbm_common *hw_priv = priv->hw_priv;
|
|
int ret = 0;
|
|
struct wsm_start start = {
|
|
.mode = WSM_START_MODE_MONITOR_DEV | (priv->if_id << 4),
|
|
.band = (chan->band == IEEE80211_BAND_5GHZ) ?
|
|
WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
|
|
.channelNumber = channel_hw_value(chan),
|
|
.beaconInterval = 100,
|
|
.DTIMPeriod = 1,
|
|
.probeDelay = 0,
|
|
.basicRateSet = 0x0F,
|
|
};
|
|
if(priv->join_status != ATBM_APOLLO_JOIN_STATUS_SIMPLE_MONITOR){
|
|
return -1;
|
|
}
|
|
/*
|
|
*when one interface in monitor mode ,othe interface can not process scan
|
|
*or remain on channel;
|
|
*/
|
|
lockdep_assert_held(&hw_priv->conf_mutex);
|
|
hw_priv->monitor_if_id = priv->if_id;
|
|
/*
|
|
*must make sure monitor_if_id has been set to priv->if_id,before send cmd to lmc;
|
|
*/
|
|
smp_mb();
|
|
/*
|
|
* clear the pkg
|
|
*/
|
|
ret = WARN_ON(__atbm_flush(hw_priv, false,priv->if_id));
|
|
if(!ret){
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
#ifndef ATBM_NOT_SUPPORT_40M_CHW
|
|
if(hw_priv->chip_version == ARES_6012B){
|
|
start.channel_type = (u32)(NL80211_CHAN_HT20);
|
|
}else{
|
|
|
|
if((channel_hw_value(chan)>=9)&&(channel_hw_value(chan)<=14))
|
|
start.channel_type = (u32)(NL80211_CHAN_HT40MINUS);
|
|
else if((channel_hw_value(chan)>0)&&(channel_hw_value(chan)<9))
|
|
start.channel_type = (u32)(NL80211_CHAN_HT40PLUS);
|
|
else if(channel_hw_value(chan) == 36)
|
|
start.channel_type = (u32)(NL80211_CHAN_HT40PLUS);
|
|
else if(channel_hw_value(chan) == 38)
|
|
start.channel_type = (u32)(NL80211_CHAN_HT40MINUS);
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
#else
|
|
start.channel_type = (u32)(NL80211_CHAN_HT20);
|
|
#endif
|
|
atbm_printk_ap("%s:if_id(%d),channel(%d)\n",__func__,priv->if_id,channel_hw_value(chan));
|
|
wsm_start(hw_priv, &start, ATBM_WIFI_GENERIC_IF_ID);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int atbm_stop_monitor_mode(struct atbm_vif *priv)
|
|
{
|
|
struct atbm_common *hw_priv = priv->hw_priv;
|
|
int ret = 0;
|
|
struct wsm_reset reset = {
|
|
.reset_statistics = true,
|
|
};
|
|
lockdep_assert_held(&hw_priv->conf_mutex);
|
|
ret = WARN_ON(__atbm_flush(hw_priv, false, priv->if_id));
|
|
if (!ret) {
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
ret = wsm_reset(priv->hw_priv, &reset, ATBM_WIFI_GENERIC_IF_ID);
|
|
smp_mb();
|
|
hw_priv->monitor_if_id = -1;
|
|
atbm_printk_ap("%s:if_id(%d)\n",__func__,priv->if_id);
|
|
return ret;
|
|
}
|
|
|