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

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

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

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

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

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

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

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

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;
}