xref: /linux/drivers/net/wireless/ath/ath9k/htc_drv_main.c (revision 87df89579a4a3e6c767603acb762115159655745)
1fb9987d0SSujith /*
2fb9987d0SSujith  * Copyright (c) 2010 Atheros Communications Inc.
3fb9987d0SSujith  *
4fb9987d0SSujith  * Permission to use, copy, modify, and/or distribute this software for any
5fb9987d0SSujith  * purpose with or without fee is hereby granted, provided that the above
6fb9987d0SSujith  * copyright notice and this permission notice appear in all copies.
7fb9987d0SSujith  *
8fb9987d0SSujith  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9fb9987d0SSujith  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10fb9987d0SSujith  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11fb9987d0SSujith  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12fb9987d0SSujith  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13fb9987d0SSujith  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14fb9987d0SSujith  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15fb9987d0SSujith  */
16fb9987d0SSujith 
17fb9987d0SSujith #include "htc.h"
18fb9987d0SSujith 
19fb9987d0SSujith #ifdef CONFIG_ATH9K_HTC_DEBUGFS
20fb9987d0SSujith static struct dentry *ath9k_debugfs_root;
21fb9987d0SSujith #endif
22fb9987d0SSujith 
23fb9987d0SSujith /*************/
24fb9987d0SSujith /* Utilities */
25fb9987d0SSujith /*************/
26fb9987d0SSujith 
27fb9987d0SSujith /* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
28fb9987d0SSujith static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
29fb9987d0SSujith 					      struct ath9k_channel *ichan)
30fb9987d0SSujith {
31fb9987d0SSujith 	enum htc_phymode mode;
32fb9987d0SSujith 
33fb9987d0SSujith 	mode = HTC_MODE_AUTO;
34fb9987d0SSujith 
35fb9987d0SSujith 	switch (ichan->chanmode) {
36fb9987d0SSujith 	case CHANNEL_G:
37fb9987d0SSujith 	case CHANNEL_G_HT20:
38fb9987d0SSujith 	case CHANNEL_G_HT40PLUS:
39fb9987d0SSujith 	case CHANNEL_G_HT40MINUS:
40fb9987d0SSujith 		mode = HTC_MODE_11NG;
41fb9987d0SSujith 		break;
42fb9987d0SSujith 	case CHANNEL_A:
43fb9987d0SSujith 	case CHANNEL_A_HT20:
44fb9987d0SSujith 	case CHANNEL_A_HT40PLUS:
45fb9987d0SSujith 	case CHANNEL_A_HT40MINUS:
46fb9987d0SSujith 		mode = HTC_MODE_11NA;
47fb9987d0SSujith 		break;
48fb9987d0SSujith 	default:
49fb9987d0SSujith 		break;
50fb9987d0SSujith 	}
51fb9987d0SSujith 
52fb9987d0SSujith 	return mode;
53fb9987d0SSujith }
54fb9987d0SSujith 
55f933ebedSSujith Manoharan bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
56bde748a4SVivek Natarajan 			enum ath9k_power_mode mode)
57bde748a4SVivek Natarajan {
58bde748a4SVivek Natarajan 	bool ret;
59bde748a4SVivek Natarajan 
60bde748a4SVivek Natarajan 	mutex_lock(&priv->htc_pm_lock);
61bde748a4SVivek Natarajan 	ret = ath9k_hw_setpower(priv->ah, mode);
62bde748a4SVivek Natarajan 	mutex_unlock(&priv->htc_pm_lock);
63bde748a4SVivek Natarajan 
64bde748a4SVivek Natarajan 	return ret;
65bde748a4SVivek Natarajan }
66bde748a4SVivek Natarajan 
67bde748a4SVivek Natarajan void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
68bde748a4SVivek Natarajan {
69bde748a4SVivek Natarajan 	mutex_lock(&priv->htc_pm_lock);
70bde748a4SVivek Natarajan 	if (++priv->ps_usecount != 1)
71bde748a4SVivek Natarajan 		goto unlock;
72bde748a4SVivek Natarajan 	ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
73bde748a4SVivek Natarajan 
74bde748a4SVivek Natarajan unlock:
75bde748a4SVivek Natarajan 	mutex_unlock(&priv->htc_pm_lock);
76bde748a4SVivek Natarajan }
77bde748a4SVivek Natarajan 
78bde748a4SVivek Natarajan void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
79bde748a4SVivek Natarajan {
80bde748a4SVivek Natarajan 	mutex_lock(&priv->htc_pm_lock);
81bde748a4SVivek Natarajan 	if (--priv->ps_usecount != 0)
82bde748a4SVivek Natarajan 		goto unlock;
83bde748a4SVivek Natarajan 
848a8572a8SVivek Natarajan 	if (priv->ps_idle)
858a8572a8SVivek Natarajan 		ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
868a8572a8SVivek Natarajan 	else if (priv->ps_enabled)
87bde748a4SVivek Natarajan 		ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
888a8572a8SVivek Natarajan 
89bde748a4SVivek Natarajan unlock:
90bde748a4SVivek Natarajan 	mutex_unlock(&priv->htc_pm_lock);
91bde748a4SVivek Natarajan }
92bde748a4SVivek Natarajan 
93bde748a4SVivek Natarajan void ath9k_ps_work(struct work_struct *work)
94bde748a4SVivek Natarajan {
95bde748a4SVivek Natarajan 	struct ath9k_htc_priv *priv =
96bde748a4SVivek Natarajan 		container_of(work, struct ath9k_htc_priv,
97bde748a4SVivek Natarajan 			     ps_work);
98bde748a4SVivek Natarajan 	ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
99bde748a4SVivek Natarajan 
100bde748a4SVivek Natarajan 	/* The chip wakes up after receiving the first beacon
101bde748a4SVivek Natarajan 	   while network sleep is enabled. For the driver to
102bde748a4SVivek Natarajan 	   be in sync with the hw, set the chip to awake and
103bde748a4SVivek Natarajan 	   only then set it to sleep.
104bde748a4SVivek Natarajan 	 */
105bde748a4SVivek Natarajan 	ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
106bde748a4SVivek Natarajan }
107bde748a4SVivek Natarajan 
1087c277349SSujith Manoharan static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
1097c277349SSujith Manoharan {
1107c277349SSujith Manoharan 	struct ath9k_htc_priv *priv = data;
1117c277349SSujith Manoharan 	struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
1127c277349SSujith Manoharan 
1137c277349SSujith Manoharan 	if (bss_conf->assoc) {
1147c277349SSujith Manoharan 		priv->rearm_ani = true;
1157c277349SSujith Manoharan 		priv->reconfig_beacon = true;
1167c277349SSujith Manoharan 	}
1177c277349SSujith Manoharan }
1187c277349SSujith Manoharan 
1197c277349SSujith Manoharan static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
1207c277349SSujith Manoharan {
1217c277349SSujith Manoharan 	priv->rearm_ani = false;
1227c277349SSujith Manoharan 	priv->reconfig_beacon = false;
1237c277349SSujith Manoharan 
1247c277349SSujith Manoharan 	ieee80211_iterate_active_interfaces_atomic(priv->hw,
1257c277349SSujith Manoharan 						   ath9k_htc_vif_iter, priv);
1267c277349SSujith Manoharan 	if (priv->rearm_ani)
1277c277349SSujith Manoharan 		ath_start_ani(priv);
1287c277349SSujith Manoharan 
1297c277349SSujith Manoharan 	if (priv->reconfig_beacon) {
1307c277349SSujith Manoharan 		ath9k_htc_ps_wakeup(priv);
1317c277349SSujith Manoharan 		ath9k_htc_beacon_reconfig(priv);
1327c277349SSujith Manoharan 		ath9k_htc_ps_restore(priv);
1337c277349SSujith Manoharan 	}
1347c277349SSujith Manoharan }
1357c277349SSujith Manoharan 
136585895cdSSujith Manoharan static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
137585895cdSSujith Manoharan {
138585895cdSSujith Manoharan 	struct ath9k_vif_iter_data *iter_data = data;
139585895cdSSujith Manoharan 	int i;
140585895cdSSujith Manoharan 
141585895cdSSujith Manoharan 	for (i = 0; i < ETH_ALEN; i++)
142585895cdSSujith Manoharan 		iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
143585895cdSSujith Manoharan }
144585895cdSSujith Manoharan 
145585895cdSSujith Manoharan static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
146585895cdSSujith Manoharan 				     struct ieee80211_vif *vif)
147585895cdSSujith Manoharan {
148585895cdSSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
149585895cdSSujith Manoharan 	struct ath9k_vif_iter_data iter_data;
150585895cdSSujith Manoharan 
151585895cdSSujith Manoharan 	/*
152585895cdSSujith Manoharan 	 * Use the hardware MAC address as reference, the hardware uses it
153585895cdSSujith Manoharan 	 * together with the BSSID mask when matching addresses.
154585895cdSSujith Manoharan 	 */
155585895cdSSujith Manoharan 	iter_data.hw_macaddr = common->macaddr;
156585895cdSSujith Manoharan 	memset(&iter_data.mask, 0xff, ETH_ALEN);
157585895cdSSujith Manoharan 
158585895cdSSujith Manoharan 	if (vif)
159585895cdSSujith Manoharan 		ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
160585895cdSSujith Manoharan 
161585895cdSSujith Manoharan 	/* Get list of all active MAC addresses */
162585895cdSSujith Manoharan 	ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
163585895cdSSujith Manoharan 						   &iter_data);
164585895cdSSujith Manoharan 
165585895cdSSujith Manoharan 	memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
166585895cdSSujith Manoharan 	ath_hw_setbssidmask(common);
167585895cdSSujith Manoharan }
168585895cdSSujith Manoharan 
16973908674SSujith Manoharan void ath9k_htc_reset(struct ath9k_htc_priv *priv)
17073908674SSujith Manoharan {
17173908674SSujith Manoharan 	struct ath_hw *ah = priv->ah;
17273908674SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(ah);
17373908674SSujith Manoharan 	struct ieee80211_channel *channel = priv->hw->conf.channel;
1744e3ae387SRajkumar Manoharan 	struct ath9k_hw_cal_data *caldata = NULL;
17573908674SSujith Manoharan 	enum htc_phymode mode;
17673908674SSujith Manoharan 	__be16 htc_mode;
17773908674SSujith Manoharan 	u8 cmd_rsp;
17873908674SSujith Manoharan 	int ret;
17973908674SSujith Manoharan 
18073908674SSujith Manoharan 	mutex_lock(&priv->mutex);
18173908674SSujith Manoharan 	ath9k_htc_ps_wakeup(priv);
18273908674SSujith Manoharan 
18373908674SSujith Manoharan 	cancel_delayed_work_sync(&priv->ath9k_ani_work);
18473908674SSujith Manoharan 	ieee80211_stop_queues(priv->hw);
18573908674SSujith Manoharan 	htc_stop(priv->htc);
18673908674SSujith Manoharan 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
18773908674SSujith Manoharan 	WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
18873908674SSujith Manoharan 	WMI_CMD(WMI_STOP_RECV_CMDID);
18973908674SSujith Manoharan 
1904e3ae387SRajkumar Manoharan 	caldata = &priv->caldata;
19173908674SSujith Manoharan 	ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
19273908674SSujith Manoharan 	if (ret) {
19373908674SSujith Manoharan 		ath_err(common,
19473908674SSujith Manoharan 			"Unable to reset device (%u Mhz) reset status %d\n",
19573908674SSujith Manoharan 			channel->center_freq, ret);
19673908674SSujith Manoharan 	}
19773908674SSujith Manoharan 
198b2a5c3dfSRajkumar Manoharan 	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
199b2a5c3dfSRajkumar Manoharan 			       &priv->curtxpow);
20073908674SSujith Manoharan 
20173908674SSujith Manoharan 	WMI_CMD(WMI_START_RECV_CMDID);
20273908674SSujith Manoharan 	ath9k_host_rx_init(priv);
20373908674SSujith Manoharan 
20473908674SSujith Manoharan 	mode = ath9k_htc_get_curmode(priv, ah->curchan);
20573908674SSujith Manoharan 	htc_mode = cpu_to_be16(mode);
20673908674SSujith Manoharan 	WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
20773908674SSujith Manoharan 
20873908674SSujith Manoharan 	WMI_CMD(WMI_ENABLE_INTR_CMDID);
20973908674SSujith Manoharan 	htc_start(priv->htc);
2107c277349SSujith Manoharan 	ath9k_htc_vif_reconfig(priv);
21173908674SSujith Manoharan 	ieee80211_wake_queues(priv->hw);
21273908674SSujith Manoharan 
21373908674SSujith Manoharan 	ath9k_htc_ps_restore(priv);
21473908674SSujith Manoharan 	mutex_unlock(&priv->mutex);
21573908674SSujith Manoharan }
21673908674SSujith Manoharan 
217fb9987d0SSujith static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
218fb9987d0SSujith 				 struct ieee80211_hw *hw,
219fb9987d0SSujith 				 struct ath9k_channel *hchan)
220fb9987d0SSujith {
221fb9987d0SSujith 	struct ath_hw *ah = priv->ah;
222fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
223fb9987d0SSujith 	struct ieee80211_conf *conf = &common->hw->conf;
224039a0721SSujith Manoharan 	bool fastcc;
225fb9987d0SSujith 	struct ieee80211_channel *channel = hw->conf.channel;
2268354dd3eSVivek Natarajan 	struct ath9k_hw_cal_data *caldata = NULL;
227fb9987d0SSujith 	enum htc_phymode mode;
2287f1f5a00SSujith 	__be16 htc_mode;
229fb9987d0SSujith 	u8 cmd_rsp;
230fb9987d0SSujith 	int ret;
231fb9987d0SSujith 
232fb9987d0SSujith 	if (priv->op_flags & OP_INVALID)
233fb9987d0SSujith 		return -EIO;
234fb9987d0SSujith 
235039a0721SSujith Manoharan 	fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
236fb9987d0SSujith 
237bde748a4SVivek Natarajan 	ath9k_htc_ps_wakeup(priv);
238fb9987d0SSujith 	htc_stop(priv->htc);
239fb9987d0SSujith 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
240fb9987d0SSujith 	WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
241fb9987d0SSujith 	WMI_CMD(WMI_STOP_RECV_CMDID);
242fb9987d0SSujith 
243226afe68SJoe Perches 	ath_dbg(common, ATH_DBG_CONFIG,
2447cf1f2ddSRajkumar Manoharan 		"(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
245fb9987d0SSujith 		priv->ah->curchan->channel,
2467cf1f2ddSRajkumar Manoharan 		channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
2477cf1f2ddSRajkumar Manoharan 		fastcc);
248fb9987d0SSujith 
2494e3ae387SRajkumar Manoharan 	if (!fastcc)
2504e3ae387SRajkumar Manoharan 		caldata = &priv->caldata;
25120bd2a09SFelix Fietkau 	ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
252fb9987d0SSujith 	if (ret) {
2533800276aSJoe Perches 		ath_err(common,
2543800276aSJoe Perches 			"Unable to reset channel (%u Mhz) reset status %d\n",
2553800276aSJoe Perches 			channel->center_freq, ret);
256fb9987d0SSujith 		goto err;
257fb9987d0SSujith 	}
258fb9987d0SSujith 
259b2a5c3dfSRajkumar Manoharan 	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
260b2a5c3dfSRajkumar Manoharan 			       &priv->curtxpow);
261fb9987d0SSujith 
262fb9987d0SSujith 	WMI_CMD(WMI_START_RECV_CMDID);
263fb9987d0SSujith 	if (ret)
264fb9987d0SSujith 		goto err;
265fb9987d0SSujith 
266fb9987d0SSujith 	ath9k_host_rx_init(priv);
267fb9987d0SSujith 
268fb9987d0SSujith 	mode = ath9k_htc_get_curmode(priv, hchan);
269fb9987d0SSujith 	htc_mode = cpu_to_be16(mode);
270fb9987d0SSujith 	WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
271fb9987d0SSujith 	if (ret)
272fb9987d0SSujith 		goto err;
273fb9987d0SSujith 
274fb9987d0SSujith 	WMI_CMD(WMI_ENABLE_INTR_CMDID);
275fb9987d0SSujith 	if (ret)
276fb9987d0SSujith 		goto err;
277fb9987d0SSujith 
278fb9987d0SSujith 	htc_start(priv->htc);
279fb9987d0SSujith err:
280bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
281fb9987d0SSujith 	return ret;
282fb9987d0SSujith }
283fb9987d0SSujith 
284a97b478cSSujith Manoharan /*
285a97b478cSSujith Manoharan  * Monitor mode handling is a tad complicated because the firmware requires
286a97b478cSSujith Manoharan  * an interface to be created exclusively, while mac80211 doesn't associate
287a97b478cSSujith Manoharan  * an interface with the mode.
288a97b478cSSujith Manoharan  *
289a97b478cSSujith Manoharan  * So, for now, only one monitor interface can be configured.
290a97b478cSSujith Manoharan  */
291cc721287SSujith Manoharan static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
29281fc2a33SRajkumar Manoharan {
29381fc2a33SRajkumar Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
29481fc2a33SRajkumar Manoharan 	struct ath9k_htc_target_vif hvif;
29581fc2a33SRajkumar Manoharan 	int ret = 0;
29681fc2a33SRajkumar Manoharan 	u8 cmd_rsp;
29781fc2a33SRajkumar Manoharan 
298cc721287SSujith Manoharan 	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
299cc721287SSujith Manoharan 	memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
300a97b478cSSujith Manoharan 	hvif.index = priv->mon_vif_idx;
301cc721287SSujith Manoharan 	WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
302cc721287SSujith Manoharan 	priv->nvifs--;
303a97b478cSSujith Manoharan 	priv->vif_slot &= ~(1 << priv->mon_vif_idx);
304cc721287SSujith Manoharan }
305cc721287SSujith Manoharan 
306cc721287SSujith Manoharan static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
307cc721287SSujith Manoharan {
308cc721287SSujith Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
309cc721287SSujith Manoharan 	struct ath9k_htc_target_vif hvif;
310cc721287SSujith Manoharan 	struct ath9k_htc_target_sta tsta;
311a97b478cSSujith Manoharan 	int ret = 0, sta_idx;
312cc721287SSujith Manoharan 	u8 cmd_rsp;
313cc721287SSujith Manoharan 
314a97b478cSSujith Manoharan 	if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
315a97b478cSSujith Manoharan 	    (priv->nstations >= ATH9K_HTC_MAX_STA)) {
316a97b478cSSujith Manoharan 		ret = -ENOBUFS;
317a97b478cSSujith Manoharan 		goto err_vif;
318a97b478cSSujith Manoharan 	}
31981fc2a33SRajkumar Manoharan 
320a97b478cSSujith Manoharan 	sta_idx = ffz(priv->sta_slot);
321a97b478cSSujith Manoharan 	if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
322a97b478cSSujith Manoharan 		ret = -ENOBUFS;
323a97b478cSSujith Manoharan 		goto err_vif;
324a97b478cSSujith Manoharan 	}
325cc721287SSujith Manoharan 
326cc721287SSujith Manoharan 	/*
327cc721287SSujith Manoharan 	 * Add an interface.
328cc721287SSujith Manoharan 	 */
32981fc2a33SRajkumar Manoharan 	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
33081fc2a33SRajkumar Manoharan 	memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
33181fc2a33SRajkumar Manoharan 
33281fc2a33SRajkumar Manoharan 	hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
333a97b478cSSujith Manoharan 	hvif.index = ffz(priv->vif_slot);
33481fc2a33SRajkumar Manoharan 
33581fc2a33SRajkumar Manoharan 	WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
33681fc2a33SRajkumar Manoharan 	if (ret)
337a97b478cSSujith Manoharan 		goto err_vif;
338a97b478cSSujith Manoharan 
339a97b478cSSujith Manoharan 	/*
340a97b478cSSujith Manoharan 	 * Assign the monitor interface index as a special case here.
341a97b478cSSujith Manoharan 	 * This is needed when the interface is brought down.
342a97b478cSSujith Manoharan 	 */
343a97b478cSSujith Manoharan 	priv->mon_vif_idx = hvif.index;
344a97b478cSSujith Manoharan 	priv->vif_slot |= (1 << hvif.index);
345a97b478cSSujith Manoharan 
346a97b478cSSujith Manoharan 	/*
347a97b478cSSujith Manoharan 	 * Set the hardware mode to monitor only if there are no
348a97b478cSSujith Manoharan 	 * other interfaces.
349a97b478cSSujith Manoharan 	 */
350a97b478cSSujith Manoharan 	if (!priv->nvifs)
351a97b478cSSujith Manoharan 		priv->ah->opmode = NL80211_IFTYPE_MONITOR;
35281fc2a33SRajkumar Manoharan 
35381fc2a33SRajkumar Manoharan 	priv->nvifs++;
354cc721287SSujith Manoharan 
355cc721287SSujith Manoharan 	/*
356cc721287SSujith Manoharan 	 * Associate a station with the interface for packet injection.
357cc721287SSujith Manoharan 	 */
358cc721287SSujith Manoharan 	memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
359cc721287SSujith Manoharan 
360cc721287SSujith Manoharan 	memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
361cc721287SSujith Manoharan 
362cc721287SSujith Manoharan 	tsta.is_vif_sta = 1;
363a97b478cSSujith Manoharan 	tsta.sta_index = sta_idx;
364cc721287SSujith Manoharan 	tsta.vif_index = hvif.index;
365cc721287SSujith Manoharan 	tsta.maxampdu = 0xffff;
366cc721287SSujith Manoharan 
367cc721287SSujith Manoharan 	WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
368cc721287SSujith Manoharan 	if (ret) {
369cc721287SSujith Manoharan 		ath_err(common, "Unable to add station entry for monitor mode\n");
370a97b478cSSujith Manoharan 		goto err_sta;
371cc721287SSujith Manoharan 	}
372cc721287SSujith Manoharan 
373a97b478cSSujith Manoharan 	priv->sta_slot |= (1 << sta_idx);
374cc721287SSujith Manoharan 	priv->nstations++;
375a97b478cSSujith Manoharan 	priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
37655de80d6SSujith Manoharan 	priv->ah->is_monitoring = true;
37755de80d6SSujith Manoharan 
378a97b478cSSujith Manoharan 	ath_dbg(common, ATH_DBG_CONFIG,
379a97b478cSSujith Manoharan 		"Attached a monitor interface at idx: %d, sta idx: %d\n",
380a97b478cSSujith Manoharan 		priv->mon_vif_idx, sta_idx);
381a97b478cSSujith Manoharan 
38281fc2a33SRajkumar Manoharan 	return 0;
383cc721287SSujith Manoharan 
384a97b478cSSujith Manoharan err_sta:
385cc721287SSujith Manoharan 	/*
386cc721287SSujith Manoharan 	 * Remove the interface from the target.
387cc721287SSujith Manoharan 	 */
388cc721287SSujith Manoharan 	__ath9k_htc_remove_monitor_interface(priv);
389a97b478cSSujith Manoharan err_vif:
390a97b478cSSujith Manoharan 	ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
391a97b478cSSujith Manoharan 
392cc721287SSujith Manoharan 	return ret;
39381fc2a33SRajkumar Manoharan }
39481fc2a33SRajkumar Manoharan 
39581fc2a33SRajkumar Manoharan static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
39681fc2a33SRajkumar Manoharan {
39781fc2a33SRajkumar Manoharan 	struct ath_common *common = ath9k_hw_common(priv->ah);
39881fc2a33SRajkumar Manoharan 	int ret = 0;
399cc721287SSujith Manoharan 	u8 cmd_rsp, sta_idx;
40081fc2a33SRajkumar Manoharan 
401cc721287SSujith Manoharan 	__ath9k_htc_remove_monitor_interface(priv);
40281fc2a33SRajkumar Manoharan 
403a97b478cSSujith Manoharan 	sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
404cc721287SSujith Manoharan 
405cc721287SSujith Manoharan 	WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
406cc721287SSujith Manoharan 	if (ret) {
407cc721287SSujith Manoharan 		ath_err(common, "Unable to remove station entry for monitor mode\n");
40881fc2a33SRajkumar Manoharan 		return ret;
40981fc2a33SRajkumar Manoharan 	}
41081fc2a33SRajkumar Manoharan 
411a97b478cSSujith Manoharan 	priv->sta_slot &= ~(1 << sta_idx);
412cc721287SSujith Manoharan 	priv->nstations--;
41355de80d6SSujith Manoharan 	priv->ah->is_monitoring = false;
414cc721287SSujith Manoharan 
415a97b478cSSujith Manoharan 	ath_dbg(common, ATH_DBG_CONFIG,
416a97b478cSSujith Manoharan 		"Removed a monitor interface at idx: %d, sta idx: %d\n",
417a97b478cSSujith Manoharan 		priv->mon_vif_idx, sta_idx);
418a97b478cSSujith Manoharan 
419cc721287SSujith Manoharan 	return 0;
420cc721287SSujith Manoharan }
421cc721287SSujith Manoharan 
422fb9987d0SSujith static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
423fb9987d0SSujith 				 struct ieee80211_vif *vif,
424fb9987d0SSujith 				 struct ieee80211_sta *sta)
425fb9987d0SSujith {
426fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
427fb9987d0SSujith 	struct ath9k_htc_target_sta tsta;
428fb9987d0SSujith 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
429fb9987d0SSujith 	struct ath9k_htc_sta *ista;
430a97b478cSSujith Manoharan 	int ret, sta_idx;
431fb9987d0SSujith 	u8 cmd_rsp;
432fb9987d0SSujith 
433fb9987d0SSujith 	if (priv->nstations >= ATH9K_HTC_MAX_STA)
434fb9987d0SSujith 		return -ENOBUFS;
435fb9987d0SSujith 
436a97b478cSSujith Manoharan 	sta_idx = ffz(priv->sta_slot);
437a97b478cSSujith Manoharan 	if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
438a97b478cSSujith Manoharan 		return -ENOBUFS;
439a97b478cSSujith Manoharan 
440fb9987d0SSujith 	memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
441fb9987d0SSujith 
442fb9987d0SSujith 	if (sta) {
443fb9987d0SSujith 		ista = (struct ath9k_htc_sta *) sta->drv_priv;
444fb9987d0SSujith 		memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
445fb9987d0SSujith 		memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
446fb9987d0SSujith 		tsta.associd = common->curaid;
447fb9987d0SSujith 		tsta.is_vif_sta = 0;
448fb9987d0SSujith 		tsta.valid = true;
449a97b478cSSujith Manoharan 		ista->index = sta_idx;
450fb9987d0SSujith 	} else {
451fb9987d0SSujith 		memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
452fb9987d0SSujith 		tsta.is_vif_sta = 1;
453fb9987d0SSujith 	}
454fb9987d0SSujith 
455a97b478cSSujith Manoharan 	tsta.sta_index = sta_idx;
456fb9987d0SSujith 	tsta.vif_index = avp->index;
457fb9987d0SSujith 	tsta.maxampdu = 0xffff;
458fb9987d0SSujith 	if (sta && sta->ht_cap.ht_supported)
459fb9987d0SSujith 		tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
460fb9987d0SSujith 
461fb9987d0SSujith 	WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
462fb9987d0SSujith 	if (ret) {
463fb9987d0SSujith 		if (sta)
4643800276aSJoe Perches 			ath_err(common,
4653800276aSJoe Perches 				"Unable to add station entry for: %pM\n",
4663800276aSJoe Perches 				sta->addr);
467fb9987d0SSujith 		return ret;
468fb9987d0SSujith 	}
469fb9987d0SSujith 
470a97b478cSSujith Manoharan 	if (sta) {
471226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
472fb9987d0SSujith 			"Added a station entry for: %pM (idx: %d)\n",
473fb9987d0SSujith 			sta->addr, tsta.sta_index);
474a97b478cSSujith Manoharan 	} else {
475a97b478cSSujith Manoharan 		ath_dbg(common, ATH_DBG_CONFIG,
476a97b478cSSujith Manoharan 			"Added a station entry for VIF %d (idx: %d)\n",
477a97b478cSSujith Manoharan 			avp->index, tsta.sta_index);
478a97b478cSSujith Manoharan 	}
479fb9987d0SSujith 
480a97b478cSSujith Manoharan 	priv->sta_slot |= (1 << sta_idx);
481fb9987d0SSujith 	priv->nstations++;
482a97b478cSSujith Manoharan 	if (!sta)
483a97b478cSSujith Manoharan 		priv->vif_sta_pos[avp->index] = sta_idx;
484a97b478cSSujith Manoharan 
485fb9987d0SSujith 	return 0;
486fb9987d0SSujith }
487fb9987d0SSujith 
488fb9987d0SSujith static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
489fb9987d0SSujith 				    struct ieee80211_vif *vif,
490fb9987d0SSujith 				    struct ieee80211_sta *sta)
491fb9987d0SSujith {
492fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
493a97b478cSSujith Manoharan 	struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
494fb9987d0SSujith 	struct ath9k_htc_sta *ista;
495fb9987d0SSujith 	int ret;
496fb9987d0SSujith 	u8 cmd_rsp, sta_idx;
497fb9987d0SSujith 
498fb9987d0SSujith 	if (sta) {
499fb9987d0SSujith 		ista = (struct ath9k_htc_sta *) sta->drv_priv;
500fb9987d0SSujith 		sta_idx = ista->index;
501fb9987d0SSujith 	} else {
502a97b478cSSujith Manoharan 		sta_idx = priv->vif_sta_pos[avp->index];
503fb9987d0SSujith 	}
504fb9987d0SSujith 
505fb9987d0SSujith 	WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
506fb9987d0SSujith 	if (ret) {
507fb9987d0SSujith 		if (sta)
5083800276aSJoe Perches 			ath_err(common,
509fb9987d0SSujith 				"Unable to remove station entry for: %pM\n",
510fb9987d0SSujith 				sta->addr);
511fb9987d0SSujith 		return ret;
512fb9987d0SSujith 	}
513fb9987d0SSujith 
514a97b478cSSujith Manoharan 	if (sta) {
515226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
516fb9987d0SSujith 			"Removed a station entry for: %pM (idx: %d)\n",
517fb9987d0SSujith 			sta->addr, sta_idx);
518a97b478cSSujith Manoharan 	} else {
519a97b478cSSujith Manoharan 		ath_dbg(common, ATH_DBG_CONFIG,
520a97b478cSSujith Manoharan 			"Removed a station entry for VIF %d (idx: %d)\n",
521a97b478cSSujith Manoharan 			avp->index, sta_idx);
522a97b478cSSujith Manoharan 	}
523fb9987d0SSujith 
524a97b478cSSujith Manoharan 	priv->sta_slot &= ~(1 << sta_idx);
525fb9987d0SSujith 	priv->nstations--;
526a97b478cSSujith Manoharan 
527fb9987d0SSujith 	return 0;
528fb9987d0SSujith }
529fb9987d0SSujith 
53055de80d6SSujith Manoharan int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
531fb9987d0SSujith {
532fb9987d0SSujith 	struct ath9k_htc_cap_target tcap;
533fb9987d0SSujith 	int ret;
534fb9987d0SSujith 	u8 cmd_rsp;
535fb9987d0SSujith 
536fb9987d0SSujith 	memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
537fb9987d0SSujith 
538fb9987d0SSujith 	/* FIXME: Values are hardcoded */
539fb9987d0SSujith 	tcap.flags = 0x240c40;
540fb9987d0SSujith 	tcap.flags_ext = 0x80601000;
541fb9987d0SSujith 	tcap.ampdu_limit = 0xffff0000;
542fb9987d0SSujith 	tcap.ampdu_subframes = 20;
54329d9075eSSujith 	tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
544fb9987d0SSujith 	tcap.protmode = 1;
54529d9075eSSujith 	tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
546fb9987d0SSujith 
547fb9987d0SSujith 	WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
548fb9987d0SSujith 
549fb9987d0SSujith 	return ret;
550fb9987d0SSujith }
551fb9987d0SSujith 
5520d425a7dSSujith static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
5530d425a7dSSujith 				 struct ieee80211_sta *sta,
5540d425a7dSSujith 				 struct ath9k_htc_target_rate *trate)
555fb9987d0SSujith {
556fb9987d0SSujith 	struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
557fb9987d0SSujith 	struct ieee80211_supported_band *sband;
558fb9987d0SSujith 	u32 caps = 0;
5590d425a7dSSujith 	int i, j;
560fb9987d0SSujith 
561ea46e644SSujith 	sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
562fb9987d0SSujith 
563fb9987d0SSujith 	for (i = 0, j = 0; i < sband->n_bitrates; i++) {
564fb9987d0SSujith 		if (sta->supp_rates[sband->band] & BIT(i)) {
5650d425a7dSSujith 			trate->rates.legacy_rates.rs_rates[j]
566fb9987d0SSujith 				= (sband->bitrates[i].bitrate * 2) / 10;
567fb9987d0SSujith 			j++;
568fb9987d0SSujith 		}
569fb9987d0SSujith 	}
5700d425a7dSSujith 	trate->rates.legacy_rates.rs_nrates = j;
571fb9987d0SSujith 
572fb9987d0SSujith 	if (sta->ht_cap.ht_supported) {
573fb9987d0SSujith 		for (i = 0, j = 0; i < 77; i++) {
574fb9987d0SSujith 			if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
5750d425a7dSSujith 				trate->rates.ht_rates.rs_rates[j++] = i;
576fb9987d0SSujith 			if (j == ATH_HTC_RATE_MAX)
577fb9987d0SSujith 				break;
578fb9987d0SSujith 		}
5790d425a7dSSujith 		trate->rates.ht_rates.rs_nrates = j;
580fb9987d0SSujith 
581fb9987d0SSujith 		caps = WLAN_RC_HT_FLAG;
58229d9075eSSujith 		if (sta->ht_cap.mcs.rx_mask[1])
58329d9075eSSujith 			caps |= WLAN_RC_DS_FLAG;
58471ba186cSVivek Natarajan 		if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
58571ba186cSVivek Natarajan 		     (conf_is_ht40(&priv->hw->conf)))
586fb9987d0SSujith 			caps |= WLAN_RC_40_FLAG;
587b4dec5e8SSujith 		if (conf_is_ht40(&priv->hw->conf) &&
588b4dec5e8SSujith 		    (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
589fb9987d0SSujith 			caps |= WLAN_RC_SGI_FLAG;
590b4dec5e8SSujith 		else if (conf_is_ht20(&priv->hw->conf) &&
591b4dec5e8SSujith 			 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
592b4dec5e8SSujith 			caps |= WLAN_RC_SGI_FLAG;
593fb9987d0SSujith 	}
594fb9987d0SSujith 
5950d425a7dSSujith 	trate->sta_index = ista->index;
5960d425a7dSSujith 	trate->isnew = 1;
5970d425a7dSSujith 	trate->capflags = cpu_to_be32(caps);
5980d425a7dSSujith }
599fb9987d0SSujith 
6000d425a7dSSujith static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
6010d425a7dSSujith 				    struct ath9k_htc_target_rate *trate)
6020d425a7dSSujith {
6030d425a7dSSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
6040d425a7dSSujith 	int ret;
6050d425a7dSSujith 	u8 cmd_rsp;
6060d425a7dSSujith 
6070d425a7dSSujith 	WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
608fb9987d0SSujith 	if (ret) {
6093800276aSJoe Perches 		ath_err(common,
610fb9987d0SSujith 			"Unable to initialize Rate information on target\n");
6110d425a7dSSujith 	}
6120d425a7dSSujith 
613fb9987d0SSujith 	return ret;
614fb9987d0SSujith }
615fb9987d0SSujith 
6160d425a7dSSujith static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
6170d425a7dSSujith 				struct ieee80211_sta *sta)
618fb9987d0SSujith {
619fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
6200d425a7dSSujith 	struct ath9k_htc_target_rate trate;
621fb9987d0SSujith 	int ret;
622fb9987d0SSujith 
6230d425a7dSSujith 	memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
6240d425a7dSSujith 	ath9k_htc_setup_rate(priv, sta, &trate);
6250d425a7dSSujith 	ret = ath9k_htc_send_rate_cmd(priv, &trate);
6260d425a7dSSujith 	if (!ret)
627226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
6280d425a7dSSujith 			"Updated target sta: %pM, rate caps: 0x%X\n",
6290d425a7dSSujith 			sta->addr, be32_to_cpu(trate.capflags));
630fb9987d0SSujith }
631fb9987d0SSujith 
6322c76ef89SSujith static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
6332c76ef89SSujith 				  struct ieee80211_vif *vif,
6342c76ef89SSujith 				  struct ieee80211_bss_conf *bss_conf)
6352c76ef89SSujith {
6362c76ef89SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
6372c76ef89SSujith 	struct ath9k_htc_target_rate trate;
6382c76ef89SSujith 	struct ieee80211_sta *sta;
6392c76ef89SSujith 	int ret;
6402c76ef89SSujith 
6412c76ef89SSujith 	memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
6422c76ef89SSujith 
6432c76ef89SSujith 	rcu_read_lock();
6442c76ef89SSujith 	sta = ieee80211_find_sta(vif, bss_conf->bssid);
6452c76ef89SSujith 	if (!sta) {
6462c76ef89SSujith 		rcu_read_unlock();
6472c76ef89SSujith 		return;
6482c76ef89SSujith 	}
6492c76ef89SSujith 	ath9k_htc_setup_rate(priv, sta, &trate);
6502c76ef89SSujith 	rcu_read_unlock();
6512c76ef89SSujith 
6522c76ef89SSujith 	ret = ath9k_htc_send_rate_cmd(priv, &trate);
6532c76ef89SSujith 	if (!ret)
654226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
6552c76ef89SSujith 			"Updated target sta: %pM, rate caps: 0x%X\n",
6562c76ef89SSujith 			bss_conf->bssid, be32_to_cpu(trate.capflags));
6572c76ef89SSujith }
6582c76ef89SSujith 
6599edd9520SLuis R. Rodriguez static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
660fb9987d0SSujith 				  struct ieee80211_vif *vif,
661d7ca2139SSujith 				  struct ieee80211_sta *sta,
6629edd9520SLuis R. Rodriguez 				  enum ieee80211_ampdu_mlme_action action,
6639edd9520SLuis R. Rodriguez 				  u16 tid)
664fb9987d0SSujith {
665fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
666fb9987d0SSujith 	struct ath9k_htc_target_aggr aggr;
667277a64d1SDan Carpenter 	struct ath9k_htc_sta *ista;
668fb9987d0SSujith 	int ret = 0;
669fb9987d0SSujith 	u8 cmd_rsp;
670fb9987d0SSujith 
6710730d114SDan Carpenter 	if (tid >= ATH9K_HTC_MAX_TID)
672fb9987d0SSujith 		return -EINVAL;
673fb9987d0SSujith 
674fb9987d0SSujith 	memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
675ef98c3cdSSujith 	ista = (struct ath9k_htc_sta *) sta->drv_priv;
676fb9987d0SSujith 
677ef98c3cdSSujith 	aggr.sta_index = ista->index;
678d7ca2139SSujith 	aggr.tidno = tid & 0xf;
679d7ca2139SSujith 	aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
680ef98c3cdSSujith 
681fb9987d0SSujith 	WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
682fb9987d0SSujith 	if (ret)
683226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
684fb9987d0SSujith 			"Unable to %s TX aggregation for (%pM, %d)\n",
685d7ca2139SSujith 			(aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
686fb9987d0SSujith 	else
687226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
688d7ca2139SSujith 			"%s TX aggregation for (%pM, %d)\n",
689d7ca2139SSujith 			(aggr.aggr_enable) ? "Starting" : "Stopping",
690d7ca2139SSujith 			sta->addr, tid);
691d7ca2139SSujith 
692d7ca2139SSujith 	spin_lock_bh(&priv->tx_lock);
693d7ca2139SSujith 	ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
694d7ca2139SSujith 	spin_unlock_bh(&priv->tx_lock);
695fb9987d0SSujith 
696fb9987d0SSujith 	return ret;
697fb9987d0SSujith }
698fb9987d0SSujith 
699fb9987d0SSujith /*********/
700fb9987d0SSujith /* DEBUG */
701fb9987d0SSujith /*********/
702fb9987d0SSujith 
703fb9987d0SSujith #ifdef CONFIG_ATH9K_HTC_DEBUGFS
704fb9987d0SSujith 
705fb9987d0SSujith static int ath9k_debugfs_open(struct inode *inode, struct file *file)
706fb9987d0SSujith {
707fb9987d0SSujith 	file->private_data = inode->i_private;
708fb9987d0SSujith 	return 0;
709fb9987d0SSujith }
710fb9987d0SSujith 
711fb9987d0SSujith static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
712fb9987d0SSujith 				   size_t count, loff_t *ppos)
713fb9987d0SSujith {
71457674308SJoe Perches 	struct ath9k_htc_priv *priv = file->private_data;
715fb9987d0SSujith 	struct ath9k_htc_target_stats cmd_rsp;
716fb9987d0SSujith 	char buf[512];
717fb9987d0SSujith 	unsigned int len = 0;
718fb9987d0SSujith 	int ret = 0;
719fb9987d0SSujith 
720fb9987d0SSujith 	memset(&cmd_rsp, 0, sizeof(cmd_rsp));
721fb9987d0SSujith 
722fb9987d0SSujith 	WMI_CMD(WMI_TGT_STATS_CMDID);
723fb9987d0SSujith 	if (ret)
724fb9987d0SSujith 		return -EINVAL;
725fb9987d0SSujith 
726fb9987d0SSujith 
727fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
728fb9987d0SSujith 			"%19s : %10u\n", "TX Short Retries",
729fb9987d0SSujith 			be32_to_cpu(cmd_rsp.tx_shortretry));
730fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
731fb9987d0SSujith 			"%19s : %10u\n", "TX Long Retries",
732fb9987d0SSujith 			be32_to_cpu(cmd_rsp.tx_longretry));
733fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
734fb9987d0SSujith 			"%19s : %10u\n", "TX Xretries",
735fb9987d0SSujith 			be32_to_cpu(cmd_rsp.tx_xretries));
736fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
737fb9987d0SSujith 			"%19s : %10u\n", "TX Unaggr. Xretries",
738fb9987d0SSujith 			be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
739fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
740fb9987d0SSujith 			"%19s : %10u\n", "TX Xretries (HT)",
741fb9987d0SSujith 			be32_to_cpu(cmd_rsp.ht_tx_xretries));
742fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
743fb9987d0SSujith 			"%19s : %10u\n", "TX Rate", priv->debug.txrate);
744fb9987d0SSujith 
7459746010bSDan Carpenter 	if (len > sizeof(buf))
7469746010bSDan Carpenter 		len = sizeof(buf);
7479746010bSDan Carpenter 
748fb9987d0SSujith 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
749fb9987d0SSujith }
750fb9987d0SSujith 
751fb9987d0SSujith static const struct file_operations fops_tgt_stats = {
752fb9987d0SSujith 	.read = read_file_tgt_stats,
753fb9987d0SSujith 	.open = ath9k_debugfs_open,
7546038f373SArnd Bergmann 	.owner = THIS_MODULE,
7556038f373SArnd Bergmann 	.llseek = default_llseek,
756fb9987d0SSujith };
757fb9987d0SSujith 
758fb9987d0SSujith static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
759fb9987d0SSujith 			      size_t count, loff_t *ppos)
760fb9987d0SSujith {
76157674308SJoe Perches 	struct ath9k_htc_priv *priv = file->private_data;
762fb9987d0SSujith 	char buf[512];
763fb9987d0SSujith 	unsigned int len = 0;
764fb9987d0SSujith 
765fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
766fb9987d0SSujith 			"%20s : %10u\n", "Buffers queued",
767fb9987d0SSujith 			priv->debug.tx_stats.buf_queued);
768fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
769fb9987d0SSujith 			"%20s : %10u\n", "Buffers completed",
770fb9987d0SSujith 			priv->debug.tx_stats.buf_completed);
771fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
772fb9987d0SSujith 			"%20s : %10u\n", "SKBs queued",
773fb9987d0SSujith 			priv->debug.tx_stats.skb_queued);
774fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
775fb9987d0SSujith 			"%20s : %10u\n", "SKBs completed",
776fb9987d0SSujith 			priv->debug.tx_stats.skb_completed);
777eac8e385SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
778eac8e385SSujith 			"%20s : %10u\n", "SKBs dropped",
779eac8e385SSujith 			priv->debug.tx_stats.skb_dropped);
780fb9987d0SSujith 
7812edb4583SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
7822edb4583SSujith 			"%20s : %10u\n", "BE queued",
7832edb4583SSujith 			priv->debug.tx_stats.queue_stats[WME_AC_BE]);
7842edb4583SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
7852edb4583SSujith 			"%20s : %10u\n", "BK queued",
7862edb4583SSujith 			priv->debug.tx_stats.queue_stats[WME_AC_BK]);
7872edb4583SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
7882edb4583SSujith 			"%20s : %10u\n", "VI queued",
7892edb4583SSujith 			priv->debug.tx_stats.queue_stats[WME_AC_VI]);
7902edb4583SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
7912edb4583SSujith 			"%20s : %10u\n", "VO queued",
7922edb4583SSujith 			priv->debug.tx_stats.queue_stats[WME_AC_VO]);
7932edb4583SSujith 
7949746010bSDan Carpenter 	if (len > sizeof(buf))
7959746010bSDan Carpenter 		len = sizeof(buf);
7969746010bSDan Carpenter 
797fb9987d0SSujith 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
798fb9987d0SSujith }
799fb9987d0SSujith 
800fb9987d0SSujith static const struct file_operations fops_xmit = {
801fb9987d0SSujith 	.read = read_file_xmit,
802fb9987d0SSujith 	.open = ath9k_debugfs_open,
8036038f373SArnd Bergmann 	.owner = THIS_MODULE,
8046038f373SArnd Bergmann 	.llseek = default_llseek,
805fb9987d0SSujith };
806fb9987d0SSujith 
807fb9987d0SSujith static ssize_t read_file_recv(struct file *file, char __user *user_buf,
808fb9987d0SSujith 			      size_t count, loff_t *ppos)
809fb9987d0SSujith {
81057674308SJoe Perches 	struct ath9k_htc_priv *priv = file->private_data;
811fb9987d0SSujith 	char buf[512];
812fb9987d0SSujith 	unsigned int len = 0;
813fb9987d0SSujith 
814fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
815fb9987d0SSujith 			"%20s : %10u\n", "SKBs allocated",
816fb9987d0SSujith 			priv->debug.rx_stats.skb_allocated);
817fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
818fb9987d0SSujith 			"%20s : %10u\n", "SKBs completed",
819fb9987d0SSujith 			priv->debug.rx_stats.skb_completed);
820fb9987d0SSujith 	len += snprintf(buf + len, sizeof(buf) - len,
821fb9987d0SSujith 			"%20s : %10u\n", "SKBs Dropped",
822fb9987d0SSujith 			priv->debug.rx_stats.skb_dropped);
823fb9987d0SSujith 
8249746010bSDan Carpenter 	if (len > sizeof(buf))
8259746010bSDan Carpenter 		len = sizeof(buf);
8269746010bSDan Carpenter 
827fb9987d0SSujith 	return simple_read_from_buffer(user_buf, count, ppos, buf, len);
828fb9987d0SSujith }
829fb9987d0SSujith 
830fb9987d0SSujith static const struct file_operations fops_recv = {
831fb9987d0SSujith 	.read = read_file_recv,
832fb9987d0SSujith 	.open = ath9k_debugfs_open,
8336038f373SArnd Bergmann 	.owner = THIS_MODULE,
8346038f373SArnd Bergmann 	.llseek = default_llseek,
835fb9987d0SSujith };
836fb9987d0SSujith 
837e1572c5eSSujith int ath9k_htc_init_debug(struct ath_hw *ah)
838fb9987d0SSujith {
839fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
840fb9987d0SSujith 	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
841fb9987d0SSujith 
842fb9987d0SSujith 	if (!ath9k_debugfs_root)
843fb9987d0SSujith 		return -ENOENT;
844fb9987d0SSujith 
845fb9987d0SSujith 	priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
846fb9987d0SSujith 						     ath9k_debugfs_root);
847fb9987d0SSujith 	if (!priv->debug.debugfs_phy)
848fb9987d0SSujith 		goto err;
849fb9987d0SSujith 
850fb9987d0SSujith 	priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
851fb9987d0SSujith 						    priv->debug.debugfs_phy,
852fb9987d0SSujith 						    priv, &fops_tgt_stats);
853fb9987d0SSujith 	if (!priv->debug.debugfs_tgt_stats)
854fb9987d0SSujith 		goto err;
855fb9987d0SSujith 
856fb9987d0SSujith 
857fb9987d0SSujith 	priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
858fb9987d0SSujith 						       priv->debug.debugfs_phy,
859fb9987d0SSujith 						       priv, &fops_xmit);
860fb9987d0SSujith 	if (!priv->debug.debugfs_xmit)
861fb9987d0SSujith 		goto err;
862fb9987d0SSujith 
863fb9987d0SSujith 	priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
864fb9987d0SSujith 						       priv->debug.debugfs_phy,
865fb9987d0SSujith 						       priv, &fops_recv);
866fb9987d0SSujith 	if (!priv->debug.debugfs_recv)
867fb9987d0SSujith 		goto err;
868fb9987d0SSujith 
869fb9987d0SSujith 	return 0;
870fb9987d0SSujith 
871fb9987d0SSujith err:
872e1572c5eSSujith 	ath9k_htc_exit_debug(ah);
873fb9987d0SSujith 	return -ENOMEM;
874fb9987d0SSujith }
875fb9987d0SSujith 
876e1572c5eSSujith void ath9k_htc_exit_debug(struct ath_hw *ah)
877fb9987d0SSujith {
878fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
879fb9987d0SSujith 	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
880fb9987d0SSujith 
881fb9987d0SSujith 	debugfs_remove(priv->debug.debugfs_recv);
882fb9987d0SSujith 	debugfs_remove(priv->debug.debugfs_xmit);
883fb9987d0SSujith 	debugfs_remove(priv->debug.debugfs_tgt_stats);
884fb9987d0SSujith 	debugfs_remove(priv->debug.debugfs_phy);
885fb9987d0SSujith }
886fb9987d0SSujith 
887e1572c5eSSujith int ath9k_htc_debug_create_root(void)
888fb9987d0SSujith {
889fb9987d0SSujith 	ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
890fb9987d0SSujith 	if (!ath9k_debugfs_root)
891fb9987d0SSujith 		return -ENOENT;
892fb9987d0SSujith 
893fb9987d0SSujith 	return 0;
894fb9987d0SSujith }
895fb9987d0SSujith 
896e1572c5eSSujith void ath9k_htc_debug_remove_root(void)
897fb9987d0SSujith {
898fb9987d0SSujith 	debugfs_remove(ath9k_debugfs_root);
899fb9987d0SSujith 	ath9k_debugfs_root = NULL;
900fb9987d0SSujith }
901fb9987d0SSujith 
902fb9987d0SSujith #endif /* CONFIG_ATH9K_HTC_DEBUGFS */
903fb9987d0SSujith 
904fb9987d0SSujith /*******/
905fb9987d0SSujith /* ANI */
906fb9987d0SSujith /*******/
907fb9987d0SSujith 
90873908674SSujith Manoharan void ath_start_ani(struct ath9k_htc_priv *priv)
909fb9987d0SSujith {
910fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
911fb9987d0SSujith 	unsigned long timestamp = jiffies_to_msecs(jiffies);
912fb9987d0SSujith 
913fb9987d0SSujith 	common->ani.longcal_timer = timestamp;
914fb9987d0SSujith 	common->ani.shortcal_timer = timestamp;
915fb9987d0SSujith 	common->ani.checkani_timer = timestamp;
916fb9987d0SSujith 
917fb9987d0SSujith 	ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
918fb9987d0SSujith 				     msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
919fb9987d0SSujith }
920fb9987d0SSujith 
921fb9987d0SSujith void ath9k_ani_work(struct work_struct *work)
922fb9987d0SSujith {
923fb9987d0SSujith 	struct ath9k_htc_priv *priv =
924fb9987d0SSujith 		container_of(work, struct ath9k_htc_priv,
925fb9987d0SSujith 			     ath9k_ani_work.work);
926fb9987d0SSujith 	struct ath_hw *ah = priv->ah;
927fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
928fb9987d0SSujith 	bool longcal = false;
929fb9987d0SSujith 	bool shortcal = false;
930fb9987d0SSujith 	bool aniflag = false;
931fb9987d0SSujith 	unsigned int timestamp = jiffies_to_msecs(jiffies);
932fb9987d0SSujith 	u32 cal_interval, short_cal_interval;
933fb9987d0SSujith 
934fb9987d0SSujith 	short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
935fb9987d0SSujith 
936bde748a4SVivek Natarajan 	/* Only calibrate if awake */
937bde748a4SVivek Natarajan 	if (ah->power_mode != ATH9K_PM_AWAKE)
938bde748a4SVivek Natarajan 		goto set_timer;
939bde748a4SVivek Natarajan 
940fb9987d0SSujith 	/* Long calibration runs independently of short calibration. */
941fb9987d0SSujith 	if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
942fb9987d0SSujith 		longcal = true;
943226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
944fb9987d0SSujith 		common->ani.longcal_timer = timestamp;
945fb9987d0SSujith 	}
946fb9987d0SSujith 
947fb9987d0SSujith 	/* Short calibration applies only while caldone is false */
948fb9987d0SSujith 	if (!common->ani.caldone) {
949fb9987d0SSujith 		if ((timestamp - common->ani.shortcal_timer) >=
950fb9987d0SSujith 		    short_cal_interval) {
951fb9987d0SSujith 			shortcal = true;
952226afe68SJoe Perches 			ath_dbg(common, ATH_DBG_ANI,
953fb9987d0SSujith 				"shortcal @%lu\n", jiffies);
954fb9987d0SSujith 			common->ani.shortcal_timer = timestamp;
955fb9987d0SSujith 			common->ani.resetcal_timer = timestamp;
956fb9987d0SSujith 		}
957fb9987d0SSujith 	} else {
958fb9987d0SSujith 		if ((timestamp - common->ani.resetcal_timer) >=
959fb9987d0SSujith 		    ATH_RESTART_CALINTERVAL) {
960fb9987d0SSujith 			common->ani.caldone = ath9k_hw_reset_calvalid(ah);
961fb9987d0SSujith 			if (common->ani.caldone)
962fb9987d0SSujith 				common->ani.resetcal_timer = timestamp;
963fb9987d0SSujith 		}
964fb9987d0SSujith 	}
965fb9987d0SSujith 
966fb9987d0SSujith 	/* Verify whether we must check ANI */
967fb9987d0SSujith 	if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
968fb9987d0SSujith 		aniflag = true;
969fb9987d0SSujith 		common->ani.checkani_timer = timestamp;
970fb9987d0SSujith 	}
971fb9987d0SSujith 
972fb9987d0SSujith 	/* Skip all processing if there's nothing to do. */
973fb9987d0SSujith 	if (longcal || shortcal || aniflag) {
974bde748a4SVivek Natarajan 
975bde748a4SVivek Natarajan 		ath9k_htc_ps_wakeup(priv);
976bde748a4SVivek Natarajan 
977fb9987d0SSujith 		/* Call ANI routine if necessary */
978fb9987d0SSujith 		if (aniflag)
979fb9987d0SSujith 			ath9k_hw_ani_monitor(ah, ah->curchan);
980fb9987d0SSujith 
981fb9987d0SSujith 		/* Perform calibration if necessary */
98235ecfe03SFelix Fietkau 		if (longcal || shortcal)
983fb9987d0SSujith 			common->ani.caldone =
984fb9987d0SSujith 				ath9k_hw_calibrate(ah, ah->curchan,
985fb9987d0SSujith 						   common->rx_chainmask,
986fb9987d0SSujith 						   longcal);
987fb9987d0SSujith 
988bde748a4SVivek Natarajan 		ath9k_htc_ps_restore(priv);
989fb9987d0SSujith 	}
990fb9987d0SSujith 
991bde748a4SVivek Natarajan set_timer:
992fb9987d0SSujith 	/*
993fb9987d0SSujith 	* Set timer interval based on previous results.
994fb9987d0SSujith 	* The interval must be the shortest necessary to satisfy ANI,
995fb9987d0SSujith 	* short calibration and long calibration.
996fb9987d0SSujith 	*/
997fb9987d0SSujith 	cal_interval = ATH_LONG_CALINTERVAL;
998fb9987d0SSujith 	if (priv->ah->config.enable_ani)
999fb9987d0SSujith 		cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
1000fb9987d0SSujith 	if (!common->ani.caldone)
1001fb9987d0SSujith 		cal_interval = min(cal_interval, (u32)short_cal_interval);
1002fb9987d0SSujith 
1003fb9987d0SSujith 	ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
1004fb9987d0SSujith 				     msecs_to_jiffies(cal_interval));
1005fb9987d0SSujith }
1006fb9987d0SSujith 
1007fb9987d0SSujith /**********************/
1008fb9987d0SSujith /* mac80211 Callbacks */
1009fb9987d0SSujith /**********************/
1010fb9987d0SSujith 
1011fb9987d0SSujith static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1012fb9987d0SSujith {
1013fb9987d0SSujith 	struct ieee80211_hdr *hdr;
1014fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
10157757dfedSSujith 	int padpos, padsize, ret;
1016fb9987d0SSujith 
1017fb9987d0SSujith 	hdr = (struct ieee80211_hdr *) skb->data;
1018fb9987d0SSujith 
1019fb9987d0SSujith 	/* Add the padding after the header if this is not already done */
1020fb9987d0SSujith 	padpos = ath9k_cmn_padpos(hdr->frame_control);
1021fb9987d0SSujith 	padsize = padpos & 3;
1022fb9987d0SSujith 	if (padsize && skb->len > padpos) {
1023fb9987d0SSujith 		if (skb_headroom(skb) < padsize)
1024fb9987d0SSujith 			return -1;
1025fb9987d0SSujith 		skb_push(skb, padsize);
1026fb9987d0SSujith 		memmove(skb->data, skb->data + padsize, padpos);
1027fb9987d0SSujith 	}
1028fb9987d0SSujith 
10297757dfedSSujith 	ret = ath9k_htc_tx_start(priv, skb);
10307757dfedSSujith 	if (ret != 0) {
10317757dfedSSujith 		if (ret == -ENOMEM) {
1032226afe68SJoe Perches 			ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
10337757dfedSSujith 				"Stopping TX queues\n");
10347757dfedSSujith 			ieee80211_stop_queues(hw);
10357757dfedSSujith 			spin_lock_bh(&priv->tx_lock);
10367757dfedSSujith 			priv->tx_queues_stop = true;
10377757dfedSSujith 			spin_unlock_bh(&priv->tx_lock);
10387757dfedSSujith 		} else {
1039226afe68SJoe Perches 			ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1040226afe68SJoe Perches 				"Tx failed\n");
10417757dfedSSujith 		}
1042fb9987d0SSujith 		goto fail_tx;
1043fb9987d0SSujith 	}
1044fb9987d0SSujith 
1045fb9987d0SSujith 	return 0;
1046fb9987d0SSujith 
1047fb9987d0SSujith fail_tx:
1048fb9987d0SSujith 	dev_kfree_skb_any(skb);
1049fb9987d0SSujith 	return 0;
1050fb9987d0SSujith }
1051fb9987d0SSujith 
1052881ac6a5SSujith static int ath9k_htc_start(struct ieee80211_hw *hw)
1053fb9987d0SSujith {
1054fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1055fb9987d0SSujith 	struct ath_hw *ah = priv->ah;
1056fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
1057fb9987d0SSujith 	struct ieee80211_channel *curchan = hw->conf.channel;
1058fb9987d0SSujith 	struct ath9k_channel *init_channel;
1059fb9987d0SSujith 	int ret = 0;
1060fb9987d0SSujith 	enum htc_phymode mode;
10617f1f5a00SSujith 	__be16 htc_mode;
1062fb9987d0SSujith 	u8 cmd_rsp;
1063fb9987d0SSujith 
1064881ac6a5SSujith 	mutex_lock(&priv->mutex);
1065881ac6a5SSujith 
1066226afe68SJoe Perches 	ath_dbg(common, ATH_DBG_CONFIG,
1067fb9987d0SSujith 		"Starting driver with initial channel: %d MHz\n",
1068fb9987d0SSujith 		curchan->center_freq);
1069fb9987d0SSujith 
107021d5130bSSujith 	/* Ensure that HW is awake before flushing RX */
107121d5130bSSujith 	ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
107221d5130bSSujith 	WMI_CMD(WMI_FLUSH_RECV_CMDID);
107321d5130bSSujith 
1074fb9987d0SSujith 	/* setup initial channel */
1075fb9987d0SSujith 	init_channel = ath9k_cmn_get_curchannel(hw, ah);
1076fb9987d0SSujith 
1077fb9987d0SSujith 	ath9k_hw_htc_resetinit(ah);
107820bd2a09SFelix Fietkau 	ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
1079fb9987d0SSujith 	if (ret) {
10803800276aSJoe Perches 		ath_err(common,
10813800276aSJoe Perches 			"Unable to reset hardware; reset status %d (freq %u MHz)\n",
10823800276aSJoe Perches 			ret, curchan->center_freq);
1083881ac6a5SSujith 		mutex_unlock(&priv->mutex);
10848a8572a8SVivek Natarajan 		return ret;
1085fb9987d0SSujith 	}
1086fb9987d0SSujith 
1087b2a5c3dfSRajkumar Manoharan 	ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1088b2a5c3dfSRajkumar Manoharan 			       &priv->curtxpow);
1089fb9987d0SSujith 
1090fb9987d0SSujith 	mode = ath9k_htc_get_curmode(priv, init_channel);
1091fb9987d0SSujith 	htc_mode = cpu_to_be16(mode);
1092fb9987d0SSujith 	WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
1093fb9987d0SSujith 	WMI_CMD(WMI_ATH_INIT_CMDID);
1094fb9987d0SSujith 	WMI_CMD(WMI_START_RECV_CMDID);
1095fb9987d0SSujith 
1096fb9987d0SSujith 	ath9k_host_rx_init(priv);
1097fb9987d0SSujith 
10981057b750SSujith Manoharan 	ret = ath9k_htc_update_cap_target(priv);
10991057b750SSujith Manoharan 	if (ret)
11001057b750SSujith Manoharan 		ath_dbg(common, ATH_DBG_CONFIG,
11011057b750SSujith Manoharan 			"Failed to update capability in target\n");
11021057b750SSujith Manoharan 
1103fb9987d0SSujith 	priv->op_flags &= ~OP_INVALID;
1104fb9987d0SSujith 	htc_start(priv->htc);
1105fb9987d0SSujith 
11067757dfedSSujith 	spin_lock_bh(&priv->tx_lock);
11077757dfedSSujith 	priv->tx_queues_stop = false;
11087757dfedSSujith 	spin_unlock_bh(&priv->tx_lock);
11097757dfedSSujith 
11107757dfedSSujith 	ieee80211_wake_queues(hw);
11117757dfedSSujith 
111221cb9879SVivek Natarajan 	if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
111321cb9879SVivek Natarajan 		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
111421cb9879SVivek Natarajan 					   AR_STOMP_LOW_WLAN_WGHT);
111521cb9879SVivek Natarajan 		ath9k_hw_btcoex_enable(ah);
111621cb9879SVivek Natarajan 		ath_htc_resume_btcoex_work(priv);
111721cb9879SVivek Natarajan 	}
11188a8572a8SVivek Natarajan 	mutex_unlock(&priv->mutex);
11198a8572a8SVivek Natarajan 
11208a8572a8SVivek Natarajan 	return ret;
11218a8572a8SVivek Natarajan }
11228a8572a8SVivek Natarajan 
1123881ac6a5SSujith static void ath9k_htc_stop(struct ieee80211_hw *hw)
1124fb9987d0SSujith {
1125fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1126fb9987d0SSujith 	struct ath_hw *ah = priv->ah;
1127fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
1128fb9987d0SSujith 	int ret = 0;
1129fb9987d0SSujith 	u8 cmd_rsp;
1130fb9987d0SSujith 
1131881ac6a5SSujith 	mutex_lock(&priv->mutex);
1132881ac6a5SSujith 
1133fb9987d0SSujith 	if (priv->op_flags & OP_INVALID) {
1134226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
1135881ac6a5SSujith 		mutex_unlock(&priv->mutex);
1136fb9987d0SSujith 		return;
1137fb9987d0SSujith 	}
1138fb9987d0SSujith 
1139bde748a4SVivek Natarajan 	ath9k_htc_ps_wakeup(priv);
1140fb9987d0SSujith 	htc_stop(priv->htc);
1141fb9987d0SSujith 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
1142fb9987d0SSujith 	WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1143fb9987d0SSujith 	WMI_CMD(WMI_STOP_RECV_CMDID);
1144ea888357SStanislaw Gruszka 
1145ea888357SStanislaw Gruszka 	tasklet_kill(&priv->swba_tasklet);
1146ea888357SStanislaw Gruszka 	tasklet_kill(&priv->rx_tasklet);
1147ea888357SStanislaw Gruszka 	tasklet_kill(&priv->tx_tasklet);
1148ea888357SStanislaw Gruszka 
1149fb9987d0SSujith 	skb_queue_purge(&priv->tx_queue);
1150fb9987d0SSujith 
1151ea888357SStanislaw Gruszka 	mutex_unlock(&priv->mutex);
1152ea888357SStanislaw Gruszka 
1153ea888357SStanislaw Gruszka 	/* Cancel all the running timers/work .. */
1154ea888357SStanislaw Gruszka 	cancel_work_sync(&priv->fatal_work);
1155ea888357SStanislaw Gruszka 	cancel_work_sync(&priv->ps_work);
1156ea888357SStanislaw Gruszka 	cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
115745655baaSRajkumar Manoharan 	cancel_delayed_work_sync(&priv->ath9k_ani_work);
1158ea888357SStanislaw Gruszka 	ath9k_led_stop_brightness(priv);
1159ea888357SStanislaw Gruszka 
1160ea888357SStanislaw Gruszka 	mutex_lock(&priv->mutex);
1161ea888357SStanislaw Gruszka 
116221cb9879SVivek Natarajan 	if (ah->btcoex_hw.enabled) {
116321cb9879SVivek Natarajan 		ath9k_hw_btcoex_disable(ah);
116421cb9879SVivek Natarajan 		if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
116521cb9879SVivek Natarajan 			ath_htc_cancel_btcoex_work(priv);
116621cb9879SVivek Natarajan 	}
116721cb9879SVivek Natarajan 
1168a97b478cSSujith Manoharan 	/* Remove a monitor interface if it's present. */
1169a97b478cSSujith Manoharan 	if (priv->ah->is_monitoring)
1170a97b478cSSujith Manoharan 		ath9k_htc_remove_monitor_interface(priv);
1171a97b478cSSujith Manoharan 
1172e9201f09SSujith 	ath9k_hw_phy_disable(ah);
1173e9201f09SSujith 	ath9k_hw_disable(ah);
1174e9201f09SSujith 	ath9k_htc_ps_restore(priv);
1175e9201f09SSujith 	ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1176e9201f09SSujith 
1177fb9987d0SSujith 	priv->op_flags |= OP_INVALID;
1178fb9987d0SSujith 
1179226afe68SJoe Perches 	ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
11808a8572a8SVivek Natarajan 	mutex_unlock(&priv->mutex);
11818a8572a8SVivek Natarajan }
11828a8572a8SVivek Natarajan 
1183fb9987d0SSujith static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1184fb9987d0SSujith 				   struct ieee80211_vif *vif)
1185fb9987d0SSujith {
1186fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1187fb9987d0SSujith 	struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1188fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
1189fb9987d0SSujith 	struct ath9k_htc_target_vif hvif;
1190fb9987d0SSujith 	int ret = 0;
1191fb9987d0SSujith 	u8 cmd_rsp;
1192fb9987d0SSujith 
1193fb9987d0SSujith 	mutex_lock(&priv->mutex);
1194fb9987d0SSujith 
1195a97b478cSSujith Manoharan 	if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
1196fb9987d0SSujith 		ret = -ENOBUFS;
1197ab77c70aSSujith Manoharan 		mutex_unlock(&priv->mutex);
1198ab77c70aSSujith Manoharan 		return ret;
1199fb9987d0SSujith 	}
1200fb9987d0SSujith 
1201bde748a4SVivek Natarajan 	ath9k_htc_ps_wakeup(priv);
1202fb9987d0SSujith 	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1203fb9987d0SSujith 	memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1204fb9987d0SSujith 
1205fb9987d0SSujith 	switch (vif->type) {
1206fb9987d0SSujith 	case NL80211_IFTYPE_STATION:
1207fb9987d0SSujith 		hvif.opmode = cpu_to_be32(HTC_M_STA);
1208fb9987d0SSujith 		break;
1209fb9987d0SSujith 	case NL80211_IFTYPE_ADHOC:
1210fb9987d0SSujith 		hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1211fb9987d0SSujith 		break;
1212fb9987d0SSujith 	default:
12133800276aSJoe Perches 		ath_err(common,
1214fb9987d0SSujith 			"Interface type %d not yet supported\n", vif->type);
1215fb9987d0SSujith 		ret = -EOPNOTSUPP;
1216fb9987d0SSujith 		goto out;
1217fb9987d0SSujith 	}
1218fb9987d0SSujith 
1219fb9987d0SSujith 	/* Index starts from zero on the target */
1220a97b478cSSujith Manoharan 	avp->index = hvif.index = ffz(priv->vif_slot);
1221fb9987d0SSujith 	hvif.rtsthreshold = cpu_to_be16(2304);
1222fb9987d0SSujith 	WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1223fb9987d0SSujith 	if (ret)
1224fb9987d0SSujith 		goto out;
1225fb9987d0SSujith 
1226fb9987d0SSujith 	/*
1227fb9987d0SSujith 	 * We need a node in target to tx mgmt frames
1228fb9987d0SSujith 	 * before association.
1229fb9987d0SSujith 	 */
1230fb9987d0SSujith 	ret = ath9k_htc_add_station(priv, vif, NULL);
1231ab77c70aSSujith Manoharan 	if (ret) {
1232ab77c70aSSujith Manoharan 		WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1233fb9987d0SSujith 		goto out;
1234ab77c70aSSujith Manoharan 	}
1235fb9987d0SSujith 
1236585895cdSSujith Manoharan 	ath9k_htc_set_bssid_mask(priv, vif);
1237585895cdSSujith Manoharan 
1238a97b478cSSujith Manoharan 	priv->ah->opmode = vif->type;
1239a97b478cSSujith Manoharan 	priv->vif_slot |= (1 << avp->index);
1240ab77c70aSSujith Manoharan 	priv->nvifs++;
1241fb9987d0SSujith 	priv->vif = vif;
1242a97b478cSSujith Manoharan 
1243a97b478cSSujith Manoharan 	ath_dbg(common, ATH_DBG_CONFIG,
1244a97b478cSSujith Manoharan 		"Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1245a97b478cSSujith Manoharan 
1246fb9987d0SSujith out:
1247bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
1248fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1249cb551df2SSujith 
1250fb9987d0SSujith 	return ret;
1251fb9987d0SSujith }
1252fb9987d0SSujith 
1253fb9987d0SSujith static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1254fb9987d0SSujith 				       struct ieee80211_vif *vif)
1255fb9987d0SSujith {
1256fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1257fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
1258fb9987d0SSujith 	struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1259fb9987d0SSujith 	struct ath9k_htc_target_vif hvif;
1260fb9987d0SSujith 	int ret = 0;
1261fb9987d0SSujith 	u8 cmd_rsp;
1262fb9987d0SSujith 
1263fb9987d0SSujith 	mutex_lock(&priv->mutex);
1264cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1265fb9987d0SSujith 
1266fb9987d0SSujith 	memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1267fb9987d0SSujith 	memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1268fb9987d0SSujith 	hvif.index = avp->index;
1269fb9987d0SSujith 	WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1270fb9987d0SSujith 	priv->nvifs--;
1271a97b478cSSujith Manoharan 	priv->vif_slot &= ~(1 << avp->index);
1272fb9987d0SSujith 
1273fb9987d0SSujith 	ath9k_htc_remove_station(priv, vif, NULL);
1274fb9987d0SSujith 	priv->vif = NULL;
1275fb9987d0SSujith 
1276a97b478cSSujith Manoharan 	ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1277a97b478cSSujith Manoharan 
1278cb551df2SSujith 	ath9k_htc_ps_restore(priv);
1279fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1280fb9987d0SSujith }
1281fb9987d0SSujith 
1282fb9987d0SSujith static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1283fb9987d0SSujith {
1284fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1285fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
1286fb9987d0SSujith 	struct ieee80211_conf *conf = &hw->conf;
1287fb9987d0SSujith 
1288fb9987d0SSujith 	mutex_lock(&priv->mutex);
1289fb9987d0SSujith 
12908a8572a8SVivek Natarajan 	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
12918a8572a8SVivek Natarajan 		bool enable_radio = false;
12928a8572a8SVivek Natarajan 		bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
12938a8572a8SVivek Natarajan 
129423367769SSujith 		mutex_lock(&priv->htc_pm_lock);
12958a8572a8SVivek Natarajan 		if (!idle && priv->ps_idle)
12968a8572a8SVivek Natarajan 			enable_radio = true;
12978a8572a8SVivek Natarajan 		priv->ps_idle = idle;
129823367769SSujith 		mutex_unlock(&priv->htc_pm_lock);
12998a8572a8SVivek Natarajan 
13008a8572a8SVivek Natarajan 		if (enable_radio) {
1301226afe68SJoe Perches 			ath_dbg(common, ATH_DBG_CONFIG,
13028a8572a8SVivek Natarajan 				"not-idle: enabling radio\n");
130323367769SSujith 			ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
130423367769SSujith 			ath9k_htc_radio_enable(hw);
13058a8572a8SVivek Natarajan 		}
13068a8572a8SVivek Natarajan 	}
13078a8572a8SVivek Natarajan 
130855de80d6SSujith Manoharan 	/*
130955de80d6SSujith Manoharan 	 * Monitor interface should be added before
131055de80d6SSujith Manoharan 	 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
131155de80d6SSujith Manoharan 	 */
131255de80d6SSujith Manoharan 	if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1313a97b478cSSujith Manoharan 		if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1314a97b478cSSujith Manoharan 		    !priv->ah->is_monitoring)
1315a97b478cSSujith Manoharan 			ath9k_htc_add_monitor_interface(priv);
1316a97b478cSSujith Manoharan 		else if (priv->ah->is_monitoring)
1317a97b478cSSujith Manoharan 			ath9k_htc_remove_monitor_interface(priv);
131855de80d6SSujith Manoharan 	}
131955de80d6SSujith Manoharan 
1320fb9987d0SSujith 	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1321fb9987d0SSujith 		struct ieee80211_channel *curchan = hw->conf.channel;
1322fb9987d0SSujith 		int pos = curchan->hw_value;
1323fb9987d0SSujith 
1324226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1325fb9987d0SSujith 			curchan->center_freq);
1326fb9987d0SSujith 
1327babcbc29SFelix Fietkau 		ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1328babcbc29SFelix Fietkau 					  hw->conf.channel,
1329babcbc29SFelix Fietkau 					  hw->conf.channel_type);
1330fb9987d0SSujith 
1331fb9987d0SSujith 		if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
13323800276aSJoe Perches 			ath_err(common, "Unable to set channel\n");
1333fb9987d0SSujith 			mutex_unlock(&priv->mutex);
1334fb9987d0SSujith 			return -EINVAL;
1335fb9987d0SSujith 		}
1336fb9987d0SSujith 
1337fb9987d0SSujith 	}
1338692d6b17SSujith Manoharan 
1339bde748a4SVivek Natarajan 	if (changed & IEEE80211_CONF_CHANGE_PS) {
1340bde748a4SVivek Natarajan 		if (conf->flags & IEEE80211_CONF_PS) {
1341bde748a4SVivek Natarajan 			ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1342bde748a4SVivek Natarajan 			priv->ps_enabled = true;
1343bde748a4SVivek Natarajan 		} else {
1344bde748a4SVivek Natarajan 			priv->ps_enabled = false;
1345bde748a4SVivek Natarajan 			cancel_work_sync(&priv->ps_work);
1346bde748a4SVivek Natarajan 			ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1347bde748a4SVivek Natarajan 		}
1348bde748a4SVivek Natarajan 	}
1349fb9987d0SSujith 
1350692d6b17SSujith Manoharan 	if (changed & IEEE80211_CONF_CHANGE_POWER) {
1351692d6b17SSujith Manoharan 		priv->txpowlimit = 2 * conf->power_level;
1352b2a5c3dfSRajkumar Manoharan 		ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1353b2a5c3dfSRajkumar Manoharan 				       priv->txpowlimit, &priv->curtxpow);
1354692d6b17SSujith Manoharan 	}
1355692d6b17SSujith Manoharan 
135623367769SSujith 	if (changed & IEEE80211_CONF_CHANGE_IDLE) {
135723367769SSujith 		mutex_lock(&priv->htc_pm_lock);
135823367769SSujith 		if (!priv->ps_idle) {
135923367769SSujith 			mutex_unlock(&priv->htc_pm_lock);
136023367769SSujith 			goto out;
136123367769SSujith 		}
136223367769SSujith 		mutex_unlock(&priv->htc_pm_lock);
136323367769SSujith 
1364226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
13658a8572a8SVivek Natarajan 			"idle: disabling radio\n");
1366881ac6a5SSujith 		ath9k_htc_radio_disable(hw);
13678a8572a8SVivek Natarajan 	}
13688a8572a8SVivek Natarajan 
136923367769SSujith out:
1370fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1371fb9987d0SSujith 	return 0;
1372fb9987d0SSujith }
1373fb9987d0SSujith 
1374fb9987d0SSujith #define SUPPORTED_FILTERS			\
1375fb9987d0SSujith 	(FIF_PROMISC_IN_BSS |			\
1376fb9987d0SSujith 	FIF_ALLMULTI |				\
1377fb9987d0SSujith 	FIF_CONTROL |				\
1378fb9987d0SSujith 	FIF_PSPOLL |				\
1379fb9987d0SSujith 	FIF_OTHER_BSS |				\
1380fb9987d0SSujith 	FIF_BCN_PRBRESP_PROMISC |		\
138194a40c0cSRajkumar Manoharan 	FIF_PROBE_REQ |				\
1382fb9987d0SSujith 	FIF_FCSFAIL)
1383fb9987d0SSujith 
1384fb9987d0SSujith static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1385fb9987d0SSujith 				       unsigned int changed_flags,
1386fb9987d0SSujith 				       unsigned int *total_flags,
1387fb9987d0SSujith 				       u64 multicast)
1388fb9987d0SSujith {
1389fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1390fb9987d0SSujith 	u32 rfilt;
1391fb9987d0SSujith 
1392fb9987d0SSujith 	mutex_lock(&priv->mutex);
1393bde748a4SVivek Natarajan 	ath9k_htc_ps_wakeup(priv);
1394cb551df2SSujith 
1395fb9987d0SSujith 	changed_flags &= SUPPORTED_FILTERS;
1396fb9987d0SSujith 	*total_flags &= SUPPORTED_FILTERS;
1397fb9987d0SSujith 
1398fb9987d0SSujith 	priv->rxfilter = *total_flags;
13990995d110SSujith 	rfilt = ath9k_htc_calcrxfilter(priv);
1400fb9987d0SSujith 	ath9k_hw_setrxfilter(priv->ah, rfilt);
1401fb9987d0SSujith 
1402226afe68SJoe Perches 	ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1403fb9987d0SSujith 		"Set HW RX filter: 0x%x\n", rfilt);
1404fb9987d0SSujith 
1405bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
1406fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1407fb9987d0SSujith }
1408fb9987d0SSujith 
1409abd984e6SSujith static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1410fb9987d0SSujith 			     struct ieee80211_vif *vif,
1411fb9987d0SSujith 			     struct ieee80211_sta *sta)
1412fb9987d0SSujith {
1413fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1414fb9987d0SSujith 	int ret;
1415fb9987d0SSujith 
141605a30f9cSSujith.Manoharan@atheros.com 	mutex_lock(&priv->mutex);
1417cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1418fb9987d0SSujith 	ret = ath9k_htc_add_station(priv, vif, sta);
1419fb9987d0SSujith 	if (!ret)
14200d425a7dSSujith 		ath9k_htc_init_rate(priv, sta);
1421cb551df2SSujith 	ath9k_htc_ps_restore(priv);
142205a30f9cSSujith.Manoharan@atheros.com 	mutex_unlock(&priv->mutex);
1423abd984e6SSujith 
1424abd984e6SSujith 	return ret;
1425abd984e6SSujith }
1426abd984e6SSujith 
1427abd984e6SSujith static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1428abd984e6SSujith 				struct ieee80211_vif *vif,
1429abd984e6SSujith 				struct ieee80211_sta *sta)
1430abd984e6SSujith {
1431abd984e6SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1432abd984e6SSujith 	int ret;
1433abd984e6SSujith 
1434abd984e6SSujith 	mutex_lock(&priv->mutex);
1435abd984e6SSujith 	ath9k_htc_ps_wakeup(priv);
1436abd984e6SSujith 	ret = ath9k_htc_remove_station(priv, vif, sta);
1437abd984e6SSujith 	ath9k_htc_ps_restore(priv);
1438abd984e6SSujith 	mutex_unlock(&priv->mutex);
1439abd984e6SSujith 
1440abd984e6SSujith 	return ret;
1441fb9987d0SSujith }
1442fb9987d0SSujith 
1443fb9987d0SSujith static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1444fb9987d0SSujith 			     const struct ieee80211_tx_queue_params *params)
1445fb9987d0SSujith {
1446fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1447fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
1448fb9987d0SSujith 	struct ath9k_tx_queue_info qi;
1449fb9987d0SSujith 	int ret = 0, qnum;
1450fb9987d0SSujith 
1451fb9987d0SSujith 	if (queue >= WME_NUM_AC)
1452fb9987d0SSujith 		return 0;
1453fb9987d0SSujith 
1454fb9987d0SSujith 	mutex_lock(&priv->mutex);
1455cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1456fb9987d0SSujith 
1457fb9987d0SSujith 	memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1458fb9987d0SSujith 
1459fb9987d0SSujith 	qi.tqi_aifs = params->aifs;
1460fb9987d0SSujith 	qi.tqi_cwmin = params->cw_min;
1461fb9987d0SSujith 	qi.tqi_cwmax = params->cw_max;
1462fb9987d0SSujith 	qi.tqi_burstTime = params->txop;
1463fb9987d0SSujith 
1464fb9987d0SSujith 	qnum = get_hw_qnum(queue, priv->hwq_map);
1465fb9987d0SSujith 
1466226afe68SJoe Perches 	ath_dbg(common, ATH_DBG_CONFIG,
1467226afe68SJoe Perches 		"Configure tx [queue/hwq] [%d/%d],  aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1468fb9987d0SSujith 		queue, qnum, params->aifs, params->cw_min,
1469fb9987d0SSujith 		params->cw_max, params->txop);
1470fb9987d0SSujith 
1471e1572c5eSSujith 	ret = ath_htc_txq_update(priv, qnum, &qi);
1472764580f5SSujith 	if (ret) {
14733800276aSJoe Perches 		ath_err(common, "TXQ Update failed\n");
1474764580f5SSujith 		goto out;
1475764580f5SSujith 	}
1476fb9987d0SSujith 
1477764580f5SSujith 	if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
1478e8c35a77SFelix Fietkau 	    (qnum == priv->hwq_map[WME_AC_BE]))
1479764580f5SSujith 		    ath9k_htc_beaconq_config(priv);
1480764580f5SSujith out:
1481cb551df2SSujith 	ath9k_htc_ps_restore(priv);
1482fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1483fb9987d0SSujith 
1484fb9987d0SSujith 	return ret;
1485fb9987d0SSujith }
1486fb9987d0SSujith 
1487fb9987d0SSujith static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1488fb9987d0SSujith 			     enum set_key_cmd cmd,
1489fb9987d0SSujith 			     struct ieee80211_vif *vif,
1490fb9987d0SSujith 			     struct ieee80211_sta *sta,
1491fb9987d0SSujith 			     struct ieee80211_key_conf *key)
1492fb9987d0SSujith {
1493fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1494fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(priv->ah);
1495fb9987d0SSujith 	int ret = 0;
1496fb9987d0SSujith 
1497e1572c5eSSujith 	if (htc_modparam_nohwcrypt)
1498fb9987d0SSujith 		return -ENOSPC;
1499fb9987d0SSujith 
1500fb9987d0SSujith 	mutex_lock(&priv->mutex);
1501226afe68SJoe Perches 	ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
1502bde748a4SVivek Natarajan 	ath9k_htc_ps_wakeup(priv);
1503fb9987d0SSujith 
1504fb9987d0SSujith 	switch (cmd) {
1505fb9987d0SSujith 	case SET_KEY:
1506040e539eSBruno Randolf 		ret = ath_key_config(common, vif, sta, key);
1507fb9987d0SSujith 		if (ret >= 0) {
1508fb9987d0SSujith 			key->hw_key_idx = ret;
1509fb9987d0SSujith 			/* push IV and Michael MIC generation to stack */
1510fb9987d0SSujith 			key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
151197359d12SJohannes Berg 			if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
1512fb9987d0SSujith 				key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
151397359d12SJohannes Berg 			if (priv->ah->sw_mgmt_crypto &&
151497359d12SJohannes Berg 			    key->cipher == WLAN_CIPHER_SUITE_CCMP)
1515fb9987d0SSujith 				key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1516fb9987d0SSujith 			ret = 0;
1517fb9987d0SSujith 		}
1518fb9987d0SSujith 		break;
1519fb9987d0SSujith 	case DISABLE_KEY:
1520040e539eSBruno Randolf 		ath_key_delete(common, key);
1521fb9987d0SSujith 		break;
1522fb9987d0SSujith 	default:
1523fb9987d0SSujith 		ret = -EINVAL;
1524fb9987d0SSujith 	}
1525fb9987d0SSujith 
1526bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
1527fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1528fb9987d0SSujith 
1529fb9987d0SSujith 	return ret;
1530fb9987d0SSujith }
1531fb9987d0SSujith 
1532fb9987d0SSujith static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1533fb9987d0SSujith 				       struct ieee80211_vif *vif,
1534fb9987d0SSujith 				       struct ieee80211_bss_conf *bss_conf,
1535fb9987d0SSujith 				       u32 changed)
1536fb9987d0SSujith {
1537fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1538fb9987d0SSujith 	struct ath_hw *ah = priv->ah;
1539fb9987d0SSujith 	struct ath_common *common = ath9k_hw_common(ah);
1540fb9987d0SSujith 
1541fb9987d0SSujith 	mutex_lock(&priv->mutex);
1542bde748a4SVivek Natarajan 	ath9k_htc_ps_wakeup(priv);
1543fb9987d0SSujith 
1544fb9987d0SSujith 	if (changed & BSS_CHANGED_ASSOC) {
1545fb9987d0SSujith 		common->curaid = bss_conf->assoc ?
1546fb9987d0SSujith 				 bss_conf->aid : 0;
1547226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1548fb9987d0SSujith 			bss_conf->assoc);
1549fb9987d0SSujith 
15507c277349SSujith Manoharan 		if (bss_conf->assoc)
1551fb9987d0SSujith 			ath_start_ani(priv);
15527c277349SSujith Manoharan 		else
1553fb9987d0SSujith 			cancel_delayed_work_sync(&priv->ath9k_ani_work);
1554fb9987d0SSujith 	}
1555fb9987d0SSujith 
1556fb9987d0SSujith 	if (changed & BSS_CHANGED_BSSID) {
1557fb9987d0SSujith 		/* Set BSSID */
1558fb9987d0SSujith 		memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1559fb9987d0SSujith 		ath9k_hw_write_associd(ah);
1560fb9987d0SSujith 
1561226afe68SJoe Perches 		ath_dbg(common, ATH_DBG_CONFIG,
1562fb9987d0SSujith 			"BSSID: %pM aid: 0x%x\n",
1563fb9987d0SSujith 			common->curbssid, common->curaid);
1564fb9987d0SSujith 	}
1565fb9987d0SSujith 
1566fb9987d0SSujith 	if ((changed & BSS_CHANGED_BEACON_INT) ||
1567fb9987d0SSujith 	    (changed & BSS_CHANGED_BEACON) ||
1568fb9987d0SSujith 	    ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1569fb9987d0SSujith 	    bss_conf->enable_beacon)) {
1570fb9987d0SSujith 		priv->op_flags |= OP_ENABLE_BEACON;
15711c3652a5SVivek Natarajan 		ath9k_htc_beacon_config(priv, vif);
1572fb9987d0SSujith 	}
1573fb9987d0SSujith 
1574fb9987d0SSujith 	if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1575fb9987d0SSujith 	    !bss_conf->enable_beacon) {
1576fb9987d0SSujith 		priv->op_flags &= ~OP_ENABLE_BEACON;
15771c3652a5SVivek Natarajan 		ath9k_htc_beacon_config(priv, vif);
1578fb9987d0SSujith 	}
1579fb9987d0SSujith 
1580fb9987d0SSujith 	if (changed & BSS_CHANGED_ERP_SLOT) {
1581fb9987d0SSujith 		if (bss_conf->use_short_slot)
1582fb9987d0SSujith 			ah->slottime = 9;
1583fb9987d0SSujith 		else
1584fb9987d0SSujith 			ah->slottime = 20;
1585fb9987d0SSujith 
1586fb9987d0SSujith 		ath9k_hw_init_global_settings(ah);
1587fb9987d0SSujith 	}
1588fb9987d0SSujith 
15892c76ef89SSujith 	if (changed & BSS_CHANGED_HT)
15902c76ef89SSujith 		ath9k_htc_update_rate(priv, vif, bss_conf);
15912c76ef89SSujith 
1592bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
1593fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1594fb9987d0SSujith }
1595fb9987d0SSujith 
1596fb9987d0SSujith static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1597fb9987d0SSujith {
1598fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1599fb9987d0SSujith 	u64 tsf;
1600fb9987d0SSujith 
1601fb9987d0SSujith 	mutex_lock(&priv->mutex);
1602cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1603fb9987d0SSujith 	tsf = ath9k_hw_gettsf64(priv->ah);
1604cb551df2SSujith 	ath9k_htc_ps_restore(priv);
1605fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1606fb9987d0SSujith 
1607fb9987d0SSujith 	return tsf;
1608fb9987d0SSujith }
1609fb9987d0SSujith 
1610fb9987d0SSujith static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1611fb9987d0SSujith {
1612fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1613fb9987d0SSujith 
1614fb9987d0SSujith 	mutex_lock(&priv->mutex);
1615cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1616fb9987d0SSujith 	ath9k_hw_settsf64(priv->ah, tsf);
1617cb551df2SSujith 	ath9k_htc_ps_restore(priv);
1618fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1619fb9987d0SSujith }
1620fb9987d0SSujith 
1621fb9987d0SSujith static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1622fb9987d0SSujith {
1623fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1624fb9987d0SSujith 
1625fb9987d0SSujith 	mutex_lock(&priv->mutex);
1626cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1627fb9987d0SSujith 	ath9k_hw_reset_tsf(priv->ah);
1628bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
1629cb551df2SSujith 	mutex_unlock(&priv->mutex);
1630fb9987d0SSujith }
1631fb9987d0SSujith 
1632fb9987d0SSujith static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1633fb9987d0SSujith 				  struct ieee80211_vif *vif,
1634fb9987d0SSujith 				  enum ieee80211_ampdu_mlme_action action,
1635fb9987d0SSujith 				  struct ieee80211_sta *sta,
16360b01f030SJohannes Berg 				  u16 tid, u16 *ssn, u8 buf_size)
1637fb9987d0SSujith {
1638fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1639fb9987d0SSujith 	struct ath9k_htc_sta *ista;
1640d7ca2139SSujith 	int ret = 0;
1641fb9987d0SSujith 
1642*87df8957SSujith Manoharan 	mutex_lock(&priv->mutex);
1643*87df8957SSujith Manoharan 
1644fb9987d0SSujith 	switch (action) {
1645fb9987d0SSujith 	case IEEE80211_AMPDU_RX_START:
1646fb9987d0SSujith 		break;
1647fb9987d0SSujith 	case IEEE80211_AMPDU_RX_STOP:
1648fb9987d0SSujith 		break;
1649fb9987d0SSujith 	case IEEE80211_AMPDU_TX_START:
1650d7ca2139SSujith 		ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1651d7ca2139SSujith 		if (!ret)
1652d7ca2139SSujith 			ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1653d7ca2139SSujith 		break;
1654fb9987d0SSujith 	case IEEE80211_AMPDU_TX_STOP:
1655d7ca2139SSujith 		ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1656d7ca2139SSujith 		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1657fb9987d0SSujith 		break;
1658fb9987d0SSujith 	case IEEE80211_AMPDU_TX_OPERATIONAL:
1659fb9987d0SSujith 		ista = (struct ath9k_htc_sta *) sta->drv_priv;
1660d7ca2139SSujith 		spin_lock_bh(&priv->tx_lock);
1661fb9987d0SSujith 		ista->tid_state[tid] = AGGR_OPERATIONAL;
1662d7ca2139SSujith 		spin_unlock_bh(&priv->tx_lock);
1663fb9987d0SSujith 		break;
1664fb9987d0SSujith 	default:
16653800276aSJoe Perches 		ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
1666fb9987d0SSujith 	}
1667fb9987d0SSujith 
1668*87df8957SSujith Manoharan 	mutex_unlock(&priv->mutex);
1669*87df8957SSujith Manoharan 
1670d7ca2139SSujith 	return ret;
1671fb9987d0SSujith }
1672fb9987d0SSujith 
1673fb9987d0SSujith static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1674fb9987d0SSujith {
1675fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1676fb9987d0SSujith 
1677fb9987d0SSujith 	mutex_lock(&priv->mutex);
1678fb9987d0SSujith 	spin_lock_bh(&priv->beacon_lock);
1679fb9987d0SSujith 	priv->op_flags |= OP_SCANNING;
1680fb9987d0SSujith 	spin_unlock_bh(&priv->beacon_lock);
1681bde748a4SVivek Natarajan 	cancel_work_sync(&priv->ps_work);
1682fb9987d0SSujith 	cancel_delayed_work_sync(&priv->ath9k_ani_work);
1683fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1684fb9987d0SSujith }
1685fb9987d0SSujith 
1686fb9987d0SSujith static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1687fb9987d0SSujith {
1688fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1689fb9987d0SSujith 
1690fb9987d0SSujith 	mutex_lock(&priv->mutex);
1691fb9987d0SSujith 	spin_lock_bh(&priv->beacon_lock);
1692fb9987d0SSujith 	priv->op_flags &= ~OP_SCANNING;
1693fb9987d0SSujith 	spin_unlock_bh(&priv->beacon_lock);
16947c277349SSujith Manoharan 	ath9k_htc_ps_wakeup(priv);
16957c277349SSujith Manoharan 	ath9k_htc_vif_reconfig(priv);
1696bde748a4SVivek Natarajan 	ath9k_htc_ps_restore(priv);
1697cb551df2SSujith 	mutex_unlock(&priv->mutex);
1698fb9987d0SSujith }
1699fb9987d0SSujith 
1700fb9987d0SSujith static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1701fb9987d0SSujith {
1702fb9987d0SSujith 	return 0;
1703fb9987d0SSujith }
1704fb9987d0SSujith 
1705fb9987d0SSujith static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1706fb9987d0SSujith 					 u8 coverage_class)
1707fb9987d0SSujith {
1708fb9987d0SSujith 	struct ath9k_htc_priv *priv = hw->priv;
1709fb9987d0SSujith 
1710fb9987d0SSujith 	mutex_lock(&priv->mutex);
1711cb551df2SSujith 	ath9k_htc_ps_wakeup(priv);
1712fb9987d0SSujith 	priv->ah->coverage_class = coverage_class;
1713fb9987d0SSujith 	ath9k_hw_init_global_settings(priv->ah);
1714cb551df2SSujith 	ath9k_htc_ps_restore(priv);
1715fb9987d0SSujith 	mutex_unlock(&priv->mutex);
1716fb9987d0SSujith }
1717fb9987d0SSujith 
1718fb9987d0SSujith struct ieee80211_ops ath9k_htc_ops = {
1719fb9987d0SSujith 	.tx                 = ath9k_htc_tx,
1720fb9987d0SSujith 	.start              = ath9k_htc_start,
1721fb9987d0SSujith 	.stop               = ath9k_htc_stop,
1722fb9987d0SSujith 	.add_interface      = ath9k_htc_add_interface,
1723fb9987d0SSujith 	.remove_interface   = ath9k_htc_remove_interface,
1724fb9987d0SSujith 	.config             = ath9k_htc_config,
1725fb9987d0SSujith 	.configure_filter   = ath9k_htc_configure_filter,
1726abd984e6SSujith 	.sta_add            = ath9k_htc_sta_add,
1727abd984e6SSujith 	.sta_remove         = ath9k_htc_sta_remove,
1728fb9987d0SSujith 	.conf_tx            = ath9k_htc_conf_tx,
1729fb9987d0SSujith 	.bss_info_changed   = ath9k_htc_bss_info_changed,
1730fb9987d0SSujith 	.set_key            = ath9k_htc_set_key,
1731fb9987d0SSujith 	.get_tsf            = ath9k_htc_get_tsf,
1732fb9987d0SSujith 	.set_tsf            = ath9k_htc_set_tsf,
1733fb9987d0SSujith 	.reset_tsf          = ath9k_htc_reset_tsf,
1734fb9987d0SSujith 	.ampdu_action       = ath9k_htc_ampdu_action,
1735fb9987d0SSujith 	.sw_scan_start      = ath9k_htc_sw_scan_start,
1736fb9987d0SSujith 	.sw_scan_complete   = ath9k_htc_sw_scan_complete,
1737fb9987d0SSujith 	.set_rts_threshold  = ath9k_htc_set_rts_threshold,
1738fb9987d0SSujith 	.rfkill_poll        = ath9k_htc_rfkill_poll_state,
1739fb9987d0SSujith 	.set_coverage_class = ath9k_htc_set_coverage_class,
1740fb9987d0SSujith };
1741