--- /dev/null
+
+#if defined(WL_ESCAN)
+
+#include <typedefs.h>
+#include <linuxver.h>
+#include <osl.h>
+
+#include <bcmutils.h>
+#include <bcmendian.h>
+#include <proto/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) "); \
+ printf x; \
+ } \
+ } while (0)
+#define ESCAN_SCAN(x) \
+ do { \
+ if (iw_msg_level & ESCAN_SCAN_LEVEL) { \
+ printf(KERN_ERR "ESCAN-SCAN) "); \
+ printf x; \
+ } \
+ } while (0)
+#define ESCAN_TRACE(x) \
+ do { \
+ if (iw_msg_level & ESCAN_TRACE_LEVEL) { \
+ printf(KERN_ERR "ESCAN-TRACE) "); \
+ 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;
+
+#if defined(RSSIAVG)
+static wl_rssi_cache_ctrl_t g_rssi_cache_ctrl;
+static wl_rssi_cache_ctrl_t g_connected_rssi_cache_ctrl;
+#endif
+#if defined(BSSCACHE)
+static wl_bss_cache_ctrl_t g_bss_cache_ctrl;
+#endif
+
+/* 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(chanspec_t chanspec)
+{
+ if (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(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(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 *ndev, 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, ndev, 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(&g_bss_cache_ctrl, (u8*)&escan->disconnected_bssid);
+#if defined(RSSIAVG)
+ wl_delete_disconnected_rssi_cache(&g_rssi_cache_ctrl, (u8*)&escan->disconnected_bssid);
+#endif
+#endif
+
+ /* Update cache */
+#if defined(RSSIAVG)
+ wl_update_rssi_cache(&g_rssi_cache_ctrl, bss_list);
+ if (!in_atomic())
+ wl_update_connected_rssi_cache(escan->dev, &g_rssi_cache_ctrl, &rssi);
+#endif
+#if defined(BSSCACHE)
+ wl_update_bss_cache(&g_bss_cache_ctrl,
+#if defined(RSSIAVG)
+ &g_rssi_cache_ctrl,
+#endif
+ bss_list);
+#endif
+
+ /* delete dirty cache */
+#if defined(RSSIAVG)
+ wl_delete_dirty_rssi_cache(&g_rssi_cache_ctrl);
+ wl_reset_rssi_cache(&g_rssi_cache_ctrl);
+#endif
+#if defined(BSSCACHE)
+ wl_delete_dirty_bss_cache(&g_bss_cache_ctrl);
+ wl_reset_bss_cache(&g_bss_cache_ctrl);
+#endif
+
+ ESCAN_TRACE(("scanned AP count (%d)\n", bss_list->count));
+
+ return err;
+}
+
+static wl_scan_params_t *
+wl_escan_alloc_params(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(¶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(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(-1, 0, ¶ms_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));
+ 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);
+ }
+ } 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(¶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(("### 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(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(void) {
+ struct wl_escan_info *escan = g_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;
+
+ 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 = NULL;
+ 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"));
+
+ escan = g_escan;
+ 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, ¶ms->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();
+ }
+ 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 defined(BSSCACHE)
+ bss_list = &g_bss_cache_ctrl.m_cache_head->results;
+ node = 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(&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(("%s: BSSID="MACSTR", channel=%d, RSSI=%d, SSID=\"%s\"\n",
+ __FUNCTION__, 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;
+}
+
+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(void)
+{
+ struct wl_escan_info *escan = g_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);
+
+#if defined(RSSIAVG)
+ wl_free_rssi_cache(&g_rssi_cache_ctrl);
+#endif
+#if defined(BSSCACHE)
+ wl_free_bss_cache(&g_bss_cache_ctrl);
+#endif
+}
+
+static s32 wl_escan_init(void)
+{
+ struct wl_escan_info *escan = g_escan;
+ int err = 0;
+
+ printf("%s: Enter\n", __FUNCTION__);
+ if (!escan) {
+ ESCAN_ERROR(("device is not ready\n")); \
+ return -EIO;
+ }
+
+ /* Init scan_timeout timer */
+ init_timer(&escan->scan_timeout);
+ escan->scan_timeout.data = (unsigned long) escan;
+ escan->scan_timeout.function = wl_escan_timeout;
+
+ 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();
+ return err;
+}
+
+void wl_escan_detach(void)
+{
+ 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();
+
+ if (escan->escan_ioctl_buf) {
+ kfree(escan->escan_ioctl_buf);
+ escan->escan_ioctl_buf = NULL;
+ }
+
+ kfree(escan);
+ g_escan = NULL;
+}
+
+int
+wl_escan_attach(struct net_device *dev, void * dhdp)
+{
+ struct wl_escan_info *escan = NULL;
+
+ printf("%s: Enter\n", __FUNCTION__);
+
+ if (!dev)
+ return 0;
+
+ escan = kmalloc(sizeof(struct wl_escan_info), GFP_KERNEL);
+ if (!escan)
+ return -ENOMEM;
+ memset(escan, 0, sizeof(struct wl_escan_info));
+
+ /* we only care about main interface so save a global here */
+ g_escan = escan;
+ 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);
+#ifdef WL_ESCAN
+ wl_escan_init();
+#endif
+
+ return 0;
+err:
+ wl_escan_detach();
+ return -ENOMEM;
+}
+
+#endif /* WL_ESCAN */
+