blob: 5aa104fe7eebf26e44d60eee2efcfc21d5129ed0 [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);
Sujith Manoharand244f212011-04-28 16:14:05 +05301006
1007#ifdef CONFIG_MAC80211_LEDS
1008 cancel_work_sync(&priv->led_work);
1009#endif
Sujith Manoharana2362542011-02-21 07:49:38 +05301010 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001011
1012 mutex_lock(&priv->mutex);
1013
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301014 if (ah->btcoex_hw.enabled) {
1015 ath9k_hw_btcoex_disable(ah);
1016 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1017 ath_htc_cancel_btcoex_work(priv);
1018 }
1019
Sujith Manoharana97b4782011-02-21 07:48:00 +05301020 /* Remove a monitor interface if it's present. */
1021 if (priv->ah->is_monitoring)
1022 ath9k_htc_remove_monitor_interface(priv);
1023
Sujithe9201f02010-06-01 15:14:17 +05301024 ath9k_hw_phy_disable(ah);
1025 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301026 ath9k_htc_ps_restore(priv);
1027 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1028
Sujithfb9987d2010-03-17 14:25:25 +05301029 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301030
Joe Perches226afe62010-12-02 19:12:37 -08001031 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301032 mutex_unlock(&priv->mutex);
1033}
1034
Sujithfb9987d2010-03-17 14:25:25 +05301035static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1036 struct ieee80211_vif *vif)
1037{
1038 struct ath9k_htc_priv *priv = hw->priv;
1039 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1040 struct ath_common *common = ath9k_hw_common(priv->ah);
1041 struct ath9k_htc_target_vif hvif;
1042 int ret = 0;
1043 u8 cmd_rsp;
1044
1045 mutex_lock(&priv->mutex);
1046
Sujith Manoharana97b4782011-02-21 07:48:00 +05301047 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301048 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301049 return -ENOBUFS;
1050 }
1051
1052 if (priv->num_ibss_vif ||
1053 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1054 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1055 mutex_unlock(&priv->mutex);
1056 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301057 }
1058
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301059 if (((vif->type == NL80211_IFTYPE_AP) ||
1060 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1061 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1062 ath_err(common, "Max. number of beaconing interfaces reached\n");
1063 mutex_unlock(&priv->mutex);
1064 return -ENOBUFS;
1065 }
1066
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301067 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301068 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1069 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1070
1071 switch (vif->type) {
1072 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301073 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301074 break;
1075 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301076 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301077 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301078 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301079 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301080 break;
Sujithfb9987d2010-03-17 14:25:25 +05301081 default:
Joe Perches38002762010-12-02 19:12:36 -08001082 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301083 "Interface type %d not yet supported\n", vif->type);
1084 ret = -EOPNOTSUPP;
1085 goto out;
1086 }
1087
Sujithfb9987d2010-03-17 14:25:25 +05301088 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301089 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301090 hvif.rtsthreshold = cpu_to_be16(2304);
1091 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1092 if (ret)
1093 goto out;
1094
Sujithfb9987d2010-03-17 14:25:25 +05301095 /*
1096 * We need a node in target to tx mgmt frames
1097 * before association.
1098 */
1099 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301100 if (ret) {
1101 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301102 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301103 }
Sujithfb9987d2010-03-17 14:25:25 +05301104
Sujith Manoharan585895c2011-02-21 07:48:46 +05301105 ath9k_htc_set_bssid_mask(priv, vif);
1106
Sujith Manoharana97b4782011-02-21 07:48:00 +05301107 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301108 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301109
Sujith Manoharan0df83592011-02-21 07:49:15 +05301110 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301111
1112 if ((vif->type == NL80211_IFTYPE_AP) ||
1113 (vif->type == NL80211_IFTYPE_ADHOC))
1114 ath9k_htc_assign_bslot(priv, vif);
1115
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301116 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301117
Sujith Manoharana2362542011-02-21 07:49:38 +05301118 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301119 !(priv->op_flags & OP_ANI_RUNNING)) {
1120 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301121 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301122 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301123
Sujith Manoharana97b4782011-02-21 07:48:00 +05301124 ath_dbg(common, ATH_DBG_CONFIG,
1125 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1126
Sujithfb9987d2010-03-17 14:25:25 +05301127out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301128 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301129 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301130
Sujithfb9987d2010-03-17 14:25:25 +05301131 return ret;
1132}
1133
1134static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1135 struct ieee80211_vif *vif)
1136{
1137 struct ath9k_htc_priv *priv = hw->priv;
1138 struct ath_common *common = ath9k_hw_common(priv->ah);
1139 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1140 struct ath9k_htc_target_vif hvif;
1141 int ret = 0;
1142 u8 cmd_rsp;
1143
Sujithfb9987d2010-03-17 14:25:25 +05301144 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301145 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301146
1147 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1148 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1149 hvif.index = avp->index;
1150 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +05301151 if (ret) {
1152 ath_err(common, "Unable to remove interface at idx: %d\n",
1153 avp->index);
1154 }
Sujithfb9987d2010-03-17 14:25:25 +05301155 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301156 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301157
1158 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301159
Sujith Manoharan0df83592011-02-21 07:49:15 +05301160 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301161
1162 if ((vif->type == NL80211_IFTYPE_AP) ||
1163 (vif->type == NL80211_IFTYPE_ADHOC))
1164 ath9k_htc_remove_bslot(priv, vif);
1165
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301166 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301167
Sujith Manoharana2362542011-02-21 07:49:38 +05301168 /*
1169 * Stop ANI only if there are no associated station interfaces.
1170 */
1171 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1172 priv->rearm_ani = false;
1173 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1174 ath9k_htc_vif_iter, priv);
1175 if (!priv->rearm_ani)
1176 ath9k_htc_stop_ani(priv);
1177 }
1178
Sujith Manoharana97b4782011-02-21 07:48:00 +05301179 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1180
Sujithcb551df2010-06-01 15:14:12 +05301181 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301182 mutex_unlock(&priv->mutex);
1183}
1184
1185static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1186{
1187 struct ath9k_htc_priv *priv = hw->priv;
1188 struct ath_common *common = ath9k_hw_common(priv->ah);
1189 struct ieee80211_conf *conf = &hw->conf;
1190
1191 mutex_lock(&priv->mutex);
1192
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301193 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1194 bool enable_radio = false;
1195 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1196
Sujith23367762010-06-01 15:14:16 +05301197 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301198 if (!idle && priv->ps_idle)
1199 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301200 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301201 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301202
1203 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001204 ath_dbg(common, ATH_DBG_CONFIG,
1205 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301206 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1207 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301208 }
1209 }
1210
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301211 /*
1212 * Monitor interface should be added before
1213 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1214 */
1215 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301216 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1217 !priv->ah->is_monitoring)
1218 ath9k_htc_add_monitor_interface(priv);
1219 else if (priv->ah->is_monitoring)
1220 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301221 }
1222
Sujithfb9987d2010-03-17 14:25:25 +05301223 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1224 struct ieee80211_channel *curchan = hw->conf.channel;
1225 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301226
Joe Perches226afe62010-12-02 19:12:37 -08001227 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1228 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301229
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001230 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1231 hw->conf.channel,
1232 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301233
1234 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001235 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301236 mutex_unlock(&priv->mutex);
1237 return -EINVAL;
1238 }
1239
1240 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301241
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301242 if (changed & IEEE80211_CONF_CHANGE_PS) {
1243 if (conf->flags & IEEE80211_CONF_PS) {
1244 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1245 priv->ps_enabled = true;
1246 } else {
1247 priv->ps_enabled = false;
1248 cancel_work_sync(&priv->ps_work);
1249 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1250 }
1251 }
Sujithfb9987d2010-03-17 14:25:25 +05301252
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301253 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1254 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301255 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1256 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301257 }
1258
Sujith23367762010-06-01 15:14:16 +05301259 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1260 mutex_lock(&priv->htc_pm_lock);
1261 if (!priv->ps_idle) {
1262 mutex_unlock(&priv->htc_pm_lock);
1263 goto out;
1264 }
1265 mutex_unlock(&priv->htc_pm_lock);
1266
Joe Perches226afe62010-12-02 19:12:37 -08001267 ath_dbg(common, ATH_DBG_CONFIG,
1268 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301269 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301270 }
1271
Sujith23367762010-06-01 15:14:16 +05301272out:
Sujithfb9987d2010-03-17 14:25:25 +05301273 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301274 return 0;
1275}
1276
1277#define SUPPORTED_FILTERS \
1278 (FIF_PROMISC_IN_BSS | \
1279 FIF_ALLMULTI | \
1280 FIF_CONTROL | \
1281 FIF_PSPOLL | \
1282 FIF_OTHER_BSS | \
1283 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301284 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301285 FIF_FCSFAIL)
1286
1287static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1288 unsigned int changed_flags,
1289 unsigned int *total_flags,
1290 u64 multicast)
1291{
1292 struct ath9k_htc_priv *priv = hw->priv;
1293 u32 rfilt;
1294
1295 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301296 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301297
Sujithfb9987d2010-03-17 14:25:25 +05301298 changed_flags &= SUPPORTED_FILTERS;
1299 *total_flags &= SUPPORTED_FILTERS;
1300
1301 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301302 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301303 ath9k_hw_setrxfilter(priv->ah, rfilt);
1304
Joe Perches226afe62010-12-02 19:12:37 -08001305 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1306 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301307
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301308 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301309 mutex_unlock(&priv->mutex);
1310}
1311
Sujithabd984e2010-05-18 15:26:04 +05301312static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1313 struct ieee80211_vif *vif,
1314 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301315{
1316 struct ath9k_htc_priv *priv = hw->priv;
1317 int ret;
1318
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301319 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301320 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301321 ret = ath9k_htc_add_station(priv, vif, sta);
1322 if (!ret)
1323 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301324 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301325 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301326
1327 return ret;
1328}
1329
1330static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1331 struct ieee80211_vif *vif,
1332 struct ieee80211_sta *sta)
1333{
1334 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301335 struct ath9k_htc_sta *ista;
Sujithabd984e2010-05-18 15:26:04 +05301336 int ret;
1337
1338 mutex_lock(&priv->mutex);
1339 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301340 ista = (struct ath9k_htc_sta *) sta->drv_priv;
1341 htc_sta_drain(priv->htc, ista->index);
Sujithabd984e2010-05-18 15:26:04 +05301342 ret = ath9k_htc_remove_station(priv, vif, sta);
1343 ath9k_htc_ps_restore(priv);
1344 mutex_unlock(&priv->mutex);
1345
1346 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301347}
1348
1349static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1350 const struct ieee80211_tx_queue_params *params)
1351{
1352 struct ath9k_htc_priv *priv = hw->priv;
1353 struct ath_common *common = ath9k_hw_common(priv->ah);
1354 struct ath9k_tx_queue_info qi;
1355 int ret = 0, qnum;
1356
1357 if (queue >= WME_NUM_AC)
1358 return 0;
1359
1360 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301361 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301362
1363 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1364
1365 qi.tqi_aifs = params->aifs;
1366 qi.tqi_cwmin = params->cw_min;
1367 qi.tqi_cwmax = params->cw_max;
1368 qi.tqi_burstTime = params->txop;
1369
1370 qnum = get_hw_qnum(queue, priv->hwq_map);
1371
Joe Perches226afe62010-12-02 19:12:37 -08001372 ath_dbg(common, ATH_DBG_CONFIG,
1373 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1374 queue, qnum, params->aifs, params->cw_min,
1375 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301376
Sujithe1572c52010-03-24 13:42:13 +05301377 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301378 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001379 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301380 goto out;
1381 }
Sujithfb9987d2010-03-17 14:25:25 +05301382
Sujith764580f2010-06-01 15:14:19 +05301383 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001384 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301385 ath9k_htc_beaconq_config(priv);
1386out:
Sujithcb551df2010-06-01 15:14:12 +05301387 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301388 mutex_unlock(&priv->mutex);
1389
1390 return ret;
1391}
1392
1393static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1394 enum set_key_cmd cmd,
1395 struct ieee80211_vif *vif,
1396 struct ieee80211_sta *sta,
1397 struct ieee80211_key_conf *key)
1398{
1399 struct ath9k_htc_priv *priv = hw->priv;
1400 struct ath_common *common = ath9k_hw_common(priv->ah);
1401 int ret = 0;
1402
Sujithe1572c52010-03-24 13:42:13 +05301403 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301404 return -ENOSPC;
1405
1406 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001407 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301408 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301409
1410 switch (cmd) {
1411 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001412 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301413 if (ret >= 0) {
1414 key->hw_key_idx = ret;
1415 /* push IV and Michael MIC generation to stack */
1416 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001417 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301418 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001419 if (priv->ah->sw_mgmt_crypto &&
1420 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301421 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1422 ret = 0;
1423 }
1424 break;
1425 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001426 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301427 break;
1428 default:
1429 ret = -EINVAL;
1430 }
1431
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301432 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301433 mutex_unlock(&priv->mutex);
1434
1435 return ret;
1436}
1437
1438static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1439 struct ieee80211_vif *vif,
1440 struct ieee80211_bss_conf *bss_conf,
1441 u32 changed)
1442{
1443 struct ath9k_htc_priv *priv = hw->priv;
1444 struct ath_hw *ah = priv->ah;
1445 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301446 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301447
1448 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301449 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301450
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301451 /*
1452 * Set the HW AID/BSSID only for the first station interface
1453 * or in IBSS mode.
1454 */
1455 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1456 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1457 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301458
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301459
1460 if (changed & BSS_CHANGED_ASSOC) {
1461 if (set_assoc) {
1462 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1463 bss_conf->assoc);
1464
1465 common->curaid = bss_conf->assoc ?
1466 bss_conf->aid : 0;
1467
1468 if (bss_conf->assoc)
1469 ath9k_htc_start_ani(priv);
1470 else
1471 ath9k_htc_stop_ani(priv);
1472 }
Sujithfb9987d2010-03-17 14:25:25 +05301473 }
1474
1475 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301476 if (set_assoc) {
1477 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1478 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301479
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301480 ath_dbg(common, ATH_DBG_CONFIG,
1481 "BSSID: %pM aid: 0x%x\n",
1482 common->curbssid, common->curaid);
1483 }
Sujithfb9987d2010-03-17 14:25:25 +05301484 }
1485
Sujith Manoharana5fae372011-02-21 07:49:53 +05301486 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1487 ath_dbg(common, ATH_DBG_CONFIG,
1488 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301489 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301490 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301491 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301492 }
1493
Sujith Manoharana5fae372011-02-21 07:49:53 +05301494 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1495 /*
1496 * Disable SWBA interrupt only if there are no
1497 * AP/IBSS interfaces.
1498 */
1499 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1500 ath_dbg(common, ATH_DBG_CONFIG,
1501 "Beacon disabled for BSS: %pM\n",
1502 bss_conf->bssid);
1503 priv->op_flags &= ~OP_ENABLE_BEACON;
1504 ath9k_htc_beacon_config(priv, vif);
1505 }
1506 }
1507
1508 if (changed & BSS_CHANGED_BEACON_INT) {
1509 /*
1510 * Reset the HW TSF for the first AP interface.
1511 */
1512 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1513 (priv->nvifs == 1) &&
1514 (priv->num_ap_vif == 1) &&
1515 (vif->type == NL80211_IFTYPE_AP)) {
1516 priv->op_flags |= OP_TSF_RESET;
1517 }
1518 ath_dbg(common, ATH_DBG_CONFIG,
1519 "Beacon interval changed for BSS: %pM\n",
1520 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301521 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301522 }
1523
Sujithfb9987d2010-03-17 14:25:25 +05301524 if (changed & BSS_CHANGED_ERP_SLOT) {
1525 if (bss_conf->use_short_slot)
1526 ah->slottime = 9;
1527 else
1528 ah->slottime = 20;
1529
1530 ath9k_hw_init_global_settings(ah);
1531 }
1532
Sujith2c76ef82010-05-17 12:01:18 +05301533 if (changed & BSS_CHANGED_HT)
1534 ath9k_htc_update_rate(priv, vif, bss_conf);
1535
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301536 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301537 mutex_unlock(&priv->mutex);
1538}
1539
1540static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1541{
1542 struct ath9k_htc_priv *priv = hw->priv;
1543 u64 tsf;
1544
1545 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301546 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301547 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301548 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301549 mutex_unlock(&priv->mutex);
1550
1551 return tsf;
1552}
1553
1554static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1555{
1556 struct ath9k_htc_priv *priv = hw->priv;
1557
1558 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301559 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301560 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301561 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301562 mutex_unlock(&priv->mutex);
1563}
1564
1565static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1566{
1567 struct ath9k_htc_priv *priv = hw->priv;
1568
1569 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301570 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301571 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301572 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301573 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301574}
1575
1576static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1577 struct ieee80211_vif *vif,
1578 enum ieee80211_ampdu_mlme_action action,
1579 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001580 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301581{
1582 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301583 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301584 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301585
Sujith Manoharan87df8952011-02-21 07:49:08 +05301586 mutex_lock(&priv->mutex);
Sujith Manoharanc58ca5b2011-04-20 11:01:10 +05301587 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan87df8952011-02-21 07:49:08 +05301588
Sujithfb9987d2010-03-17 14:25:25 +05301589 switch (action) {
1590 case IEEE80211_AMPDU_RX_START:
1591 break;
1592 case IEEE80211_AMPDU_RX_STOP:
1593 break;
1594 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301595 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1596 if (!ret)
1597 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1598 break;
Sujithfb9987d2010-03-17 14:25:25 +05301599 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301600 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1601 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301602 break;
1603 case IEEE80211_AMPDU_TX_OPERATIONAL:
1604 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301605 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301606 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301607 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301608 break;
1609 default:
Joe Perches38002762010-12-02 19:12:36 -08001610 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301611 }
1612
Sujith Manoharanc58ca5b2011-04-20 11:01:10 +05301613 ath9k_htc_ps_restore(priv);
Sujith Manoharan87df8952011-02-21 07:49:08 +05301614 mutex_unlock(&priv->mutex);
1615
Sujithd7ca2132010-06-15 10:24:37 +05301616 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301617}
1618
1619static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1620{
1621 struct ath9k_htc_priv *priv = hw->priv;
1622
1623 mutex_lock(&priv->mutex);
1624 spin_lock_bh(&priv->beacon_lock);
1625 priv->op_flags |= OP_SCANNING;
1626 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301627 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301628 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301629 mutex_unlock(&priv->mutex);
1630}
1631
1632static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1633{
1634 struct ath9k_htc_priv *priv = hw->priv;
1635
1636 mutex_lock(&priv->mutex);
1637 spin_lock_bh(&priv->beacon_lock);
1638 priv->op_flags &= ~OP_SCANNING;
1639 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301640 ath9k_htc_ps_wakeup(priv);
1641 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301642 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301643 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301644}
1645
1646static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1647{
1648 return 0;
1649}
1650
1651static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1652 u8 coverage_class)
1653{
1654 struct ath9k_htc_priv *priv = hw->priv;
1655
1656 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301657 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301658 priv->ah->coverage_class = coverage_class;
1659 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301660 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301661 mutex_unlock(&priv->mutex);
1662}
1663
Sujith Manoharane2186b72011-04-27 17:13:40 +05301664/*
1665 * Currently, this is used only for selecting the minimum rate
1666 * for management frames, rate selection for data frames remain
1667 * unaffected.
1668 */
1669static int ath9k_htc_set_bitrate_mask(struct ieee80211_hw *hw,
1670 struct ieee80211_vif *vif,
1671 const struct cfg80211_bitrate_mask *mask)
1672{
1673 struct ath9k_htc_priv *priv = hw->priv;
1674 struct ath_common *common = ath9k_hw_common(priv->ah);
1675 struct ath9k_htc_target_rate_mask tmask;
1676 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1677 int ret = 0;
1678 u8 cmd_rsp;
1679
1680 memset(&tmask, 0, sizeof(struct ath9k_htc_target_rate_mask));
1681
1682 tmask.vif_index = avp->index;
1683 tmask.band = IEEE80211_BAND_2GHZ;
1684 tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_2GHZ].legacy);
1685
1686 WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask);
1687 if (ret) {
1688 ath_err(common,
1689 "Unable to set 2G rate mask for "
1690 "interface at idx: %d\n", avp->index);
1691 goto out;
1692 }
1693
1694 tmask.band = IEEE80211_BAND_5GHZ;
1695 tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_5GHZ].legacy);
1696
1697 WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask);
1698 if (ret) {
1699 ath_err(common,
1700 "Unable to set 5G rate mask for "
1701 "interface at idx: %d\n", avp->index);
1702 goto out;
1703 }
1704
1705 ath_dbg(common, ATH_DBG_CONFIG,
1706 "Set bitrate masks: 0x%x, 0x%x\n",
1707 mask->control[IEEE80211_BAND_2GHZ].legacy,
1708 mask->control[IEEE80211_BAND_5GHZ].legacy);
1709out:
1710 return ret;
1711}
1712
Sujithfb9987d2010-03-17 14:25:25 +05301713struct ieee80211_ops ath9k_htc_ops = {
1714 .tx = ath9k_htc_tx,
1715 .start = ath9k_htc_start,
1716 .stop = ath9k_htc_stop,
1717 .add_interface = ath9k_htc_add_interface,
1718 .remove_interface = ath9k_htc_remove_interface,
1719 .config = ath9k_htc_config,
1720 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301721 .sta_add = ath9k_htc_sta_add,
1722 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301723 .conf_tx = ath9k_htc_conf_tx,
1724 .bss_info_changed = ath9k_htc_bss_info_changed,
1725 .set_key = ath9k_htc_set_key,
1726 .get_tsf = ath9k_htc_get_tsf,
1727 .set_tsf = ath9k_htc_set_tsf,
1728 .reset_tsf = ath9k_htc_reset_tsf,
1729 .ampdu_action = ath9k_htc_ampdu_action,
1730 .sw_scan_start = ath9k_htc_sw_scan_start,
1731 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1732 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1733 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1734 .set_coverage_class = ath9k_htc_set_coverage_class,
Sujith Manoharane2186b72011-04-27 17:13:40 +05301735 .set_bitrate_mask = ath9k_htc_set_bitrate_mask,
Sujithfb9987d2010-03-17 14:25:25 +05301736};