7287 lines
		
	
	
		
			202 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			7287 lines
		
	
	
		
			202 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| 
 | |
| #include <linux/module.h>
 | |
| #include <linux/netdevice.h>
 | |
| #include <net/netlink.h>
 | |
| #include <typedefs.h>
 | |
| #include <linuxver.h>
 | |
| #include <osl.h>
 | |
| 
 | |
| #include <bcmutils.h>
 | |
| #include <bcmendian.h>
 | |
| #include <ethernet.h>
 | |
| 
 | |
| #include <wl_android.h>
 | |
| #include <linux/if_arp.h>
 | |
| #include <asm/uaccess.h>
 | |
| #include <linux/wireless.h>
 | |
| #if defined(WL_WIRELESS_EXT)
 | |
| #include <wl_iw.h>
 | |
| #endif /* WL_WIRELESS_EXT */
 | |
| #include <wldev_common.h>
 | |
| #include <wlioctl.h>
 | |
| #include <bcmutils.h>
 | |
| #include <linux_osl.h>
 | |
| #include <dhd_dbg.h>
 | |
| #include <dngl_stats.h>
 | |
| #include <dhd.h>
 | |
| #include <dhd_config.h>
 | |
| #ifdef WL_CFG80211
 | |
| #include <wl_cfg80211.h>
 | |
| #endif /* WL_CFG80211 */
 | |
| #ifdef WL_ESCAN
 | |
| #include <wl_escan.h>
 | |
| #endif /* WL_ESCAN */
 | |
| 
 | |
| #define AEXT_ERROR(name, arg1, args...) \
 | |
| 	do { \
 | |
| 		if (android_msg_level & ANDROID_ERROR_LEVEL) { \
 | |
| 			printk(KERN_ERR "[dhd-%s] AEXT-ERROR) %s : " arg1, name, __func__, ## args); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| #define AEXT_TRACE(name, arg1, args...) \
 | |
| 	do { \
 | |
| 		if (android_msg_level & ANDROID_TRACE_LEVEL) { \
 | |
| 			printk(KERN_INFO "[dhd-%s] AEXT-TRACE) %s : " arg1, name, __func__, ## args); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| #define AEXT_INFO(name, arg1, args...) \
 | |
| 	do { \
 | |
| 		if (android_msg_level & ANDROID_INFO_LEVEL) { \
 | |
| 			printk(KERN_INFO "[dhd-%s] AEXT-INFO) %s : " arg1, name, __func__, ## args); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| #define AEXT_DBG(name, arg1, args...) \
 | |
| 	do { \
 | |
| 		if (android_msg_level & ANDROID_DBG_LEVEL) { \
 | |
| 			printk(KERN_INFO "[dhd-%s] AEXT-DBG) %s : " arg1, name, __func__, ## args); \
 | |
| 		} \
 | |
| 	} while (0)
 | |
| 
 | |
| #ifndef WL_CFG80211
 | |
| #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 IEEE80211_BAND_2GHZ 0
 | |
| #define IEEE80211_BAND_5GHZ 1
 | |
| #define WL_SCAN_JOIN_PROBE_INTERVAL_MS 		20
 | |
| #define WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 	320
 | |
| #define WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 	400
 | |
| #endif /* WL_CFG80211 */
 | |
| #define strtoul(nptr, endptr, base) bcm_strtoul((nptr), (endptr), (base))
 | |
| 
 | |
| #ifndef IW_CUSTOM_MAX
 | |
| #define IW_CUSTOM_MAX 256 /* size of extra buffer used for translation of events */
 | |
| #endif /* IW_CUSTOM_MAX */
 | |
| 
 | |
| #define CMD_CHANNEL				"CHANNEL"
 | |
| #define CMD_CHANNELS			"CHANNELS"
 | |
| #define CMD_ROAM_TRIGGER		"ROAM_TRIGGER"
 | |
| #define CMD_PM					"PM"
 | |
| #define CMD_MONITOR				"MONITOR"
 | |
| #define CMD_SET_SUSPEND_BCN_LI_DTIM		"SET_SUSPEND_BCN_LI_DTIM"
 | |
| 
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| #include <net/rtnetlink.h>
 | |
| #define CMD_IAPSTA_INIT			"IAPSTA_INIT"
 | |
| #define CMD_IAPSTA_CONFIG		"IAPSTA_CONFIG"
 | |
| #define CMD_IAPSTA_ENABLE		"IAPSTA_ENABLE"
 | |
| #define CMD_IAPSTA_DISABLE		"IAPSTA_DISABLE"
 | |
| #define CMD_ISAM_INIT			"ISAM_INIT"
 | |
| #define CMD_ISAM_CONFIG			"ISAM_CONFIG"
 | |
| #define CMD_ISAM_ENABLE			"ISAM_ENABLE"
 | |
| #define CMD_ISAM_DISABLE		"ISAM_DISABLE"
 | |
| #define CMD_ISAM_STATUS			"ISAM_STATUS"
 | |
| #define CMD_ISAM_PEER_PATH		"ISAM_PEER_PATH"
 | |
| #define CMD_ISAM_PARAM			"ISAM_PARAM"
 | |
| #ifdef PROP_TXSTATUS
 | |
| #ifdef PROP_TXSTATUS_VSDB
 | |
| #include <dhd_wlfc.h>
 | |
| extern int disable_proptx;
 | |
| #endif /* PROP_TXSTATUS_VSDB */
 | |
| #endif /* PROP_TXSTATUS */
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| #define CMD_AUTOCHANNEL		"AUTOCHANNEL"
 | |
| #define CMD_WL		"WL"
 | |
| 
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| typedef enum APSTAMODE {
 | |
| 	IUNKNOWN_MODE = 0,
 | |
| 	ISTAONLY_MODE = 1,
 | |
| 	IAPONLY_MODE = 2,
 | |
| 	ISTAAP_MODE = 3,
 | |
| 	ISTAGO_MODE = 4,
 | |
| 	ISTASTA_MODE = 5,
 | |
| 	IDUALAP_MODE = 6,
 | |
| 	ISTAAPAP_MODE = 7,
 | |
| 	IMESHONLY_MODE = 8,
 | |
| 	ISTAMESH_MODE = 9,
 | |
| 	IMESHAP_MODE = 10,
 | |
| 	ISTAAPMESH_MODE = 11,
 | |
| 	IMESHAPAP_MODE = 12
 | |
| } apstamode_t;
 | |
| 
 | |
| typedef enum IFMODE {
 | |
| 	ISTA_MODE = 1,
 | |
| 	IAP_MODE,
 | |
| 	IGO_MODE,
 | |
| 	IGC_MODE,
 | |
| 	IMESH_MODE
 | |
| } ifmode_t;
 | |
| 
 | |
| typedef enum BGNMODE {
 | |
| 	IEEE80211B = 1,
 | |
| 	IEEE80211G,
 | |
| 	IEEE80211BG,
 | |
| 	IEEE80211BGN,
 | |
| 	IEEE80211BGNAC
 | |
| } bgnmode_t;
 | |
| 
 | |
| typedef enum AUTHMODE {
 | |
| 	AUTH_OPEN,
 | |
| 	AUTH_SHARED,
 | |
| 	AUTH_WPAPSK,
 | |
| 	AUTH_WPA2PSK,
 | |
| 	AUTH_WPAWPA2PSK,
 | |
| 	AUTH_SAE
 | |
| } authmode_t;
 | |
| 
 | |
| typedef enum ENCMODE {
 | |
| 	ENC_NONE,
 | |
| 	ENC_WEP,
 | |
| 	ENC_TKIP,
 | |
| 	ENC_AES,
 | |
| 	ENC_TKIPAES
 | |
| } encmode_t;
 | |
| 
 | |
| enum wl_if_list {
 | |
| 	IF_PIF,
 | |
| 	IF_VIF,
 | |
| 	IF_VIF2,
 | |
| 	MAX_IF_NUM
 | |
| };
 | |
| 
 | |
| typedef enum WL_PRIO {
 | |
| 	PRIO_AP,
 | |
| 	PRIO_MESH,
 | |
| 	PRIO_STA
 | |
| } wl_prio_t;
 | |
| 
 | |
| typedef struct wl_if_info {
 | |
| 	struct net_device *dev;
 | |
| 	ifmode_t ifmode;
 | |
| 	unsigned long status;
 | |
| 	char prefix;
 | |
| 	wl_prio_t prio;
 | |
| 	int ifidx;
 | |
| 	uint8 bssidx;
 | |
| 	char ifname[IFNAMSIZ+1];
 | |
| 	char ssid[DOT11_MAX_SSID_LEN];
 | |
| 	struct ether_addr bssid;
 | |
| 	bgnmode_t bgnmode;
 | |
| 	int hidden;
 | |
| 	int maxassoc;
 | |
| 	uint16 channel;
 | |
| 	authmode_t amode;
 | |
| 	encmode_t emode;
 | |
| 	char key[100];
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 	struct wl_escan_info *escan;
 | |
| 	timer_list_compat_t delay_scan;
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 	struct delayed_work pm_enable_work;
 | |
| 	struct mutex pm_sync;
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| 	int transit_maxcount;
 | |
| #endif /* PROP_TXSTATUS_VSDB */
 | |
| } wl_if_info_t;
 | |
| 
 | |
| #define CSA_FW_BIT		(1<<0)
 | |
| #define CSA_DRV_BIT		(1<<1)
 | |
| 
 | |
| typedef struct wl_apsta_params {
 | |
| 	struct wl_if_info if_info[MAX_IF_NUM];
 | |
| 	struct dhd_pub *dhd;
 | |
| 	int ioctl_ver;
 | |
| 	bool init;
 | |
| 	int rsdb;
 | |
| 	bool vsdb;
 | |
| 	uint csa;
 | |
| 	uint acs;
 | |
| 	bool radar;
 | |
| 	apstamode_t apstamode;
 | |
| 	wait_queue_head_t netif_change_event;
 | |
| 	struct mutex usr_sync;
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 	int macs;
 | |
| 	struct wl_mesh_params mesh_info;
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| } wl_apsta_params_t;
 | |
| 
 | |
| #define MAX_AP_LINK_WAIT_TIME   3000
 | |
| #define MAX_STA_LINK_WAIT_TIME   15000
 | |
| enum wifi_isam_status {
 | |
| 	ISAM_STATUS_IF_ADDING = 0,
 | |
| 	ISAM_STATUS_IF_READY,
 | |
| 	ISAM_STATUS_STA_CONNECTING,
 | |
| 	ISAM_STATUS_STA_CONNECTED,
 | |
| 	ISAM_STATUS_AP_CREATING,
 | |
| 	ISAM_STATUS_AP_CREATED
 | |
| };
 | |
| 
 | |
| #define wl_get_isam_status(cur_if, stat) \
 | |
| 	(test_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
 | |
| #define wl_set_isam_status(cur_if, stat) \
 | |
| 	(set_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
 | |
| #define wl_clr_isam_status(cur_if, stat) \
 | |
| 	(clear_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
 | |
| #define wl_chg_isam_status(cur_if, stat) \
 | |
| 	(change_bit(ISAM_STATUS_ ## stat, &(cur_if)->status))
 | |
| 
 | |
| static int wl_ext_enable_iface(struct net_device *dev, char *ifname, int wait_up);
 | |
| static int wl_ext_disable_iface(struct net_device *dev, char *ifname);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| static int wl_mesh_escan_attach(dhd_pub_t *dhd, struct wl_if_info *cur_if);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| 
 | |
| #ifdef IDHCP
 | |
| typedef struct dhcpc_parameter {
 | |
| 	uint32 ip_addr;
 | |
| 	uint32 ip_serv;
 | |
| 	uint32 lease_time;
 | |
| } dhcpc_para_t;
 | |
| #endif /* IDHCP */
 | |
| 
 | |
| #ifdef WL_EXT_WOWL
 | |
| #define WL_WOWL_TCPFIN	(1 << 26)
 | |
| typedef struct wl_wowl_pattern2 {
 | |
| 	char cmd[4];
 | |
| 	wl_wowl_pattern_t wowl_pattern;
 | |
| } wl_wowl_pattern2_t;
 | |
| #endif /* WL_EXT_WOWL */
 | |
| 
 | |
| #ifdef WL_EXT_TCPKA
 | |
| typedef struct tcpka_conn {
 | |
| 	uint32 sess_id;
 | |
| 	struct ether_addr dst_mac;	/* Destinition Mac */
 | |
| 	struct ipv4_addr  src_ip;	/* Sorce IP */
 | |
| 	struct ipv4_addr  dst_ip;	/* Destinition IP */
 | |
| 	uint16 ipid;	/* Ip Identification */
 | |
| 	uint16 srcport;	/* Source Port Address */
 | |
| 	uint16 dstport;	/* Destination Port Address */
 | |
| 	uint32 seq;		/* TCP Sequence Number */
 | |
| 	uint32 ack;		/* TCP Ack Number */
 | |
| 	uint16 tcpwin;	/* TCP window */
 | |
| 	uint32 tsval;	/* Timestamp Value */
 | |
| 	uint32 tsecr;	/* Timestamp Echo Reply */
 | |
| 	uint32 len;		/* last packet payload len */
 | |
| 	uint32 ka_payload_len;	/* keep alive payload length */
 | |
| 	uint8  ka_payload[1];	/* keep alive payload */
 | |
| } tcpka_conn_t;
 | |
| 
 | |
| typedef struct tcpka_conn_sess {
 | |
| 	uint32 sess_id;	/* session id */
 | |
| 	uint32 flag;	/* enable/disable flag */
 | |
| 	wl_mtcpkeep_alive_timers_pkt_t  tcpka_timers;
 | |
| } tcpka_conn_sess_t;
 | |
| 
 | |
| typedef struct tcpka_conn_info {
 | |
| 	uint32 ipid;
 | |
| 	uint32 seq;
 | |
| 	uint32 ack;
 | |
| } tcpka_conn_sess_info_t;
 | |
| #endif /* WL_EXT_TCPKA */
 | |
| 
 | |
| static int wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len);
 | |
| 
 | |
| static int
 | |
| wl_ext_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len, u32 set)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = wldev_ioctl(dev, cmd, arg, len, set);
 | |
| 	if (ret)
 | |
| 		AEXT_ERROR(dev->name, "cmd=%d, ret=%d\n", cmd, ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iovar_getint(struct net_device *dev, s8 *iovar, s32 *val)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = wldev_iovar_getint(dev, iovar, val);
 | |
| 	if (ret)
 | |
| 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iovar_setint(struct net_device *dev, s8 *iovar, s32 val)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = wldev_iovar_setint(dev, iovar, val);
 | |
| 	if (ret)
 | |
| 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iovar_getbuf(struct net_device *dev, s8 *iovar_name,
 | |
| 	void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = wldev_iovar_getbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
 | |
| 	if (ret != 0)
 | |
| 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iovar_setbuf(struct net_device *dev, s8 *iovar_name,
 | |
| 	void *param, s32 paramlen, void *buf, s32 buflen, struct mutex* buf_sync)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = wldev_iovar_setbuf(dev, iovar_name, param, paramlen, buf, buflen, buf_sync);
 | |
| 	if (ret != 0)
 | |
| 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iovar_setbuf_bsscfg(struct net_device *dev, s8 *iovar_name,
 | |
| 	void *param, s32 paramlen, void *buf, s32 buflen, s32 bsscfg_idx,
 | |
| 	struct mutex* buf_sync)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = wldev_iovar_setbuf_bsscfg(dev, iovar_name, param, paramlen,
 | |
| 		buf, buflen, bsscfg_idx, buf_sync);
 | |
| 	if (ret < 0)
 | |
| 		AEXT_ERROR(dev->name, "iovar=%s, ret=%d\n", iovar_name, ret);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static chanspec_t
 | |
| wl_ext_chspec_to_legacy(chanspec_t chspec)
 | |
| {
 | |
| 	chanspec_t lchspec;
 | |
| 
 | |
| 	if (wf_chspec_malformed(chspec)) {
 | |
| 		AEXT_ERROR("wlan", "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];
 | |
| 		AEXT_ERROR("wlan", "unable to convert chanspec %s (0x%04X) "
 | |
| 			"to pre-11ac format\n",
 | |
| 			wf_chspec_ntoa(chspec, chanbuf), chspec);
 | |
| 		return INVCHANSPEC;
 | |
| 	}
 | |
| 
 | |
| 	return lchspec;
 | |
| }
 | |
| 
 | |
| static chanspec_t
 | |
| wl_ext_chspec_host_to_driver(int ioctl_ver, chanspec_t chanspec)
 | |
| {
 | |
| 	if (ioctl_ver == 1) {
 | |
| 		chanspec = wl_ext_chspec_to_legacy(chanspec);
 | |
| 		if (chanspec == INVCHANSPEC) {
 | |
| 			return chanspec;
 | |
| 		}
 | |
| 	}
 | |
| 	chanspec = htodchanspec(chanspec);
 | |
| 
 | |
| 	return chanspec;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_ch_to_chanspec(int ioctl_ver, int ch,
 | |
| 	struct wl_join_params *join_params, size_t *join_params_size)
 | |
| {
 | |
| 	chanspec_t chanspec = 0;
 | |
| 
 | |
| 	if (ch != 0) {
 | |
| 		join_params->params.chanspec_num = 1;
 | |
| 		join_params->params.chanspec_list[0] = ch;
 | |
| 
 | |
| 		if (join_params->params.chanspec_list[0] <= 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;
 | |
| 
 | |
| 		*join_params_size += WL_ASSOC_PARAMS_FIXED_SIZE +
 | |
| 			join_params->params.chanspec_num * sizeof(chanspec_t);
 | |
| 
 | |
| 		join_params->params.chanspec_list[0]  &= WL_CHANSPEC_CHAN_MASK;
 | |
| 		join_params->params.chanspec_list[0] |= chanspec;
 | |
| 		join_params->params.chanspec_list[0] =
 | |
| 			wl_ext_chspec_host_to_driver(ioctl_ver,
 | |
| 				join_params->params.chanspec_list[0]);
 | |
| 
 | |
| 		join_params->params.chanspec_num =
 | |
| 			htod32(join_params->params.chanspec_num);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #if defined(WL_EXT_IAPSTA) || defined(WL_CFG80211) || defined(WL_ESCAN)
 | |
| static chanspec_t
 | |
| wl_ext_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)) {
 | |
| 		AEXT_ERROR("wlan", "output chanspec (0x%04X) malformed\n", chspec);
 | |
| 		return INVCHANSPEC;
 | |
| 	}
 | |
| 
 | |
| 	return chspec;
 | |
| }
 | |
| 
 | |
| static chanspec_t
 | |
| wl_ext_chspec_driver_to_host(int ioctl_ver, chanspec_t chanspec)
 | |
| {
 | |
| 	chanspec = dtohchanspec(chanspec);
 | |
| 	if (ioctl_ver == 1) {
 | |
| 		chanspec = wl_ext_chspec_from_legacy(chanspec);
 | |
| 	}
 | |
| 
 | |
| 	return chanspec;
 | |
| }
 | |
| #endif /* WL_EXT_IAPSTA || WL_CFG80211 || WL_ESCAN */
 | |
| 
 | |
| bool
 | |
| wl_ext_check_scan(struct net_device *dev, dhd_pub_t *dhdp)
 | |
| {
 | |
| #ifdef WL_CFG80211
 | |
| 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
 | |
| #endif /* WL_CFG80211 */
 | |
| #ifdef WL_ESCAN
 | |
| 	struct wl_escan_info *escan = dhdp->escan;
 | |
| #endif /* WL_ESCAN */
 | |
| 
 | |
| #ifdef WL_CFG80211
 | |
| 	if (wl_get_drv_status_all(cfg, SCANNING)) {
 | |
| 		AEXT_ERROR(dev->name, "cfg80211 scanning...\n");
 | |
| 		return TRUE;
 | |
| 	}
 | |
| #endif /* WL_CFG80211 */
 | |
| 
 | |
| #ifdef WL_ESCAN
 | |
| 	if (escan->escan_state == ESCAN_STATE_SCANING) {
 | |
| 		AEXT_ERROR(dev->name, "escan scanning...\n");
 | |
| 		return TRUE;
 | |
| 	}
 | |
| #endif /* WL_ESCAN */
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| #if defined(WL_CFG80211) || defined(WL_ESCAN)
 | |
| void
 | |
| wl_ext_user_sync(struct dhd_pub *dhd, int ifidx, bool lock)
 | |
| {
 | |
| 	struct net_device *dev = dhd_idx2net(dhd, ifidx);
 | |
| #ifdef WL_CFG80211
 | |
| 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
 | |
| #endif /* WL_CFG80211 */
 | |
| #ifdef WL_ESCAN
 | |
| 	struct wl_escan_info *escan = dhd->escan;
 | |
| #endif /* WL_ESCAN */
 | |
| 
 | |
| 	AEXT_INFO(dev->name, "lock=%d\n", lock);
 | |
| 
 | |
| 	if (lock) {
 | |
| #if defined(WL_CFG80211)
 | |
| 		mutex_lock(&cfg->usr_sync);
 | |
| #endif
 | |
| #if defined(WL_ESCAN)
 | |
| 		mutex_lock(&escan->usr_sync);
 | |
| #endif
 | |
| 	} else {
 | |
| #if defined(WL_CFG80211)
 | |
| 		mutex_unlock(&cfg->usr_sync);
 | |
| #endif
 | |
| #if defined(WL_ESCAN)
 | |
| 		mutex_unlock(&escan->usr_sync);
 | |
| #endif
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool
 | |
| wl_ext_event_complete(struct dhd_pub *dhd, int ifidx)
 | |
| {
 | |
| 	struct net_device *dev = dhd_idx2net(dhd, ifidx);
 | |
| #ifdef WL_CFG80211
 | |
| 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
 | |
| #endif /* WL_CFG80211 */
 | |
| #ifdef WL_ESCAN
 | |
| 	struct wl_escan_info *escan = dhd->escan;
 | |
| #endif /* WL_ESCAN */
 | |
| 	bool complete = TRUE;
 | |
| 
 | |
| #ifdef WL_CFG80211
 | |
| 	if (wl_get_drv_status_all(cfg, SCANNING)) {
 | |
| 		AEXT_INFO(dev->name, "SCANNING\n");
 | |
| 		complete = FALSE;
 | |
| 	}
 | |
| 	if (wl_get_drv_status_all(cfg, CONNECTING)) {
 | |
| 		AEXT_INFO(dev->name, "CONNECTING\n");
 | |
| 		complete = FALSE;
 | |
| 	}
 | |
| 	if (wl_get_drv_status_all(cfg, DISCONNECTING)) {
 | |
| 		AEXT_INFO(dev->name, "DISCONNECTING\n");
 | |
| 		complete = FALSE;
 | |
| 	}
 | |
| #endif /* WL_CFG80211 */
 | |
| #ifdef WL_ESCAN
 | |
| 	if (escan->escan_state == ESCAN_STATE_SCANING) {
 | |
| 		AEXT_INFO(dev->name, "ESCAN_STATE_SCANING\n");
 | |
| 		complete = FALSE;
 | |
| 	}
 | |
| #endif /* WL_ESCAN */
 | |
| 	if (dhd->conf->eapol_status >= EAPOL_STATUS_4WAY_START &&
 | |
| 			dhd->conf->eapol_status < EAPOL_STATUS_4WAY_DONE) {
 | |
| 		AEXT_INFO(dev->name, "4-WAY handshaking\n");
 | |
| 		complete = FALSE;
 | |
| 	}
 | |
| 
 | |
| 	return complete;
 | |
| }
 | |
| #endif /* WL_CFG80211 && WL_ESCAN */
 | |
| 
 | |
| static int
 | |
| wl_ext_get_ioctl_ver(struct net_device *dev, int *ioctl_ver)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	s32 val = 0;
 | |
| 
 | |
| 	val = 1;
 | |
| 	ret = wl_ext_ioctl(dev, WLC_GET_VERSION, &val, sizeof(val), 0);
 | |
| 	if (ret) {
 | |
| 		return ret;
 | |
| 	}
 | |
| 	val = dtoh32(val);
 | |
| 	if (val != WLC_IOCTL_VERSION && val != 1) {
 | |
| 		AEXT_ERROR(dev->name, "Version mismatch, please upgrade. Got %d, expected %d or 1\n",
 | |
| 			val, WLC_IOCTL_VERSION);
 | |
| 		return BCME_VERSION;
 | |
| 	}
 | |
| 	*ioctl_ver = val;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_bss_iovar_war(struct net_device *ndev, s32 *val)
 | |
| {
 | |
| 	dhd_pub_t *dhd = dhd_get_pub(ndev);
 | |
| 	uint chip;
 | |
| 	bool need_war = false;
 | |
| 
 | |
| 	chip = dhd_conf_get_chip(dhd);
 | |
| 
 | |
| 	if (chip == BCM43362_CHIP_ID || chip == BCM4330_CHIP_ID ||
 | |
| 		chip == BCM43430_CHIP_ID || chip == BCM43012_CHIP_ID ||
 | |
| 		chip == BCM4345_CHIP_ID || chip == BCM4356_CHIP_ID ||
 | |
| 		chip == BCM4359_CHIP_ID ||
 | |
| 		chip == BCM43143_CHIP_ID || chip == BCM43242_CHIP_ID ||
 | |
| 		chip == BCM43569_CHIP_ID) {
 | |
| 		need_war = true;
 | |
| 	}
 | |
| 
 | |
| 	if (need_war) {
 | |
| 		/* Few firmware branches have issues in bss iovar handling and
 | |
| 		 * that can't be changed since they are in production.
 | |
| 		 */
 | |
| 		if (*val == WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE) {
 | |
| 			*val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
 | |
| 		} else if (*val == WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE) {
 | |
| 			*val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
 | |
| 		} else {
 | |
| 			/* Ignore for other bss enums */
 | |
| 			return;
 | |
| 		}
 | |
| 		AEXT_TRACE(ndev->name, "wl bss %d\n", *val);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_set_chanspec(struct net_device *dev, int ioctl_ver,
 | |
| 	uint16 channel, chanspec_t *ret_chspec)
 | |
| {
 | |
| 	s32 _chan = channel;
 | |
| 	chanspec_t chspec = 0;
 | |
| 	chanspec_t fw_chspec = 0;
 | |
| 	u32 bw = WL_CHANSPEC_BW_20;
 | |
| 	s32 err = BCME_OK;
 | |
| 	s32 bw_cap = 0;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	struct {
 | |
| 		u32 band;
 | |
| 		u32 bw_cap;
 | |
| 	} param = {0, 0};
 | |
| 	uint band;
 | |
| 
 | |
| 	if (_chan <= CH_MAX_2G_CHANNEL)
 | |
| 		band = IEEE80211_BAND_2GHZ;
 | |
| 	else
 | |
| 		band = IEEE80211_BAND_5GHZ;
 | |
| 
 | |
| 	if (band == IEEE80211_BAND_5GHZ) {
 | |
| 		param.band = WLC_BAND_5G;
 | |
| 		err = wl_ext_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (err) {
 | |
| 			if (err != BCME_UNSUPPORTED) {
 | |
| 				AEXT_ERROR(dev->name, "bw_cap failed, %d\n", err);
 | |
| 				return err;
 | |
| 			} else {
 | |
| 				err = wl_ext_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
 | |
| 				if (bw_cap != WLC_N_BW_20ALL)
 | |
| 					bw = WL_CHANSPEC_BW_40;
 | |
| 			}
 | |
| 		} else {
 | |
| 			if (WL_BW_CAP_80MHZ(iovar_buf[0]))
 | |
| 				bw = WL_CHANSPEC_BW_80;
 | |
| 			else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
 | |
| 				bw = WL_CHANSPEC_BW_40;
 | |
| 			else
 | |
| 				bw = WL_CHANSPEC_BW_20;
 | |
| 
 | |
| 		}
 | |
| 	}
 | |
| 	else if (band == IEEE80211_BAND_2GHZ)
 | |
| 		bw = WL_CHANSPEC_BW_20;
 | |
| 
 | |
| set_channel:
 | |
| 	chspec = wf_channel2chspec(_chan, bw);
 | |
| 	if (wf_chspec_valid(chspec)) {
 | |
| 		fw_chspec = wl_ext_chspec_host_to_driver(ioctl_ver, chspec);
 | |
| 		if (fw_chspec != INVCHANSPEC) {
 | |
| 			if ((err = wl_ext_iovar_setint(dev, "chanspec", fw_chspec)) == BCME_BADCHAN) {
 | |
| 				if (bw == WL_CHANSPEC_BW_80)
 | |
| 					goto change_bw;
 | |
| 				err = wl_ext_ioctl(dev, WLC_SET_CHANNEL, &_chan, sizeof(_chan), 1);
 | |
| 				WL_MSG(dev->name, "channel %d\n", _chan);
 | |
| 			} else if (err) {
 | |
| 				AEXT_ERROR(dev->name, "failed to set chanspec error %d\n", err);
 | |
| 			} else
 | |
| 				WL_MSG(dev->name, "channel %d, 0x%x\n", channel, chspec);
 | |
| 		} else {
 | |
| 			AEXT_ERROR(dev->name, "failed to convert host chanspec to fw chanspec\n");
 | |
| 			err = BCME_ERROR;
 | |
| 		}
 | |
| 	} else {
 | |
| change_bw:
 | |
| 		if (bw == WL_CHANSPEC_BW_80)
 | |
| 			bw = WL_CHANSPEC_BW_40;
 | |
| 		else if (bw == WL_CHANSPEC_BW_40)
 | |
| 			bw = WL_CHANSPEC_BW_20;
 | |
| 		else
 | |
| 			bw = 0;
 | |
| 		if (bw)
 | |
| 			goto set_channel;
 | |
| 		AEXT_ERROR(dev->name, "Invalid chanspec 0x%x\n", chspec);
 | |
| 		err = BCME_ERROR;
 | |
| 	}
 | |
| 	*ret_chspec = fw_chspec;
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_channel(struct net_device *dev, char* command, int total_len)
 | |
| {
 | |
| 	int ret;
 | |
| 	int channel=0;
 | |
| 	channel_info_t ci;
 | |
| 	int bytes_written = 0;
 | |
| 	chanspec_t fw_chspec;
 | |
| 	int ioctl_ver = 0;
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "cmd %s", command);
 | |
| 
 | |
| 	sscanf(command, "%*s %d", &channel);
 | |
| 
 | |
| 	if (channel > 0) {
 | |
| 		wl_ext_get_ioctl_ver(dev, &ioctl_ver);
 | |
| 		ret = wl_ext_set_chanspec(dev, ioctl_ver, channel, &fw_chspec);
 | |
| 	} else {
 | |
| 		if (!(ret = wl_ext_ioctl(dev, WLC_GET_CHANNEL, &ci,
 | |
| 				sizeof(channel_info_t), FALSE))) {
 | |
| 			AEXT_TRACE(dev->name, "hw_channel %d\n", ci.hw_channel);
 | |
| 			AEXT_TRACE(dev->name, "target_channel %d\n", ci.target_channel);
 | |
| 			AEXT_TRACE(dev->name, "scan_channel %d\n", ci.scan_channel);
 | |
| 			bytes_written = snprintf(command, sizeof(channel_info_t)+2,
 | |
| 				"channel %d", ci.hw_channel);
 | |
| 			AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 			ret = bytes_written;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_channels(struct net_device *dev, char* command, int total_len)
 | |
| {
 | |
| 	int ret, i;
 | |
| 	int bytes_written = -1;
 | |
| 	u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
 | |
| 	wl_uint32_list_t *list;
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "cmd %s", command);
 | |
| 
 | |
| 	memset(valid_chan_list, 0, sizeof(valid_chan_list));
 | |
| 	list = (wl_uint32_list_t *)(void *) valid_chan_list;
 | |
| 	list->count = htod32(WL_NUMCHANNELS);
 | |
| 	ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
 | |
| 		sizeof(valid_chan_list), 0);
 | |
| 	if (ret<0) {
 | |
| 		AEXT_ERROR(dev->name, "get channels failed with %d\n", ret);
 | |
| 	} else {
 | |
| 		bytes_written = snprintf(command, total_len, "channels");
 | |
| 		for (i = 0; i < dtoh32(list->count); i++) {
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len, " %d",
 | |
| 				dtoh32(list->element[i]));
 | |
| 		}
 | |
| 		AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 		ret = bytes_written;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_roam_trigger(struct net_device *dev, char* command, int total_len)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	int roam_trigger[2] = {0, 0};
 | |
| 	int trigger[2]= {0, 0};
 | |
| 	int bytes_written=-1;
 | |
| 
 | |
| 	sscanf(command, "%*s %10d", &roam_trigger[0]);
 | |
| 
 | |
| 	if (roam_trigger[0]) {
 | |
| 		roam_trigger[1] = WLC_BAND_ALL;
 | |
| 		ret = wl_ext_ioctl(dev, WLC_SET_ROAM_TRIGGER, roam_trigger,
 | |
| 			sizeof(roam_trigger), 1);
 | |
| 	} else {
 | |
| 		roam_trigger[1] = WLC_BAND_2G;
 | |
| 		ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, roam_trigger,
 | |
| 			sizeof(roam_trigger), 0);
 | |
| 		if (!ret)
 | |
| 			trigger[0] = roam_trigger[0];
 | |
| 
 | |
| 		roam_trigger[1] = WLC_BAND_5G;
 | |
| 		ret = wl_ext_ioctl(dev, WLC_GET_ROAM_TRIGGER, &roam_trigger,
 | |
| 			sizeof(roam_trigger), 0);
 | |
| 		if (!ret)
 | |
| 			trigger[1] = roam_trigger[0];
 | |
| 
 | |
| 		AEXT_TRACE(dev->name, "roam_trigger %d %d\n", trigger[0], trigger[1]);
 | |
| 		bytes_written = snprintf(command, total_len, "%d %d", trigger[0], trigger[1]);
 | |
| 		ret = bytes_written;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_pm(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	int pm=-1, ret = -1;
 | |
| 	char *pm_local;
 | |
| 	int bytes_written=-1;
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "cmd %s", command);
 | |
| 
 | |
| 	sscanf(command, "%*s %d", &pm);
 | |
| 
 | |
| 	if (pm >= 0) {
 | |
| 		ret = wl_ext_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), 1);
 | |
| 	} else {
 | |
| 		ret = wl_ext_ioctl(dev, WLC_GET_PM, &pm, sizeof(pm), 0);
 | |
| 		if (!ret) {
 | |
| 			AEXT_TRACE(dev->name, "PM = %d", pm);
 | |
| 			if (pm == PM_OFF)
 | |
| 				pm_local = "PM_OFF";
 | |
| 			else if(pm == PM_MAX)
 | |
| 				pm_local = "PM_MAX";
 | |
| 			else if(pm == PM_FAST)
 | |
| 				pm_local = "PM_FAST";
 | |
| 			else {
 | |
| 				pm = 0;
 | |
| 				pm_local = "Invalid";
 | |
| 			}
 | |
| 			bytes_written = snprintf(command, total_len, "PM %s", pm_local);
 | |
| 			AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 			ret = bytes_written;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_monitor(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	int val = -1, ret = -1;
 | |
| 	int bytes_written=-1;
 | |
| 
 | |
| 	sscanf(command, "%*s %d", &val);
 | |
| 
 | |
| 	if (val >=0) {
 | |
| 		ret = wl_ext_ioctl(dev, WLC_SET_MONITOR, &val, sizeof(val), 1);
 | |
| 	} else {
 | |
| 		ret = wl_ext_ioctl(dev, WLC_GET_MONITOR, &val, sizeof(val), 0);
 | |
| 		if (!ret) {
 | |
| 			AEXT_TRACE(dev->name, "monitor = %d\n", val);
 | |
| 			bytes_written = snprintf(command, total_len, "monitor %d", val);
 | |
| 			AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 			ret = bytes_written;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| s32
 | |
| wl_ext_connect(struct net_device *dev, struct wl_conn_info *conn_info)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	wl_extjoin_params_t *ext_join_params = NULL;
 | |
| 	struct wl_join_params join_params;
 | |
| 	size_t join_params_size;
 | |
| 	s32 err = 0;
 | |
| 	u32 chan_cnt = 0;
 | |
| 	s8 *iovar_buf = NULL;
 | |
| 	int ioctl_ver = 0;
 | |
| 	char sec[32];
 | |
| 
 | |
| 	wl_ext_get_ioctl_ver(dev, &ioctl_ver);
 | |
| 
 | |
| 	if (dhd->conf->chip == BCM43362_CHIP_ID)
 | |
| 		goto set_ssid;
 | |
| 
 | |
| 	if (conn_info->channel) {
 | |
| 		chan_cnt = 1;
 | |
| 	}
 | |
| 
 | |
| 	iovar_buf = kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
 | |
| 	if (iovar_buf == NULL) {
 | |
| 		err = -ENOMEM;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/*
 | |
| 	 *	Join with specific BSSID and cached SSID
 | |
| 	 *	If SSID is zero join based on BSSID only
 | |
| 	 */
 | |
| 	join_params_size = WL_EXTJOIN_PARAMS_FIXED_SIZE +
 | |
| 		chan_cnt * sizeof(chanspec_t);
 | |
| 	ext_join_params =  (wl_extjoin_params_t*)kzalloc(join_params_size, GFP_KERNEL);
 | |
| 	if (ext_join_params == NULL) {
 | |
| 		err = -ENOMEM;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 	ext_join_params->ssid.SSID_len = min((uint32)sizeof(ext_join_params->ssid.SSID),
 | |
| 		conn_info->ssid.SSID_len);
 | |
| 	memcpy(&ext_join_params->ssid.SSID, conn_info->ssid.SSID, ext_join_params->ssid.SSID_len);
 | |
| 	ext_join_params->ssid.SSID_len = htod32(ext_join_params->ssid.SSID_len);
 | |
| 	/* increate dwell time to receive probe response or detect Beacon
 | |
| 	* from target AP at a noisy air only during connect command
 | |
| 	*/
 | |
| 	ext_join_params->scan.active_time = chan_cnt ? WL_SCAN_JOIN_ACTIVE_DWELL_TIME_MS : -1;
 | |
| 	ext_join_params->scan.passive_time = chan_cnt ? WL_SCAN_JOIN_PASSIVE_DWELL_TIME_MS : -1;
 | |
| 	/* Set up join scan parameters */
 | |
| 	ext_join_params->scan.scan_type = -1;
 | |
| 	ext_join_params->scan.nprobes = chan_cnt ?
 | |
| 		(ext_join_params->scan.active_time/WL_SCAN_JOIN_PROBE_INTERVAL_MS) : -1;
 | |
| 	ext_join_params->scan.home_time = -1;
 | |
| 
 | |
| 	if (memcmp(ðer_null, &conn_info->bssid, ETHER_ADDR_LEN))
 | |
| 		memcpy(&ext_join_params->assoc.bssid, &conn_info->bssid, ETH_ALEN);
 | |
| 	else
 | |
| 		memcpy(&ext_join_params->assoc.bssid, ðer_bcast, ETH_ALEN);
 | |
| 	ext_join_params->assoc.chanspec_num = chan_cnt;
 | |
| 	if (chan_cnt) {
 | |
| 		u16 band, bw, ctl_sb;
 | |
| 		chanspec_t chspec;
 | |
| 		band = (conn_info->channel <= CH_MAX_2G_CHANNEL) ? WL_CHANSPEC_BAND_2G
 | |
| 			: WL_CHANSPEC_BAND_5G;
 | |
| 		bw = WL_CHANSPEC_BW_20;
 | |
| 		ctl_sb = WL_CHANSPEC_CTL_SB_NONE;
 | |
| 		chspec = (conn_info->channel | band | bw | ctl_sb);
 | |
| 		ext_join_params->assoc.chanspec_list[0]  &= WL_CHANSPEC_CHAN_MASK;
 | |
| 		ext_join_params->assoc.chanspec_list[0] |= chspec;
 | |
| 		ext_join_params->assoc.chanspec_list[0] =
 | |
| 			wl_ext_chspec_host_to_driver(ioctl_ver,
 | |
| 				ext_join_params->assoc.chanspec_list[0]);
 | |
| 	}
 | |
| 	ext_join_params->assoc.chanspec_num = htod32(ext_join_params->assoc.chanspec_num);
 | |
| 
 | |
| 	wl_ext_get_sec(dev, 0, sec, sizeof(sec));
 | |
| 	WL_MSG(dev->name,
 | |
| 		"Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
 | |
| 		&ext_join_params->assoc.bssid, conn_info->channel,
 | |
| 		ext_join_params->ssid.SSID, ext_join_params->ssid.SSID_len, sec);
 | |
| 	err = wl_ext_iovar_setbuf_bsscfg(dev, "join", ext_join_params,
 | |
| 		join_params_size, iovar_buf, WLC_IOCTL_MAXLEN, conn_info->bssidx, NULL);
 | |
| 
 | |
| 	if (err) {
 | |
| 		if (err == BCME_UNSUPPORTED) {
 | |
| 			AEXT_TRACE(dev->name, "join iovar is not supported\n");
 | |
| 			goto set_ssid;
 | |
| 		} else {
 | |
| 			AEXT_ERROR(dev->name, "error (%d)\n", err);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 	} else
 | |
| 		goto exit;
 | |
| 
 | |
| set_ssid:
 | |
| 	memset(&join_params, 0, sizeof(join_params));
 | |
| 	join_params_size = sizeof(join_params.ssid);
 | |
| 
 | |
| 	join_params.ssid.SSID_len = min((uint32)sizeof(join_params.ssid.SSID),
 | |
| 		conn_info->ssid.SSID_len);
 | |
| 	memcpy(&join_params.ssid.SSID, conn_info->ssid.SSID, join_params.ssid.SSID_len);
 | |
| 	join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
 | |
| 	if (memcmp(ðer_null, &conn_info->bssid, ETHER_ADDR_LEN))
 | |
| 		memcpy(&join_params.params.bssid, &conn_info->bssid, ETH_ALEN);
 | |
| 	else
 | |
| 		memcpy(&join_params.params.bssid, ðer_bcast, ETH_ALEN);
 | |
| 
 | |
| 	wl_ext_ch_to_chanspec(ioctl_ver, conn_info->channel, &join_params, &join_params_size);
 | |
| 	AEXT_TRACE(dev->name, "join_param_size %zu\n", join_params_size);
 | |
| 
 | |
| 	if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
 | |
| 		AEXT_INFO(dev->name, "ssid \"%s\", len (%d)\n", join_params.ssid.SSID,
 | |
| 			join_params.ssid.SSID_len);
 | |
| 	}
 | |
| 	wl_ext_get_sec(dev, 0, sec, sizeof(sec));
 | |
| 	WL_MSG(dev->name,
 | |
| 		"Connecting with %pM channel (%d) ssid \"%s\", len (%d), sec=%s\n\n",
 | |
| 		&join_params.params.bssid, conn_info->channel,
 | |
| 		join_params.ssid.SSID, join_params.ssid.SSID_len, sec);
 | |
| 	err = wl_ext_ioctl(dev, WLC_SET_SSID, &join_params, join_params_size, 1);
 | |
| 
 | |
| exit:
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 	if (!err)
 | |
| 		wl_ext_add_remove_pm_enable_work(dev, TRUE);
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| 	if (iovar_buf)
 | |
| 		kfree(iovar_buf);
 | |
| 	if (ext_join_params)
 | |
| 		kfree(ext_join_params);
 | |
| 	return err;
 | |
| 
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_get_sec(struct net_device *dev, int ifmode, char *sec, int total_len)
 | |
| {
 | |
| 	int auth=0, wpa_auth=0, wsec=0, mfp=0;
 | |
| 	int bytes_written=0;
 | |
| 
 | |
| 	memset(sec, 0, total_len);
 | |
| 	wl_ext_iovar_getint(dev, "auth", &auth);
 | |
| 	wl_ext_iovar_getint(dev, "wpa_auth", &wpa_auth);
 | |
| 	wl_ext_iovar_getint(dev, "wsec", &wsec);
 | |
| 	wldev_iovar_getint(dev, "mfp", &mfp);
 | |
| 
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 	if (ifmode == IMESH_MODE) {
 | |
| 		if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "open");
 | |
| 		} else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "sae");
 | |
| 		} else {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
 | |
| 				auth, wpa_auth);
 | |
| 		}
 | |
| 	} else
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| 	{
 | |
| 		if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_DISABLED) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "open");
 | |
| 		} else if (auth == WL_AUTH_SHARED_KEY && wpa_auth == WPA_AUTH_DISABLED) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "shared");
 | |
| 		} else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA_AUTH_PSK) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "wpapsk");
 | |
| 		} else if (auth == WL_AUTH_OPEN_SYSTEM && wpa_auth == WPA2_AUTH_PSK) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "wpa2psk");
 | |
| 		} else if ((auth == WL_AUTH_OPEN_SYSTEM || auth == WL_AUTH_SAE_KEY) &&
 | |
| 				wpa_auth == WPA3_AUTH_SAE_PSK) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "wpa3");
 | |
| 		} else if ((auth == WL_AUTH_OPEN_SYSTEM || auth == WL_AUTH_SAE_KEY) &&
 | |
| 				wpa_auth == 0x20) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "wpa3");
 | |
| 		} else {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "%d/0x%x",
 | |
| 				auth, wpa_auth);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (mfp == WL_MFP_NONE) {
 | |
| 		bytes_written += snprintf(sec+bytes_written, total_len, "/mfpn");
 | |
| 	} else if (mfp == WL_MFP_CAPABLE) {
 | |
| 		bytes_written += snprintf(sec+bytes_written, total_len, "/mfpc");
 | |
| 	} else if (mfp == WL_MFP_REQUIRED) {
 | |
| 		bytes_written += snprintf(sec+bytes_written, total_len, "/mfpr");
 | |
| 	} else {
 | |
| 		bytes_written += snprintf(sec+bytes_written, total_len, "/%d", mfp);
 | |
| 	}
 | |
| 
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 	if (ifmode == IMESH_MODE) {
 | |
| 		if (wsec == WSEC_NONE) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/none");
 | |
| 		} else {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
 | |
| 		}
 | |
| 	} else
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| 	{
 | |
| 		if (wsec == WSEC_NONE) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/none");
 | |
| 		} else if (wsec == WEP_ENABLED) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/wep");
 | |
| 		} else if (wsec == (TKIP_ENABLED|AES_ENABLED) ||
 | |
| 				wsec == (WSEC_SWFLAG|TKIP_ENABLED|AES_ENABLED)) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/tkipaes");
 | |
| 		} else if (wsec == TKIP_ENABLED || wsec == (WSEC_SWFLAG|TKIP_ENABLED)) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/tkip");
 | |
| 		} else if (wsec == AES_ENABLED || wsec == (WSEC_SWFLAG|AES_ENABLED)) {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/aes");
 | |
| 		} else {
 | |
| 			bytes_written += snprintf(sec+bytes_written, total_len, "/0x%x", wsec);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_ext_dfs_chan(uint16 chan)
 | |
| {
 | |
| 	if (chan >= 52 && chan <= 144)
 | |
| 		return TRUE;
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static uint16
 | |
| wl_ext_get_default_chan(struct net_device *dev,
 | |
| 	uint16 *chan_2g, uint16 *chan_5g, bool nodfs)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	uint16 chan_tmp = 0, chan = 0;
 | |
| 	wl_uint32_list_t *list;
 | |
| 	u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
 | |
| 	s32 ret = BCME_OK;
 | |
| 	int i;
 | |
| 
 | |
| 	*chan_2g = 0;
 | |
| 	*chan_5g = 0;
 | |
| 	memset(valid_chan_list, 0, sizeof(valid_chan_list));
 | |
| 	list = (wl_uint32_list_t *)(void *) valid_chan_list;
 | |
| 	list->count = htod32(WL_NUMCHANNELS);
 | |
| 	ret = wl_ext_ioctl(dev, WLC_GET_VALID_CHANNELS, valid_chan_list,
 | |
| 		sizeof(valid_chan_list), 0);
 | |
| 	if (ret == 0) {
 | |
| 		for (i=0; i<dtoh32(list->count); i++) {
 | |
| 			chan_tmp = dtoh32(list->element[i]);
 | |
| 			if (!dhd_conf_match_channel(dhd, chan_tmp))
 | |
| 				continue;
 | |
| 			if (chan_tmp <= 13) {
 | |
| 				*chan_2g = chan_tmp;
 | |
| 			} else {
 | |
| 				if (wl_ext_dfs_chan(chan_tmp) && nodfs)
 | |
| 					continue;
 | |
| 				else if (chan_tmp >= 36 && chan_tmp <= 161)
 | |
| 					*chan_5g = chan_tmp;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return chan;
 | |
| }
 | |
| 
 | |
| #if defined(SENDPROB) || (defined(WLMESH) && defined(WL_ESCAN))
 | |
| static int
 | |
| wl_ext_add_del_ie(struct net_device *dev, uint pktflag, char *ie_data, const char* add_del_cmd)
 | |
| {
 | |
| 	vndr_ie_setbuf_t *vndr_ie = NULL;
 | |
| 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
 | |
| 	int ie_data_len = 0, tot_len = 0, iecount;
 | |
| 	int err = -1;
 | |
| 
 | |
| 	if (!strlen(ie_data)) {
 | |
| 		AEXT_ERROR(dev->name, "wrong ie %s\n", ie_data);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	tot_len = (int)(sizeof(vndr_ie_setbuf_t) + ((strlen(ie_data)-2)/2));
 | |
| 	vndr_ie = (vndr_ie_setbuf_t *) kzalloc(tot_len, GFP_KERNEL);
 | |
| 	if (!vndr_ie) {
 | |
| 		AEXT_ERROR(dev->name, "IE memory alloc failed\n");
 | |
| 		err = -ENOMEM;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
 | |
| 	strncpy(vndr_ie->cmd, add_del_cmd, VNDR_IE_CMD_LEN - 1);
 | |
| 	vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
 | |
| 
 | |
| 	/* Set the IE count - the buffer contains only 1 IE */
 | |
| 	iecount = htod32(1);
 | |
| 	memcpy((void *)&vndr_ie->vndr_ie_buffer.iecount, &iecount, sizeof(s32));
 | |
| 
 | |
| 	/* Set packet flag to indicate that BEACON's will contain this IE */
 | |
| 	pktflag = htod32(pktflag);
 | |
| 	memcpy((void *)&vndr_ie->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag,
 | |
| 		sizeof(u32));
 | |
| 
 | |
| 	/* Set the IE ID */
 | |
| 	vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.id = (uchar)DOT11_MNG_VS_ID;
 | |
| 
 | |
| 	/* Set the IE LEN */
 | |
| 	vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = (strlen(ie_data)-2)/2;
 | |
| 
 | |
| 	/* Set the IE OUI and DATA */
 | |
| 	ie_data_len = wl_pattern_atoh(ie_data,
 | |
| 		(char *)vndr_ie->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui);
 | |
| 	if (ie_data_len <= 0) {
 | |
| 		AEXT_ERROR(dev->name, "wrong ie_data_len %d\n", (int)strlen(ie_data)-2);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, tot_len, iovar_buf,
 | |
| 		sizeof(iovar_buf), NULL);
 | |
| 
 | |
| exit:
 | |
| 	if (vndr_ie) {
 | |
| 		kfree(vndr_ie);
 | |
| 	}
 | |
| 	return err;
 | |
| }
 | |
| #endif /* SENDPROB || (WLMESH && WL_ESCAN) */
 | |
| 
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| #define WL_PM_ENABLE_TIMEOUT 10000
 | |
| #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
 | |
| 	4 && __GNUC_MINOR__ >= 6))
 | |
| #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_CONTAINER_OF(entry, ptr, type, member) \
 | |
| entry = container_of((ptr), type, member);
 | |
| #endif /* STRICT_GCC_WARNINGS */
 | |
| 
 | |
| static void
 | |
| wl_ext_pm_work_handler(struct work_struct *work)
 | |
| {
 | |
| 	struct wl_if_info *cur_if;
 | |
| 	s32 pm = PM_FAST;
 | |
| 	dhd_pub_t *dhd;
 | |
| 
 | |
| 	BCM_SET_CONTAINER_OF(cur_if, work, struct wl_if_info, pm_enable_work.work);
 | |
| 
 | |
| 	WL_TRACE(("%s: Enter\n", __FUNCTION__));
 | |
| 
 | |
| 	if (cur_if->dev == NULL)
 | |
| 		return;
 | |
| 
 | |
| #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
 | |
| 	4 && __GNUC_MINOR__ >= 6))
 | |
| _Pragma("GCC diagnostic push")
 | |
| _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
 | |
| #endif
 | |
| 
 | |
| 	dhd = dhd_get_pub(cur_if->dev);
 | |
| 
 | |
| 	if (!dhd || !dhd->up) {
 | |
| 		AEXT_TRACE(cur_if->ifname, "dhd is null or not up\n");
 | |
| 		return;
 | |
| 	}
 | |
| 	if (dhd_conf_get_pm(dhd) >= 0)
 | |
| 		pm = dhd_conf_get_pm(dhd);
 | |
| 	wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
 | |
| #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == \
 | |
| 	4 && __GNUC_MINOR__ >= 6))
 | |
| _Pragma("GCC diagnostic pop")
 | |
| #endif
 | |
| 	DHD_PM_WAKE_UNLOCK(dhd);
 | |
| 
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_add_remove_pm_enable_work(struct net_device *dev, bool add)
 | |
| {
 | |
| 	dhd_pub_t *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
 | |
| 	u16 wq_duration = 0;
 | |
| 	s32 pm = PM_OFF;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && tmp_if->dev == dev) {
 | |
| 			cur_if = tmp_if;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!cur_if)
 | |
| 		return;
 | |
| 
 | |
| 	mutex_lock(&cur_if->pm_sync);
 | |
| 	/*
 | |
| 	 * Make cancel and schedule work part mutually exclusive
 | |
| 	 * so that while cancelling, we are sure that there is no
 | |
| 	 * work getting scheduled.
 | |
| 	 */
 | |
| 
 | |
| 	if (delayed_work_pending(&cur_if->pm_enable_work)) {
 | |
| 		cancel_delayed_work_sync(&cur_if->pm_enable_work);
 | |
| 		DHD_PM_WAKE_UNLOCK(dhd);
 | |
| 	}
 | |
| 
 | |
| 	if (add) {
 | |
| 		wq_duration = (WL_PM_ENABLE_TIMEOUT);
 | |
| 	}
 | |
| 
 | |
| 	/* It should schedule work item only if driver is up */
 | |
| 	if (dhd->up) {
 | |
| 		if (dhd_conf_get_pm(dhd) >= 0)
 | |
| 			pm = dhd_conf_get_pm(dhd);
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
 | |
| 		if (wq_duration) {
 | |
| 			if (schedule_delayed_work(&cur_if->pm_enable_work,
 | |
| 					msecs_to_jiffies((const unsigned int)wq_duration))) {
 | |
| 				DHD_PM_WAKE_LOCK_TIMEOUT(dhd, wq_duration);
 | |
| 			} else {
 | |
| 				AEXT_ERROR(cur_if->ifname, "Can't schedule pm work handler\n");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	mutex_unlock(&cur_if->pm_sync);
 | |
| 
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_parse_wep(char *key, struct wl_wsec_key *wsec_key)
 | |
| {
 | |
| 	char hex[] = "XX";
 | |
| 	unsigned char *data = wsec_key->data;
 | |
| 	char *keystr = key;
 | |
| 
 | |
| 	switch (strlen(keystr)) {
 | |
| 	case 5:
 | |
| 	case 13:
 | |
| 	case 16:
 | |
| 		wsec_key->len = strlen(keystr);
 | |
| 		memcpy(data, keystr, wsec_key->len + 1);
 | |
| 		break;
 | |
| 	case 12:
 | |
| 	case 28:
 | |
| 	case 34:
 | |
| 	case 66:
 | |
| 		/* strip leading 0x */
 | |
| 		if (!strnicmp(keystr, "0x", 2))
 | |
| 			keystr += 2;
 | |
| 		else
 | |
| 			return -1;
 | |
| 		/* fall through */
 | |
| 	case 10:
 | |
| 	case 26:
 | |
| 	case 32:
 | |
| 	case 64:
 | |
| 		wsec_key->len = strlen(keystr) / 2;
 | |
| 		while (*keystr) {
 | |
| 			strncpy(hex, keystr, 2);
 | |
| 			*data++ = (char) strtoul(hex, NULL, 16);
 | |
| 			keystr += 2;
 | |
| 		}
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	switch (wsec_key->len) {
 | |
| 	case 5:
 | |
| 		wsec_key->algo = CRYPTO_ALGO_WEP1;
 | |
| 		break;
 | |
| 	case 13:
 | |
| 		wsec_key->algo = CRYPTO_ALGO_WEP128;
 | |
| 		break;
 | |
| 	case 16:
 | |
| 		/* default to AES-CCM */
 | |
| 		wsec_key->algo = CRYPTO_ALGO_AES_CCM;
 | |
| 		break;
 | |
| 	case 32:
 | |
| 		wsec_key->algo = CRYPTO_ALGO_TKIP;
 | |
| 		break;
 | |
| 	default:
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	/* Set as primary wsec_key by default */
 | |
| 	wsec_key->flags |= WL_PRIMARY_KEY;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_set_bgnmode(struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct net_device *dev = cur_if->dev;
 | |
| 	bgnmode_t bgnmode = cur_if->bgnmode;
 | |
| 	int val;
 | |
| 
 | |
| 	if (bgnmode == 0)
 | |
| 		return 0;
 | |
| 
 | |
| 	wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 	if (bgnmode == IEEE80211B) {
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 0);
 | |
| 		val = 0;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
 | |
| 		AEXT_TRACE(dev->name, "Network mode: B only\n");
 | |
| 	} else if (bgnmode == IEEE80211G) {
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 0);
 | |
| 		val = 2;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
 | |
| 		AEXT_TRACE(dev->name, "Network mode: G only\n");
 | |
| 	} else if (bgnmode == IEEE80211BG) {
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 0);
 | |
| 		val = 1;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
 | |
| 		AEXT_TRACE(dev->name, "Network mode: B/G mixed\n");
 | |
| 	} else if (bgnmode == IEEE80211BGN) {
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 0);
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 1);
 | |
| 		wl_ext_iovar_setint(dev, "vhtmode", 0);
 | |
| 		val = 1;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
 | |
| 		AEXT_TRACE(dev->name, "Network mode: B/G/N mixed\n");
 | |
| 	} else if (bgnmode == IEEE80211BGNAC) {
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 0);
 | |
| 		wl_ext_iovar_setint(dev, "nmode", 1);
 | |
| 		wl_ext_iovar_setint(dev, "vhtmode", 1);
 | |
| 		val = 1;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_GMODE, &val, sizeof(val), 1);
 | |
| 		AEXT_TRACE(dev->name, "Network mode: B/G/N/AC mixed\n");
 | |
| 	}
 | |
| 	wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_set_amode(struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct net_device *dev = cur_if->dev;
 | |
| 	authmode_t amode = cur_if->amode;
 | |
| 	int auth=0, wpa_auth=0;
 | |
| 
 | |
| #ifdef WLMESH
 | |
| 	if (cur_if->ifmode == IMESH_MODE) {
 | |
| 		if (amode == AUTH_SAE) {
 | |
| 			auth = WL_AUTH_OPEN_SYSTEM;
 | |
| 			wpa_auth = WPA2_AUTH_PSK;
 | |
| 			AEXT_INFO(dev->name, "SAE\n");
 | |
| 		} else {
 | |
| 			auth = WL_AUTH_OPEN_SYSTEM;
 | |
| 			wpa_auth = WPA_AUTH_DISABLED;
 | |
| 			AEXT_INFO(dev->name, "Open System\n");
 | |
| 		}
 | |
| 	} else
 | |
| #endif /* WLMESH */
 | |
| 	if (amode == AUTH_OPEN) {
 | |
| 		auth = WL_AUTH_OPEN_SYSTEM;
 | |
| 		wpa_auth = WPA_AUTH_DISABLED;
 | |
| 		AEXT_INFO(dev->name, "Open System\n");
 | |
| 	} else if (amode == AUTH_SHARED) {
 | |
| 		auth = WL_AUTH_SHARED_KEY;
 | |
| 		wpa_auth = WPA_AUTH_DISABLED;
 | |
| 		AEXT_INFO(dev->name, "Shared Key\n");
 | |
| 	} else if (amode == AUTH_WPAPSK) {
 | |
| 		auth = WL_AUTH_OPEN_SYSTEM;
 | |
| 		wpa_auth = WPA_AUTH_PSK;
 | |
| 		AEXT_INFO(dev->name, "WPA-PSK\n");
 | |
| 	} else if (amode == AUTH_WPA2PSK) {
 | |
| 		auth = WL_AUTH_OPEN_SYSTEM;
 | |
| 		wpa_auth = WPA2_AUTH_PSK;
 | |
| 		AEXT_INFO(dev->name, "WPA2-PSK\n");
 | |
| 	} else if (amode == AUTH_WPAWPA2PSK) {
 | |
| 		auth = WL_AUTH_OPEN_SYSTEM;
 | |
| 		wpa_auth = WPA2_AUTH_PSK | WPA_AUTH_PSK;
 | |
| 		AEXT_INFO(dev->name, "WPA/WPA2-PSK\n");
 | |
| 	}
 | |
| #ifdef WLMESH
 | |
| 	if (cur_if->ifmode == IMESH_MODE) {
 | |
| 		s32 val = WL_BSSTYPE_MESH;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);
 | |
| 	} else
 | |
| #endif /* WLMESH */
 | |
| 	if (cur_if->ifmode == ISTA_MODE) {
 | |
| 		s32 val = WL_BSSTYPE_INFRA;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_INFRA, &val, sizeof(val), 1);
 | |
| 	}
 | |
| 	wl_ext_iovar_setint(dev, "auth", auth);
 | |
| 
 | |
| 	wl_ext_iovar_setint(dev, "wpa_auth", wpa_auth);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_set_emode(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct net_device *dev = cur_if->dev;
 | |
| 	int wsec=0;
 | |
| 	struct wl_wsec_key wsec_key;
 | |
| 	wsec_pmk_t psk;
 | |
| 	authmode_t amode = cur_if->amode;
 | |
| 	encmode_t emode = cur_if->emode;
 | |
| 	char *key = cur_if->key;
 | |
| 	struct dhd_pub *dhd = apsta_params->dhd;
 | |
| 
 | |
| 	memset(&wsec_key, 0, sizeof(wsec_key));
 | |
| 	memset(&psk, 0, sizeof(psk));
 | |
| 
 | |
| #ifdef WLMESH
 | |
| 	if (cur_if->ifmode == IMESH_MODE) {
 | |
| 		if (amode == AUTH_SAE) {
 | |
| 			wsec = AES_ENABLED;
 | |
| 		} else {
 | |
| 			wsec = WSEC_NONE;
 | |
| 		}
 | |
| 	} else
 | |
| #endif /* WLMESH */
 | |
| 	if (emode == ENC_NONE) {
 | |
| 		wsec = WSEC_NONE;
 | |
| 		AEXT_INFO(dev->name, "No securiy\n");
 | |
| 	} else if (emode == ENC_WEP) {
 | |
| 		wsec = WEP_ENABLED;
 | |
| 		wl_ext_parse_wep(key, &wsec_key);
 | |
| 		AEXT_INFO(dev->name, "WEP key \"%s\"\n", wsec_key.data);
 | |
| 	} else if (emode == ENC_TKIP) {
 | |
| 		wsec = TKIP_ENABLED;
 | |
| 		psk.key_len = strlen(key);
 | |
| 		psk.flags = WSEC_PASSPHRASE;
 | |
| 		memcpy(psk.key, key, strlen(key));
 | |
| 		AEXT_INFO(dev->name, "TKIP key \"%s\"\n", psk.key);
 | |
| 	} else if (emode == ENC_AES || amode == AUTH_SAE) {
 | |
| 		wsec = AES_ENABLED;
 | |
| 		psk.key_len = strlen(key);
 | |
| 		psk.flags = WSEC_PASSPHRASE;
 | |
| 		memcpy(psk.key, key, strlen(key));
 | |
| 		AEXT_INFO(dev->name, "AES key \"%s\"\n", psk.key);
 | |
| 	} else if (emode == ENC_TKIPAES) {
 | |
| 		wsec = TKIP_ENABLED | AES_ENABLED;
 | |
| 		psk.key_len = strlen(key);
 | |
| 		psk.flags = WSEC_PASSPHRASE;
 | |
| 		memcpy(psk.key, key, strlen(key));
 | |
| 		AEXT_INFO(dev->name, "TKIP/AES key \"%s\"\n", psk.key);
 | |
| 	}
 | |
| 	if (dhd->conf->chip == BCM43430_CHIP_ID && cur_if->ifidx > 0 && wsec >= 2 &&
 | |
| 			apsta_params->apstamode == ISTAAP_MODE) {
 | |
| 		wsec |= WSEC_SWFLAG; // terence 20180628: fix me, this is a workaround
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_iovar_setint(dev, "wsec", wsec);
 | |
| 
 | |
| #ifdef WLMESH
 | |
| 	if (cur_if->ifmode == IMESH_MODE) {
 | |
| 		if (amode == AUTH_SAE) {
 | |
| 			s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 			AEXT_INFO(dev->name, "AES key \"%s\"\n", key);
 | |
| 			wl_ext_iovar_setint(dev, "mesh_auth_proto", 1);
 | |
| 			wl_ext_iovar_setint(dev, "mfp", WL_MFP_REQUIRED);
 | |
| 			wl_ext_iovar_setbuf(dev, "sae_password", key, strlen(key),
 | |
| 				iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		} else {
 | |
| 			AEXT_INFO(dev->name, "No securiy\n");
 | |
| 			wl_ext_iovar_setint(dev, "mesh_auth_proto", 0);
 | |
| 			wl_ext_iovar_setint(dev, "mfp", WL_MFP_NONE);
 | |
| 		}
 | |
| 	} else
 | |
| #endif /* WLMESH */
 | |
| 	if (emode == ENC_WEP) {
 | |
| 		wl_ext_ioctl(dev, WLC_SET_KEY, &wsec_key, sizeof(wsec_key), 1);
 | |
| 	} else if (emode == ENC_TKIP || emode == ENC_AES || emode == ENC_TKIPAES) {
 | |
| 		if (cur_if->ifmode == ISTA_MODE)
 | |
| 			wl_ext_iovar_setint(dev, "sup_wpa", 1);
 | |
| 		wl_ext_ioctl(dev, WLC_SET_WSEC_PMK, &psk, sizeof(psk), 1);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static u32
 | |
| wl_ext_get_chanspec(struct wl_apsta_params *apsta_params,
 | |
| 	struct net_device *dev)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	struct ether_addr bssid;
 | |
| 	u32 chanspec = 0;
 | |
| 
 | |
| 	ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
 | |
| 	if (ret != BCME_NOTASSOCIATED && memcmp(ðer_null, &bssid, ETHER_ADDR_LEN)) {
 | |
| 		if (wl_ext_iovar_getint(dev, "chanspec", (s32 *)&chanspec) == BCME_OK) {
 | |
| 			chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec);
 | |
| 			return chanspec;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static uint16
 | |
| wl_ext_get_chan(struct wl_apsta_params *apsta_params, struct net_device *dev)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	uint16 chan = 0, ctl_chan;
 | |
| 	struct ether_addr bssid;
 | |
| 	u32 chanspec = 0;
 | |
| 
 | |
| 	ret = wldev_ioctl(dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
 | |
| 	if (ret != BCME_NOTASSOCIATED && memcmp(ðer_null, &bssid, ETHER_ADDR_LEN)) {
 | |
| 		if (wl_ext_iovar_getint(dev, "chanspec", (s32 *)&chanspec) == BCME_OK) {
 | |
| 			chanspec = wl_ext_chspec_driver_to_host(apsta_params->ioctl_ver, chanspec);
 | |
| 			ctl_chan = wf_chspec_ctlchan(chanspec);
 | |
| 			chan = (u16)(ctl_chan & 0x00FF);
 | |
| 			return chan;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static chanspec_t
 | |
| wl_ext_chan_to_chanspec(struct wl_apsta_params *apsta_params,
 | |
| 	struct net_device *dev, uint16 channel)
 | |
| {
 | |
| 	s32 _chan = channel;
 | |
| 	chanspec_t chspec = 0;
 | |
| 	chanspec_t fw_chspec = 0;
 | |
| 	u32 bw = WL_CHANSPEC_BW_20;
 | |
| 	s32 err = BCME_OK;
 | |
| 	s32 bw_cap = 0;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	struct {
 | |
| 		u32 band;
 | |
| 		u32 bw_cap;
 | |
| 	} param = {0, 0};
 | |
| 	uint band;
 | |
| 
 | |
| 	if (_chan <= CH_MAX_2G_CHANNEL)
 | |
| 		band = IEEE80211_BAND_2GHZ;
 | |
| 	else
 | |
| 		band = IEEE80211_BAND_5GHZ;
 | |
| 
 | |
| 	if (band == IEEE80211_BAND_5GHZ) {
 | |
| 		param.band = WLC_BAND_5G;
 | |
| 		err = wl_ext_iovar_getbuf(dev, "bw_cap", ¶m, sizeof(param),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (err) {
 | |
| 			if (err != BCME_UNSUPPORTED) {
 | |
| 				AEXT_ERROR(dev->name, "bw_cap failed, %d\n", err);
 | |
| 				return err;
 | |
| 			} else {
 | |
| 				err = wl_ext_iovar_getint(dev, "mimo_bw_cap", &bw_cap);
 | |
| 				if (bw_cap != WLC_N_BW_20ALL)
 | |
| 					bw = WL_CHANSPEC_BW_40;
 | |
| 			}
 | |
| 		} else {
 | |
| 			if (WL_BW_CAP_80MHZ(iovar_buf[0]))
 | |
| 				bw = WL_CHANSPEC_BW_80;
 | |
| 			else if (WL_BW_CAP_40MHZ(iovar_buf[0]))
 | |
| 				bw = WL_CHANSPEC_BW_40;
 | |
| 			else
 | |
| 				bw = WL_CHANSPEC_BW_20;
 | |
| 		}
 | |
| 	}
 | |
| 	else if (band == IEEE80211_BAND_2GHZ)
 | |
| 		bw = WL_CHANSPEC_BW_20;
 | |
| 
 | |
| set_channel:
 | |
| 	chspec = wf_channel2chspec(_chan, bw);
 | |
| 	if (wf_chspec_valid(chspec)) {
 | |
| 		fw_chspec = wl_ext_chspec_host_to_driver(apsta_params->ioctl_ver, chspec);
 | |
| 		if (fw_chspec == INVCHANSPEC) {
 | |
| 			AEXT_ERROR(dev->name, "failed to convert host chanspec to fw chanspec\n");
 | |
| 			fw_chspec = 0;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (bw == WL_CHANSPEC_BW_80)
 | |
| 			bw = WL_CHANSPEC_BW_40;
 | |
| 		else if (bw == WL_CHANSPEC_BW_40)
 | |
| 			bw = WL_CHANSPEC_BW_20;
 | |
| 		else
 | |
| 			bw = 0;
 | |
| 		if (bw)
 | |
| 			goto set_channel;
 | |
| 		AEXT_ERROR(dev->name, "Invalid chanspec 0x%x\n", chspec);
 | |
| 		err = BCME_ERROR;
 | |
| 	}
 | |
| 
 | |
| 	return fw_chspec;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_ext_radar_detect(struct net_device *dev)
 | |
| {
 | |
| 	int ret = BCME_OK;
 | |
| 	bool radar = FALSE;
 | |
| 	s32 val = 0;
 | |
| 
 | |
| 	if ((ret = wldev_ioctl(dev, WLC_GET_RADAR, &val, sizeof(int), false) == 0)) {
 | |
| 		radar = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	return radar;
 | |
| }
 | |
| 
 | |
| static struct wl_if_info *
 | |
| wl_ext_if_enabled(struct wl_apsta_params *apsta_params, ifmode_t ifmode)
 | |
| {
 | |
| 	struct wl_if_info *tmp_if, *target_if = NULL;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if && tmp_if->ifmode == ifmode &&
 | |
| 				wl_get_isam_status(tmp_if, IF_READY)) {
 | |
| 			if (wl_ext_get_chan(apsta_params, tmp_if->dev)) {
 | |
| 				target_if = tmp_if;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return target_if;
 | |
| }
 | |
| 
 | |
| #ifndef WL_STATIC_IF
 | |
| s32
 | |
| wl_ext_add_del_bss(struct net_device *ndev, s32 bsscfg_idx,
 | |
| 	int iftype, s32 del, u8 *addr)
 | |
| {
 | |
| 	s32 ret = BCME_OK;
 | |
| 	s32 val = 0;
 | |
| 	u8 ioctl_buf[WLC_IOCTL_SMLEN];
 | |
| 	struct {
 | |
| 		s32 cfg;
 | |
| 		s32 val;
 | |
| 		struct ether_addr ea;
 | |
| 	} bss_setbuf;
 | |
| 
 | |
| 	AEXT_TRACE(ndev->name, "wl_iftype:%d del:%d \n", iftype, del);
 | |
| 
 | |
| 	bzero(&bss_setbuf, sizeof(bss_setbuf));
 | |
| 
 | |
| 	/* AP=2, STA=3, up=1, down=0, val=-1 */
 | |
| 	if (del) {
 | |
| 		val = WLC_AP_IOV_OP_DELETE;
 | |
| 	} else if (iftype == WL_INTERFACE_CREATE_AP) {
 | |
| 		/* Add/role change to AP Interface */
 | |
| 		AEXT_TRACE(ndev->name, "Adding AP Interface\n");
 | |
| 		val = WLC_AP_IOV_OP_MANUAL_AP_BSSCFG_CREATE;
 | |
| 	} else if (iftype == WL_INTERFACE_CREATE_STA) {
 | |
| 		/* Add/role change to STA Interface */
 | |
| 		AEXT_TRACE(ndev->name, "Adding STA Interface\n");
 | |
| 		val = WLC_AP_IOV_OP_MANUAL_STA_BSSCFG_CREATE;
 | |
| 	} else {
 | |
| 		AEXT_ERROR(ndev->name, "add_del_bss NOT supported for IFACE type:0x%x", iftype);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (!del) {
 | |
| 		wl_ext_bss_iovar_war(ndev, &val);
 | |
| 	}
 | |
| 
 | |
| 	bss_setbuf.cfg = htod32(bsscfg_idx);
 | |
| 	bss_setbuf.val = htod32(val);
 | |
| 
 | |
| 	if (addr) {
 | |
| 		memcpy(&bss_setbuf.ea.octet, addr, ETH_ALEN);
 | |
| 	}
 | |
| 
 | |
| 	AEXT_INFO(ndev->name, "wl bss %d bssidx:%d\n", val, bsscfg_idx);
 | |
| 	ret = wl_ext_iovar_setbuf(ndev, "bss", &bss_setbuf, sizeof(bss_setbuf),
 | |
| 		ioctl_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 	if (ret != 0)
 | |
| 		WL_ERR(("'bss %d' failed with %d\n", val, ret));
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_interface_ops(struct net_device *dev,
 | |
| 	struct wl_apsta_params *apsta_params, int iftype, u8 *addr)
 | |
| {
 | |
| 	s32 ret;
 | |
| 	struct wl_interface_create_v2 iface;
 | |
| 	wl_interface_create_v3_t iface_v3;
 | |
| 	struct wl_interface_info_v1 *info;
 | |
| 	wl_interface_info_v2_t *info_v2;
 | |
| 	uint32 ifflags = 0;
 | |
| 	bool use_iface_info_v2 = false;
 | |
| 	u8 ioctl_buf[WLC_IOCTL_SMLEN];
 | |
| 	wl_wlc_version_t wlc_ver;
 | |
| 
 | |
| 	/* Interface create */
 | |
| 	bzero(&iface, sizeof(iface));
 | |
| 
 | |
| 	if (addr) {
 | |
| 		ifflags |= WL_INTERFACE_MAC_USE;
 | |
| 	}
 | |
| 
 | |
| 	ret = wldev_iovar_getbuf(dev, "wlc_ver", NULL, 0,
 | |
| 		&wlc_ver, sizeof(wl_wlc_version_t), NULL);
 | |
| 	if ((ret == BCME_OK) && (wlc_ver.wlc_ver_major >= 5)) {
 | |
| 		ret = wldev_iovar_getbuf(dev, "interface_create",
 | |
| 			&iface, sizeof(struct wl_interface_create_v2),
 | |
| 			ioctl_buf, sizeof(ioctl_buf), NULL);
 | |
| 		if ((ret == BCME_OK) && (*((uint32 *)ioctl_buf) == WL_INTERFACE_CREATE_VER_3)) {
 | |
| 			use_iface_info_v2 = true;
 | |
| 			bzero(&iface_v3, sizeof(wl_interface_create_v3_t));
 | |
| 			iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
 | |
| 			iface_v3.iftype = iftype;
 | |
| 			iface_v3.flags = ifflags;
 | |
| 			if (addr) {
 | |
| 				memcpy(&iface_v3.mac_addr.octet, addr, ETH_ALEN);
 | |
| 			}
 | |
| 			ret = wl_ext_iovar_getbuf(dev, "interface_create",
 | |
| 				&iface_v3, sizeof(wl_interface_create_v3_t),
 | |
| 				ioctl_buf, sizeof(ioctl_buf), NULL);
 | |
| 			if (unlikely(ret)) {
 | |
| 				WL_ERR(("Interface v3 create failed!! ret %d\n", ret));
 | |
| 				return ret;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* success case */
 | |
| 	if (use_iface_info_v2 == true) {
 | |
| 		info_v2 = (wl_interface_info_v2_t *)ioctl_buf;
 | |
| 		ret = info_v2->bsscfgidx;
 | |
| 	} else {
 | |
| 		/* Use v1 struct */
 | |
| 		iface.ver = WL_INTERFACE_CREATE_VER_2;
 | |
| 		iface.iftype = iftype;
 | |
| 		iface.flags = ifflags;
 | |
| 		if (addr) {
 | |
| 			memcpy(&iface.mac_addr.octet, addr, ETH_ALEN);
 | |
| 		}
 | |
| 		ret = wldev_iovar_getbuf(dev, "interface_create",
 | |
| 			&iface, sizeof(struct wl_interface_create_v2),
 | |
| 			ioctl_buf, sizeof(ioctl_buf), NULL);
 | |
| 		if (ret == BCME_OK) {
 | |
| 			info = (struct wl_interface_info_v1 *)ioctl_buf;
 | |
| 			ret = info->bsscfgidx;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	AEXT_INFO(dev->name, "wl interface create success!! bssidx:%d \n", ret);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_wait_netif_change(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	rtnl_unlock();
 | |
| 	wait_event_interruptible_timeout(apsta_params->netif_change_event,
 | |
| 		wl_get_isam_status(cur_if, IF_READY),
 | |
| 		msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
 | |
| 	rtnl_lock();
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_interface_create(struct net_device *dev, struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if, int iftype, u8 *addr)
 | |
| {
 | |
| 	s32 ret;
 | |
| 
 | |
| 	wl_set_isam_status(cur_if, IF_ADDING);
 | |
| 	ret = wl_ext_interface_ops(dev, apsta_params, iftype, addr);
 | |
| 	if (ret == BCME_UNSUPPORTED) {
 | |
| 		wl_ext_add_del_bss(dev, 1, iftype, 0, addr);
 | |
| 	}
 | |
| 	wl_ext_wait_netif_change(apsta_params, cur_if);
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_iapsta_intf_add(struct net_device *dev, struct wl_apsta_params *apsta_params)
 | |
| {
 | |
| 	struct dhd_pub *dhd;
 | |
| 	apstamode_t apstamode = apsta_params->apstamode;
 | |
| 	struct wl_if_info *cur_if;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	wl_p2p_if_t ifreq;
 | |
| 	struct ether_addr mac_addr;
 | |
| 
 | |
| 	dhd = dhd_get_pub(dev);
 | |
| 	bzero(&mac_addr, sizeof(mac_addr));
 | |
| 
 | |
| 	if (apstamode == ISTAAP_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
 | |
| 	}
 | |
| 	else if (apstamode == ISTAGO_MODE) {
 | |
| 		bzero(&ifreq, sizeof(wl_p2p_if_t));
 | |
| 		ifreq.type = htod32(WL_P2P_IF_GO);
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_set_isam_status(cur_if, IF_ADDING);
 | |
| 		wl_ext_iovar_setbuf(dev, "p2p_ifadd", &ifreq, sizeof(ifreq),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		wl_ext_wait_netif_change(apsta_params, cur_if);
 | |
| 	}
 | |
| 	else if (apstamode == ISTASTA_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
 | |
| 		mac_addr.octet[0] |= 0x02;
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA,
 | |
| 			(u8*)&mac_addr);
 | |
| 	}
 | |
| 	else if (apstamode == IDUALAP_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
 | |
| 	}
 | |
| 	else if (apstamode == ISTAAPAP_MODE) {
 | |
| 		u8 rand_bytes[2] = {0, };
 | |
| 		get_random_bytes(&rand_bytes, sizeof(rand_bytes));
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
 | |
| 		mac_addr.octet[0] |= 0x02;
 | |
| 		mac_addr.octet[5] += 0x01;
 | |
| 		memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
 | |
| 			(u8*)&mac_addr);
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF2];
 | |
| 		memcpy(&mac_addr, dev->dev_addr, ETHER_ADDR_LEN);
 | |
| 		mac_addr.octet[0] |= 0x02;
 | |
| 		mac_addr.octet[5] += 0x02;
 | |
| 		memcpy(&mac_addr.octet[3], rand_bytes, sizeof(rand_bytes));
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP,
 | |
| 			(u8*)&mac_addr);
 | |
| 	}
 | |
| #ifdef WLMESH
 | |
| 	else if (apstamode == ISTAMESH_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
 | |
| 	}
 | |
| 	else if (apstamode == IMESHAP_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
 | |
| 	}
 | |
| 	else if (apstamode == ISTAAPMESH_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF2];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_STA, NULL);
 | |
| 	}
 | |
| 	else if (apstamode == IMESHAPAP_MODE) {
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
 | |
| 		cur_if = &apsta_params->if_info[IF_VIF2];
 | |
| 		wl_ext_interface_create(dev, apsta_params, cur_if, WL_INTERFACE_CREATE_AP, NULL);
 | |
| 	}
 | |
| #endif /* WLMESH */
 | |
| 
 | |
| }
 | |
| #endif /* WL_STATIC_IF */
 | |
| 
 | |
| static void
 | |
| wl_ext_iapsta_preinit(struct net_device *dev, struct wl_apsta_params *apsta_params)
 | |
| {
 | |
| 	struct dhd_pub *dhd;
 | |
| 	apstamode_t apstamode = apsta_params->apstamode;
 | |
| 	struct wl_if_info *cur_if;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	s32 val = 0;
 | |
| 	int i;
 | |
| 
 | |
| 	dhd = dhd_get_pub(dev);
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		cur_if = &apsta_params->if_info[i];
 | |
| 		if (i >= 1 && !strlen(cur_if->ifname))
 | |
| 			snprintf(cur_if->ifname, IFNAMSIZ, "wlan%d", i);
 | |
| 		if (cur_if->ifmode == ISTA_MODE) {
 | |
| 			cur_if->channel = 0;
 | |
| 			cur_if->maxassoc = -1;
 | |
| 			cur_if->prio = PRIO_STA;
 | |
| 			cur_if->prefix = 'S';
 | |
| 			snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
 | |
| 		} else if (cur_if->ifmode == IAP_MODE) {
 | |
| 			cur_if->channel = 1;
 | |
| 			cur_if->maxassoc = -1;
 | |
| 			cur_if->prio = PRIO_AP;
 | |
| 			cur_if->prefix = 'A';
 | |
| 			snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
 | |
| #ifdef WLMESH
 | |
| 		} else if (cur_if->ifmode == IMESH_MODE) {
 | |
| 			cur_if->channel = 1;
 | |
| 			cur_if->maxassoc = -1;
 | |
| 			cur_if->prio = PRIO_MESH;
 | |
| 			cur_if->prefix = 'M';
 | |
| 			snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
 | |
| #ifdef WL_ESCAN
 | |
| 			if (i == 0 && apsta_params->macs)
 | |
| 				wl_mesh_escan_attach(dhd, cur_if);
 | |
| #endif /* WL_ESCAN */
 | |
| #endif /* WLMESH */
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (FW_SUPPORTED(dhd, rsdb)) {
 | |
| 		if (apstamode == IDUALAP_MODE)
 | |
| 			apsta_params->rsdb = -1;
 | |
| 		else if (apstamode == ISTAAPAP_MODE)
 | |
| 			apsta_params->rsdb = 0;
 | |
| 		if (apstamode == ISTAAPAP_MODE || apstamode == IDUALAP_MODE ||
 | |
| 				apstamode == IMESHONLY_MODE || apstamode == ISTAMESH_MODE ||
 | |
| 				apstamode == IMESHAP_MODE || apstamode == ISTAAPMESH_MODE ||
 | |
| 				apstamode == IMESHAPAP_MODE) {
 | |
| 			wl_config_t rsdb_mode_cfg = {0, 0};
 | |
| 			rsdb_mode_cfg.config = apsta_params->rsdb;
 | |
| 			AEXT_INFO(dev->name, "set rsdb_mode %d\n", rsdb_mode_cfg.config);
 | |
| 			wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 			wl_ext_iovar_setbuf(dev, "rsdb_mode", &rsdb_mode_cfg,
 | |
| 				sizeof(rsdb_mode_cfg), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 			wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 		}
 | |
| 	} else {
 | |
| 		apsta_params->rsdb = 0;
 | |
| 	}
 | |
| 
 | |
| 	if (apstamode == ISTAONLY_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
 | |
| 		// don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 	} else if (apstamode == IAPONLY_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| #ifdef ARP_OFFLOAD_SUPPORT
 | |
| 		/* IF SoftAP is enabled, disable arpoe */
 | |
| 		dhd_arp_offload_set(dhd, 0);
 | |
| 		dhd_arp_offload_enable(dhd, FALSE);
 | |
| #endif /* ARP_OFFLOAD_SUPPORT */
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 0);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 0);
 | |
| 		val = 1;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);
 | |
| #ifdef PROP_TXSTATUS_VSDB
 | |
| #if defined(BCMSDIO)
 | |
| 		if (!(FW_SUPPORTED(dhd, rsdb)) && !disable_proptx) {
 | |
| 			bool enabled;
 | |
| 			dhd_wlfc_get_enable(dhd, &enabled);
 | |
| 			if (!enabled) {
 | |
| 				dhd_wlfc_init(dhd);
 | |
| 				wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 			}
 | |
| 		}
 | |
| #endif /* BCMSDIO */
 | |
| #endif /* PROP_TXSTATUS_VSDB */
 | |
| 	}
 | |
| 	else if (apstamode == ISTAAP_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 0);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 1);
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 	}
 | |
| 	else if (apstamode == ISTAGO_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 1);
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 	}
 | |
| 	else if (apstamode == ISTASTA_MODE) {
 | |
| 	}
 | |
| 	else if (apstamode == IDUALAP_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		/* IF SoftAP is enabled, disable arpoe or wlan1 will ping fail */
 | |
| #ifdef ARP_OFFLOAD_SUPPORT
 | |
| 		/* IF SoftAP is enabled, disable arpoe */
 | |
| 		dhd_arp_offload_set(dhd, 0);
 | |
| 		dhd_arp_offload_enable(dhd, FALSE);
 | |
| #endif /* ARP_OFFLOAD_SUPPORT */
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 0);
 | |
| 		wl_ext_iovar_setint(dev, "mbcn", 1);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 0);
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 		val = 1;
 | |
| 		wl_ext_ioctl(dev, WLC_SET_AP, &val, sizeof(val), 1);
 | |
| 	}
 | |
| 	else if (apstamode == ISTAAPAP_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 0);
 | |
| 		wl_ext_iovar_setint(dev, "mbss", 1);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 		// don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
 | |
| 	}
 | |
| #ifdef WLMESH
 | |
| 	else if (apstamode == IMESHONLY_MODE || apstamode == ISTAMESH_MODE ||
 | |
| 			apstamode == IMESHAP_MODE || apstamode == ISTAAPMESH_MODE ||
 | |
| 			apstamode == IMESHAPAP_MODE) {
 | |
| 		int pm = 0;
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 0);
 | |
| 		if (apstamode == IMESHONLY_MODE)
 | |
| 			wl_ext_ioctl(dev, WLC_SET_PM, &pm, sizeof(pm), 1);
 | |
| 		else
 | |
| 			wl_ext_iovar_setint(dev, "mbcn", 1);
 | |
| 		wl_ext_iovar_setint(dev, "apsta", 1); // keep 1 as we set in dhd_preinit_ioctls
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 		// don't set WLC_SET_AP to 0, some parameters will be reset, such as bcn_timeout and roam_off
 | |
| 	}
 | |
| #endif /* WLMESH */
 | |
| 
 | |
| 	wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
 | |
| 	apsta_params->init = TRUE;
 | |
| 
 | |
| 	WL_MSG(dev->name, "apstamode=%d\n", apstamode);
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_isam_param(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	int ret = -1;
 | |
| 	char *pick_tmp, *data, *param;
 | |
| 	int bytes_written=-1;
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
 | |
| 
 | |
| 	pick_tmp = command;
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0); // pick isam_param
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
 | |
| 	while (param != NULL) {
 | |
| 		data = bcmstrtok(&pick_tmp, " ", 0); // pick data
 | |
| 		if (!strcmp(param, "acs")) {
 | |
| 			if (data) {
 | |
| 				apsta_params->acs = simple_strtol(data, NULL, 0);
 | |
| 				ret = 0;
 | |
| 			} else {
 | |
| 				bytes_written = snprintf(command, total_len, "%d", apsta_params->acs);
 | |
| 				ret = bytes_written;
 | |
| 				goto exit;
 | |
| 			}
 | |
| 		}
 | |
| 		param = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_isam_init(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	char *pch, *pick_tmp, *pick_tmp2, *param;
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	int i;
 | |
| 
 | |
| 	if (apsta_params->init) {
 | |
| 		AEXT_ERROR(dev->name, "don't init twice\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 	AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
 | |
| 
 | |
| 	pick_tmp = command;
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_init
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 	while (param != NULL) {
 | |
| 		pick_tmp2 = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 		if (!pick_tmp2) {
 | |
| 			AEXT_ERROR(dev->name, "wrong param %s\n", param);
 | |
| 			return -1;
 | |
| 		}
 | |
| 		if (!strcmp(param, "mode")) {
 | |
| 			pch = NULL;
 | |
| 			if (!strcmp(pick_tmp2, "sta")) {
 | |
| 				apsta_params->apstamode = ISTAONLY_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "ap")) {
 | |
| 				apsta_params->apstamode = IAPONLY_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "sta-ap")) {
 | |
| 				apsta_params->apstamode = ISTAAP_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "sta-sta")) {
 | |
| 				apsta_params->apstamode = ISTASTA_MODE;
 | |
| 				apsta_params->vsdb = TRUE;
 | |
| 			} else if (!strcmp(pick_tmp2, "ap-ap")) {
 | |
| 				apsta_params->apstamode = IDUALAP_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "sta-ap-ap")) {
 | |
| 				apsta_params->apstamode = ISTAAPAP_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "apsta")) {
 | |
| 				apsta_params->apstamode = ISTAAP_MODE;
 | |
| 				apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
 | |
| 				apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "dualap")) {
 | |
| 				apsta_params->apstamode = IDUALAP_MODE;
 | |
| 				apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
 | |
| 				apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "sta-go") ||
 | |
| 					!strcmp(pick_tmp2, "gosta")) {
 | |
| 				if (!FW_SUPPORTED(dhd, p2p)) {
 | |
| 					return -1;
 | |
| 				}
 | |
| 				apsta_params->apstamode = ISTAGO_MODE;
 | |
| 				apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
 | |
| 				apsta_params->if_info[IF_VIF].ifmode = IAP_MODE;
 | |
| #ifdef WLMESH
 | |
| 			} else if (!strcmp(pick_tmp2, "mesh")) {
 | |
| 				apsta_params->apstamode = IMESHONLY_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "sta-mesh")) {
 | |
| 				apsta_params->apstamode = ISTAMESH_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "sta-ap-mesh")) {
 | |
| 				apsta_params->apstamode = ISTAAPMESH_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "mesh-ap")) {
 | |
| 				apsta_params->apstamode = IMESHAP_MODE;
 | |
| 			} else if (!strcmp(pick_tmp2, "mesh-ap-ap")) {
 | |
| 				apsta_params->apstamode = IMESHAPAP_MODE;
 | |
| #endif /* WLMESH */
 | |
| 			} else {
 | |
| 				AEXT_ERROR(dev->name, "mode [sta|ap|sta-ap|ap-ap]\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 			pch = bcmstrtok(&pick_tmp2, " -", 0);
 | |
| 			for (i=0; i<MAX_IF_NUM && pch; i++) {
 | |
| 				if (!strcmp(pch, "sta"))
 | |
| 					apsta_params->if_info[i].ifmode = ISTA_MODE;
 | |
| 				else if (!strcmp(pch, "ap"))
 | |
| 					apsta_params->if_info[i].ifmode = IAP_MODE;
 | |
| #ifdef WLMESH
 | |
| 				else if (!strcmp(pch, "mesh")) {
 | |
| 					if (dhd->conf->fw_type != FW_TYPE_MESH) {
 | |
| 						AEXT_ERROR(dev->name, "wrong fw type\n");
 | |
| 						return -1;
 | |
| 					}
 | |
| 					apsta_params->if_info[i].ifmode = IMESH_MODE;
 | |
| 				}
 | |
| #endif /* WLMESH */
 | |
| 				pch = bcmstrtok(&pick_tmp2, " -", 0);
 | |
| 			}
 | |
| 		}
 | |
| 		else if (!strcmp(param, "rsdb")) {
 | |
| 			apsta_params->rsdb = (int)simple_strtol(pick_tmp2, NULL, 0);
 | |
| 		} else if (!strcmp(param, "vsdb")) {
 | |
| 			if (!strcmp(pick_tmp2, "y")) {
 | |
| 				apsta_params->vsdb = TRUE;
 | |
| 			} else if (!strcmp(pick_tmp2, "n")) {
 | |
| 				apsta_params->vsdb = FALSE;
 | |
| 			} else {
 | |
| 				AEXT_ERROR(dev->name, "vsdb [y|n]\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 		} else if (!strcmp(param, "csa")) {
 | |
| 			apsta_params->csa = (int)simple_strtol(pick_tmp2, NULL, 0);
 | |
| 		} else if (!strcmp(param, "acs")) {
 | |
| 			apsta_params->acs = (int)simple_strtol(pick_tmp2, NULL, 0);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 		} else if (!strcmp(param, "macs")) {
 | |
| 			apsta_params->macs = (int)simple_strtol(pick_tmp2, NULL, 0);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 		} else if (!strcmp(param, "ifname")) {
 | |
| 			pch = NULL;
 | |
| 			pch = bcmstrtok(&pick_tmp2, " -", 0);
 | |
| 			for (i=0; i<MAX_IF_NUM && pch; i++) {
 | |
| 				strcpy(apsta_params->if_info[i].ifname, pch);
 | |
| 				pch = bcmstrtok(&pick_tmp2, " -", 0);
 | |
| 			}
 | |
| 		} else if (!strcmp(param, "vifname")) {
 | |
| 			strcpy(apsta_params->if_info[IF_VIF].ifname, pick_tmp2);
 | |
| 		}
 | |
| 		param = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 	}
 | |
| 
 | |
| 	if (apsta_params->apstamode == 0) {
 | |
| 		AEXT_ERROR(dev->name, "mode [sta|ap|sta-ap|ap-ap]\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_iapsta_preinit(dev, apsta_params);
 | |
| #ifndef WL_STATIC_IF
 | |
| 	wl_ext_iapsta_intf_add(dev, apsta_params);
 | |
| #endif /* WL_STATIC_IF */
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_parse_config(struct wl_if_info *cur_if, char *command, char **pick_next)
 | |
| {
 | |
| 	char *pch, *pick_tmp;
 | |
| 	char name[20], data[100];
 | |
| 	int i, j, len;
 | |
| 	char *ifname_head = NULL;
 | |
| 
 | |
| 	typedef struct config_map_t {
 | |
| 		char name[20];
 | |
| 		char *head;
 | |
| 		char *tail;
 | |
| 	} config_map_t;
 | |
| 
 | |
| 	config_map_t config_map [] = {
 | |
| 		{" ifname ",	NULL, NULL},
 | |
| 		{" ssid ",		NULL, NULL},
 | |
| 		{" bssid ", 	NULL, NULL},
 | |
| 		{" bgnmode ",	NULL, NULL},
 | |
| 		{" hidden ",	NULL, NULL},
 | |
| 		{" maxassoc ",	NULL, NULL},
 | |
| 		{" chan ",		NULL, NULL},
 | |
| 		{" amode ", 	NULL, NULL},
 | |
| 		{" emode ", 	NULL, NULL},
 | |
| 		{" key ",		NULL, NULL},
 | |
| 	};
 | |
| 	config_map_t *row, *row_prev;
 | |
| 
 | |
| 	pick_tmp = command;
 | |
| 
 | |
| 	// reset head and tail
 | |
| 	for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
 | |
| 		row = &config_map[i];
 | |
| 		row->head = NULL;
 | |
| 		row->tail = pick_tmp + strlen(pick_tmp);
 | |
| 	}
 | |
| 
 | |
| 	// pick head
 | |
| 	for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
 | |
| 		row = &config_map[i];
 | |
| 		pch = strstr(pick_tmp, row->name);
 | |
| 		if (pch) {
 | |
| 			row->head = pch;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// sort by head
 | |
| 	for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) {
 | |
| 		row_prev = &config_map[i];
 | |
| 		for (j = i+1; j < sizeof(config_map)/sizeof(config_map[0]); j++) {
 | |
| 			row = &config_map[j];
 | |
| 			if (row->head < row_prev->head) {
 | |
| 				strcpy(name, row_prev->name);
 | |
| 				strcpy(row_prev->name, row->name);
 | |
| 				strcpy(row->name, name);
 | |
| 				pch = row_prev->head;
 | |
| 				row_prev->head = row->head;
 | |
| 				row->head = pch;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// pick tail
 | |
| 	for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]) - 1; i++) {
 | |
| 		row_prev = &config_map[i];
 | |
| 		row = &config_map[i+1];
 | |
| 		if (row_prev->head) {
 | |
| 			row_prev->tail = row->head;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// remove name from head
 | |
| 	for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
 | |
| 		row = &config_map[i];
 | |
| 		if (row->head) {
 | |
| 			if (!strcmp(row->name, " ifname ")) {
 | |
| 				ifname_head = row->head + 1;
 | |
| 				break;
 | |
| 			}
 | |
| 			row->head += strlen(row->name);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for (i = 0; i < sizeof(config_map)/sizeof(config_map[0]); i++) {
 | |
| 		row = &config_map[i];
 | |
| 		if (row->head) {
 | |
| 			memset(data, 0, sizeof(data));
 | |
| 			if (row->tail && row->tail > row->head) {
 | |
| 				strncpy(data, row->head, row->tail-row->head);
 | |
| 			} else {
 | |
| 				strcpy(data, row->head);
 | |
| 			}
 | |
| 			pick_tmp = data;
 | |
| 
 | |
| 			if (!strcmp(row->name, " ifname ")) {
 | |
| 				break;
 | |
| 			} else if (!strcmp(row->name, " ssid ")) {
 | |
| 				len = strlen(pick_tmp);
 | |
| 				memset(cur_if->ssid, 0, sizeof(cur_if->ssid));
 | |
| 				if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"')
 | |
| 					strncpy(cur_if->ssid, &pick_tmp[1], len-2);
 | |
| 				else
 | |
| 					strcpy(cur_if->ssid, pick_tmp);
 | |
| 			} else if (!strcmp(row->name, " bssid ")) {
 | |
| 				pch = bcmstrtok(&pick_tmp, ": ", 0);
 | |
| 				for (j=0; j<6 && pch; j++) {
 | |
| 					((u8 *)&cur_if->bssid)[j] = (int)simple_strtol(pch, NULL, 16);
 | |
| 					pch = bcmstrtok(&pick_tmp, ": ", 0);
 | |
| 				}
 | |
| 			} else if (!strcmp(row->name, " bgnmode ")) {
 | |
| 				if (!strcmp(pick_tmp, "b"))
 | |
| 					cur_if->bgnmode = IEEE80211B;
 | |
| 				else if (!strcmp(pick_tmp, "g"))
 | |
| 					cur_if->bgnmode = IEEE80211G;
 | |
| 				else if (!strcmp(pick_tmp, "bg"))
 | |
| 					cur_if->bgnmode = IEEE80211BG;
 | |
| 				else if (!strcmp(pick_tmp, "bgn"))
 | |
| 					cur_if->bgnmode = IEEE80211BGN;
 | |
| 				else if (!strcmp(pick_tmp, "bgnac"))
 | |
| 					cur_if->bgnmode = IEEE80211BGNAC;
 | |
| 				else {
 | |
| 					AEXT_ERROR(cur_if->dev->name, "bgnmode [b|g|bg|bgn|bgnac]\n");
 | |
| 					return -1;
 | |
| 				}
 | |
| 			} else if (!strcmp(row->name, " hidden ")) {
 | |
| 				if (!strcmp(pick_tmp, "n"))
 | |
| 					cur_if->hidden = 0;
 | |
| 				else if (!strcmp(pick_tmp, "y"))
 | |
| 					cur_if->hidden = 1;
 | |
| 				else {
 | |
| 					AEXT_ERROR(cur_if->dev->name, "hidden [y|n]\n");
 | |
| 					return -1;
 | |
| 				}
 | |
| 			} else if (!strcmp(row->name, " maxassoc ")) {
 | |
| 				cur_if->maxassoc = (int)simple_strtol(pick_tmp, NULL, 10);
 | |
| 			} else if (!strcmp(row->name, " chan ")) {
 | |
| 				cur_if->channel = (int)simple_strtol(pick_tmp, NULL, 10);
 | |
| 			} else if (!strcmp(row->name, " amode ")) {
 | |
| 				if (!strcmp(pick_tmp, "open"))
 | |
| 					cur_if->amode = AUTH_OPEN;
 | |
| 				else if (!strcmp(pick_tmp, "shared"))
 | |
| 					cur_if->amode = AUTH_SHARED;
 | |
| 				else if (!strcmp(pick_tmp, "wpapsk"))
 | |
| 					cur_if->amode = AUTH_WPAPSK;
 | |
| 				else if (!strcmp(pick_tmp, "wpa2psk"))
 | |
| 					cur_if->amode = AUTH_WPA2PSK;
 | |
| 				else if (!strcmp(pick_tmp, "wpawpa2psk"))
 | |
| 					cur_if->amode = AUTH_WPAWPA2PSK;
 | |
| 				else if (!strcmp(pick_tmp, "sae"))
 | |
| 					cur_if->amode = AUTH_SAE;
 | |
| 				else {
 | |
| 					AEXT_ERROR(cur_if->dev->name, "amode [open|shared|wpapsk|wpa2psk|wpawpa2psk]\n");
 | |
| 					return -1;
 | |
| 				}
 | |
| 			} else if (!strcmp(row->name, " emode ")) {
 | |
| 				if (!strcmp(pick_tmp, "none"))
 | |
| 					cur_if->emode = ENC_NONE;
 | |
| 				else if (!strcmp(pick_tmp, "wep"))
 | |
| 					cur_if->emode = ENC_WEP;
 | |
| 				else if (!strcmp(pick_tmp, "tkip"))
 | |
| 					cur_if->emode = ENC_TKIP;
 | |
| 				else if (!strcmp(pick_tmp, "aes"))
 | |
| 					cur_if->emode = ENC_AES;
 | |
| 				else if (!strcmp(pick_tmp, "tkipaes"))
 | |
| 					cur_if->emode = ENC_TKIPAES;
 | |
| 				else {
 | |
| 					AEXT_ERROR(cur_if->dev->name, "emode [none|wep|tkip|aes|tkipaes]\n");
 | |
| 					return -1;
 | |
| 				}
 | |
| 			} else if (!strcmp(row->name, " key ")) {
 | |
| 				len = strlen(pick_tmp);
 | |
| 				memset(cur_if->key, 0, sizeof(cur_if->key));
 | |
| 				if (pick_tmp[0] == '"' && pick_tmp[len-1] == '"')
 | |
| 					strncpy(cur_if->key, &pick_tmp[1], len-2);
 | |
| 				else
 | |
| 					strcpy(cur_if->key, pick_tmp);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*pick_next = ifname_head;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iapsta_config(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	int ret=0, i;
 | |
| 	char *pch, *pch2, *pick_tmp, *pick_next=NULL, *param;
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	char ifname[IFNAMSIZ+1];
 | |
| 	struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
 | |
| 
 | |
| 	if (!apsta_params->init) {
 | |
| 		AEXT_ERROR(dev->name, "please init first\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
 | |
| 
 | |
| 	pick_tmp = command;
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_config
 | |
| 
 | |
| 	mutex_lock(&apsta_params->usr_sync);
 | |
| 
 | |
| 	while (pick_tmp != NULL) {
 | |
| 		memset(ifname, 0, IFNAMSIZ+1);
 | |
| 		if (!strncmp(pick_tmp, "ifname ", strlen("ifname "))) {
 | |
| 			pch = pick_tmp + strlen("ifname ");
 | |
| 			pch2 = strchr(pch, ' ');
 | |
| 			if (pch && pch2) {
 | |
| 				strncpy(ifname, pch, pch2-pch);
 | |
| 			} else {
 | |
| 				AEXT_ERROR(dev->name, "ifname [wlanX]\n");
 | |
| 				ret = -1;
 | |
| 				break;
 | |
| 			}
 | |
| 			for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 				tmp_if = &apsta_params->if_info[i];
 | |
| 				if (tmp_if->dev && !strcmp(tmp_if->dev->name, ifname)) {
 | |
| 					cur_if = tmp_if;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 			if (!cur_if) {
 | |
| 				AEXT_ERROR(dev->name, "wrong ifname=%s in apstamode=%d\n",
 | |
| 					ifname, apsta_params->apstamode);
 | |
| 				ret = -1;
 | |
| 				break;
 | |
| 			}
 | |
| 			ret = wl_ext_parse_config(cur_if, pick_tmp, &pick_next);
 | |
| 			if (ret)
 | |
| 				break;
 | |
| 			pick_tmp = pick_next;
 | |
| 		} else {
 | |
| 			AEXT_ERROR(dev->name, "first arg must be ifname\n");
 | |
| 			ret = -1;
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 
 | |
| 	mutex_unlock(&apsta_params->usr_sync);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_assoclist(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int ret = 0, i, maxassoc = 0, bytes_written = 0;
 | |
| 	char mac_buf[MAX_NUM_OF_ASSOCLIST *
 | |
| 		sizeof(struct ether_addr) + sizeof(uint)] = {0};
 | |
| 	struct maclist *assoc_maclist = (struct maclist *)mac_buf;
 | |
| 
 | |
| 	assoc_maclist->count = htod32(MAX_NUM_OF_ASSOCLIST);
 | |
| 	ret = wl_ext_ioctl(dev, WLC_GET_ASSOCLIST, assoc_maclist, sizeof(mac_buf), 0);
 | |
| 	if (ret)
 | |
| 		return -1;
 | |
| 	maxassoc = dtoh32(assoc_maclist->count);
 | |
| 	bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 		"%2s: %12s",
 | |
| 		"no", "------addr------");
 | |
| 	for (i=0; i<maxassoc; i++) {
 | |
| 		bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 			"\n%2d: %pM", i, &assoc_maclist->ea[i]);
 | |
| 	}
 | |
| 
 | |
| 	return bytes_written;
 | |
| }
 | |
| 
 | |
| #ifdef WLMESH
 | |
| static int
 | |
| wl_mesh_print_peer_info(mesh_peer_info_ext_t *mpi_ext,
 | |
| 	uint32 peer_results_count, char *command, int total_len)
 | |
| {
 | |
| 	char *peering_map[] = MESH_PEERING_STATE_STRINGS;
 | |
| 	uint32 count = 0;
 | |
| 	int bytes_written = 0;
 | |
| 
 | |
| 	bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 		"%2s: %12s : %6s : %-6s : %6s :"
 | |
| 		" %5s : %4s : %4s : %11s : %4s",
 | |
| 		"no", "------addr------ ", "l.aid", "state", "p.aid",
 | |
| 		"mppid", "llid", "plid", "entry_state", "rssi");
 | |
| 	for (count=0; count < peer_results_count; count++) {
 | |
| 		if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT) {
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"\n%2d: %pM : 0x%4x : %6s : 0x%4x :"
 | |
| 				" %5d : %4d : %4d : %11s : %4d",
 | |
| 				count, &mpi_ext->ea, mpi_ext->local_aid,
 | |
| 				peering_map[mpi_ext->peer_info.state],
 | |
| 				mpi_ext->peer_info.peer_aid,
 | |
| 				mpi_ext->peer_info.mesh_peer_prot_id,
 | |
| 				mpi_ext->peer_info.local_link_id,
 | |
| 				mpi_ext->peer_info.peer_link_id,
 | |
| 				(mpi_ext->entry_state == MESH_SELF_PEER_ENTRY_STATE_ACTIVE) ?
 | |
| 				"ACTIVE" :
 | |
| 				"EXTERNAL",
 | |
| 				mpi_ext->rssi);
 | |
| 		} else {
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"\n%2d: %pM : %6s : %5s : %6s :"
 | |
| 				" %5s : %4s : %4s : %11s : %4s",
 | |
| 				count, &mpi_ext->ea, "  NA  ", "  NA  ", "  NA  ",
 | |
| 				"  NA ", " NA ", " NA ", "  TIMEDOUT ", " NA ");
 | |
| 		}
 | |
| 		mpi_ext++;
 | |
| 	}
 | |
| 
 | |
| 	return bytes_written;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_mesh_get_peer_results(struct net_device *dev, char *buf, int len)
 | |
| {
 | |
| 	int indata, inlen;
 | |
| 	mesh_peer_info_dump_t *peer_results;
 | |
| 	int ret;
 | |
| 
 | |
| 	memset(buf, 0, len);
 | |
| 	peer_results = (mesh_peer_info_dump_t *)buf;
 | |
| 	indata = htod32(len);
 | |
| 	inlen = 4;
 | |
| 	ret = wl_ext_iovar_getbuf(dev, "mesh_peer_status", &indata, inlen, buf, len, NULL);
 | |
| 	if (!ret) {
 | |
| 		peer_results = (mesh_peer_info_dump_t *)buf;
 | |
| 		ret = peer_results->count;
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_mesh_peer_status(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	int i;
 | |
| 	struct wl_if_info *cur_if;
 | |
| 	mesh_peer_info_dump_t *peer_results;
 | |
| 	mesh_peer_info_ext_t *mpi_ext;
 | |
| 	char *peer_buf = NULL;
 | |
| 	int peer_len = WLC_IOCTL_MAXLEN;
 | |
| 	int dump_written = 0, ret;
 | |
| 
 | |
| 	if (!data) {
 | |
| 		peer_buf = kmalloc(peer_len, GFP_KERNEL);
 | |
| 		if (peer_buf == NULL) {
 | |
| 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 				peer_len); 
 | |
| 			return -1;
 | |
| 		}
 | |
| 		for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 			cur_if = &apsta_params->if_info[i];
 | |
| 			if (cur_if && dev == cur_if->dev && cur_if->ifmode == IMESH_MODE) {
 | |
| 				memset(peer_buf, 0, peer_len);
 | |
| 				ret = wl_mesh_get_peer_results(dev, peer_buf, peer_len);
 | |
| 				if (ret >= 0) {
 | |
| 					peer_results = (mesh_peer_info_dump_t *)peer_buf;
 | |
| 					mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
 | |
| 					dump_written += wl_mesh_print_peer_info(mpi_ext,
 | |
| 						peer_results->count, command+dump_written,
 | |
| 						total_len-dump_written);
 | |
| 				}
 | |
| 			} else if (cur_if && dev == cur_if->dev) {
 | |
| 				AEXT_ERROR(dev->name, "[%s][%c] is not mesh interface\n",
 | |
| 					cur_if->ifname, cur_if->prefix);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (peer_buf)
 | |
| 		kfree(peer_buf);
 | |
| 	return dump_written;
 | |
| }
 | |
| 
 | |
| #ifdef WL_ESCAN
 | |
| #define WL_MESH_DELAY_SCAN_MS	3000
 | |
| static void
 | |
| wl_mesh_timer(unsigned long data)
 | |
| {
 | |
| 	wl_event_msg_t msg;
 | |
| 	struct wl_if_info *mesh_if = (struct wl_if_info *)data;
 | |
| 	struct dhd_pub *dhd;
 | |
| 
 | |
| 	if (!mesh_if) {
 | |
| 		AEXT_ERROR("wlan", "mesh_if is not ready\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!mesh_if->dev) {
 | |
| 		AEXT_ERROR("wlan", "ifidx %d is not ready\n", mesh_if->ifidx);
 | |
| 		return;
 | |
| 	}
 | |
| 	dhd = dhd_get_pub(mesh_if->dev);
 | |
| 
 | |
| 	bzero(&msg, sizeof(wl_event_msg_t));
 | |
| 	AEXT_TRACE(mesh_if->dev->name, "timer expired\n");
 | |
| 
 | |
| 	msg.ifidx = mesh_if->ifidx;
 | |
| 	msg.event_type = hton32(WLC_E_RESERVED);
 | |
| 	msg.reason = 0xFFFFFFFF;
 | |
| 	wl_ext_event_send(dhd->event_params, &msg, NULL);
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_mesh_set_timer(struct wl_if_info *mesh_if, uint timeout)
 | |
| {
 | |
| 	AEXT_TRACE(mesh_if->dev->name, "timeout=%d\n", timeout);
 | |
| 
 | |
| 	if (timer_pending(&mesh_if->delay_scan))
 | |
| 		del_timer_sync(&mesh_if->delay_scan);
 | |
| 
 | |
| 	if (timeout) {
 | |
| 		if (timer_pending(&mesh_if->delay_scan))
 | |
| 			del_timer_sync(&mesh_if->delay_scan);
 | |
| 		mod_timer(&mesh_if->delay_scan, jiffies + msecs_to_jiffies(timeout));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_mesh_clear_vndr_ie(struct net_device *dev, uchar *oui)
 | |
| {
 | |
| 	char *vndr_ie_buf = NULL;
 | |
| 	vndr_ie_setbuf_t *vndr_ie = NULL;
 | |
| 	ie_getbuf_t vndr_ie_tmp;
 | |
| 	char *iovar_buf = NULL;
 | |
| 	int err = -1, i;
 | |
| 	vndr_ie_buf_t *vndr_ie_dump = NULL;
 | |
| 	uchar *iebuf;
 | |
| 	vndr_ie_info_t *ie_info;
 | |
| 	vndr_ie_t *ie;
 | |
| 
 | |
| 	vndr_ie_buf = kzalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
 | |
| 	if (!vndr_ie_buf) {
 | |
| 		AEXT_ERROR(dev->name, "IE memory alloc failed\n");
 | |
| 		err = -ENOMEM;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	iovar_buf = kzalloc(WLC_IOCTL_MEDLEN, GFP_KERNEL);
 | |
| 	if (!iovar_buf) {
 | |
| 		AEXT_ERROR(dev->name, "iovar_buf alloc failed\n");
 | |
| 		err = -ENOMEM;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	memset(iovar_buf, 0, WLC_IOCTL_MEDLEN);
 | |
| 	vndr_ie_tmp.pktflag = (uint32) -1;
 | |
| 	vndr_ie_tmp.id = (uint8) DOT11_MNG_PROPR_ID;
 | |
| 	err = wl_ext_iovar_getbuf(dev, "vndr_ie", &vndr_ie_tmp, sizeof(vndr_ie_tmp),
 | |
| 		iovar_buf, WLC_IOCTL_MEDLEN, NULL);
 | |
| 	if (err)
 | |
| 		goto exit;
 | |
| 
 | |
| 	vndr_ie_dump = (vndr_ie_buf_t *)iovar_buf;
 | |
| 	if (!vndr_ie_dump->iecount)
 | |
| 		goto exit;
 | |
| 
 | |
| 	iebuf = (uchar *)&vndr_ie_dump->vndr_ie_list[0];
 | |
| 	for (i=0; i<vndr_ie_dump->iecount; i++) {
 | |
| 		ie_info = (vndr_ie_info_t *) iebuf;
 | |
| 		ie = &ie_info->vndr_ie_data;
 | |
| 		if (memcmp(ie->oui, oui, 3))
 | |
| 			memset(ie->oui, 0, 3);
 | |
| 		iebuf += sizeof(uint32) + ie->len + VNDR_IE_HDR_LEN;
 | |
| 	}
 | |
| 
 | |
| 	vndr_ie = (vndr_ie_setbuf_t *) vndr_ie_buf;
 | |
| 	strncpy(vndr_ie->cmd, "del", VNDR_IE_CMD_LEN - 1);
 | |
| 	vndr_ie->cmd[VNDR_IE_CMD_LEN - 1] = '\0';
 | |
| 	memcpy(&vndr_ie->vndr_ie_buffer, vndr_ie_dump, WLC_IOCTL_SMLEN-VNDR_IE_CMD_LEN-1);
 | |
| 
 | |
| 	memset(iovar_buf, 0, WLC_IOCTL_MEDLEN);
 | |
| 	err = wl_ext_iovar_setbuf(dev, "vndr_ie", vndr_ie, WLC_IOCTL_SMLEN, iovar_buf,
 | |
| 		WLC_IOCTL_MEDLEN, NULL);
 | |
| 
 | |
| exit:
 | |
| 	if (vndr_ie) {
 | |
| 		kfree(vndr_ie);
 | |
| 	}
 | |
| 	if (iovar_buf) {
 | |
| 		kfree(iovar_buf);
 | |
| 	}
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_mesh_clear_mesh_info(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *mesh_if, bool scan)
 | |
| {
 | |
| 	struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
 | |
| 	uchar mesh_oui[]={0x00, 0x22, 0xf4};
 | |
| 	int ret;
 | |
| 
 | |
| 	AEXT_TRACE(mesh_if->dev->name, "Enter\n");
 | |
| 
 | |
| 	ret = wl_mesh_clear_vndr_ie(mesh_if->dev, mesh_oui);
 | |
| 	memset(mesh_info, 0, sizeof(struct wl_mesh_params));
 | |
| 	if (scan) {
 | |
| 		mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
 | |
| 		wl_mesh_set_timer(mesh_if, 100);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_mesh_update_vndr_ie(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *mesh_if)
 | |
| {
 | |
| 	struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
 | |
| 	char *vndr_ie;
 | |
| 	uchar mesh_oui[]={0x00, 0x22, 0xf4};
 | |
| 	int bytes_written = 0;
 | |
| 	int ret = 0, i, vndr_ie_len;
 | |
| 	uint8 *peer_bssid;
 | |
| 
 | |
| 	wl_mesh_clear_vndr_ie(mesh_if->dev, mesh_oui);
 | |
| 
 | |
| 	vndr_ie_len = WLC_IOCTL_MEDLEN;
 | |
| 	vndr_ie = kmalloc(vndr_ie_len, GFP_KERNEL);
 | |
| 	if (vndr_ie == NULL) {
 | |
| 		AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 			WLC_IOCTL_MEDLEN); 
 | |
| 		ret = -1;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
 | |
| 		"0x%02x%02x%02x", mesh_oui[0], mesh_oui[1], mesh_oui[2]);
 | |
| 
 | |
| 	bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
 | |
| 		"%02x%02x%02x%02x%02x%02x%02x%02x", MESH_INFO_MASTER_BSSID, ETHER_ADDR_LEN,
 | |
| 		((u8 *)(&mesh_info->master_bssid))[0], ((u8 *)(&mesh_info->master_bssid))[1],
 | |
| 		((u8 *)(&mesh_info->master_bssid))[2], ((u8 *)(&mesh_info->master_bssid))[3],
 | |
| 		((u8 *)(&mesh_info->master_bssid))[4], ((u8 *)(&mesh_info->master_bssid))[5]);
 | |
| 
 | |
| 	bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
 | |
| 		"%02x%02x%02x", MESH_INFO_MASTER_CHANNEL, 1, mesh_info->master_channel);
 | |
| 
 | |
| 	bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
 | |
| 		"%02x%02x%02x", MESH_INFO_HOP_CNT, 1, mesh_info->hop_cnt);
 | |
| 
 | |
| 	bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
 | |
| 		"%02x%02x", MESH_INFO_PEER_BSSID, mesh_info->hop_cnt*ETHER_ADDR_LEN);
 | |
| 	for (i=0; i<mesh_info->hop_cnt && i<MAX_HOP_LIST; i++) {
 | |
| 		peer_bssid = (uint8 *)&mesh_info->peer_bssid[i];
 | |
| 		bytes_written += snprintf(vndr_ie+bytes_written, vndr_ie_len,
 | |
| 			"%02x%02x%02x%02x%02x%02x",
 | |
|  			peer_bssid[0], peer_bssid[1], peer_bssid[2],
 | |
| 			peer_bssid[3], peer_bssid[4], peer_bssid[5]);
 | |
| 	}
 | |
| 
 | |
| 	ret = wl_ext_add_del_ie(mesh_if->dev, VNDR_IE_BEACON_FLAG|VNDR_IE_PRBRSP_FLAG,
 | |
| 		vndr_ie, "add");
 | |
| 	if (!ret) {
 | |
| 		AEXT_INFO(mesh_if->dev->name, "mbssid=%pM, mchannel=%d, hop=%d, pbssid=%pM\n",
 | |
| 			&mesh_info->master_bssid, mesh_info->master_channel, mesh_info->hop_cnt,
 | |
| 			mesh_info->peer_bssid); 
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (vndr_ie)
 | |
| 		kfree(vndr_ie);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_mesh_update_master_info(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *mesh_if)
 | |
| {
 | |
| 	struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
 | |
| 	struct wl_if_info *sta_if = NULL;
 | |
| 	bool updated = FALSE;
 | |
| 
 | |
| 	sta_if = wl_ext_if_enabled(apsta_params, ISTA_MODE);
 | |
| 	if (sta_if) {
 | |
| 		wldev_ioctl(mesh_if->dev, WLC_GET_BSSID, &mesh_info->master_bssid,
 | |
| 			ETHER_ADDR_LEN, 0);
 | |
| 		mesh_info->master_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
 | |
| 		mesh_info->hop_cnt = 0;
 | |
| 		memset(mesh_info->peer_bssid, 0, MAX_HOP_LIST*ETHER_ADDR_LEN);
 | |
| 		if (!wl_mesh_update_vndr_ie(apsta_params, mesh_if))
 | |
| 			updated = TRUE;
 | |
| 	}
 | |
| 
 | |
| 	return updated;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_mesh_update_mesh_info(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *mesh_if)
 | |
| {
 | |
| 	struct wl_mesh_params *mesh_info = &apsta_params->mesh_info, peer_mesh_info;
 | |
| 	uint32 count = 0;
 | |
| 	char *dump_buf = NULL;
 | |
| 	mesh_peer_info_dump_t *peer_results;
 | |
| 	mesh_peer_info_ext_t *mpi_ext;
 | |
| 	struct ether_addr bssid;
 | |
| 	bool updated = FALSE, bss_found = FALSE;
 | |
| 	uint16 cur_chan;
 | |
| 
 | |
| 	dump_buf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
 | |
| 	if (dump_buf == NULL) {
 | |
| 		AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 			WLC_IOCTL_MAXLEN); 
 | |
| 		return FALSE;
 | |
| 	}
 | |
| 	count = wl_mesh_get_peer_results(mesh_if->dev, dump_buf, WLC_IOCTL_MAXLEN);
 | |
| 	if (count > 0) {
 | |
| 		memset(&bssid, 0, ETHER_ADDR_LEN);
 | |
| 		wldev_ioctl(mesh_if->dev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN, 0);
 | |
| 		peer_results = (mesh_peer_info_dump_t *)dump_buf;
 | |
| 		mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
 | |
| 		for (count = 0; count < peer_results->count; count++) {
 | |
| 			if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT &&
 | |
| 					mpi_ext->peer_info.state == MESH_PEERING_ESTAB) {
 | |
| 				memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
 | |
| 				bss_found = wl_escan_mesh_info(mesh_if->dev, mesh_if->escan,
 | |
| 					&mpi_ext->ea, &peer_mesh_info);
 | |
| 				if (bss_found && (mesh_info->master_channel == 0 ||
 | |
| 						peer_mesh_info.hop_cnt <= mesh_info->hop_cnt) &&
 | |
| 						memcmp(&peer_mesh_info.peer_bssid, &bssid, ETHER_ADDR_LEN)) {
 | |
| 					memcpy(&mesh_info->master_bssid, &peer_mesh_info.master_bssid,
 | |
| 						ETHER_ADDR_LEN);
 | |
| 					mesh_info->master_channel = peer_mesh_info.master_channel;
 | |
| 					mesh_info->hop_cnt = peer_mesh_info.hop_cnt+1;
 | |
| 					memset(mesh_info->peer_bssid, 0, MAX_HOP_LIST*ETHER_ADDR_LEN);
 | |
| 					memcpy(&mesh_info->peer_bssid, &mpi_ext->ea, ETHER_ADDR_LEN);
 | |
| 					memcpy(&mesh_info->peer_bssid[1], peer_mesh_info.peer_bssid,
 | |
| 						(MAX_HOP_LIST-1)*ETHER_ADDR_LEN);
 | |
| 					updated = TRUE;
 | |
| 				}
 | |
| 			}
 | |
| 			mpi_ext++;
 | |
| 		}
 | |
| 		if (updated) {
 | |
| 			if (wl_mesh_update_vndr_ie(apsta_params, mesh_if)) {
 | |
| 				AEXT_ERROR(mesh_if->dev->name, "update failed\n");
 | |
| 				mesh_info->master_channel = 0;
 | |
| 				updated = FALSE;
 | |
| 				goto exit;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!mesh_info->master_channel) {
 | |
| 		wlc_ssid_t cur_ssid;
 | |
| 		char sec[32];
 | |
| 		bool sae = FALSE;
 | |
| 		memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
 | |
| 		wl_ext_ioctl(mesh_if->dev, WLC_GET_SSID, &cur_ssid, sizeof(cur_ssid), 0);
 | |
| 		wl_ext_get_sec(mesh_if->dev, mesh_if->ifmode, sec, sizeof(sec));
 | |
| 		if (strnicmp(sec, "sae/sae", strlen("sae/sae")) == 0)
 | |
| 			sae = TRUE;
 | |
| 		cur_chan = wl_ext_get_chan(apsta_params, mesh_if->dev);
 | |
| 		bss_found = wl_escan_mesh_peer(mesh_if->dev, mesh_if->escan, &cur_ssid, cur_chan,
 | |
| 			sae, &peer_mesh_info);
 | |
| 
 | |
| 		if (bss_found && peer_mesh_info.master_channel&&
 | |
| 				(cur_chan != peer_mesh_info.master_channel)) {
 | |
| 			WL_MSG(mesh_if->ifname, "moving channel %d -> %d\n",
 | |
| 				cur_chan, peer_mesh_info.master_channel);
 | |
| 			wl_ext_disable_iface(mesh_if->dev, mesh_if->ifname);
 | |
| 			mesh_if->channel = peer_mesh_info.master_channel;
 | |
| 			wl_ext_enable_iface(mesh_if->dev, mesh_if->ifname, 500);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (dump_buf)
 | |
| 		kfree(dump_buf);
 | |
| 	return updated;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_mesh_event_handler(	struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *mesh_if, const wl_event_msg_t *e, void *data)
 | |
| {
 | |
| 	struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
 | |
| 	uint32 event_type = ntoh32(e->event_type);
 | |
| 	uint32 status = ntoh32(e->status);
 | |
| 	uint32 reason = ntoh32(e->reason);
 | |
| 	wlc_ssid_t ssid;
 | |
| 	int ret;
 | |
| 
 | |
| 	if (wl_get_isam_status(mesh_if, AP_CREATED) &&
 | |
| 			((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) ||
 | |
| 			(event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
 | |
| 			reason == WLC_E_REASON_INITIAL_ASSOC))) {
 | |
| 		if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
 | |
| 			mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
 | |
| 			wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
 | |
| 		}
 | |
| 	}
 | |
| 	else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
 | |
| 			(event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
 | |
| 			reason == WLC_E_REASON_DEAUTH)) {
 | |
| 		wl_mesh_clear_mesh_info(apsta_params, mesh_if, FALSE);
 | |
| 	}
 | |
| 	else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
 | |
| 			(event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
 | |
| 			reason == DOT11_SC_SUCCESS) {
 | |
| 		mesh_info->scan_channel = wl_ext_get_chan(apsta_params, mesh_if->dev);
 | |
| 		wl_mesh_set_timer(mesh_if, 100);
 | |
| 	}
 | |
| 	else if (event_type == WLC_E_DISASSOC_IND || event_type == WLC_E_DEAUTH_IND ||
 | |
| 			(event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
 | |
| 		if (!memcmp(&mesh_info->peer_bssid, &e->addr, ETHER_ADDR_LEN))
 | |
| 			wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
 | |
| 	}
 | |
| 	else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
 | |
| 			event_type == WLC_E_RESERVED && reason == 0xFFFFFFFF) {
 | |
| 		if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
 | |
| 			wl_ext_ioctl(mesh_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
 | |
| 			ret = wl_escan_set_scan(mesh_if->dev, apsta_params->dhd, &ssid,
 | |
| 				mesh_info->scan_channel, FALSE);
 | |
| 			if (ret)
 | |
| 				wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
 | |
| 		}
 | |
| 	}
 | |
| 	else if (wl_get_isam_status(mesh_if, AP_CREATED) &&
 | |
| 			((event_type == WLC_E_ESCAN_RESULT && status == WLC_E_STATUS_SUCCESS) ||
 | |
| 			(event_type == WLC_E_ESCAN_RESULT &&
 | |
| 			(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 || status == WLC_E_STATUS_TIMEOUT)))) {
 | |
| 		if (!wl_mesh_update_master_info(apsta_params, mesh_if)) {
 | |
| 			if (!wl_mesh_update_mesh_info(apsta_params, mesh_if)) {
 | |
| 				mesh_info->scan_channel = 0;
 | |
| 				wl_mesh_set_timer(mesh_if, WL_MESH_DELAY_SCAN_MS);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_mesh_escan_detach(dhd_pub_t *dhd, struct wl_if_info *mesh_if)
 | |
| {
 | |
| 	AEXT_TRACE(mesh_if->dev->name, "Enter\n");
 | |
| 
 | |
| 	del_timer_sync(&mesh_if->delay_scan);
 | |
| 
 | |
| 	if (mesh_if->escan) {
 | |
| 		mesh_if->escan = NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_mesh_escan_attach(dhd_pub_t *dhd, struct wl_if_info *mesh_if)
 | |
| {
 | |
| 	AEXT_TRACE(mesh_if->dev->name, "Enter\n");
 | |
| 
 | |
| 	mesh_if->escan = dhd->escan;
 | |
| 	init_timer_compat(&mesh_if->delay_scan, wl_mesh_timer, mesh_if);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static uint
 | |
| wl_mesh_update_peer_path(struct wl_if_info *mesh_if, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	struct wl_mesh_params peer_mesh_info;
 | |
| 	uint32 count = 0;
 | |
| 	char *dump_buf = NULL;
 | |
| 	mesh_peer_info_dump_t *peer_results;
 | |
| 	mesh_peer_info_ext_t *mpi_ext;
 | |
| 	int bytes_written = 0, j, k;
 | |
| 	bool bss_found = FALSE;
 | |
| 
 | |
| 	dump_buf = kmalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
 | |
| 	if (dump_buf == NULL) {
 | |
| 		AEXT_ERROR(mesh_if->dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 			WLC_IOCTL_MAXLEN); 
 | |
| 		return FALSE;
 | |
| 	}
 | |
| 	count = wl_mesh_get_peer_results(mesh_if->dev, dump_buf, WLC_IOCTL_MAXLEN);
 | |
| 	if (count > 0) {
 | |
| 		peer_results = (mesh_peer_info_dump_t *)dump_buf;
 | |
| 		mpi_ext = (mesh_peer_info_ext_t *)peer_results->mpi_ext;
 | |
| 		for (count = 0; count < peer_results->count; count++) {
 | |
| 			if (mpi_ext->entry_state != MESH_SELF_PEER_ENTRY_STATE_TIMEDOUT &&
 | |
| 					mpi_ext->peer_info.state == MESH_PEERING_ESTAB) {
 | |
| 				memset(&peer_mesh_info, 0, sizeof(struct wl_mesh_params));
 | |
| 				bss_found = wl_escan_mesh_info(mesh_if->dev, mesh_if->escan,
 | |
| 					&mpi_ext->ea, &peer_mesh_info);
 | |
| 				if (bss_found) {
 | |
| 					bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 						"\npeer=%pM, hop=%d",
 | |
| 						&mpi_ext->ea, peer_mesh_info.hop_cnt);
 | |
| 					for (j=1; j<peer_mesh_info.hop_cnt; j++) {
 | |
| 						bytes_written += snprintf(command+bytes_written,
 | |
| 							total_len, "\n");
 | |
| 						for (k=0; k<j; k++) {
 | |
| 							bytes_written += snprintf(command+bytes_written,
 | |
| 								total_len, " ");
 | |
| 						}
 | |
| 						bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 							"%pM", &peer_mesh_info.peer_bssid[j]);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			mpi_ext++;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (dump_buf)
 | |
| 		kfree(dump_buf);
 | |
| 	return bytes_written;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_isam_peer_path(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_mesh_params *mesh_info = &apsta_params->mesh_info;
 | |
| 	struct wl_if_info *tmp_if;
 | |
| 	uint16 chan = 0;
 | |
| 	char *dump_buf = NULL;
 | |
| 	int dump_len = WLC_IOCTL_MEDLEN;
 | |
| 	int dump_written = 0;
 | |
| 	int i;
 | |
| 
 | |
| 	if (command || android_msg_level & ANDROID_INFO_LEVEL) {
 | |
| 		if (command) {
 | |
| 			dump_buf = command;
 | |
| 			dump_len = total_len;
 | |
| 		} else {
 | |
| 			dump_buf = kmalloc(dump_len, GFP_KERNEL);
 | |
| 			if (dump_buf == NULL) {
 | |
| 				AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 					dump_len); 
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 		for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 			tmp_if = &apsta_params->if_info[i];
 | |
| 			if (tmp_if->dev && tmp_if->ifmode == IMESH_MODE && apsta_params->macs) {
 | |
| 				chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
 | |
| 				if (chan) {
 | |
| 					dump_written += snprintf(dump_buf+dump_written, dump_len,
 | |
| 						"[dhd-%s-%c] mbssid=%pM, mchan=%d, hop=%d, pbssid=%pM",
 | |
| 						tmp_if->ifname, tmp_if->prefix, &mesh_info->master_bssid,
 | |
| 						mesh_info->master_channel, mesh_info->hop_cnt,
 | |
| 						&mesh_info->peer_bssid);
 | |
| 					dump_written += wl_mesh_update_peer_path(tmp_if,
 | |
| 						dump_buf+dump_written, dump_len-dump_written);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		AEXT_INFO(dev->name, "%s\n", dump_buf);
 | |
| 	}
 | |
| 
 | |
| 	if (!command && dump_buf)
 | |
| 		kfree(dump_buf);
 | |
| 	return dump_written;
 | |
| }
 | |
| #endif /* WL_ESCAN */
 | |
| #endif /* WLMESH */
 | |
| 
 | |
| static int
 | |
| wl_ext_isam_status(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	int i;
 | |
| 	struct wl_if_info *tmp_if;
 | |
| 	uint16 chan = 0;
 | |
| 	wlc_ssid_t ssid = { 0, {0} };
 | |
| 	struct ether_addr bssid;
 | |
| 	scb_val_t scb_val;
 | |
| 	char sec[32];
 | |
| 	u32 chanspec = 0;
 | |
| 	char *dump_buf = NULL;
 | |
| 	int dump_len = WLC_IOCTL_MEDLEN;
 | |
| 	int dump_written = 0;
 | |
| 
 | |
| 	if (command || android_msg_level & ANDROID_INFO_LEVEL) {
 | |
| 		if (command) {
 | |
| 			dump_buf = command;
 | |
| 			dump_len = total_len;
 | |
| 		} else {
 | |
| 			dump_buf = kmalloc(dump_len, GFP_KERNEL);
 | |
| 			if (dump_buf == NULL) {
 | |
| 				AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 					dump_len); 
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 		dump_written += snprintf(dump_buf+dump_written, dump_len,
 | |
| 			"apstamode=%d", apsta_params->apstamode);
 | |
| 		for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 			memset(&ssid, 0, sizeof(ssid));
 | |
| 			memset(&bssid, 0, sizeof(bssid));
 | |
| 			memset(&scb_val, 0, sizeof(scb_val));
 | |
| 			tmp_if = &apsta_params->if_info[i];
 | |
| 			if (tmp_if->dev) {
 | |
| 				chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
 | |
| 				if (chan) {
 | |
| 					wl_ext_ioctl(tmp_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
 | |
| 					wldev_ioctl(tmp_if->dev, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
 | |
| 					wldev_ioctl(tmp_if->dev, WLC_GET_RSSI, &scb_val,
 | |
| 						sizeof(scb_val_t), 0);
 | |
| 					chanspec = wl_ext_get_chanspec(apsta_params, tmp_if->dev);
 | |
| 					wl_ext_get_sec(tmp_if->dev, tmp_if->ifmode, sec, sizeof(sec));
 | |
| 					dump_written += snprintf(dump_buf+dump_written, dump_len,
 | |
| 						"\n[dhd-%s-%c]: bssid=%pM, chan=%3d(0x%x %sMHz), "
 | |
| 						"rssi=%3d, sec=%-15s, SSID=\"%s\"",
 | |
| 						tmp_if->ifname, tmp_if->prefix, &bssid, chan, chanspec,
 | |
| 						CHSPEC_IS20(chanspec)?"20":
 | |
| 						CHSPEC_IS40(chanspec)?"40":
 | |
| 						CHSPEC_IS80(chanspec)?"80":"160",
 | |
| 						dtoh32(scb_val.val), sec, ssid.SSID);
 | |
| 					if (tmp_if->ifmode == IAP_MODE) {
 | |
| 						dump_written += snprintf(dump_buf+dump_written, dump_len, "\n");
 | |
| 						dump_written += wl_ext_assoclist(tmp_if->dev, NULL,
 | |
| 							dump_buf+dump_written, dump_len-dump_written);
 | |
| 					}
 | |
| #ifdef WLMESH
 | |
| 					else if (tmp_if->ifmode == IMESH_MODE) {
 | |
| 						dump_written += snprintf(dump_buf+dump_written, dump_len, "\n");
 | |
| 						dump_written += wl_ext_mesh_peer_status(tmp_if->dev, NULL,
 | |
| 							dump_buf+dump_written, dump_len-dump_written);
 | |
| 					}
 | |
| #endif /* WLMESH */
 | |
| 				} else {
 | |
| 					dump_written += snprintf(dump_buf+dump_written, dump_len,
 | |
| 						"\n[dhd-%s-%c]:", tmp_if->ifname, tmp_if->prefix);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		AEXT_INFO(dev->name, "%s\n", dump_buf);
 | |
| 	}
 | |
| 
 | |
| 	if (!command && dump_buf)
 | |
| 		kfree(dump_buf);
 | |
| 	return dump_written;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_ext_master_if(struct wl_if_info *cur_if)
 | |
| {
 | |
| 	if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE)
 | |
| 		return TRUE;
 | |
| 	else
 | |
| 		return FALSE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_if_down(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	scb_val_t scbval;
 | |
| 	struct {
 | |
| 		s32 cfg;
 | |
| 		s32 val;
 | |
| 	} bss_setbuf;
 | |
| 	apstamode_t apstamode = apsta_params->apstamode;
 | |
| 
 | |
| 	WL_MSG(cur_if->ifname, "[%c] Turning off...\n", cur_if->prefix);
 | |
| 
 | |
| 	if (cur_if->ifmode == ISTA_MODE) {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);
 | |
| 	} else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		// deauthenticate all STA first
 | |
| 		memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN);
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1);
 | |
| 	}
 | |
| 
 | |
| 	if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_DOWN, NULL, 0, 1);
 | |
| 	} else {
 | |
| 		bss_setbuf.cfg = 0xffffffff;
 | |
| 		bss_setbuf.val = htod32(0);
 | |
| 		wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 	}
 | |
| 	wl_clr_isam_status(cur_if, AP_CREATED);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_if_up(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	struct {
 | |
| 		s32 cfg;
 | |
| 		s32 val;
 | |
| 	} bss_setbuf;
 | |
| 	apstamode_t apstamode = apsta_params->apstamode;
 | |
| 	chanspec_t fw_chspec;
 | |
| 	u32 timeout;
 | |
| 	wlc_ssid_t ssid = { 0, {0} };
 | |
| 	uint16 chan = 0;
 | |
| 
 | |
| 	if (cur_if->ifmode != IAP_MODE) {
 | |
| 		AEXT_ERROR(cur_if->ifname, "Wrong ifmode\n");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (wl_ext_dfs_chan(cur_if->channel) && !apsta_params->radar) {
 | |
| 		WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
 | |
| 			cur_if->prefix, cur_if->channel);
 | |
| 		return 0;
 | |
| 	} else if (!cur_if->channel) {
 | |
| 		WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	WL_MSG(cur_if->ifname, "[%c] Turning on...\n", cur_if->prefix);
 | |
| 
 | |
| 	wl_ext_set_chanspec(cur_if->dev, apsta_params->ioctl_ver, cur_if->channel,
 | |
| 		&fw_chspec);
 | |
| 
 | |
| 	wl_clr_isam_status(cur_if, AP_CREATED);
 | |
| 	wl_set_isam_status(cur_if, AP_CREATING);
 | |
| 	if (apstamode == IAPONLY_MODE) {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_UP, NULL, 0, 1);
 | |
| 	} else {
 | |
| 		bss_setbuf.cfg = 0xffffffff;	
 | |
| 		bss_setbuf.val = htod32(1);
 | |
| 		wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf,
 | |
| 			sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 	}
 | |
| 
 | |
| 	timeout = wait_event_interruptible_timeout(apsta_params->netif_change_event,
 | |
| 		wl_get_isam_status(cur_if, AP_CREATED),
 | |
| 		msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
 | |
| 	if (timeout <= 0 || !wl_get_isam_status(cur_if, AP_CREATED)) {
 | |
| 		wl_ext_if_down(apsta_params, cur_if);
 | |
| 		WL_MSG(cur_if->ifname, "[%c] failed to up with SSID: \"%s\"\n",
 | |
| 			cur_if->prefix, cur_if->ssid);
 | |
| 	} else {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_GET_SSID, &ssid, sizeof(ssid), 0);
 | |
| 		chan = wl_ext_get_chan(apsta_params, cur_if->dev);
 | |
| 		WL_MSG(cur_if->ifname, "[%c] enabled with SSID: \"%s\" on channel %d\n",
 | |
| 			cur_if->prefix, ssid.SSID, chan);
 | |
| 	}
 | |
| 	wl_clr_isam_status(cur_if, AP_CREATING);
 | |
| 
 | |
| 	wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_disable_iface(struct net_device *dev, char *ifname)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	int i;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	wlc_ssid_t ssid = { 0, {0} };
 | |
| 	scb_val_t scbval;
 | |
| 	struct {
 | |
| 		s32 cfg;
 | |
| 		s32 val;
 | |
| 	} bss_setbuf;
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	apstamode_t apstamode = apsta_params->apstamode;
 | |
| 	struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && !strcmp(tmp_if->dev->name, ifname)) {
 | |
| 			cur_if = tmp_if;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!cur_if) {
 | |
| 		AEXT_ERROR(dev->name, "wrong ifname=%s or dev not ready\n", ifname);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	mutex_lock(&apsta_params->usr_sync);
 | |
| 	WL_MSG(ifname, "[%c] Disabling...\n", cur_if->prefix);
 | |
| 
 | |
| 	if (cur_if->ifmode == ISTA_MODE) {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_DISASSOC, NULL, 0, 1);
 | |
| 		wl_ext_add_remove_pm_enable_work(dev, FALSE);
 | |
| 	} else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		// deauthenticate all STA first
 | |
| 		memcpy(scbval.ea.octet, ðer_bcast, ETHER_ADDR_LEN);
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_SCB_DEAUTHENTICATE, &scbval.ea, ETHER_ADDR_LEN, 1);
 | |
| 	}
 | |
| 
 | |
| 	if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) {
 | |
| 		wl_ext_ioctl(dev, WLC_DOWN, NULL, 0, 1);
 | |
| 		wl_ext_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1); // reset ssid
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 1);
 | |
| 	} else if ((apstamode==ISTAAP_MODE || apstamode==ISTAGO_MODE) &&
 | |
| 			cur_if->ifmode == IAP_MODE) {
 | |
| 		bss_setbuf.cfg = 0xffffffff;
 | |
| 		bss_setbuf.val = htod32(0);
 | |
| 		wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 1);
 | |
| #ifdef ARP_OFFLOAD_SUPPORT
 | |
| 		/* IF SoftAP is disabled, enable arpoe back for STA mode. */
 | |
| 		dhd_arp_offload_set(dhd, dhd_arp_mode);
 | |
| 		dhd_arp_offload_enable(dhd, TRUE);
 | |
| #endif /* ARP_OFFLOAD_SUPPORT */
 | |
| #ifdef PROP_TXSTATUS_VSDB
 | |
| #if defined(BCMSDIO)
 | |
| 		if (dhd->conf->disable_proptx!=0) {
 | |
| 			bool enabled;
 | |
| 			dhd_wlfc_get_enable(dhd, &enabled);
 | |
| 			if (enabled) {
 | |
| 				dhd_wlfc_deinit(dhd);
 | |
| 			}
 | |
| 		}
 | |
| #endif /* BCMSDIO */
 | |
| #endif /* PROP_TXSTATUS_VSDB */
 | |
| 	}
 | |
| 	else if (apstamode == IDUALAP_MODE || apstamode == ISTAAPAP_MODE) {
 | |
| 		bss_setbuf.cfg = 0xffffffff;
 | |
| 		bss_setbuf.val = htod32(0);
 | |
| 		wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| #ifdef WLMESH
 | |
| 	} else if (apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
 | |
| 			apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE) {
 | |
| 		bss_setbuf.cfg = 0xffffffff;
 | |
| 		bss_setbuf.val = htod32(0);
 | |
| 		wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf, sizeof(bss_setbuf),
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (cur_if->ifmode == IMESH_MODE) {
 | |
| 			int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME;
 | |
| 			for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 				tmp_if = &apsta_params->if_info[i];
 | |
| 				if (tmp_if->dev && tmp_if->ifmode == ISTA_MODE) {
 | |
| 					wl_ext_ioctl(tmp_if->dev, WLC_SET_SCAN_CHANNEL_TIME,
 | |
| 						&scan_assoc_time, sizeof(scan_assoc_time), 1);
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| #endif /* WLMESH */
 | |
| 	}
 | |
| 
 | |
| 	wl_clr_isam_status(cur_if, AP_CREATED);
 | |
| 
 | |
| 	WL_MSG(ifname, "[%c] Exit\n", cur_if->prefix);
 | |
| 	mutex_unlock(&apsta_params->usr_sync);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iapsta_disable(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	char *pch, *pick_tmp, *param;
 | |
| 	char ifname[IFNAMSIZ+1];
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
 | |
| 
 | |
| 	pick_tmp = command;
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_disable
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 	while (param != NULL) {
 | |
| 		if (!strcmp(param, "ifname")) {
 | |
| 			pch = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 			if (pch) {
 | |
| 				strcpy(ifname, pch);
 | |
| 				ret = wl_ext_disable_iface(dev, ifname);
 | |
| 				if (ret)
 | |
| 					return ret;
 | |
| 			}
 | |
| 			else {
 | |
| 				AEXT_ERROR(dev->name, "ifname [wlanX]\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 		param = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_ext_diff_band(uint16 chan1, uint16 chan2)
 | |
| {
 | |
| 	if ((chan1 <= CH_MAX_2G_CHANNEL && chan2 > CH_MAX_2G_CHANNEL) ||
 | |
| 		(chan1 > CH_MAX_2G_CHANNEL && chan2 <= CH_MAX_2G_CHANNEL)) {
 | |
| 		return TRUE;
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static uint16
 | |
| wl_ext_same_band(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if, bool nodfs)
 | |
| {
 | |
| 	struct wl_if_info *tmp_if;
 | |
| 	uint16 tmp_chan, target_chan = 0;
 | |
| 	wl_prio_t max_prio;
 | |
| 	int i;
 | |
| 
 | |
| 	// find the max prio
 | |
| 	max_prio = cur_if->prio;
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
 | |
| 				tmp_if->prio > max_prio) {
 | |
| 			tmp_chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
 | |
| 			if (wl_ext_dfs_chan(tmp_chan) && nodfs)
 | |
| 				continue;
 | |
| 			if (tmp_chan && !wl_ext_diff_band(cur_if->channel, tmp_chan)) {
 | |
| 				target_chan = tmp_chan;
 | |
| 				max_prio = tmp_if->prio;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return target_chan;
 | |
| }
 | |
| 
 | |
| static uint16
 | |
| wl_ext_get_vsdb_chan(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if, struct wl_if_info *target_if)
 | |
| {
 | |
| 	uint16 target_chan = 0, cur_chan = cur_if->channel;
 | |
| 
 | |
| 	target_chan = wl_ext_get_chan(apsta_params, target_if->dev);
 | |
| 	if (target_chan) {
 | |
| 		AEXT_INFO(cur_if->ifname, "cur_chan=%d, target_chan=%d\n",
 | |
| 			cur_chan, target_chan);
 | |
| 		if (wl_ext_diff_band(cur_chan, target_chan)) {
 | |
| 			if (!apsta_params->rsdb)
 | |
| 				return target_chan;
 | |
| 		} else {
 | |
| 			if (cur_chan != target_chan)
 | |
| 				return target_chan;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_rsdb_core_conflict(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct wl_if_info *tmp_if;
 | |
| 	uint16 cur_chan, tmp_chan;
 | |
| 	int i;
 | |
| 
 | |
| 	if (apsta_params->rsdb) {
 | |
| 		cur_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
 | |
| 		for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 			tmp_if = &apsta_params->if_info[i];
 | |
| 			if (tmp_if != cur_if && wl_get_isam_status(tmp_if, IF_READY) &&
 | |
| 					tmp_if->prio > cur_if->prio) {
 | |
| 				tmp_chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
 | |
| 				if (!tmp_chan)
 | |
| 					continue;
 | |
| 				if (wl_ext_diff_band(cur_chan, tmp_chan) &&
 | |
| 						wl_ext_diff_band(cur_chan, cur_if->channel))
 | |
| 					return TRUE;
 | |
| 				else if (!wl_ext_diff_band(cur_chan, tmp_chan) &&
 | |
| 						wl_ext_diff_band(cur_chan, cur_if->channel))
 | |
| 					return TRUE;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_trigger_csa(struct wl_apsta_params *apsta_params, struct wl_if_info *cur_if)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	bool core_conflict = FALSE;
 | |
| 
 | |
| 	if (wl_ext_master_if(cur_if) && (apsta_params->csa & CSA_DRV_BIT)) {
 | |
| 		if (!cur_if->channel) {
 | |
| 			WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
 | |
| 		} else if (wl_ext_dfs_chan(cur_if->channel) && !apsta_params->radar) {
 | |
| 			WL_MSG(cur_if->ifname, "[%c] skip DFS channel %d\n",
 | |
| 				cur_if->prefix, cur_if->channel);
 | |
| 			wl_ext_if_down(apsta_params, cur_if);
 | |
| 		} else {
 | |
| 			wl_chan_switch_t csa_arg;
 | |
| 			memset(&csa_arg, 0, sizeof(csa_arg));
 | |
| 			csa_arg.mode = 1;
 | |
| 			csa_arg.count = 3;
 | |
| 			csa_arg.chspec = wl_ext_chan_to_chanspec(apsta_params, cur_if->dev,
 | |
| 				cur_if->channel);
 | |
| 			core_conflict = wl_ext_rsdb_core_conflict(apsta_params, cur_if);
 | |
| 			if (core_conflict) {
 | |
| 				WL_MSG(cur_if->ifname, "[%c] Skip CSA due to rsdb core conflict\n",
 | |
| 					cur_if->prefix);
 | |
| 			} else if (csa_arg.chspec) {
 | |
| 				WL_MSG(cur_if->ifname, "[%c] Trigger CSA to channel %d(0x%x)\n",
 | |
| 					cur_if->prefix, cur_if->channel, csa_arg.chspec);
 | |
| 				wl_set_isam_status(cur_if, AP_CREATING);
 | |
| 				wl_ext_iovar_setbuf(cur_if->dev, "csa", &csa_arg, sizeof(csa_arg),
 | |
| 					iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 				OSL_SLEEP(500);
 | |
| 				wl_clr_isam_status(cur_if, AP_CREATING);
 | |
| 				wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 			} else {
 | |
| 				AEXT_ERROR(cur_if->ifname, "fail to get chanspec\n");
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_move_cur_dfs_channel(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	uint16 other_chan = 0, cur_chan = cur_if->channel;
 | |
| 	uint16 chan_2g = 0, chan_5g = 0;
 | |
| 	uint32 auto_band = WLC_BAND_2G;
 | |
| 
 | |
| 	if (wl_ext_master_if(cur_if) && wl_ext_dfs_chan(cur_if->channel) &&
 | |
| 			!apsta_params->radar) {
 | |
| 
 | |
| 		wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
 | |
| 		if (!chan_2g && !chan_5g) {
 | |
| 			cur_if->channel = 0;
 | |
| 			WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (apsta_params->vsdb) {
 | |
| 			if (chan_5g) {
 | |
| 				cur_if->channel = chan_5g;
 | |
| 				auto_band = WLC_BAND_5G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 			} else {
 | |
| 				cur_if->channel = chan_2g;
 | |
| 				auto_band = WLC_BAND_2G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 			}
 | |
| 			if (!other_chan) {
 | |
| 				other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
 | |
| 					auto_band);
 | |
| 			}
 | |
| 			if (other_chan)
 | |
| 				cur_if->channel = other_chan;
 | |
| 		} else if (apsta_params->rsdb) {
 | |
| 			if (chan_5g) {
 | |
| 				cur_if->channel = chan_5g;
 | |
| 				auto_band = WLC_BAND_5G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, FALSE);
 | |
| 				if (wl_ext_dfs_chan(other_chan) && chan_2g) {
 | |
| 					cur_if->channel = chan_2g;
 | |
| 					auto_band = WLC_BAND_2G;
 | |
| 					other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 				}
 | |
| 			} else {
 | |
| 				cur_if->channel = chan_2g;
 | |
| 				auto_band = WLC_BAND_2G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 			}
 | |
| 			if (!other_chan) {
 | |
| 				other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
 | |
| 					auto_band);
 | |
| 			}
 | |
| 			if (other_chan)
 | |
| 				cur_if->channel = other_chan;
 | |
| 		} else {
 | |
| 			cur_if->channel = chan_5g;
 | |
| 			auto_band = WLC_BAND_5G;
 | |
| 			other_chan = wl_ext_same_band(apsta_params, cur_if, FALSE);
 | |
| 			if (wl_ext_dfs_chan(other_chan)) {
 | |
| 				cur_if->channel = 0;
 | |
| 			}
 | |
| 			else if (!other_chan) {
 | |
| 				other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
 | |
| 					auto_band);
 | |
| 			}
 | |
| 			if (other_chan)
 | |
| 				cur_if->channel = other_chan;
 | |
| 		}
 | |
| 		WL_MSG(cur_if->ifname, "[%c] move channel %d => %d\n",
 | |
| 			cur_if->prefix, cur_chan, cur_if->channel);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_move_other_dfs_channel(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	uint16 other_chan = 0, cur_chan = cur_if->channel;
 | |
| 	uint16 chan_2g = 0, chan_5g = 0;
 | |
| 	uint32 auto_band = WLC_BAND_2G;
 | |
| 
 | |
| 	if (wl_ext_master_if(cur_if) && wl_ext_dfs_chan(cur_if->channel) &&
 | |
| 			!apsta_params->radar) {
 | |
| 
 | |
| 		wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
 | |
| 		if (!chan_2g && !chan_5g) {
 | |
| 			cur_if->channel = 0;
 | |
| 			WL_MSG(cur_if->ifname, "[%c] no valid channel\n", cur_if->prefix);
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (apsta_params->vsdb) {
 | |
| 			if (chan_5g) {
 | |
| 				cur_if->channel = chan_5g;
 | |
| 				auto_band = WLC_BAND_5G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 			} else {
 | |
| 				cur_if->channel = chan_2g;
 | |
| 				auto_band = WLC_BAND_2G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 			}
 | |
| 			if (!other_chan) {
 | |
| 				other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
 | |
| 					auto_band);
 | |
| 			}
 | |
| 			if (other_chan)
 | |
| 				cur_if->channel = other_chan;
 | |
| 		} else if (apsta_params->rsdb) {
 | |
| 			if (chan_2g) {
 | |
| 				cur_if->channel = chan_2g;
 | |
| 				auto_band = WLC_BAND_2G;
 | |
| 				other_chan = wl_ext_same_band(apsta_params, cur_if, TRUE);
 | |
| 				if (!other_chan) {
 | |
| 					other_chan = wl_ext_autochannel(cur_if->dev, ACS_FW_BIT|ACS_DRV_BIT,
 | |
| 						auto_band);
 | |
| 				}
 | |
| 			} else {
 | |
| 				cur_if->channel = 0;
 | |
| 			}
 | |
| 			if (other_chan)
 | |
| 				cur_if->channel = other_chan;
 | |
| 		} else {
 | |
| 			cur_if->channel = 0;
 | |
| 		}
 | |
| 		WL_MSG(cur_if->ifname, "[%c] move channel %d => %d\n",
 | |
| 			cur_if->prefix, cur_chan, cur_if->channel);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static uint16
 | |
| wl_ext_move_cur_channel(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct wl_if_info *tmp_if, *target_if = NULL;
 | |
| 	uint16 tmp_chan, target_chan = 0;
 | |
| 	wl_prio_t max_prio;
 | |
| 	int i;
 | |
| 
 | |
| 	if (apsta_params->vsdb) {
 | |
| 		target_chan = cur_if->channel;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	// find the max prio
 | |
| 	max_prio = cur_if->prio;
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
 | |
| 				tmp_if->prio > max_prio) {
 | |
| 			tmp_chan = wl_ext_get_vsdb_chan(apsta_params, cur_if, tmp_if);
 | |
| 			if (tmp_chan) {
 | |
| 				target_if = tmp_if;
 | |
| 				target_chan = tmp_chan;
 | |
| 				max_prio = tmp_if->prio;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (target_chan) {
 | |
| 		tmp_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
 | |
| 		if (apsta_params->rsdb && tmp_chan &&
 | |
| 				wl_ext_diff_band(tmp_chan, target_chan)) {
 | |
| 			WL_MSG(cur_if->ifname, "[%c] keep on current channel %d\n",
 | |
| 				cur_if->prefix, tmp_chan);
 | |
| 			cur_if->channel = 0;
 | |
| 		} else {
 | |
| 			WL_MSG(cur_if->ifname, "[%c] channel=%d => %s[%c] channel=%d\n",
 | |
| 				cur_if->prefix, cur_if->channel,
 | |
| 				target_if->ifname, target_if->prefix, target_chan);
 | |
| 			cur_if->channel = target_chan;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	wl_ext_move_cur_dfs_channel(apsta_params, cur_if);
 | |
| 
 | |
| 	return cur_if->channel;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_move_other_channel(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct wl_if_info *tmp_if, *target_if=NULL;
 | |
| 	uint16 tmp_chan, target_chan = 0;
 | |
| 	wl_prio_t max_prio = 0, cur_prio;
 | |
| 	int i;
 | |
| 
 | |
| 	if (apsta_params->vsdb || !cur_if->channel) {
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	// find the max prio, but lower than cur_if
 | |
| 	cur_prio = cur_if->prio;
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (cur_if != tmp_if && wl_get_isam_status(tmp_if, IF_READY) &&
 | |
| 				tmp_if->prio >= max_prio && tmp_if->prio <= cur_prio) {
 | |
| 			tmp_chan = wl_ext_get_vsdb_chan(apsta_params, cur_if, tmp_if);
 | |
| 			if (tmp_chan) {
 | |
| 				target_if = tmp_if;
 | |
| 				target_chan = tmp_chan;
 | |
| 				max_prio = tmp_if->prio;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (target_if) {
 | |
| 		WL_MSG(target_if->ifname, "channel=%d => %s channel=%d\n",
 | |
| 			target_chan, cur_if->ifname, cur_if->channel);
 | |
| 		target_if->channel = cur_if->channel;
 | |
| 		wl_ext_move_other_dfs_channel(apsta_params, target_if);
 | |
| 		if (apsta_params->csa == 0) {
 | |
| 			wl_ext_if_down(apsta_params, target_if);
 | |
| 			wl_ext_move_other_channel(apsta_params, cur_if);
 | |
| 			if (target_if->ifmode == ISTA_MODE || target_if->ifmode == IMESH_MODE) {
 | |
| 				wl_ext_enable_iface(target_if->dev, target_if->ifname, 0);
 | |
| 			} else if (target_if->ifmode == IAP_MODE) {
 | |
| 				wl_ext_if_up(apsta_params, target_if);
 | |
| 			}
 | |
| 		} else {
 | |
| 			wl_ext_trigger_csa(apsta_params, target_if);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| static bool
 | |
| wl_ext_wait_other_enabling(struct wl_apsta_params *apsta_params,
 | |
| 	struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct wl_if_info *tmp_if;
 | |
| 	bool enabling = FALSE;
 | |
| 	u32 timeout = 1;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && tmp_if->dev != cur_if->dev) {
 | |
| 			if (tmp_if->ifmode == ISTA_MODE)
 | |
| 				enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
 | |
| 			else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
 | |
| 				enabling = wl_get_isam_status(tmp_if, AP_CREATING);
 | |
| 			if (enabling)
 | |
| 				WL_MSG(cur_if->ifname, "waiting for %s[%c] enabling...\n",
 | |
| 					tmp_if->ifname, tmp_if->prefix);
 | |
| 			if (enabling && tmp_if->ifmode == ISTA_MODE) {
 | |
| 				timeout = wait_event_interruptible_timeout(
 | |
| 					apsta_params->netif_change_event,
 | |
| 					!wl_get_isam_status(tmp_if, STA_CONNECTING),
 | |
| 					msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
 | |
| 			} else if (enabling &&
 | |
| 					(tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)) {
 | |
| 				timeout = wait_event_interruptible_timeout(
 | |
| 					apsta_params->netif_change_event,
 | |
| 					!wl_get_isam_status(tmp_if, AP_CREATING),
 | |
| 					msecs_to_jiffies(MAX_STA_LINK_WAIT_TIME));
 | |
| 			}
 | |
| 			if (tmp_if->ifmode == ISTA_MODE)
 | |
| 				enabling = wl_get_isam_status(tmp_if, STA_CONNECTING);
 | |
| 			else if (tmp_if->ifmode == IAP_MODE || tmp_if->ifmode == IMESH_MODE)
 | |
| 				enabling = wl_get_isam_status(tmp_if, AP_CREATING);
 | |
| 			if (timeout <= 0 || enabling) {
 | |
| 				WL_MSG(cur_if->ifname, "%s[%c] is still enabling...\n",
 | |
| 					tmp_if->ifname, tmp_if->prefix);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return enabling;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_enable_iface(struct net_device *dev, char *ifname, int wait_up)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	int i, ret = 0;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	wlc_ssid_t ssid = { 0, {0} };
 | |
| 	chanspec_t fw_chspec;
 | |
| 	struct {
 | |
| 		s32 cfg;
 | |
| 		s32 val;
 | |
| 	} bss_setbuf;
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	apstamode_t apstamode = apsta_params->apstamode;
 | |
| 	struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
 | |
| 	uint16 cur_chan;
 | |
| 	struct wl_conn_info conn_info;
 | |
| 	u32 timeout;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && !strcmp(tmp_if->dev->name, ifname)) {
 | |
| 			cur_if = tmp_if;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 	if (!cur_if) {
 | |
| 		AEXT_ERROR(dev->name, "wrong ifname=%s or dev not ready\n", ifname);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	mutex_lock(&apsta_params->usr_sync);
 | |
| 
 | |
| 	if (cur_if->ifmode == ISTA_MODE) {
 | |
| 		wl_set_isam_status(cur_if, STA_CONNECTING);
 | |
| 	} else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		wl_set_isam_status(cur_if, AP_CREATING);
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 	WL_MSG(ifname, "[%c] Enabling...\n", cur_if->prefix);
 | |
| 
 | |
| 	wl_ext_wait_other_enabling(apsta_params, cur_if);
 | |
| 
 | |
| 	if (wl_ext_master_if(cur_if) && apsta_params->acs) {
 | |
| 		uint16 chan_2g, chan_5g;
 | |
| 		uint auto_band;
 | |
| 		auto_band = WL_GET_BAND(cur_if->channel);
 | |
| 		wl_ext_get_default_chan(cur_if->dev, &chan_2g, &chan_5g, TRUE);
 | |
| 		if ((chan_2g && auto_band == WLC_BAND_2G) ||
 | |
| 				(chan_5g && auto_band == WLC_BAND_5G)) {
 | |
| 			cur_if->channel = wl_ext_autochannel(cur_if->dev, apsta_params->acs,
 | |
| 				auto_band);
 | |
| 		} else {
 | |
| 			AEXT_ERROR(ifname, "invalid channel\n");
 | |
| 			ret = -1;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_move_cur_channel(apsta_params, cur_if);
 | |
| 
 | |
| 	if (wl_ext_master_if(cur_if) && !cur_if->channel) {
 | |
| 		AEXT_ERROR(ifname, "skip channel 0\n");
 | |
| 		ret = -1;
 | |
| 		goto exit;
 | |
| 	}
 | |
| 
 | |
| 	cur_chan = wl_ext_get_chan(apsta_params, cur_if->dev);
 | |
| 	if (cur_chan) {
 | |
| 		AEXT_INFO(cur_if->ifname, "Associated\n");
 | |
| 		if (cur_chan != cur_if->channel) {
 | |
| 			wl_ext_trigger_csa(apsta_params, cur_if);
 | |
| 		}
 | |
| 		goto exit;
 | |
| 	}
 | |
| 	if (cur_if->ifmode == ISTA_MODE) {
 | |
| 		wl_clr_isam_status(cur_if, STA_CONNECTED);
 | |
| 	} else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		wl_clr_isam_status(cur_if, AP_CREATED);
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_move_other_channel(apsta_params, cur_if);
 | |
| 
 | |
| 	if (cur_if->ifidx > 0) {
 | |
| 		wl_ext_iovar_setbuf(cur_if->dev, "cur_etheraddr", (u8 *)cur_if->dev->dev_addr,
 | |
| 			ETHER_ADDR_LEN, iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 	}
 | |
| 
 | |
| 	// set ssid for AP
 | |
| 	ssid.SSID_len = strlen(cur_if->ssid);
 | |
| 	memcpy(ssid.SSID, cur_if->ssid, ssid.SSID_len);
 | |
| 	if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		wl_ext_iovar_setint(dev, "mpc", 0);
 | |
| 		if (apstamode == IAPONLY_MODE || apstamode == IMESHONLY_MODE) {
 | |
| 			wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 		} else if (apstamode==ISTAAP_MODE || apstamode==ISTAGO_MODE) {
 | |
| 			wl_ext_iovar_setbuf_bsscfg(cur_if->dev, "ssid", &ssid, sizeof(ssid),
 | |
| 				iovar_buf, WLC_IOCTL_SMLEN, cur_if->bssidx, NULL);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (wl_ext_master_if(cur_if)) {
 | |
| 		wl_ext_set_bgnmode(cur_if);
 | |
| 		if (!cur_if->channel) {
 | |
| 			cur_if->channel = 1;
 | |
| 		}
 | |
| 		ret = wl_ext_set_chanspec(cur_if->dev, apsta_params->ioctl_ver,
 | |
| 			cur_if->channel, &fw_chspec);
 | |
| 		if (ret)
 | |
| 			goto exit;
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_set_amode(cur_if);
 | |
| 	wl_ext_set_emode(apsta_params, cur_if);
 | |
| 
 | |
| 	if (cur_if->ifmode == ISTA_MODE) {
 | |
| 		conn_info.bssidx = cur_if->bssidx;
 | |
| 		conn_info.channel = cur_if->channel;
 | |
| 		memcpy(conn_info.ssid.SSID, cur_if->ssid, strlen(cur_if->ssid));
 | |
| 		conn_info.ssid.SSID_len = strlen(cur_if->ssid);
 | |
| 		memcpy(&conn_info.bssid, &cur_if->bssid, ETHER_ADDR_LEN);
 | |
| 	}
 | |
| 	if (cur_if->ifmode == IAP_MODE) {
 | |
| 		if (cur_if->maxassoc >= 0)
 | |
| 			wl_ext_iovar_setint(dev, "maxassoc", cur_if->maxassoc);
 | |
| 		// terence: fix me, hidden does not work in dualAP mode
 | |
| 		if (cur_if->hidden > 0) {
 | |
| 			wl_ext_ioctl(cur_if->dev, WLC_SET_CLOSED, &cur_if->hidden,
 | |
| 				sizeof(cur_if->hidden), 1);
 | |
| 			WL_MSG(ifname, "[%c] Broadcast SSID: %s\n",
 | |
| 				cur_if->prefix, cur_if->hidden ? "OFF":"ON");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (apstamode == ISTAONLY_MODE) {
 | |
| 		wl_ext_connect(cur_if->dev, &conn_info);
 | |
| 	} else if (apstamode == IAPONLY_MODE) {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
 | |
| 		wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 	} else if (apstamode == ISTAAP_MODE || apstamode == ISTAGO_MODE) {
 | |
| 		if (cur_if->ifmode == ISTA_MODE) {
 | |
| 			wl_ext_connect(cur_if->dev, &conn_info);
 | |
| 		} else {
 | |
| 			if (FW_SUPPORTED(dhd, rsdb)) {
 | |
| 				wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
 | |
| 			} else {
 | |
| 				bss_setbuf.cfg = htod32(cur_if->bssidx);
 | |
| 				bss_setbuf.val = htod32(1);
 | |
| 				wl_ext_iovar_setbuf(cur_if->dev, "bss", &bss_setbuf,
 | |
| 					sizeof(bss_setbuf), iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 			}
 | |
| #ifdef ARP_OFFLOAD_SUPPORT
 | |
| 			/* IF SoftAP is enabled, disable arpoe */
 | |
| 			dhd_arp_offload_set(dhd, 0);
 | |
| 			dhd_arp_offload_enable(dhd, FALSE);
 | |
| #endif /* ARP_OFFLOAD_SUPPORT */
 | |
| #ifdef PROP_TXSTATUS_VSDB
 | |
| #if defined(BCMSDIO)
 | |
| 			if (!(FW_SUPPORTED(dhd, rsdb)) && !disable_proptx) {
 | |
| 				bool enabled;
 | |
| 				dhd_wlfc_get_enable(dhd, &enabled);
 | |
| 				if (!enabled) {
 | |
| 					dhd_wlfc_init(dhd);
 | |
| 					wl_ext_ioctl(dev, WLC_UP, NULL, 0, 1);
 | |
| 				}
 | |
| 			}
 | |
| #endif /* BCMSDIO */
 | |
| #endif /* PROP_TXSTATUS_VSDB */
 | |
| 		}
 | |
| 	}
 | |
| 	else if (apstamode == IDUALAP_MODE) {
 | |
| 		wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
 | |
| 	} else if (apstamode == ISTAAPAP_MODE) {
 | |
| 		if (cur_if->ifmode == ISTA_MODE) {
 | |
| 			wl_ext_connect(cur_if->dev, &conn_info);
 | |
| 		} else if (cur_if->ifmode == IAP_MODE) {
 | |
| 			wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
 | |
| 		} else {
 | |
| 			AEXT_ERROR(cur_if->ifname, "wrong ifmode %d\n", cur_if->ifmode);
 | |
| 		}
 | |
| #ifdef WLMESH
 | |
| 	} else if (apstamode == IMESHONLY_MODE ||
 | |
| 			apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
 | |
| 			apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE) {
 | |
| 		if (cur_if->ifmode == ISTA_MODE) {
 | |
| 			wl_ext_connect(cur_if->dev, &conn_info);
 | |
| 		} else if (cur_if->ifmode == IAP_MODE) {
 | |
| 			wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &ssid, sizeof(ssid), 1);
 | |
| 		} else if (cur_if->ifmode == IMESH_MODE) {
 | |
| 			struct wl_join_params join_params;
 | |
| 			// need to up before setting ssid
 | |
| 			memset(&join_params, 0, sizeof(join_params));
 | |
| 			join_params.ssid.SSID_len = strlen(cur_if->ssid);
 | |
| 			memcpy((void *)join_params.ssid.SSID, cur_if->ssid, strlen(cur_if->ssid));
 | |
| 			join_params.params.chanspec_list[0] = fw_chspec;
 | |
| 			join_params.params.chanspec_num = 1;
 | |
| 			wl_ext_ioctl(cur_if->dev, WLC_SET_SSID, &join_params, sizeof(join_params), 1);
 | |
| 		} else {
 | |
| 			AEXT_ERROR(cur_if->ifname, "wrong ifmode %d\n", cur_if->ifmode);
 | |
| 		}
 | |
| #endif /* WLMESH */
 | |
| 	}
 | |
| 
 | |
| 	if (wait_up) {
 | |
| 		OSL_SLEEP(wait_up);
 | |
| 	} else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		timeout = wait_event_interruptible_timeout(apsta_params->netif_change_event,
 | |
| 			wl_get_isam_status(cur_if, AP_CREATED),
 | |
| 			msecs_to_jiffies(MAX_AP_LINK_WAIT_TIME));
 | |
| 		if (timeout <= 0 || !wl_get_isam_status(cur_if, AP_CREATED)) {
 | |
| 			mutex_unlock(&apsta_params->usr_sync);
 | |
| 			wl_ext_disable_iface(dev, cur_if->ifname);
 | |
| 			WL_MSG(ifname, "[%c] failed to enable with SSID: \"%s\"\n",
 | |
| 				cur_if->prefix, cur_if->ssid);
 | |
| 			ret = -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (wl_get_isam_status(cur_if, AP_CREATED) &&
 | |
| 			(cur_if->ifmode == IMESH_MODE || cur_if->ifmode == IAP_MODE) &&
 | |
| 			(apstamode == ISTAAP_MODE || apstamode == ISTAAPAP_MODE ||
 | |
| 			apstamode == ISTAMESH_MODE || apstamode == IMESHAP_MODE ||
 | |
| 			apstamode == ISTAAPMESH_MODE || apstamode == IMESHAPAP_MODE)) {
 | |
| 		int scan_assoc_time = 80;
 | |
| 		for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 			tmp_if = &apsta_params->if_info[i];
 | |
| 			if (tmp_if->dev && tmp_if->ifmode == ISTA_MODE) {
 | |
| 				wl_ext_ioctl(tmp_if->dev, WLC_SET_SCAN_CHANNEL_TIME,
 | |
| 					&scan_assoc_time, sizeof(scan_assoc_time), 1);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 
 | |
| exit:
 | |
| 	if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		wl_clr_isam_status(cur_if, AP_CREATING);
 | |
| 	}
 | |
| 	WL_MSG(ifname, "[%c] Exit ret=%d\n", cur_if->prefix, ret);
 | |
| 	mutex_unlock(&apsta_params->usr_sync);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iapsta_enable(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	char *pch, *pick_tmp, *param;
 | |
| 	char ifname[IFNAMSIZ+1];
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "command=%s, len=%d\n", command, total_len);
 | |
| 
 | |
| 	pick_tmp = command;
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0); // skip iapsta_enable
 | |
| 	param = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 	while (param != NULL) {
 | |
| 		if (!strcmp(param, "ifname")) {
 | |
| 			pch = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 			if (pch) {
 | |
| 				strcpy(ifname, pch);
 | |
| 				ret = wl_ext_enable_iface(dev, ifname, 0);
 | |
| 				if (ret)
 | |
| 					return ret;
 | |
| 			} else {
 | |
| 				AEXT_ERROR(dev->name, "ifname [wlanX]\n");
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 		param = bcmstrtok(&pick_tmp, " ", 0);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| int
 | |
| wl_ext_get_wlfc_maxcount(struct dhd_pub *dhd, int ifidx)
 | |
| {
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *tmp_if, *cur_if = NULL;
 | |
| 	int i, maxcount = WL_TXSTATUS_FREERUNCTR_MASK;
 | |
| 
 | |
| 	if (!apsta_params->rsdb)
 | |
| 		return maxcount;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && tmp_if->ifidx == ifidx) {
 | |
| 			cur_if = tmp_if;
 | |
| 			maxcount = cur_if->transit_maxcount;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (cur_if)
 | |
| 		AEXT_INFO(cur_if->ifname, "update maxcount %d\n", maxcount);
 | |
| 	else
 | |
| 		AEXT_INFO("wlan", "update maxcount %d for ifidx %d\n", maxcount, ifidx);
 | |
| 	return maxcount;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_update_wlfc_maxcount(struct dhd_pub *dhd)
 | |
| {
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *tmp_if;
 | |
| 	bool band_5g = FALSE;
 | |
| 	uint16 chan = 0;
 | |
| 	int i, ret;
 | |
| 
 | |
| 	if (!apsta_params->rsdb)
 | |
| 		return;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev) {
 | |
| 			chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
 | |
| 			if (chan > CH_MAX_2G_CHANNEL) {
 | |
| 				tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_5g;
 | |
| 				ret = dhd_wlfc_update_maxcount(dhd, tmp_if->ifidx,
 | |
| 					tmp_if->transit_maxcount);
 | |
| 				if (ret == 0)
 | |
| 					AEXT_INFO(tmp_if->ifname, "updated maxcount %d\n",
 | |
| 						tmp_if->transit_maxcount);
 | |
| 				band_5g = TRUE;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev) {
 | |
| 			chan = wl_ext_get_chan(apsta_params, tmp_if->dev);
 | |
| 			if ((chan == 0) || (chan <= CH_MAX_2G_CHANNEL && chan >= CH_MIN_2G_CHANNEL)) {
 | |
| 				if (chan == 0) {
 | |
| 					tmp_if->transit_maxcount = WL_TXSTATUS_FREERUNCTR_MASK;
 | |
| 				} else if (band_5g) {
 | |
| 					tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_2g;
 | |
| 				} else {
 | |
| 					tmp_if->transit_maxcount = dhd->conf->proptx_maxcnt_5g;
 | |
| 				}
 | |
| 				ret = dhd_wlfc_update_maxcount(dhd, tmp_if->ifidx,
 | |
| 					tmp_if->transit_maxcount);
 | |
| 				if (ret == 0)
 | |
| 					AEXT_INFO(tmp_if->ifname, "updated maxcount %d\n",
 | |
| 						tmp_if->transit_maxcount);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| #endif /* PROPTX_MAXCOUNT */
 | |
| 
 | |
| static int
 | |
| wl_ext_iapsta_event(struct net_device *dev,
 | |
| 	struct wl_apsta_params *apsta_params, wl_event_msg_t *e, void* data)
 | |
| {
 | |
| 	struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 	struct wl_if_info *mesh_if = NULL;
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 	int i;
 | |
| 	uint32 event_type = ntoh32(e->event_type);
 | |
| 	uint32 status =  ntoh32(e->status);
 | |
| 	uint32 reason =  ntoh32(e->reason);
 | |
| 	uint16 flags =  ntoh16(e->flags);
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev == dev) {
 | |
| 			cur_if = tmp_if;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && tmp_if->ifmode == IMESH_MODE) {
 | |
| 			mesh_if = tmp_if;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 	if (!cur_if || !cur_if->dev) {
 | |
| 		AEXT_DBG(dev->name, "ifidx %d is not ready\n", e->ifidx);
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	if (cur_if->ifmode == ISTA_MODE || cur_if->ifmode == IGC_MODE) {
 | |
| 		if (event_type == WLC_E_LINK) {
 | |
| 			if (!(flags & WLC_EVENT_MSG_LINK)) {
 | |
| 				WL_MSG(cur_if->ifname,
 | |
| 					"[%c] Link down with %pM, %s(%d), reason %d\n",
 | |
| 					cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
 | |
| 					event_type, reason);
 | |
| 				wl_clr_isam_status(cur_if, STA_CONNECTED);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 				if (mesh_if && apsta_params->macs)
 | |
| 					wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 			} else {
 | |
| 				WL_MSG(cur_if->ifname, "[%c] Link UP with %pM\n",
 | |
| 					cur_if->prefix, &e->addr);
 | |
| 				wl_set_isam_status(cur_if, STA_CONNECTED);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 				if (mesh_if && apsta_params->macs)
 | |
| 					wl_mesh_update_master_info(apsta_params, mesh_if);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 			}
 | |
| 			wl_clr_isam_status(cur_if, STA_CONNECTING);
 | |
| 			wake_up_interruptible(&apsta_params->netif_change_event);
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| 			wl_ext_update_wlfc_maxcount(apsta_params->dhd);
 | |
| #endif /* PROPTX_MAXCOUNT */
 | |
| 		} else if (event_type == WLC_E_SET_SSID && status != WLC_E_STATUS_SUCCESS) {
 | |
| 			WL_MSG(cur_if->ifname,
 | |
| 				"connect failed event=%d, reason=%d, status=%d\n",
 | |
| 				event_type, reason, status);
 | |
| 			wl_clr_isam_status(cur_if, STA_CONNECTING);
 | |
| 			wake_up_interruptible(&apsta_params->netif_change_event);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 			if (mesh_if && apsta_params->macs)
 | |
| 				wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| 			wl_ext_update_wlfc_maxcount(apsta_params->dhd);
 | |
| #endif /* PROPTX_MAXCOUNT */
 | |
| 		} else if (event_type == WLC_E_DEAUTH || event_type == WLC_E_DEAUTH_IND ||
 | |
| 				event_type == WLC_E_DISASSOC || event_type == WLC_E_DISASSOC_IND) {
 | |
| 			WL_MSG(cur_if->ifname, "[%c] Link down with %pM, %s(%d), reason %d\n",
 | |
| 				cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
 | |
| 				event_type, reason);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 			if (mesh_if && apsta_params->macs)
 | |
| 				wl_mesh_clear_mesh_info(apsta_params, mesh_if, TRUE);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 		}
 | |
| 	}
 | |
| 	else if (cur_if->ifmode == IAP_MODE || cur_if->ifmode == IMESH_MODE) {
 | |
| 		if ((event_type == WLC_E_SET_SSID && status == WLC_E_STATUS_SUCCESS) ||
 | |
| 				(event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
 | |
| 				reason == WLC_E_REASON_INITIAL_ASSOC)) {
 | |
| 			if (wl_get_isam_status(cur_if, AP_CREATING)) {
 | |
| 				WL_MSG(cur_if->ifname, "[%c] Link up (etype=%d)\n",
 | |
| 					cur_if->prefix, event_type);
 | |
| 				wl_set_isam_status(cur_if, AP_CREATED);
 | |
| 				wake_up_interruptible(&apsta_params->netif_change_event);
 | |
| 			} else {
 | |
| 				wl_set_isam_status(cur_if, AP_CREATED);
 | |
| 				WL_MSG(cur_if->ifname, "[%c] Link up w/o creating? (etype=%d)\n",
 | |
| 					cur_if->prefix, event_type);
 | |
| 			}
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| 			wl_ext_update_wlfc_maxcount(apsta_params->dhd);
 | |
| #endif /* PROPTX_MAXCOUNT */
 | |
| 		}
 | |
| 		else if ((event_type == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) ||
 | |
| 				(event_type == WLC_E_LINK && status == WLC_E_STATUS_SUCCESS &&
 | |
| 				reason == WLC_E_REASON_DEAUTH)) {
 | |
| 			wl_clr_isam_status(cur_if, AP_CREATED);
 | |
| 			WL_MSG(cur_if->ifname, "[%c] Link down, reason=%d\n",
 | |
| 				cur_if->prefix, reason);
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| 			wl_ext_update_wlfc_maxcount(apsta_params->dhd);
 | |
| #endif /* PROPTX_MAXCOUNT */
 | |
| 		}
 | |
| 		else if ((event_type == WLC_E_ASSOC_IND || event_type == WLC_E_REASSOC_IND) &&
 | |
| 				reason == DOT11_SC_SUCCESS) {
 | |
| 			WL_MSG(cur_if->ifname, "[%c] connected device %pM\n",
 | |
| 				cur_if->prefix, &e->addr);
 | |
| 			wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 		}
 | |
| 		else if (event_type == WLC_E_DISASSOC_IND ||
 | |
| 				event_type == WLC_E_DEAUTH_IND ||
 | |
| 				(event_type == WLC_E_DEAUTH && reason != DOT11_RC_RESERVED)) {
 | |
| 			WL_MSG_RLMT(cur_if->ifname, &e->addr, ETHER_ADDR_LEN,
 | |
| 				"[%c] disconnected device %pM, %s(%d), reason=%d\n",
 | |
| 				cur_if->prefix, &e->addr, bcmevent_get_name(event_type),
 | |
| 				event_type, reason);
 | |
| 			wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 		}
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 		if (cur_if->ifmode == IMESH_MODE && apsta_params->macs)
 | |
| 			wl_mesh_event_handler(apsta_params, cur_if, e, data);
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| #ifdef WL_CFG80211
 | |
| u32
 | |
| wl_ext_iapsta_update_channel(dhd_pub_t *dhd, struct net_device *dev,
 | |
| 	u32 channel)
 | |
| {
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL, *tmp_if = NULL;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		tmp_if = &apsta_params->if_info[i];
 | |
| 		if (tmp_if->dev && tmp_if->dev == dev) {
 | |
| 			cur_if = tmp_if;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (cur_if) {
 | |
| 		wl_ext_isam_status(cur_if->dev, NULL, 0);
 | |
| 		cur_if->channel = channel;
 | |
| 		if (wl_ext_master_if(cur_if) && apsta_params->acs) {
 | |
| 			uint auto_band = WL_GET_BAND(channel);
 | |
| 			cur_if->channel = wl_ext_autochannel(cur_if->dev, apsta_params->acs,
 | |
| 				auto_band);
 | |
| 		}
 | |
| 		channel = wl_ext_move_cur_channel(apsta_params, cur_if);
 | |
| 		if (channel)
 | |
| 			wl_ext_move_other_channel(apsta_params, cur_if);
 | |
| 		if (cur_if->ifmode == ISTA_MODE)
 | |
| 			wl_set_isam_status(cur_if, STA_CONNECTING);
 | |
| 	}
 | |
| 
 | |
| 	return channel;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iftype_to_ifmode(struct net_device *net, int wl_iftype, ifmode_t *ifmode)
 | |
| {	
 | |
| 	switch (wl_iftype) {
 | |
| 		case WL_IF_TYPE_STA:
 | |
| 			*ifmode = ISTA_MODE;
 | |
| 			break;
 | |
| 		case WL_IF_TYPE_AP:
 | |
| 			*ifmode = IAP_MODE;
 | |
| 			break;
 | |
| 		case WL_IF_TYPE_P2P_GO:
 | |
| 			*ifmode = IGO_MODE;
 | |
| 			break;
 | |
| 		case WL_IF_TYPE_P2P_GC:
 | |
| 			*ifmode = IGC_MODE;
 | |
| 			break;
 | |
| 		default:
 | |
| 			AEXT_ERROR(net->name, "Unknown interface wl_iftype:0x%x\n", wl_iftype);
 | |
| 			return BCME_ERROR;
 | |
| 	}
 | |
| 	return BCME_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_iapsta_update_iftype(struct net_device *net, int ifidx, int wl_iftype)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL;
 | |
| 
 | |
| 	AEXT_TRACE(net->name, "ifidx=%d, wl_iftype=%d\n", ifidx, wl_iftype);
 | |
| 
 | |
| 	if (ifidx < MAX_IF_NUM) {
 | |
| 		cur_if = &apsta_params->if_info[ifidx];
 | |
| 	}
 | |
| 
 | |
| 	if (cur_if) {
 | |
| 		if (wl_iftype == WL_IF_TYPE_STA) {
 | |
| 			cur_if->ifmode = ISTA_MODE;
 | |
| 			cur_if->prio = PRIO_STA;
 | |
| 			cur_if->prefix = 'S';
 | |
| 		} else if (wl_iftype == WL_IF_TYPE_AP && cur_if->ifmode != IMESH_MODE) {
 | |
| 			cur_if->ifmode = IAP_MODE;
 | |
| 			cur_if->prio = PRIO_AP;
 | |
| 			cur_if->prefix = 'A';
 | |
| 		} else if (wl_iftype == WL_IF_TYPE_P2P_GO) {
 | |
| 			cur_if->ifmode = IGO_MODE;
 | |
| 			cur_if->prio = PRIO_AP;
 | |
| 			cur_if->prefix = 'P';
 | |
| 			apsta_params->vsdb = TRUE;
 | |
| 		} else if (wl_iftype == WL_IF_TYPE_P2P_GC) {
 | |
| 			cur_if->ifmode = IGC_MODE;
 | |
| 			cur_if->prio = PRIO_STA;
 | |
| 			cur_if->prefix = 'P';
 | |
| 			apsta_params->vsdb = TRUE;
 | |
| 			wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 3);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_iapsta_ifadding(struct net_device *net, int ifidx)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL;
 | |
| 
 | |
| 	AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
 | |
| 	if (ifidx < MAX_IF_NUM) {
 | |
| 		cur_if = &apsta_params->if_info[ifidx];
 | |
| 		wl_set_isam_status(cur_if, IF_ADDING);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| bool
 | |
| wl_ext_iapsta_iftype_enabled(struct net_device *net, int wl_iftype)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL;
 | |
| 	ifmode_t ifmode = 0;
 | |
| 
 | |
| 	wl_ext_iftype_to_ifmode(net, wl_iftype, &ifmode);
 | |
| 	cur_if = wl_ext_if_enabled(apsta_params, ifmode);
 | |
| 	if (cur_if)
 | |
| 		return TRUE;
 | |
| 
 | |
| 	return FALSE;
 | |
| }
 | |
| 
 | |
| bool
 | |
| wl_ext_iapsta_mesh_creating(struct net_device *net)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if;
 | |
| 	int i;
 | |
| 
 | |
| 	if (apsta_params) {
 | |
| 		for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 			cur_if = &apsta_params->if_info[i];
 | |
| 			if (cur_if->ifmode==IMESH_MODE && wl_get_isam_status(cur_if, IF_ADDING))
 | |
| 				return TRUE;
 | |
| 		}
 | |
| 	}
 | |
| 	return FALSE;
 | |
| }
 | |
| #endif /* WL_CFG80211 */
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_alive_preinit(struct net_device *dev)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 
 | |
| 	if (apsta_params->init == TRUE) {
 | |
| 		AEXT_ERROR(dev->name, "don't init twice\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "Enter\n");
 | |
| 
 | |
| 	apsta_params->init = TRUE;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_alive_postinit(struct net_device *dev)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	s32 apsta = 0, ap = 0;
 | |
| 	struct wl_if_info *cur_if;
 | |
| 	int i;
 | |
| 
 | |
| 	wl_ext_iovar_getint(dev, "apsta", &apsta);
 | |
| 	wl_ext_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap), 0);
 | |
| 	if (apsta == 1 || ap == 0) {
 | |
| 		apsta_params->apstamode = ISTAONLY_MODE;
 | |
| 		apsta_params->if_info[IF_PIF].ifmode = ISTA_MODE;
 | |
| 		op_mode = DHD_FLAG_STA_MODE;
 | |
| 	} else {
 | |
| 		apsta_params->apstamode = IAPONLY_MODE;
 | |
| 		apsta_params->if_info[IF_PIF].ifmode = IAP_MODE;
 | |
| 		op_mode = DHD_FLAG_HOSTAP_MODE;
 | |
| 	}
 | |
| 	// fix me: how to check it's ISTAAP_MODE or IDUALAP_MODE?
 | |
| 
 | |
| 	wl_ext_get_ioctl_ver(dev, &apsta_params->ioctl_ver);
 | |
| 	WL_MSG(dev->name, "apstamode=%d\n", apsta_params->apstamode);
 | |
| 
 | |
| 	for (i=0; i<MAX_IF_NUM; i++) {
 | |
| 		cur_if = &apsta_params->if_info[i];
 | |
| 		if (i == 1 && !strlen(cur_if->ifname))
 | |
| 			strcpy(cur_if->ifname, "wlan1");
 | |
| 		if (i == 2 && !strlen(cur_if->ifname))
 | |
| 			strcpy(cur_if->ifname, "wlan2");
 | |
| 		if (cur_if->ifmode == ISTA_MODE) {
 | |
| 			cur_if->channel = 0;
 | |
| 			cur_if->maxassoc = -1;
 | |
| 			wl_set_isam_status(cur_if, IF_READY);
 | |
| 			cur_if->prio = PRIO_STA;
 | |
| 			cur_if->prefix = 'S';
 | |
| 			snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_sta");
 | |
| 		} else if (cur_if->ifmode == IAP_MODE) {
 | |
| 			cur_if->channel = 1;
 | |
| 			cur_if->maxassoc = -1;
 | |
| 			wl_set_isam_status(cur_if, IF_READY);
 | |
| 			cur_if->prio = PRIO_AP;
 | |
| 			cur_if->prefix = 'A';
 | |
| 			snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_ap");
 | |
| #ifdef WLMESH
 | |
| 		} else if (cur_if->ifmode == IMESH_MODE) {
 | |
| 			cur_if->channel = 1;
 | |
| 			cur_if->maxassoc = -1;
 | |
| 			wl_set_isam_status(cur_if, IF_READY);
 | |
| 			cur_if->prio = PRIO_MESH;
 | |
| 			cur_if->prefix = 'M';
 | |
| 			snprintf(cur_if->ssid, DOT11_MAX_SSID_LEN, "ttt_mesh");
 | |
| #endif /* WLMESH */
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return op_mode;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_iapsta_get_rsdb(struct net_device *net, struct dhd_pub *dhd)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	wl_config_t *rsdb_p;
 | |
| 	int ret = 0, rsdb = 0;
 | |
| 
 | |
| 	if (dhd->conf->chip == BCM4359_CHIP_ID) {
 | |
| 		ret = wldev_iovar_getbuf(net, "rsdb_mode", NULL, 0,
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (!ret) {
 | |
| 			if (dhd->conf->fw_type == FW_TYPE_MESH) {
 | |
| 				rsdb = 1;
 | |
| 			} else {
 | |
| 				rsdb_p = (wl_config_t *) iovar_buf;
 | |
| 				rsdb = rsdb_p->config;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	AEXT_INFO(net->name, "rsdb_mode=%d\n", rsdb);
 | |
| 
 | |
| 	return rsdb;
 | |
| }
 | |
| 
 | |
| static void
 | |
| wl_ext_iapsta_postinit(struct net_device *net, struct wl_if_info *cur_if)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	int pm;
 | |
| 
 | |
| 	AEXT_TRACE(cur_if->ifname, "ifidx=%d\n", cur_if->ifidx);
 | |
| 	if (cur_if->ifidx == 0) {
 | |
| 		apsta_params->rsdb = wl_ext_iapsta_get_rsdb(net, dhd);
 | |
| 		apsta_params->vsdb = FALSE;
 | |
| 		apsta_params->csa = 0;
 | |
| 		apsta_params->acs = 0;
 | |
| 		apsta_params->radar = wl_ext_radar_detect(net);
 | |
| 		if (dhd->conf->fw_type == FW_TYPE_MESH) {
 | |
| 			apsta_params->csa |= (CSA_FW_BIT | CSA_DRV_BIT);
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (cur_if->ifmode == ISTA_MODE) {
 | |
| 			wl_ext_iovar_setint(cur_if->dev, "roam_off", dhd->conf->roam_off);
 | |
| 			wl_ext_iovar_setint(cur_if->dev, "bcn_timeout", dhd->conf->bcn_timeout);
 | |
| 			if (dhd->conf->pm >= 0)
 | |
| 				pm = dhd->conf->pm;
 | |
| 			else
 | |
| 				pm = PM_FAST;
 | |
| 			wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
 | |
| 			wl_ext_iovar_setint(cur_if->dev, "assoc_retry_max", 20);
 | |
| 		}
 | |
| #ifdef WLMESH
 | |
| 		else if (cur_if->ifmode == IMESH_MODE) {
 | |
| 			pm = 0;
 | |
| 			wl_ext_ioctl(cur_if->dev, WLC_SET_PM, &pm, sizeof(pm), 1);
 | |
| 		}
 | |
| #endif /* WLMESH */
 | |
| 	}
 | |
| #ifdef PROPTX_MAXCOUNT
 | |
| 	wl_ext_update_wlfc_maxcount(dhd);
 | |
| #endif /* PROPTX_MAXCOUNT */
 | |
| 
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_attach_name(struct net_device *net, int ifidx)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL;
 | |
| 
 | |
| 	AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
 | |
| 	if (ifidx < MAX_IF_NUM) {
 | |
| 		cur_if = &apsta_params->if_info[ifidx];
 | |
| 	}
 | |
| 	if (ifidx == 0) {
 | |
| 		strcpy(cur_if->ifname, net->name);
 | |
| 		wl_ext_iapsta_postinit(net, cur_if);
 | |
| 		wl_set_isam_status(cur_if, IF_READY);
 | |
| 	} else if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
 | |
| 		strcpy(cur_if->ifname, net->name);
 | |
| 		wl_ext_iapsta_postinit(net, cur_if);
 | |
| 		wl_clr_isam_status(cur_if, IF_ADDING);
 | |
| 		wl_set_isam_status(cur_if, IF_READY);
 | |
| #ifndef WL_STATIC_IF
 | |
| 		wake_up_interruptible(&apsta_params->netif_change_event);
 | |
| #endif /* WL_STATIC_IF */
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_update_net_device(struct net_device *net, int ifidx)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL, *primary_if;
 | |
| 
 | |
| 	AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
 | |
| 	if (ifidx < MAX_IF_NUM) {
 | |
| 		cur_if = &apsta_params->if_info[ifidx];
 | |
| 	}
 | |
| 	if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
 | |
| 		primary_if = &apsta_params->if_info[IF_PIF];
 | |
| 		if (strlen(cur_if->ifname)) {
 | |
| 			memset(net->name, 0, sizeof(IFNAMSIZ));
 | |
| 			strcpy(net->name, cur_if->ifname);
 | |
| 			net->name[IFNAMSIZ-1] = '\0';
 | |
| 		}
 | |
| #ifndef WL_STATIC_IF
 | |
| 		if (apsta_params->apstamode != IUNKNOWN_MODE &&
 | |
| 				apsta_params->apstamode != ISTAAPAP_MODE &&
 | |
| 				apsta_params->apstamode != ISTASTA_MODE) {
 | |
| 			memcpy(net->dev_addr, primary_if->dev->dev_addr, ETHER_ADDR_LEN);
 | |
| 			net->dev_addr[0] |= 0x02;
 | |
| 			if (ifidx >= 2) {
 | |
| 				net->dev_addr[4] ^= 0x80;
 | |
| 				net->dev_addr[4] += ifidx;
 | |
| 				net->dev_addr[5] += (ifidx-1);
 | |
| 			}
 | |
| 		}
 | |
| #endif /* WL_STATIC_IF */
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_attach_netdev(struct net_device *net, int ifidx, uint8 bssidx)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL, *primary_if;
 | |
| 
 | |
| 	AEXT_TRACE(net->name, "ifidx=%d, bssidx=%d\n", ifidx, bssidx);
 | |
| 	if (ifidx < MAX_IF_NUM) {
 | |
| 		cur_if = &apsta_params->if_info[ifidx];
 | |
| 	}
 | |
| 	if (ifidx == 0) {
 | |
| 		memset(apsta_params, 0, sizeof(struct wl_apsta_params));
 | |
| 		apsta_params->dhd = dhd;
 | |
| 		cur_if->dev = net;
 | |
| 		cur_if->ifidx = ifidx;
 | |
| 		cur_if->bssidx = bssidx;
 | |
| 		cur_if->ifmode = ISTA_MODE;
 | |
| 		cur_if->prio = PRIO_STA;
 | |
| 		cur_if->prefix = 'S';
 | |
| 		wl_ext_event_register(net, dhd, WLC_E_LAST, wl_ext_iapsta_event,
 | |
| 			apsta_params, PRIO_EVENT_IAPSTA);
 | |
| 		strcpy(cur_if->ifname, net->name);
 | |
| 		init_waitqueue_head(&apsta_params->netif_change_event);
 | |
| 		mutex_init(&apsta_params->usr_sync);
 | |
| 		mutex_init(&cur_if->pm_sync);
 | |
| 		INIT_DELAYED_WORK(&cur_if->pm_enable_work, wl_ext_pm_work_handler);
 | |
| 	} else if (cur_if && wl_get_isam_status(cur_if, IF_ADDING)) {
 | |
| 		primary_if = &apsta_params->if_info[IF_PIF];
 | |
| 		cur_if->dev = net;
 | |
| 		cur_if->ifidx = ifidx;
 | |
| 		cur_if->bssidx = bssidx;
 | |
| 		wl_ext_event_register(net, dhd, WLC_E_LAST, wl_ext_iapsta_event,
 | |
| 			apsta_params, PRIO_EVENT_IAPSTA);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 		if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
 | |
| 			wl_mesh_escan_attach(dhd, cur_if);
 | |
| 		}
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 		mutex_init(&cur_if->pm_sync);
 | |
| 		INIT_DELAYED_WORK(&cur_if->pm_enable_work, wl_ext_pm_work_handler);
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_dettach_netdev(struct net_device *net, int ifidx)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 	struct wl_apsta_params *apsta_params = dhd->iapsta_params;
 | |
| 	struct wl_if_info *cur_if = NULL;
 | |
| 
 | |
| 	if (!apsta_params)
 | |
| 		return 0;
 | |
| 
 | |
| 	AEXT_TRACE(net->name, "ifidx=%d\n", ifidx);
 | |
| 	if (ifidx < MAX_IF_NUM) {
 | |
| 		cur_if = &apsta_params->if_info[ifidx];
 | |
| 	}
 | |
| 
 | |
| 	if (ifidx == 0) {
 | |
| 		wl_ext_add_remove_pm_enable_work(net, FALSE);
 | |
| 		wl_ext_event_deregister(net, dhd, WLC_E_LAST, wl_ext_iapsta_event);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 		if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
 | |
| 			wl_mesh_escan_detach(dhd, cur_if);
 | |
| 		}
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 		memset(apsta_params, 0, sizeof(struct wl_apsta_params));
 | |
| 	} else if (cur_if && (wl_get_isam_status(cur_if, IF_READY) ||
 | |
| 			wl_get_isam_status(cur_if, IF_ADDING))) {
 | |
| 		wl_ext_add_remove_pm_enable_work(net, FALSE);
 | |
| 		wl_ext_event_deregister(net, dhd, WLC_E_LAST, wl_ext_iapsta_event);
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 		if (cur_if->ifmode == IMESH_MODE && apsta_params->macs) {
 | |
| 			wl_mesh_escan_detach(dhd, cur_if);
 | |
| 		}
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| 		memset(cur_if, 0, sizeof(struct wl_if_info));
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_iapsta_attach(dhd_pub_t *pub)
 | |
| {
 | |
| 	struct wl_apsta_params *iapsta_params;
 | |
| 
 | |
| 	iapsta_params = kzalloc(sizeof(struct wl_apsta_params), GFP_KERNEL);
 | |
| 	if (unlikely(!iapsta_params)) {
 | |
| 		AEXT_ERROR("wlan", "Could not allocate apsta_params\n");
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 	pub->iapsta_params = (void *)iapsta_params;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_ext_iapsta_dettach(dhd_pub_t *pub)
 | |
| {
 | |
| 	if (pub->iapsta_params) {
 | |
| 		kfree(pub->iapsta_params);
 | |
| 		pub->iapsta_params = NULL;
 | |
| 	}
 | |
| }
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| 
 | |
| #ifdef IDHCP
 | |
| /*
 | |
| terence 20190409:
 | |
| dhd_priv wl dhcpc_dump
 | |
| dhd_priv wl dhcpc_param <client ip> <server ip> <lease time>
 | |
| */
 | |
| static int
 | |
| wl_ext_dhcpc_dump(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	int bytes_written = 0;
 | |
| 	uint32 ip_addr;
 | |
| 	char buf[20]="";
 | |
| 
 | |
| 	if (!data) {
 | |
| 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_addr", &ip_addr);
 | |
| 		if (!ret) {
 | |
| 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"ipaddr %s ", buf);
 | |
| 		}
 | |
| 
 | |
| 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_mask", &ip_addr);
 | |
| 		if (!ret) {
 | |
| 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"mask %s ", buf);
 | |
| 		}
 | |
| 
 | |
| 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_gateway", &ip_addr);
 | |
| 		if (!ret) {
 | |
| 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"gw %s ", buf);
 | |
| 		}
 | |
| 
 | |
| 		ret = wl_ext_iovar_getint(dev, "dhcpc_ip_dnsserv", &ip_addr);
 | |
| 		if (!ret) {
 | |
| 			bcm_ip_ntoa((struct ipv4_addr *)&ip_addr, buf);
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"dnsserv %s ", buf);
 | |
| 		}
 | |
| 
 | |
| 		if (!bytes_written)
 | |
| 			bytes_written = -1;
 | |
| 
 | |
| 		AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 	}
 | |
| 
 | |
| 	return bytes_written;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_dhcpc_param(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int ret = -1, bytes_written = 0;
 | |
| 	char ip_addr_str[20]="", ip_serv_str[20]="";
 | |
| 	struct dhcpc_parameter dhcpc_param;
 | |
| 	uint32 ip_addr, ip_serv, lease_time;
 | |
| 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
 | |
| 
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "cmd %s", command);
 | |
| 		sscanf(data, "%s %s %d", ip_addr_str, ip_serv_str, &lease_time);
 | |
| 		AEXT_TRACE(dev->name, "ip_addr = %s, ip_serv = %s, lease_time = %d",
 | |
| 			ip_addr_str, ip_serv_str, lease_time);
 | |
| 
 | |
| 		memset(&dhcpc_param, 0, sizeof(struct dhcpc_parameter));
 | |
| 		if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_addr)) {
 | |
| 			AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
 | |
| 			ret = -1;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		dhcpc_param.ip_addr = ip_addr;
 | |
| 
 | |
| 		if (!bcm_atoipv4(ip_addr_str, (struct ipv4_addr *)&ip_serv)) {
 | |
| 			AEXT_ERROR(dev->name, "wrong ip_addr_str %s\n", ip_addr_str);
 | |
| 			ret = -1;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		dhcpc_param.ip_serv = ip_serv;
 | |
| 		dhcpc_param.lease_time = lease_time;
 | |
| 		ret = wl_ext_iovar_setbuf(dev, "dhcpc_param", &dhcpc_param,
 | |
| 			sizeof(struct dhcpc_parameter), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 	} else {
 | |
| 		ret = wl_ext_iovar_getbuf(dev, "dhcpc_param", &dhcpc_param,
 | |
| 			sizeof(struct dhcpc_parameter), iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (!ret) {
 | |
| 			bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_addr, ip_addr_str);
 | |
| 			bytes_written += snprintf(command + bytes_written, total_len,
 | |
| 				"ip_addr %s\n", ip_addr_str);
 | |
| 			bcm_ip_ntoa((struct ipv4_addr *)&dhcpc_param.ip_serv, ip_serv_str);
 | |
| 			bytes_written += snprintf(command + bytes_written, total_len,
 | |
| 				"ip_serv %s\n", ip_serv_str);
 | |
| 			bytes_written += snprintf(command + bytes_written, total_len,
 | |
| 				"lease_time %d\n", dhcpc_param.lease_time);
 | |
| 			AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 			ret = bytes_written;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	exit:
 | |
| 		return ret;
 | |
| }
 | |
| #endif /* IDHCP */
 | |
| 
 | |
| int
 | |
| wl_ext_mkeep_alive(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	wl_mkeep_alive_pkt_t *mkeep_alive_pktp;
 | |
| 	int ret = -1, i, ifidx, id, period=-1;
 | |
| 	char *packet = NULL, *buf = NULL;
 | |
| 	int bytes_written = 0;
 | |
| 
 | |
| 	if (data) {
 | |
| 		buf = kmalloc(total_len, GFP_KERNEL);
 | |
| 		if (buf == NULL) {
 | |
| 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		packet = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
 | |
| 		if (packet == NULL) {
 | |
| 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		AEXT_TRACE(dev->name, "cmd %s", command);
 | |
| 		sscanf(data, "%d %d %s", &id, &period, packet);
 | |
| 		AEXT_TRACE(dev->name, "id=%d, period=%d, packet=%s", id, period, packet);
 | |
| 		if (period >= 0) {
 | |
| 			ifidx = dhd_net2idx(dhd->info, dev);
 | |
| 			ret = dhd_conf_mkeep_alive(dhd, ifidx, id, period, packet, FALSE);
 | |
| 		} else {
 | |
| 			if (id < 0)
 | |
| 				id = 0;
 | |
| 			ret = wl_ext_iovar_getbuf(dev, "mkeep_alive", &id, sizeof(id), buf,
 | |
| 				total_len, NULL);
 | |
| 			if (!ret) {
 | |
| 				mkeep_alive_pktp = (wl_mkeep_alive_pkt_t *) buf;
 | |
| 				bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 					"Id            :%d\n"
 | |
| 					"Period (msec) :%d\n"
 | |
| 					"Length        :%d\n"
 | |
| 					"Packet        :0x",
 | |
| 					mkeep_alive_pktp->keep_alive_id,
 | |
| 					dtoh32(mkeep_alive_pktp->period_msec),
 | |
| 					dtoh16(mkeep_alive_pktp->len_bytes));
 | |
| 				for (i=0; i<mkeep_alive_pktp->len_bytes; i++) {
 | |
| 					bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 						"%02x", mkeep_alive_pktp->data[i]);
 | |
| 				}
 | |
| 				AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 				ret = bytes_written;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (buf)
 | |
| 		kfree(buf);
 | |
| 	if (packet)
 | |
| 		kfree(packet);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #ifdef WL_EXT_TCPKA
 | |
| static int
 | |
| wl_ext_tcpka_conn_add(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	tcpka_conn_t *tcpka = NULL;
 | |
| 	uint32 sess_id = 0, ipid = 0, srcport = 0, dstport = 0, seq = 0, ack = 0,
 | |
| 		tcpwin = 0, tsval = 0, tsecr = 0, len = 0, ka_payload_len = 0;
 | |
| 	char dst_mac[ETHER_ADDR_STR_LEN], src_ip[IPV4_ADDR_STR_LEN],
 | |
| 		dst_ip[IPV4_ADDR_STR_LEN], ka_payload[32];
 | |
| 
 | |
| 	if (data) {
 | |
| 		memset(dst_mac, 0, sizeof(dst_mac));
 | |
| 		memset(src_ip, 0, sizeof(src_ip));
 | |
| 		memset(dst_ip, 0, sizeof(dst_ip));
 | |
| 		memset(ka_payload, 0, sizeof(ka_payload));
 | |
| 		sscanf(data, "%d %s %s %s %d %d %d %u %u %d %u %u %u %32s",
 | |
| 			&sess_id, dst_mac, src_ip, dst_ip, &ipid, &srcport, &dstport, &seq,
 | |
| 			&ack, &tcpwin, &tsval, &tsecr, &len, ka_payload);
 | |
| 
 | |
| 		ka_payload_len = strlen(ka_payload) / 2;
 | |
| 		tcpka = kmalloc(sizeof(struct tcpka_conn) + ka_payload_len, GFP_KERNEL);
 | |
| 		if (tcpka == NULL) {
 | |
| 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n",
 | |
| 				sizeof(struct tcpka_conn) + ka_payload_len);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		memset(tcpka, 0, sizeof(struct tcpka_conn) + ka_payload_len);
 | |
| 
 | |
| 		tcpka->sess_id = sess_id;
 | |
| 		if (!(ret = bcm_ether_atoe(dst_mac, &tcpka->dst_mac))) {
 | |
| 			AEXT_ERROR(dev->name, "mac parsing err addr=%s\n", dst_mac);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		if (!bcm_atoipv4(src_ip, &tcpka->src_ip)) {
 | |
| 			AEXT_ERROR(dev->name, "src_ip parsing err ip=%s\n", src_ip);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		if (!bcm_atoipv4(dst_ip, &tcpka->dst_ip)) {
 | |
| 			AEXT_ERROR(dev->name, "dst_ip parsing err ip=%s\n", dst_ip);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		tcpka->ipid = ipid;
 | |
| 		tcpka->srcport = srcport;
 | |
| 		tcpka->dstport = dstport;
 | |
| 		tcpka->seq = seq;
 | |
| 		tcpka->ack = ack;
 | |
| 		tcpka->tcpwin = tcpwin;
 | |
| 		tcpka->tsval = tsval;
 | |
| 		tcpka->tsecr = tsecr;
 | |
| 		tcpka->len = len;
 | |
| 		ka_payload_len = wl_pattern_atoh(ka_payload, (char *)tcpka->ka_payload);
 | |
| 		if (ka_payload_len == -1) {
 | |
| 			AEXT_ERROR(dev->name,"rejecting ka_payload=%s\n", ka_payload);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		tcpka->ka_payload_len = ka_payload_len;
 | |
| 
 | |
| 		AEXT_INFO(dev->name,
 | |
| 			"tcpka_conn_add %d %pM %pM %pM %d %d %d %u %u %d %u %u %u %u \"%s\"\n",
 | |
| 			tcpka->sess_id, &tcpka->dst_mac, &tcpka->src_ip, &tcpka->dst_ip,
 | |
| 			tcpka->ipid, tcpka->srcport, tcpka->dstport, tcpka->seq,
 | |
| 			tcpka->ack, tcpka->tcpwin, tcpka->tsval, tcpka->tsecr,
 | |
| 			tcpka->len, tcpka->ka_payload_len, tcpka->ka_payload);
 | |
| 
 | |
| 		ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_add", (char *)tcpka,
 | |
| 			(sizeof(tcpka_conn_t) + tcpka->ka_payload_len - 1),
 | |
| 			iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (tcpka)
 | |
| 		kfree(tcpka);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_tcpka_conn_enable(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	tcpka_conn_sess_t tcpka_conn;
 | |
| 	int ret = 0;
 | |
| 	uint32 sess_id = 0, flag, interval = 0, retry_interval = 0, retry_count = 0;
 | |
| 
 | |
| 	if (data) {
 | |
| 		sscanf(data, "%d %d %d %d %d",
 | |
| 			&sess_id, &flag, &interval, &retry_interval, &retry_count);
 | |
| 		tcpka_conn.sess_id = sess_id;
 | |
| 		tcpka_conn.flag = flag;
 | |
| 		if (tcpka_conn.flag) {
 | |
| 			tcpka_conn.tcpka_timers.interval = interval;
 | |
| 			tcpka_conn.tcpka_timers.retry_interval = retry_interval;
 | |
| 			tcpka_conn.tcpka_timers.retry_count = retry_count;
 | |
| 		} else {
 | |
| 			tcpka_conn.tcpka_timers.interval = 0;
 | |
| 			tcpka_conn.tcpka_timers.retry_interval = 0;
 | |
| 			tcpka_conn.tcpka_timers.retry_count = 0;
 | |
| 		}
 | |
| 
 | |
| 		AEXT_INFO(dev->name, "tcpka_conn_enable %d %d %d %d %d\n",
 | |
| 			tcpka_conn.sess_id, tcpka_conn.flag,
 | |
| 			tcpka_conn.tcpka_timers.interval,
 | |
| 			tcpka_conn.tcpka_timers.retry_interval,
 | |
| 			tcpka_conn.tcpka_timers.retry_count);
 | |
| 
 | |
| 		ret = wl_ext_iovar_setbuf(dev, "tcpka_conn_enable", (char *)&tcpka_conn,
 | |
| 			sizeof(tcpka_conn_sess_t), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_tcpka_conn_info(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	tcpka_conn_sess_info_t *info = NULL;
 | |
| 	uint32 sess_id = 0;
 | |
| 	int ret = 0, bytes_written = 0;
 | |
| 
 | |
| 	if (data) {
 | |
| 		sscanf(data, "%d", &sess_id);
 | |
| 		AEXT_INFO(dev->name, "tcpka_conn_sess_info %d\n", sess_id);
 | |
| 		ret = wl_ext_iovar_getbuf(dev, "tcpka_conn_sess_info", (char *)&sess_id,
 | |
| 			sizeof(uint32), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		if (!ret) {
 | |
| 			info = (tcpka_conn_sess_info_t *) iovar_buf;
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"id   :%d\n"
 | |
| 				"ipid :%d\n"
 | |
| 				"seq  :%u\n"
 | |
| 				"ack  :%u",
 | |
| 				sess_id, info->ipid, info->seq, info->ack);
 | |
| 			AEXT_INFO(dev->name, "%s\n", command);
 | |
| 			ret = bytes_written;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| #endif /* WL_EXT_TCPKA */
 | |
| 
 | |
| static int
 | |
| wl_ext_rsdb_mode(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	wl_config_t rsdb_mode_cfg = {1, 0}, *rsdb_p;
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (data) {
 | |
| 		rsdb_mode_cfg.config = (int)simple_strtol(data, NULL, 0);
 | |
| 		ret = wl_ext_iovar_setbuf(dev, "rsdb_mode", (char *)&rsdb_mode_cfg,
 | |
| 			sizeof(rsdb_mode_cfg), iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		AEXT_INFO(dev->name, "rsdb_mode %d\n", rsdb_mode_cfg.config);
 | |
| 	} else {
 | |
| 		ret = wl_ext_iovar_getbuf(dev, "rsdb_mode", NULL, 0,
 | |
| 			iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (!ret) {
 | |
| 			rsdb_p = (wl_config_t *) iovar_buf;
 | |
| 			ret = snprintf(command, total_len, "%d", rsdb_p->config);
 | |
| 			AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_recal(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int ret = 0, i, nchan, nssid = 0;
 | |
| 	int params_size = WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
 | |
| 	wl_scan_params_t *params = NULL;
 | |
| 	int ioctl_ver;
 | |
| 	char *p;
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "Enter\n");
 | |
| 
 | |
| 	if (data) {
 | |
| 		params_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
 | |
| 		params = (wl_scan_params_t *) kzalloc(params_size, GFP_KERNEL);
 | |
| 		if (params == NULL) {
 | |
| 			ret = -ENOMEM;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		memset(params, 0, params_size);
 | |
| 
 | |
| 		wl_ext_get_ioctl_ver(dev, &ioctl_ver);
 | |
| 
 | |
| 		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->scan_type |= WL_SCANFLAGS_PASSIVE;
 | |
| 		nchan = 2;
 | |
| 		params->channel_list[0] = wf_channel2chspec(1, WL_CHANSPEC_BW_20);
 | |
| 		params->channel_list[1] = wf_channel2chspec(2, WL_CHANSPEC_BW_20);
 | |
| 
 | |
| 		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);
 | |
| 
 | |
| 		for (i = 0; i < nchan; i++) {
 | |
| 			wl_ext_chspec_host_to_driver(ioctl_ver, params->channel_list[i]);
 | |
| 		}
 | |
| 
 | |
| 		p = (char*)params->channel_list + nchan * sizeof(uint16);
 | |
| 
 | |
| 		params->channel_num = htod32((nssid << WL_SCAN_PARAMS_NSSID_SHIFT) |
 | |
| 		                             (nchan & WL_SCAN_PARAMS_COUNT_MASK));
 | |
| 		params_size = p - (char*)params + nssid * sizeof(wlc_ssid_t);
 | |
| 
 | |
| 		AEXT_INFO(dev->name, "recal\n");
 | |
| 		ret = wl_ext_ioctl(dev, WLC_SCAN, params, params_size, 1);
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (params)
 | |
| 		kfree(params);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static s32
 | |
| wl_ext_add_remove_eventmsg(struct net_device *ndev, u16 event, bool add)
 | |
| {
 | |
| 	s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
 | |
| 	s8 eventmask[WL_EVENTING_MASK_LEN];
 | |
| 	s32 err = 0;
 | |
| 
 | |
| 	if (!ndev)
 | |
| 		return -ENODEV;
 | |
| 
 | |
| 	/* Setup event_msgs */
 | |
| 	err = wldev_iovar_getbuf(ndev, "event_msgs", NULL, 0, iovbuf, sizeof(iovbuf), NULL);
 | |
| 	if (unlikely(err)) {
 | |
| 		AEXT_ERROR(ndev->name, "Get event_msgs error (%d)\n", err);
 | |
| 		goto eventmsg_out;
 | |
| 	}
 | |
| 	memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
 | |
| 	if (add) {
 | |
| 		setbit(eventmask, event);
 | |
| 	} else {
 | |
| 		clrbit(eventmask, event);
 | |
| 	}
 | |
| 	err = wldev_iovar_setbuf(ndev, "event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf,
 | |
| 			sizeof(iovbuf), NULL);
 | |
| 	if (unlikely(err)) {
 | |
| 		AEXT_ERROR(ndev->name, "Set event_msgs error (%d)\n", err);
 | |
| 		goto eventmsg_out;
 | |
| 	}
 | |
| 
 | |
| eventmsg_out:
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_event_msg(struct net_device *dev, char *data,
 | |
| 	char *command, int total_len)
 | |
| {
 | |
| 	s8 iovbuf[WL_EVENTING_MASK_LEN + 12];
 | |
| 	s8 eventmask[WL_EVENTING_MASK_LEN];
 | |
| 	int i, bytes_written = 0, add = -1;
 | |
| 	uint event;
 | |
| 	char *vbuf;
 | |
| 	bool skipzeros;
 | |
| 
 | |
| 	/* dhd_priv wl event_msg [offset] [1/0, 1 for add, 0 for remove] */
 | |
| 	/* dhd_priv wl event_msg 40 1 */
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 		sscanf(data, "%d %d", &event, &add);
 | |
| 		/* Setup event_msgs */
 | |
| 		bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
 | |
| 			sizeof(iovbuf), NULL);
 | |
| 		if (unlikely(bytes_written)) {
 | |
| 			AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
 | |
| 			goto eventmsg_out;
 | |
| 		}
 | |
| 		memcpy(eventmask, iovbuf, WL_EVENTING_MASK_LEN);
 | |
| 		if (add == -1) {
 | |
| 			if (isset(eventmask, event))
 | |
| 				bytes_written += snprintf(command+bytes_written, total_len, "1");
 | |
| 			else
 | |
| 				bytes_written += snprintf(command+bytes_written, total_len, "0");
 | |
| 			AEXT_INFO(dev->name, "%s\n", command);
 | |
| 			goto eventmsg_out;
 | |
| 		}
 | |
| 		bytes_written = wl_ext_add_remove_eventmsg(dev, event, add);
 | |
| 	}
 | |
| 	else {
 | |
| 		/* Setup event_msgs */
 | |
| 		bytes_written = wldev_iovar_getbuf(dev, "event_msgs", NULL, 0, iovbuf,
 | |
| 			sizeof(iovbuf), NULL);
 | |
| 		if (bytes_written) {
 | |
| 			AEXT_ERROR(dev->name, "Get event_msgs error (%d)\n", bytes_written);
 | |
| 			goto eventmsg_out;
 | |
| 		}
 | |
| 		vbuf = (char *)iovbuf;
 | |
| 		bytes_written += snprintf(command+bytes_written, total_len, "0x");
 | |
| 		for (i = (sizeof(eventmask) - 1); i >= 0; i--) {
 | |
| 			if (vbuf[i] || (i == 0))
 | |
| 				skipzeros = FALSE;
 | |
| 			if (skipzeros)
 | |
| 				continue;
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"%02x", vbuf[i] & 0xff);
 | |
| 		}
 | |
| 		AEXT_INFO(dev->name, "%s\n", command);
 | |
| 	}
 | |
| 
 | |
| eventmsg_out:
 | |
| 	return bytes_written;
 | |
| }
 | |
| 
 | |
| #ifdef PKT_FILTER_SUPPORT
 | |
| extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg);
 | |
| extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id);
 | |
| extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode);
 | |
| static int
 | |
| wl_ext_pkt_filter_add(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	int i, filter_id, new_id = 0, cnt;
 | |
| 	conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
 | |
| 	char **pktfilter = dhd->pktfilter;
 | |
| 	int err = 0;
 | |
| 
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 
 | |
| 		new_id = simple_strtol(data, NULL, 10);
 | |
| 		if (new_id <= 0) {
 | |
| 			AEXT_ERROR(dev->name, "wrong id %d\n", new_id);
 | |
| 			return -1;
 | |
| 		}
 | |
| 
 | |
| 		cnt = dhd->pktfilter_count;
 | |
| 		for (i=0; i<cnt; i++) {
 | |
| 			if (!pktfilter[i])
 | |
| 				continue;
 | |
| 			filter_id = simple_strtol(pktfilter[i], NULL, 10);
 | |
| 			if (new_id == filter_id) {
 | |
| 				AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		cnt = filter_add->count;
 | |
| 		if (cnt >= DHD_CONF_FILTER_MAX) {
 | |
| 			AEXT_ERROR(dev->name, "not enough filter\n");
 | |
| 			return -1;
 | |
| 		}
 | |
| 		for (i=0; i<cnt; i++) {
 | |
| 			filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
 | |
| 			if (new_id == filter_id) {
 | |
| 				AEXT_ERROR(dev->name, "filter id %d already in list\n", filter_id);
 | |
| 				return -1;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		strcpy(&filter_add->filter[cnt][0], data);
 | |
| 		dhd->pktfilter[dhd->pktfilter_count] = filter_add->filter[cnt];
 | |
| 		filter_add->count++;
 | |
| 		dhd->pktfilter_count++;
 | |
| 
 | |
| 		dhd_pktfilter_offload_set(dhd, data);
 | |
| 		AEXT_INFO(dev->name, "filter id %d added\n", new_id);
 | |
| 	}
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_pkt_filter_delete(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	int i, j, filter_id, cnt;
 | |
| 	char **pktfilter = dhd->pktfilter;
 | |
| 	conf_pkt_filter_add_t *filter_add = &dhd->conf->pkt_filter_add;
 | |
| 	bool in_filter = FALSE;
 | |
| 	int id, err = 0;
 | |
| 
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 		id = (int)simple_strtol(data, NULL, 0);
 | |
| 
 | |
| 		cnt = filter_add->count;
 | |
| 		for (i=0; i<cnt; i++) {
 | |
| 			filter_id = simple_strtol(filter_add->filter[i], NULL, 10);
 | |
| 			if (id == filter_id) {
 | |
| 				in_filter = TRUE;
 | |
| 				memset(filter_add->filter[i], 0, PKT_FILTER_LEN);
 | |
| 				for (j=i; j<(cnt-1); j++) {
 | |
| 					strcpy(filter_add->filter[j], filter_add->filter[j+1]);
 | |
| 					memset(filter_add->filter[j+1], 0, PKT_FILTER_LEN);
 | |
| 				}
 | |
| 				cnt--;
 | |
| 				filter_add->count--;
 | |
| 				dhd->pktfilter_count--;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		cnt = dhd->pktfilter_count;
 | |
| 		for (i=0; i<cnt; i++) {
 | |
| 			if (!pktfilter[i])
 | |
| 				continue;
 | |
| 			filter_id = simple_strtol(pktfilter[i], NULL, 10);
 | |
| 			if (id == filter_id) {
 | |
| 				in_filter = TRUE;
 | |
| 				memset(pktfilter[i], 0, strlen(pktfilter[i]));
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (in_filter) {
 | |
| 			dhd_pktfilter_offload_delete(dhd, id);
 | |
| 			AEXT_INFO(dev->name, "filter id %d deleted\n", id);
 | |
| 		} else {
 | |
| 			AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
 | |
| 			err = -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_pkt_filter_enable(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	int err = 0, id, enable;
 | |
| 	int i, filter_id, cnt;
 | |
| 	char **pktfilter = dhd->pktfilter;
 | |
| 	bool in_filter = FALSE;
 | |
| 
 | |
| 	/* dhd_priv wl pkt_filter_enable [id] [1/0] */
 | |
| 	/* dhd_priv wl pkt_filter_enable 141 1 */
 | |
| 	if (data) {
 | |
| 		sscanf(data, "%d %d", &id, &enable);
 | |
| 
 | |
| 		cnt = dhd->pktfilter_count;
 | |
| 		for (i=0; i<cnt; i++) {
 | |
| 			if (!pktfilter[i])
 | |
| 				continue;
 | |
| 			filter_id = simple_strtol(pktfilter[i], NULL, 10);
 | |
| 			if (id == filter_id) {
 | |
| 				in_filter = TRUE;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (in_filter) {
 | |
| 			dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
 | |
| 				enable, dhd_master_mode);
 | |
| 			AEXT_INFO(dev->name, "filter id %d %s\n", id, enable?"enabled":"disabled");
 | |
| 		} else {
 | |
| 			AEXT_ERROR(dev->name, "filter id %d not in list\n", id);
 | |
| 			err = -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return err;
 | |
| }
 | |
| #endif /* PKT_FILTER_SUPPORT */
 | |
| 
 | |
| #ifdef SENDPROB
 | |
| static int
 | |
| wl_ext_send_probreq(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int err = 0;
 | |
| 	char addr_str[16], addr[6];
 | |
| 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
 | |
| 	char ie_data[WLC_IOCTL_SMLEN] = "\0";
 | |
| 	wl_probe_params_t params;
 | |
| 
 | |
| 	/* dhd_priv wl send_probreq [dest. addr] [OUI+VAL] */
 | |
| 	/* dhd_priv wl send_probreq 0x00904c010203 0x00904c01020304050607 */
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 		sscanf(data, "%s %s", addr_str, ie_data);
 | |
| 		AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
 | |
| 
 | |
| 		if (strlen(addr_str) != 14) {
 | |
| 			AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		wl_pattern_atoh(addr_str, (char *) addr);
 | |
| 		memset(¶ms, 0, sizeof(params));
 | |
| 		memcpy(¶ms.bssid, addr, ETHER_ADDR_LEN);
 | |
| 		memcpy(¶ms.mac, addr, ETHER_ADDR_LEN);
 | |
| 
 | |
| 		err = wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "add");
 | |
| 		if (err)
 | |
| 			goto exit;
 | |
| 		err = wl_ext_iovar_setbuf(dev, "sendprb", (char *)¶ms, sizeof(params),
 | |
| 			iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		OSL_SLEEP(100);
 | |
| 		wl_ext_add_del_ie(dev, VNDR_IE_PRBREQ_FLAG, ie_data, "del");
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_send_probresp(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int err = 0;
 | |
| 	char addr_str[16], addr[6];
 | |
| 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
 | |
| 	char ie_data[WLC_IOCTL_SMLEN] = "\0";
 | |
| 
 | |
| 	/* dhd_priv wl send_probresp [dest. addr] [OUI+VAL] */
 | |
| 	/* dhd_priv wl send_probresp 0x00904c010203 0x00904c01020304050607 */
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 		sscanf(data, "%s %s", addr_str, ie_data);
 | |
| 		AEXT_TRACE(dev->name, "addr=%s, ie=%s\n", addr_str, ie_data);
 | |
| 
 | |
| 		if (strlen(addr_str) != 14) {
 | |
| 			AEXT_ERROR(dev->name, "wrong addr %s\n", addr_str);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		wl_pattern_atoh(addr_str, (char *) addr);
 | |
| 
 | |
| 		err = wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "add");
 | |
| 		if (err)
 | |
| 			goto exit;
 | |
| 		err = wl_ext_iovar_setbuf(dev, "send_probresp", addr, sizeof(addr),
 | |
| 			iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		OSL_SLEEP(100);
 | |
| 		wl_ext_add_del_ie(dev, VNDR_IE_PRBRSP_FLAG, ie_data, "del");
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_recv_probreq(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int err = 0, enable = 0;
 | |
| 	char cmd[32];
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 
 | |
| 	/* enable:
 | |
| 	    1. dhd_priv wl 86 0
 | |
| 	    2. dhd_priv wl event_msg 44 1
 | |
| 	    disable:
 | |
| 	    1. dhd_priv wl 86 2;
 | |
| 	    2. dhd_priv wl event_msg 44 0
 | |
| 	*/
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 		sscanf(data, "%d", &enable);
 | |
| 		if (enable) {
 | |
| 			strcpy(cmd, "wl 86 0");
 | |
| 			err = wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			if (err)
 | |
| 				goto exit;
 | |
| 			strcpy(cmd, "wl event_msg 44 1");
 | |
| 			err = wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			if (err)
 | |
| 				goto exit;
 | |
| 			dhd->recv_probereq = TRUE;
 | |
| 		} else {
 | |
| 			if (dhd->conf->pm)
 | |
| 				strcpy(cmd, "wl 86 2"); {
 | |
| 				wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			}
 | |
| 			strcpy(cmd, "wl event_msg 44 0");
 | |
| 			wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			dhd->recv_probereq = FALSE;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_recv_probresp(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	int err = 0, enable = 0;
 | |
| 	char cmd[32];
 | |
| 
 | |
| 	/* enable:
 | |
| 	    1. dhd_priv wl pkt_filter_add 150 0 0 0 0xFF 0x50
 | |
| 	    2. dhd_priv wl pkt_filter_enable 150 1 
 | |
| 	    3. dhd_priv wl mpc 0
 | |
| 	    4. dhd_priv wl 108 1
 | |
| 	    disable:
 | |
| 	    1. dhd_priv wl 108 0
 | |
| 	    2. dhd_priv wl mpc 1
 | |
| 	    3. dhd_priv wl pkt_filter_disable 150 0
 | |
| 	    4. dhd_priv pkt_filter_delete 150
 | |
| 	*/
 | |
| 	if (data) {
 | |
| 		AEXT_TRACE(dev->name, "data = %s\n", data);
 | |
| 		sscanf(data, "%d", &enable);
 | |
| 		if (enable) {
 | |
| 			strcpy(cmd, "wl pkt_filter_add 150 0 0 0 0xFF 0x50");
 | |
| 			err = wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			if (err)
 | |
| 				goto exit;
 | |
| 			strcpy(cmd, "wl pkt_filter_enable 150 1");
 | |
| 			err = wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			if (err)
 | |
| 				goto exit;
 | |
| 			strcpy(cmd, "wl mpc 0");
 | |
| 			err = wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			if (err)
 | |
| 				goto exit;
 | |
| 			strcpy(cmd, "wl 108 1");
 | |
| 			err= wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 		} else {
 | |
| 			strcpy(cmd, "wl 108 0");
 | |
| 			wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			strcpy(cmd, "wl mpc 1");
 | |
| 			wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			strcpy(cmd, "wl pkt_filter_enable 150 0");
 | |
| 			wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 			strcpy(cmd, "wl pkt_filter_delete 150");
 | |
| 			wl_ext_wl_iovar(dev, cmd, total_len);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
|     return err;
 | |
| }
 | |
| #endif /* SENDPROB */
 | |
| 
 | |
| static int
 | |
| wl_ext_gtk_key_info(struct net_device *dev, char *data, char *command, int total_len)
 | |
| {
 | |
| 	int err = 0;
 | |
| 	char iovar_buf[WLC_IOCTL_SMLEN]="\0";
 | |
| 	gtk_keyinfo_t keyinfo;
 | |
| 	bcol_gtk_para_t bcol_keyinfo;
 | |
| 
 | |
| 	/* wl gtk_key_info [kck kek replay_ctr] */
 | |
| 	/* wl gtk_key_info 001122..FF001122..FF00000000000001 */
 | |
| 	if (data) {
 | |
| 		memset(&keyinfo, 0, sizeof(keyinfo));
 | |
| 		memcpy(&keyinfo, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH+RSN_REPLAY_LEN);
 | |
| 		if (android_msg_level & ANDROID_INFO_LEVEL) {
 | |
| 			prhex("kck", (uchar *)keyinfo.KCK, RSN_KCK_LENGTH);
 | |
| 			prhex("kek", (uchar *)keyinfo.KEK, RSN_KEK_LENGTH);
 | |
| 			prhex("replay_ctr", (uchar *)keyinfo.ReplayCounter, RSN_REPLAY_LEN);
 | |
| 		}
 | |
| 
 | |
| 		memset(&bcol_keyinfo, 0, sizeof(bcol_keyinfo));
 | |
| 		bcol_keyinfo.enable = 1;
 | |
| 		bcol_keyinfo.ptk_len = 64;
 | |
| 		memcpy(&bcol_keyinfo.ptk, data, RSN_KCK_LENGTH+RSN_KEK_LENGTH);
 | |
| 		err = wl_ext_iovar_setbuf(dev, "bcol_gtk_rekey_ptk", &bcol_keyinfo,
 | |
| 			sizeof(bcol_keyinfo), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		if (!err) {
 | |
| 			goto exit;
 | |
| 		}
 | |
| 
 | |
| 		err = wl_ext_iovar_setbuf(dev, "gtk_key_info", &keyinfo, sizeof(keyinfo),
 | |
| 			iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		if (err) {
 | |
| 			AEXT_ERROR(dev->name, "failed to set gtk_key_info\n");
 | |
| 			goto exit;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| #ifdef WL_EXT_WOWL
 | |
| static int
 | |
| wl_ext_wowl_pattern(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	uint buf_len = 0;
 | |
| 	int	offset;
 | |
| 	char mask[128]="\0", pattern[128]="\0", add[4]="\0",
 | |
| 		mask_tmp[128], *pmask_tmp;
 | |
| 	uint32 masksize, patternsize, pad_len = 0;
 | |
| 	wl_wowl_pattern2_t *wowl_pattern2 = NULL;
 | |
| 	wl_wowl_pattern_t *wowl_pattern = NULL;
 | |
| 	char *mask_and_pattern;
 | |
| 	wl_wowl_pattern_list_t *list;
 | |
| 	uint8 *ptr;
 | |
| 	int ret = 0, i, j, v;
 | |
| 
 | |
| 	if (data) {
 | |
| 		sscanf(data, "%s %d %s %s", add, &offset, mask_tmp, pattern);
 | |
| 		if (strcmp(add, "add") != 0 && strcmp(add, "clr") != 0) {
 | |
| 			AEXT_ERROR(dev->name, "first arg should be add or clr\n");
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		if (!strcmp(add, "clr")) {
 | |
| 			AEXT_INFO(dev->name, "wowl_pattern clr\n");
 | |
| 			ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", add,
 | |
| 				sizeof(add), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		masksize = strlen(mask_tmp) -2;
 | |
| 		AEXT_TRACE(dev->name, "0 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
 | |
| 
 | |
| 		// add pading
 | |
| 		if (masksize % 16)
 | |
| 			pad_len = (16 - masksize % 16);
 | |
| 		for (i=0; i<pad_len; i++)
 | |
| 			strcat(mask_tmp, "0");
 | |
| 		masksize += pad_len;
 | |
| 		AEXT_TRACE(dev->name, "1 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
 | |
| 
 | |
| 		// translate 0x00 to 0, others to 1
 | |
| 		j = 0;
 | |
| 		pmask_tmp = &mask_tmp[2];
 | |
| 		for (i=0; i<masksize/2; i++) {
 | |
| 			if(strncmp(&pmask_tmp[i*2], "00", 2))
 | |
| 				pmask_tmp[j] = '1';
 | |
| 			else
 | |
| 				pmask_tmp[j] = '0';
 | |
| 			j++;
 | |
| 		}
 | |
| 		pmask_tmp[j] = '\0';
 | |
| 		masksize = masksize / 2;
 | |
| 		AEXT_TRACE(dev->name, "2 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
 | |
| 
 | |
| 		// reorder per 8bits
 | |
| 		pmask_tmp = &mask_tmp[2];
 | |
| 		for (i=0; i<masksize/8; i++) {
 | |
| 			char c;
 | |
| 			for (j=0; j<4; j++) {
 | |
| 				c = pmask_tmp[i*8+j];
 | |
| 				pmask_tmp[i*8+j] = pmask_tmp[(i+1)*8-j-1];
 | |
| 				pmask_tmp[(i+1)*8-j-1] = c;
 | |
| 			}
 | |
| 		}
 | |
| 		AEXT_TRACE(dev->name, "3 mask_tmp=%s, masksize=%d\n", mask_tmp, masksize);
 | |
| 
 | |
| 		// translate 8bits to 1byte
 | |
| 		j = 0; v = 0;
 | |
| 		pmask_tmp = &mask_tmp[2];
 | |
| 		strcpy(mask, "0x");
 | |
| 		for (i=0; i<masksize; i++) {
 | |
| 			v = (v<<1) | (pmask_tmp[i]=='1');
 | |
| 			if (((i+1)%4) == 0) {
 | |
| 				if (v < 10)
 | |
| 					mask[j+2] = v + '0';
 | |
| 				else
 | |
| 					mask[j+2] = (v-10) + 'a';
 | |
| 				j++;
 | |
| 				v = 0;
 | |
| 			}
 | |
| 		}
 | |
| 		mask[j+2] = '\0';
 | |
| 		masksize = j/2;
 | |
| 		AEXT_TRACE(dev->name, "4 mask=%s, masksize=%d\n", mask, masksize);
 | |
| 
 | |
| 		patternsize = (strlen(pattern)-2)/2;
 | |
| 		buf_len = sizeof(wl_wowl_pattern2_t) + patternsize + masksize;
 | |
| 		wowl_pattern2 = kmalloc(buf_len, GFP_KERNEL);
 | |
| 		if (wowl_pattern2 == NULL) {
 | |
| 			AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", buf_len);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		memset(wowl_pattern2, 0, sizeof(wl_wowl_pattern2_t));
 | |
| 
 | |
| 		strncpy(wowl_pattern2->cmd, add, sizeof(add));
 | |
| 		wowl_pattern2->wowl_pattern.type = 0;
 | |
| 		wowl_pattern2->wowl_pattern.offset = offset;
 | |
| 		mask_and_pattern = (char*)wowl_pattern2 + sizeof(wl_wowl_pattern2_t);
 | |
| 
 | |
| 		wowl_pattern2->wowl_pattern.masksize = masksize;
 | |
| 		ret = wl_pattern_atoh(mask, mask_and_pattern);
 | |
| 		if (ret == -1) {
 | |
| 			AEXT_ERROR(dev->name, "rejecting mask=%s\n", mask);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 
 | |
| 		mask_and_pattern += wowl_pattern2->wowl_pattern.masksize;
 | |
| 		wowl_pattern2->wowl_pattern.patternoffset = sizeof(wl_wowl_pattern_t) +
 | |
| 			wowl_pattern2->wowl_pattern.masksize;
 | |
| 
 | |
| 		wowl_pattern2->wowl_pattern.patternsize = patternsize;
 | |
| 		ret = wl_pattern_atoh(pattern, mask_and_pattern);
 | |
| 		if (ret == -1) {
 | |
| 			AEXT_ERROR(dev->name, "rejecting pattern=%s\n", pattern);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 
 | |
| 		AEXT_INFO(dev->name, "%s %d %s %s\n", add, offset, mask, pattern);
 | |
| 
 | |
| 		ret = wl_ext_iovar_setbuf(dev, "wowl_pattern", (char *)wowl_pattern2,
 | |
| 			buf_len, iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 	}
 | |
| 	else {
 | |
| 		ret = wl_ext_iovar_getbuf(dev, "wowl_pattern", NULL, 0,
 | |
| 			iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		if (!ret) {
 | |
| 			list = (wl_wowl_pattern_list_t *)iovar_buf;
 | |
| 			ret = snprintf(command, total_len, "#of patterns :%d\n", list->count);
 | |
| 			ptr = (uint8 *)list->pattern;
 | |
| 			for (i=0; i<list->count; i++) {
 | |
| 				uint8 *pattern;
 | |
| 				wowl_pattern = (wl_wowl_pattern_t *)ptr;
 | |
| 				ret += snprintf(command+ret, total_len,
 | |
| 					"Pattern %d:\n"
 | |
| 					"ID         :0x%x\n"
 | |
| 					"Offset     :%d\n"
 | |
| 					"Masksize   :%d\n"
 | |
| 					"Mask       :0x",
 | |
| 					i+1, (uint32)wowl_pattern->id, wowl_pattern->offset,
 | |
| 					wowl_pattern->masksize);
 | |
| 				pattern = ((uint8 *)wowl_pattern + sizeof(wl_wowl_pattern_t));
 | |
| 				for (j = 0; j < wowl_pattern->masksize; j++) {
 | |
| 					ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
 | |
| 				}
 | |
| 				ret += snprintf(command+ret, total_len, "\n");
 | |
| 				ret += snprintf(command+ret, total_len,
 | |
| 					"PatternSize:%d\n"
 | |
| 					"Pattern    :0x",
 | |
| 					wowl_pattern->patternsize);
 | |
| 
 | |
| 				pattern = ((uint8*)wowl_pattern + wowl_pattern->patternoffset);
 | |
| 				for (j=0; j<wowl_pattern->patternsize; j++)
 | |
| 					ret += snprintf(command+ret, total_len, "%02x", pattern[j]);
 | |
| 				ret += snprintf(command+ret, total_len, "\n");
 | |
| 				ptr += (wowl_pattern->masksize + wowl_pattern->patternsize +
 | |
| 				        sizeof(wl_wowl_pattern_t));
 | |
| 			}
 | |
| 
 | |
| 			AEXT_INFO(dev->name, "%s\n", command);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (wowl_pattern2)
 | |
| 		kfree(wowl_pattern2);
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_wowl_wakeind(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	wl_wowl_wakeind_t *wake = NULL;
 | |
| 	int ret = -1;
 | |
| 	char clr[6]="\0";
 | |
| 
 | |
| 	if (data) {
 | |
| 		sscanf(data, "%s", clr);
 | |
| 		if (!strcmp(clr, "clear")) {
 | |
| 			AEXT_INFO(dev->name, "wowl_wakeind clear\n");
 | |
| 			ret = wl_ext_iovar_setbuf(dev, "wowl_wakeind", clr, sizeof(clr),
 | |
| 				iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		} else {
 | |
| 			AEXT_ERROR(dev->name, "first arg should be clear\n");
 | |
| 		}
 | |
| 	} else {
 | |
| 		ret = wl_ext_iovar_getbuf(dev, "wowl_wakeind", NULL, 0,
 | |
| 			iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 		if (!ret) {
 | |
| 			wake = (wl_wowl_wakeind_t *) iovar_buf;
 | |
| 			ret = snprintf(command, total_len, "wakeind=0x%x", wake->ucode_wakeind);
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_MAGIC)
 | |
| 				ret += snprintf(command+ret, total_len, " (MAGIC packet)");
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_NET)
 | |
| 				ret += snprintf(command+ret, total_len, " (Netpattern)");
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_DIS)
 | |
| 				ret += snprintf(command+ret, total_len, " (Disassoc/Deauth)");
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_BCN)
 | |
| 				ret += snprintf(command+ret, total_len, " (Loss of beacon)");
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_TIME)
 | |
| 				ret += snprintf(command+ret, total_len, " (TCPKA timeout)");
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_TCPKEEP_DATA)
 | |
| 				ret += snprintf(command+ret, total_len, " (TCPKA data)");
 | |
| 			if (wake->ucode_wakeind & WL_WOWL_TCPFIN)
 | |
| 				ret += snprintf(command+ret, total_len, " (TCP FIN)");
 | |
| 			AEXT_INFO(dev->name, "%s\n", command);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| #endif /* WL_EXT_WOWL */
 | |
| 
 | |
| #ifdef WL_GPIO_NOTIFY
 | |
| typedef struct notify_payload {
 | |
| 	int index;
 | |
| 	int len;
 | |
| 	char payload[128];
 | |
| } notify_payload_t;
 | |
| 
 | |
| static int
 | |
| wl_ext_gpio_notify(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len)
 | |
| {
 | |
| 	s8 iovar_buf[WLC_IOCTL_SMLEN];
 | |
| 	notify_payload_t notify, *pnotify = NULL;
 | |
| 	int i, ret = 0, bytes_written = 0;
 | |
| 	char frame_str[WLC_IOCTL_SMLEN+3];
 | |
| 
 | |
| 	if (data) {
 | |
| 		memset(¬ify, 0, sizeof(notify));
 | |
| 		memset(frame_str, 0, sizeof(frame_str));
 | |
| 		sscanf(data, "%d %s", ¬ify.index, frame_str);
 | |
| 
 | |
| 		if (notify.index < 0)
 | |
| 			notify.index = 0;
 | |
| 
 | |
| 		if (strlen(frame_str)) {
 | |
| 			notify.len = wl_pattern_atoh(frame_str, notify.payload);
 | |
| 			if (notify.len == -1) {
 | |
| 				AEXT_ERROR(dev->name, "rejecting pattern=%s\n", frame_str);
 | |
| 				goto exit;
 | |
| 			}
 | |
| 			AEXT_INFO(dev->name, "index=%d, len=%d\n", notify.index, notify.len);
 | |
| 			if (android_msg_level & ANDROID_INFO_LEVEL)
 | |
| 				prhex("payload", (uchar *)notify.payload, notify.len);
 | |
| 			ret = wl_ext_iovar_setbuf(dev, "bcol_gpio_noti", (char *)¬ify,
 | |
| 				sizeof(notify), iovar_buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		} else {
 | |
| 			AEXT_INFO(dev->name, "index=%d\n", notify.index);
 | |
| 			ret = wl_ext_iovar_getbuf(dev, "bcol_gpio_noti", ¬ify.index,
 | |
| 				sizeof(notify.index), iovar_buf, sizeof(iovar_buf), NULL);
 | |
| 			if (!ret) {
 | |
| 				pnotify = (notify_payload_t *)iovar_buf;
 | |
| 				bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 					"Id            :%d\n"
 | |
| 					"Packet        :0x",
 | |
| 					pnotify->index);
 | |
| 				for (i=0; i<pnotify->len; i++) {
 | |
| 					bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 						"%02x", pnotify->payload[i]);
 | |
| 				}
 | |
| 				AEXT_TRACE(dev->name, "command result is\n%s\n", command);
 | |
| 				ret = bytes_written;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	return ret;
 | |
| }
 | |
| #endif /* WL_GPIO_NOTIFY */
 | |
| 
 | |
| #ifdef CSI_SUPPORT
 | |
| typedef struct csi_config {
 | |
| 	/* Peer device mac address. */
 | |
| 	struct ether_addr addr;
 | |
| 	/* BW to be used in the measurements. This needs to be supported both by the */
 | |
| 	/* device itself and the peer. */
 | |
| 	uint32 bw;
 | |
| 	/* Time interval between measurements (units: 1 ms). */
 | |
| 	uint32 period;
 | |
| 	/* CSI method */
 | |
| 	uint32 method;
 | |
| } csi_config_t;
 | |
| 
 | |
| typedef struct csi_list {
 | |
| 	uint32 cnt;
 | |
| 	csi_config_t configs[1];
 | |
| } csi_list_t;
 | |
| 
 | |
| static int
 | |
| wl_ether_atoe(const char *a, struct ether_addr *n)
 | |
| {
 | |
| 	char *c = NULL;
 | |
| 	int i = 0;
 | |
| 
 | |
| 	memset(n, 0, ETHER_ADDR_LEN);
 | |
| 	for (;;) {
 | |
| 		n->octet[i++] = (uint8)strtoul(a, &c, 16);
 | |
| 		if (!*c++ || i == ETHER_ADDR_LEN)
 | |
| 			break;
 | |
| 		a = c;
 | |
| 	}
 | |
| 	return (i == ETHER_ADDR_LEN);
 | |
| }
 | |
| 
 | |
| static int
 | |
| wl_ext_csi(struct net_device *dev, char *data, char *command, int total_len)
 | |
| {
 | |
| 	csi_config_t csi, *csip;
 | |
| 	csi_list_t *csi_list;
 | |
| 	int ret = -1, period=-1, i;
 | |
| 	char mac[32], *buf = NULL;
 | |
| 	struct ether_addr ea;
 | |
| 	int bytes_written = 0;
 | |
| 
 | |
| 	buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
 | |
| 	if (buf == NULL) {
 | |
| 		AEXT_ERROR(dev->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
 | |
| 		goto exit;
 | |
| 	}
 | |
| 	memset(buf, 0, WLC_IOCTL_SMLEN);
 | |
| 
 | |
| 	if (data) {
 | |
| 		sscanf(data, "%s %d", mac, &period);
 | |
| 		ret = wl_ether_atoe(mac, &ea);
 | |
| 		if (!ret) {
 | |
| 			AEXT_ERROR(dev->name, "rejecting mac=%s, ret=%d\n", mac, ret);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		AEXT_TRACE(dev->name, "mac=%pM, period=%d", &ea, period);
 | |
| 		if (period > 0) {
 | |
| 			memset(&csi, 0, sizeof(csi_config_t));
 | |
| 			bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
 | |
| 			csi.period = period;
 | |
| 			ret = wl_ext_iovar_setbuf(dev, "csi", (char *)&csi, sizeof(csi),
 | |
| 				buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		} else if (period == 0) {
 | |
| 			memset(&csi, 0, sizeof(csi_config_t));
 | |
| 			bcopy(&ea, &csi.addr, ETHER_ADDR_LEN);
 | |
| 			ret = wl_ext_iovar_setbuf(dev, "csi_del", (char *)&csi, sizeof(csi),
 | |
| 				buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		} else {
 | |
| 			ret = wl_ext_iovar_getbuf(dev, "csi", &ea, ETHER_ADDR_LEN, buf,
 | |
| 				WLC_IOCTL_SMLEN, NULL);
 | |
| 			if (!ret) {
 | |
| 				csip = (csi_config_t *) buf;
 | |
| 					/* Dump all lists */
 | |
| 				bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 					"Mac    :%pM\n"
 | |
| 					"Period :%d\n"
 | |
| 					"BW     :%d\n"
 | |
| 					"Method :%d\n",
 | |
| 					&csip->addr, csip->period, csip->bw, csip->method);
 | |
| 				AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 				ret = bytes_written;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else {
 | |
| 		ret = wl_ext_iovar_getbuf(dev, "csi_list", NULL, 0, buf, WLC_IOCTL_SMLEN, NULL);
 | |
| 		if (!ret) {
 | |
| 			csi_list = (csi_list_t *)buf;
 | |
| 			bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 				"Total number :%d\n", csi_list->cnt);
 | |
| 			for (i=0; i<csi_list->cnt; i++) {
 | |
| 				csip = &csi_list->configs[i];
 | |
| 				bytes_written += snprintf(command+bytes_written, total_len,
 | |
| 					"Idx    :%d\n"
 | |
| 					"Mac    :%pM\n"
 | |
| 					"Period :%d\n"
 | |
| 					"BW     :%d\n"
 | |
| 					"Method :%d\n\n",
 | |
| 					i+1, &csip->addr, csip->period, csip->bw, csip->method);
 | |
| 			}
 | |
| 			AEXT_TRACE(dev->name, "command result is %s\n", command);
 | |
| 			ret = bytes_written;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	if (buf)
 | |
| 		kfree(buf);
 | |
| 	return ret;
 | |
| }
 | |
| #endif /* CSI_SUPPORT */
 | |
| 
 | |
| typedef int (wl_ext_tpl_parse_t)(struct net_device *dev, char *data, char *command,
 | |
| 	int total_len);
 | |
| 
 | |
| typedef struct wl_ext_iovar_tpl_t {
 | |
| 	int get;
 | |
| 	int set;
 | |
| 	char *name;
 | |
| 	wl_ext_tpl_parse_t *parse;
 | |
| } wl_ext_iovar_tpl_t;
 | |
| 
 | |
| const wl_ext_iovar_tpl_t wl_ext_iovar_tpl_list[] = {
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"event_msg",	wl_ext_event_msg},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"gtk_key_info",	wl_ext_gtk_key_info},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"recal",		wl_ext_recal},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"rsdb_mode",	wl_ext_rsdb_mode},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"mkeep_alive",	wl_ext_mkeep_alive},
 | |
| #ifdef PKT_FILTER_SUPPORT
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"pkt_filter_add",		wl_ext_pkt_filter_add},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"pkt_filter_delete",	wl_ext_pkt_filter_delete},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"pkt_filter_enable",	wl_ext_pkt_filter_enable},
 | |
| #endif /* PKT_FILTER_SUPPORT */
 | |
| #if defined(WL_EXT_IAPSTA) && defined(WLMESH)
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"mesh_peer_status",	wl_ext_mesh_peer_status},
 | |
| #endif /* WL_EXT_IAPSTA && WLMESH */
 | |
| #ifdef SENDPROB
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"send_probreq",		wl_ext_send_probreq},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"send_probresp",	wl_ext_send_probresp},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"recv_probreq",		wl_ext_recv_probreq},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"recv_probresp",	wl_ext_recv_probresp},
 | |
| #endif /* SENDPROB */
 | |
| #ifdef WL_EXT_TCPKA
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"tcpka_conn_add",		wl_ext_tcpka_conn_add},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"tcpka_conn_enable",	wl_ext_tcpka_conn_enable},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"tcpka_conn_sess_info",	wl_ext_tcpka_conn_info},
 | |
| #endif /* WL_EXT_TCPKA */
 | |
| #ifdef WL_EXT_WOWL
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"wowl_pattern",		wl_ext_wowl_pattern},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"wowl_wakeind",		wl_ext_wowl_wakeind},
 | |
| #endif /* WL_EXT_WOWL */
 | |
| #ifdef IDHCP
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"dhcpc_dump",		wl_ext_dhcpc_dump},
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"dhcpc_param",		wl_ext_dhcpc_param},
 | |
| #endif /* IDHCP */
 | |
| #ifdef WL_GPIO_NOTIFY
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"bcol_gpio_noti",	wl_ext_gpio_notify},
 | |
| #endif /* WL_GPIO_NOTIFY */
 | |
| #ifdef CSI_SUPPORT
 | |
| 	{WLC_GET_VAR,	WLC_SET_VAR,	"csi",				wl_ext_csi},
 | |
| #endif /* CSI_SUPPORT */
 | |
| };
 | |
| 
 | |
| /*
 | |
| Ex: dhd_priv wl [cmd] [val]
 | |
|   dhd_priv wl 85
 | |
|   dhd_priv wl 86 1
 | |
|   dhd_priv wl mpc
 | |
|   dhd_priv wl mpc 1
 | |
| */
 | |
| static int
 | |
| wl_ext_wl_iovar(struct net_device *dev, char *command, int total_len)
 | |
| {
 | |
| 	int cmd, val, ret = -1, i;
 | |
| 	char name[32], *pch, *pick_tmp, *data;
 | |
| 	int bytes_written=-1;
 | |
| 	const wl_ext_iovar_tpl_t *tpl = wl_ext_iovar_tpl_list;
 | |
| 	int tpl_count = ARRAY_SIZE(wl_ext_iovar_tpl_list);
 | |
| 
 | |
| 	AEXT_TRACE(dev->name, "cmd %s\n", command);
 | |
| 	pick_tmp = command;
 | |
| 
 | |
| 	pch = bcmstrtok(&pick_tmp, " ", 0); // pick wl
 | |
| 	if (!pch || strncmp(pch, "wl", 2))
 | |
| 		goto exit;
 | |
| 
 | |
| 	pch = bcmstrtok(&pick_tmp, " ", 0); // pick cmd
 | |
| 	if (!pch)
 | |
| 		goto exit;
 | |
| 
 | |
| 	memset(name, 0 , sizeof (name));
 | |
| 	cmd = (int)simple_strtol(pch, NULL, 0);
 | |
| 	if (cmd == 0) {
 | |
| 		strcpy(name, pch);
 | |
| 	}
 | |
| 	data = bcmstrtok(&pick_tmp, "", 0); // pick data
 | |
| 	if (data && cmd == 0) {
 | |
| 		cmd = WLC_SET_VAR;
 | |
| 	} else if (cmd == 0) {
 | |
| 		cmd = WLC_GET_VAR;
 | |
| 	}
 | |
| 
 | |
| 	/* look for a matching code in the table */
 | |
| 	for (i = 0; i < tpl_count; i++, tpl++) {
 | |
| 		if ((tpl->get == cmd || tpl->set == cmd) && !strcmp(tpl->name, name))
 | |
| 			break;
 | |
| 	}
 | |
| 	if (i < tpl_count && tpl->parse) {
 | |
| 		ret = tpl->parse(dev, data, command, total_len);
 | |
| 	} else {
 | |
| 		if (cmd == WLC_SET_VAR) {
 | |
| 			val = (int)simple_strtol(data, NULL, 0);
 | |
| 			AEXT_INFO(dev->name, "set %s %d\n", name, val);
 | |
| 			ret = wl_ext_iovar_setint(dev, name, val);
 | |
| 		} else if (cmd == WLC_GET_VAR) {
 | |
| 			AEXT_INFO(dev->name, "get %s\n", name);
 | |
| 			ret = wl_ext_iovar_getint(dev, name, &val);
 | |
| 			if (!ret) {
 | |
| 				bytes_written = snprintf(command, total_len, "%d", val);
 | |
| 				AEXT_INFO(dev->name, "command result is %s\n", command);
 | |
| 				ret = bytes_written;
 | |
| 			}
 | |
| 		} else if (data) {
 | |
| 			val = (int)simple_strtol(data, NULL, 0);
 | |
| 			AEXT_INFO(dev->name, "set %d %d\n", cmd, val);
 | |
| 			ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), TRUE);
 | |
| 		} else {
 | |
| 			AEXT_INFO(dev->name, "get %d\n", cmd);
 | |
| 			ret = wl_ext_ioctl(dev, cmd, &val, sizeof(val), FALSE);
 | |
| 			if (!ret) {
 | |
| 				bytes_written = snprintf(command, total_len, "%d", val);
 | |
| 				AEXT_INFO(dev->name, "command result is %s\n", command);
 | |
| 				ret = bytes_written;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_android_ext_priv_cmd(struct net_device *net, char *command,
 | |
| 	int total_len, int *bytes_written)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	if (strnicmp(command, CMD_CHANNELS, strlen(CMD_CHANNELS)) == 0) {
 | |
| 		*bytes_written = wl_ext_channels(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_CHANNEL, strlen(CMD_CHANNEL)) == 0) {
 | |
| 		*bytes_written = wl_ext_channel(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_ROAM_TRIGGER, strlen(CMD_ROAM_TRIGGER)) == 0) {
 | |
| 		*bytes_written = wl_ext_roam_trigger(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_PM, strlen(CMD_PM)) == 0) {
 | |
| 		*bytes_written = wl_ext_pm(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_MONITOR, strlen(CMD_MONITOR)) == 0) {
 | |
| 		*bytes_written = wl_ext_monitor(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_SET_SUSPEND_BCN_LI_DTIM, strlen(CMD_SET_SUSPEND_BCN_LI_DTIM)) == 0) {
 | |
| 		int bcn_li_dtim;
 | |
| 		bcn_li_dtim = (int)simple_strtol((command + strlen(CMD_SET_SUSPEND_BCN_LI_DTIM) + 1), NULL, 10);
 | |
| 		*bytes_written = net_os_set_suspend_bcn_li_dtim(net, bcn_li_dtim);
 | |
| 	}
 | |
| #ifdef WL_EXT_IAPSTA
 | |
| 	else if (strnicmp(command, CMD_IAPSTA_INIT, strlen(CMD_IAPSTA_INIT)) == 0 ||
 | |
| 			strnicmp(command, CMD_ISAM_INIT, strlen(CMD_ISAM_INIT)) == 0) {
 | |
| 		*bytes_written = wl_ext_isam_init(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_IAPSTA_CONFIG, strlen(CMD_IAPSTA_CONFIG)) == 0 ||
 | |
| 			strnicmp(command, CMD_ISAM_CONFIG, strlen(CMD_ISAM_CONFIG)) == 0) {
 | |
| 		*bytes_written = wl_ext_iapsta_config(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_IAPSTA_ENABLE, strlen(CMD_IAPSTA_ENABLE)) == 0 ||
 | |
| 			strnicmp(command, CMD_ISAM_ENABLE, strlen(CMD_ISAM_ENABLE)) == 0) {
 | |
| 		*bytes_written = wl_ext_iapsta_enable(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_IAPSTA_DISABLE, strlen(CMD_IAPSTA_DISABLE)) == 0 ||
 | |
| 			strnicmp(command, CMD_ISAM_DISABLE, strlen(CMD_ISAM_DISABLE)) == 0) {
 | |
| 		*bytes_written = wl_ext_iapsta_disable(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_ISAM_STATUS, strlen(CMD_ISAM_STATUS)) == 0) {
 | |
| 		*bytes_written = wl_ext_isam_status(net, command, total_len);
 | |
| 	}
 | |
| 	else if (strnicmp(command, CMD_ISAM_PARAM, strlen(CMD_ISAM_PARAM)) == 0) {
 | |
| 		*bytes_written = wl_ext_isam_param(net, command, total_len);
 | |
| 	}
 | |
| #if defined(WLMESH) && defined(WL_ESCAN)
 | |
| 	else if (strnicmp(command, CMD_ISAM_PEER_PATH, strlen(CMD_ISAM_PEER_PATH)) == 0) {
 | |
| 		*bytes_written = wl_ext_isam_peer_path(net, command, total_len);
 | |
| 	}
 | |
| #endif /* WLMESH && WL_ESCAN */
 | |
| #endif /* WL_EXT_IAPSTA */
 | |
| #ifdef WL_CFG80211
 | |
| 	else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
 | |
| 		*bytes_written = wl_cfg80211_autochannel(net, command, total_len);
 | |
| 	}
 | |
| #endif /* WL_CFG80211 */
 | |
| #if defined(WL_WIRELESS_EXT) && defined(WL_ESCAN)
 | |
| 	else if (strnicmp(command, CMD_AUTOCHANNEL, strlen(CMD_AUTOCHANNEL)) == 0) {
 | |
| 		*bytes_written = wl_iw_autochannel(net, command, total_len);
 | |
| 	}
 | |
| #endif /* WL_WIRELESS_EXT && WL_ESCAN */
 | |
| 	else if (strnicmp(command, CMD_WL, strlen(CMD_WL)) == 0) {
 | |
| 		*bytes_written = wl_ext_wl_iovar(net, command, total_len);
 | |
| 	}
 | |
| 	else
 | |
| 		ret = -1;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| #if defined(WL_CFG80211) || defined(WL_ESCAN)
 | |
| int
 | |
| wl_ext_get_distance(struct net_device *net, u32 band)
 | |
| {
 | |
| 	u32 bw = WL_CHANSPEC_BW_20;
 | |
| 	s32 bw_cap = 0, distance = 0;
 | |
| 	struct {
 | |
| 		u32 band;
 | |
| 		u32 bw_cap;
 | |
| 	} param = {0, 0};
 | |
| 	char buf[WLC_IOCTL_SMLEN]="\0";
 | |
| 	s32 err = BCME_OK;
 | |
| 
 | |
| 	param.band = band;
 | |
| 	err = wl_ext_iovar_getbuf(net, "bw_cap", ¶m, sizeof(param), buf,
 | |
| 		sizeof(buf), NULL);
 | |
| 	if (err) {
 | |
| 		if (err != BCME_UNSUPPORTED) {
 | |
| 			AEXT_ERROR(net->name, "bw_cap failed, %d\n", err);
 | |
| 			return err;
 | |
| 		} else {
 | |
| 			err = wl_ext_iovar_getint(net, "mimo_bw_cap", &bw_cap);
 | |
| 			if (bw_cap != WLC_N_BW_20ALL)
 | |
| 				bw = WL_CHANSPEC_BW_40;
 | |
| 		}
 | |
| 	} else {
 | |
| 		if (WL_BW_CAP_80MHZ(buf[0]))
 | |
| 			bw = WL_CHANSPEC_BW_80;
 | |
| 		else if (WL_BW_CAP_40MHZ(buf[0]))
 | |
| 			bw = WL_CHANSPEC_BW_40;
 | |
| 		else
 | |
| 			bw = WL_CHANSPEC_BW_20;
 | |
| 	}
 | |
| 
 | |
| 	if (bw == WL_CHANSPEC_BW_20)
 | |
| 		distance = 2;
 | |
| 	else if (bw == WL_CHANSPEC_BW_40)
 | |
| 		distance = 4;
 | |
| 	else if (bw == WL_CHANSPEC_BW_80)
 | |
| 		distance = 8;
 | |
| 	else
 | |
| 		distance = 16;
 | |
| 	AEXT_INFO(net->name, "bw=0x%x, distance=%d\n", bw, distance);
 | |
| 
 | |
| 	return distance;
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_ext_get_best_channel(struct net_device *net,
 | |
| #if defined(BSSCACHE)
 | |
| 	wl_bss_cache_ctrl_t *bss_cache_ctrl,
 | |
| #else
 | |
| 	struct wl_scan_results *bss_list,
 | |
| #endif /* BSSCACHE */
 | |
| 	int ioctl_ver, int *best_2g_ch, int *best_5g_ch
 | |
| )
 | |
| {
 | |
| 	struct wl_bss_info *bi = NULL;	/* must be initialized */
 | |
| 	s32 i, j;
 | |
| #if defined(BSSCACHE)
 | |
| 	wl_bss_cache_t *node;
 | |
| #endif /* BSSCACHE */
 | |
| 	int b_band[CH_MAX_2G_CHANNEL]={0}, a_band1[4]={0}, a_band4[5]={0};
 | |
| 	s32 cen_ch, distance, distance_2g, distance_5g, ch, min_ap=999;
 | |
| 	u8 valid_chan_list[sizeof(u32)*(WL_NUMCHANNELS + 1)];
 | |
| 	wl_uint32_list_t *list;
 | |
| 	int ret;
 | |
| 	chanspec_t chanspec;
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(net);
 | |
| 
 | |
| 	memset(b_band, -1, sizeof(b_band));
 | |
| 	memset(a_band1, -1, sizeof(a_band1));
 | |
| 	memset(a_band4, -1, sizeof(a_band4));
 | |
| 
 | |
| 	memset(valid_chan_list, 0, sizeof(valid_chan_list));
 | |
| 	list = (wl_uint32_list_t *)(void *) valid_chan_list;
 | |
| 	list->count = htod32(WL_NUMCHANNELS);
 | |
| 	ret = wl_ext_ioctl(net, WLC_GET_VALID_CHANNELS, &valid_chan_list,
 | |
| 		sizeof(valid_chan_list), 0);
 | |
| 	if (ret<0) {
 | |
| 		AEXT_ERROR(net->name, "get channels failed with %d\n", ret);
 | |
| 		return 0;
 | |
| 	} else {
 | |
| 		for (i = 0; i < dtoh32(list->count); i++) {
 | |
| 			ch = dtoh32(list->element[i]);
 | |
| 			if (!dhd_conf_match_channel(dhd, ch))
 | |
| 				continue;
 | |
| 			if (ch < CH_MAX_2G_CHANNEL)
 | |
| 				b_band[ch-1] = 0;
 | |
| 			else if (ch <= 48)
 | |
| 				a_band1[(ch-36)/4] = 0;
 | |
| 			else if (ch >= 149 && ch <= 161)
 | |
| 				a_band4[(ch-149)/4] = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	distance_2g = wl_ext_get_distance(net, WLC_BAND_2G);
 | |
| 	distance_5g = wl_ext_get_distance(net, WLC_BAND_5G);
 | |
| 
 | |
| #if defined(BSSCACHE)
 | |
| 	node = bss_cache_ctrl->m_cache_head;
 | |
| 	for (i=0; node && i<256; i++)
 | |
| #else
 | |
| 	for (i=0; i < bss_list->count; i++)
 | |
| #endif /* BSSCACHE */
 | |
| 	{
 | |
| #if defined(BSSCACHE)
 | |
| 		bi = node->results.bss_info;
 | |
| #else
 | |
| 		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : bss_list->bss_info;
 | |
| #endif /* BSSCACHE */
 | |
| 		chanspec = wl_ext_chspec_driver_to_host(ioctl_ver, bi->chanspec);
 | |
| 		cen_ch = CHSPEC_CHANNEL(bi->chanspec);
 | |
| 		distance = 0;
 | |
| 		if (CHSPEC_IS20(chanspec))
 | |
| 			distance += 2;
 | |
| 		else if (CHSPEC_IS40(chanspec))
 | |
| 			distance += 4;
 | |
| 		else if (CHSPEC_IS80(chanspec))
 | |
| 			distance += 8;
 | |
| 		else
 | |
| 			distance += 16;
 | |
| 
 | |
| 		if (CHSPEC_IS2G(chanspec)) {
 | |
| 			distance += distance_2g;
 | |
| 			for (j=0; j<ARRAYSIZE(b_band); j++) {
 | |
| 				if (b_band[j] >= 0 && abs(cen_ch-(1+j)) <= distance)
 | |
| 					b_band[j] += 1;
 | |
| 			}
 | |
| 		} else {
 | |
| 			distance += distance_5g;
 | |
| 			if (cen_ch <= 48) {
 | |
| 				for (j=0; j<ARRAYSIZE(a_band1); j++) {
 | |
| 					if (a_band1[j] >= 0 && abs(cen_ch-(36+j*4)) <= distance)
 | |
| 						a_band1[j] += 1;
 | |
| 				}
 | |
| 			} else if (cen_ch >= 149) {
 | |
| 				for (j=0; j<ARRAYSIZE(a_band4); j++) {
 | |
| 					if (a_band4[j] >= 0 && abs(cen_ch-(149+j*4)) <= distance)
 | |
| 						a_band4[j] += 1;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| #if defined(BSSCACHE)
 | |
| 		node = node->next;
 | |
| #endif /* BSSCACHE */
 | |
| 	}
 | |
| 
 | |
| 	*best_2g_ch = 0;
 | |
| 	min_ap = 999;
 | |
| 	for (i=0; i<CH_MAX_2G_CHANNEL; i++) {
 | |
| 		if(b_band[i] < min_ap && b_band[i] >= 0) {
 | |
| 			min_ap = b_band[i];
 | |
| 			*best_2g_ch = i+1;
 | |
| 		}
 | |
| 	}
 | |
| 	*best_5g_ch = 0;
 | |
| 	min_ap = 999;
 | |
| 	for (i=0; i<ARRAYSIZE(a_band1); i++) {
 | |
| 		if(a_band1[i] < min_ap && a_band1[i] >= 0) {
 | |
| 			min_ap = a_band1[i];
 | |
| 			*best_5g_ch = i*4 + 36;
 | |
| 		}
 | |
| 	}
 | |
| 	for (i=0; i<ARRAYSIZE(a_band4); i++) {
 | |
| 		if(a_band4[i] < min_ap && a_band4[i] >= 0) {
 | |
| 			min_ap = a_band4[i];
 | |
| 			*best_5g_ch = i*4 + 149;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (android_msg_level & ANDROID_INFO_LEVEL) {
 | |
| 		struct bcmstrbuf strbuf;
 | |
| 		char *tmp_buf = NULL;
 | |
| 		tmp_buf = kmalloc(WLC_IOCTL_SMLEN, GFP_KERNEL);
 | |
| 		if (tmp_buf == NULL) {
 | |
| 			AEXT_ERROR(net->name, "Failed to allocate buffer of %d bytes\n", WLC_IOCTL_SMLEN);
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		bcm_binit(&strbuf, tmp_buf, WLC_IOCTL_SMLEN);
 | |
| 		for (j=0; j<ARRAYSIZE(b_band); j++)
 | |
| 			bcm_bprintf(&strbuf, "%d/%d, ", b_band[j], 1+j);
 | |
| 		bcm_bprintf(&strbuf, "\n");
 | |
| 		for (j=0; j<ARRAYSIZE(a_band1); j++)
 | |
| 			bcm_bprintf(&strbuf, "%d/%d, ", a_band1[j], 36+j*4);
 | |
| 		bcm_bprintf(&strbuf, "\n");
 | |
| 		for (j=0; j<ARRAYSIZE(a_band4); j++)
 | |
| 			bcm_bprintf(&strbuf, "%d/%d, ", a_band4[j], 149+j*4);
 | |
| 		bcm_bprintf(&strbuf, "\n");
 | |
| 		bcm_bprintf(&strbuf, "best_2g_ch=%d, best_5g_ch=%d\n",
 | |
| 			*best_2g_ch, *best_5g_ch);
 | |
| 		AEXT_INFO(net->name, "\n%s", strbuf.origbuf);
 | |
| 		if (tmp_buf) {
 | |
| 			kfree(tmp_buf);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| exit:
 | |
| 	return 0;
 | |
| }
 | |
| #endif /* WL_CFG80211 || WL_ESCAN */
 | |
| 
 | |
| #ifdef WL_CFG80211
 | |
| #define APCS_MAX_RETRY		10
 | |
| static int
 | |
| wl_ext_fw_apcs(struct net_device *dev, uint32 band)
 | |
| {
 | |
| 	int channel = 0, chosen = 0, retry = 0, ret = 0, spect = 0;
 | |
| 	u8 *reqbuf = NULL;
 | |
| 	uint32 buf_size;
 | |
| 
 | |
| 	ret = wldev_ioctl_get(dev, WLC_GET_SPECT_MANAGMENT, &spect, sizeof(spect));
 | |
| 	if (ret) {
 | |
| 		AEXT_ERROR(dev->name, "ACS: error getting the spect, ret=%d\n", ret);
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	if (spect > 0) {
 | |
| 		ret = wl_cfg80211_set_spect(dev, 0);
 | |
| 		if (ret < 0) {
 | |
| 			AEXT_ERROR(dev->name, "ACS: error while setting spect, ret=%d\n", ret);
 | |
| 			goto done;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	reqbuf = kmalloc(CHANSPEC_BUF_SIZE, GFP_KERNEL);
 | |
| 	if (reqbuf == NULL) {
 | |
| 		AEXT_ERROR(dev->name, "failed to allocate chanspec buffer\n");
 | |
| 		goto done;
 | |
| 	}
 | |
| 	memset(reqbuf, 0, CHANSPEC_BUF_SIZE);
 | |
| 
 | |
| 	if (band == WLC_BAND_AUTO) {
 | |
| 		AEXT_INFO(dev->name, "ACS full channel scan \n");
 | |
| 		reqbuf[0] = htod32(0);
 | |
| 	} else if (band == WLC_BAND_5G) {
 | |
| 		AEXT_INFO(dev->name, "ACS 5G band scan \n");
 | |
| 		if ((ret = wl_cfg80211_get_chanspecs_5g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
 | |
| 			AEXT_ERROR(dev->name, "ACS 5g chanspec retreival failed! \n");
 | |
| 			goto done;
 | |
| 		}
 | |
| 	} else if (band == WLC_BAND_2G) {
 | |
| 		/*
 | |
| 		 * If channel argument is not provided/ argument 20 is provided,
 | |
| 		 * Restrict channel to 2GHz, 20MHz BW, No SB
 | |
| 		 */
 | |
| 		AEXT_INFO(dev->name, "ACS 2G band scan \n");
 | |
| 		if ((ret = wl_cfg80211_get_chanspecs_2g(dev, reqbuf, CHANSPEC_BUF_SIZE)) < 0) {
 | |
| 			AEXT_ERROR(dev->name, "ACS 2g chanspec retreival failed! \n");
 | |
| 			goto done;
 | |
| 		}
 | |
| 	} else {
 | |
| 		AEXT_ERROR(dev->name, "ACS: No band chosen\n");
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	buf_size = (band == WLC_BAND_AUTO) ? sizeof(int) : CHANSPEC_BUF_SIZE;
 | |
| 	ret = wldev_ioctl_set(dev, WLC_START_CHANNEL_SEL, (void *)reqbuf,
 | |
| 		buf_size);
 | |
| 	if (ret < 0) {
 | |
| 		AEXT_ERROR(dev->name, "can't start auto channel scan, err = %d\n", ret);
 | |
| 		channel = 0;
 | |
| 		goto done;
 | |
| 	}
 | |
| 
 | |
| 	/* Wait for auto channel selection, max 3000 ms */
 | |
| 	if ((band == WLC_BAND_2G) || (band == WLC_BAND_5G)) {
 | |
| 		OSL_SLEEP(500);
 | |
| 	} else {
 | |
| 		/*
 | |
| 		 * Full channel scan at the minimum takes 1.2secs
 | |
| 		 * even with parallel scan. max wait time: 3500ms
 | |
| 		 */
 | |
| 		OSL_SLEEP(1000);
 | |
| 	}
 | |
| 
 | |
| 	retry = APCS_MAX_RETRY;
 | |
| 	while (retry--) {
 | |
| 		ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &chosen,
 | |
| 			sizeof(chosen));
 | |
| 		if (ret < 0) {
 | |
| 			chosen = 0;
 | |
| 		} else {
 | |
| 			chosen = dtoh32(chosen);
 | |
| 		}
 | |
| 
 | |
| 		if (chosen) {
 | |
| 			int chosen_band;
 | |
| 			int apcs_band;
 | |
| #ifdef D11AC_IOTYPES
 | |
| 			if (wl_cfg80211_get_ioctl_version() == 1) {
 | |
| 				channel = LCHSPEC_CHANNEL((chanspec_t)chosen);
 | |
| 			} else {
 | |
| 				channel = CHSPEC_CHANNEL((chanspec_t)chosen);
 | |
| 			}
 | |
| #else
 | |
| 			channel = CHSPEC_CHANNEL((chanspec_t)chosen);
 | |
| #endif /* D11AC_IOTYPES */
 | |
| 			apcs_band = (band == WLC_BAND_AUTO) ? WLC_BAND_2G : band;
 | |
| 			chosen_band = (channel <= CH_MAX_2G_CHANNEL) ? WLC_BAND_2G : WLC_BAND_5G;
 | |
| 			if (apcs_band == chosen_band) {
 | |
| 				WL_MSG(dev->name, "selected channel = %d\n", channel);
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 		AEXT_INFO(dev->name, "%d tried, ret = %d, chosen = 0x%x\n",
 | |
| 			(APCS_MAX_RETRY - retry), ret, chosen);
 | |
| 		OSL_SLEEP(250);
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	if (spect > 0) {
 | |
| 		if ((ret = wl_cfg80211_set_spect(dev, spect) < 0)) {
 | |
| 			AEXT_ERROR(dev->name, "ACS: error while setting spect\n");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (reqbuf) {
 | |
| 		kfree(reqbuf);
 | |
| 	}
 | |
| 
 | |
| 	return channel;
 | |
| }
 | |
| #endif /* WL_CFG80211 */
 | |
| 
 | |
| #ifdef WL_ESCAN
 | |
| int
 | |
| wl_ext_drv_apcs(struct net_device *dev, uint32 band)
 | |
| {
 | |
| 	int ret = 0, channel = 0;
 | |
| 	struct dhd_pub *dhd = dhd_get_pub(dev);
 | |
| 	struct wl_escan_info *escan = NULL;
 | |
| 	int retry = 0, retry_max, retry_interval = 250, up = 1;
 | |
| #ifdef WL_CFG80211
 | |
| 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
 | |
| #endif /* WL_CFG80211 */
 | |
| 
 | |
| 	escan = dhd->escan;
 | |
| 	if (dhd) {
 | |
| 		retry_max = WL_ESCAN_TIMER_INTERVAL_MS/retry_interval;
 | |
| 		ret = wldev_ioctl_get(dev, WLC_GET_UP, &up, sizeof(s32));
 | |
| 		if (ret < 0 || up == 0) {
 | |
| 			ret = wldev_ioctl_set(dev, WLC_UP, &up, sizeof(s32));
 | |
| 		}
 | |
| 		retry = retry_max;
 | |
| 		while (retry--) {
 | |
| 			if (escan->escan_state == ESCAN_STATE_SCANING
 | |
| #ifdef WL_CFG80211
 | |
| 				|| wl_get_drv_status_all(cfg, SCANNING)
 | |
| #endif
 | |
| 			)
 | |
| 			{
 | |
| 				AEXT_INFO(dev->name, "Scanning %d tried, ret = %d\n",
 | |
| 					(retry_max - retry), ret);
 | |
| 			} else {
 | |
| 				escan->autochannel = 1;
 | |
| 				ret = wl_escan_set_scan(dev, dhd, NULL, 0, TRUE);
 | |
| 				if (!ret)
 | |
| 					break;
 | |
| 			}
 | |
| 			OSL_SLEEP(retry_interval);
 | |
| 		}
 | |
| 		if ((retry == 0) || (ret < 0))
 | |
| 			goto done;
 | |
| 		retry = retry_max;
 | |
| 		while (retry--) {
 | |
| 			if (escan->escan_state == ESCAN_STATE_IDLE) {
 | |
| 				if (band == WLC_BAND_5G)
 | |
| 					channel = escan->best_5g_ch;
 | |
| 				else
 | |
| 					channel = escan->best_2g_ch;
 | |
| 				WL_MSG(dev->name, "selected channel = %d\n", channel);
 | |
| 				goto done;
 | |
| 			}
 | |
| 			AEXT_INFO(dev->name, "escan_state=%d, %d tried, ret = %d\n",
 | |
| 				escan->escan_state, (retry_max - retry), ret);
 | |
| 			OSL_SLEEP(retry_interval);
 | |
| 		}
 | |
| 		if ((retry == 0) || (ret < 0))
 | |
| 			goto done;
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	if (escan)
 | |
| 		escan->autochannel = 0;
 | |
| 
 | |
| 	return channel;
 | |
| }
 | |
| #endif /* WL_ESCAN */
 | |
| 
 | |
| int
 | |
| wl_ext_autochannel(struct net_device *dev, uint acs, uint32 band)
 | |
| {
 | |
| 	int channel = 0;
 | |
| 	uint16 chan_2g, chan_5g;
 | |
| 
 | |
| 	AEXT_INFO(dev->name, "acs=0x%x, band=%d \n", acs, band);
 | |
| 
 | |
| #ifdef WL_CFG80211
 | |
| 	if (acs & ACS_FW_BIT) {
 | |
| 		int ret = 0;
 | |
| 		ret = wldev_ioctl_get(dev, WLC_GET_CHANNEL_SEL, &channel, sizeof(channel));
 | |
| 		channel = 0;
 | |
| 		if (ret != BCME_UNSUPPORTED)
 | |
| 			channel = wl_ext_fw_apcs(dev, band);
 | |
| 		if (channel)
 | |
| 			return channel;
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| #ifdef WL_ESCAN
 | |
| 	if (acs & ACS_DRV_BIT)
 | |
| 		channel = wl_ext_drv_apcs(dev, band);
 | |
| #endif /* WL_ESCAN */
 | |
| 
 | |
| 	if (channel == 0) {
 | |
| 		wl_ext_get_default_chan(dev, &chan_2g, &chan_5g, TRUE);
 | |
| 		if (band == WLC_BAND_5G) {
 | |
| 			channel = chan_5g;
 | |
| 		} else {
 | |
| 			channel = chan_2g;
 | |
| 		}
 | |
| 		AEXT_ERROR(dev->name, "ACS failed. Fall back to default channel (%d) \n", channel);
 | |
| 	}
 | |
| 
 | |
| 	return channel;
 | |
| }
 | |
| 
 | |
| #if defined(RSSIAVG)
 | |
| void
 | |
| wl_free_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, *cur, **rssi_head;
 | |
| 	int i=0;
 | |
| 
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 	node = *rssi_head;
 | |
| 
 | |
| 	for (;node;) {
 | |
| 		AEXT_INFO("wlan", "Free %d with BSSID %pM\n", i, &node->BSSID);
 | |
| 		cur = node;
 | |
| 		node = cur->next;
 | |
| 		kfree(cur);
 | |
| 		i++;
 | |
| 	}
 | |
| 	*rssi_head = NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_delete_dirty_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, *prev, **rssi_head;
 | |
| 	int i = -1, tmp = 0;
 | |
| 	struct osl_timespec now;
 | |
| 
 | |
| 	osl_do_gettimeofday(&now);
 | |
| 
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 	node = *rssi_head;
 | |
| 	prev = node;
 | |
| 	for (;node;) {
 | |
| 		i++;
 | |
| 		if (now.tv_sec > node->tv.tv_sec) {
 | |
| 			if (node == *rssi_head) {
 | |
| 				tmp = 1;
 | |
| 				*rssi_head = node->next;
 | |
| 			} else {
 | |
| 				tmp = 0;
 | |
| 				prev->next = node->next;
 | |
| 			}
 | |
| 			AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
 | |
| 			kfree(node);
 | |
| 			if (tmp == 1) {
 | |
| 				node = *rssi_head;
 | |
| 				prev = node;
 | |
| 			} else {
 | |
| 				node = prev->next;
 | |
| 			}
 | |
| 			continue;
 | |
| 		}
 | |
| 		prev = node;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_delete_disconnected_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
 | |
| 	u8 *bssid)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, *prev, **rssi_head;
 | |
| 	int i = -1, tmp = 0;
 | |
| 
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 	node = *rssi_head;
 | |
| 	prev = node;
 | |
| 	for (;node;) {
 | |
| 		i++;
 | |
| 		if (!memcmp(&node->BSSID, bssid, ETHER_ADDR_LEN)) {
 | |
| 			if (node == *rssi_head) {
 | |
| 				tmp = 1;
 | |
| 				*rssi_head = node->next;
 | |
| 			} else {
 | |
| 				tmp = 0;
 | |
| 				prev->next = node->next;
 | |
| 			}
 | |
| 			AEXT_INFO("wlan", "Del %d with BSSID %pM\n", i, &node->BSSID);
 | |
| 			kfree(node);
 | |
| 			if (tmp == 1) {
 | |
| 				node = *rssi_head;
 | |
| 				prev = node;
 | |
| 			} else {
 | |
| 				node = prev->next;
 | |
| 			}
 | |
| 			continue;
 | |
| 		}
 | |
| 		prev = node;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_reset_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, **rssi_head;
 | |
| 
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 
 | |
| 	/* reset dirty */
 | |
| 	node = *rssi_head;
 | |
| 	for (;node;) {
 | |
| 		node->dirty += 1;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int
 | |
| wl_update_connected_rssi_cache(struct net_device *net,
 | |
| 	wl_rssi_cache_ctrl_t *rssi_cache_ctrl, int *rssi_avg)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
 | |
| 	int j, k=0;
 | |
| 	int rssi, error=0;
 | |
| 	struct ether_addr bssid;
 | |
| 	struct osl_timespec now, timeout;
 | |
| 	scb_val_t scbval;
 | |
| 
 | |
| 	if (!g_wifi_on)
 | |
| 		return 0;
 | |
| 
 | |
| 	error = wldev_ioctl(net, WLC_GET_BSSID, &bssid, sizeof(bssid), 0);
 | |
| 	if (error == BCME_NOTASSOCIATED) {
 | |
| 		AEXT_INFO("wlan", "Not Associated! res:%d\n", error);
 | |
| 		return 0;
 | |
| 	}
 | |
| 	if (error) {
 | |
| 		AEXT_ERROR(net->name, "Could not get bssid (%d)\n", error);
 | |
| 	}
 | |
| 	error = wldev_get_rssi(net, &scbval);
 | |
| 	if (error) {
 | |
| 		AEXT_ERROR(net->name, "Could not get rssi (%d)\n", error);
 | |
| 		return error;
 | |
| 	}
 | |
| 	rssi = scbval.val;
 | |
| 
 | |
| 	osl_do_gettimeofday(&now);
 | |
| 	timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
 | |
| 	if (timeout.tv_sec < now.tv_sec) {
 | |
| 		/*
 | |
| 		 * Integer overflow - assume long enough timeout to be assumed
 | |
| 		 * to be infinite, i.e., the timeout would never happen.
 | |
| 		 */
 | |
| 		AEXT_TRACE(net->name,
 | |
| 			"Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
 | |
| 			RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
 | |
| 	}
 | |
| 
 | |
| 	/* update RSSI */
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 	node = *rssi_head;
 | |
| 	prev = NULL;
 | |
| 	for (;node;) {
 | |
| 		if (!memcmp(&node->BSSID, &bssid, ETHER_ADDR_LEN)) {
 | |
| 			AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%d\n", k, &bssid, rssi);
 | |
| 			for (j=0; j<RSSIAVG_LEN-1; j++)
 | |
| 				node->RSSI[j] = node->RSSI[j+1];
 | |
| 			node->RSSI[j] = rssi;
 | |
| 			node->dirty = 0;
 | |
| 			node->tv = timeout;
 | |
| 			goto exit;
 | |
| 		}
 | |
| 		prev = node;
 | |
| 		node = node->next;
 | |
| 		k++;
 | |
| 	}
 | |
| 
 | |
| 	leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
 | |
| 	if (!leaf) {
 | |
| 		AEXT_ERROR(net->name, "Memory alloc failure %d\n", (int)sizeof(wl_rssi_cache_t));
 | |
| 		return 0;
 | |
| 	}
 | |
| 	AEXT_INFO(net->name, "Add %d with cached BSSID %pM, RSSI=%3d in the leaf\n",
 | |
| 		k, &bssid, rssi);
 | |
| 
 | |
| 	leaf->next = NULL;
 | |
| 	leaf->dirty = 0;
 | |
| 	leaf->tv = timeout;
 | |
| 	memcpy(&leaf->BSSID, &bssid, ETHER_ADDR_LEN);
 | |
| 	for (j=0; j<RSSIAVG_LEN; j++)
 | |
| 		leaf->RSSI[j] = rssi;
 | |
| 
 | |
| 	if (!prev)
 | |
| 		*rssi_head = leaf;
 | |
| 	else
 | |
| 		prev->next = leaf;
 | |
| 
 | |
| exit:
 | |
| 	*rssi_avg = (int)wl_get_avg_rssi(rssi_cache_ctrl, &bssid);
 | |
| 
 | |
| 	return error;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_update_rssi_cache(wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
 | |
| 	wl_scan_results_t *ss_list)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, *prev, *leaf, **rssi_head;
 | |
| 	wl_bss_info_t *bi = NULL;
 | |
| 	int i, j, k;
 | |
| 	struct osl_timespec now, timeout;
 | |
| 
 | |
| 	if (!ss_list->count)
 | |
| 		return;
 | |
| 
 | |
| 	osl_do_gettimeofday(&now);
 | |
| 	timeout.tv_sec = now.tv_sec + RSSICACHE_TIMEOUT;
 | |
| 	if (timeout.tv_sec < now.tv_sec) {
 | |
| 		/*
 | |
| 		 * Integer overflow - assume long enough timeout to be assumed
 | |
| 		 * to be infinite, i.e., the timeout would never happen.
 | |
| 		 */
 | |
| 		AEXT_TRACE("wlan",
 | |
| 			"Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
 | |
| 			RSSICACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
 | |
| 	}
 | |
| 
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 
 | |
| 	/* update RSSI */
 | |
| 	for (i = 0; i < ss_list->count; i++) {
 | |
| 		node = *rssi_head;
 | |
| 		prev = NULL;
 | |
| 		k = 0;
 | |
| 		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
 | |
| 		for (;node;) {
 | |
| 			if (!memcmp(&node->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
 | |
| 				AEXT_INFO("wlan", "Update %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
 | |
| 					k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
 | |
| 				for (j=0; j<RSSIAVG_LEN-1; j++)
 | |
| 					node->RSSI[j] = node->RSSI[j+1];
 | |
| 				node->RSSI[j] = dtoh16(bi->RSSI);
 | |
| 				node->dirty = 0;
 | |
| 				node->tv = timeout;
 | |
| 				break;
 | |
| 			}
 | |
| 			prev = node;
 | |
| 			node = node->next;
 | |
| 			k++;
 | |
| 		}
 | |
| 
 | |
| 		if (node)
 | |
| 			continue;
 | |
| 
 | |
| 		leaf = kmalloc(sizeof(wl_rssi_cache_t), GFP_KERNEL);
 | |
| 		if (!leaf) {
 | |
| 			AEXT_ERROR("wlan", "Memory alloc failure %d\n",
 | |
| 				(int)sizeof(wl_rssi_cache_t));
 | |
| 			return;
 | |
| 		}
 | |
| 		AEXT_INFO("wlan", "Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\" in the leaf\n",
 | |
| 			k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
 | |
| 
 | |
| 		leaf->next = NULL;
 | |
| 		leaf->dirty = 0;
 | |
| 		leaf->tv = timeout;
 | |
| 		memcpy(&leaf->BSSID, &bi->BSSID, ETHER_ADDR_LEN);
 | |
| 		for (j=0; j<RSSIAVG_LEN; j++)
 | |
| 			leaf->RSSI[j] = dtoh16(bi->RSSI);
 | |
| 
 | |
| 		if (!prev)
 | |
| 			*rssi_head = leaf;
 | |
| 		else
 | |
| 			prev->next = leaf;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int16
 | |
| wl_get_avg_rssi(wl_rssi_cache_ctrl_t *rssi_cache_ctrl, void *addr)
 | |
| {
 | |
| 	wl_rssi_cache_t *node, **rssi_head;
 | |
| 	int j, rssi_sum, rssi=RSSI_MINVAL;
 | |
| 
 | |
| 	rssi_head = &rssi_cache_ctrl->m_cache_head;
 | |
| 
 | |
| 	node = *rssi_head;
 | |
| 	for (;node;) {
 | |
| 		if (!memcmp(&node->BSSID, addr, ETHER_ADDR_LEN)) {
 | |
| 			rssi_sum = 0;
 | |
| 			rssi = 0;
 | |
| 			for (j=0; j<RSSIAVG_LEN; j++)
 | |
| 				rssi_sum += node->RSSI[RSSIAVG_LEN-j-1];
 | |
| 			rssi = rssi_sum / j;
 | |
| 			break;
 | |
| 		}
 | |
| 		node = node->next;
 | |
| 	}
 | |
| 	rssi = MIN(rssi, RSSI_MAXVAL);
 | |
| 	if (rssi == RSSI_MINVAL) {
 | |
| 		AEXT_ERROR("wlan", "BSSID %pM does not in RSSI cache\n", addr);
 | |
| 	}
 | |
| 	return (int16)rssi;
 | |
| }
 | |
| #endif /* RSSIAVG */
 | |
| 
 | |
| #if defined(RSSIOFFSET)
 | |
| int
 | |
| wl_update_rssi_offset(struct net_device *net, int rssi)
 | |
| {
 | |
| #if defined(RSSIOFFSET_NEW)
 | |
| 	int j;
 | |
| #endif /* RSSIOFFSET_NEW */
 | |
| 
 | |
| 	if (!g_wifi_on)
 | |
| 		return rssi;
 | |
| 
 | |
| #if defined(RSSIOFFSET_NEW)
 | |
| 	for (j=0; j<RSSI_OFFSET; j++) {
 | |
| 		if (rssi - (RSSI_OFFSET_MINVAL+RSSI_OFFSET_INTVAL*(j+1)) < 0)
 | |
| 			break;
 | |
| 	}
 | |
| 	rssi += j;
 | |
| #else
 | |
| 	rssi += RSSI_OFFSET;
 | |
| #endif /* RSSIOFFSET_NEW */
 | |
| 	return MIN(rssi, RSSI_MAXVAL);
 | |
| }
 | |
| #endif /* RSSIOFFSET */
 | |
| 
 | |
| #if defined(BSSCACHE)
 | |
| void
 | |
| wl_free_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
 | |
| {
 | |
| 	wl_bss_cache_t *node, *cur, **bss_head;
 | |
| 	int i=0;
 | |
| 
 | |
| 	AEXT_TRACE("wlan", "called\n");
 | |
| 
 | |
| 	bss_head = &bss_cache_ctrl->m_cache_head;
 | |
| 	node = *bss_head;
 | |
| 
 | |
| 	for (;node;) {
 | |
| 		AEXT_TRACE("wlan", "Free %d with BSSID %pM\n",
 | |
| 			i, &node->results.bss_info->BSSID);
 | |
| 		cur = node;
 | |
| 		node = cur->next;
 | |
| 		kfree(cur);
 | |
| 		i++;
 | |
| 	}
 | |
| 	*bss_head = NULL;
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_delete_dirty_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
 | |
| {
 | |
| 	wl_bss_cache_t *node, *prev, **bss_head;
 | |
| 	int i = -1, tmp = 0;
 | |
| 	struct osl_timespec now;
 | |
| 
 | |
| 	osl_do_gettimeofday(&now);
 | |
| 
 | |
| 	bss_head = &bss_cache_ctrl->m_cache_head;
 | |
| 	node = *bss_head;
 | |
| 	prev = node;
 | |
| 	for (;node;) {
 | |
| 		i++;
 | |
| 		if (now.tv_sec > node->tv.tv_sec) {
 | |
| 			if (node == *bss_head) {
 | |
| 				tmp = 1;
 | |
| 				*bss_head = node->next;
 | |
| 			} else {
 | |
| 				tmp = 0;
 | |
| 				prev->next = node->next;
 | |
| 			}
 | |
| 			AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
 | |
| 				i, &node->results.bss_info->BSSID,
 | |
| 				dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
 | |
| 			kfree(node);
 | |
| 			if (tmp == 1) {
 | |
| 				node = *bss_head;
 | |
| 				prev = node;
 | |
| 			} else {
 | |
| 				node = prev->next;
 | |
| 			}
 | |
| 			continue;
 | |
| 		}
 | |
| 		prev = node;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_delete_disconnected_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
 | |
| 	u8 *bssid)
 | |
| {
 | |
| 	wl_bss_cache_t *node, *prev, **bss_head;
 | |
| 	int i = -1, tmp = 0;
 | |
| 
 | |
| 	bss_head = &bss_cache_ctrl->m_cache_head;
 | |
| 	node = *bss_head;
 | |
| 	prev = node;
 | |
| 	for (;node;) {
 | |
| 		i++;
 | |
| 		if (!memcmp(&node->results.bss_info->BSSID, bssid, ETHER_ADDR_LEN)) {
 | |
| 			if (node == *bss_head) {
 | |
| 				tmp = 1;
 | |
| 				*bss_head = node->next;
 | |
| 			} else {
 | |
| 				tmp = 0;
 | |
| 				prev->next = node->next;
 | |
| 			}
 | |
| 			AEXT_TRACE("wlan", "Del %d with BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
 | |
| 				i, &node->results.bss_info->BSSID,
 | |
| 				dtoh16(node->results.bss_info->RSSI), node->results.bss_info->SSID);
 | |
| 			kfree(node);
 | |
| 			if (tmp == 1) {
 | |
| 				node = *bss_head;
 | |
| 				prev = node;
 | |
| 			} else {
 | |
| 				node = prev->next;
 | |
| 			}
 | |
| 			continue;
 | |
| 		}
 | |
| 		prev = node;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_reset_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl)
 | |
| {
 | |
| 	wl_bss_cache_t *node, **bss_head;
 | |
| 
 | |
| 	bss_head = &bss_cache_ctrl->m_cache_head;
 | |
| 
 | |
| 	/* reset dirty */
 | |
| 	node = *bss_head;
 | |
| 	for (;node;) {
 | |
| 		node->dirty += 1;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void dump_bss_cache(
 | |
| #if defined(RSSIAVG)
 | |
| 	wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
 | |
| #endif /* RSSIAVG */
 | |
| 	wl_bss_cache_t *node)
 | |
| {
 | |
| 	int k = 0;
 | |
| 	int16 rssi;
 | |
| 
 | |
| 	for (;node;) {
 | |
| #if defined(RSSIAVG)
 | |
| 		rssi = wl_get_avg_rssi(rssi_cache_ctrl, &node->results.bss_info->BSSID);
 | |
| #else
 | |
| 		rssi = dtoh16(node->results.bss_info->RSSI);
 | |
| #endif /* RSSIAVG */
 | |
| 		AEXT_TRACE("wlan", "dump %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
 | |
| 			k, &node->results.bss_info->BSSID, rssi, node->results.bss_info->SSID);
 | |
| 		k++;
 | |
| 		node = node->next;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_update_bss_cache(wl_bss_cache_ctrl_t *bss_cache_ctrl,
 | |
| #if defined(RSSIAVG)
 | |
| 	wl_rssi_cache_ctrl_t *rssi_cache_ctrl,
 | |
| #endif /* RSSIAVG */
 | |
| 	wl_scan_results_t *ss_list)
 | |
| {
 | |
| 	wl_bss_cache_t *node, *prev, *leaf, **bss_head;
 | |
| 	wl_bss_info_t *bi = NULL;
 | |
| 	int i, k=0;
 | |
| #if defined(SORT_BSS_BY_RSSI)
 | |
| 	int16 rssi, rssi_node;
 | |
| #endif /* SORT_BSS_BY_RSSI */
 | |
| 	struct osl_timespec now, timeout;
 | |
| 
 | |
| 	if (!ss_list->count)
 | |
| 		return;
 | |
| 
 | |
| 	osl_do_gettimeofday(&now);
 | |
| 	timeout.tv_sec = now.tv_sec + BSSCACHE_TIMEOUT;
 | |
| 	if (timeout.tv_sec < now.tv_sec) {
 | |
| 		/*
 | |
| 		 * Integer overflow - assume long enough timeout to be assumed
 | |
| 		 * to be infinite, i.e., the timeout would never happen.
 | |
| 		 */
 | |
| 		AEXT_TRACE("wlan",
 | |
| 			"Too long timeout (secs=%d) to ever happen - now=%lu, timeout=%lu\n",
 | |
| 			BSSCACHE_TIMEOUT, now.tv_sec, timeout.tv_sec);
 | |
| 	}
 | |
| 
 | |
| 	bss_head = &bss_cache_ctrl->m_cache_head;
 | |
| 
 | |
| 	for (i=0; i < ss_list->count; i++) {
 | |
| 		node = *bss_head;
 | |
| 		prev = NULL;
 | |
| 		bi = bi ? (wl_bss_info_t *)((uintptr)bi + dtoh32(bi->length)) : ss_list->bss_info;
 | |
| 
 | |
| 		for (;node;) {
 | |
| 			if (!memcmp(&node->results.bss_info->BSSID, &bi->BSSID, ETHER_ADDR_LEN)) {
 | |
| 				if (node == *bss_head)
 | |
| 					*bss_head = node->next;
 | |
| 				else {
 | |
| 					prev->next = node->next;
 | |
| 				}
 | |
| 				break;
 | |
| 			}
 | |
| 			prev = node;
 | |
| 			node = node->next;
 | |
| 		}
 | |
| 
 | |
| 		leaf = kmalloc(dtoh32(bi->length) + sizeof(wl_bss_cache_t), GFP_KERNEL);
 | |
| 		if (!leaf) {
 | |
| 			AEXT_ERROR("wlan", "Memory alloc failure %d\n",
 | |
| 				dtoh32(bi->length) + (int)sizeof(wl_bss_cache_t));
 | |
| 			return;
 | |
| 		}
 | |
| 		if (node) {
 | |
| 			kfree(node);
 | |
| 			node = NULL;
 | |
| 			AEXT_TRACE("wlan",
 | |
| 				"Update %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
 | |
| 				k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
 | |
| 		} else
 | |
| 			AEXT_TRACE("wlan",
 | |
| 				"Add %d with cached BSSID %pM, RSSI=%3d, SSID \"%s\"\n",
 | |
| 				k, &bi->BSSID, dtoh16(bi->RSSI), bi->SSID);
 | |
| 
 | |
| 		memcpy(leaf->results.bss_info, bi, dtoh32(bi->length));
 | |
| 		leaf->next = NULL;
 | |
| 		leaf->dirty = 0;
 | |
| 		leaf->tv = timeout;
 | |
| 		leaf->results.count = 1;
 | |
| 		leaf->results.version = ss_list->version;
 | |
| 		k++;
 | |
| 
 | |
| 		if (*bss_head == NULL)
 | |
| 			*bss_head = leaf;
 | |
| 		else {
 | |
| #if defined(SORT_BSS_BY_RSSI)
 | |
| 			node = *bss_head;
 | |
| #if defined(RSSIAVG)
 | |
| 			rssi = wl_get_avg_rssi(rssi_cache_ctrl, &leaf->results.bss_info->BSSID);
 | |
| #else
 | |
| 			rssi = dtoh16(leaf->results.bss_info->RSSI);
 | |
| #endif /* RSSIAVG */
 | |
| 			for (;node;) {
 | |
| #if defined(RSSIAVG)
 | |
| 				rssi_node = wl_get_avg_rssi(rssi_cache_ctrl,
 | |
| 					&node->results.bss_info->BSSID);
 | |
| #else
 | |
| 				rssi_node = dtoh16(node->results.bss_info->RSSI);
 | |
| #endif /* RSSIAVG */
 | |
| 				if (rssi > rssi_node) {
 | |
| 					leaf->next = node;
 | |
| 					if (node == *bss_head)
 | |
| 						*bss_head = leaf;
 | |
| 					else
 | |
| 						prev->next = leaf;
 | |
| 					break;
 | |
| 				}
 | |
| 				prev = node;
 | |
| 				node = node->next;
 | |
| 			}
 | |
| 			if (node == NULL)
 | |
| 				prev->next = leaf;
 | |
| #else
 | |
| 			leaf->next = *bss_head;
 | |
| 			*bss_head = leaf;
 | |
| #endif /* SORT_BSS_BY_RSSI */
 | |
| 		}
 | |
| 	}
 | |
| 	dump_bss_cache(
 | |
| #if defined(RSSIAVG)
 | |
| 		rssi_cache_ctrl,
 | |
| #endif /* RSSIAVG */
 | |
| 		*bss_head);
 | |
| }
 | |
| 
 | |
| void
 | |
| wl_release_bss_cache_ctrl(wl_bss_cache_ctrl_t *bss_cache_ctrl)
 | |
| {
 | |
| 	AEXT_TRACE("wlan", "Enter\n");
 | |
| 	wl_free_bss_cache(bss_cache_ctrl);
 | |
| }
 | |
| #endif /* BSSCACHE */
 | |
| 
 | |
| 
 | 
