blob: 6065c2fc97f001691228bf17efa71132675c86b2 [file] [log] [blame]
Sujithfb9987d2010-03-17 14:25:25 +05301/*
2 * Copyright (c) 2010 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "htc.h"
18
Sujithfb9987d2010-03-17 14:25:25 +053019/*************/
20/* Utilities */
21/*************/
22
Sujithfb9987d2010-03-17 14:25:25 +053023/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
24static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
25 struct ath9k_channel *ichan)
26{
27 enum htc_phymode mode;
28
Sujith Manoharanc75197a2011-05-17 12:41:20 +053029 mode = -EINVAL;
Sujithfb9987d2010-03-17 14:25:25 +053030
31 switch (ichan->chanmode) {
32 case CHANNEL_G:
33 case CHANNEL_G_HT20:
34 case CHANNEL_G_HT40PLUS:
35 case CHANNEL_G_HT40MINUS:
36 mode = HTC_MODE_11NG;
37 break;
38 case CHANNEL_A:
39 case CHANNEL_A_HT20:
40 case CHANNEL_A_HT40PLUS:
41 case CHANNEL_A_HT40MINUS:
42 mode = HTC_MODE_11NA;
43 break;
44 default:
45 break;
46 }
47
Sujith Manoharanc75197a2011-05-17 12:41:20 +053048 WARN_ON(mode < 0);
49
Sujithfb9987d2010-03-17 14:25:25 +053050 return mode;
51}
52
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053053bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
54 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053055{
56 bool ret;
57
58 mutex_lock(&priv->htc_pm_lock);
59 ret = ath9k_hw_setpower(priv->ah, mode);
60 mutex_unlock(&priv->htc_pm_lock);
61
62 return ret;
63}
64
65void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
66{
67 mutex_lock(&priv->htc_pm_lock);
68 if (++priv->ps_usecount != 1)
69 goto unlock;
70 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
71
72unlock:
73 mutex_unlock(&priv->htc_pm_lock);
74}
75
76void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
77{
78 mutex_lock(&priv->htc_pm_lock);
79 if (--priv->ps_usecount != 0)
80 goto unlock;
81
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053082 if (priv->ps_idle)
83 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
84 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053085 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053086
Vivek Natarajanbde748a2010-04-05 14:48:05 +053087unlock:
88 mutex_unlock(&priv->htc_pm_lock);
89}
90
91void ath9k_ps_work(struct work_struct *work)
92{
93 struct ath9k_htc_priv *priv =
94 container_of(work, struct ath9k_htc_priv,
95 ps_work);
96 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
97
98 /* The chip wakes up after receiving the first beacon
99 while network sleep is enabled. For the driver to
100 be in sync with the hw, set the chip to awake and
101 only then set it to sleep.
102 */
103 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
104}
105
Sujith Manoharan7c277342011-02-21 07:48:39 +0530106static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
107{
108 struct ath9k_htc_priv *priv = data;
109 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
110
Sujith Manoharana5fae372011-02-21 07:49:53 +0530111 if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
112 priv->reconfig_beacon = true;
113
Sujith Manoharan7c277342011-02-21 07:48:39 +0530114 if (bss_conf->assoc) {
115 priv->rearm_ani = true;
116 priv->reconfig_beacon = true;
117 }
118}
119
120static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
121{
122 priv->rearm_ani = false;
123 priv->reconfig_beacon = false;
124
125 ieee80211_iterate_active_interfaces_atomic(priv->hw,
126 ath9k_htc_vif_iter, priv);
127 if (priv->rearm_ani)
Sujith Manoharana2362542011-02-21 07:49:38 +0530128 ath9k_htc_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530129
130 if (priv->reconfig_beacon) {
131 ath9k_htc_ps_wakeup(priv);
132 ath9k_htc_beacon_reconfig(priv);
133 ath9k_htc_ps_restore(priv);
134 }
135}
136
Sujith Manoharan585895c2011-02-21 07:48:46 +0530137static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
138{
139 struct ath9k_vif_iter_data *iter_data = data;
140 int i;
141
142 for (i = 0; i < ETH_ALEN; i++)
143 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
144}
145
146static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
147 struct ieee80211_vif *vif)
148{
149 struct ath_common *common = ath9k_hw_common(priv->ah);
150 struct ath9k_vif_iter_data iter_data;
151
152 /*
153 * Use the hardware MAC address as reference, the hardware uses it
154 * together with the BSSID mask when matching addresses.
155 */
156 iter_data.hw_macaddr = common->macaddr;
157 memset(&iter_data.mask, 0xff, ETH_ALEN);
158
159 if (vif)
160 ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
161
162 /* Get list of all active MAC addresses */
163 ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
164 &iter_data);
165
166 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
167 ath_hw_setbssidmask(common);
168}
169
Sujith Manoharanffbe7c82011-02-21 07:49:31 +0530170static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
171{
172 if (priv->num_ibss_vif)
173 priv->ah->opmode = NL80211_IFTYPE_ADHOC;
174 else if (priv->num_ap_vif)
175 priv->ah->opmode = NL80211_IFTYPE_AP;
176 else
177 priv->ah->opmode = NL80211_IFTYPE_STATION;
178
179 ath9k_hw_setopmode(priv->ah);
180}
181
Sujith Manoharan73908672010-12-28 14:28:27 +0530182void ath9k_htc_reset(struct ath9k_htc_priv *priv)
183{
184 struct ath_hw *ah = priv->ah;
185 struct ath_common *common = ath9k_hw_common(ah);
186 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530187 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530188 enum htc_phymode mode;
189 __be16 htc_mode;
190 u8 cmd_rsp;
191 int ret;
192
193 mutex_lock(&priv->mutex);
194 ath9k_htc_ps_wakeup(priv);
195
Sujith Manoharana2362542011-02-21 07:49:38 +0530196 ath9k_htc_stop_ani(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530197 ieee80211_stop_queues(priv->hw);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530198
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530199 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530200 ath9k_htc_tx_drain(priv);
201
Sujith Manoharan73908672010-12-28 14:28:27 +0530202 WMI_CMD(WMI_DISABLE_INTR_CMDID);
203 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
204 WMI_CMD(WMI_STOP_RECV_CMDID);
205
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530206 ath9k_wmi_event_drain(priv);
207
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530208 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530209 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
210 if (ret) {
211 ath_err(common,
212 "Unable to reset device (%u Mhz) reset status %d\n",
213 channel->center_freq, ret);
214 }
215
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530216 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
217 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530218
219 WMI_CMD(WMI_START_RECV_CMDID);
220 ath9k_host_rx_init(priv);
221
222 mode = ath9k_htc_get_curmode(priv, ah->curchan);
223 htc_mode = cpu_to_be16(mode);
224 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
225
226 WMI_CMD(WMI_ENABLE_INTR_CMDID);
227 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530228 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530229 ieee80211_wake_queues(priv->hw);
230
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530231 mod_timer(&priv->tx.cleanup_timer,
232 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
233
Sujith Manoharan73908672010-12-28 14:28:27 +0530234 ath9k_htc_ps_restore(priv);
235 mutex_unlock(&priv->mutex);
236}
237
Sujithfb9987d2010-03-17 14:25:25 +0530238static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
239 struct ieee80211_hw *hw,
240 struct ath9k_channel *hchan)
241{
242 struct ath_hw *ah = priv->ah;
243 struct ath_common *common = ath9k_hw_common(ah);
244 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530245 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530246 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530247 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530248 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530249 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530250 u8 cmd_rsp;
251 int ret;
252
253 if (priv->op_flags & OP_INVALID)
254 return -EIO;
255
Sujith Manoharan039a0722010-12-28 14:28:37 +0530256 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530257
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530258 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530259
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530260 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530261 ath9k_htc_tx_drain(priv);
262
Sujithfb9987d2010-03-17 14:25:25 +0530263 WMI_CMD(WMI_DISABLE_INTR_CMDID);
264 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
265 WMI_CMD(WMI_STOP_RECV_CMDID);
266
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530267 ath9k_wmi_event_drain(priv);
268
Joe Perches226afe62010-12-02 19:12:37 -0800269 ath_dbg(common, ATH_DBG_CONFIG,
270 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
271 priv->ah->curchan->channel,
272 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
273 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530274
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530275 if (!fastcc)
276 caldata = &priv->caldata;
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530277
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200278 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530279 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800280 ath_err(common,
281 "Unable to reset channel (%u Mhz) reset status %d\n",
282 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530283 goto err;
284 }
285
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530286 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
287 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530288
289 WMI_CMD(WMI_START_RECV_CMDID);
290 if (ret)
291 goto err;
292
293 ath9k_host_rx_init(priv);
294
295 mode = ath9k_htc_get_curmode(priv, hchan);
296 htc_mode = cpu_to_be16(mode);
297 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
298 if (ret)
299 goto err;
300
301 WMI_CMD(WMI_ENABLE_INTR_CMDID);
302 if (ret)
303 goto err;
304
305 htc_start(priv->htc);
Sujith Manoharana5fae372011-02-21 07:49:53 +0530306
307 if (!(priv->op_flags & OP_SCANNING) &&
308 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
309 ath9k_htc_vif_reconfig(priv);
310
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530311 mod_timer(&priv->tx.cleanup_timer,
312 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
313
Sujithfb9987d2010-03-17 14:25:25 +0530314err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530315 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530316 return ret;
317}
318
Sujith Manoharana97b4782011-02-21 07:48:00 +0530319/*
320 * Monitor mode handling is a tad complicated because the firmware requires
321 * an interface to be created exclusively, while mac80211 doesn't associate
322 * an interface with the mode.
323 *
324 * So, for now, only one monitor interface can be configured.
325 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530326static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530327{
328 struct ath_common *common = ath9k_hw_common(priv->ah);
329 struct ath9k_htc_target_vif hvif;
330 int ret = 0;
331 u8 cmd_rsp;
332
Sujith Manoharancc721282011-01-03 21:22:18 +0530333 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
334 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530335 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530336 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +0530337 if (ret) {
338 ath_err(common, "Unable to remove monitor interface at idx: %d\n",
339 priv->mon_vif_idx);
340 }
341
Sujith Manoharancc721282011-01-03 21:22:18 +0530342 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530343 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530344}
345
346static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
347{
348 struct ath_common *common = ath9k_hw_common(priv->ah);
349 struct ath9k_htc_target_vif hvif;
350 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530351 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530352 u8 cmd_rsp;
353
Sujith Manoharana97b4782011-02-21 07:48:00 +0530354 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
355 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
356 ret = -ENOBUFS;
357 goto err_vif;
358 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530359
Sujith Manoharana97b4782011-02-21 07:48:00 +0530360 sta_idx = ffz(priv->sta_slot);
361 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
362 ret = -ENOBUFS;
363 goto err_vif;
364 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530365
366 /*
367 * Add an interface.
368 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530369 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
370 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
371
Sujith Manoharane4c62502011-04-13 11:24:43 +0530372 hvif.opmode = HTC_M_MONITOR;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530373 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530374
375 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
376 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530377 goto err_vif;
378
379 /*
380 * Assign the monitor interface index as a special case here.
381 * This is needed when the interface is brought down.
382 */
383 priv->mon_vif_idx = hvif.index;
384 priv->vif_slot |= (1 << hvif.index);
385
386 /*
387 * Set the hardware mode to monitor only if there are no
388 * other interfaces.
389 */
390 if (!priv->nvifs)
391 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530392
393 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530394
395 /*
396 * Associate a station with the interface for packet injection.
397 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530398 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
399
400 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
401
402 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530403 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530404 tsta.vif_index = hvif.index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530405 tsta.maxampdu = cpu_to_be16(0xffff);
Sujith Manoharancc721282011-01-03 21:22:18 +0530406
407 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
408 if (ret) {
409 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530410 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530411 }
412
Sujith Manoharana97b4782011-02-21 07:48:00 +0530413 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530414 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530415 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530416 priv->ah->is_monitoring = true;
417
Sujith Manoharana97b4782011-02-21 07:48:00 +0530418 ath_dbg(common, ATH_DBG_CONFIG,
419 "Attached a monitor interface at idx: %d, sta idx: %d\n",
420 priv->mon_vif_idx, sta_idx);
421
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530422 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530423
Sujith Manoharana97b4782011-02-21 07:48:00 +0530424err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530425 /*
426 * Remove the interface from the target.
427 */
428 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530429err_vif:
430 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
431
Sujith Manoharancc721282011-01-03 21:22:18 +0530432 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530433}
434
435static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
436{
437 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530438 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530439 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530440
Sujith Manoharancc721282011-01-03 21:22:18 +0530441 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530442
Sujith Manoharana97b4782011-02-21 07:48:00 +0530443 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530444
445 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
446 if (ret) {
447 ath_err(common, "Unable to remove station entry for monitor mode\n");
448 return ret;
449 }
450
Sujith Manoharana97b4782011-02-21 07:48:00 +0530451 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530452 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530453 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530454
Sujith Manoharana97b4782011-02-21 07:48:00 +0530455 ath_dbg(common, ATH_DBG_CONFIG,
456 "Removed a monitor interface at idx: %d, sta idx: %d\n",
457 priv->mon_vif_idx, sta_idx);
458
Sujith Manoharancc721282011-01-03 21:22:18 +0530459 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530460}
461
Sujithfb9987d2010-03-17 14:25:25 +0530462static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
463 struct ieee80211_vif *vif,
464 struct ieee80211_sta *sta)
465{
466 struct ath_common *common = ath9k_hw_common(priv->ah);
467 struct ath9k_htc_target_sta tsta;
468 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
469 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530470 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530471 u8 cmd_rsp;
Sujith Manoharanf0dd4982011-04-20 11:01:00 +0530472 u16 maxampdu;
Sujithfb9987d2010-03-17 14:25:25 +0530473
474 if (priv->nstations >= ATH9K_HTC_MAX_STA)
475 return -ENOBUFS;
476
Sujith Manoharana97b4782011-02-21 07:48:00 +0530477 sta_idx = ffz(priv->sta_slot);
478 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
479 return -ENOBUFS;
480
Sujithfb9987d2010-03-17 14:25:25 +0530481 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
482
483 if (sta) {
484 ista = (struct ath9k_htc_sta *) sta->drv_priv;
485 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
486 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
Sujithfb9987d2010-03-17 14:25:25 +0530487 tsta.is_vif_sta = 0;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530488 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530489 } else {
490 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
491 tsta.is_vif_sta = 1;
492 }
493
Sujith Manoharana97b4782011-02-21 07:48:00 +0530494 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530495 tsta.vif_index = avp->index;
Sujith Manoharanf0dd4982011-04-20 11:01:00 +0530496
497 if (!sta) {
498 tsta.maxampdu = cpu_to_be16(0xffff);
499 } else {
500 maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
501 sta->ht_cap.ampdu_factor);
502 tsta.maxampdu = cpu_to_be16(maxampdu);
503 }
504
Sujithfb9987d2010-03-17 14:25:25 +0530505 if (sta && sta->ht_cap.ht_supported)
506 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
507
508 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
509 if (ret) {
510 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800511 ath_err(common,
512 "Unable to add station entry for: %pM\n",
513 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530514 return ret;
515 }
516
Sujith Manoharana97b4782011-02-21 07:48:00 +0530517 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800518 ath_dbg(common, ATH_DBG_CONFIG,
519 "Added a station entry for: %pM (idx: %d)\n",
520 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530521 } else {
522 ath_dbg(common, ATH_DBG_CONFIG,
523 "Added a station entry for VIF %d (idx: %d)\n",
524 avp->index, tsta.sta_index);
525 }
Sujithfb9987d2010-03-17 14:25:25 +0530526
Sujith Manoharana97b4782011-02-21 07:48:00 +0530527 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530528 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530529 if (!sta)
530 priv->vif_sta_pos[avp->index] = sta_idx;
531
Sujithfb9987d2010-03-17 14:25:25 +0530532 return 0;
533}
534
535static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
536 struct ieee80211_vif *vif,
537 struct ieee80211_sta *sta)
538{
539 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530540 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530541 struct ath9k_htc_sta *ista;
542 int ret;
543 u8 cmd_rsp, sta_idx;
544
545 if (sta) {
546 ista = (struct ath9k_htc_sta *) sta->drv_priv;
547 sta_idx = ista->index;
548 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530549 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530550 }
551
552 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
553 if (ret) {
554 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800555 ath_err(common,
556 "Unable to remove station entry for: %pM\n",
557 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530558 return ret;
559 }
560
Sujith Manoharana97b4782011-02-21 07:48:00 +0530561 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800562 ath_dbg(common, ATH_DBG_CONFIG,
563 "Removed a station entry for: %pM (idx: %d)\n",
564 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530565 } else {
566 ath_dbg(common, ATH_DBG_CONFIG,
567 "Removed a station entry for VIF %d (idx: %d)\n",
568 avp->index, sta_idx);
569 }
Sujithfb9987d2010-03-17 14:25:25 +0530570
Sujith Manoharana97b4782011-02-21 07:48:00 +0530571 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530572 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530573
Sujithfb9987d2010-03-17 14:25:25 +0530574 return 0;
575}
576
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530577int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv,
578 u8 enable_coex)
Sujithfb9987d2010-03-17 14:25:25 +0530579{
580 struct ath9k_htc_cap_target tcap;
581 int ret;
582 u8 cmd_rsp;
583
584 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
585
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530586 tcap.ampdu_limit = cpu_to_be32(0xffff);
587 tcap.ampdu_subframes = priv->hw->max_tx_aggregation_subframes;
588 tcap.enable_coex = enable_coex;
Sujith29d90752010-06-02 15:53:43 +0530589 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530590
591 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
592
593 return ret;
594}
595
Sujith0d425a72010-05-17 12:01:16 +0530596static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
597 struct ieee80211_sta *sta,
598 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530599{
Sujithfb9987d2010-03-17 14:25:25 +0530600 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
601 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530602 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530603 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530604
Sujithea46e642010-06-02 15:53:50 +0530605 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530606
607 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
608 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530609 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530610 = (sband->bitrates[i].bitrate * 2) / 10;
611 j++;
612 }
613 }
Sujith0d425a72010-05-17 12:01:16 +0530614 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530615
616 if (sta->ht_cap.ht_supported) {
617 for (i = 0, j = 0; i < 77; i++) {
618 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530619 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530620 if (j == ATH_HTC_RATE_MAX)
621 break;
622 }
Sujith0d425a72010-05-17 12:01:16 +0530623 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530624
625 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200626 if (sta->ht_cap.mcs.rx_mask[1])
627 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530628 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
629 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530630 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530631 if (conf_is_ht40(&priv->hw->conf) &&
632 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530633 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530634 else if (conf_is_ht20(&priv->hw->conf) &&
635 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
636 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530637 }
638
Sujith0d425a72010-05-17 12:01:16 +0530639 trate->sta_index = ista->index;
640 trate->isnew = 1;
641 trate->capflags = cpu_to_be32(caps);
642}
Sujithfb9987d2010-03-17 14:25:25 +0530643
Sujith0d425a72010-05-17 12:01:16 +0530644static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
645 struct ath9k_htc_target_rate *trate)
646{
647 struct ath_common *common = ath9k_hw_common(priv->ah);
648 int ret;
649 u8 cmd_rsp;
650
651 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530652 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800653 ath_err(common,
654 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530655 }
656
Sujith0d425a72010-05-17 12:01:16 +0530657 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530658}
659
Sujith0d425a72010-05-17 12:01:16 +0530660static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
661 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530662{
Sujithfb9987d2010-03-17 14:25:25 +0530663 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530664 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530665 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530666
Sujith0d425a72010-05-17 12:01:16 +0530667 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
668 ath9k_htc_setup_rate(priv, sta, &trate);
669 ret = ath9k_htc_send_rate_cmd(priv, &trate);
670 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800671 ath_dbg(common, ATH_DBG_CONFIG,
672 "Updated target sta: %pM, rate caps: 0x%X\n",
673 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530674}
675
Sujith2c76ef82010-05-17 12:01:18 +0530676static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
677 struct ieee80211_vif *vif,
678 struct ieee80211_bss_conf *bss_conf)
679{
680 struct ath_common *common = ath9k_hw_common(priv->ah);
681 struct ath9k_htc_target_rate trate;
682 struct ieee80211_sta *sta;
683 int ret;
684
685 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
686
687 rcu_read_lock();
688 sta = ieee80211_find_sta(vif, bss_conf->bssid);
689 if (!sta) {
690 rcu_read_unlock();
691 return;
692 }
693 ath9k_htc_setup_rate(priv, sta, &trate);
694 rcu_read_unlock();
695
696 ret = ath9k_htc_send_rate_cmd(priv, &trate);
697 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800698 ath_dbg(common, ATH_DBG_CONFIG,
699 "Updated target sta: %pM, rate caps: 0x%X\n",
700 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530701}
702
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400703static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
704 struct ieee80211_vif *vif,
705 struct ieee80211_sta *sta,
706 enum ieee80211_ampdu_mlme_action action,
707 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530708{
709 struct ath_common *common = ath9k_hw_common(priv->ah);
710 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200711 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530712 int ret = 0;
713 u8 cmd_rsp;
714
Dan Carpenter0730d112010-05-08 18:24:02 +0200715 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530716 return -EINVAL;
717
Sujithfb9987d2010-03-17 14:25:25 +0530718 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530719 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530720
Sujithef98c3c2010-03-29 16:07:11 +0530721 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530722 aggr.tidno = tid & 0xf;
723 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530724
Sujithfb9987d2010-03-17 14:25:25 +0530725 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
726 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800727 ath_dbg(common, ATH_DBG_CONFIG,
728 "Unable to %s TX aggregation for (%pM, %d)\n",
729 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530730 else
Joe Perches226afe62010-12-02 19:12:37 -0800731 ath_dbg(common, ATH_DBG_CONFIG,
732 "%s TX aggregation for (%pM, %d)\n",
733 (aggr.aggr_enable) ? "Starting" : "Stopping",
734 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530735
Sujith Manoharan658ef042011-04-13 11:25:00 +0530736 spin_lock_bh(&priv->tx.tx_lock);
Sujithd7ca2132010-06-15 10:24:37 +0530737 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530738 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530739
740 return ret;
741}
742
Sujithfb9987d2010-03-17 14:25:25 +0530743/*******/
744/* ANI */
745/*******/
746
Sujith Manoharana2362542011-02-21 07:49:38 +0530747void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530748{
749 struct ath_common *common = ath9k_hw_common(priv->ah);
750 unsigned long timestamp = jiffies_to_msecs(jiffies);
751
752 common->ani.longcal_timer = timestamp;
753 common->ani.shortcal_timer = timestamp;
754 common->ani.checkani_timer = timestamp;
755
Sujith Manoharana2362542011-02-21 07:49:38 +0530756 priv->op_flags |= OP_ANI_RUNNING;
757
758 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530759 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
760}
761
Sujith Manoharana2362542011-02-21 07:49:38 +0530762void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
763{
764 cancel_delayed_work_sync(&priv->ani_work);
765 priv->op_flags &= ~OP_ANI_RUNNING;
766}
767
768void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530769{
770 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530771 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530772 struct ath_hw *ah = priv->ah;
773 struct ath_common *common = ath9k_hw_common(ah);
774 bool longcal = false;
775 bool shortcal = false;
776 bool aniflag = false;
777 unsigned int timestamp = jiffies_to_msecs(jiffies);
778 u32 cal_interval, short_cal_interval;
779
Sujith Manoharana2362542011-02-21 07:49:38 +0530780 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
781 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530782
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530783 /* Only calibrate if awake */
784 if (ah->power_mode != ATH9K_PM_AWAKE)
785 goto set_timer;
786
Sujithfb9987d2010-03-17 14:25:25 +0530787 /* Long calibration runs independently of short calibration. */
788 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
789 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800790 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530791 common->ani.longcal_timer = timestamp;
792 }
793
794 /* Short calibration applies only while caldone is false */
795 if (!common->ani.caldone) {
796 if ((timestamp - common->ani.shortcal_timer) >=
797 short_cal_interval) {
798 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800799 ath_dbg(common, ATH_DBG_ANI,
800 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530801 common->ani.shortcal_timer = timestamp;
802 common->ani.resetcal_timer = timestamp;
803 }
804 } else {
805 if ((timestamp - common->ani.resetcal_timer) >=
806 ATH_RESTART_CALINTERVAL) {
807 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
808 if (common->ani.caldone)
809 common->ani.resetcal_timer = timestamp;
810 }
811 }
812
813 /* Verify whether we must check ANI */
814 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
815 aniflag = true;
816 common->ani.checkani_timer = timestamp;
817 }
818
819 /* Skip all processing if there's nothing to do. */
820 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530821
822 ath9k_htc_ps_wakeup(priv);
823
Sujithfb9987d2010-03-17 14:25:25 +0530824 /* Call ANI routine if necessary */
825 if (aniflag)
826 ath9k_hw_ani_monitor(ah, ah->curchan);
827
828 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200829 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530830 common->ani.caldone =
831 ath9k_hw_calibrate(ah, ah->curchan,
832 common->rx_chainmask,
833 longcal);
834
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530835 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530836 }
837
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530838set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530839 /*
840 * Set timer interval based on previous results.
841 * The interval must be the shortest necessary to satisfy ANI,
842 * short calibration and long calibration.
843 */
844 cal_interval = ATH_LONG_CALINTERVAL;
845 if (priv->ah->config.enable_ani)
846 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
847 if (!common->ani.caldone)
848 cal_interval = min(cal_interval, (u32)short_cal_interval);
849
Sujith Manoharana2362542011-02-21 07:49:38 +0530850 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530851 msecs_to_jiffies(cal_interval));
852}
853
Sujithfb9987d2010-03-17 14:25:25 +0530854/**********************/
855/* mac80211 Callbacks */
856/**********************/
857
Johannes Berg7bb45682011-02-24 14:42:06 +0100858static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +0530859{
860 struct ieee80211_hdr *hdr;
861 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530862 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530863 int padpos, padsize, ret, slot;
Sujithfb9987d2010-03-17 14:25:25 +0530864
865 hdr = (struct ieee80211_hdr *) skb->data;
866
867 /* Add the padding after the header if this is not already done */
868 padpos = ath9k_cmn_padpos(hdr->frame_control);
869 padsize = padpos & 3;
870 if (padsize && skb->len > padpos) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530871 if (skb_headroom(skb) < padsize) {
872 ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n");
Johannes Berg7bb45682011-02-24 14:42:06 +0100873 goto fail_tx;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530874 }
Sujithfb9987d2010-03-17 14:25:25 +0530875 skb_push(skb, padsize);
876 memmove(skb->data, skb->data + padsize, padpos);
877 }
878
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530879 slot = ath9k_htc_tx_get_slot(priv);
880 if (slot < 0) {
881 ath_dbg(common, ATH_DBG_XMIT, "No free TX slot\n");
882 goto fail_tx;
883 }
884
885 ret = ath9k_htc_tx_start(priv, skb, slot, false);
Sujith7757dfe2010-03-29 16:07:17 +0530886 if (ret != 0) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530887 ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530888 goto clear_slot;
Sujithfb9987d2010-03-17 14:25:25 +0530889 }
890
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530891 ath9k_htc_check_stop_queues(priv);
892
Johannes Berg7bb45682011-02-24 14:42:06 +0100893 return;
Sujithfb9987d2010-03-17 14:25:25 +0530894
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530895clear_slot:
896 ath9k_htc_tx_clear_slot(priv, slot);
Sujithfb9987d2010-03-17 14:25:25 +0530897fail_tx:
898 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530899}
900
Sujith881ac6a2010-06-01 15:14:11 +0530901static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530902{
903 struct ath9k_htc_priv *priv = hw->priv;
904 struct ath_hw *ah = priv->ah;
905 struct ath_common *common = ath9k_hw_common(ah);
906 struct ieee80211_channel *curchan = hw->conf.channel;
907 struct ath9k_channel *init_channel;
908 int ret = 0;
909 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530910 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530911 u8 cmd_rsp;
912
Sujith881ac6a2010-06-01 15:14:11 +0530913 mutex_lock(&priv->mutex);
914
Joe Perches226afe62010-12-02 19:12:37 -0800915 ath_dbg(common, ATH_DBG_CONFIG,
916 "Starting driver with initial channel: %d MHz\n",
917 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530918
Sujith21d51302010-06-01 15:14:18 +0530919 /* Ensure that HW is awake before flushing RX */
920 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
921 WMI_CMD(WMI_FLUSH_RECV_CMDID);
922
Sujithfb9987d2010-03-17 14:25:25 +0530923 /* setup initial channel */
924 init_channel = ath9k_cmn_get_curchannel(hw, ah);
925
Sujithfb9987d2010-03-17 14:25:25 +0530926 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200927 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530928 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800929 ath_err(common,
930 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
931 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530932 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530933 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530934 }
935
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530936 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
937 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530938
939 mode = ath9k_htc_get_curmode(priv, init_channel);
940 htc_mode = cpu_to_be16(mode);
941 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530942 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530943 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530944
945 ath9k_host_rx_init(priv);
946
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530947 ret = ath9k_htc_update_cap_target(priv, 0);
Sujith Manoharan1057b752011-02-21 07:48:09 +0530948 if (ret)
949 ath_dbg(common, ATH_DBG_CONFIG,
950 "Failed to update capability in target\n");
951
Sujithfb9987d2010-03-17 14:25:25 +0530952 priv->op_flags &= ~OP_INVALID;
953 htc_start(priv->htc);
954
Sujith Manoharan658ef042011-04-13 11:25:00 +0530955 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530956 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530957 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530958
959 ieee80211_wake_queues(hw);
960
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530961 mod_timer(&priv->tx.cleanup_timer,
962 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
963
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530964 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
965 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
966 AR_STOMP_LOW_WLAN_WGHT);
967 ath9k_hw_btcoex_enable(ah);
968 ath_htc_resume_btcoex_work(priv);
969 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530970 mutex_unlock(&priv->mutex);
971
972 return ret;
973}
974
Sujith881ac6a2010-06-01 15:14:11 +0530975static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530976{
977 struct ath9k_htc_priv *priv = hw->priv;
978 struct ath_hw *ah = priv->ah;
979 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +0530980 int ret __attribute__ ((unused));
Sujithfb9987d2010-03-17 14:25:25 +0530981 u8 cmd_rsp;
982
Sujith881ac6a2010-06-01 15:14:11 +0530983 mutex_lock(&priv->mutex);
984
Sujithfb9987d2010-03-17 14:25:25 +0530985 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -0800986 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530987 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530988 return;
989 }
990
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530991 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530992
Sujithfb9987d2010-03-17 14:25:25 +0530993 WMI_CMD(WMI_DISABLE_INTR_CMDID);
994 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
995 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100996
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100997 tasklet_kill(&priv->rx_tasklet);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100998
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530999 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +05301000 ath9k_htc_tx_drain(priv);
Sujith Manoharanf4c88992011-04-13 11:23:52 +05301001 ath9k_wmi_event_drain(priv);
1002
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001003 mutex_unlock(&priv->mutex);
1004
1005 /* Cancel all the running timers/work .. */
1006 cancel_work_sync(&priv->fatal_work);
1007 cancel_work_sync(&priv->ps_work);
Sujith Manoharand244f212011-04-28 16:14:05 +05301008
1009#ifdef CONFIG_MAC80211_LEDS
1010 cancel_work_sync(&priv->led_work);
1011#endif
Sujith Manoharana2362542011-02-21 07:49:38 +05301012 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001013
1014 mutex_lock(&priv->mutex);
1015
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301016 if (ah->btcoex_hw.enabled) {
1017 ath9k_hw_btcoex_disable(ah);
1018 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1019 ath_htc_cancel_btcoex_work(priv);
1020 }
1021
Sujith Manoharana97b4782011-02-21 07:48:00 +05301022 /* Remove a monitor interface if it's present. */
1023 if (priv->ah->is_monitoring)
1024 ath9k_htc_remove_monitor_interface(priv);
1025
Sujithe9201f02010-06-01 15:14:17 +05301026 ath9k_hw_phy_disable(ah);
1027 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301028 ath9k_htc_ps_restore(priv);
1029 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1030
Sujithfb9987d2010-03-17 14:25:25 +05301031 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301032
Joe Perches226afe62010-12-02 19:12:37 -08001033 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301034 mutex_unlock(&priv->mutex);
1035}
1036
Sujithfb9987d2010-03-17 14:25:25 +05301037static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1038 struct ieee80211_vif *vif)
1039{
1040 struct ath9k_htc_priv *priv = hw->priv;
1041 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1042 struct ath_common *common = ath9k_hw_common(priv->ah);
1043 struct ath9k_htc_target_vif hvif;
1044 int ret = 0;
1045 u8 cmd_rsp;
1046
1047 mutex_lock(&priv->mutex);
1048
Sujith Manoharana97b4782011-02-21 07:48:00 +05301049 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301050 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301051 return -ENOBUFS;
1052 }
1053
1054 if (priv->num_ibss_vif ||
1055 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1056 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1057 mutex_unlock(&priv->mutex);
1058 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301059 }
1060
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301061 if (((vif->type == NL80211_IFTYPE_AP) ||
1062 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1063 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1064 ath_err(common, "Max. number of beaconing interfaces reached\n");
1065 mutex_unlock(&priv->mutex);
1066 return -ENOBUFS;
1067 }
1068
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301069 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301070 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1071 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1072
1073 switch (vif->type) {
1074 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301075 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301076 break;
1077 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301078 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301079 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301080 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301081 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301082 break;
Sujithfb9987d2010-03-17 14:25:25 +05301083 default:
Joe Perches38002762010-12-02 19:12:36 -08001084 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301085 "Interface type %d not yet supported\n", vif->type);
1086 ret = -EOPNOTSUPP;
1087 goto out;
1088 }
1089
Sujithfb9987d2010-03-17 14:25:25 +05301090 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301091 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301092 hvif.rtsthreshold = cpu_to_be16(2304);
1093 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1094 if (ret)
1095 goto out;
1096
Sujithfb9987d2010-03-17 14:25:25 +05301097 /*
1098 * We need a node in target to tx mgmt frames
1099 * before association.
1100 */
1101 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301102 if (ret) {
1103 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301104 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301105 }
Sujithfb9987d2010-03-17 14:25:25 +05301106
Sujith Manoharan585895c2011-02-21 07:48:46 +05301107 ath9k_htc_set_bssid_mask(priv, vif);
1108
Sujith Manoharana97b4782011-02-21 07:48:00 +05301109 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301110 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301111
Sujith Manoharan0df83592011-02-21 07:49:15 +05301112 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301113
1114 if ((vif->type == NL80211_IFTYPE_AP) ||
1115 (vif->type == NL80211_IFTYPE_ADHOC))
1116 ath9k_htc_assign_bslot(priv, vif);
1117
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301118 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301119
Sujith Manoharana2362542011-02-21 07:49:38 +05301120 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301121 !(priv->op_flags & OP_ANI_RUNNING)) {
1122 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301123 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301124 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301125
Sujith Manoharana97b4782011-02-21 07:48:00 +05301126 ath_dbg(common, ATH_DBG_CONFIG,
1127 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1128
Sujithfb9987d2010-03-17 14:25:25 +05301129out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301130 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301131 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301132
Sujithfb9987d2010-03-17 14:25:25 +05301133 return ret;
1134}
1135
1136static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1137 struct ieee80211_vif *vif)
1138{
1139 struct ath9k_htc_priv *priv = hw->priv;
1140 struct ath_common *common = ath9k_hw_common(priv->ah);
1141 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1142 struct ath9k_htc_target_vif hvif;
1143 int ret = 0;
1144 u8 cmd_rsp;
1145
Sujithfb9987d2010-03-17 14:25:25 +05301146 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301147 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301148
1149 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1150 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1151 hvif.index = avp->index;
1152 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +05301153 if (ret) {
1154 ath_err(common, "Unable to remove interface at idx: %d\n",
1155 avp->index);
1156 }
Sujithfb9987d2010-03-17 14:25:25 +05301157 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301158 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301159
1160 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301161
Sujith Manoharan0df83592011-02-21 07:49:15 +05301162 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301163
1164 if ((vif->type == NL80211_IFTYPE_AP) ||
1165 (vif->type == NL80211_IFTYPE_ADHOC))
1166 ath9k_htc_remove_bslot(priv, vif);
1167
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301168 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301169
Sujith Manoharana2362542011-02-21 07:49:38 +05301170 /*
1171 * Stop ANI only if there are no associated station interfaces.
1172 */
1173 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1174 priv->rearm_ani = false;
1175 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1176 ath9k_htc_vif_iter, priv);
1177 if (!priv->rearm_ani)
1178 ath9k_htc_stop_ani(priv);
1179 }
1180
Sujith Manoharana97b4782011-02-21 07:48:00 +05301181 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1182
Sujithcb551df2010-06-01 15:14:12 +05301183 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301184 mutex_unlock(&priv->mutex);
1185}
1186
1187static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1188{
1189 struct ath9k_htc_priv *priv = hw->priv;
1190 struct ath_common *common = ath9k_hw_common(priv->ah);
1191 struct ieee80211_conf *conf = &hw->conf;
1192
1193 mutex_lock(&priv->mutex);
1194
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301195 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1196 bool enable_radio = false;
1197 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1198
Sujith23367762010-06-01 15:14:16 +05301199 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301200 if (!idle && priv->ps_idle)
1201 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301202 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301203 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301204
1205 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001206 ath_dbg(common, ATH_DBG_CONFIG,
1207 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301208 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1209 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301210 }
1211 }
1212
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301213 /*
1214 * Monitor interface should be added before
1215 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1216 */
1217 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301218 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1219 !priv->ah->is_monitoring)
1220 ath9k_htc_add_monitor_interface(priv);
1221 else if (priv->ah->is_monitoring)
1222 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301223 }
1224
Sujithfb9987d2010-03-17 14:25:25 +05301225 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1226 struct ieee80211_channel *curchan = hw->conf.channel;
1227 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301228
Joe Perches226afe62010-12-02 19:12:37 -08001229 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1230 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301231
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001232 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1233 hw->conf.channel,
1234 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301235
1236 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001237 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301238 mutex_unlock(&priv->mutex);
1239 return -EINVAL;
1240 }
1241
1242 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301243
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301244 if (changed & IEEE80211_CONF_CHANGE_PS) {
1245 if (conf->flags & IEEE80211_CONF_PS) {
1246 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1247 priv->ps_enabled = true;
1248 } else {
1249 priv->ps_enabled = false;
1250 cancel_work_sync(&priv->ps_work);
1251 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1252 }
1253 }
Sujithfb9987d2010-03-17 14:25:25 +05301254
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301255 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1256 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301257 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1258 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301259 }
1260
Sujith23367762010-06-01 15:14:16 +05301261 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1262 mutex_lock(&priv->htc_pm_lock);
1263 if (!priv->ps_idle) {
1264 mutex_unlock(&priv->htc_pm_lock);
1265 goto out;
1266 }
1267 mutex_unlock(&priv->htc_pm_lock);
1268
Joe Perches226afe62010-12-02 19:12:37 -08001269 ath_dbg(common, ATH_DBG_CONFIG,
1270 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301271 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301272 }
1273
Sujith23367762010-06-01 15:14:16 +05301274out:
Sujithfb9987d2010-03-17 14:25:25 +05301275 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301276 return 0;
1277}
1278
1279#define SUPPORTED_FILTERS \
1280 (FIF_PROMISC_IN_BSS | \
1281 FIF_ALLMULTI | \
1282 FIF_CONTROL | \
1283 FIF_PSPOLL | \
1284 FIF_OTHER_BSS | \
1285 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301286 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301287 FIF_FCSFAIL)
1288
1289static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1290 unsigned int changed_flags,
1291 unsigned int *total_flags,
1292 u64 multicast)
1293{
1294 struct ath9k_htc_priv *priv = hw->priv;
1295 u32 rfilt;
1296
1297 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301298 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301299
Sujithfb9987d2010-03-17 14:25:25 +05301300 changed_flags &= SUPPORTED_FILTERS;
1301 *total_flags &= SUPPORTED_FILTERS;
1302
1303 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301304 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301305 ath9k_hw_setrxfilter(priv->ah, rfilt);
1306
Joe Perches226afe62010-12-02 19:12:37 -08001307 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1308 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301309
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301310 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301311 mutex_unlock(&priv->mutex);
1312}
1313
Sujithabd984e2010-05-18 15:26:04 +05301314static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1315 struct ieee80211_vif *vif,
1316 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301317{
1318 struct ath9k_htc_priv *priv = hw->priv;
1319 int ret;
1320
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301321 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301322 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301323 ret = ath9k_htc_add_station(priv, vif, sta);
1324 if (!ret)
1325 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301326 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301327 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301328
1329 return ret;
1330}
1331
1332static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1333 struct ieee80211_vif *vif,
1334 struct ieee80211_sta *sta)
1335{
1336 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301337 struct ath9k_htc_sta *ista;
Sujithabd984e2010-05-18 15:26:04 +05301338 int ret;
1339
1340 mutex_lock(&priv->mutex);
1341 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301342 ista = (struct ath9k_htc_sta *) sta->drv_priv;
1343 htc_sta_drain(priv->htc, ista->index);
Sujithabd984e2010-05-18 15:26:04 +05301344 ret = ath9k_htc_remove_station(priv, vif, sta);
1345 ath9k_htc_ps_restore(priv);
1346 mutex_unlock(&priv->mutex);
1347
1348 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301349}
1350
1351static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1352 const struct ieee80211_tx_queue_params *params)
1353{
1354 struct ath9k_htc_priv *priv = hw->priv;
1355 struct ath_common *common = ath9k_hw_common(priv->ah);
1356 struct ath9k_tx_queue_info qi;
1357 int ret = 0, qnum;
1358
1359 if (queue >= WME_NUM_AC)
1360 return 0;
1361
1362 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301363 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301364
1365 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1366
1367 qi.tqi_aifs = params->aifs;
1368 qi.tqi_cwmin = params->cw_min;
1369 qi.tqi_cwmax = params->cw_max;
1370 qi.tqi_burstTime = params->txop;
1371
1372 qnum = get_hw_qnum(queue, priv->hwq_map);
1373
Joe Perches226afe62010-12-02 19:12:37 -08001374 ath_dbg(common, ATH_DBG_CONFIG,
1375 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1376 queue, qnum, params->aifs, params->cw_min,
1377 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301378
Sujithe1572c52010-03-24 13:42:13 +05301379 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301380 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001381 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301382 goto out;
1383 }
Sujithfb9987d2010-03-17 14:25:25 +05301384
Sujith764580f2010-06-01 15:14:19 +05301385 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001386 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301387 ath9k_htc_beaconq_config(priv);
1388out:
Sujithcb551df2010-06-01 15:14:12 +05301389 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301390 mutex_unlock(&priv->mutex);
1391
1392 return ret;
1393}
1394
1395static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1396 enum set_key_cmd cmd,
1397 struct ieee80211_vif *vif,
1398 struct ieee80211_sta *sta,
1399 struct ieee80211_key_conf *key)
1400{
1401 struct ath9k_htc_priv *priv = hw->priv;
1402 struct ath_common *common = ath9k_hw_common(priv->ah);
1403 int ret = 0;
1404
Sujithe1572c52010-03-24 13:42:13 +05301405 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301406 return -ENOSPC;
1407
1408 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001409 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301410 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301411
1412 switch (cmd) {
1413 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001414 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301415 if (ret >= 0) {
1416 key->hw_key_idx = ret;
1417 /* push IV and Michael MIC generation to stack */
1418 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001419 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301420 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001421 if (priv->ah->sw_mgmt_crypto &&
1422 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301423 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1424 ret = 0;
1425 }
1426 break;
1427 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001428 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301429 break;
1430 default:
1431 ret = -EINVAL;
1432 }
1433
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301434 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301435 mutex_unlock(&priv->mutex);
1436
1437 return ret;
1438}
1439
1440static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1441 struct ieee80211_vif *vif,
1442 struct ieee80211_bss_conf *bss_conf,
1443 u32 changed)
1444{
1445 struct ath9k_htc_priv *priv = hw->priv;
1446 struct ath_hw *ah = priv->ah;
1447 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301448 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301449
1450 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301451 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301452
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301453 /*
1454 * Set the HW AID/BSSID only for the first station interface
1455 * or in IBSS mode.
1456 */
1457 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1458 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1459 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301460
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301461
1462 if (changed & BSS_CHANGED_ASSOC) {
1463 if (set_assoc) {
1464 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1465 bss_conf->assoc);
1466
1467 common->curaid = bss_conf->assoc ?
1468 bss_conf->aid : 0;
1469
1470 if (bss_conf->assoc)
1471 ath9k_htc_start_ani(priv);
1472 else
1473 ath9k_htc_stop_ani(priv);
1474 }
Sujithfb9987d2010-03-17 14:25:25 +05301475 }
1476
1477 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301478 if (set_assoc) {
1479 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1480 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301481
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301482 ath_dbg(common, ATH_DBG_CONFIG,
1483 "BSSID: %pM aid: 0x%x\n",
1484 common->curbssid, common->curaid);
1485 }
Sujithfb9987d2010-03-17 14:25:25 +05301486 }
1487
Sujith Manoharana5fae372011-02-21 07:49:53 +05301488 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1489 ath_dbg(common, ATH_DBG_CONFIG,
1490 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301491 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301492 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301493 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301494 }
1495
Sujith Manoharana5fae372011-02-21 07:49:53 +05301496 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1497 /*
1498 * Disable SWBA interrupt only if there are no
1499 * AP/IBSS interfaces.
1500 */
1501 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1502 ath_dbg(common, ATH_DBG_CONFIG,
1503 "Beacon disabled for BSS: %pM\n",
1504 bss_conf->bssid);
1505 priv->op_flags &= ~OP_ENABLE_BEACON;
1506 ath9k_htc_beacon_config(priv, vif);
1507 }
1508 }
1509
1510 if (changed & BSS_CHANGED_BEACON_INT) {
1511 /*
1512 * Reset the HW TSF for the first AP interface.
1513 */
1514 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1515 (priv->nvifs == 1) &&
1516 (priv->num_ap_vif == 1) &&
1517 (vif->type == NL80211_IFTYPE_AP)) {
1518 priv->op_flags |= OP_TSF_RESET;
1519 }
1520 ath_dbg(common, ATH_DBG_CONFIG,
1521 "Beacon interval changed for BSS: %pM\n",
1522 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301523 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301524 }
1525
Sujithfb9987d2010-03-17 14:25:25 +05301526 if (changed & BSS_CHANGED_ERP_SLOT) {
1527 if (bss_conf->use_short_slot)
1528 ah->slottime = 9;
1529 else
1530 ah->slottime = 20;
1531
1532 ath9k_hw_init_global_settings(ah);
1533 }
1534
Sujith2c76ef82010-05-17 12:01:18 +05301535 if (changed & BSS_CHANGED_HT)
1536 ath9k_htc_update_rate(priv, vif, bss_conf);
1537
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301538 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301539 mutex_unlock(&priv->mutex);
1540}
1541
1542static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1543{
1544 struct ath9k_htc_priv *priv = hw->priv;
1545 u64 tsf;
1546
1547 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301548 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301549 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301550 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301551 mutex_unlock(&priv->mutex);
1552
1553 return tsf;
1554}
1555
1556static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1557{
1558 struct ath9k_htc_priv *priv = hw->priv;
1559
1560 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301561 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301562 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301563 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301564 mutex_unlock(&priv->mutex);
1565}
1566
1567static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1568{
1569 struct ath9k_htc_priv *priv = hw->priv;
1570
1571 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301572 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301573 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301574 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301575 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301576}
1577
1578static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1579 struct ieee80211_vif *vif,
1580 enum ieee80211_ampdu_mlme_action action,
1581 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001582 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301583{
1584 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301585 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301586 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301587
Sujith Manoharan87df8952011-02-21 07:49:08 +05301588 mutex_lock(&priv->mutex);
Sujith Manoharanc58ca5b2011-04-20 11:01:10 +05301589 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan87df8952011-02-21 07:49:08 +05301590
Sujithfb9987d2010-03-17 14:25:25 +05301591 switch (action) {
1592 case IEEE80211_AMPDU_RX_START:
1593 break;
1594 case IEEE80211_AMPDU_RX_STOP:
1595 break;
1596 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301597 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1598 if (!ret)
1599 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1600 break;
Sujithfb9987d2010-03-17 14:25:25 +05301601 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301602 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1603 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301604 break;
1605 case IEEE80211_AMPDU_TX_OPERATIONAL:
1606 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301607 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301608 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301609 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301610 break;
1611 default:
Joe Perches38002762010-12-02 19:12:36 -08001612 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301613 }
1614
Sujith Manoharanc58ca5b2011-04-20 11:01:10 +05301615 ath9k_htc_ps_restore(priv);
Sujith Manoharan87df8952011-02-21 07:49:08 +05301616 mutex_unlock(&priv->mutex);
1617
Sujithd7ca2132010-06-15 10:24:37 +05301618 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301619}
1620
1621static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1622{
1623 struct ath9k_htc_priv *priv = hw->priv;
1624
1625 mutex_lock(&priv->mutex);
1626 spin_lock_bh(&priv->beacon_lock);
1627 priv->op_flags |= OP_SCANNING;
1628 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301629 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301630 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301631 mutex_unlock(&priv->mutex);
1632}
1633
1634static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1635{
1636 struct ath9k_htc_priv *priv = hw->priv;
1637
1638 mutex_lock(&priv->mutex);
1639 spin_lock_bh(&priv->beacon_lock);
1640 priv->op_flags &= ~OP_SCANNING;
1641 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301642 ath9k_htc_ps_wakeup(priv);
1643 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301644 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301645 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301646}
1647
1648static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1649{
1650 return 0;
1651}
1652
1653static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1654 u8 coverage_class)
1655{
1656 struct ath9k_htc_priv *priv = hw->priv;
1657
1658 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301659 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301660 priv->ah->coverage_class = coverage_class;
1661 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301662 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301663 mutex_unlock(&priv->mutex);
1664}
1665
Sujith Manoharane2186b72011-04-27 17:13:40 +05301666/*
1667 * Currently, this is used only for selecting the minimum rate
1668 * for management frames, rate selection for data frames remain
1669 * unaffected.
1670 */
1671static int ath9k_htc_set_bitrate_mask(struct ieee80211_hw *hw,
1672 struct ieee80211_vif *vif,
1673 const struct cfg80211_bitrate_mask *mask)
1674{
1675 struct ath9k_htc_priv *priv = hw->priv;
1676 struct ath_common *common = ath9k_hw_common(priv->ah);
1677 struct ath9k_htc_target_rate_mask tmask;
1678 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1679 int ret = 0;
1680 u8 cmd_rsp;
1681
1682 memset(&tmask, 0, sizeof(struct ath9k_htc_target_rate_mask));
1683
1684 tmask.vif_index = avp->index;
1685 tmask.band = IEEE80211_BAND_2GHZ;
1686 tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_2GHZ].legacy);
1687
1688 WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask);
1689 if (ret) {
1690 ath_err(common,
1691 "Unable to set 2G rate mask for "
1692 "interface at idx: %d\n", avp->index);
1693 goto out;
1694 }
1695
1696 tmask.band = IEEE80211_BAND_5GHZ;
1697 tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_5GHZ].legacy);
1698
1699 WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask);
1700 if (ret) {
1701 ath_err(common,
1702 "Unable to set 5G rate mask for "
1703 "interface at idx: %d\n", avp->index);
1704 goto out;
1705 }
1706
1707 ath_dbg(common, ATH_DBG_CONFIG,
1708 "Set bitrate masks: 0x%x, 0x%x\n",
1709 mask->control[IEEE80211_BAND_2GHZ].legacy,
1710 mask->control[IEEE80211_BAND_5GHZ].legacy);
1711out:
1712 return ret;
1713}
1714
Sujithfb9987d2010-03-17 14:25:25 +05301715struct ieee80211_ops ath9k_htc_ops = {
1716 .tx = ath9k_htc_tx,
1717 .start = ath9k_htc_start,
1718 .stop = ath9k_htc_stop,
1719 .add_interface = ath9k_htc_add_interface,
1720 .remove_interface = ath9k_htc_remove_interface,
1721 .config = ath9k_htc_config,
1722 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301723 .sta_add = ath9k_htc_sta_add,
1724 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301725 .conf_tx = ath9k_htc_conf_tx,
1726 .bss_info_changed = ath9k_htc_bss_info_changed,
1727 .set_key = ath9k_htc_set_key,
1728 .get_tsf = ath9k_htc_get_tsf,
1729 .set_tsf = ath9k_htc_set_tsf,
1730 .reset_tsf = ath9k_htc_reset_tsf,
1731 .ampdu_action = ath9k_htc_ampdu_action,
1732 .sw_scan_start = ath9k_htc_sw_scan_start,
1733 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1734 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1735 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1736 .set_coverage_class = ath9k_htc_set_coverage_class,
Sujith Manoharane2186b72011-04-27 17:13:40 +05301737 .set_bitrate_mask = ath9k_htc_set_bitrate_mask,
Sujithfb9987d2010-03-17 14:25:25 +05301738};