9012b74eea
The driver implements a mac80211 testmode_cmd operation that dispatches
to a set of vendor commands (GET_TX_POWER_LEVEL, GET_TX_POWER_RANGE,
SET_SNAP_FRAME, TSM_STATS, GET_ROAM_DELAY, GET_STREAM, etc) plus the
BES2600 RF-test path (bes2600_vendor_rf_cmd → firmware
patch_wifi_testMode). The testmode handlers and the .testmode_cmd
binding in struct ieee80211_ops are conditionally compiled under
CONFIG_BES2600_TESTMODE, which previously defaulted to n.
Flip the Makefile default from n to y so wifi_testmode_cmd.o is
included in the build and the .testmode_cmd op is populated. On the
PineTab2 target kernel (linux-pinetab2 6.19.10-danctnix1, built with
CONFIG_NL80211_TESTMODE=y) this exposes the BES2600 RF-test surface
through the standard nl80211 testmode interface ('iw phy0 ...').
This also makes visible two classes of bit-rot that had accumulated
while nobody was building with CONFIG_BES2600_TESTMODE=y:
1. sta.c contains ~41 calls to bes2600_info() / bes2600_err() /
bes2600_warn() / bes2600_dbg() / bes2600_err_with_cond() - a
legacy log-macro family carrying a BES2600_DBG_* subsystem-id
first argument. Neither the macros nor any of the BES2600_DBG_*
constants are defined anywhere in the tree. The same call pattern
appears under #if defined(BES2600_DETECTION_LOGIC) in hwio.c and
under CONFIG_BES2600_ITP in itp.c, both normally disabled.
Add minimal shim macros to bes_log.h that rewire the calls onto
the existing bes_info() / bes_err() / bes_warn() / bes_devel()
family (ignoring the subsystem id). Define BES2600_DBG_SBUS,
BES2600_DBG_DOWNLOAD, BES2600_DBG_ITP and BES2600_DBG_TEST_MODE
as 0 constants for documentation / grep.
2. bes2600_start_stop_tsm(), bes2600_get_tsm_params(), and
bes2600_get_roam_delay() are declared in sta.c with external
linkage but have no prototype in any header. All callers live in
sta.c (inside bes2600_testmode_cmd). With CONFIG_BES2600_TESTMODE
off the compiler never sees them; with it on gcc
-Werror=missing-prototypes breaks the build.
Mark the three functions static. (Keeping them file-local also
matches their actual usage.)
Both changes are strictly scoped to make CONFIG_BES2600_TESTMODE=y
buildable; no behavioural change when the flag is off.
Tested-on: PineTab2 (BES2600WM + RK3566) running linux-pinetab2
6.19.10-danctnix1-1 with CONFIG_NL80211_TESTMODE=y. Module builds
cleanly, nl80211 testmode interface reachable via 'iw phy0 ...' from
userspace.
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
4482 lines
131 KiB
C
4482 lines
131 KiB
C
/*
|
|
* Mac80211 STA API for BES2600 drivers
|
|
*
|
|
* Copyright (c) 2022, Bestechnic
|
|
* Author:
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/if_arp.h>
|
|
#include <linux/ipv6.h>
|
|
#include <linux/icmpv6.h>
|
|
#include <net/ndisc.h>
|
|
|
|
#include "bes2600.h"
|
|
#include "sta.h"
|
|
#include "ap.h"
|
|
#include "fwio.h"
|
|
#include "bh.h"
|
|
#include "debug.h"
|
|
#include "wsm.h"
|
|
#ifdef ROAM_OFFLOAD
|
|
#include <net/netlink.h>
|
|
#endif /*ROAM_OFFLOAD*/
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
#include "bes_nl80211_testmode_msg.h"
|
|
#include <net/netlink.h>
|
|
#endif /* CONFIG_BES2600_TESTMODE */
|
|
#include "net/mac80211.h"
|
|
#include "bes_chardev.h"
|
|
#include "bes_log.h"
|
|
|
|
#include "epta_request.h"
|
|
#include "epta_coex.h"
|
|
|
|
#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
|
#include "bes2600_factory.h"
|
|
#endif
|
|
|
|
#define WEP_ENCRYPT_HDR_SIZE 4
|
|
#define WEP_ENCRYPT_TAIL_SIZE 4
|
|
#define WPA_ENCRYPT_HDR_SIZE 8
|
|
#define WPA_ENCRYPT_TAIL_SIZE 12
|
|
#define WPA2_ENCRYPT_HDR_SIZE 8
|
|
#define WPA2_ENCRYPT_TAIL_SIZE 8
|
|
#define WAPI_ENCRYPT_HDR_SIZE 18
|
|
#define WAPI_ENCRYPT_TAIL_SIZE 16
|
|
#define MAX_ARP_REPLY_TEMPLATE_SIZE 120
|
|
#define MAX_TCP_ALIVE_TEMPLATE_SIZE 256
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
const int bes2600_1d_to_ac[8] = {
|
|
IEEE80211_AC_BE,
|
|
IEEE80211_AC_BK,
|
|
IEEE80211_AC_BK,
|
|
IEEE80211_AC_BE,
|
|
IEEE80211_AC_VI,
|
|
IEEE80211_AC_VI,
|
|
IEEE80211_AC_VO,
|
|
IEEE80211_AC_VO
|
|
};
|
|
|
|
/**
|
|
* enum bes2600_ac_numbers - AC numbers as used in bes2600
|
|
* @BES2600_AC_VO: voice
|
|
* @BES2600_AC_VI: video
|
|
* @BES2600_AC_BE: best effort
|
|
* @BES2600_AC_BK: background
|
|
*/
|
|
enum bes2600_ac_numbers {
|
|
BES2600_AC_VO = 0,
|
|
BES2600_AC_VI = 1,
|
|
BES2600_AC_BE = 2,
|
|
BES2600_AC_BK = 3,
|
|
};
|
|
|
|
int bes2600_testmode_reply(struct wiphy *wiphy, const void *data, int len);
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
|
|
#ifdef IPV6_FILTERING
|
|
#define MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE 144
|
|
#endif /*IPV6_FILTERING*/
|
|
|
|
static inline void __bes2600_free_event_queue(struct list_head *list)
|
|
{
|
|
while (!list_empty(list)) {
|
|
struct bes2600_wsm_event *event =
|
|
list_first_entry(list, struct bes2600_wsm_event,
|
|
link);
|
|
list_del(&event->link);
|
|
kfree(event);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
/* User priority to WSM queue mapping */
|
|
const int bes2600_priority_to_queueId[8] = {
|
|
WSM_QUEUE_BEST_EFFORT,
|
|
WSM_QUEUE_BACKGROUND,
|
|
WSM_QUEUE_BACKGROUND,
|
|
WSM_QUEUE_BEST_EFFORT,
|
|
WSM_QUEUE_VIDEO,
|
|
WSM_QUEUE_VIDEO,
|
|
WSM_QUEUE_VOICE,
|
|
WSM_QUEUE_VOICE
|
|
};
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
static inline void __bes2600_bf_configure(struct bes2600_vif *priv)
|
|
{
|
|
priv->bf_table.numOfIEs = __cpu_to_le32(3);
|
|
priv->bf_table.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC;
|
|
priv->bf_table.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
|
|
WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
|
|
WSM_BEACON_FILTER_IE_HAS_APPEARED;
|
|
priv->bf_table.entry[0].oui[0] = 0x50;
|
|
priv->bf_table.entry[0].oui[1] = 0x6F;
|
|
priv->bf_table.entry[0].oui[2] = 0x9A;
|
|
|
|
priv->bf_table.entry[1].ieId = WLAN_EID_ERP_INFO;
|
|
priv->bf_table.entry[1].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
|
|
WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
|
|
WSM_BEACON_FILTER_IE_HAS_APPEARED;
|
|
|
|
priv->bf_table.entry[2].ieId = WLAN_EID_HT_OPERATION;
|
|
priv->bf_table.entry[2].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
|
|
WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
|
|
WSM_BEACON_FILTER_IE_HAS_APPEARED;
|
|
|
|
priv->bf_control.enabled = __cpu_to_le32(WSM_BEACON_FILTER_ENABLE);
|
|
}
|
|
|
|
/* ******************************************************************** */
|
|
/* STA API */
|
|
|
|
int bes2600_start(struct ieee80211_hw *dev)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
int ret = 0;
|
|
|
|
bes_devel("bes2600_start\n");
|
|
|
|
if (!bes2600_chrdev_is_signal_mode()) {
|
|
return -1;
|
|
}
|
|
|
|
bes2600_pwr_prepare(hw_priv);
|
|
|
|
/* Assign Max SSIDs supported based on the firmware revision*/
|
|
if (hw_priv->hw_revision <= BES2600_HW_REV_CUT22) {
|
|
dev->wiphy->max_scan_ssids = 2; /* for backward compatibility */
|
|
}
|
|
down(&hw_priv->conf_lock);
|
|
|
|
ret = bes2600_wifi_start(hw_priv);
|
|
if (ret)
|
|
goto out;
|
|
|
|
tx_policy_init(hw_priv);
|
|
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
spin_lock_bh(&hw_priv->tsm_lock);
|
|
memset(&hw_priv->tsm_stats, 0, sizeof(struct bes_tsm_stats));
|
|
memset(&hw_priv->tsm_info, 0, sizeof(struct bes2600_tsm_info));
|
|
spin_unlock_bh(&hw_priv->tsm_lock);
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
memcpy(hw_priv->mac_addr, dev->wiphy->perm_addr, ETH_ALEN);
|
|
|
|
atomic_inc(&hw_priv->netdevice_start);
|
|
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
|
coex_start(hw_priv);
|
|
#endif
|
|
|
|
bes_devel("%s %pM.\n", __func__, hw_priv->mac_addr);
|
|
ret = bes2600_setup_mac(hw_priv);
|
|
if (WARN_ON(ret))
|
|
goto out;
|
|
|
|
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
|
bwifi_change_current_status(hw_priv, BWIFI_STATUS_IDLE);
|
|
#endif
|
|
|
|
out:
|
|
up(&hw_priv->conf_lock);
|
|
if(ret != 0)
|
|
bes2600_pwr_complete(hw_priv);
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_stop(struct ieee80211_hw *dev, bool suspend)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
struct bes2600_vif *priv = NULL;
|
|
LIST_HEAD(list);
|
|
int i;
|
|
|
|
bes_devel("bes2600_stop\n");
|
|
|
|
atomic_dec(&hw_priv->netdevice_start);
|
|
|
|
wsm_lock_tx(hw_priv);
|
|
|
|
while (down_trylock(&hw_priv->scan.lock)) {
|
|
/* Scan is in progress. Force it to stop. */
|
|
hw_priv->scan.req = NULL;
|
|
schedule();
|
|
}
|
|
up(&hw_priv->scan.lock);
|
|
|
|
cancel_delayed_work_sync(&hw_priv->scan.probe_work);
|
|
cancel_delayed_work_sync(&hw_priv->scan.timeout);
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
cancel_delayed_work_sync(&hw_priv->advance_scan_timeout);
|
|
#endif
|
|
flush_workqueue(hw_priv->workqueue);
|
|
del_timer_sync(&hw_priv->ba_timer);
|
|
|
|
down(&hw_priv->conf_lock);
|
|
|
|
spin_lock(&hw_priv->event_queue_lock);
|
|
list_splice_init(&hw_priv->event_queue, &list);
|
|
spin_unlock(&hw_priv->event_queue_lock);
|
|
__bes2600_free_event_queue(&list);
|
|
|
|
spin_lock(&hw_priv->vif_list_lock);
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
if (!priv)
|
|
continue;
|
|
if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
|
|
return;
|
|
/* protect tx confirm flow */
|
|
spin_lock(&priv->vif_lock);
|
|
for (i = 0; i < 4; i++)
|
|
bes2600_queue_clear(&hw_priv->tx_queue[i], i);
|
|
spin_unlock(&priv->vif_lock);
|
|
}
|
|
spin_unlock(&hw_priv->vif_list_lock);
|
|
|
|
|
|
/* HACK! */
|
|
if (atomic_xchg(&hw_priv->tx_lock, 1) != 1)
|
|
bes_devel("[STA] TX is force-unlocked "
|
|
"due to stop request.\n");
|
|
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
if (!priv)
|
|
continue;
|
|
priv->mode = NL80211_IFTYPE_UNSPECIFIED;
|
|
priv->listening = false;
|
|
priv->delayed_link_loss = 0;
|
|
priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
|
|
cancel_delayed_work_sync(&priv->join_timeout);
|
|
cancel_delayed_work_sync(&priv->bss_loss_work);
|
|
cancel_delayed_work_sync(&priv->connection_loss_work);
|
|
cancel_delayed_work_sync(&priv->link_id_gc_work);
|
|
del_timer_sync(&priv->mcast_timeout);
|
|
}
|
|
|
|
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
|
cancel_work_sync(&hw_priv->coex_work);
|
|
coex_stop(hw_priv);
|
|
#endif
|
|
|
|
bes2600_wifi_stop(hw_priv);
|
|
|
|
wsm_unlock_tx(hw_priv);
|
|
|
|
up(&hw_priv->conf_lock);
|
|
bes2600_pwr_complete(hw_priv);
|
|
}
|
|
|
|
|
|
int bes2600_add_interface(struct ieee80211_hw *dev,
|
|
struct ieee80211_vif *vif)
|
|
{
|
|
int ret;
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
struct bes2600_vif *priv;
|
|
struct bes2600_vif **drv_priv = (void *)vif->drv_priv;
|
|
#ifndef P2P_MULTIVIF
|
|
int i;
|
|
|
|
if (atomic_read(&hw_priv->num_vifs) >= CW12XX_MAX_VIFS) {
|
|
bes_err("%s %d.",__func__, __LINE__);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
#endif
|
|
bes_devel(" !!! %s: type %d p2p %d addr %pM\n", __func__, vif->type, vif->p2p, vif->addr);
|
|
|
|
priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
atomic_set(&priv->enabled, 0);
|
|
|
|
*drv_priv = priv;
|
|
/* __le32 auto_calibration_mode = __cpu_to_le32(1); */
|
|
|
|
down(&hw_priv->conf_lock);
|
|
|
|
priv->mode = vif->type;
|
|
|
|
spin_lock(&hw_priv->vif_list_lock);
|
|
if (atomic_read(&hw_priv->num_vifs) < CW12XX_MAX_VIFS) {
|
|
#ifdef P2P_MULTIVIF
|
|
if (!memcmp(vif->addr, hw_priv->addresses[0].addr, ETH_ALEN)) {
|
|
priv->if_id = 0;
|
|
} else if (!memcmp(vif->addr, hw_priv->addresses[1].addr,
|
|
ETH_ALEN)) {
|
|
priv->if_id = 2;
|
|
} else if (!memcmp(vif->addr, hw_priv->addresses[2].addr,
|
|
ETH_ALEN)) {
|
|
priv->if_id = 1;
|
|
}
|
|
bes_devel("%s: if_id %d mac %pM\n",
|
|
__func__, priv->if_id, vif->addr);
|
|
#else
|
|
for (i = 0; i < CW12XX_MAX_VIFS; i++)
|
|
if (!memcmp(vif->addr, hw_priv->addresses[i].addr,
|
|
ETH_ALEN))
|
|
break;
|
|
if (i == CW12XX_MAX_VIFS) {
|
|
spin_unlock(&hw_priv->vif_list_lock);
|
|
up(&hw_priv->conf_lock);
|
|
return -EINVAL;
|
|
}
|
|
priv->if_id = i;
|
|
#endif
|
|
priv->hw_priv = hw_priv;
|
|
priv->hw = dev;
|
|
priv->vif = vif;
|
|
} else {
|
|
spin_unlock(&hw_priv->vif_list_lock);
|
|
up(&hw_priv->conf_lock);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
spin_unlock(&hw_priv->vif_list_lock);
|
|
/* TODO:COMBO :Check if MAC address matches the one expected by FW */
|
|
memcpy(hw_priv->mac_addr, vif->addr, ETH_ALEN);
|
|
|
|
/* Enable auto-calibration */
|
|
/* Exception in subsequent channel switch; disabled.
|
|
WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_SET_AUTO_CALIBRATION_MODE,
|
|
&auto_calibration_mode, sizeof(auto_calibration_mode)));
|
|
*/
|
|
bes_devel("[STA] Interface ID:%d of type:%d added\n",
|
|
priv->if_id, priv->mode);
|
|
|
|
up(&hw_priv->conf_lock);
|
|
|
|
bes2600_vif_setup(priv);
|
|
|
|
ret = WARN_ON(bes2600_setup_mac_pvif(priv));
|
|
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_remove_interface(struct ieee80211_hw *dev,
|
|
struct ieee80211_vif *vif)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
struct wsm_reset reset = {
|
|
.reset_statistics = true,
|
|
};
|
|
int i;
|
|
bool is_htcapie = false;
|
|
struct bes2600_vif *tmp_priv;
|
|
|
|
bes_devel(" !!! %s: type %d p2p %d addr %pM\n",
|
|
__func__, vif->type, vif->p2p, vif->addr);
|
|
atomic_set(&priv->enabled, 0);
|
|
down(&hw_priv->scan.lock);
|
|
down(&hw_priv->conf_lock);
|
|
if (!__cw12xx_hwpriv_to_vifpriv(hw_priv, priv->if_id)) {
|
|
bes_devel(" !!! %s: interface addr %pM already removed\n",
|
|
__func__, vif->addr);
|
|
up(&hw_priv->conf_lock);
|
|
up(&hw_priv->scan.lock);
|
|
return;
|
|
}
|
|
bes2600_tx_queues_lock(hw_priv);
|
|
wsm_lock_tx(hw_priv);
|
|
switch (priv->join_status) {
|
|
case BES2600_JOIN_STATUS_STA:
|
|
wsm_lock_tx(hw_priv);
|
|
if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
|
|
wsm_unlock_tx(hw_priv);
|
|
break;
|
|
case BES2600_JOIN_STATUS_AP:
|
|
for (i = 0; priv->link_id_map; ++i) {
|
|
if (priv->link_id_map & BIT(i)) {
|
|
cw12xx_unmap_link(priv, i);
|
|
priv->link_id_map &= ~BIT(i);
|
|
}
|
|
}
|
|
memset(priv->link_id_db, 0,
|
|
sizeof(priv->link_id_db));
|
|
priv->sta_asleep_mask = 0;
|
|
priv->enable_beacon = false;
|
|
priv->tx_multicast = false;
|
|
priv->aid0_bit_set = false;
|
|
priv->buffered_multicasts = false;
|
|
priv->pspoll_mask = 0;
|
|
reset.link_id = 0;
|
|
wsm_reset(hw_priv, &reset, priv->if_id);
|
|
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) && tmp_priv->htcap)
|
|
is_htcapie = true;
|
|
}
|
|
|
|
if (is_htcapie) {
|
|
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE;
|
|
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE;
|
|
bes_devel("AP REMOVE 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("AP REMOVE 11BG %d\n",hw_priv->vif0_throttle);
|
|
}
|
|
bes2600_pwr_clear_busy_event(hw_priv, BES2600_JOIN_STATUS_AP);
|
|
#ifdef BES2600_TX_RX_OPT
|
|
bes2600_txrx_opt_timer_restore();
|
|
#endif
|
|
break;
|
|
case BES2600_JOIN_STATUS_MONITOR:
|
|
bes2600_disable_listening(priv);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
/* TODO:COMBO: Change Queue Module */
|
|
if (!__bes2600_flush(hw_priv, false, priv->if_id))
|
|
wsm_unlock_tx(hw_priv);
|
|
|
|
cancel_delayed_work_sync(&priv->bss_loss_work);
|
|
cancel_delayed_work_sync(&priv->connection_loss_work);
|
|
cancel_delayed_work_sync(&priv->link_id_gc_work);
|
|
cancel_delayed_work_sync(&priv->join_timeout);
|
|
cancel_delayed_work_sync(&priv->set_cts_work);
|
|
cancel_delayed_work_sync(&priv->pending_offchanneltx_work);
|
|
|
|
del_timer_sync(&priv->mcast_timeout);
|
|
/* TODO:COMBO: May be reset of these variables "delayed_link_loss and
|
|
* join_status to default can be removed as dev_priv will be freed by
|
|
* mac80211 */
|
|
priv->delayed_link_loss = 0;
|
|
wsm_unlock_tx(hw_priv);
|
|
|
|
if ((priv->if_id ==1) && (priv->mode == NL80211_IFTYPE_AP
|
|
|| priv->mode == NL80211_IFTYPE_P2P_GO)) {
|
|
hw_priv->is_go_thru_go_neg = false;
|
|
}
|
|
spin_lock(&hw_priv->vif_list_lock);
|
|
spin_lock(&priv->vif_lock);
|
|
hw_priv->vif_list[priv->if_id] = NULL;
|
|
hw_priv->if_id_slot &= (~BIT(priv->if_id));
|
|
atomic_dec(&hw_priv->num_vifs);
|
|
if (atomic_read(&hw_priv->num_vifs) == 0) {
|
|
bes2600_free_keys(hw_priv);
|
|
memset(hw_priv->mac_addr, 0, ETH_ALEN);
|
|
}
|
|
spin_unlock(&priv->vif_lock);
|
|
spin_unlock(&hw_priv->vif_list_lock);
|
|
priv->listening = false;
|
|
|
|
bes2600_debug_release_priv(priv);
|
|
|
|
bes2600_tx_queues_unlock(hw_priv);
|
|
up(&hw_priv->conf_lock);
|
|
|
|
if (atomic_read(&hw_priv->num_vifs) == 0)
|
|
flush_workqueue(hw_priv->workqueue);
|
|
memset(priv, 0, sizeof(struct bes2600_vif));
|
|
up(&hw_priv->scan.lock);
|
|
}
|
|
|
|
int bes2600_change_interface(struct ieee80211_hw *dev,
|
|
struct ieee80211_vif *vif,
|
|
enum nl80211_iftype new_type,
|
|
bool p2p)
|
|
{
|
|
int ret = 0;
|
|
bes_devel(" !!! %s: type %d (%d), p2p %d (%d)\n",
|
|
__func__, new_type, vif->type, p2p, vif->p2p);
|
|
if (new_type != vif->type || vif->p2p != p2p) {
|
|
bes2600_remove_interface(dev, vif);
|
|
vif->type = new_type;
|
|
vif->p2p = p2p;
|
|
ret = bes2600_add_interface(dev, vif);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_config(struct ieee80211_hw *dev, u32 changed)
|
|
{
|
|
int ret = 0;
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
struct ieee80211_conf *conf = &dev->conf;
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
int max_power_level = 0;
|
|
int min_power_level = 0;
|
|
#endif
|
|
/* TODO:COMBO: adjust to multi vif interface
|
|
* IEEE80211_CONF_CHANGE_IDLE is still handled per bes2600_vif*/
|
|
int if_id = 0;
|
|
struct bes2600_vif *priv;
|
|
|
|
bes_devel("CONFIG CHANGED: %08x\n", changed);
|
|
|
|
if (changed &
|
|
(IEEE80211_CONF_CHANGE_MONITOR|IEEE80211_CONF_CHANGE_IDLE)) {
|
|
/* TBD: It looks like it's transparent
|
|
* there's a monitor interface present -- use this
|
|
* to determine for example whether to calculate
|
|
* timestamps for packets or not, do not use instead
|
|
* of filter flags! */
|
|
bes_devel(
|
|
"ignore IEEE80211_CONF_CHANGE_MONITOR (%d)"
|
|
"IEEE80211_CONF_CHANGE_IDLE (%d)\n",
|
|
(changed & IEEE80211_CONF_CHANGE_MONITOR) ? 1 : 0,
|
|
(changed & IEEE80211_CONF_CHANGE_IDLE) ? 1 : 0);
|
|
return ret;
|
|
}
|
|
|
|
down(&hw_priv->scan.lock);
|
|
down(&hw_priv->conf_lock);
|
|
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
|
|
/* TODO: IEEE80211_CONF_CHANGE_QOS */
|
|
/* TODO:COMBO:Change when support is available mac80211*/
|
|
if (changed & IEEE80211_CONF_CHANGE_POWER) {
|
|
/*hw_priv->output_power = conf->power_level;*/
|
|
bes_devel("Output power ++%d\n",conf->power_level);
|
|
hw_priv->output_power = 20;
|
|
bes_devel("Output power --%d\n",hw_priv->output_power);
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
/* Testing if Power Level to set is out of device power range */
|
|
if (conf->chandef.chan->band == NL80211_BAND_2GHZ) {
|
|
max_power_level = hw_priv->txPowerRange[0].max_power_level;
|
|
min_power_level = hw_priv->txPowerRange[0].min_power_level;
|
|
} else {
|
|
max_power_level = hw_priv->txPowerRange[1].max_power_level;
|
|
min_power_level = hw_priv->txPowerRange[1].min_power_level;
|
|
}
|
|
if (hw_priv->output_power > max_power_level)
|
|
hw_priv->output_power = max_power_level;
|
|
else if (hw_priv->output_power < min_power_level)
|
|
hw_priv->output_power = min_power_level;
|
|
#endif /* CONFIG_BES2600_TESTMODE */
|
|
bes_devel("[STA] TX power: %d\n",
|
|
hw_priv->output_power);
|
|
WARN_ON(wsm_set_output_power(hw_priv,
|
|
hw_priv->output_power * 10,
|
|
if_id));
|
|
}
|
|
|
|
if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
|
|
/* Switch Channel commented for CC Mode */
|
|
struct ieee80211_channel *ch = conf->chandef.chan;
|
|
|
|
bes_devel("[STA] Freq %d (wsm ch: %d, type: %d).\n",
|
|
ch->center_freq, ch->hw_value,
|
|
cfg80211_get_chandef_type(&conf->chandef));
|
|
/* Earlier there was a call to __bes2600_flush().
|
|
Removed as deemed unnecessary */
|
|
|
|
hw_priv->channel = ch;
|
|
|
|
}
|
|
|
|
if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
|
|
bes_devel("[STA] Retry limits: %d (long), %d (short).\n",
|
|
conf->long_frame_max_tx_count,
|
|
conf->short_frame_max_tx_count);
|
|
spin_lock_bh(&hw_priv->tx_policy_cache.lock);
|
|
hw_priv->long_frame_max_tx_count = conf->long_frame_max_tx_count;
|
|
hw_priv->short_frame_max_tx_count =
|
|
(conf->short_frame_max_tx_count < 0x0F) ?
|
|
conf->short_frame_max_tx_count : 0x0F;
|
|
hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count;
|
|
spin_unlock_bh(&hw_priv->tx_policy_cache.lock);
|
|
}
|
|
up(&hw_priv->conf_lock);
|
|
up(&hw_priv->scan.lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_update_filtering(struct bes2600_vif *priv)
|
|
{
|
|
int ret;
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
bool bssid_filtering = !priv->rx_filter.bssid;
|
|
static struct wsm_beacon_filter_control bf_disabled = {
|
|
.enabled = __cpu_to_le32(0),
|
|
.bcn_count = __cpu_to_le32(1),
|
|
};
|
|
bool ap_mode = 0;
|
|
static struct wsm_beacon_filter_table bf_table_auto = {
|
|
.numOfIEs = __cpu_to_le32(1),
|
|
.entry[0].ieId = WLAN_EID_VENDOR_SPECIFIC,
|
|
.entry[0].actionFlags = WSM_BEACON_FILTER_IE_HAS_CHANGED |
|
|
WSM_BEACON_FILTER_IE_NO_LONGER_PRESENT |
|
|
WSM_BEACON_FILTER_IE_HAS_APPEARED,
|
|
.entry[0].oui[0] = 0x50,
|
|
.entry[0].oui[1] = 0x6F,
|
|
.entry[0].oui[2] = 0x9A,
|
|
};
|
|
static struct wsm_beacon_filter_control bf_auto = {
|
|
.enabled = __cpu_to_le32(WSM_BEACON_FILTER_ENABLE |
|
|
WSM_BEACON_FILTER_AUTO_ERP | WSM_BEACON_FILTER_AUTO_HT),
|
|
// .bcn_count = __cpu_to_le32(priv->bf_control.bcn_count);
|
|
};
|
|
// bf_table_auto.numOfIEs = 0; /* No filtering to do - so discarding */
|
|
bf_auto.bcn_count = __cpu_to_le32(priv->bf_control.bcn_count);
|
|
|
|
if (priv->join_status == BES2600_JOIN_STATUS_PASSIVE)
|
|
return;
|
|
else if (priv->join_status == BES2600_JOIN_STATUS_MONITOR)
|
|
bssid_filtering = false;
|
|
|
|
if (priv->vif && (priv->vif->type == NL80211_IFTYPE_AP))
|
|
ap_mode = true;
|
|
/*
|
|
* When acting as p2p client being connected to p2p GO, in order to
|
|
* receive frames from a different p2p device, turn off bssid filter.
|
|
*
|
|
* WARNING: FW dependency!
|
|
* This can only be used with FW WSM371 and its successors.
|
|
* In that FW version even with bssid filter turned off,
|
|
* device will block most of the unwanted frames.
|
|
*/
|
|
if (priv->vif && priv->vif->p2p)
|
|
bssid_filtering = false;
|
|
|
|
ret = wsm_set_rx_filter(hw_priv, &priv->rx_filter, priv->if_id);
|
|
if (!ret && !ap_mode) {
|
|
if (priv->vif) {
|
|
if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type)
|
|
ret = wsm_set_beacon_filter_table(hw_priv, &priv->bf_table,
|
|
priv->if_id);
|
|
else
|
|
ret = wsm_set_beacon_filter_table(hw_priv, &bf_table_auto,
|
|
priv->if_id);
|
|
} else
|
|
WARN_ON(1);
|
|
}
|
|
if (!ret && !ap_mode) {
|
|
if (priv->disable_beacon_filter)
|
|
ret = wsm_beacon_filter_control(hw_priv,
|
|
&bf_disabled, priv->if_id);
|
|
else {
|
|
if (priv->vif) {
|
|
if (priv->vif->p2p || NL80211_IFTYPE_STATION != priv->vif->type)
|
|
ret = wsm_beacon_filter_control(hw_priv,
|
|
&priv->bf_control, priv->if_id);
|
|
else
|
|
ret = wsm_beacon_filter_control(hw_priv,
|
|
&bf_auto, priv->if_id);
|
|
} else
|
|
WARN_ON(1);
|
|
}
|
|
}
|
|
|
|
if (!ret)
|
|
ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering,
|
|
priv->if_id);
|
|
|
|
if (!ret) {
|
|
ret = wsm_set_multicast_filter(hw_priv, &priv->multicast_filter,
|
|
priv->if_id);
|
|
}
|
|
|
|
if (ret)
|
|
wiphy_err(priv->hw->wiphy,
|
|
"%s: Update filtering failed: %d.\n",
|
|
__func__, ret);
|
|
return;
|
|
}
|
|
|
|
void bes2600_update_filtering_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif,
|
|
update_filtering_work);
|
|
|
|
bes2600_update_filtering(priv);
|
|
}
|
|
|
|
void bes2600_set_beacon_wakeup_period_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif,
|
|
set_beacon_wakeup_period_work);
|
|
|
|
WARN_ON(wsm_set_beacon_wakeup_period(priv->hw_priv,
|
|
priv->beacon_int * priv->join_dtim_period >
|
|
MAX_BEACON_SKIP_TIME_MS ? 1 :
|
|
priv->join_dtim_period, 0, priv->if_id));
|
|
}
|
|
|
|
u64 bes2600_prepare_multicast(struct ieee80211_hw *hw,
|
|
struct netdev_hw_addr_list *mc_list)
|
|
{
|
|
static u8 broadcast_ipv6[ETH_ALEN] = {
|
|
0x33, 0x33, 0x00, 0x00, 0x00, 0x01
|
|
};
|
|
static u8 broadcast_ipv4[ETH_ALEN] = {
|
|
0x01, 0x00, 0x5e, 0x00, 0x00, 0x01
|
|
};
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
|
|
struct netdev_hw_addr *ha;
|
|
int count = 0;
|
|
|
|
if(priv == NULL){
|
|
bes_devel("wlan0 removed before p2p-device\n");
|
|
return netdev_hw_addr_list_count(mc_list);
|
|
}
|
|
|
|
/* Disable multicast filtering */
|
|
priv->has_multicast_subscription = false;
|
|
memset(&priv->multicast_filter, 0x00, sizeof(priv->multicast_filter));
|
|
|
|
if (netdev_hw_addr_list_count(mc_list) > WSM_MAX_GRP_ADDRTABLE_ENTRIES)
|
|
return 0;
|
|
|
|
/* Enable if requested */
|
|
netdev_hw_addr_list_for_each(ha, mc_list) {
|
|
bes_devel("[STA] multicast: %pM\n", ha->addr);
|
|
memcpy(&priv->multicast_filter.macAddress[count],
|
|
ha->addr, ETH_ALEN);
|
|
if (memcmp(ha->addr, broadcast_ipv4, ETH_ALEN) &&
|
|
memcmp(ha->addr, broadcast_ipv6, ETH_ALEN))
|
|
priv->has_multicast_subscription = true;
|
|
count++;
|
|
}
|
|
|
|
if (count) {
|
|
priv->multicast_filter.enable = __cpu_to_le32(1);
|
|
priv->multicast_filter.numOfAddresses = __cpu_to_le32(count);
|
|
}
|
|
|
|
return netdev_hw_addr_list_count(mc_list);
|
|
}
|
|
|
|
void bes2600_configure_filter(struct ieee80211_hw *hw,
|
|
unsigned int changed_flags,
|
|
unsigned int *total_flags,
|
|
u64 multicast)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 0);
|
|
#if 0
|
|
bool listening = !!(*total_flags &
|
|
(FIF_PROMISC_IN_BSS |
|
|
FIF_OTHER_BSS |
|
|
FIF_BCN_PRBRESP_PROMISC |
|
|
FIF_PROBE_REQ));
|
|
#endif
|
|
*total_flags &= FIF_OTHER_BSS |
|
|
FIF_FCSFAIL |
|
|
FIF_BCN_PRBRESP_PROMISC |
|
|
FIF_PROBE_REQ;
|
|
|
|
if(priv != NULL){
|
|
down(&hw_priv->scan.lock);
|
|
down(&hw_priv->conf_lock);
|
|
|
|
priv->rx_filter.promiscuous = 0;
|
|
priv->rx_filter.bssid = (*total_flags & (FIF_OTHER_BSS |
|
|
FIF_PROBE_REQ)) ? 1 : 0;
|
|
priv->rx_filter.fcs = (*total_flags & FIF_FCSFAIL) ? 1 : 0;
|
|
priv->bf_control.bcn_count = __cpu_to_le32((*total_flags &
|
|
(FIF_BCN_PRBRESP_PROMISC | FIF_PROBE_REQ)) ? 1 : 0);
|
|
#if 0
|
|
if (priv->listening ^ listening) {
|
|
priv->listening = listening;
|
|
wsm_lock_tx(hw_priv);
|
|
bes2600_update_listening(priv, listening);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
#endif
|
|
bes2600_update_filtering(priv);
|
|
up(&hw_priv->conf_lock);
|
|
up(&hw_priv->scan.lock);
|
|
}
|
|
}
|
|
|
|
int bes2600_conf_tx(struct ieee80211_hw *dev, struct ieee80211_vif *vif,
|
|
unsigned int link_id,
|
|
u16 queue, const struct ieee80211_tx_queue_params *params)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
int ret = 0;
|
|
/* To prevent re-applying PM request OID again and again*/
|
|
bool old_uapsdFlags;
|
|
|
|
if (WARN_ON(!priv))
|
|
return -EOPNOTSUPP;
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
if (priv->if_id == CW12XX_GENERIC_IF_ID)
|
|
return 0;
|
|
#endif
|
|
down(&hw_priv->conf_lock);
|
|
|
|
if (queue < dev->queues) {
|
|
old_uapsdFlags = priv->uapsd_info.uapsdFlags;
|
|
|
|
WSM_TX_QUEUE_SET(&priv->tx_queue_params, queue, 0, 0, 0);
|
|
ret = wsm_set_tx_queue_params(hw_priv,
|
|
&priv->tx_queue_params.params[queue],
|
|
queue, priv->if_id);
|
|
if (ret) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
WSM_EDCA_SET(&priv->edca, queue, params->aifs,
|
|
params->cw_min, params->cw_max, params->txop, 0xc8,
|
|
params->uapsd);
|
|
ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
|
|
if (ret) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (priv->mode == NL80211_IFTYPE_STATION) {
|
|
ret = bes2600_set_uapsd_param(priv, &priv->edca);
|
|
#if 0
|
|
if (!ret && priv->setbssparams_done &&
|
|
(priv->join_status == BES2600_JOIN_STATUS_STA) &&
|
|
(old_uapsdFlags != priv->uapsd_info.uapsdFlags))
|
|
bes2600_set_pm(priv, &priv->powersave_mode);
|
|
#endif
|
|
}
|
|
} else
|
|
ret = -EINVAL;
|
|
|
|
out:
|
|
up(&hw_priv->conf_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_get_stats(struct ieee80211_hw *dev,
|
|
struct ieee80211_low_level_stats *stats)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
|
|
memcpy(stats, &hw_priv->stats, sizeof(*stats));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
int bes2600_get_tx_stats(struct ieee80211_hw *dev,
|
|
struct ieee80211_tx_queue_stats *stats)
|
|
{
|
|
int i;
|
|
struct bes2600_common *priv = dev->priv;
|
|
|
|
for (i = 0; i < dev->queues; ++i)
|
|
bes2600_queue_get_stats(&priv->tx_queue[i], &stats[i]);
|
|
|
|
return 0;
|
|
}
|
|
*/
|
|
|
|
int bes2600_set_pm(struct bes2600_vif *priv, const struct wsm_set_pm *arg)
|
|
{
|
|
struct wsm_set_pm pm = *arg;
|
|
|
|
if (priv->uapsd_info.uapsdFlags != 0)
|
|
pm.pmMode &= ~WSM_PSM_FAST_PS_FLAG;
|
|
|
|
if (memcmp(&pm, &priv->firmware_ps_mode,
|
|
sizeof(struct wsm_set_pm))) {
|
|
priv->firmware_ps_mode = pm;
|
|
return wsm_set_pm(priv->hw_priv, &pm,
|
|
priv->if_id);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int bes2600_set_key(struct ieee80211_hw *dev, enum set_key_cmd cmd,
|
|
struct ieee80211_vif *vif, struct ieee80211_sta *sta,
|
|
struct ieee80211_key_conf *key)
|
|
{
|
|
int ret = -EOPNOTSUPP;
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
struct wsm_protected_mgmt_policy mgmt_policy;
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
WARN_ON(priv->if_id == CW12XX_GENERIC_IF_ID);
|
|
#endif
|
|
memset(&mgmt_policy, 0, sizeof(mgmt_policy));
|
|
down(&hw_priv->conf_lock);
|
|
bes_devel("%s, cmd:%d cipher:0x%08x idx:%d\n",
|
|
__func__, cmd, key->cipher, key->keyidx);
|
|
|
|
if (cmd == SET_KEY) {
|
|
u8 *peer_addr = NULL;
|
|
int pairwise = (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) ?
|
|
1 : 0;
|
|
int idx = bes2600_alloc_key(hw_priv);
|
|
struct wsm_add_key *wsm_key = &hw_priv->keys[idx];
|
|
|
|
if (idx < 0) {
|
|
ret = -EINVAL;
|
|
goto finally;
|
|
}
|
|
|
|
BUG_ON(pairwise && !sta);
|
|
if (sta)
|
|
peer_addr = sta->addr;
|
|
|
|
key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE |
|
|
IEEE80211_KEY_FLAG_RESERVE_TAILROOM;
|
|
|
|
/* only need to update cipher type of pairwise key */
|
|
if (pairwise)
|
|
priv->cipherType = key->cipher;
|
|
|
|
switch (key->cipher) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
if (key->keylen > 16) {
|
|
bes2600_free_key(hw_priv, idx);
|
|
ret = -EINVAL;
|
|
goto finally;
|
|
}
|
|
|
|
if (pairwise) {
|
|
wsm_key->type = WSM_KEY_TYPE_WEP_PAIRWISE;
|
|
memcpy(wsm_key->wepPairwiseKey.peerAddress,
|
|
peer_addr, ETH_ALEN);
|
|
memcpy(wsm_key->wepPairwiseKey.keyData,
|
|
&key->key[0], key->keylen);
|
|
wsm_key->wepPairwiseKey.keyLength = key->keylen;
|
|
} else {
|
|
wsm_key->type = WSM_KEY_TYPE_WEP_DEFAULT;
|
|
memcpy(wsm_key->wepGroupKey.keyData,
|
|
&key->key[0], key->keylen);
|
|
wsm_key->wepGroupKey.keyLength = key->keylen;
|
|
wsm_key->wepGroupKey.keyId = key->keyidx;
|
|
}
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
if (pairwise) {
|
|
wsm_key->type = WSM_KEY_TYPE_TKIP_PAIRWISE;
|
|
memcpy(wsm_key->tkipPairwiseKey.peerAddress,
|
|
peer_addr, ETH_ALEN);
|
|
memcpy(wsm_key->tkipPairwiseKey.tkipKeyData,
|
|
&key->key[0], 16);
|
|
memcpy(wsm_key->tkipPairwiseKey.txMicKey,
|
|
&key->key[16], 8);
|
|
memcpy(wsm_key->tkipPairwiseKey.rxMicKey,
|
|
&key->key[24], 8);
|
|
} else {
|
|
size_t mic_offset =
|
|
(priv->mode == NL80211_IFTYPE_AP) ?
|
|
16 : 24;
|
|
wsm_key->type = WSM_KEY_TYPE_TKIP_GROUP;
|
|
memcpy(wsm_key->tkipGroupKey.tkipKeyData,
|
|
&key->key[0], 16);
|
|
memcpy(wsm_key->tkipGroupKey.rxMicKey,
|
|
&key->key[mic_offset], 8);
|
|
|
|
/* TODO: Where can I find TKIP SEQ? */
|
|
memset(wsm_key->tkipGroupKey.rxSeqCounter,
|
|
0, 8);
|
|
wsm_key->tkipGroupKey.keyId = key->keyidx;
|
|
|
|
}
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
if (pairwise) {
|
|
wsm_key->type = WSM_KEY_TYPE_AES_PAIRWISE;
|
|
memcpy(wsm_key->aesPairwiseKey.peerAddress,
|
|
peer_addr, ETH_ALEN);
|
|
memcpy(wsm_key->aesPairwiseKey.aesKeyData,
|
|
&key->key[0], 16);
|
|
} else {
|
|
wsm_key->type = WSM_KEY_TYPE_AES_GROUP;
|
|
memcpy(wsm_key->aesGroupKey.aesKeyData,
|
|
&key->key[0], 16);
|
|
/* TODO: Where can I find AES SEQ? */
|
|
memset(wsm_key->aesGroupKey.rxSeqCounter,
|
|
0, 8);
|
|
wsm_key->aesGroupKey.keyId = key->keyidx;
|
|
}
|
|
break;
|
|
case WLAN_CIPHER_SUITE_AES_CMAC:
|
|
wsm_key->type = WSM_KEY_TYPE_IGTK_GROUP;
|
|
memcpy(wsm_key->igtkGroupKey.IGTKKeyData, &key->key[0], 16);
|
|
memset(wsm_key->igtkGroupKey.IPN, 0, 8);
|
|
wsm_key->igtkGroupKey.keyId = key->keyidx;
|
|
mgmt_policy.protectedMgmtEnable = 1;
|
|
wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy, priv->if_id);
|
|
break;
|
|
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
if (pairwise) {
|
|
wsm_key->type = WSM_KEY_TYPE_WAPI_PAIRWISE;
|
|
memcpy(wsm_key->wapiPairwiseKey.peerAddress,
|
|
peer_addr, ETH_ALEN);
|
|
memcpy(wsm_key->wapiPairwiseKey.wapiKeyData,
|
|
&key->key[0], 16);
|
|
memcpy(wsm_key->wapiPairwiseKey.micKeyData,
|
|
&key->key[16], 16);
|
|
wsm_key->wapiPairwiseKey.keyId = key->keyidx;
|
|
} else {
|
|
wsm_key->type = WSM_KEY_TYPE_WAPI_GROUP;
|
|
memcpy(wsm_key->wapiGroupKey.wapiKeyData,
|
|
&key->key[0], 16);
|
|
memcpy(wsm_key->wapiGroupKey.micKeyData,
|
|
&key->key[16], 16);
|
|
wsm_key->wapiGroupKey.keyId = key->keyidx;
|
|
}
|
|
break;
|
|
#endif /* CONFIG_BES2600_WAPI_SUPPORT */
|
|
default:
|
|
WARN_ON(1);
|
|
bes2600_free_key(hw_priv, idx);
|
|
ret = -EOPNOTSUPP;
|
|
goto finally;
|
|
}
|
|
|
|
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
|
/* workaround: beacuse bes2600 can't install two unicast keys at the same time,
|
|
* remove last unicast key when install new unicast key.
|
|
*/
|
|
if(key->cipher == WLAN_CIPHER_SUITE_SMS4 &&
|
|
pairwise && hw_priv->last_ins_wapi_usk_id != -1) {
|
|
struct wsm_remove_key last_wsm_key = {
|
|
.entryIndex = hw_priv->last_ins_wapi_usk_id,
|
|
};
|
|
bes2600_free_key(hw_priv, last_wsm_key.entryIndex);
|
|
wsm_remove_key(hw_priv, &last_wsm_key, priv->if_id);
|
|
hw_priv->last_del_wapi_usk_id = hw_priv->last_ins_wapi_usk_id;
|
|
}
|
|
#endif
|
|
|
|
ret = WARN_ON(wsm_add_key(hw_priv, wsm_key, priv->if_id));
|
|
if (!ret) {
|
|
key->hw_key_idx = idx;
|
|
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
|
if(wsm_key->type == WSM_KEY_TYPE_WAPI_PAIRWISE)
|
|
hw_priv->last_ins_wapi_usk_id = idx;
|
|
#endif
|
|
} else {
|
|
bes2600_free_key(hw_priv, idx);
|
|
}
|
|
|
|
if (!ret && (pairwise
|
|
|| wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT)
|
|
&& (priv->filter4.enable & 0x2))
|
|
bes2600_set_arpreply(dev, vif);
|
|
#ifdef IPV6_FILTERING
|
|
if (!ret && (pairwise
|
|
|| wsm_key->type == WSM_KEY_TYPE_WEP_DEFAULT)
|
|
&& (priv->filter6.enable & 0x2))
|
|
bes2600_set_na(dev, vif);
|
|
#endif /*IPV6_FILTERING*/
|
|
|
|
} else if (cmd == DISABLE_KEY) {
|
|
struct wsm_remove_key wsm_key = {
|
|
.entryIndex = key->hw_key_idx,
|
|
};
|
|
|
|
if (wsm_key.entryIndex > WSM_KEY_MAX_IDX) {
|
|
ret = -EINVAL;
|
|
goto finally;
|
|
}
|
|
|
|
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
|
/* workaround: beacuse bes2600 can't install two unicast keys at the same time,
|
|
* remove last unicast key when install new unicast key.
|
|
*/
|
|
if(hw_priv->last_del_wapi_usk_id == key->hw_key_idx) {
|
|
hw_priv->last_del_wapi_usk_id = -1;
|
|
ret = 0;
|
|
goto finally;
|
|
} else if (hw_priv->last_ins_wapi_usk_id == key->hw_key_idx) {
|
|
hw_priv->last_ins_wapi_usk_id = -1;
|
|
}
|
|
#endif
|
|
|
|
bes2600_free_key(hw_priv, wsm_key.entryIndex);
|
|
ret = wsm_remove_key(hw_priv, &wsm_key, priv->if_id);
|
|
} else {
|
|
BUG_ON("Unsupported command");
|
|
}
|
|
|
|
finally:
|
|
up(&hw_priv->conf_lock);
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_wep_key_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif , wep_key_work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
|
|
struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
|
|
__le32 wep_default_key_id = __cpu_to_le32(
|
|
priv->wep_default_key_id);
|
|
|
|
BUG_ON(queueId >= 4);
|
|
|
|
bes_devel("[STA] Setting default WEP key: %d\n",
|
|
priv->wep_default_key_id);
|
|
wsm_flush_tx(hw_priv);
|
|
WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_WEP_DEFAULT_KEY_ID,
|
|
&wep_default_key_id, sizeof(wep_default_key_id), priv->if_id));
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_requeue(hw_priv, queue,
|
|
hw_priv->pending_frame_id, true);
|
|
#else
|
|
bes2600_queue_requeue(queue, hw_priv->pending_frame_id, true);
|
|
#endif
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
int bes2600_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
int ret;
|
|
__le32 val32;
|
|
bes_devel("set RTS threshold = %d\n\r", hw_priv->rtsvalue);
|
|
|
|
spin_lock(&hw_priv->rtsvalue_lock);
|
|
if (hw_priv->rtsvalue != value) {
|
|
hw_priv->rtsvalue = value;
|
|
//bes_devel("set RTS value = %d\n\r", hw_priv->rtsvalue);
|
|
} else {
|
|
spin_unlock(&hw_priv->rtsvalue_lock);
|
|
return 0;
|
|
}
|
|
spin_unlock(&hw_priv->rtsvalue_lock);
|
|
|
|
if (value != (u32) -1)
|
|
val32 = __cpu_to_le32(value);
|
|
else
|
|
val32 = 0; /* disabled */
|
|
/* mutex_lock(&priv->conf_mutex); */
|
|
ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_RTS_THRESHOLD,
|
|
&val32, sizeof(val32), 0));
|
|
/* mutex_unlock(&priv->conf_mutex); */
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* TODO: COMBO: Flush only a particular interface specific parts */
|
|
int __bes2600_flush(struct bes2600_common *hw_priv, bool drop, int if_id)
|
|
{
|
|
int i, ret;
|
|
struct bes2600_vif *priv =
|
|
__cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
|
|
|
|
/* clear tx queue directly if there is a bus error */
|
|
if (hw_priv->bh_error || bes2600_chrdev_is_bus_error())
|
|
drop = true;
|
|
|
|
for (;;) {
|
|
/* TODO: correct flush handling is required when dev_stop.
|
|
* Temporary workaround: 2s
|
|
*/
|
|
if (drop) {
|
|
/* protect tx confirm flow */
|
|
spin_lock(&priv->vif_lock);
|
|
for (i = 0; i < 4; ++i)
|
|
bes2600_queue_clear(&hw_priv->tx_queue[i],
|
|
if_id);
|
|
spin_unlock(&priv->vif_lock);
|
|
} else {
|
|
ret = wait_event_timeout(
|
|
hw_priv->tx_queue_stats.wait_link_id_empty,
|
|
bes2600_queue_stats_is_empty(
|
|
&hw_priv->tx_queue_stats, -1, if_id),
|
|
#ifdef FPGA_SETUP
|
|
10 * HZ);
|
|
#else
|
|
2 * HZ);
|
|
#endif
|
|
}
|
|
|
|
if (!drop && unlikely(ret <= 0)) {
|
|
bes_err("__bes2600_flush: ETIMEDOUT.....\n");
|
|
ret = -ETIMEDOUT;
|
|
break;
|
|
} else {
|
|
ret = 0;
|
|
}
|
|
|
|
wsm_vif_lock_tx(priv);
|
|
if (unlikely(!bes2600_queue_stats_is_empty(
|
|
&hw_priv->tx_queue_stats, -1, if_id))) {
|
|
/* Highly unlekely: WSM requeued frames. */
|
|
wsm_unlock_tx(hw_priv);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|
u32 queues, bool drop)
|
|
{
|
|
struct bes2600_vif *priv = NULL;
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
unsigned int ret = 0;
|
|
int hw_bufs_used = 0;
|
|
int i;
|
|
|
|
/* get queue status */
|
|
if (vif) {
|
|
priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
ret |= !bes2600_queue_stats_is_empty(
|
|
&hw_priv->tx_queue_stats, -1, priv->if_id);
|
|
hw_bufs_used = hw_priv->hw_bufs_used_vif[priv->if_id];
|
|
} else {
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
if (!priv)
|
|
continue;
|
|
if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
|
|
return;
|
|
ret |= !bes2600_queue_stats_is_empty(
|
|
&hw_priv->tx_queue_stats, -1, priv->if_id);
|
|
hw_bufs_used = hw_priv->hw_bufs_used;
|
|
}
|
|
}
|
|
|
|
/* no need to do the next work if queue was already clear */
|
|
if((ret == 0) && (hw_bufs_used == 0)) {
|
|
bes_devel("no need to flush\n");
|
|
return;
|
|
}
|
|
|
|
/* do flush operation */
|
|
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_FLUSH);
|
|
if (vif) {
|
|
priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
|
|
return;
|
|
if (!WARN_ON(__bes2600_flush(hw_priv, drop, priv->if_id)))
|
|
wsm_unlock_tx(hw_priv);
|
|
} else {
|
|
bes2600_for_each_vif(hw_priv, priv, i) {
|
|
if (!priv)
|
|
continue;
|
|
if (!(hw_priv->if_id_slot & BIT(priv->if_id)))
|
|
return;
|
|
if (!WARN_ON(__bes2600_flush(hw_priv, drop, priv->if_id)))
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
}
|
|
bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_FLUSH);
|
|
|
|
return;
|
|
}
|
|
|
|
int bes2600_remain_on_channel(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif,
|
|
struct ieee80211_channel *chan,
|
|
int duration,
|
|
enum ieee80211_roc_type type)
|
|
{
|
|
int ret;
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
int if_id = priv->if_id;
|
|
|
|
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC);
|
|
down(&hw_priv->scan.lock);
|
|
down(&hw_priv->conf_lock);
|
|
|
|
|
|
bes_devel("ROC IN %d ch %d\n", priv->if_id, chan->hw_value);
|
|
hw_priv->roc_if_id = priv->if_id;
|
|
ret = WARN_ON(__bes2600_flush(hw_priv, false, if_id));
|
|
wsm_unlock_tx(hw_priv);
|
|
ret = bes2600_enable_listening(priv, chan);
|
|
|
|
if (!ret) {
|
|
atomic_set(&hw_priv->remain_on_channel, 1);
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&hw_priv->rem_chan_timeout,
|
|
duration * HZ / 1000);
|
|
priv->join_status = BES2600_JOIN_STATUS_MONITOR;
|
|
ieee80211_ready_on_channel(hw);
|
|
} else {
|
|
hw_priv->roc_if_id = -1;
|
|
up(&hw_priv->scan.lock);
|
|
bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC);
|
|
}
|
|
|
|
bes_devel("ROC OUT %d\n", priv->if_id);
|
|
|
|
/* set the channel to supplied ieee80211_channel pointer, if it
|
|
is not set. This is to remove the crash while sending a probe res
|
|
in listen state. Later channel will updated on
|
|
IEEE80211_CONF_CHANGE_CHANNEL event*/
|
|
if(!hw_priv->channel) {
|
|
hw_priv->channel = chan;
|
|
}
|
|
//hw_priv->roc_cookie = cookie;
|
|
up(&hw_priv->conf_lock);
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_cancel_remain_on_channel(struct ieee80211_hw *hw
|
|
, struct ieee80211_vif *vif
|
|
)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
|
|
bes_devel("[STA] Cancel remain on channel\n");
|
|
|
|
if (atomic_read(&hw_priv->remain_on_channel))
|
|
cancel_delayed_work_sync(&hw_priv->rem_chan_timeout);
|
|
|
|
if (atomic_read(&hw_priv->remain_on_channel))
|
|
bes2600_rem_chan_timeout(&hw_priv->rem_chan_timeout.work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ******************************************************************** */
|
|
/* WSM callbacks */
|
|
|
|
void bes2600_channel_switch_cb(struct bes2600_common *hw_priv)
|
|
{
|
|
//wsm_unlock_tx(hw_priv); /* already unlock in wsm_channel_switch_indication */
|
|
}
|
|
|
|
void bes2600_free_event_queue(struct bes2600_common *hw_priv)
|
|
{
|
|
LIST_HEAD(list);
|
|
|
|
spin_lock(&hw_priv->event_queue_lock);
|
|
list_splice_init(&hw_priv->event_queue, &list);
|
|
spin_unlock(&hw_priv->event_queue_lock);
|
|
|
|
__bes2600_free_event_queue(&list);
|
|
}
|
|
|
|
void bes2600_event_handler(struct work_struct *work)
|
|
{
|
|
struct bes2600_common *hw_priv =
|
|
container_of(work, struct bes2600_common, event_handler);
|
|
struct bes2600_vif *priv;
|
|
struct bes2600_wsm_event *event;
|
|
LIST_HEAD(list);
|
|
|
|
spin_lock(&hw_priv->event_queue_lock);
|
|
list_splice_init(&hw_priv->event_queue, &list);
|
|
spin_unlock(&hw_priv->event_queue_lock);
|
|
|
|
down(&hw_priv->conf_lock);
|
|
list_for_each_entry(event, &list, link) {
|
|
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, event->if_id);
|
|
if (!priv) {
|
|
bes_devel("[CQM] Event for non existing "
|
|
"interface, ignoring.\n");
|
|
continue;
|
|
}
|
|
switch (event->evt.eventId) {
|
|
case WSM_EVENT_ERROR:
|
|
/* I even don't know what is it about.. */
|
|
//STUB();
|
|
break;
|
|
case WSM_EVENT_BSS_LOST:
|
|
{
|
|
if (bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_WSME) {
|
|
bes2600_chrdev_wifi_update_wakeup_reason(WAKEUP_REASON_WIFI_BSSLOST, 0);
|
|
bes2600_chrdev_wakeup_by_event_set(WAKEUP_EVENT_NONE);
|
|
}
|
|
spin_lock(&priv->bss_loss_lock);
|
|
if (priv->bss_loss_status > BES2600_BSS_LOSS_NONE) {
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
break;
|
|
}
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING;
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
bes_devel("[CQM] BSS lost.\n");
|
|
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
|
|
cancel_delayed_work_sync(&priv->bss_loss_work);
|
|
cancel_delayed_work_sync(&priv->connection_loss_work);
|
|
if (!down_trylock(&hw_priv->scan.lock)) {
|
|
up(&hw_priv->scan.lock);
|
|
priv->delayed_link_loss = 0;
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->bss_loss_work, 0);
|
|
} else {
|
|
/* Scan is in progress. Delay reporting. */
|
|
/* Scan complete will trigger bss_loss_work */
|
|
priv->delayed_link_loss = 1;
|
|
/* Also we're starting watchdog. */
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->bss_loss_work, 10 * HZ);
|
|
}
|
|
break;
|
|
}
|
|
case WSM_EVENT_BSS_REGAINED:
|
|
{
|
|
bes_devel("[CQM] BSS regained.\n");
|
|
priv->delayed_link_loss = 0;
|
|
spin_lock(&priv->bss_loss_lock);
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
|
|
cancel_delayed_work_sync(&priv->bss_loss_work);
|
|
cancel_delayed_work_sync(&priv->connection_loss_work);
|
|
break;
|
|
}
|
|
case WSM_EVENT_RADAR_DETECTED:
|
|
//STUB();
|
|
break;
|
|
case WSM_EVENT_RCPI_RSSI:
|
|
{
|
|
/* RSSI: signed Q8.0, RCPI: unsigned Q7.1
|
|
* RSSI = RCPI / 2 - 110
|
|
*/
|
|
int rcpi_rssi = (int)(event->evt.eventData & 0xFF);
|
|
int cqm_evt;
|
|
if (priv->cqm_use_rssi)
|
|
rcpi_rssi = (s8)rcpi_rssi;
|
|
else
|
|
rcpi_rssi = rcpi_rssi / 2 - 110;
|
|
|
|
cqm_evt = (rcpi_rssi <= priv->cqm_rssi_thold) ?
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW :
|
|
NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH;
|
|
bes_devel("[CQM] RSSI event: %d.\n", rcpi_rssi);
|
|
ieee80211_cqm_rssi_notify(priv->vif, cqm_evt, rcpi_rssi, GFP_KERNEL);
|
|
break;
|
|
}
|
|
case WSM_EVENT_BT_INACTIVE:
|
|
//STUB();
|
|
break;
|
|
case WSM_EVENT_BT_ACTIVE:
|
|
//STUB();
|
|
break;
|
|
case WSM_EVENT_INACTIVITY:
|
|
{
|
|
int link_id = ffs((u32)(event->evt.eventData)) - 1;
|
|
struct sk_buff *skb;
|
|
struct ieee80211_mgmt *deauth;
|
|
struct bes2600_link_entry *entry = NULL;
|
|
|
|
bes_devel("Inactivity Event Recieved for "
|
|
"link_id %d\n", link_id);
|
|
cw12xx_unmap_link(priv, link_id);
|
|
|
|
skb = dev_alloc_skb(sizeof(struct ieee80211_mgmt) + 64);
|
|
skb_reserve(skb, 64);
|
|
deauth = (struct ieee80211_mgmt *)skb_put(skb, sizeof(struct ieee80211_mgmt));
|
|
WARN_ON(!deauth);
|
|
entry = &priv->link_id_db[link_id - 1];
|
|
deauth->duration = 0;
|
|
memcpy(deauth->da, priv->vif->addr, ETH_ALEN);
|
|
memcpy(deauth->sa, entry->mac/*priv->link_id_db[i].mac*/, ETH_ALEN);
|
|
memcpy(deauth->bssid, priv->vif->addr, ETH_ALEN);
|
|
deauth->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
|
|
IEEE80211_STYPE_DEAUTH | IEEE80211_FCTL_TODS);
|
|
deauth->u.deauth.reason_code = WLAN_REASON_DEAUTH_LEAVING;
|
|
deauth->seq_ctrl = 0;
|
|
ieee80211_rx_irqsafe(priv->hw, skb);
|
|
bes_devel(" Inactivity Deauth Frame sent for MAC SA %pM \t and DA %pM\n", deauth->sa, deauth->da);
|
|
queue_work(priv->hw_priv->workqueue, &priv->set_tim_work);
|
|
break;
|
|
}
|
|
case WSM_EVENT_PS_MODE_ERROR:
|
|
{
|
|
bes_err("PS Mode Error, Reason:%u\n", event->evt.eventData);
|
|
|
|
if (event->evt.eventData != WSM_PS_ERROR_AP_NO_DATA_AFTER_TIM &&
|
|
!priv->uapsd_info.uapsdFlags &&
|
|
(priv->user_pm_mode != WSM_PSM_PS))
|
|
{
|
|
bes2600_pwr_mark_ap_lp_bad(hw_priv);
|
|
priv->powersave_mode.pmMode = WSM_PSM_ACTIVE;
|
|
}
|
|
break;
|
|
}
|
|
case WSM_EVENT_WAKEUP_EVENT:
|
|
{
|
|
if (bes2600_chrdev_wakeup_by_event_get() == WAKEUP_EVENT_WSME) {
|
|
u16 src_port, reason;
|
|
reason = event->evt.eventData & 0xFFFF;
|
|
src_port = event->evt.eventData >> 16;
|
|
src_port = __be16_to_cpu(src_port);
|
|
bes_devel("wifi wakeup, reason: %u, src_port: %u\n", reason, src_port);
|
|
bes2600_chrdev_wifi_update_wakeup_reason(reason, src_port);
|
|
bes2600_chrdev_wakeup_by_event_set(WAKEUP_EVENT_NONE);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
bes2600_chrdev_wakeup_by_event_set(WAKEUP_EVENT_NONE);
|
|
up(&hw_priv->conf_lock);
|
|
__bes2600_free_event_queue(&list);
|
|
}
|
|
|
|
void bes2600_bss_loss_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, bss_loss_work.work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
int timeout; /* in beacons */
|
|
#ifdef BSS_LOSS_CHECK
|
|
struct sk_buff *skb;
|
|
struct ieee80211_tx_info *info;
|
|
static int bl_ck_cnt = 0;
|
|
static int bl_cfm_cnt = 0;
|
|
#endif
|
|
|
|
timeout = priv->cqm_link_loss_count -
|
|
priv->cqm_beacon_loss_count;
|
|
|
|
/* Skip the confimration procedure in P2P case */
|
|
if (priv->vif->p2p)
|
|
goto report;
|
|
|
|
spin_lock(&priv->bss_loss_lock);
|
|
#ifdef BSS_LOSS_CHECK
|
|
if (!priv->vif->cfg.assoc) {
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
bl_ck_cnt = 0;
|
|
bl_cfm_cnt = 0;
|
|
return;
|
|
}
|
|
#endif
|
|
if (priv->bss_loss_status == BES2600_BSS_LOSS_CHECKING) {
|
|
#ifdef BSS_LOSS_CHECK
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
bes_devel("bl checking\n");
|
|
priv->cmq_tx_success_count = 0;
|
|
bl_ck_cnt = 0;
|
|
bl_cfm_cnt = 0;
|
|
skb = ieee80211_nullfunc_get(priv->hw, priv->vif, 0, false);
|
|
if (!(WARN_ON(!skb))) {
|
|
info = IEEE80211_SKB_CB(skb);
|
|
info->control.vif = priv->vif;
|
|
bes2600_tx(priv->hw, NULL, skb);
|
|
/* Start watchdog -- if nullfunc TX doesn't fail
|
|
* in 1 sec, forward event to upper layers */
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->bss_loss_work, 1 * HZ);
|
|
}
|
|
return;
|
|
#else
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_CONFIRMED;
|
|
#endif
|
|
} else if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMING) {
|
|
#ifdef BSS_LOSS_CHECK
|
|
/* reset cca to workaround rx stuck issue */
|
|
// bes2600_cca_soft_reset();
|
|
|
|
/* succeeded to send last null frame */
|
|
bl_cfm_cnt = 0;
|
|
/* keep checking to wait bss regain*/
|
|
bes_devel("bl ck %d\n", bl_ck_cnt);
|
|
if (bl_ck_cnt++ < BSS_LOSS_CK_THR) {
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING;
|
|
skb = ieee80211_nullfunc_get(priv->hw, priv->vif, 0, false);
|
|
if (!(WARN_ON(!skb))) {
|
|
info = IEEE80211_SKB_CB(skb);
|
|
info->control.vif = priv->vif;
|
|
bes2600_tx(priv->hw, NULL, skb);
|
|
/* detect every 3s until receive bss regain event */
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->bss_loss_work, BSS_LOSS_CK_INV * HZ / 1000);
|
|
}
|
|
return;
|
|
} else {
|
|
bl_ck_cnt = 0;
|
|
}
|
|
#else
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
return;
|
|
#endif
|
|
} else if (priv->bss_loss_status == BES2600_BSS_LOSS_CONFIRMED) {
|
|
#ifdef BSS_LOSS_CHECK
|
|
/* failed to send last null frame */
|
|
/* check if continous failures occur */
|
|
if (priv->cmq_tx_success_count != 0) {
|
|
bes_devel("bl reset ck cfm s_cnt=%d\n", priv->cmq_tx_success_count);
|
|
bl_ck_cnt = 0;
|
|
bl_cfm_cnt = 0;
|
|
priv->cmq_tx_success_count = 0;
|
|
}
|
|
bes_devel("bl cfm %d\n", bl_cfm_cnt);
|
|
if (bl_cfm_cnt++ < BSS_LOSS_CFM_THR) {
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_CHECKING;
|
|
skb = ieee80211_nullfunc_get(priv->hw, priv->vif, 0, false);
|
|
if (!(WARN_ON(!skb))) {
|
|
info = IEEE80211_SKB_CB(skb);
|
|
info->control.vif = priv->vif;
|
|
bes2600_tx(priv->hw, NULL, skb);
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->bss_loss_work, BSS_LOSS_CK_INV * HZ / 1000);
|
|
}
|
|
return;
|
|
} else {
|
|
bl_cfm_cnt = 0;
|
|
}
|
|
#else
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
return;
|
|
#endif
|
|
}
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
|
|
report:
|
|
if (priv->cqm_beacon_loss_count) {
|
|
bes_devel("[CQM] Beacon loss.\n");
|
|
if (timeout <= 0)
|
|
timeout = 0;
|
|
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
|
ieee80211_cqm_beacon_miss_notify(priv->vif, GFP_KERNEL);
|
|
#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
|
|
} else {
|
|
timeout = 0;
|
|
}
|
|
|
|
cancel_delayed_work_sync(&priv->connection_loss_work);
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->connection_loss_work,
|
|
timeout * HZ / 10);
|
|
|
|
spin_lock(&priv->bss_loss_lock);
|
|
priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
|
|
spin_unlock(&priv->bss_loss_lock);
|
|
}
|
|
|
|
void bes2600_connection_loss_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif,
|
|
connection_loss_work.work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
|
|
bes_devel("[CQM] Reporting connection loss.\n");
|
|
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_BSS_LOST);
|
|
if(bes2600_suspend_status_get(hw_priv)) {
|
|
bes2600_pending_unjoin_set(hw_priv, priv->if_id);
|
|
} else
|
|
ieee80211_connection_loss(priv->vif);
|
|
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
|
// set disconnected in BSS_CHANGED_ASSOC
|
|
// bwifi_change_current_status(hw_priv, BWIFI_STATUS_DISCONNECTED);
|
|
#endif
|
|
}
|
|
|
|
void bes2600_tx_failure_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, tx_failure_work);
|
|
bes_devel("[CQM] Reporting TX failure.\n");
|
|
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
|
ieee80211_cqm_tx_fail_notify(priv->vif, GFP_KERNEL);
|
|
#else /* CONFIG_BES2600_USE_STE_EXTENSIONS */
|
|
(void)priv;
|
|
#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
|
|
}
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
/**
|
|
* bes2600_device_power_calc- Device power calculation
|
|
* from values fetch from SDD File.
|
|
*
|
|
* @priv: the private structure
|
|
* @Max_output_power: Power fetch from SDD
|
|
* @fe_cor: front-end loss correction
|
|
* @band: Either 2GHz or 5GHz
|
|
*
|
|
*/
|
|
void bes2600_device_power_calc(struct bes2600_common *hw_priv,
|
|
s16 max_output_power, s16 fe_cor, u32 band)
|
|
{
|
|
s16 power_calc;
|
|
|
|
power_calc = max_output_power - fe_cor;
|
|
if ((power_calc % 16) != 0)
|
|
power_calc += 16;
|
|
|
|
hw_priv->txPowerRange[band].max_power_level = power_calc/16;
|
|
/*
|
|
* 12dBm is control range supported by firmware.
|
|
* This means absolute min power is
|
|
* max_power_level - 12.
|
|
*/
|
|
hw_priv->txPowerRange[band].min_power_level =
|
|
hw_priv->txPowerRange[band].max_power_level - 12;
|
|
hw_priv->txPowerRange[band].stepping = 1;
|
|
|
|
}
|
|
#endif
|
|
/* ******************************************************************** */
|
|
/* Internal API */
|
|
|
|
|
|
|
|
/*
|
|
* This function is called to Parse the SDD file
|
|
*to extract listen_interval and PTA related information
|
|
*/
|
|
|
|
// UNUSED
|
|
/*
|
|
static int bes2600_parse_SDD_file(struct bes2600_common *hw_priv)
|
|
{
|
|
u8 *sdd_data = (u8 *)hw_priv->sdd->data;
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
s16 max_output_power_2G = 0;
|
|
s16 max_output_power_5G = 0;
|
|
s16 fe_cor_2G = 0;
|
|
s16 fe_cor_5G = 0;
|
|
int i;
|
|
#endif
|
|
struct bes2600_sdd {
|
|
u8 id ;
|
|
u8 length ;
|
|
u8 data[] ;
|
|
} *pElement;
|
|
int parsedLength = 0;
|
|
#define SDD_PTA_CFG_ELT_ID 0xEB
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
#define SDD_MAX_OUTPUT_POWER_2G4_ELT_ID 0xE3
|
|
#define SDD_MAX_OUTPUT_POWER_5G_ELT_ID 0xE4
|
|
#define SDD_FE_COR_2G4_ELT_ID 0x30
|
|
#define SDD_FE_COR_5G_ELT_ID 0x31
|
|
#define MIN(x, y, z) (x < y ? (x < z ? x : z) : (y < z ? y : z))
|
|
#endif
|
|
#define FIELD_OFFSET(type, field) ((u8 *)&((type *)0)->field - (u8 *)0)
|
|
|
|
hw_priv->is_BT_Present = false;
|
|
|
|
pElement = (struct bes2600_sdd *)sdd_data;
|
|
|
|
pElement = (struct bes2600_sdd *)((u8 *)pElement +
|
|
FIELD_OFFSET(struct bes2600_sdd, data) + pElement->length);
|
|
|
|
parsedLength += (FIELD_OFFSET(struct bes2600_sdd, data) +
|
|
pElement->length);
|
|
|
|
while (parsedLength <= hw_priv->sdd->size) {
|
|
switch (pElement->id) {
|
|
case SDD_PTA_CFG_ELT_ID:
|
|
{
|
|
hw_priv->conf_listen_interval =
|
|
(*((u16 *)pElement->data+1) >> 7) & 0x1F;
|
|
hw_priv->is_BT_Present = true;
|
|
bes_devel("PTA element found.\n");
|
|
bes_devel("Listen Interval %d\n",
|
|
hw_priv->conf_listen_interval);
|
|
}
|
|
break;
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
case SDD_MAX_OUTPUT_POWER_2G4_ELT_ID:
|
|
{
|
|
max_output_power_2G =
|
|
*((s16 *)pElement->data);
|
|
}
|
|
break;
|
|
case SDD_FE_COR_2G4_ELT_ID:
|
|
{
|
|
fe_cor_2G =
|
|
*((s16 *)pElement->data);
|
|
}
|
|
break;
|
|
case SDD_MAX_OUTPUT_POWER_5G_ELT_ID:
|
|
{
|
|
max_output_power_5G =
|
|
*((s16 *)(pElement->data + 4));
|
|
}
|
|
break;
|
|
case SDD_FE_COR_5G_ELT_ID:
|
|
{
|
|
fe_cor_5G = MIN(
|
|
*((s16 *)pElement->data),
|
|
*((s16 *)(pElement->data + 2)),
|
|
*((s16 *)(pElement->data + 4)));
|
|
|
|
fe_cor_5G = MIN(
|
|
fe_cor_5G,
|
|
*((s16 *)(pElement->data + 6)),
|
|
*((s16 *)(pElement->data + 8)));
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pElement = (struct bes2600_sdd *)
|
|
((u8 *)pElement + FIELD_OFFSET(struct bes2600_sdd, data)
|
|
+ pElement->length);
|
|
parsedLength +=
|
|
(FIELD_OFFSET(struct bes2600_sdd, data) + pElement->length);
|
|
}
|
|
|
|
if (hw_priv->is_BT_Present == false) {
|
|
bes_devel("PTA element NOT found.\n");
|
|
hw_priv->conf_listen_interval = 0;
|
|
}
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_device_power_calc(hw_priv, max_output_power_2G,
|
|
fe_cor_2G, NL80211_BAND_2GHZ);
|
|
bes2600_device_power_calc(hw_priv, max_output_power_5G,
|
|
fe_cor_5G, NL80211_BAND_5GHZ);
|
|
|
|
for (i = 0; i < 2; ++i) {
|
|
bes_devel("[STA] Power Values Read from SDD %s:"
|
|
"min_power_level[%d]: %d max_power_level[%d]:"
|
|
"%d stepping[%d]: %d\n", __func__, i,
|
|
hw_priv->txPowerRange[i].min_power_level, i,
|
|
hw_priv->txPowerRange[i].max_power_level, i,
|
|
hw_priv->txPowerRange[i].stepping);
|
|
}
|
|
|
|
|
|
bes_devel("%s output power before %d\n",__func__,hw_priv->output_power);
|
|
if (!hw_priv->output_power)
|
|
hw_priv->output_power=hw_priv->txPowerRange[NL80211_BAND_2GHZ].max_power_level;
|
|
|
|
bes_devel("%s output power after %d\n",__func__,hw_priv->output_power);
|
|
#else
|
|
bes_devel("%s output power before %d\n",__func__,hw_priv->output_power);
|
|
if (!hw_priv->output_power)
|
|
hw_priv->output_power=20;
|
|
bes_devel("%s output power after %d\n",__func__,hw_priv->output_power);
|
|
#endif
|
|
return 0;
|
|
|
|
#undef SDD_PTA_CFG_ELT_ID
|
|
#undef FIELD_OFFSET
|
|
}
|
|
*/
|
|
|
|
#ifndef USE_FEM
|
|
#define USE_FEM 0
|
|
#endif
|
|
const uint8_t sdd_22[] =
|
|
{
|
|
0xc0, 0x02, 0x02, 0x03,
|
|
0xc1, 0x06, 0x00, 0x01, 0x00, 0x00, 0x4c, 0x00,
|
|
0xc4, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0xc5, 0x02, 0x90, 0x65,
|
|
0xc7, 0x02, 0x05, 0x00,
|
|
0xe9, 0x02, 0xB8, 0x0B, // SddWakeupDriftInUsPerSecond=3000
|
|
0xea, 0x0a, 0x00, 0x00, 0x38, 0x00, 0x80, 0x00, 0x78, 0x00, 0x80, 0x00,
|
|
0xeb, 0x1e, 0x06, 0x00, 0x81, 0x02, 0x01, 0x34, 0x0d, 0x21, 0x11, 0x1d, 0x7c, 0x85, 0x6b, 0x6e, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0xfe, 0x02, 0x00, 0x00,
|
|
0xc1, 0x06, 0x01, 0x01, 0x00, 0x00, 0x30, 0x00,
|
|
0xf0, 0x22, 0x41, 0x44, 0x42, 0x5f, 0x32, 0x39, 0x30, 0x35, 0x5f, 0x31, 0x32, 0x35, 0x30, 0x5f, 0x52, 0x32, 0x30, 0x5f, 0x4c, 0x6f, 0x77, 0x5f, 0x53, 0x4d, 0x50, 0x53, 0x5f, 0x76, 0x37, 0x2e, 0x30, 0x31, 0x00, 0x00,
|
|
0xfe, 0x02, 0x00, 0x00,
|
|
0xc1, 0x06, 0x03, 0x01, 0x00, 0x00, 0xb4, 0x02,
|
|
//2 TODO: we must make sure the function bes2600_parse_SDD_file's array & sdd values the same(0xec& 0xed)
|
|
#if TEST_11B //2020-2-4
|
|
0xec, 0x12, 0x05, 0x00, 0x01, 0x78, 0x78, 0x02, 0x78, 0x78, 0x0b, 0x78, 0x78, 0x0c, 0x78, 0x78, 0x0d, 0x78, 0x78, 0x00,
|
|
#else
|
|
0xec, 0x12, 0x05, 0x00, 0x01, 0xc0, 0xc0, 0x02, 0xc0, 0xc0, 0x0b, 0xc0, 0xc0, 0x0c, 0xc0, 0xc0, 0x0d, 0xc0, 0xc0, 0x00,
|
|
#endif
|
|
0xed, 0x0e, 0x05, 0x00, 0xb8, 0x78, 0x22, 0x78, 0x34, 0x78, 0x64, 0x78, 0x95, 0x78, 0x00, 0x00,
|
|
0xe3, 0x1a, 0x1c, 0x01, 0x1c, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x18, 0x01, 0x10, 0x01, 0x00, 0x01, 0xf0, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0xe4, 0x1a, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x24, 0x01, 0x14, 0x01, 0x04, 0x01, 0xf4, 0x00, 0xe4, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x20, 0x1a, 0x8d, 0x03, 0x73, 0x02, 0x73, 0x03, 0x40, 0x03, 0xb3, 0x02, 0x40, 0x04, 0xcd, 0x04, 0x5f, 0x00, 0x00, 0x05, 0x5f, 0x00, 0x33, 0x05, 0x5f, 0x00, 0x00, 0x00,
|
|
0x21, 0x1a, 0x73, 0x03, 0x73, 0x02, 0x8d, 0x02, 0x40, 0x03, 0x1a, 0x02, 0x40, 0x04, 0x00, 0x04, 0x38, 0x00, 0xcd, 0x04, 0x66, 0x00, 0x00, 0x05, 0x57, 0x00, 0x00, 0x00,
|
|
0x22, 0x02, 0x07, 0x00,
|
|
0x23, 0x02, 0x07, 0x00,
|
|
0x30, 0x02, 0x08, 0x00,
|
|
0x31, 0x02, 0x08, 0x00,
|
|
0xe0, 0x02, 0x3a, 0x00,
|
|
0xe1, 0x02, 0x32, 0x00,
|
|
0x40, 0x0a, 0xf4, 0xff, 0x42, 0x00, 0xdd, 0xff, 0x3f, 0x00, 0x00, 0x00,
|
|
0x41, 0x0a, 0x80, 0xff, 0x63, 0x00, 0xd8, 0xff, 0x20, 0x00, 0x00, 0x00,
|
|
0x46, 0x22, 0x00, 0x00, 0x04, 0x1a, 0x08, 0x05, 0x01, 0x9f, 0x00, 0x00, 0x9a, 0xfd, 0x92, 0xff, 0x0a, 0x0a, 0x00, 0x00, 0xc0, 0x02, 0x6c, 0x00, 0x7c, 0xf5, 0x00, 0x00, 0x64, 0xfe, 0xca, 0xff, 0x78, 0x06, 0x00, 0x00,
|
|
0x47, 0x22, 0x00, 0x00, 0xbe, 0xf6, 0xe4, 0x00, 0x10, 0x05, 0x00, 0x00, 0x27, 0x01, 0x5c, 0x01, 0x44, 0x00, 0x00, 0x00, 0x14, 0xfe, 0x93, 0xff, 0xb0, 0x00, 0x00, 0x00, 0x70, 0x01, 0x4a, 0x00, 0x6f, 0xff, 0x00, 0x00,
|
|
0x44, 0x22, 0x00, 0x00, 0xb0, 0x03, 0x53, 0xfc, 0xf9, 0x05, 0x00, 0x00, 0xf6, 0xfe, 0x84, 0x00, 0x36, 0xff, 0x00, 0x00, 0x3b, 0x01, 0x68, 0xff, 0xe8, 0x00, 0x00, 0x00, 0x78, 0xfe, 0x7d, 0x00, 0x51, 0xff, 0x00, 0x00,
|
|
0x45, 0x22, 0x00, 0x00, 0xda, 0x03, 0xf0, 0xff, 0x34, 0xff, 0x00, 0x00, 0x9e, 0xff, 0xfd, 0xff, 0x0d, 0x00, 0x00, 0x00, 0xa8, 0x00, 0xe0, 0xff, 0xfe, 0xff, 0x00, 0x00, 0x7e, 0xff, 0x29, 0x00, 0xf0, 0xff, 0x00, 0x00,
|
|
0x42, 0x22, 0x00, 0x00, 0x08, 0xf8, 0xf0, 0xff, 0x58, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x89, 0xff, 0x06, 0x00, 0x05, 0x00, 0x00, 0x00, 0xd2, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x00, 0x00,
|
|
0x43, 0x22, 0x00, 0x00, 0xac, 0x00, 0x0a, 0xff, 0x63, 0x03, 0x00, 0x00, 0x31, 0xff, 0xf2, 0x00, 0xed, 0xfe, 0x00, 0x00, 0x58, 0x00, 0xa2, 0xff, 0x93, 0x00, 0x00, 0x00, 0xb4, 0xff, 0x65, 0x00, 0x74, 0xff, 0x00, 0x00,
|
|
#if DPD_CALI
|
|
#if ALI_CONFG
|
|
//2020-2-4
|
|
#if TEST_11B
|
|
0x48, 0x16, 0x20, 0x01, 0x20, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01, 0xe0, 0x01,
|
|
#else
|
|
0x48, 0x16, 0x00, 0x02, 0x00, 0x02, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xa0, 0x01, 0xa0, 0x01, 0xa0, 0x01,
|
|
#endif
|
|
#else
|
|
0x48, 0x16, 0x00, 0x01, 0x00, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01, 0xd0, 0x01,
|
|
#endif
|
|
#else
|
|
0x48, 0x16, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01, 0x40, 0x01,
|
|
#endif
|
|
#if (CHIP_WIFI_ROM_VER == 2)
|
|
0x49, 0x16, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x70, 0x01, 0x4c, 0x01, 0x2d, 0x01, 0x10, 0x01, 0xf5, 0x00, 0xde, 0x00,
|
|
#else
|
|
0x49, 0x16, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01, 0x60, 0x01,
|
|
#endif
|
|
0x50, 0x06, 0xe4, 0x48, 0x87, 0x43, 0xef, 0xe8,
|
|
0x51, 0x06, 0x9b, 0x0c, 0x67, 0xef, 0x4f, 0x16,
|
|
0x52, 0x1a, 0x00, 0x00, 0xb4, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0xae, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x53, 0x1a, 0x00, 0x00, 0x9a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0xcb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x54, 0x1a, 0x00, 0x00, 0x06, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x55, 0x1a, 0x00, 0x00, 0x10, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x98, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe4, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x56, 0x1a, 0x00, 0x00, 0xf3, 0xb5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x57, 0x1a, 0x00, 0x00, 0xc0, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd4, 0x4b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
#if WIFI_OUT_FEM
|
|
0xc6, 0x22, 0, 0, 0, 8, 0, 16, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 16, 0, 16, 0, 0, 0, 8, 0, 16, 0, 16, 0, 0,
|
|
#else
|
|
|
|
#ifdef CHIP_BEST2002
|
|
//bes EVB sw gpio20
|
|
0xc6, 0x22, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
#else //2003
|
|
//gpio24 io number = 2*8+4=20
|
|
#if (20 == RF_TX_CONTROL_IO )
|
|
//0xc6 eco proprietary; bailu EVB sw gpio24
|
|
0xc6, 0x22, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0, 16, 16, 0, 0, 0, 0, 0, 0,
|
|
#elif(12 == RF_TX_CONTROL_IO )
|
|
//0xc6 eco proprietary; yunque EVB sw gpio14 or bes EVB sw gpio20
|
|
0xc6, 0x22, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
#elif(2 == RF_TX_CONTROL_IO )
|
|
0xc6, 0x22, 0, 2, 0, 2, 0, 16, 0, 16, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 0,
|
|
#else
|
|
//0xc6 eco proprietary; yunque EVB sw gpio14 or bes EVB sw gpio20
|
|
0xc6, 0x22, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
#endif
|
|
#endif
|
|
|
|
#endif
|
|
|
|
0xfe, 0x02, 0x00, 0x00,
|
|
0xc1, 0x06, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
struct firmware g_sdd;
|
|
|
|
int bes2600_setup_mac(struct bes2600_common *hw_priv)
|
|
{
|
|
int ret = 0, if_id;
|
|
if (!hw_priv->sdd) {
|
|
const char *sdd_path = NULL;
|
|
struct wsm_configuration cfg = {
|
|
.dot11StationId = &hw_priv->mac_addr[0],
|
|
};
|
|
|
|
switch (hw_priv->hw_revision) {
|
|
case BES2600_HW_REV_CUT10:
|
|
sdd_path = SDD_FILE_10;
|
|
break;
|
|
case BES2600_HW_REV_CUT11:
|
|
sdd_path = SDD_FILE_11;
|
|
break;
|
|
case BES2600_HW_REV_CUT20:
|
|
sdd_path = SDD_FILE_20;
|
|
break;
|
|
case BES2600_HW_REV_CUT22:
|
|
sdd_path = SDD_FILE_22;
|
|
break;
|
|
case CW1250_HW_REV_CUT11:
|
|
sdd_path = SDD_FILE_1250_11;
|
|
break;
|
|
#if defined(BES2600_DETECTION_LOGIC)
|
|
case BES2600_HW_REV_CUT10:
|
|
sdd_path = SDD_FILE_1260_10;
|
|
break;
|
|
#endif/*BES2600_DETECTION_LOGIC*/
|
|
default:
|
|
BUG_ON(1);
|
|
}
|
|
|
|
#ifdef CONFIG_BES2600_STATIC_SDD
|
|
g_sdd.data = sdd_22;
|
|
g_sdd.size = sizeof(sdd_22);
|
|
hw_priv->sdd = &g_sdd;
|
|
cfg.dpdData = sdd_22;
|
|
cfg.dpdData_size = g_sdd.size;
|
|
#else
|
|
ret = request_firmware(&hw_priv->sdd,
|
|
sdd_path, hw_priv->pdev);
|
|
|
|
if (unlikely(ret)) {
|
|
bes_devel(
|
|
"%s: can't load sdd file %s.\n",
|
|
__func__, sdd_path);
|
|
return ret;
|
|
}
|
|
|
|
cfg.dpdData = hw_priv->sdd->data;
|
|
cfg.dpdData_size = hw_priv->sdd->size;
|
|
#endif
|
|
|
|
for (if_id = 0; if_id < 2;
|
|
if_id++) {
|
|
/* Set low-power mode. */
|
|
ret |= WARN_ON(wsm_configuration(hw_priv, &cfg,
|
|
if_id));
|
|
}
|
|
/* Parse SDD file for PTA element */
|
|
//bes2600_parse_SDD_file(hw_priv);
|
|
}
|
|
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bes2600_pending_offchanneltx_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, pending_offchanneltx_work.work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
|
|
down(&hw_priv->conf_lock);
|
|
|
|
bes_devel("OFFCHAN PEND IN\n");
|
|
bes2600_disable_listening(priv);
|
|
hw_priv->roc_if_id = -1;
|
|
bes_devel("OFFCHAN PEND OUT\n");
|
|
|
|
up(&hw_priv->scan.lock);
|
|
up(&hw_priv->conf_lock);
|
|
}
|
|
|
|
void bes2600_offchannel_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, offchannel_work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
|
|
struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
|
|
|
|
BUG_ON(queueId >= 4);
|
|
BUG_ON(!hw_priv->channel);
|
|
|
|
if (unlikely(down_trylock(&hw_priv->scan.lock))) {
|
|
int ret = 0;
|
|
bes_devel("bes2600_offchannel_work***** drop frame\n");
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_remove(hw_priv, queue,
|
|
hw_priv->pending_frame_id);
|
|
#else
|
|
ret = bes2600_queue_remove(queue, hw_priv->pending_frame_id);
|
|
#endif
|
|
if (ret)
|
|
bes_err("bes2600_offchannel_work: "
|
|
"queue_remove failed %d\n", ret);
|
|
wsm_unlock_tx(hw_priv);
|
|
return;
|
|
}
|
|
down(&hw_priv->conf_lock);
|
|
bes_devel("OFFCHAN WORK IN %d\n", priv->if_id);
|
|
hw_priv->roc_if_id = priv->if_id;
|
|
if (likely(!priv->join_status)) {
|
|
wsm_vif_flush_tx(priv);
|
|
bes2600_enable_listening(priv, hw_priv->channel);
|
|
/* bes2600_update_filtering(priv); */
|
|
}
|
|
if (unlikely(!priv->join_status))
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_remove(hw_priv, queue,
|
|
hw_priv->pending_frame_id);
|
|
#else
|
|
bes2600_queue_remove(queue, hw_priv->pending_frame_id);
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
else
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_requeue(hw_priv, queue,
|
|
hw_priv->pending_frame_id, false);
|
|
#else
|
|
bes2600_queue_requeue(queue, hw_priv->pending_frame_id, false);
|
|
#endif
|
|
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->pending_offchanneltx_work, 204 * HZ/1000);
|
|
bes_devel("OFFCHAN WORK OUT %d\n", priv->if_id);
|
|
up(&hw_priv->conf_lock);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
void bes2600_join_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, join_work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
u8 queueId = bes2600_queue_get_queue_id(hw_priv->pending_frame_id);
|
|
struct bes2600_queue *queue = &hw_priv->tx_queue[queueId];
|
|
const struct bes2600_txpriv *txpriv = NULL;
|
|
struct sk_buff *skb = NULL;
|
|
const struct wsm_tx *wsm;
|
|
const struct ieee80211_hdr *frame;
|
|
const u8 *bssid;
|
|
struct cfg80211_bss *bss;
|
|
const u8 *ssidie;
|
|
const u8 *dtimie;
|
|
const struct ieee80211_tim_ie *tim = NULL;
|
|
struct wsm_protected_mgmt_policy mgmt_policy;
|
|
struct ieee80211_conf *conf = &hw_priv->hw->conf;
|
|
struct wsm_template_frame probe_tmp = {
|
|
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
|
|
};
|
|
/*struct wsm_reset reset = {
|
|
.reset_statistics = true,
|
|
};*/
|
|
|
|
|
|
BUG_ON(queueId >= 4);
|
|
if (bes2600_queue_get_skb(queue, hw_priv->pending_frame_id,
|
|
&skb, &txpriv)) {
|
|
wsm_unlock_tx(hw_priv);
|
|
return;
|
|
}
|
|
wsm = (struct wsm_tx *)&skb->data[0];
|
|
frame = (struct ieee80211_hdr *)&skb->data[txpriv->offset];
|
|
bssid = &frame->addr1[0]; /* AP SSID in a 802.11 frame */
|
|
|
|
BUG_ON(!wsm);
|
|
BUG_ON(!hw_priv->channel);
|
|
|
|
if (unlikely(priv->join_status)) {
|
|
atomic_set(&priv->connect_in_process, 0);
|
|
wsm_lock_tx(hw_priv);
|
|
bes2600_unjoin_work(&priv->unjoin_work);
|
|
}
|
|
|
|
cancel_delayed_work_sync(&priv->join_timeout);
|
|
|
|
bss = cfg80211_get_bss(hw_priv->hw->wiphy, hw_priv->channel, bssid, NULL, 0,
|
|
IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
|
|
|
|
if (!bss) {
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_remove(hw_priv, queue, hw_priv->pending_frame_id);
|
|
#else
|
|
bes2600_queue_remove(queue, hw_priv->pending_frame_id);
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
wsm_unlock_tx(hw_priv);
|
|
return;
|
|
}
|
|
ssidie = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
|
|
dtimie = ieee80211_bss_get_ie(bss, WLAN_EID_TIM);
|
|
if (dtimie)
|
|
tim = (struct ieee80211_tim_ie *)&dtimie[2];
|
|
|
|
down(&hw_priv->conf_lock);
|
|
{
|
|
struct wsm_switch_channel channel;
|
|
struct wsm_join join = {
|
|
.mode = (bss->capability & WLAN_CAPABILITY_IBSS) ?
|
|
WSM_JOIN_MODE_IBSS : WSM_JOIN_MODE_BSS,
|
|
.preambleType = WSM_JOIN_PREAMBLE_SHORT,
|
|
.probeForJoin = 1,
|
|
/* dtimPeriod will be updated after association */
|
|
.dtimPeriod = 1,
|
|
.beaconInterval = bss->beacon_interval,
|
|
};
|
|
|
|
if (priv->if_id)
|
|
join.flags |= WSM_FLAG_MAC_INSTANCE_1;
|
|
else
|
|
join.flags &= ~WSM_FLAG_MAC_INSTANCE_1;
|
|
|
|
/* BT Coex related changes */
|
|
if (hw_priv->is_BT_Present) {
|
|
if (((hw_priv->conf_listen_interval * 100) %
|
|
bss->beacon_interval) == 0)
|
|
priv->listen_interval =
|
|
((hw_priv->conf_listen_interval * 100) /
|
|
bss->beacon_interval);
|
|
else
|
|
priv->listen_interval =
|
|
((hw_priv->conf_listen_interval * 100) /
|
|
bss->beacon_interval + 1);
|
|
}
|
|
|
|
if (tim && tim->dtim_period > 1) {
|
|
join.dtimPeriod = tim->dtim_period;
|
|
priv->join_dtim_period = tim->dtim_period;
|
|
}
|
|
priv->beacon_int = bss->beacon_interval;
|
|
bes_devel("[STA] Join DTIM: %d, interval: %d\n",
|
|
join.dtimPeriod, priv->beacon_int);
|
|
|
|
hw_priv->is_go_thru_go_neg = false;
|
|
join.channelNumber = hw_priv->channel->hw_value;
|
|
|
|
/* basicRateSet will be updated after association.
|
|
Currently these values are hardcoded */
|
|
if (hw_priv->channel->band == NL80211_BAND_5GHZ) {
|
|
join.band = WSM_PHY_BAND_5G;
|
|
join.basicRateSet = 64; /*6 mbps*/
|
|
}else{
|
|
join.band = WSM_PHY_BAND_2_4G;
|
|
join.basicRateSet = 7; /*1, 2, 5.5 mbps*/
|
|
}
|
|
memcpy(&join.bssid[0], bssid, sizeof(join.bssid));
|
|
memcpy(&priv->join_bssid[0], bssid, sizeof(priv->join_bssid));
|
|
|
|
if (ssidie) {
|
|
join.ssidLength = ssidie[1];
|
|
if (WARN_ON(join.ssidLength > sizeof(join.ssid)))
|
|
join.ssidLength = sizeof(join.ssid);
|
|
memcpy(&join.ssid[0], &ssidie[2], join.ssidLength);
|
|
if(strstr(&join.ssid[0],"5.1.4"))
|
|
msleep(200);
|
|
#ifdef ROAM_OFFLOAD
|
|
if((priv->vif->type == NL80211_IFTYPE_STATION)) {
|
|
priv->ssid_length = join.ssidLength;
|
|
memcpy(priv->ssid, &join.ssid[0], priv->ssid_length);
|
|
}
|
|
#endif /*ROAM_OFFLOAD*/
|
|
}
|
|
|
|
if (priv->vif->p2p) {
|
|
join.flags |= WSM_JOIN_FLAGS_P2P_GO;
|
|
#ifdef P2P_MULTIVIF
|
|
join.flags |= (1 << 6);
|
|
#endif
|
|
join.basicRateSet =
|
|
bes2600_rate_mask_to_wsm(hw_priv, 0xFF0);
|
|
}
|
|
|
|
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_JOIN);
|
|
wsm_flush_tx(hw_priv);
|
|
|
|
/* Queue unjoin if not associated in 3 sec. */
|
|
queue_delayed_work(hw_priv->workqueue,
|
|
&priv->join_timeout, 3 * HZ);
|
|
|
|
bes2600_disable_listening(priv);
|
|
|
|
//WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id));
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
0, hw_priv->ba_tid_mask, priv->if_id));
|
|
spin_lock_bh(&hw_priv->ba_lock);
|
|
hw_priv->ba_ena = false;
|
|
hw_priv->ba_cnt = 0;
|
|
hw_priv->ba_acc = 0;
|
|
hw_priv->ba_hist = 0;
|
|
hw_priv->ba_cnt_rx = 0;
|
|
hw_priv->ba_acc_rx = 0;
|
|
spin_unlock_bh(&hw_priv->ba_lock);
|
|
|
|
mgmt_policy.protectedMgmtEnable = 0;
|
|
mgmt_policy.unprotectedMgmtFramesAllowed = 1;
|
|
mgmt_policy.encryptionForAuthFrame = 1;
|
|
wsm_set_protected_mgmt_policy(hw_priv, &mgmt_policy,
|
|
priv->if_id);
|
|
|
|
/* need to switch channel before join */
|
|
channel.channelMode = NL80211_CHAN_NO_HT << 4;
|
|
channel.channelSwitchCount = 0;
|
|
channel.newChannelNumber = conf->chandef.chan->hw_value;
|
|
wsm_switch_channel(hw_priv, &channel, priv->if_id);
|
|
|
|
/* avoid lmac assert when wpa_supplicant connect to ap without scan */
|
|
probe_tmp.skb = ieee80211_probereq_get(hw_priv->hw, priv->vif->addr, NULL, 0, 0);
|
|
if (probe_tmp.skb) {
|
|
#ifdef P2P_MULTIVIF
|
|
wsm_set_template_frame(hw_priv, &probe_tmp, 0);
|
|
#else
|
|
wsm_set_template_frame(hw_priv, &probe_tmp, priv->if_id);
|
|
#endif
|
|
dev_kfree_skb(probe_tmp.skb);
|
|
}
|
|
|
|
if (wsm_join(hw_priv, &join, priv->if_id)) {
|
|
memset(&priv->join_bssid[0],
|
|
0, sizeof(priv->join_bssid));
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_remove(hw_priv, queue,
|
|
hw_priv->pending_frame_id);
|
|
#else
|
|
bes2600_queue_remove(queue, hw_priv->pending_frame_id);
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
cancel_delayed_work_sync(&priv->join_timeout);
|
|
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
|
|
} else {
|
|
/* Upload keys */
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
bes2600_queue_requeue(hw_priv, queue,
|
|
hw_priv->pending_frame_id, true);
|
|
#else
|
|
bes2600_queue_requeue(queue, hw_priv->pending_frame_id,
|
|
true);
|
|
#endif
|
|
priv->join_status = BES2600_JOIN_STATUS_STA;
|
|
atomic_set(&priv->connect_in_process, 1);
|
|
|
|
/* 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;
|
|
|
|
}
|
|
bes2600_update_filtering(priv);
|
|
}
|
|
up(&hw_priv->conf_lock);
|
|
if (bss)
|
|
cfg80211_put_bss(hw_priv->hw->wiphy, bss);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
void bes2600_join_timeout(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, join_timeout.work);
|
|
bes_devel("[WSM] Issue unjoin command (TMO).\n");
|
|
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_JOIN);
|
|
atomic_set(&priv->connect_in_process, 0);
|
|
wsm_lock_tx(priv->hw_priv);
|
|
bes2600_unjoin_work(&priv->unjoin_work);
|
|
}
|
|
|
|
void bes2600_unjoin_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_vif *priv =
|
|
container_of(work, struct bes2600_vif, unjoin_work);
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
|
|
struct wsm_reset reset = {
|
|
.reset_statistics = true,
|
|
};
|
|
bool is_htcapie = false;
|
|
int i;
|
|
struct bes2600_vif *tmp_priv;
|
|
|
|
del_timer_sync(&hw_priv->ba_timer);
|
|
down(&hw_priv->conf_lock);
|
|
if (unlikely(atomic_read(&hw_priv->scan.in_progress)
|
|
|| atomic_read(&priv->connect_in_process))) {
|
|
if (priv->delayed_unjoin) {
|
|
wiphy_dbg(priv->hw->wiphy,
|
|
"%s: Delayed unjoin "
|
|
"is already scheduled.\n",
|
|
__func__);
|
|
wsm_unlock_tx(hw_priv);
|
|
} else {
|
|
bes_devel("delay unjoin work, scan:%d connect:%d\n",
|
|
atomic_read(&hw_priv->scan.in_progress), atomic_read(&priv->connect_in_process));
|
|
priv->delayed_unjoin = true;
|
|
}
|
|
up(&hw_priv->conf_lock);
|
|
return;
|
|
}
|
|
|
|
if (priv->join_status &&
|
|
priv->join_status > BES2600_JOIN_STATUS_STA) {
|
|
wiphy_err(priv->hw->wiphy,
|
|
"%s: Unexpected: join status: %d\n",
|
|
__func__, priv->join_status);
|
|
BUG_ON(1);
|
|
}
|
|
if (priv->join_status) {
|
|
cancel_work_sync(&priv->update_filtering_work);
|
|
cancel_work_sync(&priv->set_beacon_wakeup_period_work);
|
|
memset(&priv->join_bssid[0], 0, sizeof(priv->join_bssid));
|
|
#ifdef BES2600_TX_RX_OPT
|
|
txrx_opt_timer_exit(priv);
|
|
#endif
|
|
bes2600_pwr_clear_busy_event(priv->hw_priv, BES_PWR_LOCK_ON_PS_ACTIVE);
|
|
bes2600_pwr_clear_ap_lp_bad_mark(hw_priv);
|
|
priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
|
|
|
|
/* Unjoin is a reset. */
|
|
wsm_flush_tx(hw_priv);
|
|
WARN_ON(wsm_keep_alive_period(hw_priv, 0, priv->if_id));
|
|
WARN_ON(wsm_reset(hw_priv, &reset, priv->if_id));
|
|
WARN_ON(wsm_set_output_power(hw_priv,
|
|
hw_priv->output_power * 10, priv->if_id));
|
|
priv->join_dtim_period = 0;
|
|
priv->cipherType = 0;
|
|
WARN_ON(bes2600_setup_mac_pvif(priv));
|
|
bes2600_free_event_queue(hw_priv);
|
|
cancel_work_sync(&hw_priv->event_handler);
|
|
cancel_delayed_work_sync(&priv->connection_loss_work);
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
0, hw_priv->ba_tid_mask, priv->if_id));
|
|
priv->disable_beacon_filter = false;
|
|
bes2600_update_filtering(priv);
|
|
priv->setbssparams_done = false;
|
|
memset(&priv->association_mode, 0,
|
|
sizeof(priv->association_mode));
|
|
memset(&priv->bss_params, 0, sizeof(priv->bss_params));
|
|
memset(&priv->firmware_ps_mode, 0,
|
|
sizeof(priv->firmware_ps_mode));
|
|
priv->htcap = false;
|
|
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) && tmp_priv->htcap)
|
|
is_htcapie = true;
|
|
}
|
|
|
|
if (is_htcapie) {
|
|
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11N_THROTTLE;
|
|
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11N_THROTTLE;
|
|
bes_devel("UNJOIN 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("UNJOIN 11BG %d\n",hw_priv->vif0_throttle);
|
|
}
|
|
bes_devel("[STA] Unjoin.\n");
|
|
priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
|
|
}
|
|
|
|
up(&hw_priv->conf_lock);
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
int bes2600_enable_listening(struct bes2600_vif *priv,
|
|
struct ieee80211_channel *chan)
|
|
{
|
|
/* TODO:COMBO: Channel is common to HW currently in mac80211.
|
|
Change the code below once channel is made per VIF */
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
struct wsm_start start = {
|
|
#ifdef P2P_MULTIVIF
|
|
.mode = WSM_START_MODE_P2P_DEV | (priv->if_id ? (5 << 4) : 0),
|
|
#else
|
|
.mode = WSM_START_MODE_P2P_DEV | (priv->if_id << 4),
|
|
#endif
|
|
.band = (chan->band == NL80211_BAND_5GHZ) ?
|
|
WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G,
|
|
.channelNumber = chan->hw_value,
|
|
.beaconInterval = 100,
|
|
.DTIMPeriod = 1,
|
|
.probeDelay = 0,
|
|
.basicRateSet = 0x0F,
|
|
};
|
|
if(priv->if_id != 2) {
|
|
//WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
if (priv->join_status == BES2600_JOIN_STATUS_MONITOR)
|
|
return 0;
|
|
if (priv->join_status == BES2600_JOIN_STATUS_PASSIVE)
|
|
priv->join_status = BES2600_JOIN_STATUS_MONITOR;
|
|
|
|
WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
|
|
bes_devel("bes2600_enable_listening if_id:%d\n", priv->if_id);
|
|
return wsm_start(hw_priv, &start, CW12XX_GENERIC_IF_ID);
|
|
}
|
|
|
|
int bes2600_disable_listening(struct bes2600_vif *priv)
|
|
{
|
|
int ret;
|
|
struct wsm_reset reset = {
|
|
.reset_statistics = true,
|
|
};
|
|
if(priv->if_id != 2) {
|
|
WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
|
|
return 0;
|
|
}
|
|
priv->join_status = BES2600_JOIN_STATUS_PASSIVE;
|
|
|
|
WARN_ON(priv->join_status > BES2600_JOIN_STATUS_MONITOR);
|
|
|
|
if (priv->hw_priv->roc_if_id == -1)
|
|
return 0;
|
|
bes_devel("bes2600_disable_listening if_id:%d\n", priv->if_id);
|
|
ret = wsm_reset(priv->hw_priv, &reset, CW12XX_GENERIC_IF_ID);
|
|
return ret;
|
|
}
|
|
|
|
/* TODO:COMBO:UAPSD will be supported only on one interface */
|
|
int bes2600_set_uapsd_param(struct bes2600_vif *priv,
|
|
const struct wsm_edca_params *arg)
|
|
{
|
|
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
|
|
int ret;
|
|
u16 uapsdFlags = 0;
|
|
|
|
/* Here's the mapping AC [queue, bit]
|
|
VO [0,3], VI [1, 2], BE [2, 1], BK [3, 0]*/
|
|
|
|
if (arg->params[0].uapsdEnable)
|
|
uapsdFlags |= 1 << 3;
|
|
|
|
if (arg->params[1].uapsdEnable)
|
|
uapsdFlags |= 1 << 2;
|
|
|
|
if (arg->params[2].uapsdEnable)
|
|
uapsdFlags |= 1 << 1;
|
|
|
|
if (arg->params[3].uapsdEnable)
|
|
uapsdFlags |= 1;
|
|
|
|
/* Currently pseudo U-APSD operation is not supported, so setting
|
|
* MinAutoTriggerInterval, MaxAutoTriggerInterval and
|
|
* AutoTriggerStep to 0 */
|
|
|
|
priv->uapsd_info.uapsdFlags = cpu_to_le16(uapsdFlags);
|
|
priv->uapsd_info.minAutoTriggerInterval = 0;
|
|
priv->uapsd_info.maxAutoTriggerInterval = 0;
|
|
priv->uapsd_info.autoTriggerStep = 0;
|
|
|
|
ret = wsm_set_uapsd_info(hw_priv, &priv->uapsd_info,
|
|
priv->if_id);
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_ba_work(struct work_struct *work)
|
|
{
|
|
struct bes2600_common *hw_priv =
|
|
container_of(work, struct bes2600_common, ba_work);
|
|
u8 tx_ba_tid_mask;
|
|
|
|
/* TODO:COMBO: reenable this part of code */
|
|
/* if (priv->join_status != BES2600_JOIN_STATUS_STA)
|
|
return;
|
|
if (!priv->setbssparams_done)
|
|
return;*/
|
|
|
|
bes_devel("BA work****\n");
|
|
spin_lock_bh(&hw_priv->ba_lock);
|
|
// tx_ba_tid_mask = hw_priv->ba_ena ? hw_priv->ba_tid_mask : 0;
|
|
tx_ba_tid_mask = hw_priv->ba_tid_mask;
|
|
spin_unlock_bh(&hw_priv->ba_lock);
|
|
|
|
wsm_lock_tx(hw_priv);
|
|
|
|
WARN_ON(wsm_set_block_ack_policy(hw_priv,
|
|
tx_ba_tid_mask, hw_priv->ba_tid_mask, -1)); /*TODO:COMBO*/
|
|
|
|
wsm_unlock_tx(hw_priv);
|
|
}
|
|
|
|
void bes2600_ba_timer(struct timer_list *t)
|
|
{
|
|
bool ba_ena;
|
|
struct bes2600_common *hw_priv = from_timer(hw_priv, t, ba_timer);
|
|
|
|
spin_lock_bh(&hw_priv->ba_lock);
|
|
bes2600_debug_ba(hw_priv, hw_priv->ba_cnt, hw_priv->ba_acc,
|
|
hw_priv->ba_cnt_rx, hw_priv->ba_acc_rx);
|
|
|
|
if (atomic_read(&hw_priv->scan.in_progress)) {
|
|
hw_priv->ba_cnt = 0;
|
|
hw_priv->ba_acc = 0;
|
|
hw_priv->ba_cnt_rx = 0;
|
|
hw_priv->ba_acc_rx = 0;
|
|
goto skip_statistic_update;
|
|
}
|
|
|
|
if (hw_priv->ba_cnt >= BES2600_BLOCK_ACK_CNT &&
|
|
(hw_priv->ba_acc / hw_priv->ba_cnt >= BES2600_BLOCK_ACK_THLD ||
|
|
(hw_priv->ba_cnt_rx >= BES2600_BLOCK_ACK_CNT &&
|
|
hw_priv->ba_acc_rx / hw_priv->ba_cnt_rx >=
|
|
BES2600_BLOCK_ACK_THLD)))
|
|
ba_ena = true;
|
|
else
|
|
ba_ena = false;
|
|
|
|
hw_priv->ba_cnt = 0;
|
|
hw_priv->ba_acc = 0;
|
|
hw_priv->ba_cnt_rx = 0;
|
|
hw_priv->ba_acc_rx = 0;
|
|
|
|
if (ba_ena != hw_priv->ba_ena) {
|
|
if (ba_ena || ++hw_priv->ba_hist >= BES2600_BLOCK_ACK_HIST) {
|
|
hw_priv->ba_ena = ba_ena;
|
|
hw_priv->ba_hist = 0;
|
|
#if 0
|
|
bes_devel("[STA] %s block ACK:\n",
|
|
ba_ena ? "enable" : "disable");
|
|
queue_work(hw_priv->workqueue, &hw_priv->ba_work);
|
|
#endif
|
|
}
|
|
} else if (hw_priv->ba_hist)
|
|
--hw_priv->ba_hist;
|
|
|
|
skip_statistic_update:
|
|
spin_unlock_bh(&hw_priv->ba_lock);
|
|
}
|
|
|
|
int bes2600_vif_setup(struct bes2600_vif *priv)
|
|
{
|
|
struct bes2600_common *hw_priv = priv->hw_priv;
|
|
int ret = 0;
|
|
|
|
/* Setup per vif workitems and locks */
|
|
spin_lock_init(&priv->vif_lock);
|
|
INIT_WORK(&priv->join_work, bes2600_join_work);
|
|
INIT_DELAYED_WORK(&priv->join_timeout, bes2600_join_timeout);
|
|
INIT_WORK(&priv->unjoin_work, bes2600_unjoin_work);
|
|
INIT_WORK(&priv->wep_key_work, bes2600_wep_key_work);
|
|
INIT_WORK(&priv->offchannel_work, bes2600_offchannel_work);
|
|
INIT_DELAYED_WORK(&priv->bss_loss_work, bes2600_bss_loss_work);
|
|
INIT_DELAYED_WORK(&priv->connection_loss_work,
|
|
bes2600_connection_loss_work);
|
|
spin_lock_init(&priv->bss_loss_lock);
|
|
INIT_WORK(&priv->tx_failure_work, bes2600_tx_failure_work);
|
|
spin_lock_init(&priv->ps_state_lock);
|
|
INIT_DELAYED_WORK(&priv->set_cts_work, bes2600_set_cts_work);
|
|
INIT_WORK(&priv->set_tim_work, bes2600_set_tim_work);
|
|
INIT_WORK(&priv->multicast_start_work, bes2600_multicast_start_work);
|
|
INIT_WORK(&priv->multicast_stop_work, bes2600_multicast_stop_work);
|
|
INIT_WORK(&priv->link_id_work, bes2600_link_id_work);
|
|
INIT_DELAYED_WORK(&priv->link_id_gc_work, bes2600_link_id_gc_work);
|
|
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
|
INIT_WORK(&priv->linkid_reset_work, bes2600_link_id_reset);
|
|
#endif
|
|
INIT_WORK(&priv->update_filtering_work, bes2600_update_filtering_work);
|
|
INIT_DELAYED_WORK(&priv->pending_offchanneltx_work,
|
|
bes2600_pending_offchanneltx_work);
|
|
INIT_WORK(&priv->set_beacon_wakeup_period_work,
|
|
bes2600_set_beacon_wakeup_period_work);
|
|
#ifdef AP_HT_CAP_UPDATE
|
|
INIT_WORK(&priv->ht_info_update_work, bes2600_ht_info_update_work);
|
|
#endif
|
|
timer_setup(&priv->mcast_timeout, bes2600_mcast_timeout, 0);
|
|
|
|
priv->setbssparams_done = false;
|
|
priv->power_set_true = 0;
|
|
priv->user_power_set_true = 0;
|
|
priv->user_pm_mode = 0;
|
|
ret = bes2600_debug_init_priv(hw_priv, priv);
|
|
if (WARN_ON(ret))
|
|
goto out;
|
|
|
|
/* Initialising the broadcast filter */
|
|
memset(priv->broadcast_filter.MacAddr, 0xFF, ETH_ALEN);
|
|
priv->broadcast_filter.nummacaddr = 1;
|
|
priv->broadcast_filter.address_mode = WSM_FILTER_ADDR_MODE_A1;
|
|
priv->broadcast_filter.filter_mode = WSM_FILTER_ACTION_FILTER_OUT;
|
|
priv->htcap = false;
|
|
|
|
bes_devel("%s: enabling priv\n", __func__);
|
|
atomic_set(&priv->enabled, 1);
|
|
|
|
spin_lock(&hw_priv->vif_list_lock);
|
|
hw_priv->if_id_slot |= BIT(priv->if_id);
|
|
hw_priv->vif_list[priv->if_id] = priv->vif;
|
|
atomic_inc(&hw_priv->num_vifs);
|
|
spin_unlock(&hw_priv->vif_list_lock);
|
|
atomic_set(&priv->connect_in_process, 0);
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
if (priv->if_id < 2) {
|
|
#endif
|
|
/* default EDCA */
|
|
WSM_EDCA_SET(&priv->edca, 0, 0x0002, 0x0003, 0x0007,
|
|
47, 0xc8, false);
|
|
WSM_EDCA_SET(&priv->edca, 1, 0x0002, 0x0007, 0x000f,
|
|
94, 0xc8, false);
|
|
WSM_EDCA_SET(&priv->edca, 2, 0x0003, 0x000f, 0x03ff,
|
|
0, 0xc8, false);
|
|
WSM_EDCA_SET(&priv->edca, 3, 0x0007, 0x000f, 0x03ff,
|
|
0, 0xc8, false);
|
|
ret = wsm_set_edca_params(hw_priv, &priv->edca, priv->if_id);
|
|
if (WARN_ON(ret))
|
|
goto out;
|
|
|
|
ret = bes2600_set_uapsd_param(priv, &priv->edca);
|
|
if (WARN_ON(ret))
|
|
goto out;
|
|
|
|
memset(priv->bssid, ~0, ETH_ALEN);
|
|
priv->wep_default_key_id = -1;
|
|
priv->cipherType = 0;
|
|
#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
|
|
|
|
/* Temporary configuration - beacon filter table */
|
|
__bes2600_bf_configure(priv);
|
|
#ifdef P2P_MULTIVIF
|
|
}
|
|
#endif
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_setup_mac_pvif(struct bes2600_vif *priv)
|
|
{
|
|
int ret = 0;
|
|
/* NOTE: There is a bug in FW: it reports signal
|
|
* as RSSI if RSSI subscription is enabled.
|
|
* It's not enough to set WSM_RCPI_RSSI_USE_RSSI. */
|
|
/* NOTE2: RSSI based reports have been switched to RCPI, since
|
|
* FW has a bug and RSSI reported values are not stable,
|
|
* what can leads to signal level oscilations in user-end applications */
|
|
struct wsm_rcpi_rssi_threshold threshold = {
|
|
.rssiRcpiMode = WSM_RCPI_RSSI_THRESHOLD_ENABLE |
|
|
WSM_RCPI_RSSI_DONT_USE_UPPER |
|
|
WSM_RCPI_RSSI_DONT_USE_LOWER,
|
|
.rollingAverageCount = 16,
|
|
};
|
|
|
|
/* Remember the decission here to make sure, we will handle
|
|
* the RCPI/RSSI value correctly on WSM_EVENT_RCPI_RSS */
|
|
if (threshold.rssiRcpiMode & WSM_RCPI_RSSI_USE_RSSI)
|
|
priv->cqm_use_rssi = true;
|
|
|
|
|
|
/* Configure RSSI/SCPI reporting as RSSI. */
|
|
#ifdef P2P_MULTIVIF
|
|
ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold,
|
|
priv->if_id ? 0 : 0);
|
|
#else
|
|
ret = wsm_set_rcpi_rssi_threshold(priv->hw_priv, &threshold,
|
|
priv->if_id);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
void bes2600_rem_chan_timeout(struct work_struct *work)
|
|
{
|
|
struct bes2600_common *hw_priv =
|
|
container_of(work, struct bes2600_common, rem_chan_timeout.work);
|
|
int ret, if_id;
|
|
struct bes2600_vif *priv;
|
|
|
|
if (atomic_read(&hw_priv->remain_on_channel) == 0) {
|
|
return;
|
|
}
|
|
ieee80211_remain_on_channel_expired(hw_priv->hw);
|
|
|
|
down(&hw_priv->conf_lock);
|
|
if_id = hw_priv->roc_if_id;
|
|
bes_devel("ROC TO IN %d\n", if_id);
|
|
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
|
|
ret = WARN_ON(__bes2600_flush(hw_priv, false, if_id));
|
|
|
|
if (!ret) {
|
|
wsm_unlock_tx(hw_priv);
|
|
bes2600_disable_listening(priv);
|
|
}
|
|
atomic_set(&hw_priv->remain_on_channel, 0);
|
|
hw_priv->roc_if_id = -1;
|
|
|
|
bes_devel("ROC TO OUT %d\n", if_id);
|
|
|
|
up(&hw_priv->conf_lock);
|
|
up(&hw_priv->scan.lock);
|
|
bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_ROC);
|
|
}
|
|
|
|
void bes2600_dynamic_opt_txrx_work(struct work_struct *work)
|
|
{
|
|
bool multivif_connected = false;
|
|
struct bes2600_common *hw_priv =
|
|
container_of(work, struct bes2600_common, dynamic_opt_txrx_work);
|
|
struct bes2600_vif *priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, 1);
|
|
|
|
if (priv != NULL && priv->join_status > BES2600_JOIN_STATUS_MONITOR) {
|
|
multivif_connected = true;
|
|
}
|
|
}
|
|
|
|
|
|
const u8 *bes2600_get_ie(u8 *start, size_t len, u8 ie)
|
|
{
|
|
u8 *end, *pos;
|
|
|
|
pos = start;
|
|
if (pos == NULL)
|
|
return NULL;
|
|
end = pos + len;
|
|
|
|
while (pos + 1 < end) {
|
|
if (pos + 2 + pos[1] > end)
|
|
break;
|
|
if (pos[0] == ie)
|
|
return pos;
|
|
pos += 2 + pos[1];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* bes2600_set_macaddrfilter -called when tesmode command
|
|
* is for setting mac address filter
|
|
*
|
|
* @hw: the hardware
|
|
* @data: incoming data
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_set_macaddrfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data)
|
|
{
|
|
struct wsm_mac_addr_filter *mac_addr_filter = NULL;
|
|
struct wsm_mac_addr_info *addr_info = NULL;
|
|
u8 action_mode = 0, no_of_mac_addr = 0, i = 0;
|
|
int ret = 0;
|
|
u16 macaddrfiltersize = 0;
|
|
|
|
/* Retrieving Action Mode */
|
|
action_mode = data[0];
|
|
/* Retrieving number of address entries */
|
|
no_of_mac_addr = data[1];
|
|
|
|
addr_info = (struct wsm_mac_addr_info *)&data[2];
|
|
|
|
/* Computing sizeof Mac addr filter */
|
|
macaddrfiltersize = sizeof(*mac_addr_filter) + \
|
|
(no_of_mac_addr * sizeof(struct wsm_mac_addr_info));
|
|
|
|
mac_addr_filter = kzalloc(macaddrfiltersize, GFP_KERNEL);
|
|
if (!mac_addr_filter) {
|
|
ret = -ENOMEM;
|
|
goto exit_p;
|
|
}
|
|
mac_addr_filter->action_mode = action_mode;
|
|
mac_addr_filter->numfilter = no_of_mac_addr;
|
|
|
|
for (i = 0; i < no_of_mac_addr; i++) {
|
|
mac_addr_filter->macaddrfilter[i].address_mode = \
|
|
addr_info[i].address_mode;
|
|
memcpy(mac_addr_filter->macaddrfilter[i].MacAddr, \
|
|
addr_info[i].MacAddr , ETH_ALEN);
|
|
mac_addr_filter->macaddrfilter[i].filter_mode = \
|
|
addr_info[i].filter_mode;
|
|
}
|
|
ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_MAC_ADDR_FILTER, \
|
|
mac_addr_filter, macaddrfiltersize, priv->if_id));
|
|
|
|
kfree(mac_addr_filter);
|
|
exit_p:
|
|
return ret;
|
|
}
|
|
|
|
#if 0
|
|
/**
|
|
* bes2600_set_multicastaddrfilter -called when tesmode command
|
|
* is for setting the ipv4 address filter
|
|
*
|
|
* @hw: the hardware
|
|
* @data: incoming data
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_set_multicastfilter(struct bes2600_common *hw_priv, struct bes2600_vif *priv, u8 *data)
|
|
{
|
|
u8 i = 0;
|
|
int ret = 0;
|
|
|
|
memset(&priv->multicast_filter, 0, sizeof(priv->multicast_filter));
|
|
priv->multicast_filter.enable = (u32)data[0];
|
|
priv->multicast_filter.numOfAddresses = (u32)data[1];
|
|
|
|
for (i = 0; i < priv->multicast_filter.numOfAddresses; i++) {
|
|
memcpy(&priv->multicast_filter.macAddress[i], \
|
|
&data[2+(i*ETH_ALEN)], ETH_ALEN);
|
|
}
|
|
/* Configure the multicast mib in case of drop all multicast */
|
|
if (priv->multicast_filter.enable != 2)
|
|
return ret;
|
|
|
|
ret = wsm_write_mib(hw_priv, WSM_MIB_ID_DOT11_GROUP_ADDRESSES_TABLE, \
|
|
&priv->multicast_filter, sizeof(priv->multicast_filter), priv->if_id);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPV6_FILTERING
|
|
/**
|
|
* bes2600_set_ipv6addrfilter -called when tesmode command
|
|
* is for setting the ipv6 address filter
|
|
*
|
|
* @hw: the hardware
|
|
* @data: incoming data
|
|
* @if_id: interface id
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_set_ipv6addrfilter(struct ieee80211_hw *hw,
|
|
u8 *data, int if_id)
|
|
{
|
|
struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
|
|
struct wsm_ipv6_filter *ipv6_filter = NULL;
|
|
struct ipv6_addr_info *ipv6_info = NULL;
|
|
u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0;
|
|
u16 ipaddrfiltersize = 0;
|
|
|
|
/* Retrieving Action Mode */
|
|
action_mode = data[0];
|
|
/* Retrieving number of ipv4 address entries */
|
|
no_of_ip_addr = data[1];
|
|
|
|
ipv6_info = (struct ipv6_addr_info *)&data[2];
|
|
|
|
/* Computing sizeof Mac addr filter */
|
|
ipaddrfiltersize = sizeof(struct wsm_ipv6_filter_header) + \
|
|
(no_of_ip_addr * sizeof(struct wsm_ip6_addr_info));
|
|
|
|
|
|
ipv6_filter = kzalloc(ipaddrfiltersize, GFP_KERNEL);
|
|
if (!ipv6_filter) {
|
|
ret = -ENOMEM;
|
|
goto exit_p;
|
|
}
|
|
ipv6_filter->hdr.action_mode = action_mode;
|
|
ipv6_filter->hdr.numfilter = no_of_ip_addr;
|
|
|
|
for (i = 0; i < no_of_ip_addr; i++) {
|
|
ipv6_filter->ipv6filter[i].address_mode = \
|
|
ipv6_info[i].address_mode;
|
|
ipv6_filter->ipv6filter[i].filter_mode = \
|
|
ipv6_info[i].filter_mode;
|
|
memcpy(ipv6_filter->ipv6filter[i].ipv6, \
|
|
(u8 *)(ipv6_info[i].ipv6), 16);
|
|
}
|
|
|
|
ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_IP_IPV6_ADDR_FILTER, \
|
|
ipv6_filter, ipaddrfiltersize, \
|
|
if_id));
|
|
|
|
kfree(ipv6_filter);
|
|
exit_p:
|
|
return ret;
|
|
}
|
|
#endif /*IPV6_FILTERING*/
|
|
|
|
/**
|
|
* bes2600_set_data_filter -configure data filter in device
|
|
*
|
|
* @hw: the hardware
|
|
* @vif: vif
|
|
* @data: incoming data
|
|
* @len: incoming data length
|
|
*
|
|
*/
|
|
void bes2600_set_data_filter(struct ieee80211_hw *hw,
|
|
struct ieee80211_vif *vif,
|
|
void *data, int len)
|
|
{
|
|
int ret = 0;
|
|
int filter_id;
|
|
#ifdef IPV6_FILTERING
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
#endif /*IPV6_FILTERING*/
|
|
|
|
if (!data) {
|
|
ret = -EINVAL;
|
|
goto exit_p;
|
|
}
|
|
filter_id=*((enum bes2600_data_filterid*)data);
|
|
|
|
switch (filter_id) {
|
|
#ifdef IPV6_FILTERING
|
|
case IPV6ADDR_FILTER_ID:
|
|
ret = bes2600_set_ipv6addrfilter(hw, \
|
|
&((u8 *)data)[4], priv->if_id);
|
|
break;
|
|
#endif /*IPV6_FILTERING*/
|
|
default:
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
exit_p:
|
|
|
|
return ;
|
|
}
|
|
|
|
u32 bes2600_bh_get_encry_hdr_len(u32 cipherType)
|
|
{
|
|
u32 encrypthdr = 0;
|
|
|
|
switch (cipherType) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
encrypthdr = WEP_ENCRYPT_HDR_SIZE;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
encrypthdr = WPA_ENCRYPT_HDR_SIZE;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
|
|
break;
|
|
default:
|
|
encrypthdr = 0;
|
|
break;
|
|
}
|
|
|
|
return encrypthdr;
|
|
}
|
|
|
|
/**
|
|
* bes2600_set_arpreply -called for creating and
|
|
* configuring arp response template frame
|
|
*
|
|
* @hw: the hardware
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_set_arpreply(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|
{
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
struct bes2600_common *hw_priv = (struct bes2600_common *)hw->priv;
|
|
u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0;
|
|
bool encrypt = false;
|
|
int ret = 0;
|
|
u8 *template_frame = NULL;
|
|
struct ieee80211_hdr_3addr *dot11hdr = NULL;
|
|
struct ieee80211_snap_hdr *snaphdr = NULL;
|
|
struct arphdr *arp_hdr = NULL;
|
|
|
|
template_frame = kzalloc(MAX_ARP_REPLY_TEMPLATE_SIZE, GFP_ATOMIC);
|
|
if (!template_frame) {
|
|
bes_err("[STA] Template frame memory failed\n");
|
|
ret = -ENOMEM;
|
|
goto exit_p;
|
|
}
|
|
dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4];
|
|
|
|
framehdrlen = sizeof(*dot11hdr);
|
|
if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
|
|
priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
|
|
switch (priv->cipherType) {
|
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
bes_devel("[STA] WEP\n");
|
|
encrypthdr = WEP_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
bes_devel("[STA] WPA\n");
|
|
encrypthdr = WPA_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
bes_devel("[STA] WPA2\n");
|
|
encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
bes_devel("[STA] WAPI\n");
|
|
encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
default:
|
|
encrypthdr = 0;
|
|
encrypttailsize = 0;
|
|
encrypt = 0;
|
|
break;
|
|
}
|
|
|
|
framehdrlen += encrypthdr;
|
|
|
|
/* Filling the 802.11 Hdr */
|
|
dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
|
|
if (priv->vif->type == NL80211_IFTYPE_STATION)
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
|
|
else
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
|
|
|
|
if (encrypt)
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
|
|
|
|
if (priv->vif->bss_conf.qos) {
|
|
bes_devel("[STA] QOS Enabled\n");
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
|
|
*(u16 *)(dot11hdr + 1) = 0x0;
|
|
framehdrlen += 2;
|
|
} else {
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
|
|
}
|
|
|
|
memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
|
|
memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
|
|
memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN);
|
|
|
|
/* Filling the LLC/SNAP Hdr */
|
|
snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
|
|
memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
|
|
sizeof(*snaphdr));
|
|
*(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_ARP);
|
|
/* Updating the framebdylen with snaphdr and LLC hdr size */
|
|
framebdylen = sizeof(*snaphdr) + 2;
|
|
|
|
/* Filling the ARP Reply Payload */
|
|
arp_hdr = (struct arphdr *)((u8 *)dot11hdr + framehdrlen + framebdylen);
|
|
arp_hdr->ar_hrd = cpu_to_be16(ARPHRD_ETHER);
|
|
arp_hdr->ar_pro = cpu_to_be16(ETH_P_IP);
|
|
arp_hdr->ar_hln = ETH_ALEN;
|
|
arp_hdr->ar_pln = 4;
|
|
arp_hdr->ar_op = cpu_to_be16(ARPOP_REPLY);
|
|
|
|
/* Updating the frmbdylen with Arp Reply Hdr and Arp payload size(20) */
|
|
framebdylen += sizeof(*arp_hdr) + 20;
|
|
|
|
/* Updating the framebdylen with Encryption Tail Size */
|
|
framebdylen += encrypttailsize;
|
|
|
|
/* Filling the Template Frame Hdr */
|
|
template_frame[0] = WSM_FRAME_TYPE_ARP_REPLY; /* Template frame type */
|
|
template_frame[1] = 0xFF; /* Rate to be fixed */
|
|
((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen;
|
|
|
|
ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \
|
|
template_frame, (framehdrlen+framebdylen+4), priv->if_id));
|
|
kfree(template_frame);
|
|
exit_p:
|
|
return ret;
|
|
}
|
|
|
|
#ifdef IPV6_FILTERING
|
|
/**
|
|
* bes2600_set_na -called for creating and
|
|
* configuring NDP Neighbor Advertisement (NA) template frame
|
|
*
|
|
* @hw: the hardware
|
|
* @vif: vif
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_set_na(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|
{
|
|
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
struct bes2600_common *hw_priv = (struct bes2600_common *)hw->priv;
|
|
u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0;
|
|
bool encrypt = false;
|
|
int ret = 0;
|
|
u8 *template_frame = NULL;
|
|
struct ieee80211_hdr_3addr *dot11hdr = NULL;
|
|
struct ieee80211_snap_hdr *snaphdr = NULL;
|
|
struct ipv6hdr *ipv6_hdr = NULL;
|
|
struct icmp6hdr *icmp6_hdr = NULL;
|
|
struct nd_msg *na = NULL;
|
|
struct nd_opt_hdr *opt_hdr = NULL;
|
|
|
|
template_frame = kzalloc(MAX_NEIGHBOR_ADVERTISEMENT_TEMPLATE_SIZE, GFP_ATOMIC);
|
|
if (!template_frame) {
|
|
bes_err("[STA] Template frame memory failed\n");
|
|
ret = -ENOMEM;
|
|
goto exit_p;
|
|
}
|
|
dot11hdr = (struct ieee80211_hdr_3addr *)&template_frame[4];
|
|
|
|
framehdrlen = sizeof(*dot11hdr);
|
|
if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
|
|
priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
|
|
switch (priv->cipherType) {
|
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
bes_devel("[STA] WEP\n");
|
|
encrypthdr = WEP_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
bes_devel("[STA] WPA\n");
|
|
encrypthdr = WPA_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
bes_devel("[STA] WPA2\n");
|
|
encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
bes_devel("[STA] WAPI\n");
|
|
encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
break;
|
|
|
|
default:
|
|
encrypthdr = 0;
|
|
encrypttailsize = 0;
|
|
encrypt = 0;
|
|
break;
|
|
}
|
|
|
|
framehdrlen += encrypthdr;
|
|
|
|
/* Filling the 802.11 Hdr */
|
|
dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
|
|
if (priv->vif->type == NL80211_IFTYPE_STATION)
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
|
|
else
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
|
|
|
|
if (encrypt)
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
|
|
|
|
if (priv->vif->bss_conf.qos) {
|
|
bes_devel("[STA] QOS Enabled\n");
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
|
|
/* Filling QOS Control Field */
|
|
*(u16 *)(dot11hdr + 1) = 0x0;
|
|
framehdrlen += 2;
|
|
} else {
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
|
|
}
|
|
|
|
memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
|
|
memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
|
|
memcpy(dot11hdr->addr3, priv->vif->bss_conf.bssid, ETH_ALEN);
|
|
|
|
/* Filling the LLC/SNAP Hdr */
|
|
snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
|
|
memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
|
|
sizeof(*snaphdr));
|
|
*(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IPV6);
|
|
/* Updating the framebdylen with snaphdr and LLC hdr size */
|
|
framebdylen = sizeof(*snaphdr) + 2;
|
|
|
|
/* Filling the ipv6 header */
|
|
ipv6_hdr = (struct ipv6hdr *)((u8 *)dot11hdr + framehdrlen + framebdylen);
|
|
ipv6_hdr->version = 6;
|
|
ipv6_hdr->priority = 0;
|
|
ipv6_hdr->payload_len = cpu_to_be16(32); /* ??? check the be or le ??? whether to use cpu_to_be16(32)*/
|
|
ipv6_hdr->nexthdr = 58;
|
|
ipv6_hdr->hop_limit = 255;
|
|
|
|
/* Updating the framebdylen with ipv6 Hdr */
|
|
framebdylen += sizeof(*ipv6_hdr);
|
|
|
|
/* Filling the Neighbor Advertisement */
|
|
na = (struct nd_msg *)((u8 *)dot11hdr + framehdrlen + framebdylen);
|
|
icmp6_hdr = (struct icmp6hdr *)(&na->icmph);
|
|
icmp6_hdr->icmp6_type = NDISC_NEIGHBOUR_ADVERTISEMENT;
|
|
icmp6_hdr->icmp6_code = 0;
|
|
/* checksum (2 bytes), RSO fields (4 bytes) and target IP address (16 bytes) shall be filled by firmware */
|
|
|
|
/* Filling the target link layer address in the optional field */
|
|
opt_hdr = (struct nd_opt_hdr *)(&na->opt[0]);
|
|
opt_hdr->nd_opt_type = 2;
|
|
opt_hdr->nd_opt_len = 1;
|
|
/* optional target link layer address (6 bytes) shall be filled by firmware */
|
|
|
|
/* Updating the framebdylen with the ipv6 payload length */
|
|
framebdylen += 32;
|
|
|
|
/* Updating the framebdylen with Encryption Tail Size */
|
|
framebdylen += encrypttailsize;
|
|
|
|
/* Filling the Template Frame Hdr */
|
|
template_frame[0] = WSM_FRAME_TYPE_NA; /* Template frame type */
|
|
template_frame[1] = 0xFF; /* Rate to be fixed */
|
|
((u16 *)&template_frame[2])[0] = framehdrlen + framebdylen;
|
|
|
|
ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_TEMPLATE_FRAME, \
|
|
template_frame, (framehdrlen+framebdylen+4), \
|
|
priv->if_id));
|
|
|
|
kfree(template_frame);
|
|
|
|
exit_p:
|
|
return ret;
|
|
}
|
|
#endif /*IPV6_FILTERING*/
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
/**
|
|
* bes2600_set_snap_frame -Set SNAP frame format
|
|
*
|
|
* @hw: the hardware
|
|
* @data: data frame
|
|
* @len: data length
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_set_snap_frame(struct ieee80211_hw *hw,
|
|
u8 *data, int len)
|
|
{
|
|
struct bes_msg_set_snap_frame *snap_frame =
|
|
(struct bes_msg_set_snap_frame *) data;
|
|
struct bes2600_common *priv = (struct bes2600_common *) hw->priv;
|
|
u8 frame_len = snap_frame->len;
|
|
u8 *frame = &snap_frame->frame[0];
|
|
|
|
/*
|
|
* Check length of incoming frame format:
|
|
* SNAP + SNAP_LEN (u8)
|
|
*/
|
|
if (frame_len + sizeof(snap_frame->len) != len)
|
|
return -EINVAL;
|
|
|
|
if (frame_len > 0) {
|
|
priv->test_frame.data = (u8 *) krealloc(priv->test_frame.data,
|
|
sizeof(u8) * frame_len,
|
|
GFP_KERNEL);
|
|
if (priv->test_frame.data == NULL) {
|
|
bes_devel("bes2600_set_snap_frame memory" \
|
|
"allocation failed");
|
|
priv->test_frame.len = 0;
|
|
return -EINVAL;
|
|
}
|
|
memcpy(priv->test_frame.data, frame, frame_len);
|
|
} else {
|
|
kfree(priv->test_frame.data);
|
|
priv->test_frame.data = NULL;
|
|
}
|
|
priv->test_frame.len = frame_len;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* bes2600_set_txqueue_params -Set txqueue params after successful TSPEC negotiation
|
|
*
|
|
* @hw: the hardware
|
|
* @data: data frame
|
|
* @len: data length
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_set_txqueue_params(struct ieee80211_hw *hw,
|
|
u8 *data, int len)
|
|
{
|
|
struct bes_msg_set_txqueue_params *txqueue_params =
|
|
(struct bes_msg_set_txqueue_params *) data;
|
|
struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
|
|
struct bes2600_vif *priv;
|
|
/* Interface ID is hard coded here, as interface is not
|
|
* passed in testmode command.
|
|
* Also it is assumed here that STA will be on interface
|
|
* 0 always.
|
|
*/
|
|
|
|
int if_id = 0;
|
|
u16 queueId = bes2600_priority_to_queueId[txqueue_params->user_priority];
|
|
|
|
priv = cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
|
|
|
|
if (unlikely(!priv)) {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "[STA] %s: Warning Priv is Null\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
spin_unlock(&priv->vif_lock);
|
|
|
|
/* Default Ack policy is WSM_ACK_POLICY_NORMAL */
|
|
WSM_TX_QUEUE_SET(&priv->tx_queue_params,
|
|
queueId,
|
|
WSM_ACK_POLICY_NORMAL,
|
|
txqueue_params->medium_time,
|
|
txqueue_params->expiry_time);
|
|
return WARN_ON(wsm_set_tx_queue_params(hw_priv,
|
|
&priv->tx_queue_params.params[queueId], queueId,
|
|
priv->if_id));
|
|
}
|
|
|
|
/**
|
|
* bes2600_testmode_reply -called inside a testmode command
|
|
* handler to send a response to user space
|
|
*
|
|
* @wiphy: the wiphy
|
|
* @data: data to be send to user space
|
|
* @len: data length
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_testmode_reply(struct wiphy *wiphy,
|
|
const void *data, int len)
|
|
{
|
|
int ret = 0;
|
|
struct sk_buff *skb = cfg80211_testmode_alloc_reply_skb(wiphy,
|
|
nla_total_size(len));
|
|
|
|
bes_devel("%s\n", __func__);
|
|
if (!skb)
|
|
return -ENOMEM;
|
|
|
|
ret = nla_put(skb, BES_TM_MSG_DATA, len, data);
|
|
if (ret) {
|
|
kfree_skb(skb);
|
|
return ret;
|
|
}
|
|
|
|
return cfg80211_testmode_reply(skb);
|
|
}
|
|
|
|
/**
|
|
* bes2600_testmode_event -send asynchronous event
|
|
* to userspace
|
|
*
|
|
* @wiphy: the wiphy
|
|
* @msg_id: BES msg ID
|
|
* @data: data to be sent
|
|
* @len: data length
|
|
* @gfp: allocation flag
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_testmode_event(struct wiphy *wiphy, const u32 msg_id,
|
|
const void *data, int len, gfp_t gfp)
|
|
{
|
|
struct sk_buff *skb = cfg80211_testmode_alloc_event_skb(wiphy,
|
|
nla_total_size(len+sizeof(msg_id)), gfp);
|
|
|
|
if (!skb)
|
|
return -ENOMEM;
|
|
|
|
if (nla_put_u32(skb, BES_TM_MSG_ID, msg_id))
|
|
goto nla_put_failure;
|
|
if (data)
|
|
if (nla_put(skb, BES_TM_MSG_DATA, len, data))
|
|
goto nla_put_failure;
|
|
|
|
cfg80211_testmode_event(skb, gfp);
|
|
return 0;
|
|
nla_put_failure:
|
|
kfree_skb(skb);
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
/**
|
|
* example function for test purposes
|
|
* sends both: synchronous reply and asynchronous event
|
|
*/
|
|
static int bes2600_test(struct ieee80211_hw *hw,
|
|
void *data, int len)
|
|
{
|
|
struct bes_msg_test_t *test_p;
|
|
struct bes_reply_test_t reply;
|
|
struct bes_event_test_t event;
|
|
|
|
if (sizeof(struct bes_msg_test_t) != len)
|
|
return -EINVAL;
|
|
|
|
test_p = (struct bes_msg_test_t *) data;
|
|
|
|
reply.dummy = test_p->dummy + 10;
|
|
|
|
event.dummy = test_p->dummy + 20;
|
|
|
|
if (bes2600_testmode_event(hw->wiphy, BES_MSG_EVENT_TEST,
|
|
&event, sizeof(event), GFP_KERNEL))
|
|
return -1;
|
|
|
|
return bes2600_testmode_reply(hw->wiphy, &reply, sizeof(reply));
|
|
}
|
|
|
|
/**
|
|
* bes2600_get_tx_power_level - send tx power level
|
|
* to userspace
|
|
*
|
|
* @hw: the hardware
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_get_tx_power_level(struct ieee80211_hw *hw)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
int get_power = 0;
|
|
get_power = hw_priv->output_power;
|
|
bes_devel("[STA] %s: Power set on Device : %d",
|
|
__func__, get_power);
|
|
return bes2600_testmode_reply(hw->wiphy, &get_power, sizeof(get_power));
|
|
}
|
|
|
|
/**
|
|
* bes2600_get_tx_power_range- send tx power range
|
|
* to userspace for each band
|
|
*
|
|
* @hw: the hardware
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_get_tx_power_range(struct ieee80211_hw *hw)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct wsm_tx_power_range txPowerRange[2];
|
|
size_t len = sizeof(txPowerRange);
|
|
memcpy(txPowerRange, hw_priv->txPowerRange, len);
|
|
return bes2600_testmode_reply(hw->wiphy, txPowerRange, len);
|
|
}
|
|
|
|
/**
|
|
* bes2600_set_advance_scan_elems -Set Advcance Scan
|
|
* elements
|
|
* @hw: the hardware
|
|
* @data: data frame
|
|
* @len: data length
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_set_advance_scan_elems(struct ieee80211_hw *hw,
|
|
u8 *data, int len)
|
|
{
|
|
struct advance_scan_elems *scan_elems =
|
|
(struct advance_scan_elems *) data;
|
|
struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
|
|
size_t elems_len = sizeof(struct advance_scan_elems);
|
|
|
|
if (elems_len != len)
|
|
return -EINVAL;
|
|
|
|
scan_elems = (struct advance_scan_elems *) data;
|
|
|
|
/* Locks required to prevent simultaneous scan */
|
|
down(&hw_priv->scan.lock);
|
|
down(&hw_priv->conf_lock);
|
|
|
|
hw_priv->advanceScanElems.scanMode = scan_elems->scanMode;
|
|
hw_priv->advanceScanElems.duration = scan_elems->duration;
|
|
hw_priv->enable_advance_scan = true;
|
|
|
|
up(&hw_priv->conf_lock);
|
|
up(&hw_priv->scan.lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bes2600_set_power_save -Set Power Save
|
|
* elements
|
|
* @hw: the hardware
|
|
* @data: data frame
|
|
* @len: data length
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_set_power_save(struct ieee80211_hw *hw,
|
|
u8 *data, int len)
|
|
{
|
|
struct power_save_elems *ps_elems =
|
|
(struct power_save_elems *) data;
|
|
struct bes2600_common *hw_priv = (struct bes2600_common *) hw->priv;
|
|
size_t elems_len = sizeof(struct power_save_elems);
|
|
struct bes2600_vif *priv;
|
|
int if_id = 0;
|
|
/* Interface ID is hard coded here, as interface is not
|
|
* passed in testmode command.
|
|
* Also it is assumed here that STA will be on interface
|
|
* 0 always.
|
|
*/
|
|
|
|
if (elems_len != len)
|
|
return -EINVAL;
|
|
|
|
priv = cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
|
|
|
|
if (unlikely(!priv)) {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "[STA] %s: Warning Priv is Null\n",
|
|
__func__);
|
|
return 0;
|
|
}
|
|
|
|
spin_unlock(&priv->vif_lock);
|
|
down(&hw_priv->conf_lock);
|
|
|
|
ps_elems = (struct power_save_elems *) data;
|
|
|
|
if (ps_elems->powerSave == 1)
|
|
priv->user_pm_mode = WSM_PSM_PS;
|
|
else
|
|
priv->user_pm_mode = WSM_PSM_FAST_PS;
|
|
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] Aid: %d, Joined: %s, Powersave: %s\n",
|
|
priv->bss_params.aid,
|
|
priv->join_status == BES2600_JOIN_STATUS_STA ? "yes" : "no",
|
|
priv->user_pm_mode == WSM_PSM_ACTIVE ? "WSM_PSM_ACTIVE" :
|
|
priv->user_pm_mode == WSM_PSM_PS ? "WSM_PSM_PS" :
|
|
priv->user_pm_mode == WSM_PSM_FAST_PS ? "WSM_PSM_FAST_PS" :
|
|
"UNKNOWN");
|
|
if (priv->join_status == BES2600_JOIN_STATUS_STA &&
|
|
priv->bss_params.aid &&
|
|
priv->setbssparams_done &&
|
|
priv->filter4.enable) {
|
|
priv->powersave_mode.pmMode = priv->user_pm_mode;
|
|
bes2600_set_pm(priv, &priv->powersave_mode);
|
|
}
|
|
else
|
|
priv->user_power_set_true = ps_elems->powerSave;
|
|
up(&hw_priv->conf_lock);
|
|
return 0;
|
|
}
|
|
/**
|
|
* bes2600_start_stop_tsm - starts/stops collecting TSM
|
|
*
|
|
* @hw: the hardware
|
|
* @data: data frame
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
static int bes2600_start_stop_tsm(struct ieee80211_hw *hw, void *data)
|
|
{
|
|
struct bes_msg_start_stop_tsm *start_stop_tsm =
|
|
(struct bes_msg_start_stop_tsm *) data;
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
hw_priv->start_stop_tsm.start = start_stop_tsm->start;
|
|
hw_priv->start_stop_tsm.up = start_stop_tsm->up;
|
|
hw_priv->start_stop_tsm.packetization_delay =
|
|
start_stop_tsm->packetization_delay;
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: start : %u: up : %u",
|
|
__func__, hw_priv->start_stop_tsm.start,
|
|
hw_priv->start_stop_tsm.up);
|
|
hw_priv->tsm_info.ac = bes2600_1d_to_ac[start_stop_tsm->up];
|
|
|
|
if (!hw_priv->start_stop_tsm.start) {
|
|
spin_lock_bh(&hw_priv->tsm_lock);
|
|
memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats));
|
|
memset(&hw_priv->tsm_info, 0, sizeof(hw_priv->tsm_info));
|
|
spin_unlock_bh(&hw_priv->tsm_lock);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* bes2600_get_tsm_params - Retrieves TSM parameters
|
|
*
|
|
* @hw: the hardware
|
|
*
|
|
* Returns: TSM parameters collected
|
|
*/
|
|
static int bes2600_get_tsm_params(struct ieee80211_hw *hw)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct bes_tsm_stats tsm_stats;
|
|
u32 pkt_count;
|
|
spin_lock_bh(&hw_priv->tsm_lock);
|
|
pkt_count = hw_priv->tsm_stats.txed_msdu_count -
|
|
hw_priv->tsm_stats.msdu_discarded_count;
|
|
if (pkt_count) {
|
|
hw_priv->tsm_stats.avg_q_delay =
|
|
hw_priv->tsm_info.sum_pkt_q_delay/(pkt_count * 1000);
|
|
hw_priv->tsm_stats.avg_transmit_delay =
|
|
hw_priv->tsm_info.sum_media_delay/pkt_count;
|
|
} else {
|
|
hw_priv->tsm_stats.avg_q_delay = 0;
|
|
hw_priv->tsm_stats.avg_transmit_delay = 0;
|
|
}
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Txed MSDU count : %u",
|
|
__func__, hw_priv->tsm_stats.txed_msdu_count);
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Average queue delay : %u",
|
|
__func__, hw_priv->tsm_stats.avg_q_delay);
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Average transmit delay : %u",
|
|
__func__, hw_priv->tsm_stats.avg_transmit_delay);
|
|
memcpy(&tsm_stats, &hw_priv->tsm_stats, sizeof(hw_priv->tsm_stats));
|
|
/* Reset the TSM statistics */
|
|
memset(&hw_priv->tsm_stats, 0, sizeof(hw_priv->tsm_stats));
|
|
hw_priv->tsm_info.sum_pkt_q_delay = 0;
|
|
hw_priv->tsm_info.sum_media_delay = 0;
|
|
spin_unlock_bh(&hw_priv->tsm_lock);
|
|
return bes2600_testmode_reply(hw->wiphy, &tsm_stats,
|
|
sizeof(hw_priv->tsm_stats));
|
|
}
|
|
|
|
/**
|
|
* bes2600_get_roam_delay - Retrieves roam delay
|
|
*
|
|
* @hw: the hardware
|
|
*
|
|
* Returns: Returns the last measured roam delay
|
|
*/
|
|
static int bes2600_get_roam_delay(struct ieee80211_hw *hw)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
u16 roam_delay = hw_priv->tsm_info.roam_delay / 1000;
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: Roam delay : %u",
|
|
__func__, roam_delay);
|
|
spin_lock_bh(&hw_priv->tsm_lock);
|
|
hw_priv->tsm_info.roam_delay = 0;
|
|
hw_priv->tsm_info.use_rx_roaming = 0;
|
|
spin_unlock_bh(&hw_priv->tsm_lock);
|
|
return bes2600_testmode_reply(hw->wiphy, &roam_delay, sizeof(u16));
|
|
}
|
|
|
|
/* tcp & udp alive test */
|
|
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
|
|
|
int bes2600_set_ipv4addrfilter(struct bes2600_common *hw_priv, u8 *data, int if_id)
|
|
{
|
|
struct wsm_ipv4_filter *ipv4_filter = NULL;
|
|
struct ipv4_addr_info *ipv4_info = NULL;
|
|
u8 action_mode = 0, no_of_ip_addr = 0, i = 0, ret = 0;
|
|
u16 ipaddrfiltersize = 0;
|
|
|
|
/* Retrieving Action Mode */
|
|
action_mode = data[0];
|
|
/* Retrieving number of ipv4 address entries */
|
|
no_of_ip_addr = data[1];
|
|
|
|
ipv4_info = (struct ipv4_addr_info *)&data[2];
|
|
|
|
/* Computing sizeof Mac addr filter */
|
|
ipaddrfiltersize = sizeof(*ipv4_filter) + \
|
|
(no_of_ip_addr * sizeof(struct wsm_ip4_addr_info));
|
|
|
|
|
|
ipv4_filter = kzalloc(ipaddrfiltersize, GFP_KERNEL);
|
|
if (!ipv4_filter) {
|
|
ret = -ENOMEM;
|
|
goto exit_p;
|
|
}
|
|
ipv4_filter->action_mode = action_mode;
|
|
ipv4_filter->numfilter = no_of_ip_addr;
|
|
|
|
for (i = 0; i < no_of_ip_addr; i++) {
|
|
ipv4_filter->ipv4filter[i].address_mode = \
|
|
ipv4_info[i].address_mode;
|
|
ipv4_filter->ipv4filter[i].filter_mode = \
|
|
ipv4_info[i].filter_mode;
|
|
memcpy(ipv4_filter->ipv4filter[i].ipv4, \
|
|
(u8 *)(ipv4_info[i].ipv4), 4);
|
|
}
|
|
|
|
ret = WARN_ON(wsm_write_mib(hw_priv, WSM_MIB_ID_IPV4_ADDR_FILTERING, \
|
|
ipv4_filter, ipaddrfiltersize, \
|
|
if_id));
|
|
|
|
kfree(ipv4_filter);
|
|
exit_p:
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* Checksum a block of data */
|
|
static uint16_t csum(uint16_t *packet, int packlen)
|
|
{
|
|
uint32_t sum = 0;
|
|
|
|
while (packlen > 1) {
|
|
sum += *(packet++);
|
|
packlen -= 2;
|
|
}
|
|
|
|
if (packlen > 0)
|
|
sum += *(unsigned char *)packet;
|
|
|
|
while (sum >> 16)
|
|
sum = (sum & 0xffff) + (sum >> 16);
|
|
|
|
return (uint16_t) ~sum;
|
|
}
|
|
|
|
|
|
static void tcpcsum(struct ip_header *ip,
|
|
struct tcp_header *tcp,
|
|
uint8_t *data,
|
|
uint16_t data_len)
|
|
{
|
|
uint8_t tcphd_len = sizeof(struct tcp_header);
|
|
uint16_t *buf = kzalloc(12 + tcphd_len + data_len, GFP_KERNEL);
|
|
uint8_t *tempbuf = (uint8_t *)buf;
|
|
|
|
if(tempbuf == NULL) {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "Out of memory: TCP checksum not computed\n");
|
|
return;
|
|
}
|
|
|
|
tcp->chksum = 0;
|
|
/* Set up the pseudo header */
|
|
memcpy(tempbuf, &(ip->src), sizeof(uint32_t));
|
|
memcpy(&(tempbuf[4]), &(ip->dest), sizeof(uint32_t));
|
|
tempbuf[8] = 0;
|
|
tempbuf[9] = (uint16_t)ip->_proto;
|
|
tempbuf[10] = (uint16_t)((tcphd_len + data_len) & 0xFF00) >> 8;
|
|
tempbuf[11] = (uint16_t)((tcphd_len + data_len) & 0x00FF);
|
|
/* Copy the TCP header and data */
|
|
memcpy(tempbuf + 12, (uint8_t *)tcp, tcphd_len);
|
|
memcpy(tempbuf + 12 + tcphd_len, data, data_len);
|
|
/* CheckSum it */
|
|
tcp->chksum = csum(buf, 12 + tcphd_len + data_len);
|
|
kfree(buf);
|
|
}
|
|
|
|
int bes2600_set_ip_offload(struct bes2600_common *hw_priv,
|
|
struct bes2600_vif *priv,
|
|
struct ip_alive_cfg *iac,
|
|
u16 idx)
|
|
{
|
|
u32 framehdrlen, encrypthdr, encrypttailsize, framebdylen = 0, tu_len, tu_proto;
|
|
bool encrypt = false;
|
|
int ret = 0;
|
|
u8 *tmp_frame = NULL;
|
|
u8 *ptr = NULL;
|
|
struct ieee80211_hdr_3addr *dot11hdr = NULL;
|
|
struct ieee80211_snap_hdr *snaphdr = NULL;
|
|
struct ip_header *ipheader = NULL;
|
|
struct tcp_header *tcpheader = NULL;
|
|
struct udp_header *udpheader = NULL;
|
|
u8 *tcpbody = NULL;
|
|
u8 *udpbody = NULL;
|
|
u8 EncrType;
|
|
|
|
u8 *payload = iac->bd.payload;
|
|
u32 bodylen = iac->bd.len;
|
|
tmp_frame = kzalloc(MAX_TCP_ALIVE_TEMPLATE_SIZE, GFP_ATOMIC);
|
|
if (!tmp_frame) {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "[STA] Template frame memory failed\n");
|
|
ret = -ENOMEM;
|
|
goto exit_p;
|
|
}
|
|
dot11hdr = (struct ieee80211_hdr_3addr *)&tmp_frame[4];
|
|
|
|
framehdrlen = sizeof(*dot11hdr);
|
|
if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
|
|
priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
|
|
switch (priv->cipherType) {
|
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
bes_devel("[STA] WEP\n");
|
|
encrypthdr = WEP_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WEP_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
EncrType = 1;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
bes_devel("[STA] WPA\n");
|
|
encrypthdr = WPA_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WPA_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
EncrType = 3;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
bes_devel("[STA] WPA2\n");
|
|
encrypthdr = WPA2_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WPA2_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
EncrType = 5;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
bes_devel("[STA] WAPI\n");
|
|
encrypthdr = WAPI_ENCRYPT_HDR_SIZE;
|
|
encrypttailsize = WAPI_ENCRYPT_TAIL_SIZE;
|
|
encrypt = 1;
|
|
EncrType = 7;
|
|
break;
|
|
|
|
default:
|
|
encrypthdr = 0;
|
|
encrypttailsize = 0;
|
|
encrypt = 0;
|
|
EncrType = 8;
|
|
break;
|
|
}
|
|
|
|
framehdrlen += encrypthdr;
|
|
|
|
/* Filling the 802.11 Hdr */
|
|
dot11hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA);
|
|
if (priv->vif->type == NL80211_IFTYPE_STATION)
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_TODS);
|
|
else
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_FROMDS);
|
|
|
|
if (encrypt)
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_WEP);
|
|
|
|
if (priv->vif->bss_conf.qos) {
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] QOS Enabled\n");
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_QOS_DATAGRP);
|
|
*(u16 *)(dot11hdr + 1) = 0x0;
|
|
framehdrlen += 2;
|
|
} else {
|
|
dot11hdr->frame_control |= cpu_to_le16(IEEE80211_STYPE_DATA);
|
|
}
|
|
|
|
memcpy(dot11hdr->addr1, priv->vif->bss_conf.bssid, ETH_ALEN);
|
|
memcpy(dot11hdr->addr2, priv->vif->addr, ETH_ALEN);
|
|
memcpy(dot11hdr->addr3, iac->bd.dest_mac, ETH_ALEN);
|
|
|
|
/* Filling the LLC/SNAP Hdr */
|
|
snaphdr = (struct ieee80211_snap_hdr *)((u8 *)dot11hdr + framehdrlen);
|
|
memcpy(snaphdr, (struct ieee80211_snap_hdr *)rfc1042_header, \
|
|
sizeof(*snaphdr));
|
|
*(u16 *)(++snaphdr) = cpu_to_be16(ETH_P_IP);
|
|
/* Updating the framebdylen with snaphdr and LLC hdr size */
|
|
framebdylen = sizeof(*snaphdr) + 2;
|
|
|
|
/* proto == 0 for udp stream or 1 for tcp stream; */
|
|
(iac->bd.proto) ? (tu_len = sizeof(struct tcp_header)):(tu_len = sizeof(struct udp_header));
|
|
(iac->bd.proto) ? (tu_proto = TCP_PROTO):(tu_proto = UDP_PROTO);
|
|
|
|
/* Filling the ip header */
|
|
ptr = (u8 *)dot11hdr + framehdrlen + framebdylen;
|
|
ipheader = (struct ip_header *)kzalloc(128, GFP_ATOMIC);
|
|
ipheader->_v_hl = 0x45;
|
|
ipheader->_tos = 0x00;
|
|
ipheader->_len = __swab16(sizeof(*ipheader) + tu_len + bodylen);
|
|
ipheader->_id = 0;
|
|
ipheader->_offset = 0;
|
|
ipheader->_ttl = 0xff;
|
|
ipheader->_proto = tu_proto;
|
|
ipheader->_chksum = 0;
|
|
ipheader->src = __swab32(iac->iphd.src);
|
|
ipheader->dest = __swab32(iac->iphd.dest);
|
|
|
|
/* Calculate ip header's checksum value; */
|
|
ipheader->_chksum = (csum((uint16_t *)ipheader, sizeof(*ipheader)));
|
|
|
|
/* Updating the frmbdylen with ip Hdr */
|
|
framebdylen += sizeof(*ipheader);
|
|
|
|
if (tu_proto == TCP_PROTO) {
|
|
/* Filling the tcp header */
|
|
tcpheader = (struct tcp_header *)((u8 *)ipheader + sizeof(struct ip_header));
|
|
tcpheader->src = __swab16(iac->tcphd.src);
|
|
tcpheader->dest = __swab16(iac->tcphd.dest);
|
|
tcpheader->seqno = __swab32(iac->bd.next_seqno);
|
|
tcpheader->ackno = __swab32(iac->tcphd.ackno);
|
|
tcpheader->_hdrlen_rsvd_flags = 0x1050;
|
|
tcpheader->wnd = 0xFFFF;
|
|
tcpheader->chksum = 0;
|
|
tcpheader->urgp = 0;
|
|
|
|
/* Updating the frmbdylen with tcp Hdr */
|
|
framebdylen += sizeof(*tcpheader);
|
|
|
|
/* Filling the tcp body */
|
|
tcpbody = (u8 *)dot11hdr + framehdrlen + framebdylen;
|
|
memcpy(tcpbody, payload, bodylen);
|
|
|
|
/* Calculate tcp's checksum value; */
|
|
tcpcsum(ipheader, tcpheader, payload, bodylen);
|
|
} else if (tu_proto == UDP_PROTO) {
|
|
/* Filling the udp header */
|
|
udpheader = (struct udp_header *)((u8 *)ipheader + sizeof(struct ip_header));
|
|
udpheader->src = __swab16(iac->udphd.src);
|
|
udpheader->dest = __swab16(iac->udphd.dest);
|
|
udpheader->len = __swab16(sizeof(struct udp_header) + bodylen);
|
|
udpheader->chksum = 0;
|
|
|
|
/* Updating the frmbdylen with udp Hdr */
|
|
framebdylen += sizeof(*udpheader);
|
|
|
|
/* Filling the tcp body */
|
|
udpbody = (u8 *)dot11hdr + framehdrlen + framebdylen;
|
|
memcpy(udpbody, payload, bodylen);
|
|
}
|
|
|
|
memcpy(ptr, ipheader, sizeof(struct ip_header) + tu_len);
|
|
kfree(ipheader);
|
|
|
|
/* Updating the frmbdylen with tcp payload */
|
|
framebdylen += bodylen;
|
|
|
|
/* Updating the framebdylen with Encryption Tail Size */
|
|
framebdylen += encrypttailsize;
|
|
|
|
/* Filling the Template Frame Hdr.
|
|
* Bit7 of the first idx stards for tcp/udp protocol,0 for udp and 1 for tcp;
|
|
* Bit6 indicates if send the heartbeat or not. 1 to send and 0 not.
|
|
* Bits 3-5 reserve for use.
|
|
* Bit 0-2 stands for stream index.
|
|
*/
|
|
if (iac->klv_vendor == KLV_VENDOR_DEFAULT) {
|
|
tmp_frame[0] = idx | (1 << 6) | (iac->bd.proto << 7);
|
|
} else {
|
|
tmp_frame[0] = idx | (1 << 6) | (iac->bd.proto << 7);
|
|
}
|
|
tmp_frame[1] = 0xFF; /* Fixed to 0xFF */
|
|
((u16 *)&tmp_frame[2])[0] = framehdrlen + framebdylen + AES_KEY_IV_LEN + 1;
|
|
memcpy(&tmp_frame[framehdrlen + framebdylen + 4], iac->aes_key, AES_KEY_LEN);
|
|
memcpy(&tmp_frame[framehdrlen + framebdylen + 20], iac->aes_iv, AES_IV_LEN);
|
|
/*
|
|
*bit0-bit3 stands for encrypt type,
|
|
*bit4-bit7 stands for klv_vendor;
|
|
*/
|
|
tmp_frame[framehdrlen + framebdylen + 4 + AES_KEY_IV_LEN] = (EncrType | (iac->klv_vendor << 4));
|
|
|
|
ret = WARN_ON(wsm_write_mib(hw_priv,
|
|
WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME,
|
|
tmp_frame,
|
|
(framehdrlen + framebdylen + 4 + AES_KEY_IV_LEN + 1),
|
|
priv->if_id));
|
|
kfree(tmp_frame);
|
|
|
|
exit_p:
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_del_ip_offload(struct bes2600_common *hw_priv,
|
|
struct bes2600_vif *priv,
|
|
u8 stream_idx)
|
|
{
|
|
u8 tmp_frame[8];
|
|
int ret;
|
|
|
|
/* Filling the Template Frame Hdr */
|
|
tmp_frame[0] = stream_idx;
|
|
tmp_frame[1] = 0xFF; /* Fixed to 0xFF */
|
|
((u16 *)&tmp_frame[2])[0] = 0;
|
|
|
|
ret = WARN_ON(wsm_write_mib(hw_priv,
|
|
WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_FRAME,
|
|
tmp_frame,
|
|
4,
|
|
priv->if_id));
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_en_ip_offload(struct bes2600_common *hw_priv,
|
|
struct bes2600_vif *priv,
|
|
u16 period_in_s)
|
|
{
|
|
int ret;
|
|
struct MIB_TCP_KEEP_ALIVE_PERIOD period;
|
|
u8 EncrType;
|
|
|
|
if ((priv->vif->type == NL80211_IFTYPE_AP) && priv->vif->p2p)
|
|
priv->cipherType = WLAN_CIPHER_SUITE_CCMP;
|
|
switch (priv->cipherType) {
|
|
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
EncrType = 1;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
EncrType = 3;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
EncrType = 5;
|
|
break;
|
|
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
EncrType = 7;
|
|
break;
|
|
|
|
default:
|
|
EncrType = 8;
|
|
break;
|
|
}
|
|
|
|
/* Send mib WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD */
|
|
period.TcpKeepAlivePeriod = period_in_s;
|
|
period.EncrType = EncrType;
|
|
period.Reserved = 0;
|
|
ret = WARN_ON(wsm_write_mib(hw_priv,
|
|
WSM_MIB_ID_EXT_TCP_KEEP_ALIVE_PERIOD,
|
|
(u8 *)&period,
|
|
sizeof(period),
|
|
priv->if_id));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int search_for_free_stream(struct bes2600_common *hw_priv, uint8_t proto,
|
|
uint16_t src_port, uint16_t dst_port,
|
|
uint32_t src_ip, uint32_t dst_ip)
|
|
{
|
|
int i = 0, free_idx = -1;
|
|
struct ip_alive_cfg *iac = hw_priv->iac;
|
|
|
|
/* If port matches, overwrites the configuration; */
|
|
for (i = 0; i < NUM_IP_FRAMES; i++) {
|
|
if (iac[i].bd.idx_used == 1 &&
|
|
iac[i].bd.proto == proto &&
|
|
iac[i].bd.src_port == src_port &&
|
|
iac[i].bd.dest_port == dst_port &&
|
|
iac[i].bd.src_ip == src_ip &&
|
|
iac[i].bd.dest_ip == dst_ip) {
|
|
free_idx = i;
|
|
break;
|
|
} else if (free_idx == -1 && iac[i].bd.idx_used == 0) {
|
|
free_idx = i;
|
|
iac[i].bd.idx_used = 1;
|
|
}
|
|
}
|
|
|
|
return free_idx;
|
|
}
|
|
|
|
static int get_keep_alive_used_stream(struct ieee80211_hw *hw)
|
|
{
|
|
int i;
|
|
struct ip_alive_cfg *iac = ((struct bes2600_common *)hw->priv)->iac;
|
|
u8 stream = 0;
|
|
|
|
for (i = 0; i < NUM_IP_FRAMES; i++) {
|
|
if (iac[i].bd.idx_used == 1)
|
|
stream |= (1 << i);
|
|
}
|
|
return bes2600_testmode_reply(hw->wiphy, &stream, sizeof(u8));
|
|
}
|
|
|
|
#ifdef VENDOR_XM_KEEPALIVE
|
|
void bes2600_get_keepalive_info(struct bes2600_common *hw_priv, struct ip_alive_satus *status)
|
|
{
|
|
int i;
|
|
struct ip_alive_cfg *iac = hw_priv->iac;
|
|
|
|
status->udp = false;
|
|
status->tcp = false;
|
|
|
|
for (i = 0; i < NUM_IP_FRAMES; i++) {
|
|
if (iac[i].bd.idx_used == 1) {
|
|
if (iac[i].bd.proto == 0)
|
|
status->udp = true;
|
|
else if (iac[i].bd.proto == 1)
|
|
status->tcp = true;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int net_device_add_ip_offload(struct ieee80211_hw *hw, u8 *data, int len)
|
|
{
|
|
struct ip_alive_paras *paras = (struct ip_alive_paras *)data;
|
|
struct ip_alive_cfg *iac = ((struct bes2600_common *)hw->priv)->iac;
|
|
int idx_check;
|
|
struct ip_stream_cfg idx_cfg = {
|
|
.err_code = 0,
|
|
.idx = -1,
|
|
};
|
|
|
|
/* only allowed paras->idx == 0xF */
|
|
if (paras->idx != 0xF) {
|
|
idx_cfg.err_code = -1; /* cmd with idx err */
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "cmd with idx err\n");
|
|
goto err_idx;
|
|
}
|
|
|
|
if (paras->payload_len <= IP_KEEPALIVE_MAX_LEN) {
|
|
/* idx == 0xF, find a free one. */
|
|
idx_check = search_for_free_stream(hw->priv, paras->proto,
|
|
paras->src_port,
|
|
paras->dst_port,
|
|
paras->src_ip,
|
|
paras->dst_ip);
|
|
if (idx_check < 0 || idx_check >= NUM_IP_FRAMES) {
|
|
paras->idx = 0xF;
|
|
idx_cfg.err_code = -2; /* get stream fail */
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "Exceeded maximum number of keepalive streams\n");
|
|
goto err_idx;
|
|
} else {
|
|
paras->idx = idx_check;
|
|
}
|
|
|
|
iac[paras->idx].bd.proto = paras->proto;
|
|
iac[paras->idx].klv_vendor = paras->klv_vendor;
|
|
iac[paras->idx].bd.src_port = paras->src_port;
|
|
iac[paras->idx].bd.dest_port = paras->dst_port;
|
|
iac[paras->idx].iphd.src = iac[paras->idx].bd.src_ip = paras->src_ip;
|
|
iac[paras->idx].iphd.dest = iac[paras->idx].bd.dest_ip = paras->dst_ip;
|
|
if (paras->proto) { //tcp
|
|
iac[paras->idx].tcphd.src = paras->src_port;
|
|
iac[paras->idx].tcphd.dest = paras->dst_port;
|
|
} else { //udp
|
|
iac[paras->idx].udphd.src = paras->src_port;
|
|
iac[paras->idx].udphd.dest = paras->dst_port;
|
|
}
|
|
|
|
if (iac[paras->idx].klv_vendor == KLV_VENDOR_DEFAULT) {
|
|
iac[paras->idx].bd.len = paras->payload_len;
|
|
} else if (iac[paras->idx].klv_vendor == KLV_VENDOR_XM) {
|
|
iac[paras->idx].bd.len = paras->payload_len;
|
|
if (paras->proto) { //tcp
|
|
//Reserve space for pkcs5 padding and base64 coding in firmware;
|
|
iac[paras->idx].bd.len = (iac[paras->idx].bd.len + (AES_KEY_LEN -
|
|
(iac[paras->idx].bd.len % AES_KEY_LEN))) * 8 / 6;
|
|
//Reserve space for websocket frame's head,
|
|
iac[paras->idx].bd.len += WEBSOCKET_HD_LEN;
|
|
}
|
|
}
|
|
|
|
memset(iac[paras->idx].bd.payload, 0, IP_KEEPALIVE_MAX_LEN);
|
|
memcpy(iac[paras->idx].bd.payload, paras->payload, paras->payload_len);
|
|
memcpy(iac[paras->idx].aes_key, paras->key, AES_KEY_LEN);
|
|
memcpy(iac[paras->idx].aes_iv, paras->iv, AES_IV_LEN);
|
|
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "idx = %d, len=%d\n", paras->idx, iac[paras->idx].bd.len);
|
|
idx_cfg.idx = paras->idx;
|
|
} else {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "payload_len = %d, too long!\n", paras->payload_len);
|
|
idx_cfg.err_code = -3; /* playload err */
|
|
}
|
|
|
|
err_idx:
|
|
return bes2600_testmode_reply(hw->wiphy, &idx_cfg, sizeof(struct ip_stream_cfg));
|
|
|
|
}
|
|
|
|
static int net_device_del_ip_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
|
|
{
|
|
struct ip_alive_iac_idx *alive_iac_idx = (struct ip_alive_iac_idx *)data;
|
|
int idx = alive_iac_idx->idx;
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct ip_alive_cfg *iac = hw_priv->iac;
|
|
struct bes2600_vif *priv;
|
|
|
|
if (vif) {
|
|
priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
} else {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "failed to get vif\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (idx <= NUM_IP_FRAMES && idx >= 0) {
|
|
iac[idx].bd.idx_used = 0;
|
|
bes2600_del_ip_offload(hw_priv, priv, idx);
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "delete idx = %d\n", idx);
|
|
} else {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "wrong idx = %d!\n", idx);
|
|
idx = -1; /* invalid idx */
|
|
}
|
|
return bes2600_testmode_reply(hw->wiphy, &idx, sizeof(int));
|
|
}
|
|
|
|
|
|
static int net_device_en_ip_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
|
|
{
|
|
struct ip_alive_period *alive_period = (struct ip_alive_period *)data;
|
|
int period = alive_period->period;
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct ip_alive_cfg *iac = hw_priv->iac;
|
|
struct bes2600_vif *priv;
|
|
u8 idx;
|
|
u8 ip_ack_filter[10] = {
|
|
1, // action mode: 0 disable, 1 filter out, 2 filter in
|
|
1, // filter number
|
|
1, // filter_mode: 0 disable, 1 filter out, 2 filter in
|
|
3, // address_mode: 1, src addr, 2 dest addr, 3 tcp ack
|
|
};
|
|
|
|
if (vif) {
|
|
priv = cw12xx_get_vif_from_ieee80211(vif);
|
|
} else {
|
|
bes2600_err(BES2600_DBG_TEST_MODE, "failed to get vif\n");
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
for (idx = 0; idx < NUM_IP_FRAMES; idx++) {
|
|
if (iac[idx].bd.idx_used) {
|
|
bes2600_set_ip_offload(hw_priv, priv, &iac[idx], idx);
|
|
bes2600_set_ipv4addrfilter(hw_priv, ip_ack_filter, priv->if_id);
|
|
}
|
|
}
|
|
|
|
bes2600_en_ip_offload(hw_priv, priv, period);
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "en period = %d\n", period);
|
|
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_BES2600_KEEP_ALIVE */
|
|
|
|
#if defined(STANDARD_FACTORY_EFUSE_FLAG)
|
|
static int bes2600_factory_cali_to_mcu(struct ieee80211_hw *hw, enum bes2600_rf_cmd_type cmd_type)
|
|
{
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
u8 *factory_data = NULL;
|
|
u8 *file_buffer = NULL;
|
|
u32 factory_data_len = 0;
|
|
int ret;
|
|
|
|
if (bes2600_chrdev_is_signal_mode())
|
|
return -EOPNOTSUPP;
|
|
|
|
if (!(file_buffer = bes2600_factory_get_file_buffer()))
|
|
return -ENOMEM;
|
|
|
|
bes2600_factory_lock();
|
|
factory_data = bes2600_get_factory_cali_data(file_buffer, &factory_data_len, FACTORY_PATH);
|
|
if (!factory_data) {
|
|
bes2600_warn(BES2600_DBG_DOWNLOAD, "factory cali data get failed.\n");
|
|
ret = -ENOENT;
|
|
} else {
|
|
bes2600_factory_data_check(factory_data);
|
|
factory_little_endian_cvrt(factory_data);
|
|
ret = wsm_save_factory_txt_to_mcu(hw_priv, factory_data, 0, cmd_type);
|
|
bes2600_err_with_cond(ret, BES2600_DBG_DOWNLOAD, "save factory data to mcu failed.\n");
|
|
}
|
|
bes2600_factory_free_file_buffer(file_buffer);
|
|
bes2600_factory_unlock();
|
|
|
|
if (ret >= 0)
|
|
return bes2600_testmode_reply(hw->wiphy, &ret, sizeof(int));
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
static int bes2600_set_select_efuse_flag_to_txt(struct ieee80211_hw *hw,
|
|
void *data, int len)
|
|
{
|
|
struct bes_select_calib_t *test_p = (struct bes_select_calib_t *)data;
|
|
int ret = bes2600_select_efuse_flag_write(test_p->select_efuse_flag);
|
|
return bes2600_testmode_reply(hw->wiphy, &ret, sizeof(int));
|
|
}
|
|
#endif
|
|
|
|
static int bes2600_vendor_cpu_usage(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
|
|
{
|
|
return wsm_cpu_usage_cmd(hw->priv);
|
|
}
|
|
|
|
static int bes2600_vendor_epta_parm_config(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u8 *data, int len)
|
|
{
|
|
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
|
|
struct bes2600_common *hw_priv = hw->priv;
|
|
struct vendor_epta_parm *epta_para = (struct vendor_epta_parm *)data;
|
|
|
|
return coex_set_epta_params(hw_priv, epta_para->wlan_duration,
|
|
epta_para->bt_duration, epta_para->hw_epta_enable);
|
|
#else
|
|
return -EOPNOTSUPP;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* bes2600_testmode_cmd -called when tesmode command
|
|
* reaches bes2600
|
|
*
|
|
* @hw: the hardware
|
|
* @data: incoming data
|
|
* @len: incoming data length
|
|
*
|
|
* Returns: 0 on success or non zero value on failure
|
|
*/
|
|
int bes2600_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len)
|
|
{
|
|
int ret = 0;
|
|
struct nlattr *type_p = nla_find(data, len, BES_TM_MSG_ID);
|
|
struct nlattr *data_p = nla_find(data, len, BES_TM_MSG_DATA);
|
|
|
|
if (!type_p || !data_p)
|
|
return -EINVAL;
|
|
|
|
bes2600_info(BES2600_DBG_TEST_MODE, "[STA] %s: type: %i\n",
|
|
__func__, nla_get_u32(type_p));
|
|
|
|
switch (nla_get_u32(type_p)) {
|
|
case BES_MSG_TEST:
|
|
ret = bes2600_test(hw,
|
|
nla_data(data_p), nla_len(data_p));
|
|
break;
|
|
case BES_MSG_SET_SNAP_FRAME:
|
|
ret = bes2600_set_snap_frame(hw, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
break;
|
|
case BES_MSG_GET_TX_POWER_LEVEL:
|
|
ret = bes2600_get_tx_power_level(hw);
|
|
break;
|
|
case BES_MSG_GET_TX_POWER_RANGE:
|
|
ret = bes2600_get_tx_power_range(hw);
|
|
break;
|
|
case BES_MSG_SET_ADVANCE_SCAN_ELEMS:
|
|
ret = bes2600_set_advance_scan_elems(hw, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
break;
|
|
case BES_MSG_SET_TX_QUEUE_PARAMS:
|
|
ret = bes2600_set_txqueue_params(hw, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
break;
|
|
case BES_MSG_GET_TSM_PARAMS:
|
|
ret = bes2600_get_tsm_params(hw);
|
|
break;
|
|
case BES_MSG_START_STOP_TSM:
|
|
ret = bes2600_start_stop_tsm(hw, (u8 *) nla_data(data_p));
|
|
break;
|
|
case BES_MSG_GET_ROAM_DELAY:
|
|
ret = bes2600_get_roam_delay(hw);
|
|
break;
|
|
case BES_MSG_SET_POWER_SAVE:
|
|
ret = bes2600_set_power_save(hw, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
break;
|
|
case BES_MSG_ADD_IP_OFFLOAD:
|
|
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
|
ret = net_device_add_ip_offload(hw, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
#else
|
|
ret = -EPERM;
|
|
#endif
|
|
break;
|
|
case BES_MSG_DEL_IP_OFFLOAD:
|
|
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
|
ret = net_device_del_ip_offload(hw, vif, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
#else
|
|
ret = -EPERM;
|
|
#endif
|
|
break;
|
|
case BES_MSG_SET_IP_OFFLOAD_PERIOD:
|
|
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
|
ret = net_device_en_ip_offload(hw, vif, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
#else
|
|
ret = -EPERM;
|
|
#endif
|
|
break;
|
|
case BES_MSG_GET_KEEP_ALIVE_STREAM:
|
|
#ifdef CONFIG_BES2600_KEEP_ALIVE
|
|
ret = get_keep_alive_used_stream(hw);
|
|
#else
|
|
ret = -EPERM;
|
|
#endif
|
|
break;
|
|
case BES_MSG_SAVE_CALI_TXT_TO_FLASH:
|
|
ret = -EPERM;
|
|
break;
|
|
case BES_MSG_SAVE_CALI_TXT_TO_EFUSE:
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
ret = bes2600_factory_cali_to_mcu(hw, BES2600_RF_CMD_CALI_TXT_TO_EFUSE);
|
|
#else
|
|
ret = -EPERM;
|
|
#endif
|
|
break;
|
|
case BES_MSG_SET_SELECT_EFUSE_FLAG:
|
|
#ifdef STANDARD_FACTORY_EFUSE_FLAG
|
|
ret = bes2600_set_select_efuse_flag_to_txt(hw, nla_data(data_p), nla_len(data_p));
|
|
#else
|
|
ret = EPERM;
|
|
#endif
|
|
break;
|
|
case BES_MSG_VENDOR_RF_CMD:
|
|
ret = bes2600_vendor_rf_cmd(hw, vif, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
break;
|
|
case BES_MSG_EPTA_PARM_CONFIG:
|
|
ret = bes2600_vendor_epta_parm_config(hw, vif, (u8 *) nla_data(data_p),
|
|
nla_len(data_p));
|
|
break;
|
|
case BES_MSG_MCU_CPUUSAGE:
|
|
ret = bes2600_vendor_cpu_usage(hw, vif);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* CONFIG_BES2600_TESTMODE */
|