diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/init.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath6kl/init.c | 1011 | 
1 files changed, 541 insertions, 470 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index c1d2366704b..7f55be3092d 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -16,6 +16,7 @@   */  #include <linux/moduleparam.h> +#include <linux/errno.h>  #include <linux/of.h>  #include <linux/mmc/sdio_func.h>  #include "core.h" @@ -26,9 +27,85 @@  unsigned int debug_mask;  static unsigned int testmode; +static bool suspend_cutpower;  module_param(debug_mask, uint, 0644);  module_param(testmode, uint, 0644); +module_param(suspend_cutpower, bool, 0444); + +static const struct ath6kl_hw hw_list[] = { +	{ +		.id				= AR6003_HW_2_0_VERSION, +		.name				= "ar6003 hw 2.0", +		.dataset_patch_addr		= 0x57e884, +		.app_load_addr			= 0x543180, +		.board_ext_data_addr		= 0x57e500, +		.reserved_ram_size		= 6912, +		.refclk_hz			= 26000000, +		.uarttx_pin			= 8, + +		/* hw2.0 needs override address hardcoded */ +		.app_start_override_addr	= 0x944C00, + +		.fw_otp			= AR6003_HW_2_0_OTP_FILE, +		.fw			= AR6003_HW_2_0_FIRMWARE_FILE, +		.fw_tcmd		= AR6003_HW_2_0_TCMD_FIRMWARE_FILE, +		.fw_patch		= AR6003_HW_2_0_PATCH_FILE, +		.fw_api2		= AR6003_HW_2_0_FIRMWARE_2_FILE, +		.fw_board		= AR6003_HW_2_0_BOARD_DATA_FILE, +		.fw_default_board	= AR6003_HW_2_0_DEFAULT_BOARD_DATA_FILE, +	}, +	{ +		.id				= AR6003_HW_2_1_1_VERSION, +		.name				= "ar6003 hw 2.1.1", +		.dataset_patch_addr		= 0x57ff74, +		.app_load_addr			= 0x1234, +		.board_ext_data_addr		= 0x542330, +		.reserved_ram_size		= 512, +		.refclk_hz			= 26000000, +		.uarttx_pin			= 8, + +		.fw_otp			= AR6003_HW_2_1_1_OTP_FILE, +		.fw			= AR6003_HW_2_1_1_FIRMWARE_FILE, +		.fw_tcmd		= AR6003_HW_2_1_1_TCMD_FIRMWARE_FILE, +		.fw_patch		= AR6003_HW_2_1_1_PATCH_FILE, +		.fw_api2		= AR6003_HW_2_1_1_FIRMWARE_2_FILE, +		.fw_board		= AR6003_HW_2_1_1_BOARD_DATA_FILE, +		.fw_default_board	= AR6003_HW_2_1_1_DEFAULT_BOARD_DATA_FILE, +	}, +	{ +		.id				= AR6004_HW_1_0_VERSION, +		.name				= "ar6004 hw 1.0", +		.dataset_patch_addr		= 0x57e884, +		.app_load_addr			= 0x1234, +		.board_ext_data_addr		= 0x437000, +		.reserved_ram_size		= 19456, +		.board_addr			= 0x433900, +		.refclk_hz			= 26000000, +		.uarttx_pin			= 11, + +		.fw			= AR6004_HW_1_0_FIRMWARE_FILE, +		.fw_api2		= AR6004_HW_1_0_FIRMWARE_2_FILE, +		.fw_board		= AR6004_HW_1_0_BOARD_DATA_FILE, +		.fw_default_board	= AR6004_HW_1_0_DEFAULT_BOARD_DATA_FILE, +	}, +	{ +		.id				= AR6004_HW_1_1_VERSION, +		.name				= "ar6004 hw 1.1", +		.dataset_patch_addr		= 0x57e884, +		.app_load_addr			= 0x1234, +		.board_ext_data_addr		= 0x437000, +		.reserved_ram_size		= 11264, +		.board_addr			= 0x43d400, +		.refclk_hz			= 40000000, +		.uarttx_pin			= 11, + +		.fw			= AR6004_HW_1_1_FIRMWARE_FILE, +		.fw_api2		= AR6004_HW_1_1_FIRMWARE_2_FILE, +		.fw_board		= AR6004_HW_1_1_BOARD_DATA_FILE, +		.fw_default_board	= AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE, +	}, +};  /*   * Include definitions here that can be used to tune the WLAN module @@ -55,7 +132,6 @@ module_param(testmode, uint, 0644);   */  #define WLAN_CONFIG_DISCONNECT_TIMEOUT 10 -#define CONFIG_AR600x_DEBUG_UART_TX_PIN 8  #define ATH6KL_DATA_OFFSET    64  struct sk_buff *ath6kl_buf_alloc(int size) @@ -73,37 +149,21 @@ struct sk_buff *ath6kl_buf_alloc(int size)  	return skb;  } -void ath6kl_init_profile_info(struct ath6kl *ar) +void ath6kl_init_profile_info(struct ath6kl_vif *vif)  { -	ar->ssid_len = 0; -	memset(ar->ssid, 0, sizeof(ar->ssid)); +	vif->ssid_len = 0; +	memset(vif->ssid, 0, sizeof(vif->ssid)); -	ar->dot11_auth_mode = OPEN_AUTH; -	ar->auth_mode = NONE_AUTH; -	ar->prwise_crypto = NONE_CRYPT; -	ar->prwise_crypto_len = 0; -	ar->grp_crypto = NONE_CRYPT; -	ar->grp_crypto_len = 0; -	memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list)); -	memset(ar->req_bssid, 0, sizeof(ar->req_bssid)); -	memset(ar->bssid, 0, sizeof(ar->bssid)); -	ar->bss_ch = 0; -	ar->nw_type = ar->next_mode = INFRA_NETWORK; -} - -static u8 ath6kl_get_fw_iftype(struct ath6kl *ar) -{ -	switch (ar->nw_type) { -	case INFRA_NETWORK: -		return HI_OPTION_FW_MODE_BSS_STA; -	case ADHOC_NETWORK: -		return HI_OPTION_FW_MODE_IBSS; -	case AP_NETWORK: -		return HI_OPTION_FW_MODE_AP; -	default: -		ath6kl_err("Unsupported interface type :%d\n", ar->nw_type); -		return 0xff; -	} +	vif->dot11_auth_mode = OPEN_AUTH; +	vif->auth_mode = NONE_AUTH; +	vif->prwise_crypto = NONE_CRYPT; +	vif->prwise_crypto_len = 0; +	vif->grp_crypto = NONE_CRYPT; +	vif->grp_crypto_len = 0; +	memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); +	memset(vif->req_bssid, 0, sizeof(vif->req_bssid)); +	memset(vif->bssid, 0, sizeof(vif->bssid)); +	vif->bss_ch = 0;  }  static int ath6kl_set_host_app_area(struct ath6kl *ar) @@ -120,7 +180,7 @@ static int ath6kl_set_host_app_area(struct ath6kl *ar)  		return -EIO;  	address = TARG_VTOP(ar->target_type, data); -	host_app_area.wmi_protocol_ver = WMI_PROTOCOL_VERSION; +	host_app_area.wmi_protocol_ver = cpu_to_le32(WMI_PROTOCOL_VERSION);  	if (ath6kl_diag_write(ar, address, (u8 *) &host_app_area,  			      sizeof(struct host_app_area)))  		return -EIO; @@ -258,40 +318,12 @@ static int ath6kl_init_service_ep(struct ath6kl *ar)  	return 0;  } -static void ath6kl_init_control_info(struct ath6kl *ar) +void ath6kl_init_control_info(struct ath6kl_vif *vif)  { -	u8 ctr; - -	clear_bit(WMI_ENABLED, &ar->flag); -	ath6kl_init_profile_info(ar); -	ar->def_txkey_index = 0; -	memset(ar->wep_key_list, 0, sizeof(ar->wep_key_list)); -	ar->ch_hint = 0; -	ar->listen_intvl_t = A_DEFAULT_LISTEN_INTERVAL; -	ar->listen_intvl_b = 0; -	ar->tx_pwr = 0; -	clear_bit(SKIP_SCAN, &ar->flag); -	set_bit(WMM_ENABLED, &ar->flag); -	ar->intra_bss = 1; -	memset(&ar->sc_params, 0, sizeof(ar->sc_params)); -	ar->sc_params.short_scan_ratio = WMI_SHORTSCANRATIO_DEFAULT; -	ar->sc_params.scan_ctrl_flags = DEFAULT_SCAN_CTRL_FLAGS; -	ar->lrssi_roam_threshold = DEF_LRSSI_ROAM_THRESHOLD; - -	memset((u8 *)ar->sta_list, 0, -	       AP_MAX_NUM_STA * sizeof(struct ath6kl_sta)); - -	spin_lock_init(&ar->mcastpsq_lock); - -	/* Init the PS queues */ -	for (ctr = 0; ctr < AP_MAX_NUM_STA; ctr++) { -		spin_lock_init(&ar->sta_list[ctr].psq_lock); -		skb_queue_head_init(&ar->sta_list[ctr].psq); -	} - -	skb_queue_head_init(&ar->mcastpsq); - -	memcpy(ar->ap_country_code, DEF_AP_COUNTRY_CODE, 3); +	ath6kl_init_profile_info(vif); +	vif->def_txkey_index = 0; +	memset(vif->wep_key_list, 0, sizeof(vif->wep_key_list)); +	vif->ch_hint = 0;  }  /* @@ -341,62 +373,7 @@ out:  	return status;  } -#define REG_DUMP_COUNT_AR6003   60 -#define REGISTER_DUMP_LEN_MAX   60 - -static void ath6kl_dump_target_assert_info(struct ath6kl *ar) -{ -	u32 address; -	u32 regdump_loc = 0; -	int status; -	u32 regdump_val[REGISTER_DUMP_LEN_MAX]; -	u32 i; - -	if (ar->target_type != TARGET_TYPE_AR6003) -		return; - -	/* the reg dump pointer is copied to the host interest area */ -	address = ath6kl_get_hi_item_addr(ar, HI_ITEM(hi_failure_state)); -	address = TARG_VTOP(ar->target_type, address); - -	/* read RAM location through diagnostic window */ -	status = ath6kl_diag_read32(ar, address, ®dump_loc); - -	if (status || !regdump_loc) { -		ath6kl_err("failed to get ptr to register dump area\n"); -		return; -	} - -	ath6kl_dbg(ATH6KL_DBG_TRC, "location of register dump data: 0x%X\n", -		regdump_loc); -	regdump_loc = TARG_VTOP(ar->target_type, regdump_loc); - -	/* fetch register dump data */ -	status = ath6kl_diag_read(ar, regdump_loc, (u8 *)®dump_val[0], -				  REG_DUMP_COUNT_AR6003 * (sizeof(u32))); - -	if (status) { -		ath6kl_err("failed to get register dump\n"); -		return; -	} -	ath6kl_dbg(ATH6KL_DBG_TRC, "Register Dump:\n"); - -	for (i = 0; i < REG_DUMP_COUNT_AR6003; i++) -		ath6kl_dbg(ATH6KL_DBG_TRC, " %d :  0x%8.8X\n", -			   i, regdump_val[i]); - -} - -void ath6kl_target_failure(struct ath6kl *ar) -{ -	ath6kl_err("target asserted\n"); - -	/* try dumping target assertion information (if any) */ -	ath6kl_dump_target_assert_info(ar); - -} - -static int ath6kl_target_config_wlan_params(struct ath6kl *ar) +static int ath6kl_target_config_wlan_params(struct ath6kl *ar, int idx)  {  	int status = 0;  	int ret; @@ -406,46 +383,46 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)  	 * default values. Required if checksum offload is needed. Set  	 * RxMetaVersion to 2.  	 */ -	if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, +	if (ath6kl_wmi_set_rx_frame_format_cmd(ar->wmi, idx,  					       ar->rx_meta_ver, 0, 0)) {  		ath6kl_err("unable to set the rx frame format\n");  		status = -EIO;  	}  	if (ar->conf_flags & ATH6KL_CONF_IGNORE_PS_FAIL_EVT_IN_SCAN) -		if ((ath6kl_wmi_pmparams_cmd(ar->wmi, 0, 1, 0, 0, 1, +		if ((ath6kl_wmi_pmparams_cmd(ar->wmi, idx, 0, 1, 0, 0, 1,  		     IGNORE_POWER_SAVE_FAIL_EVENT_DURING_SCAN)) != 0) {  			ath6kl_err("unable to set power save fail event policy\n");  			status = -EIO;  		}  	if (!(ar->conf_flags & ATH6KL_CONF_IGNORE_ERP_BARKER)) -		if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, 0, +		if ((ath6kl_wmi_set_lpreamble_cmd(ar->wmi, idx, 0,  		     WMI_DONOT_IGNORE_BARKER_IN_ERP)) != 0) {  			ath6kl_err("unable to set barker preamble policy\n");  			status = -EIO;  		} -	if (ath6kl_wmi_set_keepalive_cmd(ar->wmi, +	if (ath6kl_wmi_set_keepalive_cmd(ar->wmi, idx,  			WLAN_CONFIG_KEEP_ALIVE_INTERVAL)) {  		ath6kl_err("unable to set keep alive interval\n");  		status = -EIO;  	} -	if (ath6kl_wmi_disctimeout_cmd(ar->wmi, +	if (ath6kl_wmi_disctimeout_cmd(ar->wmi, idx,  			WLAN_CONFIG_DISCONNECT_TIMEOUT)) {  		ath6kl_err("unable to set disconnect timeout\n");  		status = -EIO;  	}  	if (!(ar->conf_flags & ATH6KL_CONF_ENABLE_TX_BURST)) -		if (ath6kl_wmi_set_wmm_txop(ar->wmi, WMI_TXOP_DISABLED)) { +		if (ath6kl_wmi_set_wmm_txop(ar->wmi, idx, WMI_TXOP_DISABLED)) {  			ath6kl_err("unable to set txop bursting\n");  			status = -EIO;  		} -	if (ar->p2p) { -		ret = ath6kl_wmi_info_req_cmd(ar->wmi, +	if (ar->p2p && (ar->vif_max == 1 || idx)) { +		ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx,  					      P2P_FLAG_CAPABILITIES_REQ |  					      P2P_FLAG_MACADDR_REQ |  					      P2P_FLAG_HMODEL_REQ); @@ -453,13 +430,13 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)  			ath6kl_dbg(ATH6KL_DBG_TRC, "failed to request P2P "  				   "capabilities (%d) - assuming P2P not "  				   "supported\n", ret); -			ar->p2p = 0; +			ar->p2p = false;  		}  	} -	if (ar->p2p) { +	if (ar->p2p && (ar->vif_max == 1 || idx)) {  		/* Enable Probe Request reporting for P2P */ -		ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, true); +		ret = ath6kl_wmi_probe_report_req_cmd(ar->wmi, idx, true);  		if (ret) {  			ath6kl_dbg(ATH6KL_DBG_TRC, "failed to enable Probe "  				   "Request reporting (%d)\n", ret); @@ -472,13 +449,40 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)  int ath6kl_configure_target(struct ath6kl *ar)  {  	u32 param, ram_reserved_size; -	u8 fw_iftype; +	u8 fw_iftype, fw_mode = 0, fw_submode = 0; +	int i, status; -	fw_iftype = ath6kl_get_fw_iftype(ar); -	if (fw_iftype == 0xff) -		return -EINVAL; +	/* +	 * Note: Even though the firmware interface type is +	 * chosen as BSS_STA for all three interfaces, can +	 * be configured to IBSS/AP as long as the fw submode +	 * remains normal mode (0 - AP, STA and IBSS). But +	 * due to an target assert in firmware only one interface is +	 * configured for now. +	 */ +	fw_iftype = HI_OPTION_FW_MODE_BSS_STA; + +	for (i = 0; i < ar->vif_max; i++) +		fw_mode |= fw_iftype << (i * HI_OPTION_FW_MODE_BITS); + +	/* +	 * By default, submodes : +	 *		vif[0] - AP/STA/IBSS +	 *		vif[1] - "P2P dev"/"P2P GO"/"P2P Client" +	 *		vif[2] - "P2P dev"/"P2P GO"/"P2P Client" +	 */ + +	for (i = 0; i < ar->max_norm_iface; i++) +		fw_submode |= HI_OPTION_FW_SUBMODE_NONE << +			      (i * HI_OPTION_FW_SUBMODE_BITS); + +	for (i = ar->max_norm_iface; i < ar->vif_max; i++) +		fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << +			      (i * HI_OPTION_FW_SUBMODE_BITS); + +	if (ar->p2p && ar->vif_max == 1) +		fw_submode = HI_OPTION_FW_SUBMODE_P2PDEV; -	/* Tell target which HTC version it is used*/  	param = HTC_PROTOCOL_VERSION;  	if (ath6kl_bmi_write(ar,  			     ath6kl_get_hi_item_addr(ar, @@ -499,12 +503,10 @@ int ath6kl_configure_target(struct ath6kl *ar)  		return -EIO;  	} -	param |= (1 << HI_OPTION_NUM_DEV_SHIFT); -	param |= (fw_iftype << HI_OPTION_FW_MODE_SHIFT); -	if (ar->p2p && fw_iftype == HI_OPTION_FW_MODE_BSS_STA) { -		param |= HI_OPTION_FW_SUBMODE_P2PDEV << -			HI_OPTION_FW_SUBMODE_SHIFT; -	} +	param |= (ar->vif_max << HI_OPTION_NUM_DEV_SHIFT); +	param |= fw_mode << HI_OPTION_FW_MODE_SHIFT; +	param |= fw_submode << HI_OPTION_FW_SUBMODE_SHIFT; +  	param |= (0 << HI_OPTION_MAC_ADDR_METHOD_SHIFT);  	param |= (0 << HI_OPTION_FW_BRIDGE_SHIFT); @@ -550,71 +552,55 @@ int ath6kl_configure_target(struct ath6kl *ar)  		/* use default number of control buffers */  		return -EIO; +	/* Configure GPIO AR600x UART */ +	param = ar->hw.uarttx_pin; +	status = ath6kl_bmi_write(ar, +				ath6kl_get_hi_item_addr(ar, +				HI_ITEM(hi_dbg_uart_txpin)), +				(u8 *)¶m, 4); +	if (status) +		return status; + +	/* Configure target refclk_hz */ +	param =  ar->hw.refclk_hz; +	status = ath6kl_bmi_write(ar, +				ath6kl_get_hi_item_addr(ar, +				HI_ITEM(hi_refclk_hz)), +				(u8 *)¶m, 4); +	if (status) +		return status; +  	return 0;  } -struct ath6kl *ath6kl_core_alloc(struct device *sdev) +void ath6kl_core_free(struct ath6kl *ar)  { -	struct net_device *dev; -	struct ath6kl *ar; -	struct wireless_dev *wdev; - -	wdev = ath6kl_cfg80211_init(sdev); -	if (!wdev) { -		ath6kl_err("ath6kl_cfg80211_init failed\n"); -		return NULL; -	} - -	ar = wdev_priv(wdev); -	ar->dev = sdev; -	ar->wdev = wdev; -	wdev->iftype = NL80211_IFTYPE_STATION; - -	if (ath6kl_debug_init(ar)) { -		ath6kl_err("Failed to initialize debugfs\n"); -		ath6kl_cfg80211_deinit(ar); -		return NULL; -	} - -	dev = alloc_netdev(0, "wlan%d", ether_setup); -	if (!dev) { -		ath6kl_err("no memory for network device instance\n"); -		ath6kl_cfg80211_deinit(ar); -		return NULL; -	} - -	dev->ieee80211_ptr = wdev; -	SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); -	wdev->netdev = dev; -	ar->sme_state = SME_DISCONNECTED; - -	init_netdev(dev); +	wiphy_free(ar->wiphy); +} -	ar->net_dev = dev; -	set_bit(WLAN_ENABLED, &ar->flag); +void ath6kl_core_cleanup(struct ath6kl *ar) +{ +	ath6kl_hif_power_off(ar); -	ar->wlan_pwr_state = WLAN_POWER_STATE_ON; +	destroy_workqueue(ar->ath6kl_wq); -	spin_lock_init(&ar->lock); +	if (ar->htc_target) +		ath6kl_htc_cleanup(ar->htc_target); -	ath6kl_init_control_info(ar); -	init_waitqueue_head(&ar->event_wq); -	sema_init(&ar->sem, 1); -	clear_bit(DESTROY_IN_PROGRESS, &ar->flag); +	ath6kl_cookie_cleanup(ar); -	INIT_LIST_HEAD(&ar->amsdu_rx_buffer_queue); +	ath6kl_cleanup_amsdu_rxbufs(ar); -	setup_timer(&ar->disconnect_timer, disconnect_timer_handler, -		    (unsigned long) dev); +	ath6kl_bmi_cleanup(ar); -	return ar; -} +	ath6kl_debug_cleanup(ar); -int ath6kl_unavail_ev(struct ath6kl *ar) -{ -	ath6kl_destroy(ar->net_dev, 1); +	kfree(ar->fw_board); +	kfree(ar->fw_otp); +	kfree(ar->fw); +	kfree(ar->fw_patch); -	return 0; +	ath6kl_deinit_ieee80211_hw(ar);  }  /* firmware upload */ @@ -643,11 +629,11 @@ static int ath6kl_get_fw(struct ath6kl *ar, const char *filename,  static const char *get_target_ver_dir(const struct ath6kl *ar)  {  	switch (ar->version.target_ver) { -	case AR6003_REV1_VERSION: +	case AR6003_HW_1_0_VERSION:  		return "ath6k/AR6003/hw1.0"; -	case AR6003_REV2_VERSION: +	case AR6003_HW_2_0_VERSION:  		return "ath6k/AR6003/hw2.0"; -	case AR6003_REV3_VERSION: +	case AR6003_HW_2_1_1_VERSION:  		return "ath6k/AR6003/hw2.1.1";  	}  	ath6kl_warn("%s: unsupported target version 0x%x.\n", __func__, @@ -705,17 +691,10 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)  	if (ar->fw_board != NULL)  		return 0; -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		filename = AR6003_REV2_BOARD_DATA_FILE; -		break; -	case AR6004_REV1_VERSION: -		filename = AR6004_REV1_BOARD_DATA_FILE; -		break; -	default: -		filename = AR6003_REV3_BOARD_DATA_FILE; -		break; -	} +	if (WARN_ON(ar->hw.fw_board == NULL)) +		return -EINVAL; + +	filename = ar->hw.fw_board;  	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,  			    &ar->fw_board_len); @@ -733,17 +712,7 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar)  	ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n",  		    filename, ret); -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		filename = AR6003_REV2_DEFAULT_BOARD_DATA_FILE; -		break; -	case AR6004_REV1_VERSION: -		filename = AR6004_REV1_DEFAULT_BOARD_DATA_FILE; -		break; -	default: -		filename = AR6003_REV3_DEFAULT_BOARD_DATA_FILE; -		break; -	} +	filename = ar->hw.fw_default_board;  	ret = ath6kl_get_fw(ar, filename, &ar->fw_board,  			    &ar->fw_board_len); @@ -767,19 +736,14 @@ static int ath6kl_fetch_otp_file(struct ath6kl *ar)  	if (ar->fw_otp != NULL)  		return 0; -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		filename = AR6003_REV2_OTP_FILE; -		break; -	case AR6004_REV1_VERSION: -		ath6kl_dbg(ATH6KL_DBG_TRC, "AR6004 doesn't need OTP file\n"); +	if (ar->hw.fw_otp == NULL) { +		ath6kl_dbg(ATH6KL_DBG_BOOT, +			   "no OTP file configured for this hw\n");  		return 0; -		break; -	default: -		filename = AR6003_REV3_OTP_FILE; -		break;  	} +	filename = ar->hw.fw_otp; +  	ret = ath6kl_get_fw(ar, filename, &ar->fw_otp,  			    &ar->fw_otp_len);  	if (ret) { @@ -800,38 +764,22 @@ static int ath6kl_fetch_fw_file(struct ath6kl *ar)  		return 0;  	if (testmode) { -		switch (ar->version.target_ver) { -		case AR6003_REV2_VERSION: -			filename = AR6003_REV2_TCMD_FIRMWARE_FILE; -			break; -		case AR6003_REV3_VERSION: -			filename = AR6003_REV3_TCMD_FIRMWARE_FILE; -			break; -		case AR6004_REV1_VERSION: -			ath6kl_warn("testmode not supported with ar6004\n"); +		if (ar->hw.fw_tcmd == NULL) { +			ath6kl_warn("testmode not supported\n");  			return -EOPNOTSUPP; -		default: -			ath6kl_warn("unknown target version: 0x%x\n", -				       ar->version.target_ver); -			return -EINVAL;  		} +		filename = ar->hw.fw_tcmd; +  		set_bit(TESTMODE, &ar->flag);  		goto get_fw;  	} -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		filename = AR6003_REV2_FIRMWARE_FILE; -		break; -	case AR6004_REV1_VERSION: -		filename = AR6004_REV1_FIRMWARE_FILE; -		break; -	default: -		filename = AR6003_REV3_FIRMWARE_FILE; -		break; -	} +	if (WARN_ON(ar->hw.fw == NULL)) +		return -EINVAL; + +	filename = ar->hw.fw;  get_fw:  	ret = ath6kl_get_fw(ar, filename, &ar->fw, &ar->fw_len); @@ -849,27 +797,20 @@ static int ath6kl_fetch_patch_file(struct ath6kl *ar)  	const char *filename;  	int ret; -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		filename = AR6003_REV2_PATCH_FILE; -		break; -	case AR6004_REV1_VERSION: -		/* FIXME: implement for AR6004 */ +	if (ar->fw_patch != NULL)  		return 0; -		break; -	default: -		filename = AR6003_REV3_PATCH_FILE; -		break; -	} -	if (ar->fw_patch == NULL) { -		ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, -				    &ar->fw_patch_len); -		if (ret) { -			ath6kl_err("Failed to get patch file %s: %d\n", -				   filename, ret); -			return ret; -		} +	if (ar->hw.fw_patch == NULL) +		return 0; + +	filename = ar->hw.fw_patch; + +	ret = ath6kl_get_fw(ar, filename, &ar->fw_patch, +			    &ar->fw_patch_len); +	if (ret) { +		ath6kl_err("Failed to get patch file %s: %d\n", +			   filename, ret); +		return ret;  	}  	return 0; @@ -904,19 +845,10 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)  	int ret, ie_id, i, index, bit;  	__le32 *val; -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		filename = AR6003_REV2_FIRMWARE_2_FILE; -		break; -	case AR6003_REV3_VERSION: -		filename = AR6003_REV3_FIRMWARE_2_FILE; -		break; -	case AR6004_REV1_VERSION: -		filename = AR6004_REV1_FIRMWARE_2_FILE; -		break; -	default: +	if (ar->hw.fw_api2 == NULL)  		return -EOPNOTSUPP; -	} + +	filename = ar->hw.fw_api2;  	ret = request_firmware(&fw, filename, ar->dev);  	if (ret) @@ -1006,12 +938,15 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)  				   ar->hw.reserved_ram_size);  			break;  		case ATH6KL_FW_IE_CAPABILITIES: +			if (ie_len < DIV_ROUND_UP(ATH6KL_FW_CAPABILITY_MAX, 8)) +				break; +  			ath6kl_dbg(ATH6KL_DBG_BOOT,  				   "found firmware capabilities ie (%zd B)\n",  				   ie_len);  			for (i = 0; i < ATH6KL_FW_CAPABILITY_MAX; i++) { -				index = ALIGN(i, 8) / 8; +				index = i / 8;  				bit = i % 8;  				if (data[index] & (1 << bit)) @@ -1030,9 +965,34 @@ static int ath6kl_fetch_fw_api2(struct ath6kl *ar)  			ar->hw.dataset_patch_addr = le32_to_cpup(val);  			ath6kl_dbg(ATH6KL_DBG_BOOT, -				   "found patch address ie 0x%d\n", +				   "found patch address ie 0x%x\n",  				   ar->hw.dataset_patch_addr);  			break; +		case ATH6KL_FW_IE_BOARD_ADDR: +			if (ie_len != sizeof(*val)) +				break; + +			val = (__le32 *) data; +			ar->hw.board_addr = le32_to_cpup(val); + +			ath6kl_dbg(ATH6KL_DBG_BOOT, +				   "found board address ie 0x%x\n", +				   ar->hw.board_addr); +			break; +		case ATH6KL_FW_IE_VIF_MAX: +			if (ie_len != sizeof(*val)) +				break; + +			val = (__le32 *) data; +			ar->vif_max = min_t(unsigned int, le32_to_cpup(val), +					    ATH6KL_VIF_MAX); + +			if (ar->vif_max > 1 && !ar->p2p) +				ar->max_norm_iface = 2; + +			ath6kl_dbg(ATH6KL_DBG_BOOT, +				   "found vif max ie %d\n", ar->vif_max); +			break;  		default:  			ath6kl_dbg(ATH6KL_DBG_BOOT, "Unknown fw ie: %u\n",  				   le32_to_cpup(&hdr->id)); @@ -1087,8 +1047,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)  	 * For AR6004, host determine Target RAM address for  	 * writing board data.  	 */ -	if (ar->target_type == TARGET_TYPE_AR6004) { -		board_address = AR6004_REV1_BOARD_DATA_ADDRESS; +	if (ar->hw.board_addr != 0) { +		board_address = ar->hw.board_addr;  		ath6kl_bmi_write(ar,  				ath6kl_get_hi_item_addr(ar,  				HI_ITEM(hi_board_data)), @@ -1106,7 +1066,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)  			HI_ITEM(hi_board_ext_data)),  			(u8 *) &board_ext_address, 4); -	if (board_ext_address == 0) { +	if (ar->target_type == TARGET_TYPE_AR6003 && +	    board_ext_address == 0) {  		ath6kl_err("Failed to get board file target address.\n");  		return -EINVAL;  	} @@ -1126,8 +1087,8 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)  		break;  	} -	if (ar->fw_board_len == (board_data_size + -				 board_ext_data_size)) { +	if (board_ext_address && +	    ar->fw_board_len == (board_data_size + board_ext_data_size)) {  		/* write extended board data */  		ath6kl_dbg(ATH6KL_DBG_BOOT, @@ -1182,10 +1143,11 @@ static int ath6kl_upload_board_file(struct ath6kl *ar)  static int ath6kl_upload_otp(struct ath6kl *ar)  {  	u32 address, param; +	bool from_hw = false;  	int ret; -	if (WARN_ON(ar->fw_otp == NULL)) -		return -ENOENT; +	if (ar->fw_otp == NULL) +		return 0;  	address = ar->hw.app_load_addr; @@ -1210,15 +1172,20 @@ static int ath6kl_upload_otp(struct ath6kl *ar)  		return ret;  	} -	ar->hw.app_start_override_addr = address; +	if (ar->hw.app_start_override_addr == 0) { +		ar->hw.app_start_override_addr = address; +		from_hw = true; +	} -	ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr 0x%x\n", +	ath6kl_dbg(ATH6KL_DBG_BOOT, "app_start_override_addr%s 0x%x\n", +		   from_hw ? " (from hw)" : "",  		   ar->hw.app_start_override_addr);  	/* execute the OTP code */ -	ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", address); +	ath6kl_dbg(ATH6KL_DBG_BOOT, "executing OTP at 0x%x\n", +		   ar->hw.app_start_override_addr);  	param = 0; -	ath6kl_bmi_execute(ar, address, ¶m); +	ath6kl_bmi_execute(ar, ar->hw.app_start_override_addr, ¶m);  	return ret;  } @@ -1229,7 +1196,7 @@ static int ath6kl_upload_firmware(struct ath6kl *ar)  	int ret;  	if (WARN_ON(ar->fw == NULL)) -		return -ENOENT; +		return 0;  	address = ar->hw.app_load_addr; @@ -1259,8 +1226,8 @@ static int ath6kl_upload_patch(struct ath6kl *ar)  	u32 address, param;  	int ret; -	if (WARN_ON(ar->fw_patch == NULL)) -		return -ENOENT; +	if (ar->fw_patch == NULL) +		return 0;  	address = ar->hw.dataset_patch_addr; @@ -1345,7 +1312,7 @@ static int ath6kl_init_upload(struct ath6kl *ar)  		return status;  	/* WAR to avoid SDIO CRC err */ -	if (ar->version.target_ver == AR6003_REV2_VERSION) { +	if (ar->version.target_ver == AR6003_HW_2_0_VERSION) {  		ath6kl_err("temporary war to avoid sdio crc error\n");  		param = 0x20; @@ -1402,43 +1369,29 @@ static int ath6kl_init_upload(struct ath6kl *ar)  	if (status)  		return status; -	/* Configure GPIO AR6003 UART */ -	param = CONFIG_AR600x_DEBUG_UART_TX_PIN; -	status = ath6kl_bmi_write(ar, -				  ath6kl_get_hi_item_addr(ar, -				  HI_ITEM(hi_dbg_uart_txpin)), -				  (u8 *)¶m, 4); -  	return status;  }  static int ath6kl_init_hw_params(struct ath6kl *ar)  { -	switch (ar->version.target_ver) { -	case AR6003_REV2_VERSION: -		ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; -		ar->hw.app_load_addr = AR6003_REV2_APP_LOAD_ADDRESS; -		ar->hw.board_ext_data_addr = AR6003_REV2_BOARD_EXT_DATA_ADDRESS; -		ar->hw.reserved_ram_size = AR6003_REV2_RAM_RESERVE_SIZE; -		break; -	case AR6003_REV3_VERSION: -		ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; -		ar->hw.app_load_addr = 0x1234; -		ar->hw.board_ext_data_addr = AR6003_REV3_BOARD_EXT_DATA_ADDRESS; -		ar->hw.reserved_ram_size = AR6003_REV3_RAM_RESERVE_SIZE; -		break; -	case AR6004_REV1_VERSION: -		ar->hw.dataset_patch_addr = AR6003_REV2_DATASET_PATCH_ADDRESS; -		ar->hw.app_load_addr = AR6003_REV3_APP_LOAD_ADDRESS; -		ar->hw.board_ext_data_addr = AR6004_REV1_BOARD_EXT_DATA_ADDRESS; -		ar->hw.reserved_ram_size = AR6004_REV1_RAM_RESERVE_SIZE; -		break; -	default: +	const struct ath6kl_hw *hw; +	int i; + +	for (i = 0; i < ARRAY_SIZE(hw_list); i++) { +		hw = &hw_list[i]; + +		if (hw->id == ar->version.target_ver) +			break; +	} + +	if (i == ARRAY_SIZE(hw_list)) {  		ath6kl_err("Unsupported hardware version: 0x%x\n",  			   ar->version.target_ver);  		return -EINVAL;  	} +	ar->hw = *hw; +  	ath6kl_dbg(ATH6KL_DBG_BOOT,  		   "target_ver 0x%x target_type 0x%x dataset_patch 0x%x app_load_addr 0x%x\n",  		   ar->version.target_ver, ar->target_type, @@ -1447,75 +1400,75 @@ static int ath6kl_init_hw_params(struct ath6kl *ar)  		   "app_start_override_addr 0x%x board_ext_data_addr 0x%x reserved_ram_size 0x%x",  		   ar->hw.app_start_override_addr, ar->hw.board_ext_data_addr,  		   ar->hw.reserved_ram_size); +	ath6kl_dbg(ATH6KL_DBG_BOOT, +		   "refclk_hz %d uarttx_pin %d", +		   ar->hw.refclk_hz, ar->hw.uarttx_pin);  	return 0;  } -static int ath6kl_init(struct net_device *dev) +static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type)  { -	struct ath6kl *ar = ath6kl_priv(dev); -	int status = 0; -	s32 timeleft; +	switch (type) { +	case ATH6KL_HIF_TYPE_SDIO: +		return "sdio"; +	case ATH6KL_HIF_TYPE_USB: +		return "usb"; +	} -	if (!ar) -		return -EIO; +	return NULL; +} + +int ath6kl_init_hw_start(struct ath6kl *ar) +{ +	long timeleft; +	int ret, i; + +	ath6kl_dbg(ATH6KL_DBG_BOOT, "hw start\n"); + +	ret = ath6kl_hif_power_on(ar); +	if (ret) +		return ret; + +	ret = ath6kl_configure_target(ar); +	if (ret) +		goto err_power_off; + +	ret = ath6kl_init_upload(ar); +	if (ret) +		goto err_power_off;  	/* Do we need to finish the BMI phase */ +	/* FIXME: return error from ath6kl_bmi_done() */  	if (ath6kl_bmi_done(ar)) { -		status = -EIO; -		goto ath6kl_init_done; +		ret = -EIO; +		goto err_power_off;  	} -	/* Indicate that WMI is enabled (although not ready yet) */ -	set_bit(WMI_ENABLED, &ar->flag); -	ar->wmi = ath6kl_wmi_init(ar); -	if (!ar->wmi) { -		ath6kl_err("failed to initialize wmi\n"); -		status = -EIO; -		goto ath6kl_init_done; -	} - -	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); -  	/*  	 * The reason we have to wait for the target here is that the  	 * driver layer has to init BMI in order to set the host block  	 * size.  	 */  	if (ath6kl_htc_wait_target(ar->htc_target)) { -		status = -EIO; -		goto err_node_cleanup; +		ret = -EIO; +		goto err_power_off;  	}  	if (ath6kl_init_service_ep(ar)) { -		status = -EIO; +		ret = -EIO;  		goto err_cleanup_scatter;  	} -	/* setup access class priority mappings */ -	ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest  */ -	ar->ac_stream_pri_map[WMM_AC_BE] = 1; -	ar->ac_stream_pri_map[WMM_AC_VI] = 2; -	ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ - -	/* give our connected endpoints some buffers */ -	ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); -	ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); - -	/* allocate some buffers that handle larger AMSDU frames */ -	ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); -  	/* setup credit distribution */ -	ath6k_setup_credit_dist(ar->htc_target, &ar->credit_state_info); - -	ath6kl_cookie_init(ar); +	ath6kl_credit_setup(ar->htc_target, &ar->credit_state_info);  	/* start HTC */ -	status = ath6kl_htc_start(ar->htc_target); - -	if (status) { +	ret = ath6kl_htc_start(ar->htc_target); +	if (ret) { +		/* FIXME: call this */  		ath6kl_cookie_cleanup(ar); -		goto err_rxbuf_cleanup; +		goto err_cleanup_scatter;  	}  	/* Wait for Wmi event to be ready */ @@ -1526,54 +1479,81 @@ static int ath6kl_init(struct net_device *dev)  	ath6kl_dbg(ATH6KL_DBG_BOOT, "firmware booted\n"); + +	if (test_and_clear_bit(FIRST_BOOT, &ar->flag)) { +		ath6kl_info("%s %s fw %s%s\n", +			    ar->hw.name, +			    ath6kl_init_get_hif_name(ar->hif_type), +			    ar->wiphy->fw_version, +			    test_bit(TESTMODE, &ar->flag) ? " testmode" : ""); +	} +  	if (ar->version.abi_ver != ATH6KL_ABI_VERSION) {  		ath6kl_err("abi version mismatch: host(0x%x), target(0x%x)\n",  			   ATH6KL_ABI_VERSION, ar->version.abi_ver); -		status = -EIO; +		ret = -EIO;  		goto err_htc_stop;  	}  	if (!timeleft || signal_pending(current)) {  		ath6kl_err("wmi is not ready or wait was interrupted\n"); -		status = -EIO; +		ret = -EIO;  		goto err_htc_stop;  	}  	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: wmi is ready\n", __func__);  	/* communicate the wmi protocol verision to the target */ +	/* FIXME: return error */  	if ((ath6kl_set_host_app_area(ar)) != 0)  		ath6kl_err("unable to set the host app area\n"); -	ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | -			 ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; +	for (i = 0; i < ar->vif_max; i++) { +		ret = ath6kl_target_config_wlan_params(ar, i); +		if (ret) +			goto err_htc_stop; +	} -	ar->wdev->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; +	ar->state = ATH6KL_STATE_ON; -	status = ath6kl_target_config_wlan_params(ar); -	if (!status) -		goto ath6kl_init_done; +	return 0;  err_htc_stop:  	ath6kl_htc_stop(ar->htc_target); -err_rxbuf_cleanup: -	ath6kl_htc_flush_rx_buf(ar->htc_target); -	ath6kl_cleanup_amsdu_rxbufs(ar);  err_cleanup_scatter:  	ath6kl_hif_cleanup_scatter(ar); -err_node_cleanup: -	ath6kl_wmi_shutdown(ar->wmi); -	clear_bit(WMI_ENABLED, &ar->flag); -	ar->wmi = NULL; +err_power_off: +	ath6kl_hif_power_off(ar); -ath6kl_init_done: -	return status; +	return ret; +} + +int ath6kl_init_hw_stop(struct ath6kl *ar) +{ +	int ret; + +	ath6kl_dbg(ATH6KL_DBG_BOOT, "hw stop\n"); + +	ath6kl_htc_stop(ar->htc_target); + +	ath6kl_hif_stop(ar); + +	ath6kl_bmi_reset(ar); + +	ret = ath6kl_hif_power_off(ar); +	if (ret) +		ath6kl_warn("failed to power off hif: %d\n", ret); + +	ar->state = ATH6KL_STATE_OFF; + +	return 0;  }  int ath6kl_core_init(struct ath6kl *ar)  { -	int ret = 0;  	struct ath6kl_bmi_target_info targ_info; +	struct net_device *ndev; +	int ret = 0, i;  	ar->ath6kl_wq = create_singlethread_workqueue("ath6kl");  	if (!ar->ath6kl_wq) @@ -1583,145 +1563,236 @@ int ath6kl_core_init(struct ath6kl *ar)  	if (ret)  		goto err_wq; -	ret = ath6kl_bmi_get_target_info(ar, &targ_info); +	/* +	 * Turn on power to get hardware (target) version and leave power +	 * on delibrately as we will boot the hardware anyway within few +	 * seconds. +	 */ +	ret = ath6kl_hif_power_on(ar);  	if (ret)  		goto err_bmi_cleanup; +	ret = ath6kl_bmi_get_target_info(ar, &targ_info); +	if (ret) +		goto err_power_off; +  	ar->version.target_ver = le32_to_cpu(targ_info.version);  	ar->target_type = le32_to_cpu(targ_info.type); -	ar->wdev->wiphy->hw_version = le32_to_cpu(targ_info.version); +	ar->wiphy->hw_version = le32_to_cpu(targ_info.version);  	ret = ath6kl_init_hw_params(ar);  	if (ret) -		goto err_bmi_cleanup; - -	ret = ath6kl_configure_target(ar); -	if (ret) -		goto err_bmi_cleanup; +		goto err_power_off;  	ar->htc_target = ath6kl_htc_create(ar);  	if (!ar->htc_target) {  		ret = -ENOMEM; -		goto err_bmi_cleanup; -	} - -	ar->aggr_cntxt = aggr_init(ar->net_dev); -	if (!ar->aggr_cntxt) { -		ath6kl_err("failed to initialize aggr\n"); -		ret = -ENOMEM; -		goto err_htc_cleanup; +		goto err_power_off;  	}  	ret = ath6kl_fetch_firmwares(ar);  	if (ret)  		goto err_htc_cleanup; -	ret = ath6kl_init_upload(ar); -	if (ret) +	/* FIXME: we should free all firmwares in the error cases below */ + +	/* Indicate that WMI is enabled (although not ready yet) */ +	set_bit(WMI_ENABLED, &ar->flag); +	ar->wmi = ath6kl_wmi_init(ar); +	if (!ar->wmi) { +		ath6kl_err("failed to initialize wmi\n"); +		ret = -EIO;  		goto err_htc_cleanup; +	} + +	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); -	ret = ath6kl_init(ar->net_dev); +	ret = ath6kl_register_ieee80211_hw(ar);  	if (ret) -		goto err_htc_cleanup; +		goto err_node_cleanup; -	/* This runs the init function if registered */ -	ret = register_netdev(ar->net_dev); +	ret = ath6kl_debug_init(ar);  	if (ret) { -		ath6kl_err("register_netdev failed\n"); -		ath6kl_destroy(ar->net_dev, 0); -		return ret; +		wiphy_unregister(ar->wiphy); +		goto err_node_cleanup; +	} + +	for (i = 0; i < ar->vif_max; i++) +		ar->avail_idx_map |= BIT(i); + +	rtnl_lock(); + +	/* Add an initial station interface */ +	ndev = ath6kl_interface_add(ar, "wlan%d", NL80211_IFTYPE_STATION, 0, +				    INFRA_NETWORK); + +	rtnl_unlock(); + +	if (!ndev) { +		ath6kl_err("Failed to instantiate a network device\n"); +		ret = -ENOMEM; +		wiphy_unregister(ar->wiphy); +		goto err_debug_init;  	} -	set_bit(NETDEV_REGISTERED, &ar->flag);  	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", -			__func__, ar->net_dev->name, ar->net_dev, ar); +			__func__, ndev->name, ndev, ar); + +	/* setup access class priority mappings */ +	ar->ac_stream_pri_map[WMM_AC_BK] = 0; /* lowest  */ +	ar->ac_stream_pri_map[WMM_AC_BE] = 1; +	ar->ac_stream_pri_map[WMM_AC_VI] = 2; +	ar->ac_stream_pri_map[WMM_AC_VO] = 3; /* highest */ + +	/* give our connected endpoints some buffers */ +	ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); +	ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); + +	/* allocate some buffers that handle larger AMSDU frames */ +	ath6kl_refill_amsdu_rxbufs(ar, ATH6KL_MAX_AMSDU_RX_BUFFERS); + +	ath6kl_cookie_init(ar); + +	ar->conf_flags = ATH6KL_CONF_IGNORE_ERP_BARKER | +			 ATH6KL_CONF_ENABLE_11N | ATH6KL_CONF_ENABLE_TX_BURST; + +	if (suspend_cutpower) +		ar->conf_flags |= ATH6KL_CONF_SUSPEND_CUTPOWER; + +	ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM | +			    WIPHY_FLAG_HAVE_AP_SME | +			    WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | +			    WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; + +	if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) +		ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; + +	ar->wiphy->probe_resp_offload = +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; + +	set_bit(FIRST_BOOT, &ar->flag); + +	ret = ath6kl_init_hw_start(ar); +	if (ret) { +		ath6kl_err("Failed to start hardware: %d\n", ret); +		goto err_rxbuf_cleanup; +	} + +	/* +	 * Set mac address which is received in ready event +	 * FIXME: Move to ath6kl_interface_add() +	 */ +	memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN);  	return ret; +err_rxbuf_cleanup: +	ath6kl_htc_flush_rx_buf(ar->htc_target); +	ath6kl_cleanup_amsdu_rxbufs(ar); +	rtnl_lock(); +	ath6kl_deinit_if_data(netdev_priv(ndev)); +	rtnl_unlock(); +	wiphy_unregister(ar->wiphy); +err_debug_init: +	ath6kl_debug_cleanup(ar); +err_node_cleanup: +	ath6kl_wmi_shutdown(ar->wmi); +	clear_bit(WMI_ENABLED, &ar->flag); +	ar->wmi = NULL;  err_htc_cleanup:  	ath6kl_htc_cleanup(ar->htc_target); +err_power_off: +	ath6kl_hif_power_off(ar);  err_bmi_cleanup:  	ath6kl_bmi_cleanup(ar);  err_wq:  	destroy_workqueue(ar->ath6kl_wq); +  	return ret;  } -void ath6kl_stop_txrx(struct ath6kl *ar) +void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready)  { -	struct net_device *ndev = ar->net_dev; +	static u8 bcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; +	bool discon_issued; -	if (!ndev) -		return; +	netif_stop_queue(vif->ndev); -	set_bit(DESTROY_IN_PROGRESS, &ar->flag); +	clear_bit(WLAN_ENABLED, &vif->flags); -	if (down_interruptible(&ar->sem)) { -		ath6kl_err("down_interruptible failed\n"); -		return; -	} +	if (wmi_ready) { +		discon_issued = test_bit(CONNECTED, &vif->flags) || +				test_bit(CONNECT_PEND, &vif->flags); +		ath6kl_disconnect(vif); +		del_timer(&vif->disconnect_timer); -	if (ar->wlan_pwr_state != WLAN_POWER_STATE_CUT_PWR) -		ath6kl_stop_endpoint(ndev, false, true); +		if (discon_issued) +			ath6kl_disconnect_event(vif, DISCONNECT_CMD, +						(vif->nw_type & AP_NETWORK) ? +						bcast_mac : vif->bssid, +						0, NULL, 0); +	} -	clear_bit(WLAN_ENABLED, &ar->flag); +	if (vif->scan_req) { +		cfg80211_scan_done(vif->scan_req, true); +		vif->scan_req = NULL; +	}  } -/* - * We need to differentiate between the surprise and planned removal of the - * device because of the following consideration: - * - * - In case of surprise removal, the hcd already frees up the pending - *   for the device and hence there is no need to unregister the function - *   driver inorder to get these requests. For planned removal, the function - *   driver has to explicitly unregister itself to have the hcd return all the - *   pending requests before the data structures for the devices are freed up. - *   Note that as per the current implementation, the function driver will - *   end up releasing all the devices since there is no API to selectively - *   release a particular device. - * - * - Certain commands issued to the target can be skipped for surprise - *   removal since they will anyway not go through. - */ -void ath6kl_destroy(struct net_device *dev, unsigned int unregister) +void ath6kl_stop_txrx(struct ath6kl *ar)  { -	struct ath6kl *ar; +	struct ath6kl_vif *vif, *tmp_vif; + +	set_bit(DESTROY_IN_PROGRESS, &ar->flag); -	if (!dev || !ath6kl_priv(dev)) { -		ath6kl_err("failed to get device structure\n"); +	if (down_interruptible(&ar->sem)) { +		ath6kl_err("down_interruptible failed\n");  		return;  	} -	ar = ath6kl_priv(dev); - -	destroy_workqueue(ar->ath6kl_wq); - -	if (ar->htc_target) -		ath6kl_htc_cleanup(ar->htc_target); - -	aggr_module_destroy(ar->aggr_cntxt); - -	ath6kl_cookie_cleanup(ar); - -	ath6kl_cleanup_amsdu_rxbufs(ar); +	spin_lock_bh(&ar->list_lock); +	list_for_each_entry_safe(vif, tmp_vif, &ar->vif_list, list) { +		list_del(&vif->list); +		spin_unlock_bh(&ar->list_lock); +		ath6kl_cleanup_vif(vif, test_bit(WMI_READY, &ar->flag)); +		rtnl_lock(); +		ath6kl_deinit_if_data(vif); +		rtnl_unlock(); +		spin_lock_bh(&ar->list_lock); +	} +	spin_unlock_bh(&ar->list_lock); -	ath6kl_bmi_cleanup(ar); +	clear_bit(WMI_READY, &ar->flag); -	ath6kl_debug_cleanup(ar); +	/* +	 * After wmi_shudown all WMI events will be dropped. We +	 * need to cleanup the buffers allocated in AP mode and +	 * give disconnect notification to stack, which usually +	 * happens in the disconnect_event. Simulate the disconnect +	 * event by calling the function directly. Sometimes +	 * disconnect_event will be received when the debug logs +	 * are collected. +	 */ +	ath6kl_wmi_shutdown(ar->wmi); -	if (unregister && test_bit(NETDEV_REGISTERED, &ar->flag)) { -		unregister_netdev(dev); -		clear_bit(NETDEV_REGISTERED, &ar->flag); +	clear_bit(WMI_ENABLED, &ar->flag); +	if (ar->htc_target) { +		ath6kl_dbg(ATH6KL_DBG_TRC, "%s: shut down htc\n", __func__); +		ath6kl_htc_stop(ar->htc_target);  	} -	free_netdev(dev); - -	kfree(ar->fw_board); -	kfree(ar->fw_otp); -	kfree(ar->fw); -	kfree(ar->fw_patch); +	/* +	 * Try to reset the device if we can. The driver may have been +	 * configure NOT to reset the target during a debug session. +	 */ +	ath6kl_dbg(ATH6KL_DBG_TRC, +			"attempting to reset target on instance destroy\n"); +	ath6kl_reset_device(ar, ar->target_type, true, true); -	ath6kl_cfg80211_deinit(ar); +	clear_bit(WLAN_ENABLED, &ar->flag);  }  |