blob: 4de38643cb5311429abe06716b8e993a7d654e72 [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);
335 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530336 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530337}
338
339static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
340{
341 struct ath_common *common = ath9k_hw_common(priv->ah);
342 struct ath9k_htc_target_vif hvif;
343 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530344 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530345 u8 cmd_rsp;
346
Sujith Manoharana97b4782011-02-21 07:48:00 +0530347 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
348 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
349 ret = -ENOBUFS;
350 goto err_vif;
351 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530352
Sujith Manoharana97b4782011-02-21 07:48:00 +0530353 sta_idx = ffz(priv->sta_slot);
354 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
355 ret = -ENOBUFS;
356 goto err_vif;
357 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530358
359 /*
360 * Add an interface.
361 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530362 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
363 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
364
Sujith Manoharane4c62502011-04-13 11:24:43 +0530365 hvif.opmode = HTC_M_MONITOR;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530366 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530367
368 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
369 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530370 goto err_vif;
371
372 /*
373 * Assign the monitor interface index as a special case here.
374 * This is needed when the interface is brought down.
375 */
376 priv->mon_vif_idx = hvif.index;
377 priv->vif_slot |= (1 << hvif.index);
378
379 /*
380 * Set the hardware mode to monitor only if there are no
381 * other interfaces.
382 */
383 if (!priv->nvifs)
384 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530385
386 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530387
388 /*
389 * Associate a station with the interface for packet injection.
390 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530391 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
392
393 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
394
395 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530396 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530397 tsta.vif_index = hvif.index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530398 tsta.maxampdu = cpu_to_be16(0xffff);
Sujith Manoharancc721282011-01-03 21:22:18 +0530399
400 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
401 if (ret) {
402 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530403 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530404 }
405
Sujith Manoharana97b4782011-02-21 07:48:00 +0530406 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530407 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530408 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530409 priv->ah->is_monitoring = true;
410
Sujith Manoharana97b4782011-02-21 07:48:00 +0530411 ath_dbg(common, ATH_DBG_CONFIG,
412 "Attached a monitor interface at idx: %d, sta idx: %d\n",
413 priv->mon_vif_idx, sta_idx);
414
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530415 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530416
Sujith Manoharana97b4782011-02-21 07:48:00 +0530417err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530418 /*
419 * Remove the interface from the target.
420 */
421 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530422err_vif:
423 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
424
Sujith Manoharancc721282011-01-03 21:22:18 +0530425 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530426}
427
428static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
429{
430 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530431 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530432 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530433
Sujith Manoharancc721282011-01-03 21:22:18 +0530434 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530435
Sujith Manoharana97b4782011-02-21 07:48:00 +0530436 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530437
438 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
439 if (ret) {
440 ath_err(common, "Unable to remove station entry for monitor mode\n");
441 return ret;
442 }
443
Sujith Manoharana97b4782011-02-21 07:48:00 +0530444 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530445 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530446 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530447
Sujith Manoharana97b4782011-02-21 07:48:00 +0530448 ath_dbg(common, ATH_DBG_CONFIG,
449 "Removed a monitor interface at idx: %d, sta idx: %d\n",
450 priv->mon_vif_idx, sta_idx);
451
Sujith Manoharancc721282011-01-03 21:22:18 +0530452 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530453}
454
Sujithfb9987d2010-03-17 14:25:25 +0530455static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
456 struct ieee80211_vif *vif,
457 struct ieee80211_sta *sta)
458{
459 struct ath_common *common = ath9k_hw_common(priv->ah);
460 struct ath9k_htc_target_sta tsta;
461 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
462 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530463 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530464 u8 cmd_rsp;
465
466 if (priv->nstations >= ATH9K_HTC_MAX_STA)
467 return -ENOBUFS;
468
Sujith Manoharana97b4782011-02-21 07:48:00 +0530469 sta_idx = ffz(priv->sta_slot);
470 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
471 return -ENOBUFS;
472
Sujithfb9987d2010-03-17 14:25:25 +0530473 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
474
475 if (sta) {
476 ista = (struct ath9k_htc_sta *) sta->drv_priv;
477 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
478 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
Sujithfb9987d2010-03-17 14:25:25 +0530479 tsta.is_vif_sta = 0;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530480 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530481 } else {
482 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
483 tsta.is_vif_sta = 1;
484 }
485
Sujith Manoharana97b4782011-02-21 07:48:00 +0530486 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530487 tsta.vif_index = avp->index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530488 tsta.maxampdu = cpu_to_be16(0xffff);
Sujithfb9987d2010-03-17 14:25:25 +0530489 if (sta && sta->ht_cap.ht_supported)
490 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
491
492 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
493 if (ret) {
494 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800495 ath_err(common,
496 "Unable to add station entry for: %pM\n",
497 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530498 return ret;
499 }
500
Sujith Manoharana97b4782011-02-21 07:48:00 +0530501 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800502 ath_dbg(common, ATH_DBG_CONFIG,
503 "Added a station entry for: %pM (idx: %d)\n",
504 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530505 } else {
506 ath_dbg(common, ATH_DBG_CONFIG,
507 "Added a station entry for VIF %d (idx: %d)\n",
508 avp->index, tsta.sta_index);
509 }
Sujithfb9987d2010-03-17 14:25:25 +0530510
Sujith Manoharana97b4782011-02-21 07:48:00 +0530511 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530512 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530513 if (!sta)
514 priv->vif_sta_pos[avp->index] = sta_idx;
515
Sujithfb9987d2010-03-17 14:25:25 +0530516 return 0;
517}
518
519static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
520 struct ieee80211_vif *vif,
521 struct ieee80211_sta *sta)
522{
523 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530524 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530525 struct ath9k_htc_sta *ista;
526 int ret;
527 u8 cmd_rsp, sta_idx;
528
529 if (sta) {
530 ista = (struct ath9k_htc_sta *) sta->drv_priv;
531 sta_idx = ista->index;
532 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530533 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530534 }
535
536 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
537 if (ret) {
538 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800539 ath_err(common,
540 "Unable to remove station entry for: %pM\n",
541 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530542 return ret;
543 }
544
Sujith Manoharana97b4782011-02-21 07:48:00 +0530545 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800546 ath_dbg(common, ATH_DBG_CONFIG,
547 "Removed a station entry for: %pM (idx: %d)\n",
548 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530549 } else {
550 ath_dbg(common, ATH_DBG_CONFIG,
551 "Removed a station entry for VIF %d (idx: %d)\n",
552 avp->index, sta_idx);
553 }
Sujithfb9987d2010-03-17 14:25:25 +0530554
Sujith Manoharana97b4782011-02-21 07:48:00 +0530555 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530556 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530557
Sujithfb9987d2010-03-17 14:25:25 +0530558 return 0;
559}
560
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530561int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530562{
563 struct ath9k_htc_cap_target tcap;
564 int ret;
565 u8 cmd_rsp;
566
567 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
568
569 /* FIXME: Values are hardcoded */
570 tcap.flags = 0x240c40;
571 tcap.flags_ext = 0x80601000;
572 tcap.ampdu_limit = 0xffff0000;
573 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530574 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530575 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530576 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530577
578 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
579
580 return ret;
581}
582
Sujith0d425a72010-05-17 12:01:16 +0530583static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
584 struct ieee80211_sta *sta,
585 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530586{
Sujithfb9987d2010-03-17 14:25:25 +0530587 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
588 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530589 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530590 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530591
Sujithea46e642010-06-02 15:53:50 +0530592 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530593
594 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
595 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530596 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530597 = (sband->bitrates[i].bitrate * 2) / 10;
598 j++;
599 }
600 }
Sujith0d425a72010-05-17 12:01:16 +0530601 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530602
603 if (sta->ht_cap.ht_supported) {
604 for (i = 0, j = 0; i < 77; i++) {
605 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530606 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530607 if (j == ATH_HTC_RATE_MAX)
608 break;
609 }
Sujith0d425a72010-05-17 12:01:16 +0530610 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530611
612 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200613 if (sta->ht_cap.mcs.rx_mask[1])
614 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530615 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
616 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530617 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530618 if (conf_is_ht40(&priv->hw->conf) &&
619 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530620 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530621 else if (conf_is_ht20(&priv->hw->conf) &&
622 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
623 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530624 }
625
Sujith0d425a72010-05-17 12:01:16 +0530626 trate->sta_index = ista->index;
627 trate->isnew = 1;
628 trate->capflags = cpu_to_be32(caps);
629}
Sujithfb9987d2010-03-17 14:25:25 +0530630
Sujith0d425a72010-05-17 12:01:16 +0530631static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
632 struct ath9k_htc_target_rate *trate)
633{
634 struct ath_common *common = ath9k_hw_common(priv->ah);
635 int ret;
636 u8 cmd_rsp;
637
638 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530639 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800640 ath_err(common,
641 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530642 }
643
Sujith0d425a72010-05-17 12:01:16 +0530644 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530645}
646
Sujith0d425a72010-05-17 12:01:16 +0530647static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
648 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530649{
Sujithfb9987d2010-03-17 14:25:25 +0530650 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530651 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530652 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530653
Sujith0d425a72010-05-17 12:01:16 +0530654 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
655 ath9k_htc_setup_rate(priv, sta, &trate);
656 ret = ath9k_htc_send_rate_cmd(priv, &trate);
657 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800658 ath_dbg(common, ATH_DBG_CONFIG,
659 "Updated target sta: %pM, rate caps: 0x%X\n",
660 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530661}
662
Sujith2c76ef82010-05-17 12:01:18 +0530663static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
664 struct ieee80211_vif *vif,
665 struct ieee80211_bss_conf *bss_conf)
666{
667 struct ath_common *common = ath9k_hw_common(priv->ah);
668 struct ath9k_htc_target_rate trate;
669 struct ieee80211_sta *sta;
670 int ret;
671
672 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
673
674 rcu_read_lock();
675 sta = ieee80211_find_sta(vif, bss_conf->bssid);
676 if (!sta) {
677 rcu_read_unlock();
678 return;
679 }
680 ath9k_htc_setup_rate(priv, sta, &trate);
681 rcu_read_unlock();
682
683 ret = ath9k_htc_send_rate_cmd(priv, &trate);
684 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800685 ath_dbg(common, ATH_DBG_CONFIG,
686 "Updated target sta: %pM, rate caps: 0x%X\n",
687 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530688}
689
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400690static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
691 struct ieee80211_vif *vif,
692 struct ieee80211_sta *sta,
693 enum ieee80211_ampdu_mlme_action action,
694 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530695{
696 struct ath_common *common = ath9k_hw_common(priv->ah);
697 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200698 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530699 int ret = 0;
700 u8 cmd_rsp;
701
Dan Carpenter0730d112010-05-08 18:24:02 +0200702 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530703 return -EINVAL;
704
Sujithfb9987d2010-03-17 14:25:25 +0530705 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530706 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530707
Sujithef98c3c2010-03-29 16:07:11 +0530708 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530709 aggr.tidno = tid & 0xf;
710 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530711
Sujithfb9987d2010-03-17 14:25:25 +0530712 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
713 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800714 ath_dbg(common, ATH_DBG_CONFIG,
715 "Unable to %s TX aggregation for (%pM, %d)\n",
716 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530717 else
Joe Perches226afe62010-12-02 19:12:37 -0800718 ath_dbg(common, ATH_DBG_CONFIG,
719 "%s TX aggregation for (%pM, %d)\n",
720 (aggr.aggr_enable) ? "Starting" : "Stopping",
721 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530722
Sujith Manoharan658ef042011-04-13 11:25:00 +0530723 spin_lock_bh(&priv->tx.tx_lock);
Sujithd7ca2132010-06-15 10:24:37 +0530724 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530725 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530726
727 return ret;
728}
729
Sujithfb9987d2010-03-17 14:25:25 +0530730/*******/
731/* ANI */
732/*******/
733
Sujith Manoharana2362542011-02-21 07:49:38 +0530734void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530735{
736 struct ath_common *common = ath9k_hw_common(priv->ah);
737 unsigned long timestamp = jiffies_to_msecs(jiffies);
738
739 common->ani.longcal_timer = timestamp;
740 common->ani.shortcal_timer = timestamp;
741 common->ani.checkani_timer = timestamp;
742
Sujith Manoharana2362542011-02-21 07:49:38 +0530743 priv->op_flags |= OP_ANI_RUNNING;
744
745 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530746 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
747}
748
Sujith Manoharana2362542011-02-21 07:49:38 +0530749void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
750{
751 cancel_delayed_work_sync(&priv->ani_work);
752 priv->op_flags &= ~OP_ANI_RUNNING;
753}
754
755void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530756{
757 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530758 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530759 struct ath_hw *ah = priv->ah;
760 struct ath_common *common = ath9k_hw_common(ah);
761 bool longcal = false;
762 bool shortcal = false;
763 bool aniflag = false;
764 unsigned int timestamp = jiffies_to_msecs(jiffies);
765 u32 cal_interval, short_cal_interval;
766
Sujith Manoharana2362542011-02-21 07:49:38 +0530767 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
768 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530769
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530770 /* Only calibrate if awake */
771 if (ah->power_mode != ATH9K_PM_AWAKE)
772 goto set_timer;
773
Sujithfb9987d2010-03-17 14:25:25 +0530774 /* Long calibration runs independently of short calibration. */
775 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
776 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800777 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530778 common->ani.longcal_timer = timestamp;
779 }
780
781 /* Short calibration applies only while caldone is false */
782 if (!common->ani.caldone) {
783 if ((timestamp - common->ani.shortcal_timer) >=
784 short_cal_interval) {
785 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800786 ath_dbg(common, ATH_DBG_ANI,
787 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530788 common->ani.shortcal_timer = timestamp;
789 common->ani.resetcal_timer = timestamp;
790 }
791 } else {
792 if ((timestamp - common->ani.resetcal_timer) >=
793 ATH_RESTART_CALINTERVAL) {
794 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
795 if (common->ani.caldone)
796 common->ani.resetcal_timer = timestamp;
797 }
798 }
799
800 /* Verify whether we must check ANI */
801 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
802 aniflag = true;
803 common->ani.checkani_timer = timestamp;
804 }
805
806 /* Skip all processing if there's nothing to do. */
807 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530808
809 ath9k_htc_ps_wakeup(priv);
810
Sujithfb9987d2010-03-17 14:25:25 +0530811 /* Call ANI routine if necessary */
812 if (aniflag)
813 ath9k_hw_ani_monitor(ah, ah->curchan);
814
815 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200816 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530817 common->ani.caldone =
818 ath9k_hw_calibrate(ah, ah->curchan,
819 common->rx_chainmask,
820 longcal);
821
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530822 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530823 }
824
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530825set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530826 /*
827 * Set timer interval based on previous results.
828 * The interval must be the shortest necessary to satisfy ANI,
829 * short calibration and long calibration.
830 */
831 cal_interval = ATH_LONG_CALINTERVAL;
832 if (priv->ah->config.enable_ani)
833 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
834 if (!common->ani.caldone)
835 cal_interval = min(cal_interval, (u32)short_cal_interval);
836
Sujith Manoharana2362542011-02-21 07:49:38 +0530837 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530838 msecs_to_jiffies(cal_interval));
839}
840
Sujithfb9987d2010-03-17 14:25:25 +0530841/**********************/
842/* mac80211 Callbacks */
843/**********************/
844
Johannes Berg7bb45682011-02-24 14:42:06 +0100845static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +0530846{
847 struct ieee80211_hdr *hdr;
848 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530849 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530850 int padpos, padsize, ret, slot;
Sujithfb9987d2010-03-17 14:25:25 +0530851
852 hdr = (struct ieee80211_hdr *) skb->data;
853
854 /* Add the padding after the header if this is not already done */
855 padpos = ath9k_cmn_padpos(hdr->frame_control);
856 padsize = padpos & 3;
857 if (padsize && skb->len > padpos) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530858 if (skb_headroom(skb) < padsize) {
859 ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n");
Johannes Berg7bb45682011-02-24 14:42:06 +0100860 goto fail_tx;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530861 }
Sujithfb9987d2010-03-17 14:25:25 +0530862 skb_push(skb, padsize);
863 memmove(skb->data, skb->data + padsize, padpos);
864 }
865
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530866 slot = ath9k_htc_tx_get_slot(priv);
867 if (slot < 0) {
868 ath_dbg(common, ATH_DBG_XMIT, "No free TX slot\n");
869 goto fail_tx;
870 }
871
872 ret = ath9k_htc_tx_start(priv, skb, slot, false);
Sujith7757dfe2010-03-29 16:07:17 +0530873 if (ret != 0) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530874 ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530875 goto clear_slot;
Sujithfb9987d2010-03-17 14:25:25 +0530876 }
877
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530878 ath9k_htc_check_stop_queues(priv);
879
Johannes Berg7bb45682011-02-24 14:42:06 +0100880 return;
Sujithfb9987d2010-03-17 14:25:25 +0530881
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530882clear_slot:
883 ath9k_htc_tx_clear_slot(priv, slot);
Sujithfb9987d2010-03-17 14:25:25 +0530884fail_tx:
885 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530886}
887
Sujith881ac6a2010-06-01 15:14:11 +0530888static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530889{
890 struct ath9k_htc_priv *priv = hw->priv;
891 struct ath_hw *ah = priv->ah;
892 struct ath_common *common = ath9k_hw_common(ah);
893 struct ieee80211_channel *curchan = hw->conf.channel;
894 struct ath9k_channel *init_channel;
895 int ret = 0;
896 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530897 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530898 u8 cmd_rsp;
899
Sujith881ac6a2010-06-01 15:14:11 +0530900 mutex_lock(&priv->mutex);
901
Joe Perches226afe62010-12-02 19:12:37 -0800902 ath_dbg(common, ATH_DBG_CONFIG,
903 "Starting driver with initial channel: %d MHz\n",
904 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530905
Sujith21d51302010-06-01 15:14:18 +0530906 /* Ensure that HW is awake before flushing RX */
907 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
908 WMI_CMD(WMI_FLUSH_RECV_CMDID);
909
Sujithfb9987d2010-03-17 14:25:25 +0530910 /* setup initial channel */
911 init_channel = ath9k_cmn_get_curchannel(hw, ah);
912
Sujithfb9987d2010-03-17 14:25:25 +0530913 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200914 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530915 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800916 ath_err(common,
917 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
918 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530919 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530920 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530921 }
922
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530923 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
924 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530925
926 mode = ath9k_htc_get_curmode(priv, init_channel);
927 htc_mode = cpu_to_be16(mode);
928 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530929 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530930 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530931
932 ath9k_host_rx_init(priv);
933
Sujith Manoharan1057b752011-02-21 07:48:09 +0530934 ret = ath9k_htc_update_cap_target(priv);
935 if (ret)
936 ath_dbg(common, ATH_DBG_CONFIG,
937 "Failed to update capability in target\n");
938
Sujithfb9987d2010-03-17 14:25:25 +0530939 priv->op_flags &= ~OP_INVALID;
940 htc_start(priv->htc);
941
Sujith Manoharan658ef042011-04-13 11:25:00 +0530942 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530943 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530944 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530945
946 ieee80211_wake_queues(hw);
947
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530948 mod_timer(&priv->tx.cleanup_timer,
949 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
950
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530951 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
952 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
953 AR_STOMP_LOW_WLAN_WGHT);
954 ath9k_hw_btcoex_enable(ah);
955 ath_htc_resume_btcoex_work(priv);
956 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530957 mutex_unlock(&priv->mutex);
958
959 return ret;
960}
961
Sujith881ac6a2010-06-01 15:14:11 +0530962static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530963{
964 struct ath9k_htc_priv *priv = hw->priv;
965 struct ath_hw *ah = priv->ah;
966 struct ath_common *common = ath9k_hw_common(ah);
967 int ret = 0;
968 u8 cmd_rsp;
969
Sujith881ac6a2010-06-01 15:14:11 +0530970 mutex_lock(&priv->mutex);
971
Sujithfb9987d2010-03-17 14:25:25 +0530972 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -0800973 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530974 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530975 return;
976 }
977
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530978 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530979
Sujithfb9987d2010-03-17 14:25:25 +0530980 WMI_CMD(WMI_DISABLE_INTR_CMDID);
981 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
982 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100983
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100984 tasklet_kill(&priv->rx_tasklet);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100985
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530986 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530987 ath9k_htc_tx_drain(priv);
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530988 ath9k_wmi_event_drain(priv);
989
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100990 mutex_unlock(&priv->mutex);
991
992 /* Cancel all the running timers/work .. */
993 cancel_work_sync(&priv->fatal_work);
994 cancel_work_sync(&priv->ps_work);
995 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +0530996 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100997 ath9k_led_stop_brightness(priv);
998
999 mutex_lock(&priv->mutex);
1000
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301001 if (ah->btcoex_hw.enabled) {
1002 ath9k_hw_btcoex_disable(ah);
1003 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1004 ath_htc_cancel_btcoex_work(priv);
1005 }
1006
Sujith Manoharana97b4782011-02-21 07:48:00 +05301007 /* Remove a monitor interface if it's present. */
1008 if (priv->ah->is_monitoring)
1009 ath9k_htc_remove_monitor_interface(priv);
1010
Sujithe9201f02010-06-01 15:14:17 +05301011 ath9k_hw_phy_disable(ah);
1012 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301013 ath9k_htc_ps_restore(priv);
1014 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1015
Sujithfb9987d2010-03-17 14:25:25 +05301016 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301017
Joe Perches226afe62010-12-02 19:12:37 -08001018 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301019 mutex_unlock(&priv->mutex);
1020}
1021
Sujithfb9987d2010-03-17 14:25:25 +05301022static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1023 struct ieee80211_vif *vif)
1024{
1025 struct ath9k_htc_priv *priv = hw->priv;
1026 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1027 struct ath_common *common = ath9k_hw_common(priv->ah);
1028 struct ath9k_htc_target_vif hvif;
1029 int ret = 0;
1030 u8 cmd_rsp;
1031
1032 mutex_lock(&priv->mutex);
1033
Sujith Manoharana97b4782011-02-21 07:48:00 +05301034 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301035 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301036 return -ENOBUFS;
1037 }
1038
1039 if (priv->num_ibss_vif ||
1040 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1041 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1042 mutex_unlock(&priv->mutex);
1043 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301044 }
1045
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301046 if (((vif->type == NL80211_IFTYPE_AP) ||
1047 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1048 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1049 ath_err(common, "Max. number of beaconing interfaces reached\n");
1050 mutex_unlock(&priv->mutex);
1051 return -ENOBUFS;
1052 }
1053
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301054 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301055 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1056 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1057
1058 switch (vif->type) {
1059 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301060 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301061 break;
1062 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301063 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301064 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301065 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301066 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301067 break;
Sujithfb9987d2010-03-17 14:25:25 +05301068 default:
Joe Perches38002762010-12-02 19:12:36 -08001069 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301070 "Interface type %d not yet supported\n", vif->type);
1071 ret = -EOPNOTSUPP;
1072 goto out;
1073 }
1074
Sujithfb9987d2010-03-17 14:25:25 +05301075 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301076 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301077 hvif.rtsthreshold = cpu_to_be16(2304);
1078 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1079 if (ret)
1080 goto out;
1081
Sujithfb9987d2010-03-17 14:25:25 +05301082 /*
1083 * We need a node in target to tx mgmt frames
1084 * before association.
1085 */
1086 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301087 if (ret) {
1088 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301089 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301090 }
Sujithfb9987d2010-03-17 14:25:25 +05301091
Sujith Manoharan585895c2011-02-21 07:48:46 +05301092 ath9k_htc_set_bssid_mask(priv, vif);
1093
Sujith Manoharana97b4782011-02-21 07:48:00 +05301094 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301095 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301096
Sujith Manoharan0df83592011-02-21 07:49:15 +05301097 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301098
1099 if ((vif->type == NL80211_IFTYPE_AP) ||
1100 (vif->type == NL80211_IFTYPE_ADHOC))
1101 ath9k_htc_assign_bslot(priv, vif);
1102
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301103 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301104
Sujith Manoharana2362542011-02-21 07:49:38 +05301105 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301106 !(priv->op_flags & OP_ANI_RUNNING)) {
1107 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301108 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301109 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301110
Sujith Manoharana97b4782011-02-21 07:48:00 +05301111 ath_dbg(common, ATH_DBG_CONFIG,
1112 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1113
Sujithfb9987d2010-03-17 14:25:25 +05301114out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301115 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301116 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301117
Sujithfb9987d2010-03-17 14:25:25 +05301118 return ret;
1119}
1120
1121static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1122 struct ieee80211_vif *vif)
1123{
1124 struct ath9k_htc_priv *priv = hw->priv;
1125 struct ath_common *common = ath9k_hw_common(priv->ah);
1126 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1127 struct ath9k_htc_target_vif hvif;
1128 int ret = 0;
1129 u8 cmd_rsp;
1130
Sujithfb9987d2010-03-17 14:25:25 +05301131 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301132 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301133
1134 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1135 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1136 hvif.index = avp->index;
1137 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1138 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301139 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301140
1141 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301142
Sujith Manoharan0df83592011-02-21 07:49:15 +05301143 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301144
1145 if ((vif->type == NL80211_IFTYPE_AP) ||
1146 (vif->type == NL80211_IFTYPE_ADHOC))
1147 ath9k_htc_remove_bslot(priv, vif);
1148
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301149 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301150
Sujith Manoharana2362542011-02-21 07:49:38 +05301151 /*
1152 * Stop ANI only if there are no associated station interfaces.
1153 */
1154 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1155 priv->rearm_ani = false;
1156 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1157 ath9k_htc_vif_iter, priv);
1158 if (!priv->rearm_ani)
1159 ath9k_htc_stop_ani(priv);
1160 }
1161
Sujith Manoharana97b4782011-02-21 07:48:00 +05301162 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1163
Sujithcb551df2010-06-01 15:14:12 +05301164 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301165 mutex_unlock(&priv->mutex);
1166}
1167
1168static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1169{
1170 struct ath9k_htc_priv *priv = hw->priv;
1171 struct ath_common *common = ath9k_hw_common(priv->ah);
1172 struct ieee80211_conf *conf = &hw->conf;
1173
1174 mutex_lock(&priv->mutex);
1175
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301176 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1177 bool enable_radio = false;
1178 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1179
Sujith23367762010-06-01 15:14:16 +05301180 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301181 if (!idle && priv->ps_idle)
1182 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301183 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301184 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301185
1186 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001187 ath_dbg(common, ATH_DBG_CONFIG,
1188 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301189 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1190 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301191 }
1192 }
1193
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301194 /*
1195 * Monitor interface should be added before
1196 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1197 */
1198 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301199 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1200 !priv->ah->is_monitoring)
1201 ath9k_htc_add_monitor_interface(priv);
1202 else if (priv->ah->is_monitoring)
1203 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301204 }
1205
Sujithfb9987d2010-03-17 14:25:25 +05301206 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1207 struct ieee80211_channel *curchan = hw->conf.channel;
1208 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301209
Joe Perches226afe62010-12-02 19:12:37 -08001210 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1211 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301212
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001213 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1214 hw->conf.channel,
1215 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301216
1217 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001218 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301219 mutex_unlock(&priv->mutex);
1220 return -EINVAL;
1221 }
1222
1223 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301224
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301225 if (changed & IEEE80211_CONF_CHANGE_PS) {
1226 if (conf->flags & IEEE80211_CONF_PS) {
1227 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1228 priv->ps_enabled = true;
1229 } else {
1230 priv->ps_enabled = false;
1231 cancel_work_sync(&priv->ps_work);
1232 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1233 }
1234 }
Sujithfb9987d2010-03-17 14:25:25 +05301235
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301236 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1237 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301238 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1239 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301240 }
1241
Sujith23367762010-06-01 15:14:16 +05301242 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1243 mutex_lock(&priv->htc_pm_lock);
1244 if (!priv->ps_idle) {
1245 mutex_unlock(&priv->htc_pm_lock);
1246 goto out;
1247 }
1248 mutex_unlock(&priv->htc_pm_lock);
1249
Joe Perches226afe62010-12-02 19:12:37 -08001250 ath_dbg(common, ATH_DBG_CONFIG,
1251 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301252 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301253 }
1254
Sujith23367762010-06-01 15:14:16 +05301255out:
Sujithfb9987d2010-03-17 14:25:25 +05301256 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301257 return 0;
1258}
1259
1260#define SUPPORTED_FILTERS \
1261 (FIF_PROMISC_IN_BSS | \
1262 FIF_ALLMULTI | \
1263 FIF_CONTROL | \
1264 FIF_PSPOLL | \
1265 FIF_OTHER_BSS | \
1266 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301267 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301268 FIF_FCSFAIL)
1269
1270static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1271 unsigned int changed_flags,
1272 unsigned int *total_flags,
1273 u64 multicast)
1274{
1275 struct ath9k_htc_priv *priv = hw->priv;
1276 u32 rfilt;
1277
1278 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301279 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301280
Sujithfb9987d2010-03-17 14:25:25 +05301281 changed_flags &= SUPPORTED_FILTERS;
1282 *total_flags &= SUPPORTED_FILTERS;
1283
1284 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301285 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301286 ath9k_hw_setrxfilter(priv->ah, rfilt);
1287
Joe Perches226afe62010-12-02 19:12:37 -08001288 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1289 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301290
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301291 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301292 mutex_unlock(&priv->mutex);
1293}
1294
Sujithabd984e2010-05-18 15:26:04 +05301295static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1296 struct ieee80211_vif *vif,
1297 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301298{
1299 struct ath9k_htc_priv *priv = hw->priv;
1300 int ret;
1301
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301302 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301303 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301304 ret = ath9k_htc_add_station(priv, vif, sta);
1305 if (!ret)
1306 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301307 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301308 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301309
1310 return ret;
1311}
1312
1313static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1314 struct ieee80211_vif *vif,
1315 struct ieee80211_sta *sta)
1316{
1317 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301318 struct ath9k_htc_sta *ista;
Sujithabd984e2010-05-18 15:26:04 +05301319 int ret;
1320
1321 mutex_lock(&priv->mutex);
1322 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301323 ista = (struct ath9k_htc_sta *) sta->drv_priv;
1324 htc_sta_drain(priv->htc, ista->index);
Sujithabd984e2010-05-18 15:26:04 +05301325 ret = ath9k_htc_remove_station(priv, vif, sta);
1326 ath9k_htc_ps_restore(priv);
1327 mutex_unlock(&priv->mutex);
1328
1329 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301330}
1331
1332static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1333 const struct ieee80211_tx_queue_params *params)
1334{
1335 struct ath9k_htc_priv *priv = hw->priv;
1336 struct ath_common *common = ath9k_hw_common(priv->ah);
1337 struct ath9k_tx_queue_info qi;
1338 int ret = 0, qnum;
1339
1340 if (queue >= WME_NUM_AC)
1341 return 0;
1342
1343 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301344 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301345
1346 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1347
1348 qi.tqi_aifs = params->aifs;
1349 qi.tqi_cwmin = params->cw_min;
1350 qi.tqi_cwmax = params->cw_max;
1351 qi.tqi_burstTime = params->txop;
1352
1353 qnum = get_hw_qnum(queue, priv->hwq_map);
1354
Joe Perches226afe62010-12-02 19:12:37 -08001355 ath_dbg(common, ATH_DBG_CONFIG,
1356 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1357 queue, qnum, params->aifs, params->cw_min,
1358 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301359
Sujithe1572c52010-03-24 13:42:13 +05301360 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301361 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001362 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301363 goto out;
1364 }
Sujithfb9987d2010-03-17 14:25:25 +05301365
Sujith764580f2010-06-01 15:14:19 +05301366 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001367 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301368 ath9k_htc_beaconq_config(priv);
1369out:
Sujithcb551df2010-06-01 15:14:12 +05301370 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301371 mutex_unlock(&priv->mutex);
1372
1373 return ret;
1374}
1375
1376static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1377 enum set_key_cmd cmd,
1378 struct ieee80211_vif *vif,
1379 struct ieee80211_sta *sta,
1380 struct ieee80211_key_conf *key)
1381{
1382 struct ath9k_htc_priv *priv = hw->priv;
1383 struct ath_common *common = ath9k_hw_common(priv->ah);
1384 int ret = 0;
1385
Sujithe1572c52010-03-24 13:42:13 +05301386 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301387 return -ENOSPC;
1388
1389 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001390 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301391 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301392
1393 switch (cmd) {
1394 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001395 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301396 if (ret >= 0) {
1397 key->hw_key_idx = ret;
1398 /* push IV and Michael MIC generation to stack */
1399 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001400 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301401 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001402 if (priv->ah->sw_mgmt_crypto &&
1403 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301404 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1405 ret = 0;
1406 }
1407 break;
1408 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001409 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301410 break;
1411 default:
1412 ret = -EINVAL;
1413 }
1414
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301415 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301416 mutex_unlock(&priv->mutex);
1417
1418 return ret;
1419}
1420
1421static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1422 struct ieee80211_vif *vif,
1423 struct ieee80211_bss_conf *bss_conf,
1424 u32 changed)
1425{
1426 struct ath9k_htc_priv *priv = hw->priv;
1427 struct ath_hw *ah = priv->ah;
1428 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301429 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301430
1431 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301432 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301433
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301434 /*
1435 * Set the HW AID/BSSID only for the first station interface
1436 * or in IBSS mode.
1437 */
1438 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1439 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1440 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301441
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301442
1443 if (changed & BSS_CHANGED_ASSOC) {
1444 if (set_assoc) {
1445 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1446 bss_conf->assoc);
1447
1448 common->curaid = bss_conf->assoc ?
1449 bss_conf->aid : 0;
1450
1451 if (bss_conf->assoc)
1452 ath9k_htc_start_ani(priv);
1453 else
1454 ath9k_htc_stop_ani(priv);
1455 }
Sujithfb9987d2010-03-17 14:25:25 +05301456 }
1457
1458 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301459 if (set_assoc) {
1460 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1461 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301462
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301463 ath_dbg(common, ATH_DBG_CONFIG,
1464 "BSSID: %pM aid: 0x%x\n",
1465 common->curbssid, common->curaid);
1466 }
Sujithfb9987d2010-03-17 14:25:25 +05301467 }
1468
Sujith Manoharana5fae372011-02-21 07:49:53 +05301469 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1470 ath_dbg(common, ATH_DBG_CONFIG,
1471 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301472 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301473 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301474 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301475 }
1476
Sujith Manoharana5fae372011-02-21 07:49:53 +05301477 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1478 /*
1479 * Disable SWBA interrupt only if there are no
1480 * AP/IBSS interfaces.
1481 */
1482 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1483 ath_dbg(common, ATH_DBG_CONFIG,
1484 "Beacon disabled for BSS: %pM\n",
1485 bss_conf->bssid);
1486 priv->op_flags &= ~OP_ENABLE_BEACON;
1487 ath9k_htc_beacon_config(priv, vif);
1488 }
1489 }
1490
1491 if (changed & BSS_CHANGED_BEACON_INT) {
1492 /*
1493 * Reset the HW TSF for the first AP interface.
1494 */
1495 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1496 (priv->nvifs == 1) &&
1497 (priv->num_ap_vif == 1) &&
1498 (vif->type == NL80211_IFTYPE_AP)) {
1499 priv->op_flags |= OP_TSF_RESET;
1500 }
1501 ath_dbg(common, ATH_DBG_CONFIG,
1502 "Beacon interval changed for BSS: %pM\n",
1503 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301504 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301505 }
1506
Sujithfb9987d2010-03-17 14:25:25 +05301507 if (changed & BSS_CHANGED_ERP_SLOT) {
1508 if (bss_conf->use_short_slot)
1509 ah->slottime = 9;
1510 else
1511 ah->slottime = 20;
1512
1513 ath9k_hw_init_global_settings(ah);
1514 }
1515
Sujith2c76ef82010-05-17 12:01:18 +05301516 if (changed & BSS_CHANGED_HT)
1517 ath9k_htc_update_rate(priv, vif, bss_conf);
1518
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301519 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301520 mutex_unlock(&priv->mutex);
1521}
1522
1523static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1524{
1525 struct ath9k_htc_priv *priv = hw->priv;
1526 u64 tsf;
1527
1528 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301529 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301530 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301531 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301532 mutex_unlock(&priv->mutex);
1533
1534 return tsf;
1535}
1536
1537static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1538{
1539 struct ath9k_htc_priv *priv = hw->priv;
1540
1541 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301542 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301543 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301544 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301545 mutex_unlock(&priv->mutex);
1546}
1547
1548static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1549{
1550 struct ath9k_htc_priv *priv = hw->priv;
1551
1552 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301553 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301554 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301555 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301556 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301557}
1558
1559static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1560 struct ieee80211_vif *vif,
1561 enum ieee80211_ampdu_mlme_action action,
1562 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001563 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301564{
1565 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301566 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301567 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301568
Sujith Manoharan87df8952011-02-21 07:49:08 +05301569 mutex_lock(&priv->mutex);
1570
Sujithfb9987d2010-03-17 14:25:25 +05301571 switch (action) {
1572 case IEEE80211_AMPDU_RX_START:
1573 break;
1574 case IEEE80211_AMPDU_RX_STOP:
1575 break;
1576 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301577 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1578 if (!ret)
1579 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1580 break;
Sujithfb9987d2010-03-17 14:25:25 +05301581 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301582 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1583 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301584 break;
1585 case IEEE80211_AMPDU_TX_OPERATIONAL:
1586 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301587 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301588 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301589 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301590 break;
1591 default:
Joe Perches38002762010-12-02 19:12:36 -08001592 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301593 }
1594
Sujith Manoharan87df8952011-02-21 07:49:08 +05301595 mutex_unlock(&priv->mutex);
1596
Sujithd7ca2132010-06-15 10:24:37 +05301597 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301598}
1599
1600static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1601{
1602 struct ath9k_htc_priv *priv = hw->priv;
1603
1604 mutex_lock(&priv->mutex);
1605 spin_lock_bh(&priv->beacon_lock);
1606 priv->op_flags |= OP_SCANNING;
1607 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301608 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301609 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301610 mutex_unlock(&priv->mutex);
1611}
1612
1613static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1614{
1615 struct ath9k_htc_priv *priv = hw->priv;
1616
1617 mutex_lock(&priv->mutex);
1618 spin_lock_bh(&priv->beacon_lock);
1619 priv->op_flags &= ~OP_SCANNING;
1620 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301621 ath9k_htc_ps_wakeup(priv);
1622 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301623 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301624 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301625}
1626
1627static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1628{
1629 return 0;
1630}
1631
1632static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1633 u8 coverage_class)
1634{
1635 struct ath9k_htc_priv *priv = hw->priv;
1636
1637 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301638 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301639 priv->ah->coverage_class = coverage_class;
1640 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301641 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301642 mutex_unlock(&priv->mutex);
1643}
1644
1645struct ieee80211_ops ath9k_htc_ops = {
1646 .tx = ath9k_htc_tx,
1647 .start = ath9k_htc_start,
1648 .stop = ath9k_htc_stop,
1649 .add_interface = ath9k_htc_add_interface,
1650 .remove_interface = ath9k_htc_remove_interface,
1651 .config = ath9k_htc_config,
1652 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301653 .sta_add = ath9k_htc_sta_add,
1654 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301655 .conf_tx = ath9k_htc_conf_tx,
1656 .bss_info_changed = ath9k_htc_bss_info_changed,
1657 .set_key = ath9k_htc_set_key,
1658 .get_tsf = ath9k_htc_get_tsf,
1659 .set_tsf = ath9k_htc_set_tsf,
1660 .reset_tsf = ath9k_htc_reset_tsf,
1661 .ampdu_action = ath9k_htc_ampdu_action,
1662 .sw_scan_start = ath9k_htc_sw_scan_start,
1663 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1664 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1665 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1666 .set_coverage_class = ath9k_htc_set_coverage_class,
1667};