blob: fbc238a0b203d5bd420bada9f08d2682a4822703 [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
29 mode = HTC_MODE_AUTO;
30
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
48 return mode;
49}
50
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053051bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
52 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053053{
54 bool ret;
55
56 mutex_lock(&priv->htc_pm_lock);
57 ret = ath9k_hw_setpower(priv->ah, mode);
58 mutex_unlock(&priv->htc_pm_lock);
59
60 return ret;
61}
62
63void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
64{
65 mutex_lock(&priv->htc_pm_lock);
66 if (++priv->ps_usecount != 1)
67 goto unlock;
68 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
69
70unlock:
71 mutex_unlock(&priv->htc_pm_lock);
72}
73
74void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
75{
76 mutex_lock(&priv->htc_pm_lock);
77 if (--priv->ps_usecount != 0)
78 goto unlock;
79
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053080 if (priv->ps_idle)
81 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
82 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053083 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053084
Vivek Natarajanbde748a2010-04-05 14:48:05 +053085unlock:
86 mutex_unlock(&priv->htc_pm_lock);
87}
88
89void ath9k_ps_work(struct work_struct *work)
90{
91 struct ath9k_htc_priv *priv =
92 container_of(work, struct ath9k_htc_priv,
93 ps_work);
94 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
95
96 /* The chip wakes up after receiving the first beacon
97 while network sleep is enabled. For the driver to
98 be in sync with the hw, set the chip to awake and
99 only then set it to sleep.
100 */
101 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
102}
103
Sujith Manoharan7c277342011-02-21 07:48:39 +0530104static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
105{
106 struct ath9k_htc_priv *priv = data;
107 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
108
Sujith Manoharana5fae372011-02-21 07:49:53 +0530109 if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
110 priv->reconfig_beacon = true;
111
Sujith Manoharan7c277342011-02-21 07:48:39 +0530112 if (bss_conf->assoc) {
113 priv->rearm_ani = true;
114 priv->reconfig_beacon = true;
115 }
116}
117
118static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
119{
120 priv->rearm_ani = false;
121 priv->reconfig_beacon = false;
122
123 ieee80211_iterate_active_interfaces_atomic(priv->hw,
124 ath9k_htc_vif_iter, priv);
125 if (priv->rearm_ani)
Sujith Manoharana2362542011-02-21 07:49:38 +0530126 ath9k_htc_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530127
128 if (priv->reconfig_beacon) {
129 ath9k_htc_ps_wakeup(priv);
130 ath9k_htc_beacon_reconfig(priv);
131 ath9k_htc_ps_restore(priv);
132 }
133}
134
Sujith Manoharan585895c2011-02-21 07:48:46 +0530135static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
136{
137 struct ath9k_vif_iter_data *iter_data = data;
138 int i;
139
140 for (i = 0; i < ETH_ALEN; i++)
141 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
142}
143
144static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
145 struct ieee80211_vif *vif)
146{
147 struct ath_common *common = ath9k_hw_common(priv->ah);
148 struct ath9k_vif_iter_data iter_data;
149
150 /*
151 * Use the hardware MAC address as reference, the hardware uses it
152 * together with the BSSID mask when matching addresses.
153 */
154 iter_data.hw_macaddr = common->macaddr;
155 memset(&iter_data.mask, 0xff, ETH_ALEN);
156
157 if (vif)
158 ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
159
160 /* Get list of all active MAC addresses */
161 ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
162 &iter_data);
163
164 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
165 ath_hw_setbssidmask(common);
166}
167
Sujith Manoharanffbe7c82011-02-21 07:49:31 +0530168static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
169{
170 if (priv->num_ibss_vif)
171 priv->ah->opmode = NL80211_IFTYPE_ADHOC;
172 else if (priv->num_ap_vif)
173 priv->ah->opmode = NL80211_IFTYPE_AP;
174 else
175 priv->ah->opmode = NL80211_IFTYPE_STATION;
176
177 ath9k_hw_setopmode(priv->ah);
178}
179
Sujith Manoharan73908672010-12-28 14:28:27 +0530180void ath9k_htc_reset(struct ath9k_htc_priv *priv)
181{
182 struct ath_hw *ah = priv->ah;
183 struct ath_common *common = ath9k_hw_common(ah);
184 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530185 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530186 enum htc_phymode mode;
187 __be16 htc_mode;
188 u8 cmd_rsp;
189 int ret;
190
191 mutex_lock(&priv->mutex);
192 ath9k_htc_ps_wakeup(priv);
193
Sujith Manoharana2362542011-02-21 07:49:38 +0530194 ath9k_htc_stop_ani(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530195 ieee80211_stop_queues(priv->hw);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530196
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530197 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530198 ath9k_htc_tx_drain(priv);
199
Sujith Manoharan73908672010-12-28 14:28:27 +0530200 WMI_CMD(WMI_DISABLE_INTR_CMDID);
201 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
202 WMI_CMD(WMI_STOP_RECV_CMDID);
203
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530204 ath9k_wmi_event_drain(priv);
205
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530206 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530207 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
208 if (ret) {
209 ath_err(common,
210 "Unable to reset device (%u Mhz) reset status %d\n",
211 channel->center_freq, ret);
212 }
213
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530214 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
215 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530216
217 WMI_CMD(WMI_START_RECV_CMDID);
218 ath9k_host_rx_init(priv);
219
220 mode = ath9k_htc_get_curmode(priv, ah->curchan);
221 htc_mode = cpu_to_be16(mode);
222 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
223
224 WMI_CMD(WMI_ENABLE_INTR_CMDID);
225 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530226 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530227 ieee80211_wake_queues(priv->hw);
228
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530229 mod_timer(&priv->tx.cleanup_timer,
230 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
231
Sujith Manoharan73908672010-12-28 14:28:27 +0530232 ath9k_htc_ps_restore(priv);
233 mutex_unlock(&priv->mutex);
234}
235
Sujithfb9987d2010-03-17 14:25:25 +0530236static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
237 struct ieee80211_hw *hw,
238 struct ath9k_channel *hchan)
239{
240 struct ath_hw *ah = priv->ah;
241 struct ath_common *common = ath9k_hw_common(ah);
242 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530243 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530244 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530245 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530246 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530247 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530248 u8 cmd_rsp;
249 int ret;
250
251 if (priv->op_flags & OP_INVALID)
252 return -EIO;
253
Sujith Manoharan039a0722010-12-28 14:28:37 +0530254 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530255
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530256 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530257
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530258 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530259 ath9k_htc_tx_drain(priv);
260
Sujithfb9987d2010-03-17 14:25:25 +0530261 WMI_CMD(WMI_DISABLE_INTR_CMDID);
262 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
263 WMI_CMD(WMI_STOP_RECV_CMDID);
264
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530265 ath9k_wmi_event_drain(priv);
266
Joe Perches226afe62010-12-02 19:12:37 -0800267 ath_dbg(common, ATH_DBG_CONFIG,
268 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
269 priv->ah->curchan->channel,
270 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
271 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530272
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530273 if (!fastcc)
274 caldata = &priv->caldata;
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530275
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200276 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530277 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800278 ath_err(common,
279 "Unable to reset channel (%u Mhz) reset status %d\n",
280 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530281 goto err;
282 }
283
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530284 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
285 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530286
287 WMI_CMD(WMI_START_RECV_CMDID);
288 if (ret)
289 goto err;
290
291 ath9k_host_rx_init(priv);
292
293 mode = ath9k_htc_get_curmode(priv, hchan);
294 htc_mode = cpu_to_be16(mode);
295 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
296 if (ret)
297 goto err;
298
299 WMI_CMD(WMI_ENABLE_INTR_CMDID);
300 if (ret)
301 goto err;
302
303 htc_start(priv->htc);
Sujith Manoharana5fae372011-02-21 07:49:53 +0530304
305 if (!(priv->op_flags & OP_SCANNING) &&
306 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
307 ath9k_htc_vif_reconfig(priv);
308
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530309 mod_timer(&priv->tx.cleanup_timer,
310 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
311
Sujithfb9987d2010-03-17 14:25:25 +0530312err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530313 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530314 return ret;
315}
316
Sujith Manoharana97b4782011-02-21 07:48:00 +0530317/*
318 * Monitor mode handling is a tad complicated because the firmware requires
319 * an interface to be created exclusively, while mac80211 doesn't associate
320 * an interface with the mode.
321 *
322 * So, for now, only one monitor interface can be configured.
323 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530324static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530325{
326 struct ath_common *common = ath9k_hw_common(priv->ah);
327 struct ath9k_htc_target_vif hvif;
328 int ret = 0;
329 u8 cmd_rsp;
330
Sujith Manoharancc721282011-01-03 21:22:18 +0530331 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
332 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530333 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530334 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +0530335 if (ret) {
336 ath_err(common, "Unable to remove monitor interface at idx: %d\n",
337 priv->mon_vif_idx);
338 }
339
Sujith Manoharancc721282011-01-03 21:22:18 +0530340 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530341 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530342}
343
344static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
345{
346 struct ath_common *common = ath9k_hw_common(priv->ah);
347 struct ath9k_htc_target_vif hvif;
348 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530349 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530350 u8 cmd_rsp;
351
Sujith Manoharana97b4782011-02-21 07:48:00 +0530352 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
353 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
354 ret = -ENOBUFS;
355 goto err_vif;
356 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530357
Sujith Manoharana97b4782011-02-21 07:48:00 +0530358 sta_idx = ffz(priv->sta_slot);
359 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
360 ret = -ENOBUFS;
361 goto err_vif;
362 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530363
364 /*
365 * Add an interface.
366 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530367 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
368 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
369
Sujith Manoharane4c62502011-04-13 11:24:43 +0530370 hvif.opmode = HTC_M_MONITOR;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530371 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530372
373 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
374 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530375 goto err_vif;
376
377 /*
378 * Assign the monitor interface index as a special case here.
379 * This is needed when the interface is brought down.
380 */
381 priv->mon_vif_idx = hvif.index;
382 priv->vif_slot |= (1 << hvif.index);
383
384 /*
385 * Set the hardware mode to monitor only if there are no
386 * other interfaces.
387 */
388 if (!priv->nvifs)
389 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530390
391 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530392
393 /*
394 * Associate a station with the interface for packet injection.
395 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530396 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
397
398 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
399
400 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530401 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530402 tsta.vif_index = hvif.index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530403 tsta.maxampdu = cpu_to_be16(0xffff);
Sujith Manoharancc721282011-01-03 21:22:18 +0530404
405 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
406 if (ret) {
407 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530408 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530409 }
410
Sujith Manoharana97b4782011-02-21 07:48:00 +0530411 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530412 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530413 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530414 priv->ah->is_monitoring = true;
415
Sujith Manoharana97b4782011-02-21 07:48:00 +0530416 ath_dbg(common, ATH_DBG_CONFIG,
417 "Attached a monitor interface at idx: %d, sta idx: %d\n",
418 priv->mon_vif_idx, sta_idx);
419
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530420 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530421
Sujith Manoharana97b4782011-02-21 07:48:00 +0530422err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530423 /*
424 * Remove the interface from the target.
425 */
426 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530427err_vif:
428 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
429
Sujith Manoharancc721282011-01-03 21:22:18 +0530430 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530431}
432
433static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
434{
435 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530436 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530437 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530438
Sujith Manoharancc721282011-01-03 21:22:18 +0530439 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530440
Sujith Manoharana97b4782011-02-21 07:48:00 +0530441 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530442
443 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
444 if (ret) {
445 ath_err(common, "Unable to remove station entry for monitor mode\n");
446 return ret;
447 }
448
Sujith Manoharana97b4782011-02-21 07:48:00 +0530449 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530450 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530451 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530452
Sujith Manoharana97b4782011-02-21 07:48:00 +0530453 ath_dbg(common, ATH_DBG_CONFIG,
454 "Removed a monitor interface at idx: %d, sta idx: %d\n",
455 priv->mon_vif_idx, sta_idx);
456
Sujith Manoharancc721282011-01-03 21:22:18 +0530457 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530458}
459
Sujithfb9987d2010-03-17 14:25:25 +0530460static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
461 struct ieee80211_vif *vif,
462 struct ieee80211_sta *sta)
463{
464 struct ath_common *common = ath9k_hw_common(priv->ah);
465 struct ath9k_htc_target_sta tsta;
466 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
467 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530468 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530469 u8 cmd_rsp;
Sujith Manoharanf0dd4982011-04-20 11:01:00 +0530470 u16 maxampdu;
Sujithfb9987d2010-03-17 14:25:25 +0530471
472 if (priv->nstations >= ATH9K_HTC_MAX_STA)
473 return -ENOBUFS;
474
Sujith Manoharana97b4782011-02-21 07:48:00 +0530475 sta_idx = ffz(priv->sta_slot);
476 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
477 return -ENOBUFS;
478
Sujithfb9987d2010-03-17 14:25:25 +0530479 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
480
481 if (sta) {
482 ista = (struct ath9k_htc_sta *) sta->drv_priv;
483 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
484 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
Sujithfb9987d2010-03-17 14:25:25 +0530485 tsta.is_vif_sta = 0;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530486 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530487 } else {
488 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
489 tsta.is_vif_sta = 1;
490 }
491
Sujith Manoharana97b4782011-02-21 07:48:00 +0530492 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530493 tsta.vif_index = avp->index;
Sujith Manoharanf0dd4982011-04-20 11:01:00 +0530494
495 if (!sta) {
496 tsta.maxampdu = cpu_to_be16(0xffff);
497 } else {
498 maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
499 sta->ht_cap.ampdu_factor);
500 tsta.maxampdu = cpu_to_be16(maxampdu);
501 }
502
Sujithfb9987d2010-03-17 14:25:25 +0530503 if (sta && sta->ht_cap.ht_supported)
504 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
505
506 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
507 if (ret) {
508 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800509 ath_err(common,
510 "Unable to add station entry for: %pM\n",
511 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530512 return ret;
513 }
514
Sujith Manoharana97b4782011-02-21 07:48:00 +0530515 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800516 ath_dbg(common, ATH_DBG_CONFIG,
517 "Added a station entry for: %pM (idx: %d)\n",
518 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530519 } else {
520 ath_dbg(common, ATH_DBG_CONFIG,
521 "Added a station entry for VIF %d (idx: %d)\n",
522 avp->index, tsta.sta_index);
523 }
Sujithfb9987d2010-03-17 14:25:25 +0530524
Sujith Manoharana97b4782011-02-21 07:48:00 +0530525 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530526 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530527 if (!sta)
528 priv->vif_sta_pos[avp->index] = sta_idx;
529
Sujithfb9987d2010-03-17 14:25:25 +0530530 return 0;
531}
532
533static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
534 struct ieee80211_vif *vif,
535 struct ieee80211_sta *sta)
536{
537 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530538 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530539 struct ath9k_htc_sta *ista;
540 int ret;
541 u8 cmd_rsp, sta_idx;
542
543 if (sta) {
544 ista = (struct ath9k_htc_sta *) sta->drv_priv;
545 sta_idx = ista->index;
546 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530547 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530548 }
549
550 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
551 if (ret) {
552 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800553 ath_err(common,
554 "Unable to remove station entry for: %pM\n",
555 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530556 return ret;
557 }
558
Sujith Manoharana97b4782011-02-21 07:48:00 +0530559 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800560 ath_dbg(common, ATH_DBG_CONFIG,
561 "Removed a station entry for: %pM (idx: %d)\n",
562 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530563 } else {
564 ath_dbg(common, ATH_DBG_CONFIG,
565 "Removed a station entry for VIF %d (idx: %d)\n",
566 avp->index, sta_idx);
567 }
Sujithfb9987d2010-03-17 14:25:25 +0530568
Sujith Manoharana97b4782011-02-21 07:48:00 +0530569 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530570 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530571
Sujithfb9987d2010-03-17 14:25:25 +0530572 return 0;
573}
574
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530575int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv,
576 u8 enable_coex)
Sujithfb9987d2010-03-17 14:25:25 +0530577{
578 struct ath9k_htc_cap_target tcap;
579 int ret;
580 u8 cmd_rsp;
581
582 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
583
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530584 tcap.ampdu_limit = cpu_to_be32(0xffff);
585 tcap.ampdu_subframes = priv->hw->max_tx_aggregation_subframes;
586 tcap.enable_coex = enable_coex;
Sujith29d90752010-06-02 15:53:43 +0530587 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530588
589 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
590
591 return ret;
592}
593
Sujith0d425a72010-05-17 12:01:16 +0530594static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
595 struct ieee80211_sta *sta,
596 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530597{
Sujithfb9987d2010-03-17 14:25:25 +0530598 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
599 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530600 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530601 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530602
Sujithea46e642010-06-02 15:53:50 +0530603 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530604
605 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
606 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530607 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530608 = (sband->bitrates[i].bitrate * 2) / 10;
609 j++;
610 }
611 }
Sujith0d425a72010-05-17 12:01:16 +0530612 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530613
614 if (sta->ht_cap.ht_supported) {
615 for (i = 0, j = 0; i < 77; i++) {
616 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530617 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530618 if (j == ATH_HTC_RATE_MAX)
619 break;
620 }
Sujith0d425a72010-05-17 12:01:16 +0530621 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530622
623 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200624 if (sta->ht_cap.mcs.rx_mask[1])
625 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530626 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
627 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530628 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530629 if (conf_is_ht40(&priv->hw->conf) &&
630 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530631 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530632 else if (conf_is_ht20(&priv->hw->conf) &&
633 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
634 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530635 }
636
Sujith0d425a72010-05-17 12:01:16 +0530637 trate->sta_index = ista->index;
638 trate->isnew = 1;
639 trate->capflags = cpu_to_be32(caps);
640}
Sujithfb9987d2010-03-17 14:25:25 +0530641
Sujith0d425a72010-05-17 12:01:16 +0530642static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
643 struct ath9k_htc_target_rate *trate)
644{
645 struct ath_common *common = ath9k_hw_common(priv->ah);
646 int ret;
647 u8 cmd_rsp;
648
649 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530650 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800651 ath_err(common,
652 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530653 }
654
Sujith0d425a72010-05-17 12:01:16 +0530655 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530656}
657
Sujith0d425a72010-05-17 12:01:16 +0530658static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
659 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530660{
Sujithfb9987d2010-03-17 14:25:25 +0530661 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530662 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530663 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530664
Sujith0d425a72010-05-17 12:01:16 +0530665 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
666 ath9k_htc_setup_rate(priv, sta, &trate);
667 ret = ath9k_htc_send_rate_cmd(priv, &trate);
668 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800669 ath_dbg(common, ATH_DBG_CONFIG,
670 "Updated target sta: %pM, rate caps: 0x%X\n",
671 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530672}
673
Sujith2c76ef82010-05-17 12:01:18 +0530674static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
675 struct ieee80211_vif *vif,
676 struct ieee80211_bss_conf *bss_conf)
677{
678 struct ath_common *common = ath9k_hw_common(priv->ah);
679 struct ath9k_htc_target_rate trate;
680 struct ieee80211_sta *sta;
681 int ret;
682
683 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
684
685 rcu_read_lock();
686 sta = ieee80211_find_sta(vif, bss_conf->bssid);
687 if (!sta) {
688 rcu_read_unlock();
689 return;
690 }
691 ath9k_htc_setup_rate(priv, sta, &trate);
692 rcu_read_unlock();
693
694 ret = ath9k_htc_send_rate_cmd(priv, &trate);
695 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800696 ath_dbg(common, ATH_DBG_CONFIG,
697 "Updated target sta: %pM, rate caps: 0x%X\n",
698 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530699}
700
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400701static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
702 struct ieee80211_vif *vif,
703 struct ieee80211_sta *sta,
704 enum ieee80211_ampdu_mlme_action action,
705 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530706{
707 struct ath_common *common = ath9k_hw_common(priv->ah);
708 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200709 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530710 int ret = 0;
711 u8 cmd_rsp;
712
Dan Carpenter0730d112010-05-08 18:24:02 +0200713 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530714 return -EINVAL;
715
Sujithfb9987d2010-03-17 14:25:25 +0530716 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530717 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530718
Sujithef98c3c2010-03-29 16:07:11 +0530719 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530720 aggr.tidno = tid & 0xf;
721 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530722
Sujithfb9987d2010-03-17 14:25:25 +0530723 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
724 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800725 ath_dbg(common, ATH_DBG_CONFIG,
726 "Unable to %s TX aggregation for (%pM, %d)\n",
727 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530728 else
Joe Perches226afe62010-12-02 19:12:37 -0800729 ath_dbg(common, ATH_DBG_CONFIG,
730 "%s TX aggregation for (%pM, %d)\n",
731 (aggr.aggr_enable) ? "Starting" : "Stopping",
732 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530733
Sujith Manoharan658ef042011-04-13 11:25:00 +0530734 spin_lock_bh(&priv->tx.tx_lock);
Sujithd7ca2132010-06-15 10:24:37 +0530735 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530736 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530737
738 return ret;
739}
740
Sujithfb9987d2010-03-17 14:25:25 +0530741/*******/
742/* ANI */
743/*******/
744
Sujith Manoharana2362542011-02-21 07:49:38 +0530745void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530746{
747 struct ath_common *common = ath9k_hw_common(priv->ah);
748 unsigned long timestamp = jiffies_to_msecs(jiffies);
749
750 common->ani.longcal_timer = timestamp;
751 common->ani.shortcal_timer = timestamp;
752 common->ani.checkani_timer = timestamp;
753
Sujith Manoharana2362542011-02-21 07:49:38 +0530754 priv->op_flags |= OP_ANI_RUNNING;
755
756 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530757 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
758}
759
Sujith Manoharana2362542011-02-21 07:49:38 +0530760void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
761{
762 cancel_delayed_work_sync(&priv->ani_work);
763 priv->op_flags &= ~OP_ANI_RUNNING;
764}
765
766void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530767{
768 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530769 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530770 struct ath_hw *ah = priv->ah;
771 struct ath_common *common = ath9k_hw_common(ah);
772 bool longcal = false;
773 bool shortcal = false;
774 bool aniflag = false;
775 unsigned int timestamp = jiffies_to_msecs(jiffies);
776 u32 cal_interval, short_cal_interval;
777
Sujith Manoharana2362542011-02-21 07:49:38 +0530778 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
779 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530780
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530781 /* Only calibrate if awake */
782 if (ah->power_mode != ATH9K_PM_AWAKE)
783 goto set_timer;
784
Sujithfb9987d2010-03-17 14:25:25 +0530785 /* Long calibration runs independently of short calibration. */
786 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
787 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800788 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530789 common->ani.longcal_timer = timestamp;
790 }
791
792 /* Short calibration applies only while caldone is false */
793 if (!common->ani.caldone) {
794 if ((timestamp - common->ani.shortcal_timer) >=
795 short_cal_interval) {
796 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800797 ath_dbg(common, ATH_DBG_ANI,
798 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530799 common->ani.shortcal_timer = timestamp;
800 common->ani.resetcal_timer = timestamp;
801 }
802 } else {
803 if ((timestamp - common->ani.resetcal_timer) >=
804 ATH_RESTART_CALINTERVAL) {
805 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
806 if (common->ani.caldone)
807 common->ani.resetcal_timer = timestamp;
808 }
809 }
810
811 /* Verify whether we must check ANI */
812 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
813 aniflag = true;
814 common->ani.checkani_timer = timestamp;
815 }
816
817 /* Skip all processing if there's nothing to do. */
818 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530819
820 ath9k_htc_ps_wakeup(priv);
821
Sujithfb9987d2010-03-17 14:25:25 +0530822 /* Call ANI routine if necessary */
823 if (aniflag)
824 ath9k_hw_ani_monitor(ah, ah->curchan);
825
826 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200827 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530828 common->ani.caldone =
829 ath9k_hw_calibrate(ah, ah->curchan,
830 common->rx_chainmask,
831 longcal);
832
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530833 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530834 }
835
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530836set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530837 /*
838 * Set timer interval based on previous results.
839 * The interval must be the shortest necessary to satisfy ANI,
840 * short calibration and long calibration.
841 */
842 cal_interval = ATH_LONG_CALINTERVAL;
843 if (priv->ah->config.enable_ani)
844 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
845 if (!common->ani.caldone)
846 cal_interval = min(cal_interval, (u32)short_cal_interval);
847
Sujith Manoharana2362542011-02-21 07:49:38 +0530848 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530849 msecs_to_jiffies(cal_interval));
850}
851
Sujithfb9987d2010-03-17 14:25:25 +0530852/**********************/
853/* mac80211 Callbacks */
854/**********************/
855
Johannes Berg7bb45682011-02-24 14:42:06 +0100856static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +0530857{
858 struct ieee80211_hdr *hdr;
859 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530860 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530861 int padpos, padsize, ret, slot;
Sujithfb9987d2010-03-17 14:25:25 +0530862
863 hdr = (struct ieee80211_hdr *) skb->data;
864
865 /* Add the padding after the header if this is not already done */
866 padpos = ath9k_cmn_padpos(hdr->frame_control);
867 padsize = padpos & 3;
868 if (padsize && skb->len > padpos) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530869 if (skb_headroom(skb) < padsize) {
870 ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n");
Johannes Berg7bb45682011-02-24 14:42:06 +0100871 goto fail_tx;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530872 }
Sujithfb9987d2010-03-17 14:25:25 +0530873 skb_push(skb, padsize);
874 memmove(skb->data, skb->data + padsize, padpos);
875 }
876
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530877 slot = ath9k_htc_tx_get_slot(priv);
878 if (slot < 0) {
879 ath_dbg(common, ATH_DBG_XMIT, "No free TX slot\n");
880 goto fail_tx;
881 }
882
883 ret = ath9k_htc_tx_start(priv, skb, slot, false);
Sujith7757dfe2010-03-29 16:07:17 +0530884 if (ret != 0) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530885 ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530886 goto clear_slot;
Sujithfb9987d2010-03-17 14:25:25 +0530887 }
888
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530889 ath9k_htc_check_stop_queues(priv);
890
Johannes Berg7bb45682011-02-24 14:42:06 +0100891 return;
Sujithfb9987d2010-03-17 14:25:25 +0530892
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530893clear_slot:
894 ath9k_htc_tx_clear_slot(priv, slot);
Sujithfb9987d2010-03-17 14:25:25 +0530895fail_tx:
896 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530897}
898
Sujith881ac6a2010-06-01 15:14:11 +0530899static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530900{
901 struct ath9k_htc_priv *priv = hw->priv;
902 struct ath_hw *ah = priv->ah;
903 struct ath_common *common = ath9k_hw_common(ah);
904 struct ieee80211_channel *curchan = hw->conf.channel;
905 struct ath9k_channel *init_channel;
906 int ret = 0;
907 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530908 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530909 u8 cmd_rsp;
910
Sujith881ac6a2010-06-01 15:14:11 +0530911 mutex_lock(&priv->mutex);
912
Joe Perches226afe62010-12-02 19:12:37 -0800913 ath_dbg(common, ATH_DBG_CONFIG,
914 "Starting driver with initial channel: %d MHz\n",
915 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530916
Sujith21d51302010-06-01 15:14:18 +0530917 /* Ensure that HW is awake before flushing RX */
918 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
919 WMI_CMD(WMI_FLUSH_RECV_CMDID);
920
Sujithfb9987d2010-03-17 14:25:25 +0530921 /* setup initial channel */
922 init_channel = ath9k_cmn_get_curchannel(hw, ah);
923
Sujithfb9987d2010-03-17 14:25:25 +0530924 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200925 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530926 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800927 ath_err(common,
928 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
929 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530930 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530931 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530932 }
933
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530934 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
935 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530936
937 mode = ath9k_htc_get_curmode(priv, init_channel);
938 htc_mode = cpu_to_be16(mode);
939 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530940 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530941 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530942
943 ath9k_host_rx_init(priv);
944
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530945 ret = ath9k_htc_update_cap_target(priv, 0);
Sujith Manoharan1057b752011-02-21 07:48:09 +0530946 if (ret)
947 ath_dbg(common, ATH_DBG_CONFIG,
948 "Failed to update capability in target\n");
949
Sujithfb9987d2010-03-17 14:25:25 +0530950 priv->op_flags &= ~OP_INVALID;
951 htc_start(priv->htc);
952
Sujith Manoharan658ef042011-04-13 11:25:00 +0530953 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530954 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530955 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530956
957 ieee80211_wake_queues(hw);
958
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530959 mod_timer(&priv->tx.cleanup_timer,
960 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
961
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530962 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
963 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
964 AR_STOMP_LOW_WLAN_WGHT);
965 ath9k_hw_btcoex_enable(ah);
966 ath_htc_resume_btcoex_work(priv);
967 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530968 mutex_unlock(&priv->mutex);
969
970 return ret;
971}
972
Sujith881ac6a2010-06-01 15:14:11 +0530973static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530974{
975 struct ath9k_htc_priv *priv = hw->priv;
976 struct ath_hw *ah = priv->ah;
977 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +0530978 int ret __attribute__ ((unused));
Sujithfb9987d2010-03-17 14:25:25 +0530979 u8 cmd_rsp;
980
Sujith881ac6a2010-06-01 15:14:11 +0530981 mutex_lock(&priv->mutex);
982
Sujithfb9987d2010-03-17 14:25:25 +0530983 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -0800984 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530985 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530986 return;
987 }
988
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530989 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530990
Sujithfb9987d2010-03-17 14:25:25 +0530991 WMI_CMD(WMI_DISABLE_INTR_CMDID);
992 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
993 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100994
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100995 tasklet_kill(&priv->rx_tasklet);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100996
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530997 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530998 ath9k_htc_tx_drain(priv);
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530999 ath9k_wmi_event_drain(priv);
1000
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001001 mutex_unlock(&priv->mutex);
1002
1003 /* Cancel all the running timers/work .. */
1004 cancel_work_sync(&priv->fatal_work);
1005 cancel_work_sync(&priv->ps_work);
1006 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301007 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001008 ath9k_led_stop_brightness(priv);
1009
1010 mutex_lock(&priv->mutex);
1011
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301012 if (ah->btcoex_hw.enabled) {
1013 ath9k_hw_btcoex_disable(ah);
1014 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1015 ath_htc_cancel_btcoex_work(priv);
1016 }
1017
Sujith Manoharana97b4782011-02-21 07:48:00 +05301018 /* Remove a monitor interface if it's present. */
1019 if (priv->ah->is_monitoring)
1020 ath9k_htc_remove_monitor_interface(priv);
1021
Sujithe9201f02010-06-01 15:14:17 +05301022 ath9k_hw_phy_disable(ah);
1023 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301024 ath9k_htc_ps_restore(priv);
1025 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1026
Sujithfb9987d2010-03-17 14:25:25 +05301027 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301028
Joe Perches226afe62010-12-02 19:12:37 -08001029 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301030 mutex_unlock(&priv->mutex);
1031}
1032
Sujithfb9987d2010-03-17 14:25:25 +05301033static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1034 struct ieee80211_vif *vif)
1035{
1036 struct ath9k_htc_priv *priv = hw->priv;
1037 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1038 struct ath_common *common = ath9k_hw_common(priv->ah);
1039 struct ath9k_htc_target_vif hvif;
1040 int ret = 0;
1041 u8 cmd_rsp;
1042
1043 mutex_lock(&priv->mutex);
1044
Sujith Manoharana97b4782011-02-21 07:48:00 +05301045 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301046 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301047 return -ENOBUFS;
1048 }
1049
1050 if (priv->num_ibss_vif ||
1051 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1052 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1053 mutex_unlock(&priv->mutex);
1054 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301055 }
1056
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301057 if (((vif->type == NL80211_IFTYPE_AP) ||
1058 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1059 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1060 ath_err(common, "Max. number of beaconing interfaces reached\n");
1061 mutex_unlock(&priv->mutex);
1062 return -ENOBUFS;
1063 }
1064
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301065 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301066 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1067 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1068
1069 switch (vif->type) {
1070 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301071 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301072 break;
1073 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301074 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301075 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301076 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301077 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301078 break;
Sujithfb9987d2010-03-17 14:25:25 +05301079 default:
Joe Perches38002762010-12-02 19:12:36 -08001080 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301081 "Interface type %d not yet supported\n", vif->type);
1082 ret = -EOPNOTSUPP;
1083 goto out;
1084 }
1085
Sujithfb9987d2010-03-17 14:25:25 +05301086 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301087 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301088 hvif.rtsthreshold = cpu_to_be16(2304);
1089 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1090 if (ret)
1091 goto out;
1092
Sujithfb9987d2010-03-17 14:25:25 +05301093 /*
1094 * We need a node in target to tx mgmt frames
1095 * before association.
1096 */
1097 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301098 if (ret) {
1099 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301100 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301101 }
Sujithfb9987d2010-03-17 14:25:25 +05301102
Sujith Manoharan585895c2011-02-21 07:48:46 +05301103 ath9k_htc_set_bssid_mask(priv, vif);
1104
Sujith Manoharana97b4782011-02-21 07:48:00 +05301105 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301106 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301107
Sujith Manoharan0df83592011-02-21 07:49:15 +05301108 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301109
1110 if ((vif->type == NL80211_IFTYPE_AP) ||
1111 (vif->type == NL80211_IFTYPE_ADHOC))
1112 ath9k_htc_assign_bslot(priv, vif);
1113
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301114 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301115
Sujith Manoharana2362542011-02-21 07:49:38 +05301116 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301117 !(priv->op_flags & OP_ANI_RUNNING)) {
1118 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301119 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301120 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301121
Sujith Manoharana97b4782011-02-21 07:48:00 +05301122 ath_dbg(common, ATH_DBG_CONFIG,
1123 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1124
Sujithfb9987d2010-03-17 14:25:25 +05301125out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301126 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301127 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301128
Sujithfb9987d2010-03-17 14:25:25 +05301129 return ret;
1130}
1131
1132static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1133 struct ieee80211_vif *vif)
1134{
1135 struct ath9k_htc_priv *priv = hw->priv;
1136 struct ath_common *common = ath9k_hw_common(priv->ah);
1137 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1138 struct ath9k_htc_target_vif hvif;
1139 int ret = 0;
1140 u8 cmd_rsp;
1141
Sujithfb9987d2010-03-17 14:25:25 +05301142 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301143 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301144
1145 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1146 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1147 hvif.index = avp->index;
1148 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +05301149 if (ret) {
1150 ath_err(common, "Unable to remove interface at idx: %d\n",
1151 avp->index);
1152 }
Sujithfb9987d2010-03-17 14:25:25 +05301153 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301154 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301155
1156 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301157
Sujith Manoharan0df83592011-02-21 07:49:15 +05301158 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301159
1160 if ((vif->type == NL80211_IFTYPE_AP) ||
1161 (vif->type == NL80211_IFTYPE_ADHOC))
1162 ath9k_htc_remove_bslot(priv, vif);
1163
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301164 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301165
Sujith Manoharana2362542011-02-21 07:49:38 +05301166 /*
1167 * Stop ANI only if there are no associated station interfaces.
1168 */
1169 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1170 priv->rearm_ani = false;
1171 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1172 ath9k_htc_vif_iter, priv);
1173 if (!priv->rearm_ani)
1174 ath9k_htc_stop_ani(priv);
1175 }
1176
Sujith Manoharana97b4782011-02-21 07:48:00 +05301177 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1178
Sujithcb551df2010-06-01 15:14:12 +05301179 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301180 mutex_unlock(&priv->mutex);
1181}
1182
1183static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1184{
1185 struct ath9k_htc_priv *priv = hw->priv;
1186 struct ath_common *common = ath9k_hw_common(priv->ah);
1187 struct ieee80211_conf *conf = &hw->conf;
1188
1189 mutex_lock(&priv->mutex);
1190
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301191 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1192 bool enable_radio = false;
1193 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1194
Sujith23367762010-06-01 15:14:16 +05301195 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301196 if (!idle && priv->ps_idle)
1197 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301198 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301199 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301200
1201 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001202 ath_dbg(common, ATH_DBG_CONFIG,
1203 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301204 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1205 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301206 }
1207 }
1208
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301209 /*
1210 * Monitor interface should be added before
1211 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1212 */
1213 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301214 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1215 !priv->ah->is_monitoring)
1216 ath9k_htc_add_monitor_interface(priv);
1217 else if (priv->ah->is_monitoring)
1218 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301219 }
1220
Sujithfb9987d2010-03-17 14:25:25 +05301221 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1222 struct ieee80211_channel *curchan = hw->conf.channel;
1223 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301224
Joe Perches226afe62010-12-02 19:12:37 -08001225 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1226 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301227
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001228 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1229 hw->conf.channel,
1230 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301231
1232 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001233 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301234 mutex_unlock(&priv->mutex);
1235 return -EINVAL;
1236 }
1237
1238 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301239
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301240 if (changed & IEEE80211_CONF_CHANGE_PS) {
1241 if (conf->flags & IEEE80211_CONF_PS) {
1242 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1243 priv->ps_enabled = true;
1244 } else {
1245 priv->ps_enabled = false;
1246 cancel_work_sync(&priv->ps_work);
1247 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1248 }
1249 }
Sujithfb9987d2010-03-17 14:25:25 +05301250
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301251 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1252 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301253 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1254 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301255 }
1256
Sujith23367762010-06-01 15:14:16 +05301257 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1258 mutex_lock(&priv->htc_pm_lock);
1259 if (!priv->ps_idle) {
1260 mutex_unlock(&priv->htc_pm_lock);
1261 goto out;
1262 }
1263 mutex_unlock(&priv->htc_pm_lock);
1264
Joe Perches226afe62010-12-02 19:12:37 -08001265 ath_dbg(common, ATH_DBG_CONFIG,
1266 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301267 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301268 }
1269
Sujith23367762010-06-01 15:14:16 +05301270out:
Sujithfb9987d2010-03-17 14:25:25 +05301271 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301272 return 0;
1273}
1274
1275#define SUPPORTED_FILTERS \
1276 (FIF_PROMISC_IN_BSS | \
1277 FIF_ALLMULTI | \
1278 FIF_CONTROL | \
1279 FIF_PSPOLL | \
1280 FIF_OTHER_BSS | \
1281 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301282 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301283 FIF_FCSFAIL)
1284
1285static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1286 unsigned int changed_flags,
1287 unsigned int *total_flags,
1288 u64 multicast)
1289{
1290 struct ath9k_htc_priv *priv = hw->priv;
1291 u32 rfilt;
1292
1293 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301294 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301295
Sujithfb9987d2010-03-17 14:25:25 +05301296 changed_flags &= SUPPORTED_FILTERS;
1297 *total_flags &= SUPPORTED_FILTERS;
1298
1299 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301300 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301301 ath9k_hw_setrxfilter(priv->ah, rfilt);
1302
Joe Perches226afe62010-12-02 19:12:37 -08001303 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1304 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301305
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301306 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301307 mutex_unlock(&priv->mutex);
1308}
1309
Sujithabd984e2010-05-18 15:26:04 +05301310static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1311 struct ieee80211_vif *vif,
1312 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301313{
1314 struct ath9k_htc_priv *priv = hw->priv;
1315 int ret;
1316
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301317 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301318 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301319 ret = ath9k_htc_add_station(priv, vif, sta);
1320 if (!ret)
1321 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301322 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301323 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301324
1325 return ret;
1326}
1327
1328static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1329 struct ieee80211_vif *vif,
1330 struct ieee80211_sta *sta)
1331{
1332 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301333 struct ath9k_htc_sta *ista;
Sujithabd984e2010-05-18 15:26:04 +05301334 int ret;
1335
1336 mutex_lock(&priv->mutex);
1337 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301338 ista = (struct ath9k_htc_sta *) sta->drv_priv;
1339 htc_sta_drain(priv->htc, ista->index);
Sujithabd984e2010-05-18 15:26:04 +05301340 ret = ath9k_htc_remove_station(priv, vif, sta);
1341 ath9k_htc_ps_restore(priv);
1342 mutex_unlock(&priv->mutex);
1343
1344 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301345}
1346
1347static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1348 const struct ieee80211_tx_queue_params *params)
1349{
1350 struct ath9k_htc_priv *priv = hw->priv;
1351 struct ath_common *common = ath9k_hw_common(priv->ah);
1352 struct ath9k_tx_queue_info qi;
1353 int ret = 0, qnum;
1354
1355 if (queue >= WME_NUM_AC)
1356 return 0;
1357
1358 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301359 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301360
1361 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1362
1363 qi.tqi_aifs = params->aifs;
1364 qi.tqi_cwmin = params->cw_min;
1365 qi.tqi_cwmax = params->cw_max;
1366 qi.tqi_burstTime = params->txop;
1367
1368 qnum = get_hw_qnum(queue, priv->hwq_map);
1369
Joe Perches226afe62010-12-02 19:12:37 -08001370 ath_dbg(common, ATH_DBG_CONFIG,
1371 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1372 queue, qnum, params->aifs, params->cw_min,
1373 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301374
Sujithe1572c52010-03-24 13:42:13 +05301375 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301376 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001377 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301378 goto out;
1379 }
Sujithfb9987d2010-03-17 14:25:25 +05301380
Sujith764580f2010-06-01 15:14:19 +05301381 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001382 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301383 ath9k_htc_beaconq_config(priv);
1384out:
Sujithcb551df2010-06-01 15:14:12 +05301385 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301386 mutex_unlock(&priv->mutex);
1387
1388 return ret;
1389}
1390
1391static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1392 enum set_key_cmd cmd,
1393 struct ieee80211_vif *vif,
1394 struct ieee80211_sta *sta,
1395 struct ieee80211_key_conf *key)
1396{
1397 struct ath9k_htc_priv *priv = hw->priv;
1398 struct ath_common *common = ath9k_hw_common(priv->ah);
1399 int ret = 0;
1400
Sujithe1572c52010-03-24 13:42:13 +05301401 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301402 return -ENOSPC;
1403
1404 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001405 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301406 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301407
1408 switch (cmd) {
1409 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001410 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301411 if (ret >= 0) {
1412 key->hw_key_idx = ret;
1413 /* push IV and Michael MIC generation to stack */
1414 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001415 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301416 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001417 if (priv->ah->sw_mgmt_crypto &&
1418 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301419 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1420 ret = 0;
1421 }
1422 break;
1423 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001424 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301425 break;
1426 default:
1427 ret = -EINVAL;
1428 }
1429
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301430 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301431 mutex_unlock(&priv->mutex);
1432
1433 return ret;
1434}
1435
1436static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1437 struct ieee80211_vif *vif,
1438 struct ieee80211_bss_conf *bss_conf,
1439 u32 changed)
1440{
1441 struct ath9k_htc_priv *priv = hw->priv;
1442 struct ath_hw *ah = priv->ah;
1443 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301444 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301445
1446 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301447 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301448
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301449 /*
1450 * Set the HW AID/BSSID only for the first station interface
1451 * or in IBSS mode.
1452 */
1453 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1454 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1455 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301456
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301457
1458 if (changed & BSS_CHANGED_ASSOC) {
1459 if (set_assoc) {
1460 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1461 bss_conf->assoc);
1462
1463 common->curaid = bss_conf->assoc ?
1464 bss_conf->aid : 0;
1465
1466 if (bss_conf->assoc)
1467 ath9k_htc_start_ani(priv);
1468 else
1469 ath9k_htc_stop_ani(priv);
1470 }
Sujithfb9987d2010-03-17 14:25:25 +05301471 }
1472
1473 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301474 if (set_assoc) {
1475 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1476 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301477
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301478 ath_dbg(common, ATH_DBG_CONFIG,
1479 "BSSID: %pM aid: 0x%x\n",
1480 common->curbssid, common->curaid);
1481 }
Sujithfb9987d2010-03-17 14:25:25 +05301482 }
1483
Sujith Manoharana5fae372011-02-21 07:49:53 +05301484 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1485 ath_dbg(common, ATH_DBG_CONFIG,
1486 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301487 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301488 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301489 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301490 }
1491
Sujith Manoharana5fae372011-02-21 07:49:53 +05301492 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1493 /*
1494 * Disable SWBA interrupt only if there are no
1495 * AP/IBSS interfaces.
1496 */
1497 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1498 ath_dbg(common, ATH_DBG_CONFIG,
1499 "Beacon disabled for BSS: %pM\n",
1500 bss_conf->bssid);
1501 priv->op_flags &= ~OP_ENABLE_BEACON;
1502 ath9k_htc_beacon_config(priv, vif);
1503 }
1504 }
1505
1506 if (changed & BSS_CHANGED_BEACON_INT) {
1507 /*
1508 * Reset the HW TSF for the first AP interface.
1509 */
1510 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1511 (priv->nvifs == 1) &&
1512 (priv->num_ap_vif == 1) &&
1513 (vif->type == NL80211_IFTYPE_AP)) {
1514 priv->op_flags |= OP_TSF_RESET;
1515 }
1516 ath_dbg(common, ATH_DBG_CONFIG,
1517 "Beacon interval changed for BSS: %pM\n",
1518 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301519 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301520 }
1521
Sujithfb9987d2010-03-17 14:25:25 +05301522 if (changed & BSS_CHANGED_ERP_SLOT) {
1523 if (bss_conf->use_short_slot)
1524 ah->slottime = 9;
1525 else
1526 ah->slottime = 20;
1527
1528 ath9k_hw_init_global_settings(ah);
1529 }
1530
Sujith2c76ef82010-05-17 12:01:18 +05301531 if (changed & BSS_CHANGED_HT)
1532 ath9k_htc_update_rate(priv, vif, bss_conf);
1533
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301534 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301535 mutex_unlock(&priv->mutex);
1536}
1537
1538static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1539{
1540 struct ath9k_htc_priv *priv = hw->priv;
1541 u64 tsf;
1542
1543 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301544 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301545 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301546 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301547 mutex_unlock(&priv->mutex);
1548
1549 return tsf;
1550}
1551
1552static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1553{
1554 struct ath9k_htc_priv *priv = hw->priv;
1555
1556 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301557 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301558 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301559 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301560 mutex_unlock(&priv->mutex);
1561}
1562
1563static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1564{
1565 struct ath9k_htc_priv *priv = hw->priv;
1566
1567 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301568 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301569 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301570 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301571 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301572}
1573
1574static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1575 struct ieee80211_vif *vif,
1576 enum ieee80211_ampdu_mlme_action action,
1577 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001578 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301579{
1580 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301581 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301582 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301583
Sujith Manoharan87df8952011-02-21 07:49:08 +05301584 mutex_lock(&priv->mutex);
1585
Sujithfb9987d2010-03-17 14:25:25 +05301586 switch (action) {
1587 case IEEE80211_AMPDU_RX_START:
1588 break;
1589 case IEEE80211_AMPDU_RX_STOP:
1590 break;
1591 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301592 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1593 if (!ret)
1594 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1595 break;
Sujithfb9987d2010-03-17 14:25:25 +05301596 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301597 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1598 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301599 break;
1600 case IEEE80211_AMPDU_TX_OPERATIONAL:
1601 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301602 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301603 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301604 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301605 break;
1606 default:
Joe Perches38002762010-12-02 19:12:36 -08001607 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301608 }
1609
Sujith Manoharan87df8952011-02-21 07:49:08 +05301610 mutex_unlock(&priv->mutex);
1611
Sujithd7ca2132010-06-15 10:24:37 +05301612 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301613}
1614
1615static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1616{
1617 struct ath9k_htc_priv *priv = hw->priv;
1618
1619 mutex_lock(&priv->mutex);
1620 spin_lock_bh(&priv->beacon_lock);
1621 priv->op_flags |= OP_SCANNING;
1622 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301623 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301624 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301625 mutex_unlock(&priv->mutex);
1626}
1627
1628static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1629{
1630 struct ath9k_htc_priv *priv = hw->priv;
1631
1632 mutex_lock(&priv->mutex);
1633 spin_lock_bh(&priv->beacon_lock);
1634 priv->op_flags &= ~OP_SCANNING;
1635 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301636 ath9k_htc_ps_wakeup(priv);
1637 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301638 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301639 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301640}
1641
1642static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1643{
1644 return 0;
1645}
1646
1647static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1648 u8 coverage_class)
1649{
1650 struct ath9k_htc_priv *priv = hw->priv;
1651
1652 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301653 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301654 priv->ah->coverage_class = coverage_class;
1655 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301656 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301657 mutex_unlock(&priv->mutex);
1658}
1659
1660struct ieee80211_ops ath9k_htc_ops = {
1661 .tx = ath9k_htc_tx,
1662 .start = ath9k_htc_start,
1663 .stop = ath9k_htc_stop,
1664 .add_interface = ath9k_htc_add_interface,
1665 .remove_interface = ath9k_htc_remove_interface,
1666 .config = ath9k_htc_config,
1667 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301668 .sta_add = ath9k_htc_sta_add,
1669 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301670 .conf_tx = ath9k_htc_conf_tx,
1671 .bss_info_changed = ath9k_htc_bss_info_changed,
1672 .set_key = ath9k_htc_set_key,
1673 .get_tsf = ath9k_htc_get_tsf,
1674 .set_tsf = ath9k_htc_set_tsf,
1675 .reset_tsf = ath9k_htc_reset_tsf,
1676 .ampdu_action = ath9k_htc_ampdu_action,
1677 .sw_scan_start = ath9k_htc_sw_scan_start,
1678 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1679 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1680 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1681 .set_coverage_class = ath9k_htc_set_coverage_class,
1682};