Files
bes2600-dkms/bes2600/ap.c
T
test0r 8dd79199f8 bes2600: Patch G — restore SPDX identifiers + ST-Ericsson attribution
The bes2600 driver is a fork of the upstream cw1200 driver
(drivers/net/wireless/st/cw1200/, ST-Ericsson, Dmitry Tarnyagin
2010-2011).  The fork's file headers have three GPL-compliance issues:

  1. NO SPDX-License-Identifier on any of 48 source files (cw1200
     mainline has them on all 25).  kernel.org-mandated since 2017.

  2. Original "Copyright (c) 2010, ST-Ericsson" lines stripped from
     all files inherited from cw1200, replaced with
     "Copyright (c) 2010, Bestechnic" — factually impossible
     (Bestechnic did not author the 2010 work) and a GPL-2.0 §1
     attribution-preservation violation.

  3. The "GPL version 2 as published by the Free Software Foundation"
     boilerplate paragraph is redundant alongside SPDX and is the
     legacy form modern kernel sources have replaced.

This patch corrects all three for the 48 .c/.h files in bes2600/:

  - Adds `// SPDX-License-Identifier: GPL-2.0-only` (or `/* ... */`
    for headers) as line 1 of every file.
  - Restores `Copyright (c) 2010, ST-Ericsson` + `Author: Dmitry
    Tarnyagin <dmitry.tarnyagin@lockless.no>` as the FIRST copyright
    chain entry on all 22 files derived from cw1200 (bh.{c,h},
    debug.{c,h}, fwio.{c,h}, hwio.{c,h}, main.c, pm.{c,h},
    queue.{c,h}, scan.{c,h}, sta.{c,h}, txrx.{c,h}, wsm.{c,h}).
  - Keeps `Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.` as
    the SECOND chain entry where Bestechnic genuinely contributed.
  - Notes "Derived from cw1200_sdio.c" + ST-Ericsson copyright on
    bes2600_sdio.c (heavy derivation, not a literal rename).
  - Notes "Replaces hwbus.h from cw1200/" + ST-Ericsson copyright
    on sbus.h.
  - Preserves the prism54/islsm authorship chain on main.c and
    bes2600.h (Michael Wu 2006 + Jean-Baptiste Note 2004-2006).
  - Drops the GPL-2.0 boilerplate paragraph in favour of SPDX.

No code changes — only file-header comment blocks.  Module build is
unaffected (verified by header-only diff scope).

This is a prerequisite for any kernel.org submission attempt.  The
existing MODULE_LICENSE("GPL") + MODULE_AUTHOR(Tarnyagin@stericsson.com)
declarations were already present and are unchanged here; the
mismatch between MODULE_AUTHOR and the (since-corrected) per-file
copyrights is now resolved.
2026-05-08 00:03:50 +02:00

1944 lines
56 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* AP mode for BES2600 mac80211 driver
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
*
*/
#include "bes2600.h"
#include "sta.h"
#include "ap.h"
#include "bh.h"
#include "net/mac80211.h"
#include <linux/umh.h>
#include "epta_request.h"
#include "epta_coex.h"
#ifdef AP_HT_CAP_UPDATE
#define HT_INFO_OFFSET 4
#define HT_INFO_MASK 0x0011
#define HT_INFO_IE_LEN 22
#endif
#define BES2600_LINK_ID_GC_TIMEOUT ((unsigned long)(10 * HZ))
#define BES2600_ENABLE_ARP_FILTER_OFFLOAD 3
#define BES2600_KEEP_ALIVE_PERIOD (15)
#ifndef ERP_INFO_BYTE_OFFSET
#define ERP_INFO_BYTE_OFFSET 2
#endif
#ifdef IPV6_FILTERING
#define BES2600_ENABLE_NDP_FILTER_OFFLOAD 3
#endif /*IPV6_FILTERING*/
static int bes2600_upload_beacon(struct bes2600_vif *priv);
#ifdef PROBE_RESP_EXTRA_IE
static int bes2600_upload_proberesp(struct bes2600_vif *priv);
#endif
static int bes2600_upload_pspoll(struct bes2600_vif *priv);
static int bes2600_upload_null(struct bes2600_vif *priv);
static int bes2600_upload_qosnull(struct bes2600_vif *priv);
static int bes2600_start_ap(struct bes2600_vif *priv);
static int bes2600_update_beaconing(struct bes2600_vif *priv);
/*
static int bes2600_enable_beaconing(struct bes2600_vif *priv,
bool enable);
*/
static void __bes2600_sta_notify(struct bes2600_vif *priv,
enum sta_notify_cmd notify_cmd,
int link_id);
/* ******************************************************************** */
/* AP API */
int bes2600_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct bes2600_sta_priv *sta_priv =
(struct bes2600_sta_priv *)&sta->drv_priv;
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct bes2600_link_entry *entry;
struct sk_buff *skb;
struct bes2600_common *hw_priv = hw->priv;
#ifdef P2P_MULTIVIF
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
#endif
bes_devel( "%s mode:%u, MFP:%u\n",
__FUNCTION__, priv->mode, sta->mfp);
if (priv->mode == NL80211_IFTYPE_STATION)
priv->pmf = sta->mfp;
if (priv->mode != NL80211_IFTYPE_AP)
return 0;
sta_priv->priv = priv;
sta_priv->link_id = bes2600_find_link_id(priv, sta->addr);
if (WARN_ON(!sta_priv->link_id)) {
/* Impossible error */
wiphy_info(priv->hw->wiphy,
"[AP] 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 = BES2600_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);
WARN_ON(wsm_set_block_ack_policy(hw_priv,
BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
priv->if_id));
wsm_unlock_tx(hw_priv);
}
#endif
return 0;
}
int bes2600_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{
struct bes2600_common *hw_priv = hw->priv;
struct bes2600_sta_priv *sta_priv =
(struct bes2600_sta_priv *)&sta->drv_priv;
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct bes2600_link_entry *entry;
#ifdef P2P_MULTIVIF
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
#endif
bes_devel( "%s mode:%u\n", __FUNCTION__, priv->mode);
if (priv->mode == NL80211_IFTYPE_STATION)
priv->pmf = false;
if (priv->mode != NL80211_IFTYPE_AP || !sta_priv->link_id)
return 0;
entry = &priv->link_id_db[sta_priv->link_id - 1];
spin_lock_bh(&priv->ps_state_lock);
entry->status = BES2600_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);
WARN_ON(wsm_set_block_ack_policy(hw_priv,
BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
priv->if_id));
wsm_unlock_tx(hw_priv);
}
}
#endif
return 0;
}
static void __bes2600_sta_notify(struct bes2600_vif *priv,
enum sta_notify_cmd notify_cmd,
int link_id)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
u32 bit, prev;
/* Zero link id means "for all link IDs" */
if (link_id)
bit = BIT(link_id);
else if (WARN_ON_ONCE(notify_cmd != STA_NOTIFY_AWAKE))
bit = 0;
else
bit = priv->link_id_map;
prev = priv->sta_asleep_mask & bit;
switch (notify_cmd) {
case STA_NOTIFY_SLEEP:
if (!prev) {
if (priv->buffered_multicasts &&
!priv->sta_asleep_mask)
queue_work(hw_priv->workqueue,
&priv->multicast_start_work);
priv->sta_asleep_mask |= bit;
}
break;
case STA_NOTIFY_AWAKE:
if (prev) {
priv->sta_asleep_mask &= ~bit;
priv->pspoll_mask &= ~bit;
if (priv->tx_multicast && link_id &&
!priv->sta_asleep_mask)
queue_work(hw_priv->workqueue,
&priv->multicast_stop_work);
bes2600_bh_wakeup(hw_priv);
}
break;
}
}
void bes2600_sta_notify(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
enum sta_notify_cmd notify_cmd,
struct ieee80211_sta *sta)
{
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct bes2600_sta_priv *sta_priv =
(struct bes2600_sta_priv *)&sta->drv_priv;
#ifdef P2P_MULTIVIF
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
#endif
spin_lock_bh(&priv->ps_state_lock);
__bes2600_sta_notify(priv, notify_cmd, sta_priv->link_id);
spin_unlock_bh(&priv->ps_state_lock);
}
static void bes2600_ps_notify(struct bes2600_vif *priv,
int link_id, bool ps)
{
if (link_id > CW1250_MAX_STA_IN_AP_MODE)
return;
bes_devel("%s for LinkId: %d. STAs asleep: %.8X\n",
ps ? "Stop" : "Start",
link_id, priv->sta_asleep_mask);
/* TODO:COMBO: __bes2600_sta_notify changed. */
__bes2600_sta_notify(priv,
ps ? STA_NOTIFY_SLEEP : STA_NOTIFY_AWAKE, link_id);
}
static int bes2600_set_tim_impl(struct bes2600_vif *priv, bool aid0_bit_set)
{
struct bes2600_common *hw_priv = cw12xx_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;
bes_devel("[AP] %s mcast: %s.\n",
__func__, aid0_bit_set ? "ena" : "dis");
skb = ieee80211_beacon_get_tim(priv->hw, priv->vif,
&tim_offset, &tim_length, 0);
if (!skb) {
if (!__bes2600_flush(hw_priv, true, priv->if_id))
wsm_unlock_tx(hw_priv);
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));
dev_kfree_skb(skb);
return 0;
}
void bes2600_set_tim_work(struct work_struct *work)
{
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, set_tim_work);
(void)bes2600_set_tim_impl(priv, priv->aid0_bit_set);
}
int bes2600_set_tim(struct ieee80211_hw *dev, struct ieee80211_sta *sta,
bool set)
{
struct bes2600_sta_priv *sta_priv =
(struct bes2600_sta_priv *)&sta->drv_priv;
struct bes2600_vif *priv = sta_priv->priv;
#ifdef P2P_MULTIVIF
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
#endif
WARN_ON(priv->mode != NL80211_IFTYPE_AP);
queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
return 0;
}
void bes2600_set_cts_work(struct work_struct *work)
{
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, set_cts_work.work);
struct bes2600_common *hw_priv = cw12xx_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;
down(&hw_priv->conf_lock);
erp_info = priv->erp_info;
up(&hw_priv->conf_lock);
use_cts_prot =
erp_info & WLAN_ERP_USE_PROTECTION ?
__cpu_to_le32(1) : 0;
erp_ie[ERP_INFO_BYTE_OFFSET] = erp_info;
bes_devel("[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 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;
}
static int bes2600_set_btcoexinfo(struct bes2600_vif *priv)
{
struct wsm_override_internal_txrate arg;
int ret = 0;
if (priv->mode == NL80211_IFTYPE_STATION) {
/* Plumb PSPOLL and NULL template */
WARN_ON(bes2600_upload_pspoll(priv));
WARN_ON(bes2600_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) {
bes_devel("[STA] STA has ERP rates\n");
/* G or BG mode */
arg.internalTxRate = (__ffs(
priv->bss_params.operationalRateSet & ~0xF));
} else {
bes_devel("[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));
}
bes_devel("[STA] BTCOEX_INFO"
"MODE %d, internalTxRate : %x, nonErpInternalTxRate: %x\n",
priv->mode,
arg.internalTxRate,
arg.nonErpInternalTxRate);
ret = WARN_ON(wsm_write_mib(cw12xx_vifpriv_to_hwpriv(priv),
WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
&arg, sizeof(arg), priv->if_id));
return ret;
}
void bes2600_bss_info_changed(struct ieee80211_hw *dev,
struct ieee80211_vif *vif,
struct ieee80211_bss_conf *info,
u64 changed)
{
struct bes2600_common *hw_priv = dev->priv;
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct ieee80211_conf *conf = &dev->conf;
const u8 override_fpsm_timeout = BES2600_FASTPS_IDLE_TIME;
struct ieee80211_vif_cfg *cfg = &vif->cfg;
#ifdef P2P_MULTIVIF
if (priv->if_id == CW12XX_GENERIC_IF_ID)
return;
#endif
bes_devel( "BSS CHANGED: %08llx\n", changed);
down(&hw_priv->conf_lock);
if (changed & BSS_CHANGED_BSSID) {
#ifdef CONFIG_BES2600_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_BES2600_TESTMODE*/
memcpy(priv->bssid, info->bssid, ETH_ALEN);
bes2600_setup_mac_pvif(priv);
}
/* TODO: BSS_CHANGED_IBSS */
if (changed & BSS_CHANGED_ARP_FILTER) {
struct wsm_arp_ipv4_filter filter = {0};
int i;
bes_devel("[STA] BSS_CHANGED_ARP_FILTER cnt: %d",
cfg->arp_addr_cnt);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (cfg->arp_addr_cnt > 0) {
bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP);
}
#endif
/* Currently only one IP address is supported by firmware.
* In case of more IPs arp filtering will be disabled. */
if (cfg->arp_addr_cnt > 0 &&
cfg->arp_addr_cnt <= WSM_MAX_ARP_IP_ADDRTABLE_ENTRIES) {
for (i = 0; i < cfg->arp_addr_cnt; i++) {
filter.ipv4Address[i] = cfg->arp_addr_list[i];
bes_devel("[STA] addr[%d]: 0x%X\n",
i, filter.ipv4Address[i]);
}
if (vif->type == NL80211_IFTYPE_STATION)
filter.enable = (u32)BES2600_ENABLE_ARP_FILTER_OFFLOAD;
else if (priv->join_status == BES2600_JOIN_STATUS_AP)
filter.enable = (u32)(1<<1);
else
filter.enable = 0;
} else
filter.enable = 0;
bes_devel("[STA] arp ip filter enable: %d\n",
__le32_to_cpu(filter.enable));
if (filter.enable)
bes2600_set_arpreply(dev, vif);
priv->filter4 = filter;
if (filter.enable &&
(priv->join_status == BES2600_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 (conf->dynamic_ps_timeout >= 0x80)
priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
else
priv->powersave_mode.fastPsmIdlePeriod =
conf->dynamic_ps_timeout << 1;
/* override fast psm idle time */
if (override_fpsm_timeout) {
priv->powersave_mode.fastPsmIdlePeriod =
override_fpsm_timeout << 1;
}
if (priv->setbssparams_done) {
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_FAST_PS;
} else
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
priv->power_set_true = 0;
priv->user_power_set_true = 0;
if(!cfg->ps) {
bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
}
}
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_GET_IP);
}
// #if (defined(IPV6_FILTERING) && LINUX_VERSION_CODE > KERNEL_VERSION(4,19,0))
#if 0
if (changed & BSS_CHANGED_NDP_FILTER) {
struct wsm_ndp_ipv6_filter filter = {0};
int i;
u16 *ipv6addr = NULL;
bes_devel("[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)BES2600_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]);
bes_devel("[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]);
bes_devel("[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)));
}
}
bes_devel("[STA] ndp ip filter enable: %d\n",
__le32_to_cpu(filter.enable));
if (filter.enable)
bes2600_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 == BES2600_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 = bes2600_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*/
if (changed & BSS_CHANGED_BEACON) {
bes_devel("BSS_CHANGED_BEACON\n");
#ifdef HIDDEN_SSID
if (priv->join_status != BES2600_JOIN_STATUS_AP)
{
priv->hidden_ssid = info->hidden_ssid;
priv->ssid_length = info->ssid_len;
memcpy(priv->ssid, info->ssid, info->ssid_len);
}
else
bes_devel("priv->join_status=%d\n",priv->join_status);
#endif
WARN_ON(bes2600_upload_beacon(priv));
WARN_ON(bes2600_update_beaconing(priv));
}
if (changed & BSS_CHANGED_BEACON_ENABLED) {
bes_devel("BSS_CHANGED_BEACON_ENABLED dummy\n");
priv->enable_beacon = info->enable_beacon;
}
if (changed & BSS_CHANGED_BEACON_INT) {
bes_devel("CHANGED_BEACON_INT\n");
/* Restart AP only when connected */
if (priv->join_status == BES2600_JOIN_STATUS_AP)
WARN_ON(bes2600_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 (!cfg->assoc /* && !info->ibss_joined */) {
#ifdef P2P_STA_COEX
priv->cqm_link_loss_count = 400;
priv->cqm_beacon_loss_count = 200;
#else
priv->cqm_link_loss_count = 100;
priv->cqm_beacon_loss_count = 50;
#endif
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 bes2600_vif *tmp_priv;
bes_devel( "BSS_CHANGED_ASSOC assoc: %d.\n", cfg->assoc);
if (cfg->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 */
if(changed & BSS_CHANGED_ASSOC) {
cancel_delayed_work_sync(&priv->join_timeout);
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_GET_IP);
#ifdef BES2600_TX_RX_OPT
txrx_opt_timer_init(priv);
#endif
}
rcu_read_lock();
if (info->bssid)
sta = ieee80211_find_sta(vif, info->bssid);
if (sta) {
/* TODO:COMBO:Change this once
* mac80211 changes are available */
enum nl80211_channel_type ch_type;
BUG_ON(!hw_priv->channel);
hw_priv->ht_info.ht_cap = sta->deflink.ht_cap;
priv->bss_params.operationalRateSet =
__cpu_to_le32(
bes2600_rate_mask_to_wsm(hw_priv,
sta->deflink.supp_rates[hw_priv->channel->band]));
rcu_read_unlock();
ch_type = cfg80211_get_chandef_type(&info->chanreq.oper);
bes_devel("[STA] ch %d, type: %d, HT oper mode: %d\n",
hw_priv->channel->hw_value,
ch_type, info->ht_operation_mode);
if ((changed & BSS_CHANGED_ASSOC) &&
priv->join_status == BES2600_JOIN_STATUS_STA) {
struct wsm_switch_channel channel;
hw_priv->ht_info.channel_type = ch_type;
channel.channelMode = hw_priv->ht_info.channel_type << 4;
channel.channelSwitchCount = 0;
channel.newChannelNumber = hw_priv->channel->hw_value;
wsm_switch_channel(hw_priv, &channel, priv->if_id);
bes_devel( "channel type changed to %d !!!\n", hw_priv->ht_info.channel_type);
}
if (hw_priv->ht_info.operation_mode != info->ht_operation_mode)
hw_priv->ht_info.operation_mode = info->ht_operation_mode;
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (changed & BSS_CHANGED_ASSOC) {
if (hw_priv->channel->band != NL80211_BAND_2GHZ)
bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTED_5G);
else
bwifi_change_current_status(hw_priv, BWIFI_STATUS_CONNECTED);
}
#endif
} else {
rcu_read_unlock();
memset(&hw_priv->ht_info, 0, sizeof(hw_priv->ht_info));
priv->bss_params.operationalRateSet = -1;
}
//rcu_read_unlock();
priv->htcap = (sta && bes2600_is_ht(&hw_priv->ht_info));
bes2600_for_each_vif(hw_priv, tmp_priv, i) {
#ifdef P2P_MULTIVIF
if ((i == (CW12XX_MAX_VIFS - 1)) || !tmp_priv)
#else
if (!tmp_priv)
#endif
continue;
if (tmp_priv->join_status >= BES2600_JOIN_STATUS_STA)
is_combo++;
}
if (is_combo > 1) {
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
bes_devel( "ASSOC is_combo %d\n",hw_priv->vif0_throttle);
} else if ((priv->join_status == BES2600_JOIN_STATUS_STA) &&
priv->htcap) {
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE;
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE;
bes_devel( "ASSOC HTCAP 11N %d\n",hw_priv->vif0_throttle);
} else {
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
bes_devel( "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) {
bes_devel( "[STA]"
" Non-GF STA present\n");
/* Non Green field capable STA */
val = __cpu_to_le32(BIT(1));
}
WARN_ON(wsm_write_mib(hw_priv,
WSM_MID_ID_SET_HT_PROTECTION,
&val, sizeof(val), priv->if_id));
}
priv->association_mode.greenfieldMode =
bes2600_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(
bes2600_rate_mask_to_wsm(hw_priv,
info->basic_rates));
priv->association_mode.mpduStartSpacing =
bes2600_ht_ampdu_density(&hw_priv->ht_info);
#if defined(CONFIG_BES2600_USE_STE_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_BES2600_USE_STE_EXTENSIONS */
priv->bss_params.beaconLostCount =
priv->cqm_beacon_loss_count ?
priv->cqm_beacon_loss_count :
priv->cqm_link_loss_count;
priv->bss_params.aid = cfg->aid;
if (priv->join_dtim_period < 1)
priv->join_dtim_period = 1;
bes_devel("[STA] DTIM %d, interval: %d\n",
priv->join_dtim_period, priv->beacon_int);
bes_devel("[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);
WARN_ON(wsm_set_association_mode(hw_priv,
&priv->association_mode, priv->if_id));
WARN_ON(wsm_keep_alive_period(hw_priv,
BES2600_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));
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_mask, hw_priv->ba_tid_mask,
priv->if_id));
wsm_unlock_tx(hw_priv);
}
if (priv->vif->p2p) {
bes_devel(
"[STA] Setting p2p powersave "
"configuration.\n");
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
&priv->p2p_ps_modeinfo, priv->if_id));
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
bes2600_notify_noa(priv, BES2600_NOA_NOTIFICATION_DELAY);
#endif
}
if (priv->mode == NL80211_IFTYPE_STATION)
WARN_ON(bes2600_upload_qosnull(priv));
if (hw_priv->is_BT_Present)
WARN_ON(bes2600_set_btcoexinfo(priv));
atomic_set(&priv->connect_in_process, 0);
if (priv->delayed_unjoin) {
priv->delayed_unjoin = false;
bes_devel( "queue delayed unjoin work for connect\n");
if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
wsm_unlock_tx(hw_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));
WARN_ON(wsm_write_mib(priv,
WSM_MIB_ID_OVERRIDE_INTERNAL_TX_RATE,
&internal_tx_rate,
sizeof(internal_tx_rate)));
#endif
} else {
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (changed & BSS_CHANGED_ASSOC)
bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTED);
#endif
memset(&hw_priv->ht_info, 0,
sizeof(hw_priv->ht_info));
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 == BES2600_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);
bes_devel("[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 (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 */
bes_devel("[CQM] RSSI threshold "
"subscribe: %d +- %d\n",
info->cqm_rssi_thold, info->cqm_rssi_hyst);
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
bes_devel("[CQM] Beacon loss subscribe: %d\n",
info->cqm_beacon_miss_thold);
bes_devel("[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;
#endif /* CONFIG_BES2600_USE_STE_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) * 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));
#if defined(CONFIG_BES2600_USE_STE_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) {
WARN_ON(wsm_set_bss_params(hw_priv,
&priv->bss_params,
priv->if_id));
priv->setbssparams_done = true;
}
}
#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
}
if (changed & BSS_CHANGED_BANDWIDTH) {
enum nl80211_channel_type ch_type = cfg80211_get_chandef_type(&info->chanreq.oper);
if (cfg->assoc &&
hw_priv->ht_info.channel_type != ch_type &&
priv->join_status == BES2600_JOIN_STATUS_STA) {
struct wsm_switch_channel channel;
hw_priv->ht_info.channel_type = ch_type;
channel.channelMode = hw_priv->ht_info.channel_type << 4;
channel.channelSwitchCount = 0;
channel.newChannelNumber = hw_priv->channel->hw_value;
wsm_switch_channel(hw_priv, &channel, priv->if_id);
bes_devel( "channel type changed to %d !!!\n", hw_priv->ht_info.channel_type);
}
}
if (changed & BSS_CHANGED_PS) {
if (cfg->ps == false)
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
else if (conf->dynamic_ps_timeout <= 0)
priv->powersave_mode.pmMode = WSM_PSM_PS;
else
priv->powersave_mode.pmMode = WSM_PSM_FAST_PS;
/* set/clear ps active power busy event */
if(priv->join_status == BES2600_JOIN_STATUS_STA) {
if(!cfg->ps) {
bes2600_pwr_set_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
} else {
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
}
}
bes_devel( "[STA] Aid: %d, Joined: %s, Powersave: %s\n",
priv->bss_params.aid,
priv->join_status == BES2600_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 (conf->dynamic_ps_timeout >= 0x80)
priv->powersave_mode.fastPsmIdlePeriod = 0xFF;
else
priv->powersave_mode.fastPsmIdlePeriod =
conf->dynamic_ps_timeout << 1;
/* override fast psm idle time */
if (override_fpsm_timeout) {
priv->powersave_mode.fastPsmIdlePeriod =
override_fpsm_timeout << 1;
}
if (priv->join_status == BES2600_JOIN_STATUS_STA &&
priv->bss_params.aid &&
priv->setbssparams_done &&
priv->filter4.enable &&
priv->powersave_mode.pmMode == WSM_PSM_ACTIVE) {
bes2600_set_pm(priv, &priv->powersave_mode);
} else {
priv->power_set_true = 1;
}
}
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
if (changed & BSS_CHANGED_P2P_PS) {
struct wsm_p2p_ps_modeinfo *modeinfo;
modeinfo = &priv->p2p_ps_modeinfo;
bes_devel("[AP] BSS_CHANGED_P2P_PS\n");
bes_devel("[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 == BES2600_JOIN_STATUS_STA)
bes2600_set_pm(priv, &priv->powersave_mode);
}
bes_devel("[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;
bes_devel("[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;
}
bes_devel("[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 == BES2600_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_BES2600_STA_DEBUG)
print_hex_dump_bytes("p2p_set_ps_modeinfo: ",
DUMP_PREFIX_NONE,
(u8 *)modeinfo,
sizeof(*modeinfo));
#endif /* CONFIG_BES2600_STA_DEBUG */
if (priv->join_status == BES2600_JOIN_STATUS_STA ||
priv->join_status == BES2600_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 */
bes2600_notify_noa(priv, 10);
}
#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
up(&hw_priv->conf_lock);
}
void bes2600_multicast_start_work(struct work_struct *work)
{
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, multicast_start_work);
long tmo = priv->join_dtim_period *
(priv->beacon_int + 20) * HZ / 1024;
cancel_work_sync(&priv->multicast_stop_work);
if (!priv->aid0_bit_set) {
wsm_lock_tx(priv->hw_priv);
bes2600_set_tim_impl(priv, true);
priv->aid0_bit_set = true;
mod_timer(&priv->mcast_timeout, jiffies + tmo);
wsm_unlock_tx(priv->hw_priv);
}
}
void bes2600_multicast_stop_work(struct work_struct *work)
{
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, multicast_stop_work);
if (priv->aid0_bit_set) {
del_timer_sync(&priv->mcast_timeout);
wsm_lock_tx(priv->hw_priv);
priv->aid0_bit_set = false;
bes2600_set_tim_impl(priv, false);
wsm_unlock_tx(priv->hw_priv);
}
}
void bes2600_mcast_timeout(struct timer_list *t)
{
struct bes2600_vif *priv = from_timer(priv, t, mcast_timeout);
wiphy_warn(priv->hw->wiphy,
"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)
bes2600_bh_wakeup(cw12xx_vifpriv_to_hwpriv(priv));
spin_unlock_bh(&priv->ps_state_lock);
}
int bes2600_ampdu_action(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_ampdu_params *params)
{
/* Aggregation is implemented fully in firmware,
* including block ack negotiation.
* In case of AMPDU aggregation in RX direction
* re-ordering of packets takes place on host. mac80211
* needs the ADDBA Request to setup reodering.mac80211 also
* sends ADDBA Response which is discarded in the driver as
* FW generates the ADDBA Response on its own.*/
int ret;
switch (params->action) {
case IEEE80211_AMPDU_RX_START:
case IEEE80211_AMPDU_RX_STOP:
/* Just return OK to mac80211 */
ret = 0;
break;
default:
ret = -ENOTSUPP;
}
return ret;
}
/* ******************************************************************** */
/* WSM callback */
void bes2600_suspend_resume(struct bes2600_vif *priv,
struct wsm_suspend_resume *arg)
{
struct bes2600_common *hw_priv =
cw12xx_vifpriv_to_hwpriv(priv);
bes_devel("[AP] %s: %s\n",
arg->stop ? "stop" : "start",
arg->multicast ? "broadcast" : "unicast");
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. */
u32 timeout = priv->join_dtim_period *
(priv->beacon_int + 20) * HZ / 1024;
timeout = jiffies_to_msecs(timeout);
bes2600_pwr_set_busy_event_with_timeout_async(hw_priv,
BES_PWR_LOCK_ON_MUL_REQ, timeout);
priv->tx_multicast = priv->aid0_bit_set &&
priv->buffered_multicasts;
if (priv->tx_multicast) {
cancel_tmo = true;
bes2600_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);
bes2600_ps_notify(priv, arg->link_id, arg->stop);
spin_unlock_bh(&priv->ps_state_lock);
if (!arg->stop)
bes2600_bh_wakeup(hw_priv);
}
return;
}
/* ******************************************************************** */
/* AP privates */
static int bes2600_upload_beacon(struct bes2600_vif *priv)
{
int ret = 0;
struct bes2600_common *hw_priv = cw12xx_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;
if (priv->vif->p2p ||
hw_priv->channel->band == NL80211_BAND_5GHZ)
frame.rate = WSM_TRANSMIT_RATE_6;
frame.skb = ieee80211_beacon_get(priv->hw, priv->vif, 0);
if (WARN_ON(!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_OPERATION, 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;
bes_devel("%s: hidden_ssid set\n", __func__);
ssid_ie = (u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
WARN_ON(!ssid_ie);
ssid_len = ssid_ie[1];
if (ssid_len) {
bes_devel("%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 {
pbes_devel("%s: hidden ssid with ssid len 0"
" not supported", __func__);
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 = bes2600_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);
WARN_ON(wsm_set_probe_responder(priv, false));
}
#endif
}
dev_kfree_skb(frame.skb);
return ret;
}
#ifdef PROBE_RESP_EXTRA_IE
static int bes2600_upload_proberesp(struct bes2600_vif *priv)
{
int ret = 0;
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_RESPONSE,
};
#ifdef HIDDEN_SSID
u8 *ssid_ie;
#endif
if (priv->vif->p2p || hw_priv->channel->band == NL80211_BAND_5GHZ)
frame.rate = WSM_TRANSMIT_RATE_6;
frame.skb = ieee80211_proberesp_get(priv->hw, priv->vif);
if (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 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 {
bes_devel("%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);
WARN_ON(wsm_set_probe_responder(priv, false));
dev_kfree_skb(frame.skb);
return ret;
}
#endif
static int bes2600_upload_pspoll(struct bes2600_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(cw12xx_vifpriv_to_hwpriv(priv), &frame,
priv->if_id);
dev_kfree_skb(frame.skb);
return ret;
}
static int bes2600_upload_null(struct bes2600_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, 0, false);
if (WARN_ON(!frame.skb))
return -ENOMEM;
ret = wsm_set_template_frame(cw12xx_vifpriv_to_hwpriv(priv),
&frame, priv->if_id);
dev_kfree_skb(frame.skb);
return ret;
}
static int bes2600_upload_qosnull(struct bes2600_vif *priv)
{
int ret = 0;
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_QOS_NULL,
.rate = 0xFF,
};
frame.skb = ieee80211_nullfunc_get(priv->hw, priv->vif, 0, false);
if (WARN_ON(!frame.skb))
return -ENOMEM;
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 bes2600_enable_beaconing(struct bes2600_vif *priv,
bool enable)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct wsm_beacon_transmit transmit = {
.enableBeaconing = enable,
};
return wsm_beacon_transmit(hw_priv, &transmit, priv->if_id);
}
#endif
static int start_dhcpd(void)
{
#if 0
volatile int result=0;
char cmdPath[]="/bin/bash";
char cmdText[128];
char* cmdArgv[]={cmdPath, "-c", cmdText, NULL};
char* cmdArgv2[]={cmdPath, "-c", "/usr/sbin/dhcpd >> /tmp/log", NULL};
char* cmdEnvp[]={"HOME=/", "PATH=/sbin:/bin:/usr/bin:/usr/sbin", NULL};
static int ifname_id = 0;
snprintf(cmdText, 128, "/sbin/ifconfig p2p-wlan0-%d 192.168.50.1/24 up >> /tmp/log2", ifname_id);
ifname_id++;
result=call_usermodehelper(cmdPath,cmdArgv,cmdEnvp, UMH_WAIT_EXEC);
msleep(100);
result=call_usermodehelper(cmdPath,cmdArgv2,cmdEnvp, UMH_WAIT_EXEC);
//msleep(100);
//result=call_usermodehelper(cmdPath,cmdArgv3,cmdEnvp, UMH_WAIT_EXEC);
#else
volatile int result=0;
char cmdPath[]="/bin/bash";
char* cmdArgv[]={cmdPath, "-c", "/usr/bin/start_dhcpd > /tmp/log", NULL};
char* cmdEnvp[]={"HOME=/", "PATH=/sbin:/bin:/usr/bin:/usr/sbin", NULL};
result=call_usermodehelper(cmdPath,cmdArgv,cmdEnvp, UMH_WAIT_EXEC);
#endif
bes_devel( "testDriver1 _init exec! The result of call_usermodehelper is %d\n",result);
bes_devel( "testDriver1 _init exec! The process is \"%s\",pid is %d\n",current->comm,current->pid);
return 0;
}
static int bes2600_start_ap(struct bes2600_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 bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct wsm_start start = {
.mode = priv->vif->p2p ?
WSM_START_MODE_P2P_GO : WSM_START_MODE_AP,
/* TODO:COMBO:Change once mac80211 support is available */
.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ?
WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
.channelNumber = hw_priv->channel->hw_value,
.beaconInterval = conf->beacon_int,
.DTIMPeriod = conf->dtim_period,
.preambleType = conf->use_short_preamble ?
WSM_JOIN_PREAMBLE_SHORT :
WSM_JOIN_PREAMBLE_LONG,
.probeDelay = 100,
.basicRateSet = bes2600_rate_mask_to_wsm(hw_priv,
conf->basic_rates),
#ifdef P2P_MULTIVIF
.CTWindow = priv->if_id ? 0xFFFFFFFF : 0, /* it makes addr1's byte4 ^0x80 in firmware,
which matches with driver's addr2 */
#endif
};
struct wsm_switch_channel channel = {
.channelMode = (cfg80211_get_chandef_type(&conf->chanreq.oper)) << 4,
.channelSwitchCount = 0,
.newChannelNumber = hw_priv->channel->hw_value,
};
struct wsm_inactivity inactivity = {
.min_inactivity = 9,
.max_inactivity = 1,
};
if (priv->vif->p2p) {
start_dhcpd();
hw_priv->ht_info.channel_type = cfg80211_get_chandef_type(&conf->chanreq.oper);
}
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, 0);
if (WARN_ON(!skb))
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 (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;
}
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));
bes_devel( "[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));
if (!ret)
ret = wsm_switch_channel(hw_priv, &channel, priv->if_id);
if (!ret && priv->vif->p2p) {
bes_devel("[AP] Setting p2p powersave "
"configuration.\n");
WARN_ON(wsm_set_p2p_ps_modeinfo(hw_priv,
&priv->p2p_ps_modeinfo, priv->if_id));
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
bes2600_notify_noa(priv, BES2600_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
WARN_ON(wsm_set_block_ack_policy(hw_priv,
BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID, priv->if_id));
#else
if ((priv->if_id ==2) && !hw_priv->is_go_thru_go_neg)
WARN_ON(wsm_set_block_ack_policy(hw_priv,
BES2600_TX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
BES2600_RX_BLOCK_ACK_DISABLED_FOR_ALL_TID,
priv->if_id));
else
WARN_ON(wsm_set_block_ack_policy(hw_priv,
BES2600_TX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
BES2600_RX_BLOCK_ACK_ENABLED_FOR_ALL_TID,
priv->if_id));
#endif
priv->join_status = BES2600_JOIN_STATUS_AP;
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_AP);
/* bes2600_update_filtering(priv); */
}
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
bes_devel( "AP/GO mode BG THROTTLE %d\n", hw_priv->vif0_throttle);
return ret;
}
static int bes2600_update_beaconing(struct bes2600_vif *priv)
{
struct ieee80211_bss_conf *conf = &priv->vif->bss_conf;
struct bes2600_common *hw_priv = cw12xx_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 != BES2600_JOIN_STATUS_AP ||
priv->beacon_int != conf->beacon_int) {
bes_devel("ap restarting\n");
wsm_lock_tx(hw_priv);
if (priv->join_status != BES2600_JOIN_STATUS_PASSIVE)
WARN_ON(wsm_reset(hw_priv, &reset,
priv->if_id));
priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
WARN_ON(bes2600_start_ap(priv));
wsm_unlock_tx(hw_priv);
} else
bes_devel("ap started join_status: %d\n",
priv->join_status);
}
return 0;
}
int bes2600_find_link_id(struct bes2600_vif *priv, const u8 *mac)
{
int i, ret = 0;
spin_lock_bh(&priv->ps_state_lock);
for (i = 0; i < CW1250_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 bes2600_alloc_link_id(struct bes2600_vif *priv, const u8 *mac)
{
int i, ret = 0;
unsigned long max_inactivity = 0;
unsigned long now = jiffies;
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
spin_lock_bh(&priv->ps_state_lock);
for (i = 0; i < CW1250_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 != BES2600_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 bes2600_link_entry *entry = &priv->link_id_db[ret - 1];
bes_devel("[AP] STA added, link_id: %d\n",
ret);
entry->status = BES2600_LINK_RESERVE;
memcpy(&entry->mac, mac, ETH_ALEN);
memset(&entry->buffered, 0, BES2600_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 {
wiphy_info(priv->hw->wiphy,
"[AP] Early: no more link IDs available.\n");
}
spin_unlock_bh(&priv->ps_state_lock);
return ret;
}
void bes2600_link_id_work(struct work_struct *work)
{
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, link_id_work);
struct bes2600_common *hw_priv = priv->hw_priv;
wsm_flush_tx(hw_priv);
bes2600_link_id_gc_work(&priv->link_id_gc_work.work);
wsm_unlock_tx(hw_priv);
}
void bes2600_link_id_gc_work(struct work_struct *work)
{
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, link_id_gc_work.work);
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct wsm_map_link map_link = {
.link_id = 0,
};
unsigned long now = jiffies;
unsigned long next_gc = -1;
long ttl;
bool need_reset;
u32 mask;
int i;
if (priv->join_status != BES2600_JOIN_STATUS_AP)
return;
wsm_lock_tx(hw_priv);
spin_lock_bh(&priv->ps_state_lock);
for (i = 0; i < CW1250_MAX_STA_IN_AP_MODE; ++i) {
need_reset = false;
mask = BIT(i + 1);
if (priv->link_id_db[i].status == BES2600_LINK_RESERVE ||
(priv->link_id_db[i].status == BES2600_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 != BES2600_LINK_HARD)
priv->link_id_db[i].status = BES2600_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) {
WARN_ON(cw12xx_unmap_link(priv, i + 1));
}
map_link.link_id = i + 1;
WARN_ON(wsm_map_link(hw_priv, &map_link, priv->if_id));
next_gc = min(next_gc, BES2600_LINK_ID_GC_TIMEOUT);
spin_lock_bh(&priv->ps_state_lock);
} else if (priv->link_id_db[i].status == BES2600_LINK_SOFT) {
ttl = priv->link_id_db[i].timestamp - now +
BES2600_LINK_ID_GC_TIMEOUT;
if (ttl <= 0) {
need_reset = true;
priv->link_id_db[i].status = BES2600_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);
WARN_ON(cw12xx_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_BES2600_USE_STE_EXTENSIONS)
} else if (priv->link_id_db[i].status == BES2600_LINK_RESET ||
priv->link_id_db[i].status ==
BES2600_LINK_RESET_REMAP) {
int status = priv->link_id_db[i].status;
priv->link_id_db[i].status = BES2600_LINK_OFF;
priv->link_id_db[i].timestamp = now;
spin_unlock_bh(&priv->ps_state_lock);
WARN_ON(cw12xx_unmap_link(priv, i + 1));
if (status == BES2600_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,
BES2600_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);
bes_devel("[AP] 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_BES2600_USE_STE_EXTENSIONS)
void bes2600_notify_noa(struct bes2600_vif *priv, int delay)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct cfg80211_p2p_ps p2p_ps = {0};
struct wsm_p2p_ps_modeinfo *modeinfo;
modeinfo = &priv->p2p_ps_modeinfo;
bes_devel("[AP]: %s called\n", __func__);
if (priv->join_status != BES2600_JOIN_STATUS_AP)
return;
if (delay)
msleep(delay);
if (!WARN_ON(wsm_get_p2p_ps_modeinfo(hw_priv, modeinfo))) {
#if defined(CONFIG_BES2600_STA_DEBUG)
print_hex_dump_bytes("[AP] p2p_get_ps_modeinfo: ",
DUMP_PREFIX_NONE,
(u8 *)modeinfo,
sizeof(*modeinfo));
#endif /* CONFIG_BES2600_STA_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
int cw12xx_unmap_link(struct bes2600_vif *priv, int link_id)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
int ret = 0;
if (is_hardware_cw1250(hw_priv) || is_hardware_cw1260(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);
return ret;
}
}
#ifdef AP_HT_CAP_UPDATE
void bes2600_ht_info_update_work(struct work_struct *work)
{
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
u8 *ht_info, *ies;
u32 ies_len;
struct bes2600_vif *priv =
container_of(work, struct bes2600_vif, ht_info_update_work);
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
struct wsm_update_ie update_ie = {
.what = WSM_UPDATE_IE_BEACON,
.count = 1,
};
skb = ieee80211_beacon_get(priv->hw, priv->vif, 0);
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 *)cfg80211_find_ie( WLAN_EID_HT_OPERATION, 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));
}
dev_kfree_skb(skb);
}
#endif