#if defined(WL_ESCAN) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 && ibss_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(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(); 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; 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(dhdp); return -ENOMEM; } #endif /* WL_ESCAN */