1458 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1458 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| 
 | |
| #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 */
 | |
| 
 | 
