Files
bes2600-dkms/bes2600/scan.c
T
test0r a02f8b7629 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-20 20:17:58 +02:00

1232 lines
35 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Scan implementation for BES2600 mac80211 driver
*
* Copyright (c) 2010, ST-Ericsson
* Author: Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no>
*
* Copyright (c) 2022, Bestechnic (Beijing) Co., Ltd.
*
*/
#include <linux/sched.h>
#include "bes2600.h"
#include "scan.h"
#include "sta.h"
#include "pm.h"
#include "epta_coex.h"
#include "epta_request.h"
#include "bes_pwr.h"
/*
* After this many consecutive WSM scan rejections from firmware, stop
* issuing new scans for BES2600_SCAN_BACKOFF_JIFFIES and let the state
* that's rejecting them (coex window, firmware-internal busy) clear.
*
* The backoff has to be at least as long as the natural mac80211 scan-
* retry cadence, otherwise the next attempt lands outside the window
* and bypasses the defer guard. Observed in the wild on PineTab2:
* roam-evaluation bursts at ~12 s cadence, idle background scans at
* ~5 min cadence. 30 s catches the burst and leaves the slow case
* alone (the firmware-policy state has had minutes to clear by then
* anyway).
*/
#define BES2600_SCAN_REJECT_THRESHOLD 3
#define BES2600_SCAN_BACKOFF_JIFFIES (30 * HZ)
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv);
/*
* Decide whether to skip sending the next WSM scan command without
* bothering the firmware. Two triggers:
*
* 1. BT A2DP is streaming in non-FDD coex mode. The firmware is
* known to reject scan requests during that window; short-
* circuiting here saves a WSM round-trip and avoids the
* wsm_generic_confirm / scan_work warning cascade that follows.
*
* 2. We already saw >= BES2600_SCAN_REJECT_THRESHOLD consecutive
* rejections on recent scan attempts and the backoff window has
* not yet elapsed. Whatever was rejecting them is likely still
* rejecting them; give it time. If the backoff has elapsed without
* a fresh reject refreshing it, the burst is over and we reset the
* count so an isolated reject doesn't immediately re-trip.
*
* Returns true if the caller should abandon the scan iteration.
*/
static bool bes2600_scan_should_defer(struct bes2600_common *hw_priv)
{
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (!coex_is_fdd_mode() && coex_is_bt_a2dp())
return true;
#endif
if (time_after(jiffies, hw_priv->scan.backoff_until))
hw_priv->scan.reject_count = 0;
if (hw_priv->scan.reject_count >= BES2600_SCAN_REJECT_THRESHOLD &&
time_before(jiffies, hw_priv->scan.backoff_until))
return true;
return false;
}
#ifdef CONFIG_BES2600_TESTMODE
static int bes2600_advance_scan_start(struct bes2600_common *hw_priv)
{
int tmo = 0;
tmo += hw_priv->advanceScanElems.duration;
bes2600_pwr_set_busy_event_with_timeout(hw_priv, BES_PWR_LOCK_ON_ADV_SCAN, tmo);
/* Invoke Advance Scan Duration Timeout Handler */
queue_delayed_work(hw_priv->workqueue,
&hw_priv->advance_scan_timeout, tmo * HZ / 1000);
return 0;
}
#endif
static void bes2600_remove_wps_p2p_ie(struct wsm_template_frame *frame)
{
u8 *ies;
u32 ies_len;
u32 ie_len;
u32 p2p_ie_len = 0;
u32 wps_ie_len = 0;
ies = &frame->skb->data[sizeof(struct ieee80211_hdr_3addr)];
ies_len = frame->skb->len - sizeof(struct ieee80211_hdr_3addr);
while (ies_len >= 6) {
ie_len = ies[1] + 2;
if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC)
&& (ies[2] == 0x00 && ies[3] == 0x50 && ies[4] == 0xf2 && ies[5] == 0x04)) {
wps_ie_len = ie_len;
memmove(ies, ies + ie_len, ies_len);
ies_len -= ie_len;
}
else if ((ies[0] == WLAN_EID_VENDOR_SPECIFIC) &&
(ies[2] == 0x50 && ies[3] == 0x6f && ies[4] == 0x9a && ies[5] == 0x09)) {
p2p_ie_len = ie_len;
memmove(ies, ies + ie_len, ies_len);
ies_len -= ie_len;
} else {
ies += ie_len;
ies_len -= ie_len;
}
}
if (p2p_ie_len || wps_ie_len) {
skb_trim(frame->skb, frame->skb->len - (p2p_ie_len + wps_ie_len));
}
}
#ifdef CONFIG_BES2600_TESTMODE
static int bes2600_disable_filtering(struct bes2600_vif *priv)
{
int ret = 0;
bool bssid_filtering = 0;
struct wsm_rx_filter rx_filter;
struct wsm_beacon_filter_control bf_control;
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
/* RX Filter Disable */
rx_filter.promiscuous = 0;
rx_filter.bssid = 0;
rx_filter.fcs = 0;
rx_filter.probeResponder = 0;
rx_filter.keepalive = 0;
ret = wsm_set_rx_filter(hw_priv, &rx_filter,
priv->if_id);
/* Beacon Filter Disable */
bf_control.enabled = __cpu_to_le32(0);
bf_control.bcn_count = __cpu_to_le32(1);
if (!ret)
ret = wsm_beacon_filter_control(hw_priv, &bf_control,
priv->if_id);
/* BSSID Filter Disable */
if (!ret)
ret = wsm_set_bssid_filtering(hw_priv, bssid_filtering,
priv->if_id);
return ret;
}
#endif
static int bes2600_scan_get_first_active_if(struct bes2600_common *hw_priv)
{
int i = 0;
struct bes2600_vif *vif;
bes2600_for_each_vif(hw_priv, vif, i) {
if (vif->join_status > BES2600_JOIN_STATUS_PASSIVE)
return i;
}
return -1;
}
static int bes2600_scan_start(struct bes2600_vif *priv, struct wsm_scan *scan)
{
int ret, i;
#ifdef FPGA_SETUP
int tmo = 5000;
#else
int tmo = 5000;
#endif
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
if (hw_priv->scan_switch_if_id == -1 &&
hw_priv->ht_info.channel_type > NL80211_CHAN_HT20 &&
priv->if_id >= 0) {
hw_priv->scan_switch_if_id = bes2600_scan_get_first_active_if(hw_priv);
if(hw_priv->scan_switch_if_id >= 0) {
struct wsm_switch_channel channel;
channel.channelMode = 0 << 4;
channel.channelSwitchCount = 0;
channel.newChannelNumber = hw_priv->channel->hw_value;
wsm_switch_channel(hw_priv, &channel, hw_priv->scan_switch_if_id);
bes_devel("scan start channel type %d num %d\n", hw_priv->ht_info.channel_type, channel.newChannelNumber);
}
}
for (i = 0; i < scan->numOfChannels; ++i)
tmo += scan->ch[i].maxChannelTime + 10;
atomic_set(&hw_priv->scan.in_progress, 1);
atomic_set(&hw_priv->recent_scan, 1);
queue_delayed_work(hw_priv->workqueue, &hw_priv->scan.timeout,
tmo * HZ / 1000);
#ifdef P2P_MULTIVIF
ret = wsm_scan(hw_priv, scan, 0);
#else
ret = wsm_scan(hw_priv, scan, priv->if_id);
#endif
if (unlikely(ret)) {
atomic_set(&hw_priv->scan.in_progress, 0);
cancel_delayed_work_sync(&hw_priv->scan.timeout);
bes2600_scan_restart_delayed(priv);
}
return ret;
}
#ifdef ROAM_OFFLOAD
static int bes2600_sched_scan_start(struct bes2600_vif *priv, struct wsm_scan *scan)
{
int ret;
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
ret = wsm_scan(hw_priv, scan, priv->if_id);
if (unlikely(ret)) {
atomic_set(&hw_priv->scan.in_progress, 0);
}
return ret;
}
#endif /*ROAM_OFFLOAD*/
int bes2600_hw_scan(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_scan_request *hw_req)
{
struct bes2600_common *hw_priv = hw->priv;
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct cfg80211_scan_request *req = &hw_req->req;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
int i;
/* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == BES2600_JOIN_STATUS_AP)
return -EOPNOTSUPP;
#if 0
if (work_pending(&priv->offchannel_work) ||
(hw_priv->roc_if_id != -1)) {
wiphy_dbg(hw_priv->hw->wiphy, "[SCAN] Offchannel work pending, "
"ignoring scan work %d\n", hw_priv->roc_if_id);
return -EBUSY;
}
#endif
if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
req->n_ssids = 0;
wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
req->n_ssids);
if (req->n_ssids > hw->wiphy->max_scan_ssids)
return -EINVAL;
bes2600_pwr_set_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
/* will be unlocked in bes2600_scan_work() */
down(&hw_priv->scan.lock);
down(&hw_priv->conf_lock);
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
if (!frame.skb) {
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return -ENOMEM;
}
if (req->ie_len)
skb_put_data(frame.skb, req->ie, req->ie_len);
if (frame.skb) {
int ret;
//if (priv->if_id == 0)
// bes2600_remove_wps_p2p_ie(&frame);
#ifdef P2P_MULTIVIF
ret = wsm_set_template_frame(hw_priv, &frame, 0);
#else
ret = wsm_set_template_frame(hw_priv, &frame,
priv->if_id);
#endif
#if 0
if (!ret) {
/* Host want to be the probe responder. */
ret = wsm_set_probe_responder(priv, true);
}
#endif
if (ret) {
dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return ret;
}
}
wsm_vif_lock_tx(priv);
BUG_ON(hw_priv->scan.req);
hw_priv->scan.req = req;
hw_priv->scan.n_ssids = 0;
hw_priv->scan.status = 0;
hw_priv->scan.begin = &req->channels[0];
hw_priv->scan.curr = hw_priv->scan.begin;
hw_priv->scan.end = &req->channels[req->n_channels];
hw_priv->scan.output_power = hw_priv->output_power;
hw_priv->scan.if_id = priv->if_id;
/* TODO:COMBO: Populate BIT4 in scanflags to decide on which MAC
* address the SCAN request will be sent */
bes_devel("%s %d if_id:%d,num_channel:%d.\n", __func__, __LINE__, priv->if_id, req->n_channels);
for (i = 0; i < req->n_ssids; ++i) {
struct wsm_ssid *dst =
&hw_priv->scan.ssids[hw_priv->scan.n_ssids];
BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid));
memcpy(&dst->ssid[0], req->ssids[i].ssid,
sizeof(dst->ssid));
dst->length = req->ssids[i].ssid_len;
++hw_priv->scan.n_ssids;
}
if (frame.skb)
dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING);
#endif
queue_work(hw_priv->workqueue, &hw_priv->scan.work);
return 0;
}
#ifdef ROAM_OFFLOAD
int bes2600_hw_sched_scan_start(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_sched_scan_ies *ies)
{
struct bes2600_common *hw_priv = hw->priv;
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
int i;
wiphy_warn(hw->wiphy, "[SCAN] Scheduled scan request-->.\n");
if (!priv->vif)
return -EINVAL;
/* Scan when P2P_GO corrupt firmware MiniAP mode */
if (priv->join_status == BES2600_JOIN_STATUS_AP)
return -EOPNOTSUPP;
wiphy_warn(hw->wiphy, "[SCAN] Scheduled scan: n_ssids %d, ssid[0].len = %d\n", req->n_ssids, req->ssids[0].ssid_len);
if (req->n_ssids == 1 && !req->ssids[0].ssid_len)
req->n_ssids = 0;
wiphy_dbg(hw->wiphy, "[SCAN] Scan request for %d SSIDs.\n",
req->n_ssids);
if (req->n_ssids > hw->wiphy->max_scan_ssids)
return -EINVAL;
/* will be unlocked in bes2600_scan_work() */
down(&hw_priv->scan.lock);
down(&hw_priv->conf_lock);
frame.skb = ieee80211_probereq_get(hw, priv->vif->addr, NULL, 0,
req->ie_len);
if (!frame.skb) {
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return -ENOMEM;
}
if (frame.skb) {
int ret;
if (priv->if_id == 0)
bes2600_remove_wps_p2p_ie(&frame);
ret = wsm_set_template_frame(hw_priv, &frame, priv->if_id);
if (0 == ret) {
/* Host want to be the probe responder. */
ret = wsm_set_probe_responder(priv, true);
}
if (ret) {
dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
up(&hw_priv->scan.lock);
return ret;
}
}
wsm_lock_tx(hw_priv);
BUG_ON(hw_priv->scan.req);
hw_priv->scan.sched_req = req;
hw_priv->scan.n_ssids = 0;
hw_priv->scan.status = 0;
hw_priv->scan.begin = &req->channels[0];
hw_priv->scan.curr = hw_priv->scan.begin;
hw_priv->scan.end = &req->channels[req->n_channels];
hw_priv->scan.output_power = hw_priv->output_power;
for (i = 0; i < req->n_ssids; ++i) {
struct wsm_ssid *dst =
&hw_priv->scan.ssids[hw_priv->scan.n_ssids];
BUG_ON(req->ssids[i].ssid_len > sizeof(dst->ssid));
memcpy(&dst->ssid[0], req->ssids[i].ssid,
sizeof(dst->ssid));
dst->length = req->ssids[i].ssid_len;
++hw_priv->scan.n_ssids;
{
u8 j;
wiphy_warn(hw->wiphy, "[SCAN] SSID %d\n",i);
for(j=0; j<req->ssids[i].ssid_len; j++)
wiphy_warn(priv->hw->wiphy, "[SCAN] 0x%x\n", req->ssids[i].ssid[j]);
}
}
if (frame.skb)
dev_kfree_skb(frame.skb);
up(&hw_priv->conf_lock);
queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
wiphy_warn(hw->wiphy, "<--[SCAN] Scheduled scan request.\n");
return 0;
}
#endif /*ROAM_OFFLOAD*/
void bes2600_scan_work(struct work_struct *work)
{
struct bes2600_common *hw_priv = container_of(work,
struct bes2600_common,
scan.work);
struct bes2600_vif *priv, *vif;
struct ieee80211_channel **it;
struct wsm_scan scan = {
.scanType = WSM_SCAN_TYPE_FOREGROUND,
.scanFlags = 0, /* TODO:COMBO */
//.scanFlags = WSM_SCAN_FLAG_SPLIT_METHOD, /* TODO:COMBO */
};
bool first_run;
int i;
const u32 ProbeRequestTime = 2;
const u32 ChannelRemainTime = 15;
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
u32 minChannelTime;
#endif
u32 maxChannelTime;
#ifdef CONFIG_BES2600_TESTMODE
int ret = 0;
u16 advance_scan_req_channel = hw_priv->scan.begin[0]->hw_value;
#endif
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, hw_priv->scan.if_id);
/*TODO: COMBO: introduce locking so vif is not removed in meanwhile */
if (!priv) {
wiphy_warn(hw_priv->hw->wiphy, "[SCAN] interface removed, "
"ignoring scan work\n");
return;
}
if (priv->if_id)
scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1;
else
scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1;
bes2600_for_each_vif(hw_priv, vif, i) {
#ifdef P2P_MULTIVIF
if ((i == (CW12XX_MAX_VIFS - 1)) || !vif)
#else
if (!vif)
#endif
continue;
if (vif->bss_loss_status > BES2600_BSS_LOSS_NONE)
scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
first_run = hw_priv->scan.begin == hw_priv->scan.curr &&
hw_priv->scan.begin != hw_priv->scan.end;
if (first_run) {
/* Firmware gets crazy if scan request is sent
* when STA is joined but not yet associated.
* Force unjoin in this case. */
if (cancel_delayed_work_sync(&priv->join_timeout) > 0) {
bes2600_join_timeout(&priv->join_timeout.work);
}
}
down(&hw_priv->conf_lock);
if (first_run) {
#ifdef CONFIG_BES2600_TESTMODE
/* Passive Scan - Serving Channel Request Handling */
if (hw_priv->enable_advance_scan &&
(hw_priv->advanceScanElems.scanMode ==
BES2600_SCAN_MEASUREMENT_PASSIVE) &&
(priv->join_status == BES2600_JOIN_STATUS_STA) &&
(hw_priv->channel->hw_value ==
advance_scan_req_channel)) {
/* If Advance Scan Request is for Serving Channel Device
* should be Active and Filtering Should be Disable */
if (priv->powersave_mode.pmMode & WSM_PSM_PS) {
struct wsm_set_pm pm = priv->powersave_mode;
pm.pmMode = WSM_PSM_ACTIVE;
wsm_set_pm(hw_priv, &pm, priv->if_id);
}
/* Disable Rx Beacon and Bssid filter */
ret = bes2600_disable_filtering(priv);
if (ret)
wiphy_err(hw_priv->hw->wiphy,
"%s: Disable BSSID or Beacon filtering failed: %d.\n",
__func__, ret);
} else if (hw_priv->enable_advance_scan &&
(hw_priv->advanceScanElems.scanMode ==
BES2600_SCAN_MEASUREMENT_PASSIVE) &&
(priv->join_status == BES2600_JOIN_STATUS_STA)) {
if (priv->join_status == BES2600_JOIN_STATUS_STA &&
!(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
struct wsm_set_pm pm = priv->powersave_mode;
pm.pmMode = WSM_PSM_PS;
bes2600_set_pm(priv, &pm);
}
} else {
#endif
#if 0
if (priv->join_status == BES2600_JOIN_STATUS_STA &&
!(priv->powersave_mode.pmMode & WSM_PSM_PS)) {
struct wsm_set_pm pm = priv->powersave_mode;
pm.pmMode = WSM_PSM_PS;
bes2600_set_pm(priv, &pm);
} else
#endif
if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) {
/* FW bug: driver has to restart p2p-dev mode
* after scan */
bes2600_disable_listening(priv);
}
#ifdef CONFIG_BES2600_TESTMODE
}
#endif
}
if (!hw_priv->scan.req || (hw_priv->scan.curr == hw_priv->scan.end)) {
struct cfg80211_scan_info info = {
.aborted = hw_priv->scan.status ? 1 : 0,
};
#ifdef CONFIG_BES2600_TESTMODE
if (hw_priv->enable_advance_scan &&
(hw_priv->advanceScanElems.scanMode ==
BES2600_SCAN_MEASUREMENT_PASSIVE) &&
(priv->join_status == BES2600_JOIN_STATUS_STA) &&
(hw_priv->channel->hw_value ==
advance_scan_req_channel)) {
/* WSM Lock should be held here for WSM APIs */
wsm_vif_lock_tx(priv);
/* wsm_lock_tx(priv); */
/* Once Duration is Over, enable filtering
* and Revert Back Power Save */
if (priv->powersave_mode.pmMode & WSM_PSM_PS)
wsm_set_pm(hw_priv, &priv->powersave_mode,
priv->if_id);
bes2600_update_filtering(priv);
} else {
if (!hw_priv->enable_advance_scan) {
#endif
if (hw_priv->scan.output_power != hw_priv->output_power)
/* TODO:COMBO: Change when mac80211 implementation
* is available for output power also */
#ifdef P2P_MULTIVIF
WARN_ON(wsm_set_output_power(hw_priv,
hw_priv->output_power * 10,
priv->if_id ? 0 : 0));
#else
WARN_ON(wsm_set_output_power(hw_priv,
hw_priv->output_power * 10,
priv->if_id));
#endif
#ifdef CONFIG_BES2600_TESTMODE
}
}
#endif
#if 0
if (priv->join_status == BES2600_JOIN_STATUS_STA &&
!(priv->powersave_mode.pmMode & WSM_PSM_PS))
bes2600_set_pm(priv, &priv->powersave_mode);
#endif
if (hw_priv->scan.status < 0)
wiphy_info(priv->hw->wiphy,
"[SCAN] Scan failed (%d).\n",
hw_priv->scan.status);
else if (hw_priv->scan.req)
wiphy_dbg(priv->hw->wiphy,
"[SCAN] Scan completed.\n");
else
wiphy_dbg(priv->hw->wiphy,
"[SCAN] Scan canceled.\n");
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (priv->join_status == BES2600_JOIN_STATUS_STA) {
if (hw_priv->channel->band != NL80211_BAND_2GHZ)
bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP_5G);
else
bwifi_change_current_status(hw_priv, BWIFI_STATUS_GOT_IP);
} else {
bwifi_change_current_status(hw_priv, BWIFI_STATUS_IDLE);
}
#endif
bes_devel("%s %d %d.", __func__, __LINE__, hw_priv->ht_info.channel_type);
/* switch to previous channel and bw mode after scan done */
if (hw_priv->scan_switch_if_id >= 0) {
struct wsm_switch_channel channel;
channel.channelMode = hw_priv->ht_info.channel_type << 4;
channel.channelSwitchCount = 0;
channel.newChannelNumber = hw_priv->channel->hw_value;
wsm_switch_channel(hw_priv, &channel, hw_priv->scan_switch_if_id);
hw_priv->scan_switch_if_id = -1;
bes_devel("scan done channel type %d num %d\n", hw_priv->ht_info.channel_type, channel.newChannelNumber);
}
hw_priv->scan.req = NULL;
bes2600_scan_restart_delayed(priv);
#ifdef CONFIG_BES2600_TESTMODE
hw_priv->enable_advance_scan = false;
#endif /* CONFIG_BES2600_TESTMODE */
wsm_unlock_tx(hw_priv);
up(&hw_priv->conf_lock);
bes2600_pwr_clear_busy_event(hw_priv, BES_PWR_LOCK_ON_SCAN);
ieee80211_scan_completed(hw_priv->hw, &info);
up(&hw_priv->scan.lock);
return;
} else {
struct ieee80211_channel *first = *hw_priv->scan.curr;
for (it = hw_priv->scan.curr + 1, i = 1;
it != hw_priv->scan.end &&
i < WSM_SCAN_MAX_NUM_OF_CHANNELS;
++it, ++i) {
if ((*it)->band != first->band)
break;
// Doen't split scan req in case of EPTA error after scan req
// if (((*it)->flags ^ first->flags) &
// IEEE80211_CHAN_NO_IR)
// break;
// if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
// (*it)->max_power != first->max_power)
// break;
}
scan.band = first->band;
if (hw_priv->scan.req->no_cck)
scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
else
scan.maxTransmitRate = WSM_TRANSMIT_RATE_1;
#ifdef CONFIG_BES2600_TESTMODE
if (hw_priv->enable_advance_scan) {
if (hw_priv->advanceScanElems.scanMode ==
BES2600_SCAN_MEASUREMENT_PASSIVE)
scan.numOfProbeRequests = 0;
else
scan.numOfProbeRequests = 2;
} else {
#endif
/* TODO: Is it optimal? */
scan.numOfProbeRequests =
(first->flags & IEEE80211_CHAN_NO_IR) ? 0 : 2;
#ifdef CONFIG_BES2600_TESTMODE
}
#endif /* CONFIG_BES2600_TESTMODE */
scan.numOfSSIDs = hw_priv->scan.n_ssids;
scan.ssids = &hw_priv->scan.ssids[0];
scan.numOfChannels = it - hw_priv->scan.curr;
/* TODO: Is it optimal? */
scan.probeDelay = 100;
/* It is not stated in WSM specification, however
* FW team says that driver may not use FG scan
* when joined. */
if (priv->join_status == BES2600_JOIN_STATUS_STA) {
scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
scan.ch = kcalloc((it - hw_priv->scan.curr),
sizeof(struct wsm_scan_ch),
GFP_KERNEL);
if (!scan.ch) {
hw_priv->scan.status = -ENOMEM;
goto fail;
}
maxChannelTime = (scan.numOfSSIDs * scan.numOfProbeRequests *
ProbeRequestTime) + ChannelRemainTime;
maxChannelTime = (maxChannelTime < 35) ? 35 : maxChannelTime;
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
if (scan.band == NL80211_BAND_2GHZ) {
coex_calc_wifi_scan_time(&minChannelTime, &maxChannelTime);
} else {
minChannelTime = 100;
maxChannelTime = 100;
}
#endif
for (i = 0; i < scan.numOfChannels; ++i) {
scan.ch[i].number = hw_priv->scan.curr[i]->hw_value;
#ifdef CONFIG_BES2600_TESTMODE
if (hw_priv->enable_advance_scan) {
scan.ch[i].minChannelTime =
hw_priv->advanceScanElems.duration;
scan.ch[i].maxChannelTime =
hw_priv->advanceScanElems.duration;
} else {
#endif
#ifndef WIFI_BT_COEXIST_EPTA_ENABLE
if (hw_priv->scan.curr[i]->flags & IEEE80211_CHAN_NO_IR) {
scan.ch[i].minChannelTime = 40;
scan.ch[i].maxChannelTime = 100;
}
else {
//TODO: modify maxChannelTime
scan.ch[i].minChannelTime = 15;
scan.ch[i].maxChannelTime = maxChannelTime;
}
#else
scan.ch[i].minChannelTime = minChannelTime;
scan.ch[i].maxChannelTime = maxChannelTime;
#endif
#ifdef CONFIG_BES2600_TESTMODE
}
#endif
}
#ifdef CONFIG_BES2600_TESTMODE
if (!hw_priv->enable_advance_scan) {
#endif
if (!(first->flags & IEEE80211_CHAN_NO_IR) &&
hw_priv->scan.output_power != first->max_power) {
hw_priv->scan.output_power = first->max_power;
/* TODO:COMBO: Change after mac80211 implementation
* complete */
#ifdef P2P_MULTIVIF
WARN_ON(wsm_set_output_power(hw_priv,
hw_priv->scan.output_power * 10,
priv->if_id ? 0 : 0));
#else
WARN_ON(wsm_set_output_power(hw_priv,
hw_priv->scan.output_power * 10,
priv->if_id));
#endif
}
#ifdef CONFIG_BES2600_TESTMODE
}
#endif
#ifdef CONFIG_BES2600_TESTMODE
if (hw_priv->enable_advance_scan &&
(hw_priv->advanceScanElems.scanMode ==
BES2600_SCAN_MEASUREMENT_PASSIVE) &&
(priv->join_status == BES2600_JOIN_STATUS_STA) &&
(hw_priv->channel->hw_value == advance_scan_req_channel)) {
/* Start Advance Scan Timer */
hw_priv->scan.status = bes2600_advance_scan_start(hw_priv);
wsm_unlock_tx(hw_priv);
} else
#endif
{
if (bes2600_scan_should_defer(hw_priv)) {
hw_priv->scan.status = -EBUSY;
hw_priv->scan.reject_count++;
hw_priv->scan.backoff_until =
jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
wiphy_dbg(priv->hw->wiphy,
"[SCAN] deferred (coex/backoff, reject_count=%u)\n",
hw_priv->scan.reject_count);
kfree(scan.ch);
goto fail;
}
hw_priv->scan.status = bes2600_scan_start(priv, &scan);
}
kfree(scan.ch);
if (hw_priv->scan.status) {
hw_priv->scan.reject_count++;
hw_priv->scan.backoff_until =
jiffies + BES2600_SCAN_BACKOFF_JIFFIES;
/* Lower callers already logged the reason at wiphy_warn. */
goto fail;
}
hw_priv->scan.reject_count = 0;
hw_priv->scan.curr = it;
}
up(&hw_priv->conf_lock);
return;
fail:
hw_priv->scan.curr = hw_priv->scan.end;
up(&hw_priv->conf_lock);
queue_work(hw_priv->workqueue, &hw_priv->scan.work);
return;
}
#ifdef ROAM_OFFLOAD
void bes2600_sched_scan_work(struct work_struct *work)
{
struct bes2600_common *hw_priv = container_of(work, struct bes2600_common,
scan.swork);
struct wsm_scan scan;
struct wsm_ssid scan_ssid;
int i;
struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
hw_priv->scan.if_id);
if (unlikely(!priv)) {
WARN_ON(1);
return;
}
spin_unlock(&priv->vif_lock);
/* Firmware gets crazy if scan request is sent
* when STA is joined but not yet associated.
* Force unjoin in this case. */
if (cancel_delayed_work_sync(&priv->join_timeout) > 0) {
bes2600_join_timeout(&priv->join_timeout.work);
}
down(&hw_priv->conf_lock);
hw_priv->auto_scanning = 1;
scan.band = 0;
if (priv->join_status == BES2600_JOIN_STATUS_STA)
scan.scanType = 3; /* auto background */
else
scan.scanType = 2; /* auto foreground */
scan.scanFlags = 0x01; /* bit 0 set => forced background scan */
scan.maxTransmitRate = WSM_TRANSMIT_RATE_6;
scan.autoScanInterval = (0xba << 24)|(30 * 1024); /* 30 seconds, -70 rssi */
scan.numOfProbeRequests = 2;
//scan.numOfChannels = 11;
scan.numOfChannels = hw_priv->num_scanchannels;
scan.numOfSSIDs = 1;
scan.probeDelay = 100;
scan_ssid.length = priv->ssid_length;
memcpy(scan_ssid.ssid, priv->ssid, priv->ssid_length);
scan.ssids = &scan_ssid;
scan.ch = kzalloc(
sizeof(struct wsm_scan_ch[scan.numOfChannels]),
GFP_KERNEL);
if (!scan.ch) {
hw_priv->scan.status = -ENOMEM;
goto fail;
}
for (i = 0; i < scan.numOfChannels; i++) {
scan.ch[i].number = hw_priv->scan_channels[i].number;
scan.ch[i].minChannelTime = hw_priv->scan_channels[i].minChannelTime;
scan.ch[i].maxChannelTime = hw_priv->scan_channels[i].maxChannelTime;
scan.ch[i].txPowerLevel = hw_priv->scan_channels[i].txPowerLevel;
}
#if 0
for (i = 1; i <= scan.numOfChannels; i++) {
scan.ch[i-1].number = i;
scan.ch[i-1].minChannelTime = 10;
scan.ch[i-1].maxChannelTime = 40;
}
#endif
hw_priv->scan.status = bes2600_sched_scan_start(priv, &scan);
kfree(scan.ch);
if (hw_priv->scan.status)
goto fail;
up(&hw_priv->conf_lock);
return;
fail:
up(&hw_priv->conf_lock);
queue_work(hw_priv->workqueue, &hw_priv->scan.swork);
return;
}
void bes2600_hw_sched_scan_stop(struct bes2600_common *hw_priv)
{
struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
hw_priv->scan.if_id);
if (unlikely(!priv))
return;
spin_unlock(&priv->vif_lock);
wsm_stop_scan(hw_priv, priv->if_id);
return;
}
#endif /*ROAM_OFFLOAD*/
static void bes2600_scan_restart_delayed(struct bes2600_vif *priv)
{
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
if (priv->delayed_link_loss) {
int tmo = priv->cqm_beacon_loss_count;
if (hw_priv->scan.direct_probe)
tmo = 0;
priv->delayed_link_loss = 0;
/* Restart beacon loss timer and requeue
BSS loss work. */
wiphy_dbg(priv->hw->wiphy,
"[CQM] Requeue BSS loss in %d "
"beacons.\n", tmo);
spin_lock(&priv->bss_loss_lock);
priv->bss_loss_status = BES2600_BSS_LOSS_NONE;
spin_unlock(&priv->bss_loss_lock);
cancel_delayed_work_sync(&priv->bss_loss_work);
queue_delayed_work(hw_priv->workqueue,
&priv->bss_loss_work,
tmo * HZ / 10);
}
/* FW bug: driver has to restart p2p-dev mode after scan. */
if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) {
/*bes2600_enable_listening(priv);*/
// WARN_ON(1);
bes_devel("scan complete join_status is monitor");
bes2600_update_filtering(priv);
}
if (priv->delayed_unjoin) {
priv->delayed_unjoin = false;
if (queue_work(hw_priv->workqueue, &priv->unjoin_work) <= 0)
wsm_unlock_tx(hw_priv);
}
}
static void bes2600_scan_complete(struct bes2600_common *hw_priv, int if_id)
{
struct bes2600_vif *priv;
atomic_xchg(&hw_priv->recent_scan, 0);
if (hw_priv->scan.direct_probe) {
down(&hw_priv->conf_lock);
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, if_id);
if (priv) {
wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe "
"complete.\n");
bes2600_scan_restart_delayed(priv);
} else {
wiphy_dbg(priv->hw->wiphy, "[SCAN] Direct probe "
"complete without interface!\n");
}
up(&hw_priv->conf_lock);
hw_priv->scan.direct_probe = 0;
up(&hw_priv->scan.lock);
wsm_unlock_tx(hw_priv);
} else {
bes2600_scan_work(&hw_priv->scan.work);
}
}
void bes2600_scan_complete_cb(struct bes2600_common *hw_priv,
struct wsm_scan_complete *arg)
{
struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
hw_priv->scan.if_id);
if (unlikely(!priv))
return;
#ifdef ROAM_OFFLOAD
if (hw_priv->auto_scanning)
queue_delayed_work(hw_priv->workqueue,
&hw_priv->scan.timeout, 0);
#endif /*ROAM_OFFLOAD*/
if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED)) {
/* STA is stopped. */
spin_unlock(&priv->vif_lock);
return;
}
spin_unlock(&priv->vif_lock);
#ifdef WIFI_BT_COEXIST_EPTA_ENABLE
// recover EPTA timer after scan wsm msg complete, in case of epta state error
// bwifi_change_current_status(hw_priv, BWIFI_STATUS_SCANNING_COMP);
#endif
wiphy_dbg(hw_priv->hw->wiphy, "bes2600_scan_complete_cb status: %u", arg->status);
if(hw_priv->scan.status == -ETIMEDOUT)
wiphy_warn(hw_priv->hw->wiphy,
"Scan timeout already occured. Don't cancel work");
if ((hw_priv->scan.status != -ETIMEDOUT) &&
(cancel_delayed_work_sync(&hw_priv->scan.timeout) > 0)) {
hw_priv->scan.status = 1;
queue_delayed_work(hw_priv->workqueue,
&hw_priv->scan.timeout, 0);
}
}
void bes2600_scan_timeout(struct work_struct *work)
{
struct bes2600_common *hw_priv =
container_of(work, struct bes2600_common, scan.timeout.work);
if (likely(atomic_xchg(&hw_priv->scan.in_progress, 0))) {
if (hw_priv->scan.status > 0)
hw_priv->scan.status = 0;
else if (!hw_priv->scan.status) {
wiphy_warn(hw_priv->hw->wiphy,
"Timeout waiting for scan "
"complete notification.\n");
hw_priv->scan.status = -ETIMEDOUT;
hw_priv->scan.curr = hw_priv->scan.end;
WARN_ON(wsm_stop_scan(hw_priv,
hw_priv->scan.if_id ? 1 : 0));
}
bes2600_scan_complete(hw_priv, hw_priv->scan.if_id);
#ifdef ROAM_OFFLOAD
} else if (hw_priv->auto_scanning) {
hw_priv->auto_scanning = 0;
ieee80211_sched_scan_results(hw_priv->hw);
#endif /*ROAM_OFFLOAD*/
}
}
#ifdef CONFIG_BES2600_TESTMODE
void bes2600_advance_scan_timeout(struct work_struct *work)
{
struct bes2600_common *hw_priv =
container_of(work, struct bes2600_common, advance_scan_timeout.work);
struct bes2600_vif *priv = cw12xx_hwpriv_to_vifpriv(hw_priv,
hw_priv->scan.if_id);
if (WARN_ON(!priv))
return;
spin_unlock(&priv->vif_lock);
hw_priv->scan.status = 0;
if (hw_priv->advanceScanElems.scanMode ==
BES2600_SCAN_MEASUREMENT_PASSIVE) {
/* Passive Scan on Serving Channel
* Timer Expire */
bes2600_scan_complete(hw_priv, hw_priv->scan.if_id);
} else {
struct cfg80211_scan_info info = {
.aborted = hw_priv->scan.status ? 1 : 0,
};
/* Active Scan on Serving Channel
* Timer Expire */
down(&hw_priv->conf_lock);
//wsm_lock_tx(priv);
wsm_vif_lock_tx(priv);
/* Once Duration is Over, enable filtering
* and Revert Back Power Save */
if ((priv->powersave_mode.pmMode & WSM_PSM_PS))
wsm_set_pm(hw_priv, &priv->powersave_mode,
priv->if_id);
hw_priv->scan.req = NULL;
bes2600_update_filtering(priv);
hw_priv->enable_advance_scan = false;
wsm_unlock_tx(hw_priv);
up(&hw_priv->conf_lock);
ieee80211_scan_completed(hw_priv->hw, &info);
up(&hw_priv->scan.lock);
}
}
#endif
void bes2600_cancel_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
{
struct bes2600_vif *priv = cw12xx_get_vif_from_ieee80211(vif);
struct bes2600_common *hw_priv = cw12xx_vifpriv_to_hwpriv(priv);
if(hw_priv->scan.if_id == priv->if_id) {
bes_devel("cancel hw_scan on intf:%d\n", priv->if_id);
down(&hw_priv->conf_lock);
hw_priv->scan.req = NULL;
up(&hw_priv->conf_lock);
/* cancel scan operation */
wsm_stop_scan(hw_priv, priv->if_id);
/* wait scan operation end */
down(&hw_priv->scan.lock);
up(&hw_priv->scan.lock);
}
}
void bes2600_probe_work(struct work_struct *work)
{
struct bes2600_common *hw_priv =
container_of(work, struct bes2600_common, scan.probe_work.work);
struct bes2600_vif *priv, *vif;
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;
struct wsm_tx *wsm;
struct wsm_template_frame frame = {
.frame_type = WSM_FRAME_TYPE_PROBE_REQUEST,
};
struct wsm_ssid ssids[1] = {{
.length = 0,
} };
struct wsm_scan_ch ch[1] = {{
.minChannelTime = 0,
.maxChannelTime = 10,
} };
struct wsm_scan scan = {
.scanType = WSM_SCAN_TYPE_FOREGROUND,
.numOfProbeRequests = 2,
.probeDelay = 0,
.numOfChannels = 1,
.ssids = ssids,
.ch = ch,
};
u8 *ies;
size_t ies_len;
int ret = 1;
int i;
wiphy_info(hw_priv->hw->wiphy, "[SCAN] Direct probe work.\n");
BUG_ON(queueId >= 4);
BUG_ON(!hw_priv->channel);
down(&hw_priv->conf_lock);
if (unlikely(down_trylock(&hw_priv->scan.lock))) {
/* Scan is already in progress. Requeue self. */
schedule();
queue_delayed_work(hw_priv->workqueue,
&hw_priv->scan.probe_work, HZ / 10);
up(&hw_priv->conf_lock);
return;
}
if (bes2600_queue_get_skb(queue, hw_priv->pending_frame_id,
&frame.skb, &txpriv)) {
up(&hw_priv->scan.lock);
up(&hw_priv->conf_lock);
wsm_unlock_tx(hw_priv);
return;
}
priv = __cw12xx_hwpriv_to_vifpriv(hw_priv, txpriv->if_id);
if (!priv) {
up(&hw_priv->scan.lock);
up(&hw_priv->conf_lock);
return;
}
wsm = (struct wsm_tx *)frame.skb->data;
scan.maxTransmitRate = wsm->maxTxRate;
scan.band = (hw_priv->channel->band == NL80211_BAND_5GHZ) ?
WSM_PHY_BAND_5G : WSM_PHY_BAND_2_4G;
if (priv->join_status == BES2600_JOIN_STATUS_STA) {
scan.scanType = WSM_SCAN_TYPE_BACKGROUND;
scan.scanFlags = WSM_SCAN_FLAG_FORCE_BACKGROUND;
if (priv->if_id)
scan.scanFlags |= WSM_FLAG_MAC_INSTANCE_1;
else
scan.scanFlags &= ~WSM_FLAG_MAC_INSTANCE_1;
}
bes2600_for_each_vif(hw_priv, vif, i) {
if (!vif)
continue;
if (vif->bss_loss_status > BES2600_BSS_LOSS_NONE)
scan.scanFlags |= WSM_SCAN_FLAG_FORCE_BACKGROUND;
}
ch[0].number = hw_priv->channel->hw_value;
skb_pull(frame.skb, txpriv->offset);
ies = &frame.skb->data[sizeof(struct ieee80211_hdr_3addr)];
ies_len = frame.skb->len - sizeof(struct ieee80211_hdr_3addr);
if (ies_len) {
u8 *ssidie =
(u8 *)cfg80211_find_ie(WLAN_EID_SSID, ies, ies_len);
if (ssidie && ssidie[1] && ssidie[1] <= sizeof(ssids[0].ssid)) {
u8 *nextie = &ssidie[2 + ssidie[1]];
/* Remove SSID from the IE list. It has to be provided
* as a separate argument in bes2600_scan_start call */
/* Store SSID localy */
ssids[0].length = ssidie[1];
memcpy(ssids[0].ssid, &ssidie[2], ssids[0].length);
scan.numOfSSIDs = 1;
/* Remove SSID from IE list */
ssidie[1] = 0;
memmove(&ssidie[2], nextie, &ies[ies_len] - nextie);
skb_trim(frame.skb, frame.skb->len - ssids[0].length);
}
}
if (priv->if_id == 0)
bes2600_remove_wps_p2p_ie(&frame);
/* FW bug: driver has to restart p2p-dev mode after scan */
if (priv->join_status == BES2600_JOIN_STATUS_MONITOR) {
WARN_ON(1);
/*bes2600_disable_listening(priv);*/
}
ret = WARN_ON(wsm_set_template_frame(hw_priv, &frame,
priv->if_id));
hw_priv->scan.direct_probe = 1;
hw_priv->scan.if_id = priv->if_id;
if (!ret) {
wsm_flush_tx(hw_priv);
ret = WARN_ON(bes2600_scan_start(priv, &scan));
}
up(&hw_priv->conf_lock);
skb_push(frame.skb, txpriv->offset);
if (!ret)
IEEE80211_SKB_CB(frame.skb)->flags |= IEEE80211_TX_STAT_ACK;
#ifdef CONFIG_BES2600_TESTMODE
BUG_ON(bes2600_queue_remove(hw_priv, queue,
hw_priv->pending_frame_id));
#else
BUG_ON(bes2600_queue_remove(queue, hw_priv->pending_frame_id));
#endif
if (ret) {
hw_priv->scan.direct_probe = 0;
up(&hw_priv->scan.lock);
wsm_unlock_tx(hw_priv);
}
return;
}