8dd79199f8
The bes2600 driver is a fork of the upstream cw1200 driver
(drivers/net/wireless/st/cw1200/, ST-Ericsson, Dmitry Tarnyagin
2010-2011). The fork's file headers have three GPL-compliance issues:
1. NO SPDX-License-Identifier on any of 48 source files (cw1200
mainline has them on all 25). kernel.org-mandated since 2017.
2. Original "Copyright (c) 2010, ST-Ericsson" lines stripped from
all files inherited from cw1200, replaced with
"Copyright (c) 2010, Bestechnic" — factually impossible
(Bestechnic did not author the 2010 work) and a GPL-2.0 §1
attribution-preservation violation.
3. The "GPL version 2 as published by the Free Software Foundation"
boilerplate paragraph is redundant alongside SPDX and is the
legacy form modern kernel sources have replaced.
This patch corrects all three for the 48 .c/.h files in bes2600/:
- Adds `// SPDX-License-Identifier: GPL-2.0-only` (or `/* ... */`
for headers) as line 1 of every file.
- Restores `Copyright (c) 2010, ST-Ericsson` + `Author: Dmitry
Tarnyagin <dmitry.tarnyagin@lockless.no>` as the FIRST copyright
chain entry on all 22 files derived from cw1200 (bh.{c,h},
debug.{c,h}, fwio.{c,h}, hwio.{c,h}, main.c, pm.{c,h},
queue.{c,h}, scan.{c,h}, sta.{c,h}, txrx.{c,h}, wsm.{c,h}).
- Keeps `Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.` as
the SECOND chain entry where Bestechnic genuinely contributed.
- Notes "Derived from cw1200_sdio.c" + ST-Ericsson copyright on
bes2600_sdio.c (heavy derivation, not a literal rename).
- Notes "Replaces hwbus.h from cw1200/" + ST-Ericsson copyright
on sbus.h.
- Preserves the prism54/islsm authorship chain on main.c and
bes2600.h (Michael Wu 2006 + Jean-Baptiste Note 2004-2006).
- Drops the GPL-2.0 boilerplate paragraph in favour of SPDX.
No code changes — only file-header comment blocks. Module build is
unaffected (verified by header-only diff scope).
This is a prerequisite for any kernel.org submission attempt. The
existing MODULE_LICENSE("GPL") + MODULE_AUTHOR(Tarnyagin@stericsson.com)
declarations were already present and are unchanged here; the
mismatch between MODULE_AUTHOR and the (since-corrected) per-file
copyrights is now resolved.
890 lines
24 KiB
C
890 lines
24 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Main entry/init for BES2600 mac80211 driver
|
|
*
|
|
* Copyright (c) 2010, ST-Ericsson
|
|
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
|
|
*
|
|
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
|
|
*
|
|
* Based on the mac80211 Prism54 code, which is
|
|
* Copyright (c) 2006, Michael Wu <flamingice@sourmilk.net>
|
|
*
|
|
* Based on the islsm (softmac prism54) driver, which is
|
|
* Copyright 2004-2006 Jean-Baptiste Note <jbnote@gmail.com>, et al.
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/init.h>
|
|
#include <linux/firmware.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/random.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/of.h>
|
|
#include <net/mac80211.h>
|
|
|
|
#include "bes2600.h"
|
|
#include "txrx.h"
|
|
#include "sbus.h"
|
|
#include "fwio.h"
|
|
#include "hwio.h"
|
|
#include "bh.h"
|
|
#include "sta.h"
|
|
#include "ap.h"
|
|
#include "scan.h"
|
|
#include "debug.h"
|
|
#include "pm.h"
|
|
#include "bes2600_factory.h"
|
|
#include "bes_chardev.h"
|
|
|
|
MODULE_AUTHOR("Dmitry Tarnyagin <dmitry.tarnyagin@stericsson.com>");
|
|
MODULE_DESCRIPTION("Softmac BES2600 common code");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("bes2600");
|
|
|
|
/* TODO: use rates and channels from the device */
|
|
#define RATETAB_ENT(_rate, _rateid, _flags) \
|
|
{ \
|
|
.bitrate = (_rate), \
|
|
.hw_value = (_rateid), \
|
|
.flags = (_flags), \
|
|
}
|
|
|
|
static struct ieee80211_rate bes2600_rates[] = {
|
|
RATETAB_ENT(10, 0, 0),
|
|
RATETAB_ENT(20, 1, 0),
|
|
RATETAB_ENT(55, 2, 0),
|
|
RATETAB_ENT(110, 3, 0),
|
|
RATETAB_ENT(60, 6, 0),
|
|
RATETAB_ENT(90, 7, 0),
|
|
RATETAB_ENT(120, 8, 0),
|
|
RATETAB_ENT(180, 9, 0),
|
|
RATETAB_ENT(240, 10, 0),
|
|
RATETAB_ENT(360, 11, 0),
|
|
RATETAB_ENT(480, 12, 0),
|
|
RATETAB_ENT(540, 13, 0),
|
|
};
|
|
|
|
static struct ieee80211_rate bes2600_mcs_rates[] = {
|
|
RATETAB_ENT(65, 14, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(130, 15, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(195, 16, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(260, 17, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(390, 18, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(520, 19, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(585, 20, IEEE80211_TX_RC_MCS),
|
|
RATETAB_ENT(650, 21, IEEE80211_TX_RC_MCS),
|
|
};
|
|
|
|
#define bes2600_a_rates (bes2600_rates + 4)
|
|
#define bes2600_a_rates_size (ARRAY_SIZE(bes2600_rates) - 4)
|
|
#define bes2600_g_rates (bes2600_rates + 0)
|
|
#define bes2600_g_rates_size (ARRAY_SIZE(bes2600_rates))
|
|
#define bes2600_n_rates (bes2600_mcs_rates)
|
|
#define bes2600_n_rates_size (ARRAY_SIZE(bes2600_mcs_rates))
|
|
|
|
|
|
#define CHAN2G(_channel, _freq, _flags) { \
|
|
.band = NL80211_BAND_2GHZ, \
|
|
.center_freq = (_freq), \
|
|
.hw_value = (_channel), \
|
|
.flags = (_flags), \
|
|
.max_antenna_gain = 0, \
|
|
.max_power = 30, \
|
|
}
|
|
|
|
#define CHAN5G(_channel, _flags) { \
|
|
.band = NL80211_BAND_5GHZ, \
|
|
.center_freq = 5000 + (5 * (_channel)), \
|
|
.hw_value = (_channel), \
|
|
.flags = (_flags), \
|
|
.max_antenna_gain = 0, \
|
|
.max_power = 30, \
|
|
}
|
|
|
|
struct device *global_dev = NULL;
|
|
|
|
static struct ieee80211_channel bes2600_2ghz_chantable[] = {
|
|
CHAN2G(1, 2412, 0),
|
|
CHAN2G(2, 2417, 0),
|
|
CHAN2G(3, 2422, 0),
|
|
CHAN2G(4, 2427, 0),
|
|
CHAN2G(5, 2432, 0),
|
|
CHAN2G(6, 2437, 0),
|
|
CHAN2G(7, 2442, 0),
|
|
CHAN2G(8, 2447, 0),
|
|
CHAN2G(9, 2452, 0),
|
|
CHAN2G(10, 2457, 0),
|
|
CHAN2G(11, 2462, 0),
|
|
CHAN2G(12, 2467, 0),
|
|
CHAN2G(13, 2472, 0),
|
|
CHAN2G(14, 2484, 0),
|
|
};
|
|
|
|
static struct ieee80211_channel bes2600_5ghz_chantable[] = {
|
|
CHAN5G(34, 0), CHAN5G(36, 0),
|
|
CHAN5G(38, 0), CHAN5G(40, 0),
|
|
CHAN5G(42, 0), CHAN5G(44, 0),
|
|
CHAN5G(46, 0), CHAN5G(48, 0),
|
|
CHAN5G(52, 0), CHAN5G(56, 0),
|
|
CHAN5G(60, 0), CHAN5G(64, 0),
|
|
CHAN5G(100, 0), CHAN5G(104, 0),
|
|
CHAN5G(108, 0), CHAN5G(112, 0),
|
|
CHAN5G(116, 0), CHAN5G(120, 0),
|
|
CHAN5G(124, 0), CHAN5G(128, 0),
|
|
CHAN5G(132, 0), CHAN5G(136, 0),
|
|
CHAN5G(140, 0), CHAN5G(149, 0),
|
|
CHAN5G(153, 0), CHAN5G(157, 0),
|
|
CHAN5G(161, 0), CHAN5G(165, 0),
|
|
CHAN5G(184, 0), CHAN5G(188, 0),
|
|
CHAN5G(192, 0), CHAN5G(196, 0),
|
|
CHAN5G(200, 0), CHAN5G(204, 0),
|
|
CHAN5G(208, 0), CHAN5G(212, 0),
|
|
CHAN5G(216, 0),
|
|
};
|
|
|
|
static struct ieee80211_supported_band bes2600_band_2ghz = {
|
|
.channels = bes2600_2ghz_chantable,
|
|
.n_channels = ARRAY_SIZE(bes2600_2ghz_chantable),
|
|
.bitrates = bes2600_g_rates,
|
|
.n_bitrates = bes2600_g_rates_size,
|
|
.ht_cap = {
|
|
.cap = IEEE80211_HT_CAP_GRN_FLD |
|
|
(STBC_RX_24G << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
|
|
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
|
IEEE80211_HT_CAP_SGI_20 |
|
|
IEEE80211_HT_CAP_SGI_40 |
|
|
IEEE80211_HT_CAP_MAX_AMSDU,
|
|
.ht_supported = 1,
|
|
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
|
|
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
|
|
.mcs = {
|
|
.rx_mask[0] = 0xFF,
|
|
.rx_highest = __cpu_to_le16(0),
|
|
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
|
|
},
|
|
},
|
|
};
|
|
|
|
static struct ieee80211_supported_band bes2600_band_5ghz = {
|
|
.channels = bes2600_5ghz_chantable,
|
|
.n_channels = ARRAY_SIZE(bes2600_5ghz_chantable),
|
|
.bitrates = bes2600_a_rates,
|
|
.n_bitrates = bes2600_a_rates_size,
|
|
.ht_cap = {
|
|
.cap = IEEE80211_HT_CAP_GRN_FLD |
|
|
(STBC_RX_5G << IEEE80211_HT_CAP_RX_STBC_SHIFT) |
|
|
IEEE80211_HT_CAP_SUP_WIDTH_20_40 |
|
|
IEEE80211_HT_CAP_SGI_20 |
|
|
IEEE80211_HT_CAP_SGI_40 |
|
|
IEEE80211_HT_CAP_MAX_AMSDU,
|
|
.ht_supported = 1,
|
|
.ampdu_factor = IEEE80211_HT_MAX_AMPDU_32K,
|
|
.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE,
|
|
.mcs = {
|
|
.rx_mask[0] = 0xFF,
|
|
.rx_highest = __cpu_to_le16(0x41),
|
|
.tx_params = IEEE80211_HT_MCS_TX_DEFINED,
|
|
},
|
|
},
|
|
};
|
|
|
|
static const unsigned long bes2600_ttl[] = {
|
|
1 * HZ, /* VO */
|
|
2 * HZ, /* VI */
|
|
5 * HZ, /* BE */
|
|
10 * HZ /* BK */
|
|
};
|
|
|
|
static const struct ieee80211_iface_limit bes2600_if_limits[] = {
|
|
{ .max = 2, .types = BIT(NL80211_IFTYPE_STATION) },
|
|
{ .max = 1, .types = BIT(NL80211_IFTYPE_AP) |
|
|
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
BIT(NL80211_IFTYPE_P2P_GO) },
|
|
#ifdef P2P_MULTIVIF
|
|
{ .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) },
|
|
#endif
|
|
};
|
|
|
|
|
|
|
|
static const struct ieee80211_iface_combination bes2600_if_comb[] = {
|
|
{
|
|
.limits = bes2600_if_limits,
|
|
.n_limits = ARRAY_SIZE(bes2600_if_limits),
|
|
.max_interfaces = CW12XX_MAX_VIFS,
|
|
.num_different_channels = 1,
|
|
},
|
|
};
|
|
|
|
|
|
static const struct ieee80211_ops bes2600_ops = {
|
|
.add_chanctx = ieee80211_emulate_add_chanctx,
|
|
.remove_chanctx = ieee80211_emulate_remove_chanctx,
|
|
.change_chanctx = ieee80211_emulate_change_chanctx,
|
|
.switch_vif_chanctx = ieee80211_emulate_switch_vif_chanctx,
|
|
.start = bes2600_start,
|
|
.stop = bes2600_stop,
|
|
.add_interface = bes2600_add_interface,
|
|
.remove_interface = bes2600_remove_interface,
|
|
.change_interface = bes2600_change_interface,
|
|
.tx = bes2600_tx,
|
|
.wake_tx_queue = ieee80211_handle_wake_tx_queue,
|
|
.hw_scan = bes2600_hw_scan,
|
|
.cancel_hw_scan = bes2600_cancel_hw_scan,
|
|
#ifdef ROAM_OFFLOAD
|
|
.sched_scan_start = bes2600_hw_sched_scan_start,
|
|
.sched_scan_stop = bes2600_hw_sched_scan_stop,
|
|
#endif /*ROAM_OFFLOAD*/
|
|
.set_tim = bes2600_set_tim,
|
|
.sta_notify = bes2600_sta_notify,
|
|
.sta_add = bes2600_sta_add,
|
|
.sta_remove = bes2600_sta_remove,
|
|
.set_key = bes2600_set_key,
|
|
.set_rts_threshold = bes2600_set_rts_threshold,
|
|
.config = bes2600_config,
|
|
.bss_info_changed = bes2600_bss_info_changed,
|
|
.prepare_multicast = bes2600_prepare_multicast,
|
|
.configure_filter = bes2600_configure_filter,
|
|
.conf_tx = bes2600_conf_tx,
|
|
.get_stats = bes2600_get_stats,
|
|
.ampdu_action = bes2600_ampdu_action,
|
|
.flush = bes2600_flush,
|
|
#ifdef CONFIG_PM
|
|
.suspend = bes2600_wow_suspend,
|
|
.resume = bes2600_wow_resume,
|
|
#endif
|
|
/* Intentionally not offloaded: */
|
|
/*.channel_switch = bes2600_channel_switch, */
|
|
.remain_on_channel = bes2600_remain_on_channel,
|
|
.cancel_remain_on_channel = bes2600_cancel_remain_on_channel,
|
|
#ifdef IPV6_FILTERING
|
|
//.set_data_filter = bes2600_set_data_filter,
|
|
#endif /*IPV6_FILTERING*/
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
.testmode_cmd = bes2600_testmode_cmd,
|
|
#endif
|
|
};
|
|
|
|
#ifdef CONFIG_PM
|
|
static const struct wiphy_wowlan_support bes2600_wowlan_support = {
|
|
/* Support only for limited wowlan functionalities */
|
|
.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT,
|
|
};
|
|
#endif
|
|
|
|
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
|
static void bes2600_init_wapi_cipher(struct ieee80211_hw *hw)
|
|
{
|
|
static struct ieee80211_cipher_scheme wapi_cs = {
|
|
.cipher = WLAN_CIPHER_SUITE_SMS4,
|
|
.iftype = BIT(NL80211_IFTYPE_STATION),
|
|
.hdr_len = 18,
|
|
.pn_len = 16,
|
|
.pn_off = 2,
|
|
.key_idx_off = 0,
|
|
.key_idx_mask = 0x01,
|
|
.key_idx_shift = 0,
|
|
.mic_len = 16
|
|
};
|
|
|
|
hw->cipher_schemes = &wapi_cs;
|
|
hw->n_cipher_schemes = 1;
|
|
}
|
|
#endif
|
|
|
|
static void bes2600_get_base_mac(struct bes2600_common *hw_priv)
|
|
{
|
|
struct device_node *np;
|
|
const u8* addr = NULL;
|
|
bool ok = false;
|
|
int len;
|
|
|
|
np = of_find_compatible_node(NULL, NULL, "bestechnic,bes2600-sdio");
|
|
if (np) {
|
|
addr = of_get_property(np, "local-mac-address", &len);
|
|
if (addr && len == ETH_ALEN) {
|
|
memcpy(hw_priv->addresses[0].addr, addr, ETH_ALEN);
|
|
ok = true;
|
|
} else {
|
|
bes_err("bestechnic,bes2600 device node does not have valid local-mac-address property, random mac will be used!\n");
|
|
}
|
|
of_node_put(np);
|
|
} else {
|
|
bes_err("bestechnic,bes2600 device node NOT found, random mac will be used!\n");
|
|
}
|
|
if (!ok)
|
|
get_random_bytes(hw_priv->addresses[0].addr, ETH_ALEN);
|
|
|
|
hw_priv->addresses[0].addr[0] &= ~1u;
|
|
}
|
|
|
|
static void bes2600_derive_mac(struct bes2600_common *hw_priv)
|
|
{
|
|
memcpy(hw_priv->addresses[1].addr, hw_priv->addresses[0].addr, ETH_ALEN);
|
|
hw_priv->addresses[1].addr[5] = hw_priv->addresses[0].addr[5] + 1;
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
memcpy(hw_priv->addresses[2].addr, hw_priv->addresses[1].addr,
|
|
ETH_ALEN);
|
|
hw_priv->addresses[2].addr[4] ^= 0x80;
|
|
#endif
|
|
}
|
|
|
|
static struct ieee80211_hw *bes2600_init_common(size_t hw_priv_data_len)
|
|
{
|
|
int i;
|
|
struct ieee80211_hw *hw;
|
|
struct bes2600_common *hw_priv;
|
|
struct ieee80211_supported_band *sband;
|
|
int band;
|
|
|
|
hw = ieee80211_alloc_hw(hw_priv_data_len, &bes2600_ops);
|
|
if (!hw)
|
|
return NULL;
|
|
|
|
hw_priv = hw->priv;
|
|
hw_priv->if_id_slot = 0;
|
|
hw_priv->roc_if_id = -1;
|
|
hw_priv->scan_switch_if_id = -1;
|
|
atomic_set(&hw_priv->num_vifs, 0);
|
|
atomic_set(&hw_priv->netdevice_start, 0);
|
|
|
|
bes2600_get_base_mac(hw_priv);
|
|
bes2600_derive_mac(hw_priv);
|
|
|
|
hw_priv->hw = hw;
|
|
hw_priv->rates = bes2600_rates; /* TODO: fetch from FW */
|
|
hw_priv->mcs_rates = bes2600_n_rates;
|
|
#ifdef ROAM_OFFLOAD
|
|
hw_priv->auto_scanning = 0;
|
|
hw_priv->frame_rcvd = 0;
|
|
hw_priv->num_scanchannels = 0;
|
|
hw_priv->num_2g_channels = 0;
|
|
hw_priv->num_5g_channels = 0;
|
|
#endif /*ROAM_OFFLOAD*/
|
|
#ifdef AP_AGGREGATE_FW_FIX
|
|
/* Enable block ACK for 4 TID (BE,VI,VI,VO). */
|
|
/*due to HW limitations*/
|
|
hw_priv->ba_tid_mask = 0xB1;
|
|
#else
|
|
/* Enable block ACK for every TID but voice. */
|
|
hw_priv->ba_tid_mask = 0xFF;//0x3F;
|
|
#endif
|
|
|
|
/* Init tx retry limit */
|
|
#ifdef BES2600_TX_RX_OPT
|
|
hw_priv->long_frame_max_tx_count = 31;
|
|
hw_priv->short_frame_max_tx_count = 31;
|
|
#else
|
|
hw_priv->long_frame_max_tx_count = 7;
|
|
hw_priv->short_frame_max_tx_count = 15;
|
|
#endif
|
|
hw_priv->hw->max_rate_tries = hw_priv->short_frame_max_tx_count;
|
|
|
|
ieee80211_hw_set(hw, SIGNAL_DBM);
|
|
ieee80211_hw_set(hw, SUPPORTS_PS);
|
|
ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS);
|
|
ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS);
|
|
ieee80211_hw_set(hw, NEED_DTIM_BEFORE_ASSOC);
|
|
ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW);
|
|
ieee80211_hw_set(hw, AMPDU_AGGREGATION);
|
|
ieee80211_hw_set(hw, CONNECTION_MONITOR);
|
|
ieee80211_hw_set(hw, MFP_CAPABLE);
|
|
|
|
hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
|
|
BIT(NL80211_IFTYPE_ADHOC) |
|
|
BIT(NL80211_IFTYPE_AP) |
|
|
BIT(NL80211_IFTYPE_MESH_POINT) |
|
|
BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
BIT(NL80211_IFTYPE_P2P_GO);
|
|
#ifdef P2P_MULTIVIF
|
|
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_DEVICE);
|
|
#endif
|
|
|
|
/* Support only for limited wowlan functionalities */
|
|
#ifdef CONFIG_PM
|
|
hw->wiphy->wowlan = &bes2600_wowlan_support;
|
|
#endif
|
|
|
|
#if defined(CONFIG_BES2600_USE_STE_EXTENSIONS)
|
|
hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD;
|
|
#endif /* CONFIG_BES2600_USE_STE_EXTENSIONS */
|
|
|
|
#ifdef PROBE_RESP_EXTRA_IE
|
|
hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD;
|
|
#endif
|
|
|
|
#if defined(CONFIG_BES2600_DISABLE_BEACON_HINTS)
|
|
hw->wiphy->flags |= WIPHY_FLAG_DISABLE_BEACON_HINTS;
|
|
#endif
|
|
hw->wiphy->n_addresses = CW12XX_MAX_VIFS;
|
|
hw->wiphy->addresses = hw_priv->addresses;
|
|
hw->wiphy->max_remain_on_channel_duration = 500;
|
|
//hw->channel_change_time = 500; /* TODO: find actual value */
|
|
/* hw_priv->beacon_req_id = cpu_to_le32(0); */
|
|
hw->queues = 4;
|
|
hw_priv->noise = -94;
|
|
|
|
hw->max_rates = 8;
|
|
hw->max_rate_tries = 15;
|
|
hw->extra_tx_headroom = WSM_TX_EXTRA_HEADROOM +
|
|
8 /* TKIP IV */ +
|
|
12 /* TKIP ICV and MIC */;
|
|
|
|
hw->sta_data_size = sizeof(struct bes2600_sta_priv);
|
|
hw->vif_data_size = sizeof(struct bes2600_vif);
|
|
|
|
hw->wiphy->bands[NL80211_BAND_2GHZ] = &bes2600_band_2ghz;
|
|
hw->wiphy->bands[NL80211_BAND_5GHZ] = &bes2600_band_5ghz;
|
|
|
|
/* Channel params have to be cleared before registering wiphy again */
|
|
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
|
sband = hw->wiphy->bands[band];
|
|
if (!sband)
|
|
continue;
|
|
for (i = 0; i < sband->n_channels; i++) {
|
|
sband->channels[i].flags = 0;
|
|
sband->channels[i].max_antenna_gain = 0;
|
|
sband->channels[i].max_power = 30;
|
|
}
|
|
}
|
|
|
|
hw->wiphy->max_scan_ssids = WSM_SCAN_MAX_NUM_OF_SSIDS;
|
|
hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN;
|
|
|
|
hw->wiphy->iface_combinations = bes2600_if_comb;
|
|
hw->wiphy->n_iface_combinations = ARRAY_SIZE(bes2600_if_comb);
|
|
|
|
#ifdef CONFIG_BES2600_WAPI_SUPPORT
|
|
hw_priv->last_ins_wapi_usk_id = -1;
|
|
hw_priv->last_del_wapi_usk_id = -1;
|
|
bes2600_init_wapi_cipher(hw);
|
|
#endif
|
|
|
|
SET_IEEE80211_PERM_ADDR(hw, hw_priv->addresses[0].addr);
|
|
|
|
spin_lock_init(&hw_priv->vif_list_lock);
|
|
sema_init(&hw_priv->wsm_cmd_sema, 1);
|
|
sema_init(&hw_priv->conf_lock, 1);
|
|
sema_init(&hw_priv->wsm_oper_lock, 1);
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
spin_lock_init(&hw_priv->tsm_lock);
|
|
#endif /*CONFIG_BES2600_TESTMODE*/
|
|
hw_priv->workqueue = create_singlethread_workqueue("bes2600_wq");
|
|
sema_init(&hw_priv->scan.lock, 1);
|
|
INIT_WORK(&hw_priv->scan.work, bes2600_scan_work);
|
|
#ifdef ROAM_OFFLOAD
|
|
INIT_WORK(&hw_priv->scan.swork, bes2600_sched_scan_work);
|
|
#endif /*ROAM_OFFLOAD*/
|
|
INIT_DELAYED_WORK(&hw_priv->scan.probe_work, bes2600_probe_work);
|
|
INIT_DELAYED_WORK(&hw_priv->scan.timeout, bes2600_scan_timeout);
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
INIT_DELAYED_WORK(&hw_priv->advance_scan_timeout,
|
|
bes2600_advance_scan_timeout);
|
|
#endif
|
|
INIT_DELAYED_WORK(&hw_priv->rem_chan_timeout, bes2600_rem_chan_timeout);
|
|
hw_priv->rtsvalue = 0;
|
|
spin_lock_init(&hw_priv->rtsvalue_lock);
|
|
INIT_WORK(&hw_priv->dynamic_opt_txrx_work, bes2600_dynamic_opt_txrx_work);
|
|
INIT_WORK(&hw_priv->tx_policy_upload_work, tx_policy_upload_work);
|
|
INIT_WORK(&hw_priv->connection_loss_storm_recover_work,
|
|
bes2600_connection_loss_storm_recover);
|
|
spin_lock_init(&hw_priv->event_queue_lock);
|
|
INIT_LIST_HEAD(&hw_priv->event_queue);
|
|
INIT_WORK(&hw_priv->event_handler, bes2600_event_handler);
|
|
INIT_WORK(&hw_priv->ba_work, bes2600_ba_work);
|
|
spin_lock_init(&hw_priv->ba_lock);
|
|
timer_setup(&hw_priv->ba_timer, bes2600_ba_timer, 0);
|
|
|
|
if (unlikely(bes2600_queue_stats_init(&hw_priv->tx_queue_stats,
|
|
WLAN_LINK_ID_MAX,
|
|
bes2600_skb_dtor,
|
|
hw_priv))) {
|
|
destroy_workqueue(hw_priv->workqueue);
|
|
ieee80211_free_hw(hw);
|
|
return NULL;
|
|
}
|
|
|
|
for (i = 0; i < 4; ++i) {
|
|
if (unlikely(bes2600_queue_init(&hw_priv->tx_queue[i],
|
|
&hw_priv->tx_queue_stats, i, CW12XX_MAX_QUEUE_SZ,
|
|
bes2600_ttl[i]))) {
|
|
for (; i > 0; i--)
|
|
bes2600_queue_deinit(&hw_priv->tx_queue[i - 1]);
|
|
bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
|
|
destroy_workqueue(hw_priv->workqueue);
|
|
ieee80211_free_hw(hw);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
init_waitqueue_head(&hw_priv->channel_switch_done);
|
|
init_waitqueue_head(&hw_priv->wsm_cmd_wq);
|
|
init_waitqueue_head(&hw_priv->wsm_startup_done);
|
|
init_waitqueue_head(&hw_priv->offchannel_wq);
|
|
hw_priv->offchannel_done = 0;
|
|
wsm_buf_init(&hw_priv->wsm_cmd_buf);
|
|
spin_lock_init(&hw_priv->wsm_cmd.lock);
|
|
timer_setup(&hw_priv->mcu_mon_timer, bes2600_bh_mcu_active_monitor, 0);
|
|
timer_setup(&hw_priv->lmac_mon_timer, bes2600_bh_lmac_active_monitor, 0);
|
|
|
|
bes2600_tx_loop_init(hw_priv);
|
|
|
|
#ifdef CONFIG_PM
|
|
bes2600_suspend_status_set(hw_priv, false);
|
|
bes2600_pending_unjoin_reset(hw_priv);
|
|
#endif
|
|
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
hw_priv->test_frame.data = NULL;
|
|
hw_priv->test_frame.len = 0;
|
|
#endif /* CONFIG_BES2600_TESTMODE */
|
|
|
|
#if defined(CONFIG_BES2600_WSM_DUMPS_SHORT)
|
|
hw_priv->wsm_dump_max_size = 20;
|
|
#endif /* CONFIG_BES2600_WSM_DUMPS_SHORT */
|
|
|
|
for (i = 0; i < CW12XX_MAX_VIFS; i++)
|
|
hw_priv->hw_bufs_used_vif[i] = 0;
|
|
|
|
#ifdef MCAST_FWDING
|
|
for (i = 0; i < WSM_MAX_BUF; i++)
|
|
wsm_init_release_buffer_request(hw_priv, i);
|
|
hw_priv->buf_released = 0;
|
|
#endif
|
|
hw_priv->vif0_throttle = CW12XX_HOST_VIF0_11BG_THROTTLE;
|
|
hw_priv->vif1_throttle = CW12XX_HOST_VIF1_11BG_THROTTLE;
|
|
return hw;
|
|
}
|
|
|
|
static int bes2600_register_common(struct ieee80211_hw *dev)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
int err;
|
|
|
|
err = ieee80211_register_hw(dev);
|
|
if (err) {
|
|
bes_err("Cannot register device (%d).\n", err);
|
|
return err;
|
|
}
|
|
|
|
bes2600_debug_init_common(hw_priv);
|
|
#ifdef CONFIG_PM
|
|
bes2600_register_pm_notifier(hw_priv);
|
|
#endif /* CONFIG_PM */
|
|
bes_info("registered as '%s'\n", wiphy_name(dev->wiphy));
|
|
return 0;
|
|
}
|
|
|
|
static void bes2600_free_common(struct ieee80211_hw *dev)
|
|
{
|
|
/* struct bes2600_common *hw_priv = dev->priv; */
|
|
#ifdef CONFIG_BES2600_TESTMODE
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
if (hw_priv->test_frame.data) {
|
|
kfree(hw_priv->test_frame.data);
|
|
hw_priv->test_frame.data = NULL;
|
|
hw_priv->test_frame.len = 0;
|
|
}
|
|
#endif /* CONFIG_BES2600_TESTMODE */
|
|
|
|
/* unsigned int i; */
|
|
|
|
ieee80211_free_hw(dev);
|
|
}
|
|
|
|
static void bes2600_unregister_common(struct ieee80211_hw *dev)
|
|
{
|
|
struct bes2600_common *hw_priv = dev->priv;
|
|
int i;
|
|
|
|
ieee80211_unregister_hw(dev);
|
|
|
|
del_timer_sync(&hw_priv->ba_timer);
|
|
|
|
hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
|
|
bes2600_unregister_bh(hw_priv);
|
|
|
|
bes2600_debug_release_common(hw_priv);
|
|
#ifdef CONFIG_PM
|
|
bes2600_unregister_pm_notifier(hw_priv);
|
|
#endif /* CONFIG_PM */
|
|
|
|
wsm_buf_deinit(&hw_priv->wsm_cmd_buf);
|
|
destroy_workqueue(hw_priv->workqueue);
|
|
hw_priv->workqueue = NULL;
|
|
if (hw_priv->skb_cache) {
|
|
dev_kfree_skb(hw_priv->skb_cache);
|
|
hw_priv->skb_cache = NULL;
|
|
}
|
|
if (hw_priv->sdd) {
|
|
#ifndef CONFIG_BES2600_STATIC_SDD
|
|
release_firmware(hw_priv->sdd);
|
|
#endif
|
|
hw_priv->sdd = NULL;
|
|
}
|
|
for (i = 0; i < 4; ++i)
|
|
bes2600_queue_deinit(&hw_priv->tx_queue[i]);
|
|
bes2600_queue_stats_deinit(&hw_priv->tx_queue_stats);
|
|
for (i = 0; i < CW12XX_MAX_VIFS; i++) {
|
|
kfree(hw_priv->vif_list[i]);
|
|
hw_priv->vif_list[i] = NULL;
|
|
}
|
|
|
|
bes2600_pwr_exit(hw_priv);
|
|
}
|
|
|
|
#if 0
|
|
static void cw12xx_set_ifce_comb(struct bes2600_common *hw_priv,
|
|
struct ieee80211_hw *hw)
|
|
{
|
|
#ifdef P2P_MULTIVIF
|
|
hw_priv->if_limits1[0].max = 2;
|
|
#else
|
|
hw_priv->if_limits1[0].max = 1;
|
|
#endif
|
|
|
|
hw_priv->if_limits1[0].types = BIT(NL80211_IFTYPE_STATION);
|
|
hw_priv->if_limits1[1].max = 1;
|
|
hw_priv->if_limits1[1].types = BIT(NL80211_IFTYPE_AP);
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
hw_priv->if_limits2[0].max = 3;
|
|
#else
|
|
hw_priv->if_limits2[0].max = 2;
|
|
#endif
|
|
hw_priv->if_limits2[0].types = BIT(NL80211_IFTYPE_STATION);
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
hw_priv->if_limits3[0].max = 2;
|
|
#else
|
|
hw_priv->if_limits3[0].max = 1;
|
|
#endif
|
|
|
|
hw_priv->if_limits3[0].types = BIT(NL80211_IFTYPE_STATION);
|
|
hw_priv->if_limits3[1].max = 1;
|
|
hw_priv->if_limits3[1].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
|
|
BIT(NL80211_IFTYPE_P2P_GO);
|
|
|
|
/* TODO:COMBO: mac80211 doesn't yet support more than 1
|
|
* different channel */
|
|
hw_priv->if_combs[0].num_different_channels = 1;
|
|
#ifdef P2P_MULTIVIF
|
|
hw_priv->if_combs[0].max_interfaces = 3;
|
|
#else
|
|
hw_priv->if_combs[0].max_interfaces = 2;
|
|
#endif
|
|
hw_priv->if_combs[0].limits = hw_priv->if_limits1;
|
|
hw_priv->if_combs[0].n_limits = 2;
|
|
|
|
hw_priv->if_combs[1].num_different_channels = 1;
|
|
|
|
#ifdef P2P_MULTIVIF
|
|
hw_priv->if_combs[1].max_interfaces = 3;
|
|
#else
|
|
hw_priv->if_combs[1].max_interfaces = 2;
|
|
#endif
|
|
hw_priv->if_combs[1].limits = hw_priv->if_limits2;
|
|
hw_priv->if_combs[1].n_limits = 1;
|
|
|
|
hw_priv->if_combs[2].num_different_channels = 1;
|
|
#ifdef P2P_MULTIVIF
|
|
hw_priv->if_combs[2].max_interfaces = 3;
|
|
#else
|
|
hw_priv->if_combs[2].max_interfaces = 2;
|
|
#endif
|
|
hw_priv->if_combs[2].limits = hw_priv->if_limits3;
|
|
hw_priv->if_combs[2].n_limits = 2;
|
|
|
|
hw->wiphy->iface_combinations = &hw_priv->if_combs[0];
|
|
hw->wiphy->n_iface_combinations = 3;
|
|
|
|
}
|
|
#endif
|
|
static int bes2600_sbus_comm_init(struct bes2600_common *hw_priv)
|
|
{
|
|
int ret = 0;
|
|
|
|
#if defined(FW_DOWNLOAD_BY_USB)
|
|
if (hw_priv->sbus_ops->reset)
|
|
hw_priv->sbus_ops->reset(hw_priv->sbus_priv);
|
|
#endif
|
|
|
|
#ifndef CONFIG_BES2600_WLAN_SPI
|
|
if (hw_priv->sbus_ops->init)
|
|
hw_priv->sbus_ops->init(hw_priv->sbus_priv, hw_priv);
|
|
#endif
|
|
|
|
/* Register Interrupt Handler */
|
|
hw_priv->sbus_ops->irq_subscribe(hw_priv->sbus_priv,
|
|
(sbus_irq_handler)bes2600_irq_handler, hw_priv);
|
|
hw_priv->hw_type = HIF_8601_SILICON;
|
|
hw_priv->hw_revision = BES2600_HW_REV_CUT10;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_core_probe(const struct sbus_ops *sbus_ops,
|
|
struct sbus_priv *sbus,
|
|
struct device *pdev,
|
|
struct bes2600_common **pself)
|
|
{
|
|
int err = -ENOMEM;
|
|
//u16 ctrl_reg;
|
|
struct ieee80211_hw *dev;
|
|
struct bes2600_common *hw_priv;
|
|
|
|
dev = bes2600_init_common(sizeof(struct bes2600_common));
|
|
if (!dev)
|
|
goto err;
|
|
|
|
global_dev = pdev;
|
|
|
|
hw_priv = dev->priv;
|
|
hw_priv->sbus_ops = sbus_ops;
|
|
hw_priv->sbus_priv = sbus;
|
|
hw_priv->pdev = pdev;
|
|
SET_IEEE80211_DEV(hw_priv->hw, pdev);
|
|
|
|
/* WSM callbacks. */
|
|
hw_priv->wsm_cbc.scan_complete = bes2600_scan_complete_cb;
|
|
hw_priv->wsm_cbc.tx_confirm = bes2600_tx_confirm_cb;
|
|
hw_priv->wsm_cbc.rx = bes2600_rx_cb;
|
|
hw_priv->wsm_cbc.suspend_resume = bes2600_suspend_resume;
|
|
/* hw_priv->wsm_cbc.set_pm_complete = bes2600_set_pm_complete_cb; */
|
|
hw_priv->wsm_cbc.channel_switch = bes2600_channel_switch_cb;
|
|
|
|
bes2600_pwr_init(hw_priv);
|
|
|
|
err = bes2600_register_bh(hw_priv);
|
|
if (err)
|
|
goto err1;
|
|
|
|
err = bes2600_sbus_comm_init(hw_priv);
|
|
if (err)
|
|
goto err2;
|
|
|
|
if (bes2600_chrdev_get_fw_type() == BES2600_FW_TYPE_WIFI_NO_SIGNAL) {
|
|
*pself = dev->priv;
|
|
if (bes2600_wifi_start(hw_priv))
|
|
goto err3;
|
|
}
|
|
|
|
err = bes2600_register_common(dev);
|
|
if (err)
|
|
goto err3;
|
|
|
|
*pself = dev->priv;
|
|
return err;
|
|
|
|
err3:
|
|
hw_priv->sbus_ops->irq_unsubscribe(hw_priv->sbus_priv);
|
|
if (sbus_ops->reset)
|
|
sbus_ops->reset(sbus);
|
|
err2:
|
|
bes2600_unregister_bh(hw_priv);
|
|
err1:
|
|
bes2600_free_common(dev);
|
|
err:
|
|
*pself = NULL;
|
|
return err;
|
|
}
|
|
|
|
void bes2600_core_release(struct bes2600_common *self)
|
|
{
|
|
bes2600_unregister_common(self->hw);
|
|
bes2600_free_common(self->hw);
|
|
return;
|
|
}
|
|
|
|
int bes2600_wifi_start(struct bes2600_common *hw_priv)
|
|
{
|
|
int ret = 0, if_id;
|
|
|
|
if (hw_priv->sbus_ops->gpio_wake) {
|
|
hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON);
|
|
}
|
|
|
|
if (hw_priv->sbus_ops->sbus_active &&
|
|
WARN_ON((ret = hw_priv->sbus_ops->sbus_active(hw_priv->sbus_priv, SUBSYSTEM_WIFI))))
|
|
goto err;
|
|
|
|
if (wait_event_interruptible_timeout(hw_priv->wsm_startup_done,
|
|
hw_priv->wsm_caps.firmwareReady, 10 * HZ) <= 0) {
|
|
|
|
/* TODO: Needs to find how to reset device */
|
|
/* in QUEUE mode properly. */
|
|
bes_info("startup timeout!!!\n");
|
|
ret = -ENODEV;
|
|
goto err;
|
|
}
|
|
|
|
if (bes2600_chrdev_is_signal_mode()) {
|
|
for (if_id = 0; if_id < 2; if_id++) {
|
|
/* Enable multi-TX confirmation */
|
|
if (WARN_ON((ret = wsm_use_multi_tx_conf(hw_priv, true, if_id)))) {
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
bes2600_pwr_start(hw_priv);
|
|
|
|
err:
|
|
if (hw_priv->sbus_ops->gpio_sleep) {
|
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_ON);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int bes2600_wifi_stop(struct bes2600_common *hw_priv)
|
|
{
|
|
int ret;
|
|
unsigned long status = 0;
|
|
|
|
status = wait_event_timeout(hw_priv->bh_evt_wq, (!hw_priv->hw_bufs_used), 3 * HZ);
|
|
if (!status)
|
|
bes_err("communication exception!\n");
|
|
|
|
if(hw_priv->sbus_ops->gpio_wake) {
|
|
hw_priv->sbus_ops->gpio_wake(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
|
|
}
|
|
|
|
bes2600_pwr_stop(hw_priv);
|
|
|
|
if (hw_priv->sbus_ops->sbus_deactive &&
|
|
WARN_ON(ret = hw_priv->sbus_ops->sbus_deactive(hw_priv->sbus_priv, SUBSYSTEM_WIFI))) {
|
|
goto err;
|
|
}
|
|
|
|
if(hw_priv->sbus_ops->gpio_sleep) {
|
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
|
|
}
|
|
|
|
memset(&hw_priv->wsm_caps, 0, sizeof(hw_priv->wsm_caps));
|
|
hw_priv->wsm_rx_seq[0] = 0;
|
|
hw_priv->wsm_rx_seq[1] = 0;
|
|
hw_priv->wsm_tx_seq[0] = 0;
|
|
hw_priv->wsm_tx_seq[1] = 0;
|
|
hw_priv->wsm_tx_pending[0] = 0;
|
|
hw_priv->wsm_tx_pending[1] = 0;
|
|
del_timer_sync(&hw_priv->mcu_mon_timer);
|
|
del_timer_sync(&hw_priv->lmac_mon_timer);
|
|
#ifdef CONFIG_BES2600_STATIC_SDD
|
|
hw_priv->sdd = NULL;
|
|
#else
|
|
#error "TO BE CONTINUED: release SDD file"
|
|
#endif
|
|
return ret;
|
|
|
|
err:
|
|
if(hw_priv->sbus_ops->gpio_sleep) {
|
|
hw_priv->sbus_ops->gpio_sleep(hw_priv->sbus_priv, GPIO_WAKE_FLAG_WIFI_OFF);
|
|
}
|
|
|
|
return ret;
|
|
}
|