diff options
Diffstat (limited to 'drivers/net/wireless/ath/ath6kl/init.c')
| -rw-r--r-- | drivers/net/wireless/ath/ath6kl/init.c | 673 | 
1 files changed, 337 insertions, 336 deletions
diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index c1d2366704b..30050af9d4c 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,11 @@  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);  /*   * Include definitions here that can be used to tune the WLAN module @@ -73,37 +76,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 +107,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 +245,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 +300,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 +310,50 @@ 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;  		} +	/* +	 * FIXME: Make sure p2p configurations are not applied to +	 * non-p2p capable interfaces when multivif support is enabled. +	 */  	if (ar->p2p) { -		ret = ath6kl_wmi_info_req_cmd(ar->wmi, +		ret = ath6kl_wmi_info_req_cmd(ar->wmi, idx,  					      P2P_FLAG_CAPABILITIES_REQ |  					      P2P_FLAG_MACADDR_REQ |  					      P2P_FLAG_HMODEL_REQ); @@ -457,9 +365,13 @@ static int ath6kl_target_config_wlan_params(struct ath6kl *ar)  		}  	} +	/* +	 * FIXME: Make sure p2p configurations are not applied to +	 * non-p2p capable interfaces when multivif support is enabled. +	 */  	if (ar->p2p) {  		/* 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 +384,44 @@ 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; -	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 < MAX_NUM_VIF; 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 < MAX_NUM_VIF; i++) +		fw_submode |= HI_OPTION_FW_SUBMODE_P2PDEV << +			      (i * HI_OPTION_FW_SUBMODE_BITS); + +	/* +	 * FIXME: This needs to be removed once the multivif +	 * support is enabled. +	 */ +	if (ar->p2p) +		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 +442,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 |= (MAX_NUM_VIF << 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); @@ -553,68 +494,34 @@ int ath6kl_configure_target(struct ath6kl *ar)  	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 */ @@ -1182,6 +1089,7 @@ 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)) @@ -1210,15 +1118,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;  } @@ -1420,6 +1333,10 @@ static int ath6kl_init_hw_params(struct ath6kl *ar)  		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; + +		/* hw2.0 needs override address hardcoded */ +		ar->hw.app_start_override_addr = 0x944C00; +  		break;  	case AR6003_REV3_VERSION:  		ar->hw.dataset_patch_addr = AR6003_REV3_DATASET_PATCH_ADDRESS; @@ -1451,71 +1368,56 @@ static int ath6kl_init_hw_params(struct ath6kl *ar)  	return 0;  } -static int ath6kl_init(struct net_device *dev) +int ath6kl_init_hw_start(struct ath6kl *ar)  { -	struct ath6kl *ar = ath6kl_priv(dev); -	int status = 0; -	s32 timeleft; +	long timeleft; +	int ret, i; -	if (!ar) -		return -EIO; +	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; -	} - -	/* 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; +		ret = -EIO; +		goto err_power_off;  	} -	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 */ @@ -1529,51 +1431,69 @@ static int ath6kl_init(struct net_device *dev)  	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 < MAX_NUM_VIF; 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 +1503,226 @@ 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; +	} -	ret = ath6kl_init(ar->net_dev); +	ath6kl_dbg(ATH6KL_DBG_TRC, "%s: got wmi @ 0x%p.\n", __func__, ar->wmi); + +	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 < MAX_NUM_VIF; 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; + +	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); +	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_cleanup_amsdu_rxbufs(ar); +	clear_bit(WMI_READY, &ar->flag); -	ath6kl_bmi_cleanup(ar); - -	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);  }  |