1594 lines
44 KiB
C
Executable File
1594 lines
44 KiB
C
Executable File
|
|
#if defined(WL_ESCAN)
|
|
#include <bcmendian.h>
|
|
#include <linux/if_arp.h>
|
|
#include <asm/uaccess.h>
|
|
#include <wl_android.h>
|
|
#include <wl_escan.h>
|
|
#include <dhd_config.h>
|
|
|
|
#define ESCAN_ERROR(name, arg1, args...) \
|
|
do { \
|
|
if (android_msg_level & ANDROID_ERROR_LEVEL) { \
|
|
printk(KERN_ERR "[dhd-%s] ESCAN-ERROR) %s : " arg1, name, __func__, ## args); \
|
|
} \
|
|
} while (0)
|
|
#define ESCAN_TRACE(name, arg1, args...) \
|
|
do { \
|
|
if (android_msg_level & ANDROID_TRACE_LEVEL) { \
|
|
printk(KERN_INFO "[dhd-%s] ESCAN-TRACE) %s : " arg1, name, __func__, ## args); \
|
|
} \
|
|
} while (0)
|
|
#define ESCAN_SCAN(name, arg1, args...) \
|
|
do { \
|
|
if (android_msg_level & ANDROID_SCAN_LEVEL) { \
|
|
printk(KERN_INFO "[dhd-%s] ESCAN-SCAN) %s : " arg1, name, __func__, ## args); \
|
|
} \
|
|
} while (0)
|
|
#define ESCAN_DBG(name, arg1, args...) \
|
|
do { \
|
|
if (android_msg_level & ANDROID_DBG_LEVEL) { \
|
|
printk(KERN_INFO "[dhd-%s] ESCAN-DBG) %s : " arg1, name, __func__, ## args); \
|
|
} \
|
|
} while (0)
|
|
|
|
/* IOCTL swapping mode for Big Endian host with Little Endian dongle. Default to off */
|
|
#define htod32(i) (i)
|
|
#define htod16(i) (i)
|
|
#define dtoh32(i) (i)
|
|
#define dtoh16(i) (i)
|
|
#define htodchanspec(i) (i)
|
|
#define dtohchanspec(i) (i)
|
|
#define WL_EXTRA_BUF_MAX 2048
|
|
|
|
#define wl_escan_get_buf(a) ((wl_scan_results_t *) (a)->escan_buf)
|
|
|
|
#define for_each_bss(list, bss, __i) \
|
|
for (__i = 0; __i < list->count && __i < IW_MAX_AP; __i++, bss = next_bss(list, bss))
|
|
|
|
#define wl_escan_set_sync_id(a) ((a) = htod16(0x1234))
|
|
|
|
#ifdef ESCAN_BUF_OVERFLOW_MGMT
|
|
#define BUF_OVERFLOW_MGMT_COUNT 3
|
|
typedef struct {
|
|
int RSSI;
|
|
int length;
|
|
struct ether_addr BSSID;
|
|
} removal_element_t;
|
|
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
|
|
|
|
/* Return a new chanspec given a legacy chanspec
|
|
* Returns INVCHANSPEC on error
|
|
*/
|
|
static chanspec_t
|
|
wl_chspec_from_legacy(chanspec_t legacy_chspec)
|
|
{
|
|
chanspec_t chspec;
|
|
|
|
/* get the channel number */
|
|
chspec = LCHSPEC_CHANNEL(legacy_chspec);
|
|
|
|
/* convert the band */
|
|
if (LCHSPEC_IS2G(legacy_chspec)) {
|
|
chspec |= WL_CHANSPEC_BAND_2G;
|
|
} else {
|
|
chspec |= WL_CHANSPEC_BAND_5G;
|
|
}
|
|
|
|
/* convert the bw and sideband */
|
|
if (LCHSPEC_IS20(legacy_chspec)) {
|
|
chspec |= WL_CHANSPEC_BW_20;
|
|
} else {
|
|
chspec |= WL_CHANSPEC_BW_40;
|
|
if (LCHSPEC_CTL_SB(legacy_chspec) == WL_LCHANSPEC_CTL_SB_LOWER) {
|
|
chspec |= WL_CHANSPEC_CTL_SB_L;
|
|
} else {
|
|
chspec |= WL_CHANSPEC_CTL_SB_U;
|
|
}
|
|
}
|
|
|
|
if (wf_chspec_malformed(chspec)) {
|
|
ESCAN_ERROR("wlan", "wl_chspec_from_legacy: output chanspec (0x%04X) malformed\n",
|
|
chspec);
|
|
return INVCHANSPEC;
|
|
}
|
|
|
|
return chspec;
|
|
}
|
|
|
|
/* Return a legacy chanspec given a new chanspec
|
|
* Returns INVCHANSPEC on error
|
|
*/
|
|
static chanspec_t
|
|
wl_chspec_to_legacy(chanspec_t chspec)
|
|
{
|
|
chanspec_t lchspec;
|
|
|
|
if (wf_chspec_malformed(chspec)) {
|
|
ESCAN_ERROR("wlan", "wl_chspec_to_legacy: input chanspec (0x%04X) malformed\n",
|
|
chspec);
|
|
return INVCHANSPEC;
|
|
}
|
|
|
|
/* get the channel number */
|
|
lchspec = CHSPEC_CHANNEL(chspec);
|
|
|
|
/* convert the band */
|
|
if (CHSPEC_IS2G(chspec)) {
|
|
lchspec |= WL_LCHANSPEC_BAND_2G;
|
|
} else {
|
|
lchspec |= WL_LCHANSPEC_BAND_5G;
|
|
}
|
|
|
|
/* convert the bw and sideband */
|
|
if (CHSPEC_IS20(chspec)) {
|
|
lchspec |= WL_LCHANSPEC_BW_20;
|
|
lchspec |= WL_LCHANSPEC_CTL_SB_NONE;
|
|
} else if (CHSPEC_IS40(chspec)) {
|
|
lchspec |= WL_LCHANSPEC_BW_40;
|
|
if (CHSPEC_CTL_SB(chspec) == WL_CHANSPEC_CTL_SB_L) {
|
|
lchspec |= WL_LCHANSPEC_CTL_SB_LOWER;
|
|
} else {
|
|
lchspec |= WL_LCHANSPEC_CTL_SB_UPPER;
|
|
}
|
|
} else {
|
|
/* cannot express the bandwidth */
|
|
char chanbuf[CHANSPEC_STR_LEN];
|
|
ESCAN_ERROR("wlan", "wl_chspec_to_legacy: unable to convert chanspec %s "
|
|
"(0x%04X) to pre-11ac format\n",
|
|
wf_chspec_ntoa(chspec, chanbuf), chspec);
|
|
return INVCHANSPEC;
|
|
}
|
|
|
|
return lchspec;
|
|
}
|
|
|
|
/* given a chanspec value from the driver, do the endian and chanspec version conversion to
|
|
* a chanspec_t value
|
|
* Returns INVCHANSPEC on error
|
|
*/
|
|
static chanspec_t
|
|
wl_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec)
|
|
{
|
|
chanspec = dtohchanspec(chanspec);
|
|
if (ioctl_ver == 1) {
|
|
chanspec = wl_chspec_from_legacy(chanspec);
|
|
}
|
|
|
|
return chanspec;
|
|
}
|
|
|
|
/* given a chanspec value, do the endian and chanspec version conversion to
|
|
* a chanspec_t value
|
|
* Returns INVCHANSPEC on error
|
|
*/
|
|
static chanspec_t
|
|
wl_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)
|
|
{
|
|
if (ioctl_ver == 1) {
|
|
chanspec = wl_chspec_to_legacy(chanspec);
|
|
if (chanspec == INVCHANSPEC) {
|
|
return chanspec;
|
|
}
|
|
}
|
|
chanspec = htodchanspec(chanspec);
|
|
|
|
return chanspec;
|
|
}
|
|
|
|
/* given a channel value, do the endian and chanspec version conversion to
|
|
* a chanspec_t value
|
|
* Returns INVCHANSPEC on error
|
|
*/
|
|
static chanspec_t
|
|
wl_ch_host_to_driver(int ioctl_ver, u16 channel)
|
|
{
|
|
chanspec_t chanspec;
|
|
|
|
chanspec = channel & WL_CHANSPEC_CHAN_MASK;
|
|
|
|
if (channel <= CH_MAX_2G_CHANNEL)
|
|
chanspec |= WL_CHANSPEC_BAND_2G;
|
|
else
|
|
chanspec |= WL_CHANSPEC_BAND_5G;
|
|
|
|
chanspec |= WL_CHANSPEC_BW_20;
|
|
|
|
chanspec |= WL_CHANSPEC_CTL_SB_NONE;
|
|
|
|
return wl_chspec_host_to_driver(ioctl_ver, chanspec);
|
|
}
|
|
|
|
static inline struct wl_bss_info *next_bss(struct wl_scan_results *list,
|
|
struct wl_bss_info *bss)
|
|
{
|
|
return bss = bss ?
|
|
(struct wl_bss_info *)((uintptr) bss + dtoh32(bss->length)) : list->bss_info;
|
|
}
|
|
|
|
#if defined(ESCAN_RESULT_PATCH)
|
|
static s32
|
|
wl_escan_inform_bss(struct net_device *dev, struct wl_escan_info *escan)
|
|
{
|
|
struct wl_scan_results *bss_list;
|
|
s32 err = 0;
|
|
#if defined(RSSIAVG)
|
|
int rssi;
|
|
#endif
|
|
|
|
bss_list = escan->bss_list;
|
|
|
|
ESCAN_SCAN(dev->name, "scanned AP count (%d)\n", bss_list->count);
|
|
|
|
/* Delete disconnected cache */
|
|
#if defined(BSSCACHE)
|
|
wl_delete_disconnected_bss_cache(&escan->g_bss_cache_ctrl,
|
|
(u8*)&escan->disconnected_bssid);
|
|
#if defined(RSSIAVG)
|
|
wl_delete_disconnected_rssi_cache(&escan->g_rssi_cache_ctrl,
|
|
(u8*)&escan->disconnected_bssid);
|
|
#endif
|
|
#endif
|
|
|
|
/* Update cache */
|
|
#if defined(RSSIAVG)
|
|
wl_update_rssi_cache(&escan->g_rssi_cache_ctrl, bss_list);
|
|
if (!in_atomic())
|
|
wl_update_connected_rssi_cache(dev, &escan->g_rssi_cache_ctrl, &rssi);
|
|
#endif
|
|
#if defined(BSSCACHE)
|
|
wl_update_bss_cache(&escan->g_bss_cache_ctrl,
|
|
#if defined(RSSIAVG)
|
|
&escan->g_rssi_cache_ctrl,
|
|
#endif
|
|
bss_list);
|
|
#endif
|
|
|
|
/* delete dirty cache */
|
|
#if defined(RSSIAVG)
|
|
wl_delete_dirty_rssi_cache(&escan->g_rssi_cache_ctrl);
|
|
wl_reset_rssi_cache(&escan->g_rssi_cache_ctrl);
|
|
#endif
|
|
#if defined(BSSCACHE)
|
|
wl_delete_dirty_bss_cache(&escan->g_bss_cache_ctrl);
|
|
wl_reset_bss_cache(&escan->g_bss_cache_ctrl);
|
|
if (escan->autochannel)
|
|
wl_ext_get_best_channel(dev, &escan->g_bss_cache_ctrl,
|
|
escan->ioctl_ver, &escan->best_2g_ch, &escan->best_5g_ch);
|
|
#else
|
|
if (escan->autochannel)
|
|
wl_ext_get_best_channel(dev, bss_list, escan->ioctl_ver,
|
|
&escan->best_2g_ch, &escan->best_5g_ch);
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
#endif /* ESCAN_RESULT_PATCH */
|
|
|
|
static wl_scan_params_t *
|
|
wl_escan_alloc_params(struct net_device *dev, struct wl_escan_info *escan,
|
|
int channel, int nprobes, int *out_params_size)
|
|
{
|
|
wl_scan_params_t *params;
|
|
int params_size;
|
|
int num_chans;
|
|
|
|
*out_params_size = 0;
|
|
|
|
/* Our scan params only need space for 1 channel and 0 ssids */
|
|
params_size = WL_SCAN_PARAMS_FIXED_SIZE + 1 * sizeof(uint16);
|
|
params = (wl_scan_params_t*) kzalloc(params_size, GFP_KERNEL);
|
|
if (params == NULL) {
|
|
ESCAN_ERROR(dev->name, "mem alloc failed (%d bytes)\n", params_size);
|
|
return params;
|
|
}
|
|
memset(params, 0, params_size);
|
|
params->nprobes = nprobes;
|
|
|
|
num_chans = (channel == 0) ? 0 : 1;
|
|
|
|
memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN);
|
|
params->bss_type = DOT11_BSSTYPE_ANY;
|
|
params->scan_type = DOT11_SCANTYPE_ACTIVE;
|
|
params->nprobes = htod32(1);
|
|
params->active_time = htod32(-1);
|
|
params->passive_time = htod32(-1);
|
|
params->home_time = htod32(10);
|
|
if (channel == -1)
|
|
params->channel_list[0] = htodchanspec(channel);
|
|
else
|
|
params->channel_list[0] = wl_ch_host_to_driver(escan->ioctl_ver, channel);
|
|
|
|
/* Our scan params have 1 channel and 0 ssids */
|
|
params->channel_num = htod32((0 << WL_SCAN_PARAMS_NSSID_SHIFT) |
|
|
(num_chans & WL_SCAN_PARAMS_COUNT_MASK));
|
|
|
|
*out_params_size = params_size; /* rtn size to the caller */
|
|
return params;
|
|
}
|
|
|
|
static void
|
|
wl_escan_abort(struct net_device *dev, struct wl_escan_info *escan)
|
|
{
|
|
wl_scan_params_t *params = NULL;
|
|
s32 params_size = 0;
|
|
s32 err = BCME_OK;
|
|
if (!in_atomic()) {
|
|
/* Our scan params only need space for 1 channel and 0 ssids */
|
|
params = wl_escan_alloc_params(dev, escan, -1, 0, ¶ms_size);
|
|
if (params == NULL) {
|
|
ESCAN_ERROR(dev->name, "scan params allocation failed \n");
|
|
err = -ENOMEM;
|
|
} else {
|
|
/* Do a scan abort to stop the driver's scan engine */
|
|
err = wldev_ioctl(dev, WLC_SCAN, params, params_size, true);
|
|
if (err < 0) {
|
|
ESCAN_ERROR(dev->name, "scan abort failed \n");
|
|
}
|
|
kfree(params);
|
|
}
|
|
}
|
|
}
|
|
|
|
static s32
|
|
wl_escan_notify_complete(struct net_device *dev,
|
|
struct wl_escan_info *escan, bool fw_abort)
|
|
{
|
|
s32 err = BCME_OK;
|
|
#if defined(WL_WIRELESS_EXT)
|
|
int cmd = 0;
|
|
#if WIRELESS_EXT > 13
|
|
union iwreq_data wrqu;
|
|
char extra[IW_CUSTOM_MAX + 1];
|
|
#endif
|
|
#endif
|
|
struct dhd_pub *dhd = dhd_get_pub(dev);
|
|
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
|
|
if (fw_abort && !in_atomic())
|
|
wl_escan_abort(dev, escan);
|
|
|
|
if (timer_pending(&escan->scan_timeout))
|
|
del_timer_sync(&escan->scan_timeout);
|
|
|
|
#if defined(ESCAN_RESULT_PATCH)
|
|
escan->bss_list = wl_escan_get_buf(escan);
|
|
wl_escan_inform_bss(dev, escan);
|
|
#endif /* ESCAN_RESULT_PATCH */
|
|
|
|
escan->escan_state = ESCAN_STATE_IDLE;
|
|
wake_up_interruptible(&dhd->conf->event_complete);
|
|
|
|
#if defined(WL_WIRELESS_EXT)
|
|
#if WIRELESS_EXT > 13
|
|
#if WIRELESS_EXT > 14
|
|
cmd = SIOCGIWSCAN;
|
|
#endif
|
|
// terence 20150224: fix "wlan0: (WE) : Wireless Event too big (65306)"
|
|
memset(&wrqu, 0, sizeof(wrqu));
|
|
memset(extra, 0, sizeof(extra));
|
|
if (cmd) {
|
|
if (cmd == SIOCGIWSCAN) {
|
|
wireless_send_event(dev, cmd, &wrqu, NULL);
|
|
} else
|
|
wireless_send_event(dev, cmd, &wrqu, extra);
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
|
|
#ifdef ESCAN_BUF_OVERFLOW_MGMT
|
|
static void
|
|
wl_escan_find_removal_candidate(struct wl_escan_info *escan,
|
|
wl_bss_info_t *bss, removal_element_t *candidate)
|
|
{
|
|
int idx;
|
|
for (idx = 0; idx < BUF_OVERFLOW_MGMT_COUNT; idx++) {
|
|
int len = BUF_OVERFLOW_MGMT_COUNT - idx - 1;
|
|
if (bss->RSSI < candidate[idx].RSSI) {
|
|
if (len)
|
|
memcpy(&candidate[idx + 1], &candidate[idx],
|
|
sizeof(removal_element_t) * len);
|
|
candidate[idx].RSSI = bss->RSSI;
|
|
candidate[idx].length = bss->length;
|
|
memcpy(&candidate[idx].BSSID, &bss->BSSID, ETHER_ADDR_LEN);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
wl_escan_remove_lowRSSI_info(struct net_device *dev, struct wl_escan_info *escan,
|
|
wl_scan_results_t *list, removal_element_t *candidate, wl_bss_info_t *bi)
|
|
{
|
|
int idx1, idx2;
|
|
int total_delete_len = 0;
|
|
for (idx1 = 0; idx1 < BUF_OVERFLOW_MGMT_COUNT; idx1++) {
|
|
int cur_len = WL_SCAN_RESULTS_FIXED_SIZE;
|
|
wl_bss_info_t *bss = NULL;
|
|
if (candidate[idx1].RSSI >= bi->RSSI)
|
|
continue;
|
|
for (idx2 = 0; idx2 < list->count; idx2++) {
|
|
bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length)) :
|
|
list->bss_info;
|
|
if (!bcmp(&candidate[idx1].BSSID, &bss->BSSID, ETHER_ADDR_LEN) &&
|
|
candidate[idx1].RSSI == bss->RSSI &&
|
|
candidate[idx1].length == dtoh32(bss->length)) {
|
|
u32 delete_len = dtoh32(bss->length);
|
|
ESCAN_DBG(dev->name,
|
|
"delete scan info of %pM to add new AP\n", &bss->BSSID);
|
|
if (idx2 < list->count -1) {
|
|
memmove((u8 *)bss, (u8 *)bss + delete_len,
|
|
list->buflen - cur_len - delete_len);
|
|
}
|
|
list->buflen -= delete_len;
|
|
list->count--;
|
|
total_delete_len += delete_len;
|
|
/* if delete_len is greater than or equal to result length */
|
|
if (total_delete_len >= bi->length) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
cur_len += dtoh32(bss->length);
|
|
}
|
|
}
|
|
}
|
|
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
|
|
|
|
static s32
|
|
wl_escan_handler(struct net_device *dev, struct wl_escan_info *escan,
|
|
const wl_event_msg_t *e, void *data)
|
|
{
|
|
s32 err = BCME_OK;
|
|
s32 status = ntoh32(e->status);
|
|
wl_bss_info_t *bi;
|
|
wl_escan_result_t *escan_result;
|
|
wl_bss_info_t *bss = NULL;
|
|
wl_scan_results_t *list;
|
|
u32 bi_length;
|
|
u32 i;
|
|
u16 channel;
|
|
|
|
mutex_lock(&escan->usr_sync);
|
|
escan_result = (wl_escan_result_t *)data;
|
|
|
|
if (escan->escan_state != ESCAN_STATE_SCANING) {
|
|
ESCAN_DBG(dev->name, "Not my scan\n");
|
|
goto exit;
|
|
}
|
|
|
|
ESCAN_DBG(dev->name, "enter event type : %d, status : %d \n",
|
|
ntoh32(e->event_type), ntoh32(e->status));
|
|
|
|
if (status == WLC_E_STATUS_PARTIAL) {
|
|
ESCAN_DBG(dev->name, "WLC_E_STATUS_PARTIAL \n");
|
|
if (!escan_result) {
|
|
ESCAN_ERROR(dev->name, "Invalid escan result (NULL pointer)\n");
|
|
goto exit;
|
|
}
|
|
if (dtoh16(escan_result->bss_count) != 1) {
|
|
ESCAN_ERROR(dev->name, "Invalid bss_count %d: ignoring\n",
|
|
escan_result->bss_count);
|
|
goto exit;
|
|
}
|
|
bi = escan_result->bss_info;
|
|
if (!bi) {
|
|
ESCAN_ERROR(dev->name, "Invalid escan bss info (NULL pointer)\n");
|
|
goto exit;
|
|
}
|
|
bi_length = dtoh32(bi->length);
|
|
if (bi_length != (dtoh32(escan_result->buflen) - WL_ESCAN_RESULTS_FIXED_SIZE)) {
|
|
ESCAN_ERROR(dev->name, "Invalid bss_info length %d: ignoring\n",
|
|
bi_length);
|
|
goto exit;
|
|
}
|
|
|
|
/* +++++ terence 20130524: skip invalid bss */
|
|
channel =
|
|
bi->ctl_ch ? bi->ctl_ch :
|
|
CHSPEC_CHANNEL(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
|
|
if (!dhd_conf_match_channel(escan->pub, channel))
|
|
goto exit;
|
|
/* ----- terence 20130524: skip invalid bss */
|
|
|
|
{
|
|
int cur_len = WL_SCAN_RESULTS_FIXED_SIZE;
|
|
#ifdef ESCAN_BUF_OVERFLOW_MGMT
|
|
removal_element_t candidate[BUF_OVERFLOW_MGMT_COUNT];
|
|
int remove_lower_rssi = FALSE;
|
|
|
|
bzero(candidate, sizeof(removal_element_t)*BUF_OVERFLOW_MGMT_COUNT);
|
|
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
|
|
|
|
list = wl_escan_get_buf(escan);
|
|
#ifdef ESCAN_BUF_OVERFLOW_MGMT
|
|
if (bi_length > ESCAN_BUF_SIZE - list->buflen)
|
|
remove_lower_rssi = TRUE;
|
|
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
|
|
|
|
ESCAN_DBG(dev->name, "%s(%pM) RSSI %d flags 0x%x length %d\n",
|
|
bi->SSID, &bi->BSSID, bi->RSSI, bi->flags, bi->length);
|
|
for (i = 0; i < list->count; i++) {
|
|
bss = bss ? (wl_bss_info_t *)((uintptr)bss + dtoh32(bss->length))
|
|
: list->bss_info;
|
|
#ifdef ESCAN_BUF_OVERFLOW_MGMT
|
|
ESCAN_DBG(dev->name,
|
|
"%s(%pM), i=%d bss: RSSI %d list->count %d\n",
|
|
bss->SSID, &bss->BSSID, i, bss->RSSI, list->count);
|
|
|
|
if (remove_lower_rssi)
|
|
wl_escan_find_removal_candidate(escan, bss, candidate);
|
|
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
|
|
if (!bcmp(&bi->BSSID, &bss->BSSID, ETHER_ADDR_LEN) &&
|
|
(CHSPEC_BAND(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec))
|
|
== CHSPEC_BAND(wl_chspec_driver_to_host(escan->ioctl_ver, bss->chanspec))) &&
|
|
bi->SSID_len == bss->SSID_len &&
|
|
!bcmp(bi->SSID, bss->SSID, bi->SSID_len)) {
|
|
|
|
/* do not allow beacon data to update
|
|
*the data recd from a probe response
|
|
*/
|
|
if (!(bss->flags & WL_BSS_FLAGS_FROM_BEACON) &&
|
|
(bi->flags & WL_BSS_FLAGS_FROM_BEACON))
|
|
goto exit;
|
|
|
|
ESCAN_DBG(dev->name,
|
|
"%s(%pM), i=%d prev: RSSI %d flags 0x%x, "
|
|
"new: RSSI %d flags 0x%x\n",
|
|
bss->SSID, &bi->BSSID, i, bss->RSSI, bss->flags,
|
|
bi->RSSI, bi->flags);
|
|
|
|
if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) ==
|
|
(bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL)) {
|
|
/* preserve max RSSI if the measurements are
|
|
* both on-channel or both off-channel
|
|
*/
|
|
ESCAN_DBG(dev->name,
|
|
"%s(%pM), same onchan, RSSI: prev %d new %d\n",
|
|
bss->SSID, &bi->BSSID, bss->RSSI, bi->RSSI);
|
|
bi->RSSI = MAX(bss->RSSI, bi->RSSI);
|
|
} else if ((bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) &&
|
|
(bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) == 0) {
|
|
/* preserve the on-channel rssi measurement
|
|
* if the new measurement is off channel
|
|
*/
|
|
ESCAN_DBG(dev->name,
|
|
"%s(%pM), prev onchan, RSSI: prev %d new %d\n",
|
|
bss->SSID, &bi->BSSID, bss->RSSI, bi->RSSI);
|
|
bi->RSSI = bss->RSSI;
|
|
bi->flags |= WL_BSS_FLAGS_RSSI_ONCHANNEL;
|
|
}
|
|
if (dtoh32(bss->length) != bi_length) {
|
|
u32 prev_len = dtoh32(bss->length);
|
|
|
|
ESCAN_DBG(dev->name,
|
|
"bss info replacement occured(bcast:%d->probresp%d)\n",
|
|
bss->ie_length, bi->ie_length);
|
|
ESCAN_DBG(dev->name,
|
|
"%s(%pM), replacement!(%d -> %d)\n",
|
|
bss->SSID, &bi->BSSID, prev_len, bi_length);
|
|
|
|
if (list->buflen - prev_len + bi_length > ESCAN_BUF_SIZE) {
|
|
ESCAN_ERROR(dev->name,
|
|
"Buffer is too small: keep the previous result "
|
|
"of this AP\n");
|
|
/* Only update RSSI */
|
|
bss->RSSI = bi->RSSI;
|
|
bss->flags |= (bi->flags
|
|
& WL_BSS_FLAGS_RSSI_ONCHANNEL);
|
|
goto exit;
|
|
}
|
|
|
|
if (i < list->count - 1) {
|
|
/* memory copy required by this case only */
|
|
memmove((u8 *)bss + bi_length,
|
|
(u8 *)bss + prev_len,
|
|
list->buflen - cur_len - prev_len);
|
|
}
|
|
list->buflen -= prev_len;
|
|
list->buflen += bi_length;
|
|
}
|
|
list->version = dtoh32(bi->version);
|
|
memcpy((u8 *)bss, (u8 *)bi, bi_length);
|
|
goto exit;
|
|
}
|
|
cur_len += dtoh32(bss->length);
|
|
}
|
|
if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
|
|
#ifdef ESCAN_BUF_OVERFLOW_MGMT
|
|
wl_escan_remove_lowRSSI_info(dev, escan, list, candidate, bi);
|
|
if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
|
|
ESCAN_DBG(dev->name,
|
|
"RSSI(%pM) is too low(%d) to add Buffer\n",
|
|
&bi->BSSID, bi->RSSI);
|
|
goto exit;
|
|
}
|
|
#else
|
|
ESCAN_ERROR(dev->name, "Buffer is too small: ignoring\n");
|
|
goto exit;
|
|
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
|
|
}
|
|
|
|
memcpy(&(((char *)list)[list->buflen]), bi, bi_length);
|
|
list->version = dtoh32(bi->version);
|
|
list->buflen += bi_length;
|
|
list->count++;
|
|
}
|
|
}
|
|
else if (status == WLC_E_STATUS_SUCCESS) {
|
|
ESCAN_DBG(dev->name, "ESCAN COMPLETED\n");
|
|
escan->bss_list = wl_escan_get_buf(escan);
|
|
ESCAN_DBG(dev->name, "SCAN COMPLETED: scanned AP count=%d\n",
|
|
escan->bss_list->count);
|
|
wl_escan_notify_complete(dev, escan, false);
|
|
} else if ((status == WLC_E_STATUS_ABORT) || (status == WLC_E_STATUS_NEWSCAN) ||
|
|
(status == WLC_E_STATUS_11HQUIET) || (status == WLC_E_STATUS_CS_ABORT) ||
|
|
(status == WLC_E_STATUS_NEWASSOC)) {
|
|
/* Handle all cases of scan abort */
|
|
ESCAN_DBG(dev->name, "ESCAN ABORT reason: %d\n", status);
|
|
escan->bss_list = wl_escan_get_buf(escan);
|
|
ESCAN_DBG(dev->name, "SCAN ABORT: scanned AP count=%d\n",
|
|
escan->bss_list->count);
|
|
wl_escan_notify_complete(dev, escan, false);
|
|
} else if (status == WLC_E_STATUS_TIMEOUT) {
|
|
ESCAN_ERROR(dev->name, "WLC_E_STATUS_TIMEOUT\n");
|
|
ESCAN_ERROR(dev->name, "reason[0x%x]\n", e->reason);
|
|
if (e->reason == 0xFFFFFFFF) {
|
|
wl_escan_notify_complete(dev, escan, true);
|
|
}
|
|
} else {
|
|
ESCAN_ERROR(dev->name, "unexpected Escan Event %d : abort\n", status);
|
|
escan->bss_list = wl_escan_get_buf(escan);
|
|
ESCAN_DBG(dev->name, "SCAN ABORTED(UNEXPECTED): scanned AP count=%d\n",
|
|
escan->bss_list->count);
|
|
wl_escan_notify_complete(dev, escan, false);
|
|
}
|
|
exit:
|
|
mutex_unlock(&escan->usr_sync);
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
wl_escan_prep(struct net_device *dev, struct wl_escan_info *escan,
|
|
wl_uint32_list_t *list, wl_scan_params_t *params, wlc_ssid_t *ssid, bool bcast)
|
|
{
|
|
int err = 0;
|
|
wl_scan_results_t *results;
|
|
s32 offset;
|
|
char *ptr;
|
|
int i = 0, j = 0;
|
|
wlc_ssid_t ssid_tmp;
|
|
u32 n_channels = 0;
|
|
uint channel;
|
|
chanspec_t chanspec;
|
|
u32 n_ssids;
|
|
|
|
results = wl_escan_get_buf(escan);
|
|
results->version = 0;
|
|
results->count = 0;
|
|
results->buflen = WL_SCAN_RESULTS_FIXED_SIZE;
|
|
escan->escan_state = ESCAN_STATE_SCANING;
|
|
|
|
/* Arm scan timeout timer */
|
|
mod_timer(&escan->scan_timeout, jiffies + msecs_to_jiffies(WL_ESCAN_TIMER_INTERVAL_MS));
|
|
|
|
memcpy(¶ms->bssid, ðer_bcast, ETHER_ADDR_LEN);
|
|
params->bss_type = DOT11_BSSTYPE_ANY;
|
|
params->scan_type = 0;
|
|
params->nprobes = -1;
|
|
params->active_time = -1;
|
|
params->passive_time = -1;
|
|
params->home_time = -1;
|
|
params->channel_num = 0;
|
|
|
|
params->nprobes = htod32(params->nprobes);
|
|
params->active_time = htod32(params->active_time);
|
|
params->passive_time = htod32(params->passive_time);
|
|
params->home_time = htod32(params->home_time);
|
|
|
|
n_channels = dtoh32(list->count);
|
|
/* Copy channel array if applicable */
|
|
ESCAN_SCAN(dev->name, "### List of channelspecs to scan ###\n");
|
|
if (n_channels > 0) {
|
|
for (i = 0; i < n_channels; i++) {
|
|
channel = dtoh32(list->element[i]);
|
|
if (!dhd_conf_match_channel(escan->pub, channel))
|
|
continue;
|
|
chanspec = WL_CHANSPEC_BW_20;
|
|
if (chanspec == INVCHANSPEC) {
|
|
ESCAN_ERROR(dev->name, "Invalid chanspec! Skipping channel\n");
|
|
continue;
|
|
}
|
|
if (channel <= CH_MAX_2G_CHANNEL) {
|
|
chanspec |= WL_CHANSPEC_BAND_2G;
|
|
} else {
|
|
chanspec |= WL_CHANSPEC_BAND_5G;
|
|
}
|
|
params->channel_list[j] = channel;
|
|
params->channel_list[j] &= WL_CHANSPEC_CHAN_MASK;
|
|
params->channel_list[j] |= chanspec;
|
|
ESCAN_SCAN(dev->name, "Chan : %d, Channel spec: %x\n",
|
|
channel, params->channel_list[j]);
|
|
params->channel_list[j] = wl_chspec_host_to_driver(escan->ioctl_ver,
|
|
params->channel_list[j]);
|
|
j++;
|
|
}
|
|
} else {
|
|
ESCAN_SCAN(dev->name, "Scanning all channels\n");
|
|
}
|
|
|
|
if (ssid && ssid->SSID_len) {
|
|
/* Copy ssid array if applicable */
|
|
ESCAN_SCAN(dev->name, "### List of SSIDs to scan ###\n");
|
|
offset = offsetof(wl_scan_params_t, channel_list) + n_channels * sizeof(u16);
|
|
offset = roundup(offset, sizeof(u32));
|
|
ptr = (char*)params + offset;
|
|
|
|
if (bcast) {
|
|
n_ssids = 2;
|
|
ESCAN_SCAN(dev->name, "0: Broadcast scan\n");
|
|
memset(&ssid_tmp, 0, sizeof(wlc_ssid_t));
|
|
ssid_tmp.SSID_len = 0;
|
|
memcpy(ptr, &ssid_tmp, sizeof(wlc_ssid_t));
|
|
ptr += sizeof(wlc_ssid_t);
|
|
} else {
|
|
n_ssids = 1;
|
|
}
|
|
|
|
memset(&ssid_tmp, 0, sizeof(wlc_ssid_t));
|
|
ssid_tmp.SSID_len = ssid->SSID_len;
|
|
memcpy(ssid_tmp.SSID, ssid->SSID, ssid->SSID_len);
|
|
memcpy(ptr, &ssid_tmp, sizeof(wlc_ssid_t));
|
|
ptr += sizeof(wlc_ssid_t);
|
|
ESCAN_SCAN(dev->name, "1: scan for %s size=%d\n",
|
|
ssid_tmp.SSID, ssid_tmp.SSID_len);
|
|
/* Adding mask to channel numbers */
|
|
params->channel_num =
|
|
htod32((n_ssids << WL_SCAN_PARAMS_NSSID_SHIFT) |
|
|
(n_channels & WL_SCAN_PARAMS_COUNT_MASK));
|
|
}
|
|
else {
|
|
ESCAN_SCAN(dev->name, "Broadcast scan\n");
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int
|
|
wl_escan_reset(struct wl_escan_info *escan)
|
|
{
|
|
if (timer_pending(&escan->scan_timeout))
|
|
del_timer_sync(&escan->scan_timeout);
|
|
escan->escan_state = ESCAN_STATE_IDLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
wl_escan_timeout(unsigned long data)
|
|
{
|
|
wl_event_msg_t msg;
|
|
struct wl_escan_info *escan = (struct wl_escan_info *)data;
|
|
struct wl_scan_results *bss_list;
|
|
struct wl_bss_info *bi = NULL;
|
|
s32 i;
|
|
u32 channel;
|
|
|
|
if (!escan->dev) {
|
|
ESCAN_ERROR("wlan", "No dev present\n");
|
|
return;
|
|
}
|
|
|
|
bss_list = wl_escan_get_buf(escan);
|
|
if (!bss_list) {
|
|
ESCAN_ERROR(escan->dev->name,
|
|
"bss_list is null. Didn't receive any partial scan results\n");
|
|
} else {
|
|
ESCAN_ERROR(escan->dev->name, "scanned AP count (%d)\n", bss_list->count);
|
|
bi = next_bss(bss_list, bi);
|
|
for_each_bss(bss_list, bi, i) {
|
|
channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(escan->ioctl_ver,
|
|
bi->chanspec));
|
|
ESCAN_ERROR(escan->dev->name, "SSID :%s Channel :%d\n", bi->SSID, channel);
|
|
}
|
|
}
|
|
|
|
bzero(&msg, sizeof(wl_event_msg_t));
|
|
ESCAN_ERROR(escan->dev->name, "timer expired\n");
|
|
|
|
msg.ifidx = dhd_net2idx(escan->pub->info, escan->dev);
|
|
msg.event_type = hton32(WLC_E_ESCAN_RESULT);
|
|
msg.status = hton32(WLC_E_STATUS_TIMEOUT);
|
|
msg.reason = 0xFFFFFFFF;
|
|
wl_ext_event_send(escan->pub->event_params, &msg, NULL);
|
|
}
|
|
|
|
int
|
|
wl_escan_set_scan(struct net_device *dev, dhd_pub_t *dhdp,
|
|
wlc_ssid_t *ssid, uint16 channel, bool bcast)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
s32 err = BCME_OK;
|
|
s32 params_size = (WL_SCAN_PARAMS_FIXED_SIZE + OFFSETOF(wl_escan_params_t, params));
|
|
wl_escan_params_t *params = NULL;
|
|
scb_val_t scbval;
|
|
static int cnt = 0;
|
|
u32 n_channels = 0;
|
|
wl_uint32_list_t *list;
|
|
u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
|
|
|
|
mutex_lock(&escan->usr_sync);
|
|
if (escan->escan_state == ESCAN_STATE_DOWN) {
|
|
ESCAN_ERROR(dev->name, "STATE is down\n");
|
|
err = -EINVAL;
|
|
goto exit2;
|
|
}
|
|
|
|
if (wl_ext_check_scan(dev, dhdp)) {
|
|
err = -EBUSY;
|
|
goto exit;
|
|
}
|
|
|
|
ESCAN_TRACE(dev->name, "Enter \n");
|
|
|
|
/* if scan request is not empty parse scan request paramters */
|
|
memset(valid_chan_list, 0, sizeof(valid_chan_list));
|
|
list = (wl_uint32_list_t *)(void *) valid_chan_list;
|
|
|
|
if (channel) {
|
|
list->count = htod32(1);
|
|
list->element[0] = htod32(channel);
|
|
} else {
|
|
list->count = htod32(WL_NUMCHANNELS);
|
|
err = wldev_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
|
|
sizeof(valid_chan_list), false);
|
|
if (err != 0) {
|
|
ESCAN_ERROR(dev->name, "get channels failed with %d\n", err);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
n_channels = dtoh32(list->count);
|
|
/* Allocate space for populating ssids in wl_escan_params_t struct */
|
|
if (dtoh32(list->count) % 2)
|
|
/* If n_channels is odd, add a padd of u16 */
|
|
params_size += sizeof(u16) * (n_channels + 1);
|
|
else
|
|
params_size += sizeof(u16) * n_channels;
|
|
if (ssid && ssid->SSID_len) {
|
|
params_size += sizeof(struct wlc_ssid) * 2;
|
|
}
|
|
|
|
params = (wl_escan_params_t *) kzalloc(params_size, GFP_KERNEL);
|
|
if (params == NULL) {
|
|
err = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
wl_escan_prep(dev, escan, list, ¶ms->params, ssid, bcast);
|
|
|
|
params->version = htod32(ESCAN_REQ_VERSION);
|
|
params->action = htod16(WL_SCAN_ACTION_START);
|
|
wl_escan_set_sync_id(params->sync_id);
|
|
if (params_size + sizeof("escan") >= WLC_IOCTL_MEDLEN) {
|
|
ESCAN_ERROR(dev->name, "ioctl buffer length not sufficient\n");
|
|
kfree(params);
|
|
err = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
params->params.scan_type = DOT11_SCANTYPE_ACTIVE;
|
|
ESCAN_SCAN(dev->name, "Passive scan_type %d\n", params->params.scan_type);
|
|
|
|
WL_MSG(dev->name, "LEGACY_SCAN\n");
|
|
err = wldev_iovar_setbuf(dev, "escan", params, params_size,
|
|
escan->escan_ioctl_buf, WLC_IOCTL_MEDLEN, NULL);
|
|
if (unlikely(err)) {
|
|
if (err == BCME_EPERM)
|
|
/* Scan Not permitted at this point of time */
|
|
ESCAN_TRACE(dev->name, "Escan not permitted at this time (%d)\n", err);
|
|
else
|
|
ESCAN_ERROR(dev->name, "Escan set error (%d)\n", err);
|
|
}
|
|
kfree(params);
|
|
|
|
if (unlikely(err)) {
|
|
/* Don't print Error incase of Scan suppress */
|
|
if (err == BCME_EPERM)
|
|
ESCAN_TRACE(dev->name, "Escan failed: Scan Suppressed\n");
|
|
else {
|
|
cnt++;
|
|
ESCAN_ERROR(dev->name, "error (%d), cnt=%d\n", err, cnt);
|
|
// terence 20140111: send disassoc to firmware
|
|
if (cnt >= 4) {
|
|
memset(&scbval, 0, sizeof(scb_val_t));
|
|
wldev_ioctl(dev, WLC_DISASSOC, &scbval, sizeof(scb_val_t), true);
|
|
ESCAN_ERROR(dev->name, "Send disassoc to break the busy\n");
|
|
cnt = 0;
|
|
}
|
|
}
|
|
} else {
|
|
cnt = 0;
|
|
escan->dev = dev;
|
|
}
|
|
exit:
|
|
if (unlikely(err)) {
|
|
wl_escan_reset(escan);
|
|
}
|
|
exit2:
|
|
mutex_unlock(&escan->usr_sync);
|
|
return err;
|
|
}
|
|
|
|
#if defined(WL_WIRELESS_EXT)
|
|
static int
|
|
rssi_to_qual(int rssi)
|
|
{
|
|
if (rssi <= WL_IW_RSSI_NO_SIGNAL)
|
|
return 0;
|
|
else if (rssi <= WL_IW_RSSI_VERY_LOW)
|
|
return 1;
|
|
else if (rssi <= WL_IW_RSSI_LOW)
|
|
return 2;
|
|
else if (rssi <= WL_IW_RSSI_GOOD)
|
|
return 3;
|
|
else if (rssi <= WL_IW_RSSI_VERY_GOOD)
|
|
return 4;
|
|
else
|
|
return 5;
|
|
}
|
|
|
|
static int
|
|
wl_escan_merge_scan_results(struct net_device *dev, struct wl_escan_info *escan,
|
|
struct iw_request_info *info, char *extra, wl_bss_info_t *bi, int *len, int max_size)
|
|
{
|
|
s32 err = BCME_OK;
|
|
struct iw_event iwe;
|
|
int j;
|
|
char *event = extra, *end = extra + max_size - WE_ADD_EVENT_FIX, *value;
|
|
int16 rssi;
|
|
int channel;
|
|
chanspec_t chanspec;
|
|
|
|
/* overflow check cover fields before wpa IEs */
|
|
if (event + ETHER_ADDR_LEN + bi->SSID_len + IW_EV_UINT_LEN + IW_EV_FREQ_LEN +
|
|
IW_EV_QUAL_LEN >= end) {
|
|
err = -E2BIG;
|
|
goto exit;
|
|
}
|
|
|
|
#if defined(RSSIAVG)
|
|
rssi = wl_get_avg_rssi(&escan->g_rssi_cache_ctrl, &bi->BSSID);
|
|
if (rssi == RSSI_MINVAL)
|
|
rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
|
|
#else
|
|
// terence 20150419: limit the max. rssi to -2 or the bss will be filtered out in android OS
|
|
rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
|
|
#endif
|
|
chanspec = wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec);
|
|
channel = wf_chspec_ctlchan(chanspec);
|
|
ESCAN_SCAN(dev->name, "BSSID %pM, channel %3d(%3d %sMHz), rssi %3d, SSID \"%s\"\n",
|
|
&bi->BSSID, channel, CHSPEC_CHANNEL(chanspec),
|
|
CHSPEC_IS20(chanspec)?"20":
|
|
CHSPEC_IS40(chanspec)?"40":
|
|
CHSPEC_IS80(chanspec)?"80":"160",
|
|
rssi, bi->SSID);
|
|
|
|
/* First entry must be the BSSID */
|
|
iwe.cmd = SIOCGIWAP;
|
|
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
|
|
memcpy(iwe.u.ap_addr.sa_data, &bi->BSSID, ETHER_ADDR_LEN);
|
|
event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_ADDR_LEN);
|
|
|
|
/* SSID */
|
|
iwe.u.data.length = dtoh32(bi->SSID_len);
|
|
iwe.cmd = SIOCGIWESSID;
|
|
iwe.u.data.flags = 1;
|
|
event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, bi->SSID);
|
|
|
|
/* Mode */
|
|
if (dtoh16(bi->capability) & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
|
|
iwe.cmd = SIOCGIWMODE;
|
|
if (dtoh16(bi->capability) & DOT11_CAP_ESS)
|
|
iwe.u.mode = IW_MODE_INFRA;
|
|
else
|
|
iwe.u.mode = IW_MODE_ADHOC;
|
|
event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_UINT_LEN);
|
|
}
|
|
|
|
/* Channel */
|
|
iwe.cmd = SIOCGIWFREQ;
|
|
#if 1
|
|
iwe.u.freq.m = wf_channel2mhz(channel, channel <= CH_MAX_2G_CHANNEL ?
|
|
WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
|
|
#else
|
|
iwe.u.freq.m = wf_channel2mhz(bi->n_cap ?
|
|
bi->ctl_ch : CHSPEC_CHANNEL(bi->chanspec),
|
|
CHSPEC_CHANNEL(bi->chanspec) <= CH_MAX_2G_CHANNEL ?
|
|
WF_CHAN_FACTOR_2_4_G : WF_CHAN_FACTOR_5_G);
|
|
#endif
|
|
iwe.u.freq.e = 6;
|
|
event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_FREQ_LEN);
|
|
|
|
/* Channel quality */
|
|
iwe.cmd = IWEVQUAL;
|
|
iwe.u.qual.qual = rssi_to_qual(rssi);
|
|
iwe.u.qual.level = 0x100 + rssi;
|
|
iwe.u.qual.noise = 0x100 + bi->phy_noise;
|
|
event = IWE_STREAM_ADD_EVENT(info, event, end, &iwe, IW_EV_QUAL_LEN);
|
|
|
|
wl_iw_handle_scanresults_ies(&event, end, info, bi);
|
|
|
|
/* Encryption */
|
|
iwe.cmd = SIOCGIWENCODE;
|
|
if (dtoh16(bi->capability) & DOT11_CAP_PRIVACY)
|
|
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
|
|
else
|
|
iwe.u.data.flags = IW_ENCODE_DISABLED;
|
|
iwe.u.data.length = 0;
|
|
event = IWE_STREAM_ADD_POINT(info, event, end, &iwe, (char *)event);
|
|
|
|
/* Rates */
|
|
if (bi->rateset.count <= sizeof(bi->rateset.rates)) {
|
|
if (event + IW_MAX_BITRATES*IW_EV_PARAM_LEN >= end) {
|
|
err = -E2BIG;
|
|
goto exit;
|
|
}
|
|
value = event + IW_EV_LCP_LEN;
|
|
iwe.cmd = SIOCGIWRATE;
|
|
/* Those two flags are ignored... */
|
|
iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
|
|
for (j = 0; j < bi->rateset.count && j < IW_MAX_BITRATES; j++) {
|
|
iwe.u.bitrate.value = (bi->rateset.rates[j] & 0x7f) * 500000;
|
|
value = IWE_STREAM_ADD_VALUE(info, event, value, end, &iwe,
|
|
IW_EV_PARAM_LEN);
|
|
}
|
|
event = value;
|
|
}
|
|
*len = event - extra;
|
|
if (*len < 0)
|
|
ESCAN_ERROR(dev->name, "==> Wrong size\n");
|
|
|
|
exit:
|
|
return err;
|
|
}
|
|
|
|
int
|
|
wl_escan_get_scan(struct net_device *dev, dhd_pub_t *dhdp,
|
|
struct iw_request_info *info, struct iw_point *dwrq, char *extra)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
s32 err = BCME_OK;
|
|
int i = 0;
|
|
int len_prep = 0, len_ret = 0;
|
|
wl_bss_info_t *bi = NULL;
|
|
struct wl_scan_results *bss_list;
|
|
__u16 buflen_from_user = dwrq->length;
|
|
#if defined(BSSCACHE)
|
|
wl_bss_cache_t *node;
|
|
#endif
|
|
char *buf = NULL;
|
|
struct ether_addr cur_bssid;
|
|
u8 ioctl_buf[WLC_IOCTL_SMLEN];
|
|
|
|
if (!extra) {
|
|
ESCAN_TRACE(dev->name, "extra is null\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&escan->usr_sync);
|
|
|
|
/* Check for scan in progress */
|
|
if (escan->escan_state == ESCAN_STATE_SCANING) {
|
|
ESCAN_DBG(dev->name, "SIOCGIWSCAN GET still scanning\n");
|
|
err = -EAGAIN;
|
|
goto exit;
|
|
}
|
|
if (!escan->bss_list) {
|
|
ESCAN_ERROR(dev->name, "scan not ready\n");
|
|
err = -EAGAIN;
|
|
goto exit;
|
|
}
|
|
if (dev != escan->dev) {
|
|
ESCAN_ERROR(dev->name, "not my scan from %s\n", escan->dev->name);
|
|
err = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
ESCAN_SCAN(dev->name, "SIOCGIWSCAN, len=%d\n", dwrq->length);
|
|
|
|
wldev_iovar_getbuf(dev, "cur_etheraddr", NULL, 0, ioctl_buf, WLC_IOCTL_SMLEN, NULL);
|
|
err = wldev_ioctl(dev, WLC_GET_BSSID, &cur_bssid, sizeof(cur_bssid), false);
|
|
if (err != BCME_NOTASSOCIATED &&
|
|
memcmp(ðer_null, &cur_bssid, ETHER_ADDR_LEN) &&
|
|
memcmp(ioctl_buf, &cur_bssid, ETHER_ADDR_LEN)) {
|
|
// merge current connected bss
|
|
buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_ATOMIC);
|
|
if (!buf) {
|
|
ESCAN_ERROR(dev->name, "buffer alloc failed.\n");
|
|
err = BCME_NOMEM;
|
|
goto exit;
|
|
}
|
|
*(u32 *)buf = htod32(WL_EXTRA_BUF_MAX);
|
|
err = wldev_ioctl(dev, WLC_GET_BSS_INFO, buf, WL_EXTRA_BUF_MAX, false);
|
|
if (unlikely(err)) {
|
|
ESCAN_ERROR(dev->name, "Could not get bss info %d\n", err);
|
|
goto exit;
|
|
}
|
|
bi = (struct wl_bss_info *)(buf + 4);
|
|
len_prep = 0;
|
|
err = wl_escan_merge_scan_results(dev, escan, info, extra+len_ret, bi,
|
|
&len_prep, buflen_from_user-len_ret);
|
|
len_ret += len_prep;
|
|
if (err)
|
|
goto exit;
|
|
bi = NULL;
|
|
}
|
|
|
|
#if defined(BSSCACHE)
|
|
bss_list = &escan->g_bss_cache_ctrl.m_cache_head->results;
|
|
node = escan->g_bss_cache_ctrl.m_cache_head;
|
|
for (i=0; node && i<IW_MAX_AP; i++)
|
|
#else
|
|
bss_list = escan->bss_list;
|
|
bi = next_bss(bss_list, bi);
|
|
for_each_bss(bss_list, bi, i)
|
|
#endif
|
|
{
|
|
#if defined(BSSCACHE)
|
|
bi = node->results.bss_info;
|
|
#endif
|
|
if (!memcmp(&bi->BSSID, &cur_bssid, ETHER_ADDR_LEN)) {
|
|
ESCAN_SCAN(dev->name, "skip connected AP %pM\n", &cur_bssid);
|
|
#if defined(BSSCACHE)
|
|
node = node->next;
|
|
#endif
|
|
continue;
|
|
}
|
|
len_prep = 0;
|
|
err = wl_escan_merge_scan_results(dev, escan, info, extra+len_ret, bi,
|
|
&len_prep, buflen_from_user-len_ret);
|
|
len_ret += len_prep;
|
|
if (err)
|
|
goto exit;
|
|
#if defined(BSSCACHE)
|
|
node = node->next;
|
|
#endif
|
|
}
|
|
|
|
if ((len_ret + WE_ADD_EVENT_FIX) < dwrq->length)
|
|
dwrq->length = len_ret;
|
|
|
|
dwrq->flags = 0; /* todo */
|
|
ESCAN_SCAN(dev->name, "scanned AP count (%d)\n", i);
|
|
exit:
|
|
kfree(buf);
|
|
dwrq->length = len_ret;
|
|
mutex_unlock(&escan->usr_sync);
|
|
return err;
|
|
}
|
|
#endif /* WL_WIRELESS_EXT */
|
|
|
|
#ifdef WLMESH
|
|
bool
|
|
wl_escan_meshid_ie(u8 *parse, u32 len, wlc_ssid_t *mesh_id)
|
|
{
|
|
bcm_tlv_t *ie;
|
|
|
|
if((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_MESH_ID)) != NULL) {
|
|
mesh_id->SSID_len = ie->len;
|
|
if (ie->len) {
|
|
strncpy(mesh_id->SSID, ie->data, ie->len);
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool
|
|
wl_escan_rsn_ie(u8 *parse, u32 len)
|
|
{
|
|
if (bcm_parse_tlvs(parse, (u32)len, DOT11_MNG_RSN_ID)) {
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
bool
|
|
wl_escan_mesh_info_ie(struct net_device *dev, u8 *parse, u32 len,
|
|
struct wl_mesh_params *mesh_info)
|
|
{
|
|
bcm_tlv_t *ie;
|
|
uchar mesh_oui[]={0x00, 0x22, 0xf4};
|
|
int totl_len;
|
|
uint8 *pie;
|
|
uint max_len;
|
|
bool found = FALSE;
|
|
|
|
memset(mesh_info, 0, sizeof(struct wl_mesh_params));
|
|
if((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID)) != NULL) {
|
|
totl_len = ie->len;
|
|
if (!memcmp(ie->data, &mesh_oui, sizeof(mesh_oui))) {
|
|
pie = ie->data + sizeof(mesh_oui);
|
|
ie = (bcm_tlv_t *)pie;
|
|
totl_len -= sizeof(mesh_oui);
|
|
while (totl_len > 2 && ie->len) {
|
|
if (ie->id == MESH_INFO_MASTER_BSSID && ie->len == ETHER_ADDR_LEN) {
|
|
memcpy(&mesh_info->master_bssid, ie->data, ETHER_ADDR_LEN);
|
|
} else if (ie->id == MESH_INFO_MASTER_CHANNEL) {
|
|
mesh_info->master_channel = ie->data[0];
|
|
found = TRUE;
|
|
} else if (ie->id == MESH_INFO_HOP_CNT) {
|
|
mesh_info->hop_cnt = ie->data[0];
|
|
} else if (ie->id == MESH_INFO_PEER_BSSID) {
|
|
max_len = min(MAX_HOP_LIST*ETHER_ADDR_LEN, (int)ie->len);
|
|
memcpy(mesh_info->peer_bssid, ie->data, max_len);
|
|
}
|
|
totl_len -= (ie->len + 2);
|
|
pie = ie->data + ie->len;
|
|
ie = (bcm_tlv_t *)pie;
|
|
}
|
|
}
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
bool
|
|
wl_escan_mesh_info(struct net_device *dev, struct wl_escan_info *escan,
|
|
struct ether_addr *peer_bssid, struct wl_mesh_params *mesh_info)
|
|
{
|
|
int i = 0;
|
|
wl_bss_info_t *bi = NULL;
|
|
struct wl_scan_results *bss_list;
|
|
int16 bi_rssi, bi_chan;
|
|
wlc_ssid_t bi_meshid;
|
|
bool is_mesh_peer = FALSE, found = FALSE;
|
|
struct wl_mesh_params peer_mesh_info;
|
|
|
|
mutex_lock(&escan->usr_sync);
|
|
|
|
/* Check for scan in progress */
|
|
if (escan->escan_state == ESCAN_STATE_SCANING) {
|
|
ESCAN_ERROR(dev->name, "SIOCGIWSCAN GET still scanning\n");
|
|
goto exit;
|
|
}
|
|
if (!escan->bss_list) {
|
|
ESCAN_ERROR(dev->name, "scan not ready\n");
|
|
goto exit;
|
|
}
|
|
if (dev != escan->dev) {
|
|
ESCAN_ERROR(dev->name, "not my scan from %s\n", escan->dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
bss_list = escan->bss_list;
|
|
bi = next_bss(bss_list, bi);
|
|
ESCAN_SCAN(dev->name, "scanned AP/Mesh count (%d)\n", bss_list->count);
|
|
for_each_bss(bss_list, bi, i)
|
|
{
|
|
memset(&bi_meshid, 0, sizeof(bi_meshid));
|
|
is_mesh_peer = FALSE;
|
|
bi_chan = wf_chspec_ctlchan(
|
|
wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
|
|
bi_rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
|
|
is_mesh_peer = wl_escan_meshid_ie(((u8*)bi)+bi->ie_offset,
|
|
bi->ie_length, &bi_meshid);
|
|
if (!(bi->capability & (DOT11_CAP_ESS|DOT11_CAP_IBSS)) && is_mesh_peer) {
|
|
bool bi_sae = FALSE, bss_found = FALSE, prefer = FALSE;
|
|
if (!memcmp(peer_bssid, &bi->BSSID, ETHER_ADDR_LEN)) {
|
|
bi_sae = wl_escan_rsn_ie(((u8*)bi)+bi->ie_offset, bi->ie_length);
|
|
bss_found = wl_escan_mesh_info_ie(dev, ((u8*)bi)+bi->ie_offset,
|
|
bi->ie_length, &peer_mesh_info);
|
|
if (bss_found) {
|
|
memcpy(&mesh_info->master_bssid, &peer_mesh_info.master_bssid,
|
|
ETHER_ADDR_LEN);
|
|
mesh_info->master_channel = peer_mesh_info.master_channel;
|
|
mesh_info->hop_cnt = peer_mesh_info.hop_cnt;
|
|
memcpy(mesh_info->peer_bssid, peer_mesh_info.peer_bssid,
|
|
sizeof(peer_mesh_info.peer_bssid));
|
|
prefer = TRUE;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
ESCAN_SCAN(dev->name,
|
|
"%s[Mesh] BSSID=%pM, channel=%d, RSSI=%d, sec=%s, "
|
|
"mbssid=%pM, mchannel=%d, hop=%d, pbssid=%pM, MeshID=\"%s\"\n",
|
|
prefer?"*":" ", &bi->BSSID, bi_chan, bi_rssi, bi_sae?"SAE":"OPEN",
|
|
&peer_mesh_info.master_bssid, peer_mesh_info.master_channel,
|
|
peer_mesh_info.hop_cnt, &peer_mesh_info.peer_bssid, bi_meshid.SSID);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
mutex_unlock(&escan->usr_sync);
|
|
return found;
|
|
}
|
|
|
|
bool
|
|
wl_escan_mesh_peer(struct net_device *dev, struct wl_escan_info *escan,
|
|
wlc_ssid_t *cur_ssid, uint16 cur_chan, bool sae,
|
|
struct wl_mesh_params *mesh_info)
|
|
{
|
|
int i = 0;
|
|
wl_bss_info_t *bi = NULL;
|
|
struct wl_scan_results *bss_list;
|
|
int16 bi_rssi, bi_chan, max_rssi = -100;
|
|
uint min_hop_cnt = 255;
|
|
wlc_ssid_t bi_meshid;
|
|
bool is_mesh_peer = FALSE, chan_matched = FALSE, found = FALSE;
|
|
struct wl_mesh_params peer_mesh_info;
|
|
|
|
mutex_lock(&escan->usr_sync);
|
|
|
|
/* Check for scan in progress */
|
|
if (escan->escan_state == ESCAN_STATE_SCANING) {
|
|
ESCAN_ERROR(dev->name, "SIOCGIWSCAN GET still scanning\n");
|
|
goto exit;
|
|
}
|
|
if (!escan->bss_list) {
|
|
ESCAN_ERROR(dev->name, "scan not ready\n");
|
|
goto exit;
|
|
}
|
|
if (dev != escan->dev) {
|
|
ESCAN_ERROR(dev->name, "not my scan from %s\n", escan->dev->name);
|
|
goto exit;
|
|
}
|
|
|
|
bss_list = escan->bss_list;
|
|
bi = next_bss(bss_list, bi);
|
|
ESCAN_SCAN(dev->name, "scanned AP/Mesh count (%d)\n", bss_list->count);
|
|
for_each_bss(bss_list, bi, i)
|
|
{
|
|
memset(&bi_meshid, 0, sizeof(bi_meshid));
|
|
is_mesh_peer = FALSE;
|
|
bi_chan = wf_chspec_ctlchan(
|
|
wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
|
|
bi_rssi = MIN(dtoh16(bi->RSSI), RSSI_MAXVAL);
|
|
is_mesh_peer = wl_escan_meshid_ie(((u8*)bi)+bi->ie_offset,
|
|
bi->ie_length, &bi_meshid);
|
|
if (!(bi->capability & (DOT11_CAP_ESS|DOT11_CAP_IBSS)) && is_mesh_peer) {
|
|
bool meshid_matched = FALSE, sec_matched = FALSE, bi_sae = FALSE,
|
|
bss_found = FALSE, prefer = FALSE;
|
|
|
|
if (cur_ssid->SSID_len && cur_ssid->SSID_len == bi_meshid.SSID_len &&
|
|
!memcmp(cur_ssid->SSID, bi_meshid.SSID, bi_meshid.SSID_len))
|
|
meshid_matched = TRUE;
|
|
|
|
bi_sae = wl_escan_rsn_ie(((u8*)bi)+bi->ie_offset, bi->ie_length);
|
|
if (bi_sae == sae)
|
|
sec_matched = TRUE;
|
|
|
|
bss_found = wl_escan_mesh_info_ie(dev, ((u8*)bi)+bi->ie_offset, bi->ie_length,
|
|
&peer_mesh_info);
|
|
if (meshid_matched && sec_matched && bss_found &&
|
|
(cur_chan == bi_chan)) {
|
|
if (peer_mesh_info.hop_cnt < min_hop_cnt) {
|
|
memcpy(&mesh_info->master_bssid, &peer_mesh_info.master_bssid,
|
|
ETHER_ADDR_LEN);
|
|
mesh_info->master_channel = peer_mesh_info.master_channel;
|
|
mesh_info->hop_cnt = peer_mesh_info.hop_cnt;
|
|
memcpy(mesh_info->peer_bssid, peer_mesh_info.peer_bssid,
|
|
sizeof(peer_mesh_info.peer_bssid));
|
|
min_hop_cnt = peer_mesh_info.hop_cnt;
|
|
prefer = TRUE;
|
|
chan_matched = TRUE;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
else if (meshid_matched && sec_matched && bss_found &&
|
|
(cur_chan != bi_chan) && !chan_matched) {
|
|
if (bi_rssi > max_rssi) {
|
|
memcpy(&mesh_info->master_bssid, &peer_mesh_info.master_bssid,
|
|
ETHER_ADDR_LEN);
|
|
mesh_info->master_channel = peer_mesh_info.master_channel;
|
|
mesh_info->hop_cnt = peer_mesh_info.hop_cnt;
|
|
memcpy(mesh_info->peer_bssid, peer_mesh_info.peer_bssid,
|
|
sizeof(peer_mesh_info.peer_bssid));
|
|
max_rssi = bi_rssi;
|
|
prefer = TRUE;
|
|
found = TRUE;
|
|
}
|
|
}
|
|
|
|
ESCAN_SCAN(dev->name,
|
|
"%s[Mesh] BSSID=%pM, channel=%d, RSSI=%d, sec=%s, "
|
|
"mbssid=%pM, mchannel=%d, hop=%d, pbssid=%pM, MeshID=\"%s\"\n",
|
|
prefer?"*":" ", &bi->BSSID, bi_chan, bi_rssi, bi_sae?"SAE":"OPEN",
|
|
&peer_mesh_info.master_bssid, peer_mesh_info.master_channel,
|
|
peer_mesh_info.hop_cnt, &peer_mesh_info.peer_bssid, bi_meshid.SSID);
|
|
} else {
|
|
ESCAN_SCAN(dev->name,
|
|
"[AP] BSSID=%pM, channel=%d, RSSI=%d, SSID=\"%s\"\n",
|
|
&bi->BSSID, bi_chan, bi_rssi, bi->SSID);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
mutex_unlock(&escan->usr_sync);
|
|
return found;
|
|
}
|
|
#endif /* WLMESH */
|
|
|
|
static void
|
|
wl_escan_deinit(struct net_device *dev, struct wl_escan_info *escan)
|
|
{
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
|
|
del_timer_sync(&escan->scan_timeout);
|
|
escan->escan_state = ESCAN_STATE_DOWN;
|
|
|
|
#if defined(RSSIAVG)
|
|
wl_free_rssi_cache(&escan->g_rssi_cache_ctrl);
|
|
#endif
|
|
#if defined(BSSCACHE)
|
|
wl_free_bss_cache(&escan->g_bss_cache_ctrl);
|
|
#endif
|
|
}
|
|
|
|
static s32
|
|
wl_escan_init(struct net_device *dev, struct wl_escan_info *escan)
|
|
{
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
|
|
/* Init scan_timeout timer */
|
|
init_timer_compat(&escan->scan_timeout, wl_escan_timeout, escan);
|
|
escan->escan_state = ESCAN_STATE_IDLE;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
wl_escan_down(struct net_device *dev, dhd_pub_t *dhdp)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
if (!escan) {
|
|
ESCAN_ERROR(dev->name, "escan is NULL\n");
|
|
return;
|
|
}
|
|
|
|
wl_escan_deinit(dev, escan);
|
|
}
|
|
|
|
int
|
|
wl_escan_up(struct net_device *dev, dhd_pub_t *dhdp)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
s32 val = 0;
|
|
int ret = -1;
|
|
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
if (!escan) {
|
|
ESCAN_ERROR(dev->name, "escan is NULL\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = wl_escan_init(dev, escan);
|
|
if (ret) {
|
|
ESCAN_ERROR(dev->name, "wl_escan_init ret %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (!escan->ioctl_ver) {
|
|
val = 1;
|
|
if ((ret = wldev_ioctl(dev, WLC_GET_VERSION, &val, sizeof(int), false) < 0)) {
|
|
ESCAN_ERROR(dev->name, "WLC_GET_VERSION failed, ret=%d\n", ret);
|
|
return ret;
|
|
}
|
|
val = dtoh32(val);
|
|
if (val != WLC_IOCTL_VERSION && val != 1) {
|
|
ESCAN_ERROR(dev->name,
|
|
"Version mismatch, please upgrade. Got %d, expected %d or 1\n",
|
|
val, WLC_IOCTL_VERSION);
|
|
return ret;
|
|
}
|
|
escan->ioctl_ver = val;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
wl_escan_event_dettach(struct net_device *dev, dhd_pub_t *dhdp)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
int ret = -1;
|
|
|
|
if (!escan) {
|
|
ESCAN_ERROR(dev->name, "escan is NULL\n");
|
|
return ret;
|
|
}
|
|
|
|
wl_ext_event_deregister(dev, dhdp, WLC_E_ESCAN_RESULT, wl_escan_handler);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
wl_escan_event_attach(struct net_device *dev, dhd_pub_t *dhdp)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
int ret = -1;
|
|
|
|
if (!escan) {
|
|
ESCAN_ERROR(dev->name, "escan is NULL\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = wl_ext_event_register(dev, dhdp, WLC_E_ESCAN_RESULT, wl_escan_handler,
|
|
escan, PRIO_EVENT_ESCAN);
|
|
if (ret) {
|
|
ESCAN_ERROR(dev->name, "wl_ext_event_register err %d\n", ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void
|
|
wl_escan_detach(struct net_device *dev, dhd_pub_t *dhdp)
|
|
{
|
|
struct wl_escan_info *escan = dhdp->escan;
|
|
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
|
|
if (!escan)
|
|
return;
|
|
|
|
wl_escan_deinit(dev, escan);
|
|
if (escan->escan_ioctl_buf) {
|
|
kfree(escan->escan_ioctl_buf);
|
|
escan->escan_ioctl_buf = NULL;
|
|
}
|
|
wl_ext_event_deregister(dev, dhdp, WLC_E_ESCAN_RESULT, wl_escan_handler);
|
|
|
|
DHD_OS_PREFREE(dhdp, escan, sizeof(struct wl_escan_info));
|
|
dhdp->escan = NULL;
|
|
}
|
|
|
|
int
|
|
wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp)
|
|
{
|
|
struct wl_escan_info *escan = NULL;
|
|
int ret = 0;
|
|
|
|
ESCAN_TRACE(dev->name, "Enter\n");
|
|
|
|
escan = (struct wl_escan_info *)DHD_OS_PREALLOC(dhdp,
|
|
DHD_PREALLOC_WL_ESCAN, sizeof(struct wl_escan_info));
|
|
if (!escan)
|
|
return -ENOMEM;
|
|
memset(escan, 0, sizeof(struct wl_escan_info));
|
|
|
|
dhdp->escan = escan;
|
|
|
|
/* we only care about main interface so save a global here */
|
|
escan->pub = dhdp;
|
|
escan->escan_state = ESCAN_STATE_DOWN;
|
|
|
|
escan->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
|
|
if (unlikely(!escan->escan_ioctl_buf)) {
|
|
ESCAN_ERROR(dev->name, "Ioctl buf alloc failed\n");
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
ret = wl_escan_init(dev, escan);
|
|
if (ret) {
|
|
ESCAN_ERROR(dev->name, "wl_escan_init err %d\n", ret);
|
|
goto exit;
|
|
}
|
|
mutex_init(&escan->usr_sync);
|
|
|
|
return 0;
|
|
|
|
exit:
|
|
wl_escan_detach(dev, dhdp);
|
|
return ret;
|
|
}
|
|
|
|
#endif /* WL_ESCAN */
|
|
|