brcmfmac: add e-scan support.
This patch adds e-scan support (currently i-scan is in use). E-scan is a more powerful and memory efficient method for scanning. E-scan will be the default scan method and eventually, i-scan support will be removed. The scan methods do not make any difference to the end-user. Reviewed-by: Arend Van Spriel <arend@broadcom.com> Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com> Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com> Signed-off-by: Hante Meuleman <meuleman@broadcom.com> Signed-off-by: Arend van Spriel <arend@broadcom.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Родитель
d74a0b514d
Коммит
e756af5b30
|
@ -55,6 +55,14 @@ config BRCMFMAC_USB
|
||||||
IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to
|
IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to
|
||||||
use the driver for an USB wireless card.
|
use the driver for an USB wireless card.
|
||||||
|
|
||||||
|
config BRCMISCAN
|
||||||
|
bool "Broadcom I-Scan (OBSOLETE)"
|
||||||
|
depends on BRCMFMAC
|
||||||
|
---help---
|
||||||
|
This option enables the I-Scan method. By default fullmac uses the
|
||||||
|
new E-Scan method which uses less memory in firmware and gives no
|
||||||
|
limitation on the number of scan results.
|
||||||
|
|
||||||
config BRCMDBG
|
config BRCMDBG
|
||||||
bool "Broadcom driver debug functions"
|
bool "Broadcom driver debug functions"
|
||||||
depends on BRCMSMAC || BRCMFMAC
|
depends on BRCMSMAC || BRCMFMAC
|
||||||
|
|
|
@ -130,6 +130,10 @@
|
||||||
#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
|
#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02
|
||||||
#define BRCMF_EVENT_MSG_GROUP 0x04
|
#define BRCMF_EVENT_MSG_GROUP 0x04
|
||||||
|
|
||||||
|
#define BRCMF_ESCAN_REQ_VERSION 1
|
||||||
|
|
||||||
|
#define WLC_BSS_RSSI_ON_CHANNEL 0x0002
|
||||||
|
|
||||||
struct brcmf_event_msg {
|
struct brcmf_event_msg {
|
||||||
__be16 version;
|
__be16 version;
|
||||||
__be16 flags;
|
__be16 flags;
|
||||||
|
@ -456,6 +460,24 @@ struct brcmf_scan_results_le {
|
||||||
__le32 count;
|
__le32 count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct brcmf_escan_params_le {
|
||||||
|
__le32 version;
|
||||||
|
__le16 action;
|
||||||
|
__le16 sync_id;
|
||||||
|
struct brcmf_scan_params_le params_le;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct brcmf_escan_result_le {
|
||||||
|
__le32 buflen;
|
||||||
|
__le32 version;
|
||||||
|
__le16 sync_id;
|
||||||
|
__le16 bss_count;
|
||||||
|
struct brcmf_bss_info_le bss_info_le;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WL_ESCAN_RESULTS_FIXED_SIZE (sizeof(struct brcmf_escan_result_le) - \
|
||||||
|
sizeof(struct brcmf_bss_info_le))
|
||||||
|
|
||||||
/* used for association with a specific BSSID and chanspec list */
|
/* used for association with a specific BSSID and chanspec list */
|
||||||
struct brcmf_assoc_params_le {
|
struct brcmf_assoc_params_le {
|
||||||
/* 00:00:00:00:00:00: broadcast scan */
|
/* 00:00:00:00:00:00: broadcast scan */
|
||||||
|
|
|
@ -205,7 +205,8 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data)
|
||||||
BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, {
|
BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, {
|
||||||
BRCMF_E_IF, "IF"}, {
|
BRCMF_E_IF, "IF"}, {
|
||||||
BRCMF_E_RSSI, "RSSI"}, {
|
BRCMF_E_RSSI, "RSSI"}, {
|
||||||
BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
|
BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}, {
|
||||||
|
BRCMF_E_ESCAN_RESULT, "ESCAN_RESULT"}
|
||||||
};
|
};
|
||||||
uint event_type, flags, auth_type, datalen;
|
uint event_type, flags, auth_type, datalen;
|
||||||
static u32 seqnum_prev;
|
static u32 seqnum_prev;
|
||||||
|
@ -350,6 +351,11 @@ brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data)
|
||||||
brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name);
|
brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BRCMF_E_ESCAN_RESULT:
|
||||||
|
brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name);
|
||||||
|
datalen = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
case BRCMF_E_PFN_NET_FOUND:
|
case BRCMF_E_PFN_NET_FOUND:
|
||||||
case BRCMF_E_PFN_NET_LOST:
|
case BRCMF_E_PFN_NET_LOST:
|
||||||
case BRCMF_E_PFN_SCAN_COMPLETE:
|
case BRCMF_E_PFN_SCAN_COMPLETE:
|
||||||
|
|
|
@ -691,11 +691,342 @@ scan_out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
|
||||||
|
struct cfg80211_scan_request *request)
|
||||||
|
{
|
||||||
|
u32 n_ssids;
|
||||||
|
u32 n_channels;
|
||||||
|
s32 i;
|
||||||
|
s32 offset;
|
||||||
|
__le16 chanspec;
|
||||||
|
u16 channel;
|
||||||
|
struct ieee80211_channel *req_channel;
|
||||||
|
char *ptr;
|
||||||
|
struct brcmf_ssid ssid;
|
||||||
|
|
||||||
|
memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
|
||||||
|
params_le->bss_type = DOT11_BSSTYPE_ANY;
|
||||||
|
params_le->scan_type = 0;
|
||||||
|
params_le->channel_num = 0;
|
||||||
|
params_le->nprobes = cpu_to_le32(-1);
|
||||||
|
params_le->active_time = cpu_to_le32(-1);
|
||||||
|
params_le->passive_time = cpu_to_le32(-1);
|
||||||
|
params_le->home_time = cpu_to_le32(-1);
|
||||||
|
memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le));
|
||||||
|
|
||||||
|
/* if request is null exit so it will be all channel broadcast scan */
|
||||||
|
if (!request)
|
||||||
|
return;
|
||||||
|
|
||||||
|
n_ssids = request->n_ssids;
|
||||||
|
n_channels = request->n_channels;
|
||||||
|
/* Copy channel array if applicable */
|
||||||
|
WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
|
||||||
|
if (n_channels > 0) {
|
||||||
|
for (i = 0; i < n_channels; i++) {
|
||||||
|
chanspec = 0;
|
||||||
|
req_channel = request->channels[i];
|
||||||
|
channel = ieee80211_frequency_to_channel(
|
||||||
|
req_channel->center_freq);
|
||||||
|
if (req_channel->band == IEEE80211_BAND_2GHZ)
|
||||||
|
chanspec |= WL_CHANSPEC_BAND_2G;
|
||||||
|
else
|
||||||
|
chanspec |= WL_CHANSPEC_BAND_5G;
|
||||||
|
|
||||||
|
if (req_channel->flags & IEEE80211_CHAN_NO_HT40) {
|
||||||
|
chanspec |= WL_CHANSPEC_BW_20;
|
||||||
|
chanspec |= WL_CHANSPEC_CTL_SB_NONE;
|
||||||
|
} else {
|
||||||
|
chanspec |= WL_CHANSPEC_BW_40;
|
||||||
|
if (req_channel->flags &
|
||||||
|
IEEE80211_CHAN_NO_HT40PLUS)
|
||||||
|
chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
|
||||||
|
else
|
||||||
|
chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
params_le->channel_list[i] =
|
||||||
|
(channel & WL_CHANSPEC_CHAN_MASK) |
|
||||||
|
chanspec;
|
||||||
|
WL_SCAN("Chan : %d, Channel spec: %x\n",
|
||||||
|
channel, params_le->channel_list[i]);
|
||||||
|
params_le->channel_list[i] =
|
||||||
|
cpu_to_le16(params_le->channel_list[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WL_SCAN("Scanning all channels\n");
|
||||||
|
}
|
||||||
|
/* Copy ssid array if applicable */
|
||||||
|
WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
|
||||||
|
if (n_ssids > 0) {
|
||||||
|
offset = offsetof(struct brcmf_scan_params_le, channel_list) +
|
||||||
|
n_channels * sizeof(u16);
|
||||||
|
offset = roundup(offset, sizeof(u32));
|
||||||
|
ptr = (char *)params_le + offset;
|
||||||
|
for (i = 0; i < n_ssids; i++) {
|
||||||
|
memset(&ssid, 0, sizeof(ssid));
|
||||||
|
ssid.SSID_len = cpu_to_le32(request->ssids[i].ssid_len);
|
||||||
|
memcpy(ssid.SSID, request->ssids[i].ssid,
|
||||||
|
request->ssids[i].ssid_len);
|
||||||
|
if (!ssid.SSID_len)
|
||||||
|
WL_SCAN("%d: Broadcast scan\n", i);
|
||||||
|
else
|
||||||
|
WL_SCAN("%d: scan for %s size =%d\n", i,
|
||||||
|
ssid.SSID, ssid.SSID_len);
|
||||||
|
memcpy(ptr, &ssid, sizeof(ssid));
|
||||||
|
ptr += sizeof(ssid);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
WL_SCAN("Broadcast scan %p\n", request->ssids);
|
||||||
|
if ((request->ssids) && request->ssids->ssid_len) {
|
||||||
|
WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
|
||||||
|
request->ssids->ssid_len);
|
||||||
|
params_le->ssid_le.SSID_len =
|
||||||
|
cpu_to_le32(request->ssids->ssid_len);
|
||||||
|
memcpy(¶ms_le->ssid_le.SSID, request->ssids->ssid,
|
||||||
|
request->ssids->ssid_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Adding mask to channel numbers */
|
||||||
|
params_le->channel_num =
|
||||||
|
cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
|
||||||
|
(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32
|
||||||
|
brcmf_notify_escan_complete(struct brcmf_cfg80211_priv *cfg_priv,
|
||||||
|
struct net_device *ndev,
|
||||||
|
bool aborted, bool fw_abort)
|
||||||
|
{
|
||||||
|
struct brcmf_scan_params_le params_le;
|
||||||
|
struct cfg80211_scan_request *scan_request;
|
||||||
|
s32 err = 0;
|
||||||
|
|
||||||
|
WL_SCAN("Enter\n");
|
||||||
|
|
||||||
|
/* clear scan request, because the FW abort can cause a second call */
|
||||||
|
/* to this functon and might cause a double cfg80211_scan_done */
|
||||||
|
scan_request = cfg_priv->scan_request;
|
||||||
|
cfg_priv->scan_request = NULL;
|
||||||
|
|
||||||
|
if (timer_pending(&cfg_priv->escan_timeout))
|
||||||
|
del_timer_sync(&cfg_priv->escan_timeout);
|
||||||
|
|
||||||
|
if (fw_abort) {
|
||||||
|
/* Do a scan abort to stop the driver's scan engine */
|
||||||
|
WL_SCAN("ABORT scan in firmware\n");
|
||||||
|
memset(¶ms_le, 0, sizeof(params_le));
|
||||||
|
memcpy(params_le.bssid, ether_bcast, ETH_ALEN);
|
||||||
|
params_le.bss_type = DOT11_BSSTYPE_ANY;
|
||||||
|
params_le.scan_type = 0;
|
||||||
|
params_le.channel_num = cpu_to_le32(1);
|
||||||
|
params_le.nprobes = cpu_to_le32(1);
|
||||||
|
params_le.active_time = cpu_to_le32(-1);
|
||||||
|
params_le.passive_time = cpu_to_le32(-1);
|
||||||
|
params_le.home_time = cpu_to_le32(-1);
|
||||||
|
/* Scan is aborted by setting channel_list[0] to -1 */
|
||||||
|
params_le.channel_list[0] = cpu_to_le16(-1);
|
||||||
|
/* E-Scan (or anyother type) can be aborted by SCAN */
|
||||||
|
err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, ¶ms_le,
|
||||||
|
sizeof(params_le));
|
||||||
|
if (err)
|
||||||
|
WL_ERR("Scan abort failed\n");
|
||||||
|
}
|
||||||
|
if (scan_request) {
|
||||||
|
WL_SCAN("ESCAN Completed scan: %s\n",
|
||||||
|
aborted ? "Aborted" : "Done");
|
||||||
|
cfg80211_scan_done(scan_request, aborted);
|
||||||
|
brcmf_set_mpc(ndev, 1);
|
||||||
|
}
|
||||||
|
if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
|
||||||
|
WL_ERR("Scan complete while device not scanning\n");
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32
|
||||||
|
brcmf_run_escan(struct brcmf_cfg80211_priv *cfg_priv, struct net_device *ndev,
|
||||||
|
struct cfg80211_scan_request *request, u16 action)
|
||||||
|
{
|
||||||
|
s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
|
||||||
|
offsetof(struct brcmf_escan_params_le, params_le);
|
||||||
|
struct brcmf_escan_params_le *params;
|
||||||
|
s32 err = 0;
|
||||||
|
|
||||||
|
WL_SCAN("E-SCAN START\n");
|
||||||
|
|
||||||
|
if (request != NULL) {
|
||||||
|
/* Allocate space for populating ssids in struct */
|
||||||
|
params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
|
||||||
|
|
||||||
|
/* Allocate space for populating ssids in struct */
|
||||||
|
params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
|
||||||
|
}
|
||||||
|
|
||||||
|
params = kzalloc(params_size, GFP_KERNEL);
|
||||||
|
if (!params) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
|
||||||
|
brcmf_escan_prep(¶ms->params_le, request);
|
||||||
|
params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
|
||||||
|
params->action = cpu_to_le16(action);
|
||||||
|
params->sync_id = cpu_to_le16(0x1234);
|
||||||
|
|
||||||
|
err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size,
|
||||||
|
cfg_priv->escan_ioctl_buf, BRCMF_DCMD_MEDLEN);
|
||||||
|
if (err) {
|
||||||
|
if (err == -EBUSY)
|
||||||
|
WL_INFO("system busy : escan canceled\n");
|
||||||
|
else
|
||||||
|
WL_ERR("error (%d)\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(params);
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32
|
||||||
|
brcmf_do_escan(struct brcmf_cfg80211_priv *cfg_priv, struct wiphy *wiphy,
|
||||||
|
struct net_device *ndev, struct cfg80211_scan_request *request)
|
||||||
|
{
|
||||||
|
s32 err;
|
||||||
|
__le32 passive_scan;
|
||||||
|
struct brcmf_scan_results *results;
|
||||||
|
|
||||||
|
WL_SCAN("Enter\n");
|
||||||
|
cfg_priv->escan_info.ndev = ndev;
|
||||||
|
cfg_priv->escan_info.wiphy = wiphy;
|
||||||
|
cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
|
||||||
|
passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1);
|
||||||
|
err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
|
||||||
|
&passive_scan, sizeof(passive_scan));
|
||||||
|
if (err) {
|
||||||
|
WL_ERR("error (%d)\n", err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
brcmf_set_mpc(ndev, 0);
|
||||||
|
results = (struct brcmf_scan_results *)cfg_priv->escan_info.escan_buf;
|
||||||
|
results->version = 0;
|
||||||
|
results->count = 0;
|
||||||
|
results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
|
||||||
|
|
||||||
|
err = brcmf_run_escan(cfg_priv, ndev, request, WL_ESCAN_ACTION_START);
|
||||||
|
if (err)
|
||||||
|
brcmf_set_mpc(ndev, 1);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32
|
||||||
|
brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
|
||||||
|
struct cfg80211_scan_request *request,
|
||||||
|
struct cfg80211_ssid *this_ssid)
|
||||||
|
{
|
||||||
|
struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
|
||||||
|
struct cfg80211_ssid *ssids;
|
||||||
|
struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int;
|
||||||
|
__le32 passive_scan;
|
||||||
|
bool escan_req;
|
||||||
|
bool spec_scan;
|
||||||
|
s32 err;
|
||||||
|
u32 SSID_len;
|
||||||
|
|
||||||
|
WL_SCAN("START ESCAN\n");
|
||||||
|
|
||||||
|
if (test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
|
||||||
|
WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status)) {
|
||||||
|
WL_ERR("Scanning being aborted : status (%lu)\n",
|
||||||
|
cfg_priv->status);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) {
|
||||||
|
WL_ERR("Connecting : status (%lu)\n",
|
||||||
|
cfg_priv->status);
|
||||||
|
return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Arm scan timeout timer */
|
||||||
|
mod_timer(&cfg_priv->escan_timeout, jiffies +
|
||||||
|
WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
|
||||||
|
|
||||||
|
escan_req = false;
|
||||||
|
if (request) {
|
||||||
|
/* scan bss */
|
||||||
|
ssids = request->ssids;
|
||||||
|
escan_req = true;
|
||||||
|
} else {
|
||||||
|
/* scan in ibss */
|
||||||
|
/* we don't do escan in ibss */
|
||||||
|
ssids = this_ssid;
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg_priv->scan_request = request;
|
||||||
|
set_bit(WL_STATUS_SCANNING, &cfg_priv->status);
|
||||||
|
if (escan_req) {
|
||||||
|
err = brcmf_do_escan(cfg_priv, wiphy, ndev, request);
|
||||||
|
if (!err)
|
||||||
|
return err;
|
||||||
|
else
|
||||||
|
goto scan_out;
|
||||||
|
} else {
|
||||||
|
WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
|
||||||
|
ssids->ssid, ssids->ssid_len);
|
||||||
|
memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
|
||||||
|
SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
|
||||||
|
sr->ssid_le.SSID_len = cpu_to_le32(0);
|
||||||
|
spec_scan = false;
|
||||||
|
if (SSID_len) {
|
||||||
|
memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
|
||||||
|
sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
|
||||||
|
spec_scan = true;
|
||||||
|
} else
|
||||||
|
WL_SCAN("Broadcast scan\n");
|
||||||
|
|
||||||
|
passive_scan = cfg_priv->active_scan ? 0 : cpu_to_le32(1);
|
||||||
|
err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
|
||||||
|
&passive_scan, sizeof(passive_scan));
|
||||||
|
if (err) {
|
||||||
|
WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
|
||||||
|
goto scan_out;
|
||||||
|
}
|
||||||
|
brcmf_set_mpc(ndev, 0);
|
||||||
|
err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
|
||||||
|
sizeof(sr->ssid_le));
|
||||||
|
if (err) {
|
||||||
|
if (err == -EBUSY)
|
||||||
|
WL_INFO("BUSY: scan for \"%s\" canceled\n",
|
||||||
|
sr->ssid_le.SSID);
|
||||||
|
else
|
||||||
|
WL_ERR("WLC_SCAN error (%d)\n", err);
|
||||||
|
|
||||||
|
brcmf_set_mpc(ndev, 1);
|
||||||
|
goto scan_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
scan_out:
|
||||||
|
clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
|
||||||
|
if (timer_pending(&cfg_priv->escan_timeout))
|
||||||
|
del_timer_sync(&cfg_priv->escan_timeout);
|
||||||
|
cfg_priv->scan_request = NULL;
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
static s32
|
static s32
|
||||||
brcmf_cfg80211_scan(struct wiphy *wiphy,
|
brcmf_cfg80211_scan(struct wiphy *wiphy,
|
||||||
struct cfg80211_scan_request *request)
|
struct cfg80211_scan_request *request)
|
||||||
{
|
{
|
||||||
struct net_device *ndev = request->wdev->netdev;
|
struct net_device *ndev = request->wdev->netdev;
|
||||||
|
struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
|
||||||
s32 err = 0;
|
s32 err = 0;
|
||||||
|
|
||||||
WL_TRACE("Enter\n");
|
WL_TRACE("Enter\n");
|
||||||
|
@ -703,7 +1034,11 @@ brcmf_cfg80211_scan(struct wiphy *wiphy,
|
||||||
if (!check_sys_up(wiphy))
|
if (!check_sys_up(wiphy))
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL);
|
if (cfg_priv->iscan_on)
|
||||||
|
err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL);
|
||||||
|
else if (cfg_priv->escan_on)
|
||||||
|
err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
WL_ERR("scan error (%d)\n", err);
|
WL_ERR("scan error (%d)\n", err);
|
||||||
|
|
||||||
|
@ -2472,6 +2807,175 @@ static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct brcmf_cfg80211_priv *cfg_priv =
|
||||||
|
container_of(work, struct brcmf_cfg80211_priv,
|
||||||
|
escan_timeout_work);
|
||||||
|
|
||||||
|
brcmf_notify_escan_complete(cfg_priv,
|
||||||
|
cfg_priv->escan_info.ndev, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void brcmf_escan_timeout(unsigned long data)
|
||||||
|
{
|
||||||
|
struct brcmf_cfg80211_priv *cfg_priv =
|
||||||
|
(struct brcmf_cfg80211_priv *)data;
|
||||||
|
|
||||||
|
if (cfg_priv->scan_request) {
|
||||||
|
WL_ERR("timer expired\n");
|
||||||
|
if (cfg_priv->escan_on)
|
||||||
|
schedule_work(&cfg_priv->escan_timeout_work);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32
|
||||||
|
brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
|
||||||
|
struct brcmf_bss_info_le *bss_info_le)
|
||||||
|
{
|
||||||
|
if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
|
||||||
|
(CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
|
||||||
|
CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
|
||||||
|
bss_info_le->SSID_len == bss->SSID_len &&
|
||||||
|
!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
|
||||||
|
if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
|
||||||
|
(bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
|
||||||
|
/* preserve max RSSI if the measurements are
|
||||||
|
* both on-channel or both off-channel
|
||||||
|
*/
|
||||||
|
if (bss_info_le->RSSI > bss->RSSI)
|
||||||
|
bss->RSSI = bss_info_le->RSSI;
|
||||||
|
} else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
|
||||||
|
(bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
|
||||||
|
/* preserve the on-channel rssi measurement
|
||||||
|
* if the new measurement is off channel
|
||||||
|
*/
|
||||||
|
bss->RSSI = bss_info_le->RSSI;
|
||||||
|
bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static s32
|
||||||
|
brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_priv *cfg_priv,
|
||||||
|
struct net_device *ndev,
|
||||||
|
const struct brcmf_event_msg *e, void *data)
|
||||||
|
{
|
||||||
|
s32 status;
|
||||||
|
s32 err = 0;
|
||||||
|
struct brcmf_escan_result_le *escan_result_le;
|
||||||
|
struct brcmf_bss_info_le *bss_info_le;
|
||||||
|
struct brcmf_bss_info_le *bss = NULL;
|
||||||
|
u32 bi_length;
|
||||||
|
struct brcmf_scan_results *list;
|
||||||
|
u32 i;
|
||||||
|
|
||||||
|
status = be32_to_cpu(e->status);
|
||||||
|
|
||||||
|
if (!ndev || !cfg_priv->escan_on ||
|
||||||
|
!test_bit(WL_STATUS_SCANNING, &cfg_priv->status)) {
|
||||||
|
WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n",
|
||||||
|
ndev, cfg_priv->escan_on,
|
||||||
|
!test_bit(WL_STATUS_SCANNING, &cfg_priv->status));
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == BRCMF_E_STATUS_PARTIAL) {
|
||||||
|
WL_SCAN("ESCAN Partial result\n");
|
||||||
|
escan_result_le = (struct brcmf_escan_result_le *) data;
|
||||||
|
if (!escan_result_le) {
|
||||||
|
WL_ERR("Invalid escan result (NULL pointer)\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!cfg_priv->scan_request) {
|
||||||
|
WL_SCAN("result without cfg80211 request\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (le16_to_cpu(escan_result_le->bss_count) != 1) {
|
||||||
|
WL_ERR("Invalid bss_count %d: ignoring\n",
|
||||||
|
escan_result_le->bss_count);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
bss_info_le = &escan_result_le->bss_info_le;
|
||||||
|
|
||||||
|
bi_length = le32_to_cpu(bss_info_le->length);
|
||||||
|
if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
|
||||||
|
WL_ESCAN_RESULTS_FIXED_SIZE)) {
|
||||||
|
WL_ERR("Invalid bss_info length %d: ignoring\n",
|
||||||
|
bi_length);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(cfg_to_wiphy(cfg_priv)->interface_modes &
|
||||||
|
BIT(NL80211_IFTYPE_ADHOC))) {
|
||||||
|
if (le16_to_cpu(bss_info_le->capability) &
|
||||||
|
WLAN_CAPABILITY_IBSS) {
|
||||||
|
WL_ERR("Ignoring IBSS result\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
list = (struct brcmf_scan_results *)
|
||||||
|
cfg_priv->escan_info.escan_buf;
|
||||||
|
if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
|
||||||
|
WL_ERR("Buffer is too small: ignoring\n");
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < list->count; i++) {
|
||||||
|
bss = bss ? (struct brcmf_bss_info_le *)
|
||||||
|
((unsigned char *)bss +
|
||||||
|
le32_to_cpu(bss->length)) : list->bss_info_le;
|
||||||
|
if (brcmf_compare_update_same_bss(bss, bss_info_le))
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
memcpy(&(cfg_priv->escan_info.escan_buf[list->buflen]),
|
||||||
|
bss_info_le, bi_length);
|
||||||
|
list->version = le32_to_cpu(bss_info_le->version);
|
||||||
|
list->buflen += bi_length;
|
||||||
|
list->count++;
|
||||||
|
} else {
|
||||||
|
cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
|
||||||
|
if (cfg_priv->scan_request) {
|
||||||
|
cfg_priv->bss_list = (struct brcmf_scan_results *)
|
||||||
|
cfg_priv->escan_info.escan_buf;
|
||||||
|
brcmf_inform_bss(cfg_priv);
|
||||||
|
if (status == BRCMF_E_STATUS_SUCCESS) {
|
||||||
|
WL_SCAN("ESCAN Completed\n");
|
||||||
|
brcmf_notify_escan_complete(cfg_priv, ndev,
|
||||||
|
false, false);
|
||||||
|
} else {
|
||||||
|
WL_ERR("ESCAN Aborted, Event 0x%x\n", status);
|
||||||
|
brcmf_notify_escan_complete(cfg_priv, ndev,
|
||||||
|
true, false);
|
||||||
|
}
|
||||||
|
brcmf_set_mpc(ndev, 1);
|
||||||
|
} else
|
||||||
|
WL_ERR("Unexpected scan result 0x%x\n", status);
|
||||||
|
}
|
||||||
|
exit:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void brcmf_init_escan(struct brcmf_cfg80211_priv *cfg_priv)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (cfg_priv->escan_on) {
|
||||||
|
cfg_priv->el.handler[BRCMF_E_ESCAN_RESULT] =
|
||||||
|
brcmf_cfg80211_escan_handler;
|
||||||
|
cfg_priv->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
|
||||||
|
/* Init scan_timeout timer */
|
||||||
|
init_timer(&cfg_priv->escan_timeout);
|
||||||
|
cfg_priv->escan_timeout.data = (unsigned long) cfg_priv;
|
||||||
|
cfg_priv->escan_timeout.function = brcmf_escan_timeout;
|
||||||
|
INIT_WORK(&cfg_priv->escan_timeout_work,
|
||||||
|
brcmf_cfg80211_escan_timeout_worker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static __always_inline void brcmf_delay(u32 ms)
|
static __always_inline void brcmf_delay(u32 ms)
|
||||||
{
|
{
|
||||||
if (ms < 1000 / HZ) {
|
if (ms < 1000 / HZ) {
|
||||||
|
@ -3240,6 +3744,8 @@ static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
|
||||||
cfg_priv->profile = NULL;
|
cfg_priv->profile = NULL;
|
||||||
kfree(cfg_priv->scan_req_int);
|
kfree(cfg_priv->scan_req_int);
|
||||||
cfg_priv->scan_req_int = NULL;
|
cfg_priv->scan_req_int = NULL;
|
||||||
|
kfree(cfg_priv->escan_ioctl_buf);
|
||||||
|
cfg_priv->escan_ioctl_buf = NULL;
|
||||||
kfree(cfg_priv->dcmd_buf);
|
kfree(cfg_priv->dcmd_buf);
|
||||||
cfg_priv->dcmd_buf = NULL;
|
cfg_priv->dcmd_buf = NULL;
|
||||||
kfree(cfg_priv->extra_buf);
|
kfree(cfg_priv->extra_buf);
|
||||||
|
@ -3268,6 +3774,9 @@ static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!cfg_priv->scan_req_int)
|
if (!cfg_priv->scan_req_int)
|
||||||
goto init_priv_mem_out;
|
goto init_priv_mem_out;
|
||||||
|
cfg_priv->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
|
||||||
|
if (!cfg_priv->escan_ioctl_buf)
|
||||||
|
goto init_priv_mem_out;
|
||||||
cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
|
cfg_priv->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
|
||||||
if (!cfg_priv->dcmd_buf)
|
if (!cfg_priv->dcmd_buf)
|
||||||
goto init_priv_mem_out;
|
goto init_priv_mem_out;
|
||||||
|
@ -3404,8 +3913,17 @@ static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv)
|
||||||
|
|
||||||
cfg_priv->scan_request = NULL;
|
cfg_priv->scan_request = NULL;
|
||||||
cfg_priv->pwr_save = true;
|
cfg_priv->pwr_save = true;
|
||||||
|
#ifdef CONFIG_BRCMISCAN
|
||||||
cfg_priv->iscan_on = true; /* iscan on & off switch.
|
cfg_priv->iscan_on = true; /* iscan on & off switch.
|
||||||
we enable iscan per default */
|
we enable iscan per default */
|
||||||
|
cfg_priv->escan_on = false; /* escan on & off switch.
|
||||||
|
we disable escan per default */
|
||||||
|
#else
|
||||||
|
cfg_priv->iscan_on = false; /* iscan on & off switch.
|
||||||
|
we disable iscan per default */
|
||||||
|
cfg_priv->escan_on = true; /* escan on & off switch.
|
||||||
|
we enable escan per default */
|
||||||
|
#endif
|
||||||
cfg_priv->roam_on = true; /* roam on & off switch.
|
cfg_priv->roam_on = true; /* roam on & off switch.
|
||||||
we enable roam per default */
|
we enable roam per default */
|
||||||
|
|
||||||
|
@ -3423,6 +3941,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv)
|
||||||
err = brcmf_init_iscan(cfg_priv);
|
err = brcmf_init_iscan(cfg_priv);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
brcmf_init_escan(cfg_priv);
|
||||||
brcmf_init_conf(cfg_priv->conf);
|
brcmf_init_conf(cfg_priv->conf);
|
||||||
brcmf_init_prof(cfg_priv->profile);
|
brcmf_init_prof(cfg_priv->profile);
|
||||||
brcmf_link_down(cfg_priv);
|
brcmf_link_down(cfg_priv);
|
||||||
|
@ -3581,6 +4100,7 @@ static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
|
||||||
setbit(eventmask, BRCMF_E_TXFAIL);
|
setbit(eventmask, BRCMF_E_TXFAIL);
|
||||||
setbit(eventmask, BRCMF_E_JOIN_START);
|
setbit(eventmask, BRCMF_E_JOIN_START);
|
||||||
setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
|
setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
|
||||||
|
setbit(eventmask, BRCMF_E_ESCAN_RESULT);
|
||||||
|
|
||||||
brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
|
brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
|
||||||
iovbuf, sizeof(iovbuf));
|
iovbuf, sizeof(iovbuf));
|
||||||
|
|
|
@ -123,6 +123,13 @@ do { \
|
||||||
#define WL_SCAN_UNASSOC_TIME 40
|
#define WL_SCAN_UNASSOC_TIME 40
|
||||||
#define WL_SCAN_PASSIVE_TIME 120
|
#define WL_SCAN_PASSIVE_TIME 120
|
||||||
|
|
||||||
|
#define WL_ESCAN_BUF_SIZE (1024 * 64)
|
||||||
|
#define WL_ESCAN_TIMER_INTERVAL_MS 8000 /* E-Scan timeout */
|
||||||
|
|
||||||
|
#define WL_ESCAN_ACTION_START 1
|
||||||
|
#define WL_ESCAN_ACTION_CONTINUE 2
|
||||||
|
#define WL_ESCAN_ACTION_ABORT 3
|
||||||
|
|
||||||
/* dongle status */
|
/* dongle status */
|
||||||
enum wl_status {
|
enum wl_status {
|
||||||
WL_STATUS_READY,
|
WL_STATUS_READY,
|
||||||
|
@ -275,6 +282,19 @@ struct brcmf_cfg80211_pmk_list {
|
||||||
struct pmkid foo[MAXPMKID - 1];
|
struct pmkid foo[MAXPMKID - 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* dongle escan state */
|
||||||
|
enum wl_escan_state {
|
||||||
|
WL_ESCAN_STATE_IDLE,
|
||||||
|
WL_ESCAN_STATE_SCANNING
|
||||||
|
};
|
||||||
|
|
||||||
|
struct escan_info {
|
||||||
|
u32 escan_state;
|
||||||
|
u8 escan_buf[WL_ESCAN_BUF_SIZE];
|
||||||
|
struct wiphy *wiphy;
|
||||||
|
struct net_device *ndev;
|
||||||
|
};
|
||||||
|
|
||||||
/* dongle private data of cfg80211 interface */
|
/* dongle private data of cfg80211 interface */
|
||||||
struct brcmf_cfg80211_priv {
|
struct brcmf_cfg80211_priv {
|
||||||
struct wireless_dev *wdev; /* representing wl cfg80211 device */
|
struct wireless_dev *wdev; /* representing wl cfg80211 device */
|
||||||
|
@ -315,6 +335,11 @@ struct brcmf_cfg80211_priv {
|
||||||
u8 *dcmd_buf; /* dcmd buffer */
|
u8 *dcmd_buf; /* dcmd buffer */
|
||||||
u8 *extra_buf; /* maily to grab assoc information */
|
u8 *extra_buf; /* maily to grab assoc information */
|
||||||
struct dentry *debugfsdir;
|
struct dentry *debugfsdir;
|
||||||
|
bool escan_on; /* escan on/off switch */
|
||||||
|
struct escan_info escan_info; /* escan information */
|
||||||
|
struct timer_list escan_timeout; /* Timer for catch scan timeout */
|
||||||
|
struct work_struct escan_timeout_work; /* scan timeout worker */
|
||||||
|
u8 *escan_ioctl_buf;
|
||||||
u8 ci[0] __aligned(NETDEV_ALIGN);
|
u8 ci[0] __aligned(NETDEV_ALIGN);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче