blob: 03a09d1350b2f12920bd26c25fe232730ebe9615 [file] [log] [blame]
#if defined(WL_ESCAN)
#include <typedefs.h>
#include <linuxver.h>
#include <osl.h>
#include <dngl_stats.h>
#include <dhd.h>
#include <bcmutils.h>
#include <bcmendian.h>
#include <ethernet.h>
#include <linux/if_arp.h>
#include <asm/uaccess.h>
#include <wlioctl.h>
#include <wl_android.h>
#include <wl_iw.h>
#include <wl_escan.h>
#include <dhd_config.h>
/* message levels */
#define ESCAN_ERROR_LEVEL 0x0001
#define ESCAN_SCAN_LEVEL 0x0002
#define ESCAN_TRACE_LEVEL 0x0004
#define ESCAN_ERROR(x) \
do { \
if (iw_msg_level & ESCAN_ERROR_LEVEL) { \
printf(KERN_ERR "ESCAN-ERROR) %s : ", __func__); \
printf x; \
} \
} while (0)
#define ESCAN_SCAN(x) \
do { \
if (iw_msg_level & ESCAN_SCAN_LEVEL) { \
printf(KERN_ERR "ESCAN-SCAN) %s : ", __func__); \
printf x; \
} \
} while (0)
#define ESCAN_TRACE(x) \
do { \
if (iw_msg_level & ESCAN_TRACE_LEVEL) { \
printf(KERN_ERR "ESCAN-TRACE) %s : ", __func__); \
printf x; \
} \
} 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_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 */
struct wl_escan_info *g_escan = NULL;
/* 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(("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(("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((
"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, s32 bssidx, 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;
}
#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
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;
}
#if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
4 && __GNUC_MINOR__ >= 6))
#define BCM_SET_LIST_FIRST_ENTRY(entry, ptr, type, member) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
(entry) = list_first_entry((ptr), type, member); \
_Pragma("GCC diagnostic pop") \
#define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wcast-qual\"") \
entry = container_of((ptr), type, member); \
_Pragma("GCC diagnostic pop") \
#else
#define BCM_SET_LIST_FIRST_ENTRY(entry, ptr, type, member) \
(entry) = list_first_entry((ptr), type, member); \
#define BCM_SET_CONTAINER_OF(entry, ptr, type, member) \
entry = container_of((ptr), type, member); \
#endif /* STRICT_GCC_WARNINGS */
static unsigned long wl_lock_eq(struct wl_escan_info *escan)
{
unsigned long flags;
spin_lock_irqsave(&escan->eq_lock, flags);
return flags;
}
static void wl_unlock_eq(struct wl_escan_info *escan, unsigned long flags)
{
spin_unlock_irqrestore(&escan->eq_lock, flags);
}
static void wl_init_eq(struct wl_escan_info *escan)
{
spin_lock_init(&escan->eq_lock);
INIT_LIST_HEAD(&escan->eq_list);
}
static void wl_flush_eq(struct wl_escan_info *escan)
{
struct escan_event_q *e;
unsigned long flags;
flags = wl_lock_eq(escan);
while (!list_empty_careful(&escan->eq_list)) {
BCM_SET_LIST_FIRST_ENTRY(e, &escan->eq_list, struct escan_event_q, eq_list);
list_del(&e->eq_list);
kfree(e);
}
wl_unlock_eq(escan, flags);
}
static struct escan_event_q *wl_deq_event(struct wl_escan_info *escan)
{
struct escan_event_q *e = NULL;
unsigned long flags;
flags = wl_lock_eq(escan);
if (likely(!list_empty(&escan->eq_list))) {
BCM_SET_LIST_FIRST_ENTRY(e, &escan->eq_list, struct escan_event_q, eq_list);
list_del(&e->eq_list);
}
wl_unlock_eq(escan, flags);
return e;
}
/*
* push event to tail of the queue
*/
static s32
wl_enq_event(struct wl_escan_info *escan, struct net_device *ndev, u32 event,
const wl_event_msg_t *msg, void *data)
{
struct escan_event_q *e;
s32 err = 0;
uint32 evtq_size;
uint32 data_len;
unsigned long flags;
gfp_t aflags;
data_len = 0;
if (data)
data_len = ntoh32(msg->datalen);
evtq_size = sizeof(struct escan_event_q) + data_len;
aflags = (in_atomic()) ? GFP_ATOMIC : GFP_KERNEL;
e = kzalloc(evtq_size, aflags);
if (unlikely(!e)) {
ESCAN_ERROR(("event alloc failed\n"));
return -ENOMEM;
}
e->etype = event;
memcpy(&e->emsg, msg, sizeof(wl_event_msg_t));
if (data)
memcpy(e->edata, data, data_len);
flags = wl_lock_eq(escan);
list_add_tail(&e->eq_list, &escan->eq_list);
wl_unlock_eq(escan, flags);
return err;
}
static void wl_put_event(struct escan_event_q *e)
{
kfree(e);
}
static void wl_wakeup_event(struct wl_escan_info *escan)
{
dhd_pub_t *dhd = (dhd_pub_t *)(escan->pub);
if (dhd->up && (escan->event_tsk.thr_pid >= 0)) {
up(&escan->event_tsk.sema);
}
}
static s32 wl_escan_event_handler(void *data)
{
struct wl_escan_info *escan = NULL;
struct escan_event_q *e;
tsk_ctl_t *tsk = (tsk_ctl_t *)data;
escan = (struct wl_escan_info *)tsk->parent;
printf("tsk Enter, tsk = 0x%p\n", tsk);
while (down_interruptible (&tsk->sema) == 0) {
SMP_RD_BARRIER_DEPENDS();
if (tsk->terminated) {
break;
}
while (escan && (e = wl_deq_event(escan))) {
ESCAN_TRACE(("dev=%p, event type (%d), ifidx: %d bssidx: %d \n",
escan->dev, e->etype, e->emsg.ifidx, e->emsg.bsscfgidx));
if (e->emsg.ifidx > WL_MAX_IFS) {
ESCAN_ERROR(("Event ifidx not in range. val:%d \n", e->emsg.ifidx));
goto fail;
}
if (escan->dev && escan->evt_handler[e->etype]) {
dhd_pub_t *dhd = (struct dhd_pub *)(escan->pub);
if (dhd->busstate == DHD_BUS_DOWN) {
ESCAN_ERROR((": BUS is DOWN.\n"));
} else {
escan->evt_handler[e->etype](escan, &e->emsg, e->edata);
}
} else {
ESCAN_TRACE(("Unknown Event (%d): ignoring\n", e->etype));
}
fail:
wl_put_event(e);
DHD_EVENT_WAKE_UNLOCK(escan->pub);
}
}
printf("%s: was terminated\n", __FUNCTION__);
complete_and_exit(&tsk->completed, 0);
return 0;
}
void
wl_escan_event(struct net_device *dev, const wl_event_msg_t * e, void *data)
{
u32 event_type = ntoh32(e->event_type);
struct wl_escan_info *escan = g_escan;
if (!escan || !escan->dev) {
return;
}
if (escan->event_tsk.thr_pid == -1) {
ESCAN_ERROR(("Event handler is not created\n"));
return;
}
if (escan == NULL) {
ESCAN_ERROR(("Stale event ignored\n"));
return;
}
if (event_type == WLC_E_PFN_NET_FOUND) {
ESCAN_TRACE(("PNOEVENT: PNO_NET_FOUND\n"));
}
else if (event_type == WLC_E_PFN_NET_LOST) {
ESCAN_TRACE(("PNOEVENT: PNO_NET_LOST\n"));
}
DHD_EVENT_WAKE_LOCK(escan->pub);
if (likely(!wl_enq_event(escan, dev, event_type, e, data))) {
wl_wakeup_event(escan);
} else {
DHD_EVENT_WAKE_UNLOCK(escan->pub);
}
}
static s32 wl_escan_inform_bss(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;
/* 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(escan->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(escan->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(escan->dev, bss_list, escan->ioctl_ver,
&escan->best_2g_ch, &escan->best_5g_ch);
#endif
ESCAN_TRACE(("scanned AP count (%d)\n", bss_list->count));
return err;
}
static wl_scan_params_t *
wl_escan_alloc_params(struct wl_escan_info *escan, int channel,
int nprobes, int *out_params_size)
{
wl_scan_params_t *params;
int params_size;
int num_chans;
int bssidx = 0;
*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(("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(&params->bssid, &ether_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, bssidx, 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 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(escan, -1, 0, &params_size);
if (params == NULL) {
ESCAN_ERROR(("scan params allocation failed \n"));
err = -ENOMEM;
} else {
/* Do a scan abort to stop the driver's scan engine */
err = wldev_ioctl(escan->dev, WLC_SCAN, params, params_size, true);
if (err < 0) {
ESCAN_ERROR(("scan abort failed \n"));
}
kfree(params);
}
}
}
static s32 wl_notify_escan_complete(struct wl_escan_info *escan, bool fw_abort)
{
s32 err = BCME_OK;
int cmd = 0;
#if WIRELESS_EXT > 13
union iwreq_data wrqu;
char extra[IW_CUSTOM_MAX + 1];
memset(extra, 0, sizeof(extra));
#endif
ESCAN_TRACE(("Enter\n"));
if (!escan || !escan->dev) {
ESCAN_ERROR(("escan or dev is null\n"));
err = BCME_ERROR;
goto out;
}
if (fw_abort && !in_atomic())
wl_escan_abort(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(escan);
#endif /* ESCAN_RESULT_PATCH */
#if WIRELESS_EXT > 13
#if WIRELESS_EXT > 14
cmd = SIOCGIWSCAN;
#endif
ESCAN_TRACE(("event WLC_E_SCAN_COMPLETE\n"));
// terence 20150224: fix "wlan0: (WE) : Wireless Event too big (65306)"
memset(&wrqu, 0, sizeof(wrqu));
if (cmd) {
if (cmd == SIOCGIWSCAN) {
wireless_send_event(escan->dev, cmd, &wrqu, NULL);
} else
wireless_send_event(escan->dev, cmd, &wrqu, extra);
}
#endif
out:
return err;
}
#ifdef ESCAN_BUF_OVERFLOW_MGMT
static void
wl_cfg80211_find_removal_candidate(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_cfg80211_remove_lowRSSI_info(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_TRACE(("delete scan info of " MACDBG " to add new AP\n",
MAC2STRDBG(bss->BSSID.octet)));
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 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;
ESCAN_TRACE(("enter event type : %d, status : %d \n",
ntoh32(e->event_type), ntoh32(e->status)));
mutex_lock(&escan->usr_sync);
escan_result = (wl_escan_result_t *)data;
if (escan->escan_state != ESCAN_STATE_SCANING) {
ESCAN_TRACE(("Not my scan\n"));
goto exit;
}
if (status == WLC_E_STATUS_PARTIAL) {
ESCAN_TRACE(("WLC_E_STATUS_PARTIAL \n"));
if (!escan_result) {
ESCAN_ERROR(("Invalid escan result (NULL pointer)\n"));
goto exit;
}
if (dtoh16(escan_result->bss_count) != 1) {
ESCAN_ERROR(("Invalid bss_count %d: ignoring\n", escan_result->bss_count));
goto exit;
}
bi = escan_result->bss_info;
if (!bi) {
ESCAN_ERROR(("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(("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_TRACE(("%s("MACDBG") RSSI %d flags 0x%x length %d\n", bi->SSID,
MAC2STRDBG(bi->BSSID.octet), 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_TRACE(("%s("MACDBG"), i=%d bss: RSSI %d list->count %d\n",
bss->SSID, MAC2STRDBG(bss->BSSID.octet),
i, bss->RSSI, list->count));
if (remove_lower_rssi)
wl_cfg80211_find_removal_candidate(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_TRACE(("%s("MACDBG"), i=%d prev: RSSI %d"
" flags 0x%x, new: RSSI %d flags 0x%x\n",
bss->SSID, MAC2STRDBG(bi->BSSID.octet), 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_TRACE(("%s("MACDBG"), same onchan"
", RSSI: prev %d new %d\n",
bss->SSID, MAC2STRDBG(bi->BSSID.octet),
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_TRACE(("%s("MACDBG"), prev onchan"
", RSSI: prev %d new %d\n",
bss->SSID, MAC2STRDBG(bi->BSSID.octet),
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_TRACE(("bss info replacement"
" is occured(bcast:%d->probresp%d)\n",
bss->ie_length, bi->ie_length));
ESCAN_TRACE(("%s("MACDBG"), replacement!(%d -> %d)\n",
bss->SSID, MAC2STRDBG(bi->BSSID.octet),
prev_len, bi_length));
if (list->buflen - prev_len + bi_length
> ESCAN_BUF_SIZE) {
ESCAN_ERROR(("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_cfg80211_remove_lowRSSI_info(list, candidate, bi);
if (bi_length > ESCAN_BUF_SIZE - list->buflen) {
ESCAN_TRACE(("RSSI(" MACDBG ") is too low(%d) to add Buffer\n",
MAC2STRDBG(bi->BSSID.octet), bi->RSSI));
goto exit;
}
#else
ESCAN_ERROR(("Buffer is too small: ignoring\n"));
goto exit;
#endif /* ESCAN_BUF_OVERFLOW_MGMT */
}
if (strlen(bi->SSID) == 0) { // terence: fix for hidden SSID
ESCAN_SCAN(("Skip hidden SSID %pM\n", &bi->BSSID));
goto exit;
}
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->escan_state = ESCAN_STATE_IDLE;
ESCAN_TRACE(("ESCAN COMPLETED\n"));
escan->bss_list = wl_escan_get_buf(escan);
ESCAN_TRACE(("SCAN COMPLETED: scanned AP count=%d\n",
escan->bss_list->count));
wl_escan_inform_bss(escan);
wl_notify_escan_complete(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->escan_state = ESCAN_STATE_IDLE;
ESCAN_TRACE(("ESCAN ABORT reason: %d\n", status));
escan->bss_list = wl_escan_get_buf(escan);
ESCAN_TRACE(("SCAN ABORT: scanned AP count=%d\n",
escan->bss_list->count));
wl_escan_inform_bss(escan);
wl_notify_escan_complete(escan, false);
} else if (status == WLC_E_STATUS_TIMEOUT) {
ESCAN_ERROR(("WLC_E_STATUS_TIMEOUT\n"));
ESCAN_ERROR(("reason[0x%x]\n", e->reason));
if (e->reason == 0xFFFFFFFF) {
wl_notify_escan_complete(escan, true);
}
escan->escan_state = ESCAN_STATE_IDLE;
} else {
ESCAN_ERROR(("unexpected Escan Event %d : abort\n", status));
escan->escan_state = ESCAN_STATE_IDLE;
escan->bss_list = wl_escan_get_buf(escan);
ESCAN_TRACE(("SCAN ABORTED(UNEXPECTED): scanned AP count=%d\n",
escan->bss_list->count));
wl_escan_inform_bss(escan);
wl_notify_escan_complete(escan, false);
}
exit:
mutex_unlock(&escan->usr_sync);
return err;
}
static int
wl_escan_prep(struct wl_escan_info *escan, wl_uint32_list_t *list,
wl_scan_params_t *params, wlc_ssid_t *ssid)
{
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;
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(&params->bssid, &ether_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(("### 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(("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(("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(("Scanning all channels\n"));
}
if (ssid && ssid->SSID_len) {
/* Copy ssid array if applicable */
ESCAN_SCAN(("### 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;
ESCAN_SCAN(("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);
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(("1: scan for %s size=%d\n", ssid_tmp.SSID, ssid_tmp.SSID_len));
/* Adding mask to channel numbers */
params->channel_num =
htod32((2 << WL_SCAN_PARAMS_NSSID_SHIFT) |
(n_channels & WL_SCAN_PARAMS_COUNT_MASK));
}
else {
ESCAN_SCAN(("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(
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
struct timer_list *t
#else
unsigned long data
#endif
)
{
wl_event_msg_t msg;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
struct wl_escan_info *escan = from_timer(escan, t, scan_timeout);
#else
struct wl_escan_info *escan = (struct wl_escan_info *)data;
#endif
struct wl_scan_results *bss_list;
struct wl_bss_info *bi = NULL;
s32 i;
u32 channel;
bss_list = wl_escan_get_buf(escan);
if (!bss_list) {
ESCAN_ERROR(("bss_list is null. Didn't receive any partial scan results\n"));
} else {
ESCAN_ERROR(("%s: scanned AP count (%d)\n", __FUNCTION__, 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(("SSID :%s Channel :%d\n", bi->SSID, channel));
}
}
if (!escan->dev) {
ESCAN_ERROR(("No dev present\n"));
return;
}
bzero(&msg, sizeof(wl_event_msg_t));
ESCAN_ERROR(("timer expired\n"));
msg.event_type = hton32(WLC_E_ESCAN_RESULT);
msg.status = hton32(WLC_E_STATUS_TIMEOUT);
msg.reason = 0xFFFFFFFF;
wl_escan_event(escan->dev, &msg, NULL);
// terence 20130729: workaround to fix out of memory in firmware
// if (dhd_conf_get_chip(dhd_get_pub(dev)) == BCM43362_CHIP_ID) {
// ESCAN_ERROR(("Send hang event\n"));
// net_os_send_hang_message(dev);
// }
}
int
wl_escan_set_scan(
struct net_device *dev,
struct iw_request_info *info,
union iwreq_data *wrqu,
char *extra
)
{
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;
struct wl_escan_info *escan = g_escan;
wlc_ssid_t ssid;
u32 n_channels = 0;
wl_uint32_list_t *list;
u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
s32 val = 0;
ESCAN_TRACE(("Enter \n"));
if (!escan) {
ESCAN_ERROR(("device is not ready\n"));
return -EIO;
}
mutex_lock(&escan->usr_sync);
if (!escan->ioctl_ver) {
val = 1;
if ((err = wldev_ioctl(dev, WLC_GET_VERSION, &val, sizeof(int), false) < 0)) {
ANDROID_ERROR(("WLC_GET_VERSION failed, err=%d\n", err));
goto exit;
}
val = dtoh32(val);
if (val != WLC_IOCTL_VERSION && val != 1) {
ANDROID_ERROR(("Version mismatch, please upgrade. Got %d, expected %d or 1\n",
val, WLC_IOCTL_VERSION));
goto exit;
}
escan->ioctl_ver = val;
printf("%s: ioctl_ver=%d\n", __FUNCTION__, val);
}
/* default Broadcast scan */
memset(&ssid, 0, sizeof(ssid));
#if WIRELESS_EXT > 17
/* check for given essid */
if (wrqu->data.length == sizeof(struct iw_scan_req)) {
if (wrqu->data.flags & IW_SCAN_THIS_ESSID) {
struct iw_scan_req *req = (struct iw_scan_req *)extra;
ssid.SSID_len = MIN(sizeof(ssid.SSID), req->essid_len);
memcpy(ssid.SSID, req->essid, ssid.SSID_len);
ssid.SSID_len = htod32(ssid.SSID_len);
}
}
#endif
if (escan->escan_state == ESCAN_STATE_SCANING) {
ESCAN_ERROR(("Scanning already\n"));
goto exit;
}
/* 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;
list->count = htod32(WL_NUMCHANNELS);
err = wldev_ioctl(escan->dev, WLC_GET_VALID_CHANNELS, valid_chan_list, sizeof(valid_chan_list), false);
if (err != 0) {
ESCAN_ERROR(("%s: get channels failed with %d\n", __FUNCTION__, 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_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(escan, list, &params->params, &ssid);
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(("ioctl buffer length not sufficient\n"));
kfree(params);
err = -ENOMEM;
goto exit;
}
params->params.scan_type = DOT11_SCANTYPE_ACTIVE;
ESCAN_TRACE(("Passive scan_type %d\n", params->params.scan_type));
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(("Escan not permitted at this time (%d)\n", err));
else
ESCAN_ERROR(("Escan set error (%d)\n", err));
wl_escan_reset(escan);
}
kfree(params);
exit:
if (unlikely(err)) {
/* Don't print Error incase of Scan suppress */
if ((err == BCME_EPERM))
ESCAN_TRACE(("Escan failed: Scan Suppressed \n"));
else {
cnt++;
ESCAN_ERROR(("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(("Send disassoc to break the busy dev=%p\n", dev));
cnt = 0;
}
}
} else {
cnt = 0;
}
mutex_unlock(&escan->usr_sync);
return err;
}
int
wl_escan_get_scan(
struct net_device *dev,
struct iw_request_info *info,
struct iw_point *dwrq,
char *extra
)
{
s32 err = BCME_OK;
struct iw_event iwe;
int i, j;
char *event = extra, *end = extra + dwrq->length, *value;
int16 rssi;
int channel;
wl_bss_info_t *bi = NULL;
struct wl_escan_info *escan = g_escan;
struct wl_scan_results *bss_list;
#if defined(BSSCACHE)
wl_bss_cache_t *node;
#endif
ESCAN_TRACE(("%s: %s SIOCGIWSCAN, len=%d\n", __FUNCTION__, dev->name, dwrq->length));
if (!extra)
return -EINVAL;
mutex_lock(&escan->usr_sync);
/* Check for scan in progress */
if (escan->escan_state == ESCAN_STATE_SCANING) {
ESCAN_TRACE(("%s: SIOCGIWSCAN GET still scanning\n", dev->name));
err = -EAGAIN;
goto exit;
}
if (!escan->bss_list) {
ESCAN_ERROR(("%s: scan not ready\n", dev->name));
err = -EAGAIN;
goto exit;
}
#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
/* 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
channel = wf_chspec_ctlchan(wl_chspec_driver_to_host(escan->ioctl_ver, bi->chanspec));
ESCAN_SCAN(("BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
MAC2STR(bi->BSSID.octet), channel, 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;
}
#if defined(BSSCACHE)
node = node->next;
#endif
}
dwrq->length = event - extra;
dwrq->flags = 0; /* todo */
ESCAN_SCAN(("scanned AP count (%d)\n", i));
exit:
mutex_unlock(&escan->usr_sync);
return err;
}
s32 wl_escan_autochannel(struct net_device *dev, char* command, int total_len)
{
struct wl_escan_info *escan = g_escan;
int ret = 0;
int bytes_written = -1;
sscanf(command, "%*s %d", &escan->autochannel);
if (escan->autochannel == 0) {
escan->best_2g_ch = 0;
escan->best_5g_ch = 0;
} else if (escan->autochannel == 2) {
bytes_written = snprintf(command, total_len, "2g=%d 5g=%d",
escan->best_2g_ch, escan->best_5g_ch);
ANDROID_TRACE(("%s: command result is %s\n", __FUNCTION__, command));
ret = bytes_written;
}
return ret;
}
static s32 wl_create_event_handler(struct wl_escan_info *escan)
{
int ret = 0;
ESCAN_TRACE(("Enter \n"));
/* Do not use DHD in cfg driver */
escan->event_tsk.thr_pid = -1;
PROC_START(wl_escan_event_handler, escan, &escan->event_tsk, 0, "wl_escan_handler");
if (escan->event_tsk.thr_pid < 0)
ret = -ENOMEM;
return ret;
}
static void wl_destroy_event_handler(struct wl_escan_info *escan)
{
if (escan->event_tsk.thr_pid >= 0)
PROC_STOP(&escan->event_tsk);
}
static void wl_escan_deinit(struct wl_escan_info *escan)
{
printf("%s: Enter\n", __FUNCTION__);
if (!escan) {
ESCAN_ERROR(("device is not ready\n"));
return;
}
wl_destroy_event_handler(escan);
wl_flush_eq(escan);
del_timer_sync(&escan->scan_timeout);
escan->escan_state = ESCAN_STATE_IDLE;
#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 wl_escan_info *escan)
{
int err = 0;
printf("%s: Enter\n", __FUNCTION__);
if (!escan) {
ESCAN_ERROR(("device is not ready\n"));
return -EIO;
}
/* Init scan_timeout timer */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
timer_setup(&escan->scan_timeout, wl_escan_timeout, 0);
#else
init_timer(&escan->scan_timeout);
escan->scan_timeout.data = (unsigned long) escan;
escan->scan_timeout.function = wl_escan_timeout;
#endif
if (wl_create_event_handler(escan)) {
err = -ENOMEM;
goto err;
}
memset(escan->evt_handler, 0, sizeof(escan->evt_handler));
escan->evt_handler[WLC_E_ESCAN_RESULT] = wl_escan_handler;
escan->escan_state = ESCAN_STATE_IDLE;
mutex_init(&escan->usr_sync);
return 0;
err:
wl_escan_deinit(escan);
return err;
}
void wl_escan_detach(dhd_pub_t *dhdp)
{
struct wl_escan_info *escan = g_escan;
printf("%s: Enter\n", __FUNCTION__);
if (!escan) {
ESCAN_ERROR(("device is not ready\n"));
return;
}
wl_escan_deinit(escan);
if (escan->escan_ioctl_buf) {
kfree(escan->escan_ioctl_buf);
escan->escan_ioctl_buf = NULL;
}
DHD_OS_PREFREE(dhdp, escan, sizeof(struct wl_escan_info));
g_escan = NULL;
}
int
wl_escan_attach(struct net_device *dev, dhd_pub_t *dhdp)
{
struct wl_escan_info *escan = NULL;
printf("%s: Enter\n", __FUNCTION__);
if (!dev)
return 0;
escan = (wl_escan_info_t *)DHD_OS_PREALLOC(dhdp, DHD_PREALLOC_WL_ESCAN_INFO, sizeof(struct wl_escan_info));
if (!escan)
return -ENOMEM;
g_escan = escan;
memset(escan, 0, sizeof(struct wl_escan_info));
/* we only care about main interface so save a global here */
escan->dev = dev;
escan->pub = dhdp;
escan->escan_state = ESCAN_STATE_IDLE;
escan->escan_ioctl_buf = (void *)kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
if (unlikely(!escan->escan_ioctl_buf)) {
ESCAN_ERROR(("Ioctl buf alloc failed\n"));
goto err ;
}
wl_init_eq(escan);
wl_escan_init(escan);
return 0;
err:
wl_escan_detach(dhdp);
return -ENOMEM;
}
#endif /* WL_ESCAN */