blob: 06101b6bdeac97777813820e87e53c4e350737ce [file] [log] [blame]
Sujithfb9987d2010-03-17 14:25:25 +05301/*
Sujith Manoharan5b681382011-05-17 13:36:18 +05302 * Copyright (c) 2010-2011 Atheros Communications Inc.
Sujithfb9987d2010-03-17 14:25:25 +05303 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "htc.h"
18
Sujithfb9987d2010-03-17 14:25:25 +053019/*************/
20/* Utilities */
21/*************/
22
Sujithfb9987d2010-03-17 14:25:25 +053023/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
24static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
25 struct ath9k_channel *ichan)
26{
27 enum htc_phymode mode;
28
Sujith Manoharanc75197a2011-05-17 12:41:20 +053029 mode = -EINVAL;
Sujithfb9987d2010-03-17 14:25:25 +053030
31 switch (ichan->chanmode) {
32 case CHANNEL_G:
33 case CHANNEL_G_HT20:
34 case CHANNEL_G_HT40PLUS:
35 case CHANNEL_G_HT40MINUS:
36 mode = HTC_MODE_11NG;
37 break;
38 case CHANNEL_A:
39 case CHANNEL_A_HT20:
40 case CHANNEL_A_HT40PLUS:
41 case CHANNEL_A_HT40MINUS:
42 mode = HTC_MODE_11NA;
43 break;
44 default:
45 break;
46 }
47
Sujith Manoharanc75197a2011-05-17 12:41:20 +053048 WARN_ON(mode < 0);
49
Sujithfb9987d2010-03-17 14:25:25 +053050 return mode;
51}
52
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053053bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
54 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053055{
56 bool ret;
57
58 mutex_lock(&priv->htc_pm_lock);
59 ret = ath9k_hw_setpower(priv->ah, mode);
60 mutex_unlock(&priv->htc_pm_lock);
61
62 return ret;
63}
64
65void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
66{
67 mutex_lock(&priv->htc_pm_lock);
68 if (++priv->ps_usecount != 1)
69 goto unlock;
70 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
71
72unlock:
73 mutex_unlock(&priv->htc_pm_lock);
74}
75
76void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
77{
78 mutex_lock(&priv->htc_pm_lock);
79 if (--priv->ps_usecount != 0)
80 goto unlock;
81
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053082 if (priv->ps_idle)
83 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
84 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053085 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053086
Vivek Natarajanbde748a2010-04-05 14:48:05 +053087unlock:
88 mutex_unlock(&priv->htc_pm_lock);
89}
90
91void ath9k_ps_work(struct work_struct *work)
92{
93 struct ath9k_htc_priv *priv =
94 container_of(work, struct ath9k_htc_priv,
95 ps_work);
96 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
97
98 /* The chip wakes up after receiving the first beacon
99 while network sleep is enabled. For the driver to
100 be in sync with the hw, set the chip to awake and
101 only then set it to sleep.
102 */
103 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
104}
105
Sujith Manoharan7c277342011-02-21 07:48:39 +0530106static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
107{
108 struct ath9k_htc_priv *priv = data;
109 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
110
Sujith Manoharana5fae372011-02-21 07:49:53 +0530111 if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
112 priv->reconfig_beacon = true;
113
Sujith Manoharan7c277342011-02-21 07:48:39 +0530114 if (bss_conf->assoc) {
115 priv->rearm_ani = true;
116 priv->reconfig_beacon = true;
117 }
118}
119
120static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
121{
122 priv->rearm_ani = false;
123 priv->reconfig_beacon = false;
124
125 ieee80211_iterate_active_interfaces_atomic(priv->hw,
126 ath9k_htc_vif_iter, priv);
127 if (priv->rearm_ani)
Sujith Manoharana2362542011-02-21 07:49:38 +0530128 ath9k_htc_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530129
130 if (priv->reconfig_beacon) {
131 ath9k_htc_ps_wakeup(priv);
132 ath9k_htc_beacon_reconfig(priv);
133 ath9k_htc_ps_restore(priv);
134 }
135}
136
Sujith Manoharan585895c2011-02-21 07:48:46 +0530137static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
138{
139 struct ath9k_vif_iter_data *iter_data = data;
140 int i;
141
142 for (i = 0; i < ETH_ALEN; i++)
143 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
144}
145
146static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
147 struct ieee80211_vif *vif)
148{
149 struct ath_common *common = ath9k_hw_common(priv->ah);
150 struct ath9k_vif_iter_data iter_data;
151
152 /*
153 * Use the hardware MAC address as reference, the hardware uses it
154 * together with the BSSID mask when matching addresses.
155 */
156 iter_data.hw_macaddr = common->macaddr;
157 memset(&iter_data.mask, 0xff, ETH_ALEN);
158
159 if (vif)
160 ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
161
162 /* Get list of all active MAC addresses */
163 ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
164 &iter_data);
165
166 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
167 ath_hw_setbssidmask(common);
168}
169
Sujith Manoharanffbe7c82011-02-21 07:49:31 +0530170static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
171{
172 if (priv->num_ibss_vif)
173 priv->ah->opmode = NL80211_IFTYPE_ADHOC;
174 else if (priv->num_ap_vif)
175 priv->ah->opmode = NL80211_IFTYPE_AP;
176 else
177 priv->ah->opmode = NL80211_IFTYPE_STATION;
178
179 ath9k_hw_setopmode(priv->ah);
180}
181
Sujith Manoharan73908672010-12-28 14:28:27 +0530182void ath9k_htc_reset(struct ath9k_htc_priv *priv)
183{
184 struct ath_hw *ah = priv->ah;
185 struct ath_common *common = ath9k_hw_common(ah);
186 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530187 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530188 enum htc_phymode mode;
189 __be16 htc_mode;
190 u8 cmd_rsp;
191 int ret;
192
193 mutex_lock(&priv->mutex);
194 ath9k_htc_ps_wakeup(priv);
195
Sujith Manoharana2362542011-02-21 07:49:38 +0530196 ath9k_htc_stop_ani(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530197 ieee80211_stop_queues(priv->hw);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530198
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530199 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530200 ath9k_htc_tx_drain(priv);
201
Sujith Manoharan73908672010-12-28 14:28:27 +0530202 WMI_CMD(WMI_DISABLE_INTR_CMDID);
203 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
204 WMI_CMD(WMI_STOP_RECV_CMDID);
205
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530206 ath9k_wmi_event_drain(priv);
207
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530208 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530209 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
210 if (ret) {
211 ath_err(common,
212 "Unable to reset device (%u Mhz) reset status %d\n",
213 channel->center_freq, ret);
214 }
215
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530216 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
217 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530218
219 WMI_CMD(WMI_START_RECV_CMDID);
220 ath9k_host_rx_init(priv);
221
222 mode = ath9k_htc_get_curmode(priv, ah->curchan);
223 htc_mode = cpu_to_be16(mode);
224 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
225
226 WMI_CMD(WMI_ENABLE_INTR_CMDID);
227 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530228 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530229 ieee80211_wake_queues(priv->hw);
230
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530231 mod_timer(&priv->tx.cleanup_timer,
232 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
233
Sujith Manoharan73908672010-12-28 14:28:27 +0530234 ath9k_htc_ps_restore(priv);
235 mutex_unlock(&priv->mutex);
236}
237
Sujithfb9987d2010-03-17 14:25:25 +0530238static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
239 struct ieee80211_hw *hw,
240 struct ath9k_channel *hchan)
241{
242 struct ath_hw *ah = priv->ah;
243 struct ath_common *common = ath9k_hw_common(ah);
244 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530245 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530246 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530247 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530248 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530249 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530250 u8 cmd_rsp;
251 int ret;
252
253 if (priv->op_flags & OP_INVALID)
254 return -EIO;
255
Sujith Manoharan039a0722010-12-28 14:28:37 +0530256 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530257
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530258 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530259
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530260 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530261 ath9k_htc_tx_drain(priv);
262
Sujithfb9987d2010-03-17 14:25:25 +0530263 WMI_CMD(WMI_DISABLE_INTR_CMDID);
264 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
265 WMI_CMD(WMI_STOP_RECV_CMDID);
266
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530267 ath9k_wmi_event_drain(priv);
268
Joe Perchesd2182b62011-12-15 14:55:53 -0800269 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800270 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
271 priv->ah->curchan->channel,
272 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
273 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530274
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530275 if (!fastcc)
276 caldata = &priv->caldata;
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530277
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200278 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530279 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800280 ath_err(common,
281 "Unable to reset channel (%u Mhz) reset status %d\n",
282 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530283 goto err;
284 }
285
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530286 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
287 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530288
289 WMI_CMD(WMI_START_RECV_CMDID);
290 if (ret)
291 goto err;
292
293 ath9k_host_rx_init(priv);
294
295 mode = ath9k_htc_get_curmode(priv, hchan);
296 htc_mode = cpu_to_be16(mode);
297 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
298 if (ret)
299 goto err;
300
301 WMI_CMD(WMI_ENABLE_INTR_CMDID);
302 if (ret)
303 goto err;
304
305 htc_start(priv->htc);
Sujith Manoharana5fae372011-02-21 07:49:53 +0530306
307 if (!(priv->op_flags & OP_SCANNING) &&
308 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
309 ath9k_htc_vif_reconfig(priv);
310
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530311 mod_timer(&priv->tx.cleanup_timer,
312 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
313
Sujithfb9987d2010-03-17 14:25:25 +0530314err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530315 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530316 return ret;
317}
318
Sujith Manoharana97b4782011-02-21 07:48:00 +0530319/*
320 * Monitor mode handling is a tad complicated because the firmware requires
321 * an interface to be created exclusively, while mac80211 doesn't associate
322 * an interface with the mode.
323 *
324 * So, for now, only one monitor interface can be configured.
325 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530326static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530327{
328 struct ath_common *common = ath9k_hw_common(priv->ah);
329 struct ath9k_htc_target_vif hvif;
330 int ret = 0;
331 u8 cmd_rsp;
332
Sujith Manoharancc721282011-01-03 21:22:18 +0530333 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
334 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530335 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530336 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +0530337 if (ret) {
338 ath_err(common, "Unable to remove monitor interface at idx: %d\n",
339 priv->mon_vif_idx);
340 }
341
Sujith Manoharancc721282011-01-03 21:22:18 +0530342 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530343 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530344}
345
346static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
347{
348 struct ath_common *common = ath9k_hw_common(priv->ah);
349 struct ath9k_htc_target_vif hvif;
350 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530351 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530352 u8 cmd_rsp;
353
Sujith Manoharana97b4782011-02-21 07:48:00 +0530354 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
355 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
356 ret = -ENOBUFS;
357 goto err_vif;
358 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530359
Sujith Manoharana97b4782011-02-21 07:48:00 +0530360 sta_idx = ffz(priv->sta_slot);
361 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
362 ret = -ENOBUFS;
363 goto err_vif;
364 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530365
366 /*
367 * Add an interface.
368 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530369 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
370 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
371
Sujith Manoharane4c62502011-04-13 11:24:43 +0530372 hvif.opmode = HTC_M_MONITOR;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530373 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530374
375 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
376 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530377 goto err_vif;
378
379 /*
380 * Assign the monitor interface index as a special case here.
381 * This is needed when the interface is brought down.
382 */
383 priv->mon_vif_idx = hvif.index;
384 priv->vif_slot |= (1 << hvif.index);
385
386 /*
387 * Set the hardware mode to monitor only if there are no
388 * other interfaces.
389 */
390 if (!priv->nvifs)
391 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530392
393 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530394
395 /*
396 * Associate a station with the interface for packet injection.
397 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530398 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
399
400 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
401
402 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530403 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530404 tsta.vif_index = hvif.index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530405 tsta.maxampdu = cpu_to_be16(0xffff);
Sujith Manoharancc721282011-01-03 21:22:18 +0530406
407 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
408 if (ret) {
409 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530410 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530411 }
412
Sujith Manoharana97b4782011-02-21 07:48:00 +0530413 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530414 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530415 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530416 priv->ah->is_monitoring = true;
417
Joe Perchesd2182b62011-12-15 14:55:53 -0800418 ath_dbg(common, CONFIG,
Sujith Manoharana97b4782011-02-21 07:48:00 +0530419 "Attached a monitor interface at idx: %d, sta idx: %d\n",
420 priv->mon_vif_idx, sta_idx);
421
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530422 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530423
Sujith Manoharana97b4782011-02-21 07:48:00 +0530424err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530425 /*
426 * Remove the interface from the target.
427 */
428 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530429err_vif:
Joe Perchesd2182b62011-12-15 14:55:53 -0800430 ath_dbg(common, FATAL, "Unable to attach a monitor interface\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530431
Sujith Manoharancc721282011-01-03 21:22:18 +0530432 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530433}
434
435static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
436{
437 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530438 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530439 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530440
Sujith Manoharancc721282011-01-03 21:22:18 +0530441 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530442
Sujith Manoharana97b4782011-02-21 07:48:00 +0530443 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530444
445 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
446 if (ret) {
447 ath_err(common, "Unable to remove station entry for monitor mode\n");
448 return ret;
449 }
450
Sujith Manoharana97b4782011-02-21 07:48:00 +0530451 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530452 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530453 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530454
Joe Perchesd2182b62011-12-15 14:55:53 -0800455 ath_dbg(common, CONFIG,
Sujith Manoharana97b4782011-02-21 07:48:00 +0530456 "Removed a monitor interface at idx: %d, sta idx: %d\n",
457 priv->mon_vif_idx, sta_idx);
458
Sujith Manoharancc721282011-01-03 21:22:18 +0530459 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530460}
461
Sujithfb9987d2010-03-17 14:25:25 +0530462static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
463 struct ieee80211_vif *vif,
464 struct ieee80211_sta *sta)
465{
466 struct ath_common *common = ath9k_hw_common(priv->ah);
467 struct ath9k_htc_target_sta tsta;
468 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
469 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530470 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530471 u8 cmd_rsp;
Sujith Manoharanf0dd4982011-04-20 11:01:00 +0530472 u16 maxampdu;
Sujithfb9987d2010-03-17 14:25:25 +0530473
474 if (priv->nstations >= ATH9K_HTC_MAX_STA)
475 return -ENOBUFS;
476
Sujith Manoharana97b4782011-02-21 07:48:00 +0530477 sta_idx = ffz(priv->sta_slot);
478 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
479 return -ENOBUFS;
480
Sujithfb9987d2010-03-17 14:25:25 +0530481 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
482
483 if (sta) {
484 ista = (struct ath9k_htc_sta *) sta->drv_priv;
485 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
486 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
Sujithfb9987d2010-03-17 14:25:25 +0530487 tsta.is_vif_sta = 0;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530488 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530489 } else {
490 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
491 tsta.is_vif_sta = 1;
492 }
493
Sujith Manoharana97b4782011-02-21 07:48:00 +0530494 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530495 tsta.vif_index = avp->index;
Sujith Manoharanf0dd4982011-04-20 11:01:00 +0530496
497 if (!sta) {
498 tsta.maxampdu = cpu_to_be16(0xffff);
499 } else {
500 maxampdu = 1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
501 sta->ht_cap.ampdu_factor);
502 tsta.maxampdu = cpu_to_be16(maxampdu);
503 }
504
Sujithfb9987d2010-03-17 14:25:25 +0530505 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
506 if (ret) {
507 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800508 ath_err(common,
509 "Unable to add station entry for: %pM\n",
510 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530511 return ret;
512 }
513
Sujith Manoharana97b4782011-02-21 07:48:00 +0530514 if (sta) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800515 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800516 "Added a station entry for: %pM (idx: %d)\n",
517 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530518 } else {
Joe Perchesd2182b62011-12-15 14:55:53 -0800519 ath_dbg(common, CONFIG,
Sujith Manoharana97b4782011-02-21 07:48:00 +0530520 "Added a station entry for VIF %d (idx: %d)\n",
521 avp->index, tsta.sta_index);
522 }
Sujithfb9987d2010-03-17 14:25:25 +0530523
Sujith Manoharana97b4782011-02-21 07:48:00 +0530524 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530525 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530526 if (!sta)
527 priv->vif_sta_pos[avp->index] = sta_idx;
528
Sujithfb9987d2010-03-17 14:25:25 +0530529 return 0;
530}
531
532static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
533 struct ieee80211_vif *vif,
534 struct ieee80211_sta *sta)
535{
536 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530537 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530538 struct ath9k_htc_sta *ista;
539 int ret;
540 u8 cmd_rsp, sta_idx;
541
542 if (sta) {
543 ista = (struct ath9k_htc_sta *) sta->drv_priv;
544 sta_idx = ista->index;
545 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530546 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530547 }
548
549 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
550 if (ret) {
551 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800552 ath_err(common,
553 "Unable to remove station entry for: %pM\n",
554 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530555 return ret;
556 }
557
Sujith Manoharana97b4782011-02-21 07:48:00 +0530558 if (sta) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800559 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800560 "Removed a station entry for: %pM (idx: %d)\n",
561 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530562 } else {
Joe Perchesd2182b62011-12-15 14:55:53 -0800563 ath_dbg(common, CONFIG,
Sujith Manoharana97b4782011-02-21 07:48:00 +0530564 "Removed a station entry for VIF %d (idx: %d)\n",
565 avp->index, sta_idx);
566 }
Sujithfb9987d2010-03-17 14:25:25 +0530567
Sujith Manoharana97b4782011-02-21 07:48:00 +0530568 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530569 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530570
Sujithfb9987d2010-03-17 14:25:25 +0530571 return 0;
572}
573
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530574int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv,
575 u8 enable_coex)
Sujithfb9987d2010-03-17 14:25:25 +0530576{
577 struct ath9k_htc_cap_target tcap;
578 int ret;
579 u8 cmd_rsp;
580
581 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
582
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530583 tcap.ampdu_limit = cpu_to_be32(0xffff);
Sujith Manoharanbd548792011-05-17 12:42:14 +0530584 tcap.ampdu_subframes = 0xff;
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530585 tcap.enable_coex = enable_coex;
Sujith29d90752010-06-02 15:53:43 +0530586 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530587
588 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
589
590 return ret;
591}
592
Sujith0d425a72010-05-17 12:01:16 +0530593static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
594 struct ieee80211_sta *sta,
595 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530596{
Sujithfb9987d2010-03-17 14:25:25 +0530597 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
598 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530599 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530600 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530601
Sujithea46e642010-06-02 15:53:50 +0530602 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530603
604 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
605 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530606 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530607 = (sband->bitrates[i].bitrate * 2) / 10;
608 j++;
609 }
610 }
Sujith0d425a72010-05-17 12:01:16 +0530611 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530612
613 if (sta->ht_cap.ht_supported) {
614 for (i = 0, j = 0; i < 77; i++) {
615 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530616 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530617 if (j == ATH_HTC_RATE_MAX)
618 break;
619 }
Sujith0d425a72010-05-17 12:01:16 +0530620 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530621
622 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200623 if (sta->ht_cap.mcs.rx_mask[1])
624 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530625 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
626 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530627 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530628 if (conf_is_ht40(&priv->hw->conf) &&
629 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530630 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530631 else if (conf_is_ht20(&priv->hw->conf) &&
632 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
633 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530634 }
635
Sujith0d425a72010-05-17 12:01:16 +0530636 trate->sta_index = ista->index;
637 trate->isnew = 1;
638 trate->capflags = cpu_to_be32(caps);
639}
Sujithfb9987d2010-03-17 14:25:25 +0530640
Sujith0d425a72010-05-17 12:01:16 +0530641static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
642 struct ath9k_htc_target_rate *trate)
643{
644 struct ath_common *common = ath9k_hw_common(priv->ah);
645 int ret;
646 u8 cmd_rsp;
647
648 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530649 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800650 ath_err(common,
651 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530652 }
653
Sujith0d425a72010-05-17 12:01:16 +0530654 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530655}
656
Sujith0d425a72010-05-17 12:01:16 +0530657static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
658 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530659{
Sujithfb9987d2010-03-17 14:25:25 +0530660 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530661 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530662 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530663
Sujith0d425a72010-05-17 12:01:16 +0530664 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
665 ath9k_htc_setup_rate(priv, sta, &trate);
666 ret = ath9k_htc_send_rate_cmd(priv, &trate);
667 if (!ret)
Joe Perchesd2182b62011-12-15 14:55:53 -0800668 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800669 "Updated target sta: %pM, rate caps: 0x%X\n",
670 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530671}
672
Sujith2c76ef82010-05-17 12:01:18 +0530673static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
674 struct ieee80211_vif *vif,
675 struct ieee80211_bss_conf *bss_conf)
676{
677 struct ath_common *common = ath9k_hw_common(priv->ah);
678 struct ath9k_htc_target_rate trate;
679 struct ieee80211_sta *sta;
680 int ret;
681
682 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
683
684 rcu_read_lock();
685 sta = ieee80211_find_sta(vif, bss_conf->bssid);
686 if (!sta) {
687 rcu_read_unlock();
688 return;
689 }
690 ath9k_htc_setup_rate(priv, sta, &trate);
691 rcu_read_unlock();
692
693 ret = ath9k_htc_send_rate_cmd(priv, &trate);
694 if (!ret)
Joe Perchesd2182b62011-12-15 14:55:53 -0800695 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800696 "Updated target sta: %pM, rate caps: 0x%X\n",
697 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530698}
699
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400700static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
701 struct ieee80211_vif *vif,
702 struct ieee80211_sta *sta,
703 enum ieee80211_ampdu_mlme_action action,
704 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530705{
706 struct ath_common *common = ath9k_hw_common(priv->ah);
707 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200708 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530709 int ret = 0;
710 u8 cmd_rsp;
711
Dan Carpenter0730d112010-05-08 18:24:02 +0200712 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530713 return -EINVAL;
714
Sujithfb9987d2010-03-17 14:25:25 +0530715 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530716 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530717
Sujithef98c3c2010-03-29 16:07:11 +0530718 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530719 aggr.tidno = tid & 0xf;
720 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530721
Sujithfb9987d2010-03-17 14:25:25 +0530722 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
723 if (ret)
Joe Perchesd2182b62011-12-15 14:55:53 -0800724 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800725 "Unable to %s TX aggregation for (%pM, %d)\n",
726 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530727 else
Joe Perchesd2182b62011-12-15 14:55:53 -0800728 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800729 "%s TX aggregation for (%pM, %d)\n",
730 (aggr.aggr_enable) ? "Starting" : "Stopping",
731 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530732
Sujith Manoharan658ef042011-04-13 11:25:00 +0530733 spin_lock_bh(&priv->tx.tx_lock);
Sujithd7ca2132010-06-15 10:24:37 +0530734 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530735 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530736
737 return ret;
738}
739
Sujithfb9987d2010-03-17 14:25:25 +0530740/*******/
741/* ANI */
742/*******/
743
Sujith Manoharana2362542011-02-21 07:49:38 +0530744void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530745{
746 struct ath_common *common = ath9k_hw_common(priv->ah);
747 unsigned long timestamp = jiffies_to_msecs(jiffies);
748
749 common->ani.longcal_timer = timestamp;
750 common->ani.shortcal_timer = timestamp;
751 common->ani.checkani_timer = timestamp;
752
Sujith Manoharana2362542011-02-21 07:49:38 +0530753 priv->op_flags |= OP_ANI_RUNNING;
754
755 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530756 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
757}
758
Sujith Manoharana2362542011-02-21 07:49:38 +0530759void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
760{
761 cancel_delayed_work_sync(&priv->ani_work);
762 priv->op_flags &= ~OP_ANI_RUNNING;
763}
764
765void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530766{
767 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530768 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530769 struct ath_hw *ah = priv->ah;
770 struct ath_common *common = ath9k_hw_common(ah);
771 bool longcal = false;
772 bool shortcal = false;
773 bool aniflag = false;
774 unsigned int timestamp = jiffies_to_msecs(jiffies);
775 u32 cal_interval, short_cal_interval;
776
Sujith Manoharana2362542011-02-21 07:49:38 +0530777 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
778 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530779
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530780 /* Only calibrate if awake */
781 if (ah->power_mode != ATH9K_PM_AWAKE)
782 goto set_timer;
783
Sujithfb9987d2010-03-17 14:25:25 +0530784 /* Long calibration runs independently of short calibration. */
785 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
786 longcal = true;
Joe Perchesd2182b62011-12-15 14:55:53 -0800787 ath_dbg(common, ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530788 common->ani.longcal_timer = timestamp;
789 }
790
791 /* Short calibration applies only while caldone is false */
792 if (!common->ani.caldone) {
793 if ((timestamp - common->ani.shortcal_timer) >=
794 short_cal_interval) {
795 shortcal = true;
Joe Perchesd2182b62011-12-15 14:55:53 -0800796 ath_dbg(common, ANI, "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530797 common->ani.shortcal_timer = timestamp;
798 common->ani.resetcal_timer = timestamp;
799 }
800 } else {
801 if ((timestamp - common->ani.resetcal_timer) >=
802 ATH_RESTART_CALINTERVAL) {
803 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
804 if (common->ani.caldone)
805 common->ani.resetcal_timer = timestamp;
806 }
807 }
808
809 /* Verify whether we must check ANI */
Nikolay Martynov42794252011-12-02 22:39:16 -0500810 if (ah->config.enable_ani &&
811 (timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
Sujithfb9987d2010-03-17 14:25:25 +0530812 aniflag = true;
813 common->ani.checkani_timer = timestamp;
814 }
815
816 /* Skip all processing if there's nothing to do. */
817 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530818
819 ath9k_htc_ps_wakeup(priv);
820
Sujithfb9987d2010-03-17 14:25:25 +0530821 /* Call ANI routine if necessary */
822 if (aniflag)
823 ath9k_hw_ani_monitor(ah, ah->curchan);
824
825 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200826 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530827 common->ani.caldone =
828 ath9k_hw_calibrate(ah, ah->curchan,
Felix Fietkau82b2d332011-09-03 01:40:23 +0200829 ah->rxchainmask, longcal);
Sujithfb9987d2010-03-17 14:25:25 +0530830
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530831 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530832 }
833
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530834set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530835 /*
836 * Set timer interval based on previous results.
837 * The interval must be the shortest necessary to satisfy ANI,
838 * short calibration and long calibration.
839 */
840 cal_interval = ATH_LONG_CALINTERVAL;
Nikolay Martynov42794252011-12-02 22:39:16 -0500841 if (ah->config.enable_ani)
Sujithfb9987d2010-03-17 14:25:25 +0530842 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
843 if (!common->ani.caldone)
844 cal_interval = min(cal_interval, (u32)short_cal_interval);
845
Sujith Manoharana2362542011-02-21 07:49:38 +0530846 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530847 msecs_to_jiffies(cal_interval));
848}
849
Sujithfb9987d2010-03-17 14:25:25 +0530850/**********************/
851/* mac80211 Callbacks */
852/**********************/
853
Johannes Berg7bb45682011-02-24 14:42:06 +0100854static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +0530855{
856 struct ieee80211_hdr *hdr;
857 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530858 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530859 int padpos, padsize, ret, slot;
Sujithfb9987d2010-03-17 14:25:25 +0530860
861 hdr = (struct ieee80211_hdr *) skb->data;
862
863 /* Add the padding after the header if this is not already done */
864 padpos = ath9k_cmn_padpos(hdr->frame_control);
865 padsize = padpos & 3;
866 if (padsize && skb->len > padpos) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530867 if (skb_headroom(skb) < padsize) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800868 ath_dbg(common, XMIT, "No room for padding\n");
Johannes Berg7bb45682011-02-24 14:42:06 +0100869 goto fail_tx;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530870 }
Sujithfb9987d2010-03-17 14:25:25 +0530871 skb_push(skb, padsize);
872 memmove(skb->data, skb->data + padsize, padpos);
873 }
874
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530875 slot = ath9k_htc_tx_get_slot(priv);
876 if (slot < 0) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800877 ath_dbg(common, XMIT, "No free TX slot\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530878 goto fail_tx;
879 }
880
881 ret = ath9k_htc_tx_start(priv, skb, slot, false);
Sujith7757dfe2010-03-29 16:07:17 +0530882 if (ret != 0) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800883 ath_dbg(common, XMIT, "Tx failed\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530884 goto clear_slot;
Sujithfb9987d2010-03-17 14:25:25 +0530885 }
886
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530887 ath9k_htc_check_stop_queues(priv);
888
Johannes Berg7bb45682011-02-24 14:42:06 +0100889 return;
Sujithfb9987d2010-03-17 14:25:25 +0530890
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530891clear_slot:
892 ath9k_htc_tx_clear_slot(priv, slot);
Sujithfb9987d2010-03-17 14:25:25 +0530893fail_tx:
894 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530895}
896
Sujith881ac6a2010-06-01 15:14:11 +0530897static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530898{
899 struct ath9k_htc_priv *priv = hw->priv;
900 struct ath_hw *ah = priv->ah;
901 struct ath_common *common = ath9k_hw_common(ah);
902 struct ieee80211_channel *curchan = hw->conf.channel;
903 struct ath9k_channel *init_channel;
904 int ret = 0;
905 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530906 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530907 u8 cmd_rsp;
908
Sujith881ac6a2010-06-01 15:14:11 +0530909 mutex_lock(&priv->mutex);
910
Joe Perchesd2182b62011-12-15 14:55:53 -0800911 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -0800912 "Starting driver with initial channel: %d MHz\n",
913 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530914
Sujith21d51302010-06-01 15:14:18 +0530915 /* Ensure that HW is awake before flushing RX */
916 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
917 WMI_CMD(WMI_FLUSH_RECV_CMDID);
918
Sujithfb9987d2010-03-17 14:25:25 +0530919 /* setup initial channel */
920 init_channel = ath9k_cmn_get_curchannel(hw, ah);
921
Sujithfb9987d2010-03-17 14:25:25 +0530922 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200923 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530924 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800925 ath_err(common,
926 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
927 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530928 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530929 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530930 }
931
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530932 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
933 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530934
935 mode = ath9k_htc_get_curmode(priv, init_channel);
936 htc_mode = cpu_to_be16(mode);
937 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530938 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530939 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530940
941 ath9k_host_rx_init(priv);
942
Sujith Manoharan3a0593e2011-04-20 14:33:28 +0530943 ret = ath9k_htc_update_cap_target(priv, 0);
Sujith Manoharan1057b752011-02-21 07:48:09 +0530944 if (ret)
Joe Perchesd2182b62011-12-15 14:55:53 -0800945 ath_dbg(common, CONFIG,
Sujith Manoharan1057b752011-02-21 07:48:09 +0530946 "Failed to update capability in target\n");
947
Sujithfb9987d2010-03-17 14:25:25 +0530948 priv->op_flags &= ~OP_INVALID;
949 htc_start(priv->htc);
950
Sujith Manoharan658ef042011-04-13 11:25:00 +0530951 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530952 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530953 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530954
955 ieee80211_wake_queues(hw);
956
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530957 mod_timer(&priv->tx.cleanup_timer,
958 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
959
Felix Fietkau8a309302011-12-17 16:47:56 +0100960 if (ath9k_hw_get_btcoex_scheme(ah) == ATH_BTCOEX_CFG_3WIRE) {
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530961 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
962 AR_STOMP_LOW_WLAN_WGHT);
963 ath9k_hw_btcoex_enable(ah);
964 ath_htc_resume_btcoex_work(priv);
965 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530966 mutex_unlock(&priv->mutex);
967
968 return ret;
969}
970
Sujith881ac6a2010-06-01 15:14:11 +0530971static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530972{
973 struct ath9k_htc_priv *priv = hw->priv;
974 struct ath_hw *ah = priv->ah;
975 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +0530976 int ret __attribute__ ((unused));
Sujithfb9987d2010-03-17 14:25:25 +0530977 u8 cmd_rsp;
978
Sujith881ac6a2010-06-01 15:14:11 +0530979 mutex_lock(&priv->mutex);
980
Sujithfb9987d2010-03-17 14:25:25 +0530981 if (priv->op_flags & OP_INVALID) {
Joe Perchesd2182b62011-12-15 14:55:53 -0800982 ath_dbg(common, ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530983 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530984 return;
985 }
986
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530987 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530988
Sujithfb9987d2010-03-17 14:25:25 +0530989 WMI_CMD(WMI_DISABLE_INTR_CMDID);
990 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
991 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100992
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100993 tasklet_kill(&priv->rx_tasklet);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100994
Sujith Manoharan859c3ca2011-04-13 11:26:39 +0530995 del_timer_sync(&priv->tx.cleanup_timer);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530996 ath9k_htc_tx_drain(priv);
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530997 ath9k_wmi_event_drain(priv);
998
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100999 mutex_unlock(&priv->mutex);
1000
1001 /* Cancel all the running timers/work .. */
1002 cancel_work_sync(&priv->fatal_work);
1003 cancel_work_sync(&priv->ps_work);
Sujith Manoharand244f212011-04-28 16:14:05 +05301004
1005#ifdef CONFIG_MAC80211_LEDS
1006 cancel_work_sync(&priv->led_work);
1007#endif
Sujith Manoharana2362542011-02-21 07:49:38 +05301008 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001009
1010 mutex_lock(&priv->mutex);
1011
Felix Fietkau8a309302011-12-17 16:47:56 +01001012 if (ah->btcoex_hw.enabled &&
1013 ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) {
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301014 ath9k_hw_btcoex_disable(ah);
1015 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1016 ath_htc_cancel_btcoex_work(priv);
1017 }
1018
Sujith Manoharana97b4782011-02-21 07:48:00 +05301019 /* Remove a monitor interface if it's present. */
1020 if (priv->ah->is_monitoring)
1021 ath9k_htc_remove_monitor_interface(priv);
1022
Sujithe9201f02010-06-01 15:14:17 +05301023 ath9k_hw_phy_disable(ah);
1024 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301025 ath9k_htc_ps_restore(priv);
1026 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1027
Sujithfb9987d2010-03-17 14:25:25 +05301028 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301029
Joe Perchesd2182b62011-12-15 14:55:53 -08001030 ath_dbg(common, CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301031 mutex_unlock(&priv->mutex);
1032}
1033
Sujithfb9987d2010-03-17 14:25:25 +05301034static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1035 struct ieee80211_vif *vif)
1036{
1037 struct ath9k_htc_priv *priv = hw->priv;
1038 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1039 struct ath_common *common = ath9k_hw_common(priv->ah);
1040 struct ath9k_htc_target_vif hvif;
1041 int ret = 0;
1042 u8 cmd_rsp;
1043
1044 mutex_lock(&priv->mutex);
1045
Sujith Manoharana97b4782011-02-21 07:48:00 +05301046 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301047 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301048 return -ENOBUFS;
1049 }
1050
1051 if (priv->num_ibss_vif ||
1052 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1053 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1054 mutex_unlock(&priv->mutex);
1055 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301056 }
1057
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301058 if (((vif->type == NL80211_IFTYPE_AP) ||
1059 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1060 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1061 ath_err(common, "Max. number of beaconing interfaces reached\n");
1062 mutex_unlock(&priv->mutex);
1063 return -ENOBUFS;
1064 }
1065
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301066 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301067 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1068 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1069
1070 switch (vif->type) {
1071 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301072 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301073 break;
1074 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301075 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301076 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301077 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301078 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301079 break;
Sujithfb9987d2010-03-17 14:25:25 +05301080 default:
Joe Perches38002762010-12-02 19:12:36 -08001081 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301082 "Interface type %d not yet supported\n", vif->type);
1083 ret = -EOPNOTSUPP;
1084 goto out;
1085 }
1086
Sujithfb9987d2010-03-17 14:25:25 +05301087 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301088 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301089 hvif.rtsthreshold = cpu_to_be16(2304);
1090 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1091 if (ret)
1092 goto out;
1093
Sujithfb9987d2010-03-17 14:25:25 +05301094 /*
1095 * We need a node in target to tx mgmt frames
1096 * before association.
1097 */
1098 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301099 if (ret) {
1100 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301101 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301102 }
Sujithfb9987d2010-03-17 14:25:25 +05301103
Sujith Manoharan585895c2011-02-21 07:48:46 +05301104 ath9k_htc_set_bssid_mask(priv, vif);
1105
Sujith Manoharana97b4782011-02-21 07:48:00 +05301106 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301107 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301108
Sujith Manoharan0df83592011-02-21 07:49:15 +05301109 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301110
1111 if ((vif->type == NL80211_IFTYPE_AP) ||
1112 (vif->type == NL80211_IFTYPE_ADHOC))
1113 ath9k_htc_assign_bslot(priv, vif);
1114
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301115 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301116
Sujith Manoharana2362542011-02-21 07:49:38 +05301117 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301118 !(priv->op_flags & OP_ANI_RUNNING)) {
1119 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301120 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301121 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301122
Joe Perchesd2182b62011-12-15 14:55:53 -08001123 ath_dbg(common, CONFIG, "Attach a VIF of type: %d at idx: %d\n",
1124 vif->type, avp->index);
Sujith Manoharana97b4782011-02-21 07:48:00 +05301125
Sujithfb9987d2010-03-17 14:25:25 +05301126out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301127 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301128 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301129
Sujithfb9987d2010-03-17 14:25:25 +05301130 return ret;
1131}
1132
1133static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1134 struct ieee80211_vif *vif)
1135{
1136 struct ath9k_htc_priv *priv = hw->priv;
1137 struct ath_common *common = ath9k_hw_common(priv->ah);
1138 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1139 struct ath9k_htc_target_vif hvif;
1140 int ret = 0;
1141 u8 cmd_rsp;
1142
Sujithfb9987d2010-03-17 14:25:25 +05301143 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301144 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301145
1146 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1147 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1148 hvif.index = avp->index;
1149 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujith Manoharan0ff2b5c2011-04-20 11:00:34 +05301150 if (ret) {
1151 ath_err(common, "Unable to remove interface at idx: %d\n",
1152 avp->index);
1153 }
Sujithfb9987d2010-03-17 14:25:25 +05301154 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301155 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301156
1157 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301158
Sujith Manoharan0df83592011-02-21 07:49:15 +05301159 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301160
1161 if ((vif->type == NL80211_IFTYPE_AP) ||
1162 (vif->type == NL80211_IFTYPE_ADHOC))
1163 ath9k_htc_remove_bslot(priv, vif);
1164
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301165 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301166
Sujith Manoharandb321242011-05-17 12:41:41 +05301167 ath9k_htc_set_bssid_mask(priv, vif);
1168
Sujith Manoharana2362542011-02-21 07:49:38 +05301169 /*
1170 * Stop ANI only if there are no associated station interfaces.
1171 */
1172 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1173 priv->rearm_ani = false;
1174 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1175 ath9k_htc_vif_iter, priv);
1176 if (!priv->rearm_ani)
1177 ath9k_htc_stop_ani(priv);
1178 }
1179
Joe Perchesd2182b62011-12-15 14:55:53 -08001180 ath_dbg(common, CONFIG, "Detach Interface at idx: %d\n", avp->index);
Sujith Manoharana97b4782011-02-21 07:48:00 +05301181
Sujithcb551df2010-06-01 15:14:12 +05301182 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301183 mutex_unlock(&priv->mutex);
1184}
1185
1186static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1187{
1188 struct ath9k_htc_priv *priv = hw->priv;
1189 struct ath_common *common = ath9k_hw_common(priv->ah);
1190 struct ieee80211_conf *conf = &hw->conf;
1191
1192 mutex_lock(&priv->mutex);
1193
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301194 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1195 bool enable_radio = false;
1196 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1197
Sujith23367762010-06-01 15:14:16 +05301198 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301199 if (!idle && priv->ps_idle)
1200 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301201 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301202 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301203
1204 if (enable_radio) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001205 ath_dbg(common, CONFIG, "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 Perchesd2182b62011-12-15 14:55:53 -08001227 ath_dbg(common, CONFIG, "Set channel: %d MHz\n",
Joe Perches226afe62010-12-02 19:12:37 -08001228 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 Perchesd2182b62011-12-15 14:55:53 -08001267 ath_dbg(common, CONFIG, "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301268 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301269 }
1270
Sujith23367762010-06-01 15:14:16 +05301271out:
Sujithfb9987d2010-03-17 14:25:25 +05301272 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301273 return 0;
1274}
1275
1276#define SUPPORTED_FILTERS \
1277 (FIF_PROMISC_IN_BSS | \
1278 FIF_ALLMULTI | \
1279 FIF_CONTROL | \
1280 FIF_PSPOLL | \
1281 FIF_OTHER_BSS | \
1282 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301283 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301284 FIF_FCSFAIL)
1285
1286static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1287 unsigned int changed_flags,
1288 unsigned int *total_flags,
1289 u64 multicast)
1290{
1291 struct ath9k_htc_priv *priv = hw->priv;
1292 u32 rfilt;
1293
1294 mutex_lock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301295 changed_flags &= SUPPORTED_FILTERS;
1296 *total_flags &= SUPPORTED_FILTERS;
1297
Rajkumar Manoharan565dfef2011-07-07 23:33:38 +05301298 if (priv->op_flags & OP_INVALID) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001299 ath_dbg(ath9k_hw_common(priv->ah), ANY,
Rajkumar Manoharan565dfef2011-07-07 23:33:38 +05301300 "Unable to configure filter on invalid state\n");
Larry Finger1ba45b92011-08-27 13:56:00 -05001301 mutex_unlock(&priv->mutex);
Rajkumar Manoharan565dfef2011-07-07 23:33:38 +05301302 return;
1303 }
1304 ath9k_htc_ps_wakeup(priv);
1305
Sujithfb9987d2010-03-17 14:25:25 +05301306 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301307 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301308 ath9k_hw_setrxfilter(priv->ah, rfilt);
1309
Joe Perchesd2182b62011-12-15 14:55:53 -08001310 ath_dbg(ath9k_hw_common(priv->ah), CONFIG, "Set HW RX filter: 0x%x\n",
1311 rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301312
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301313 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301314 mutex_unlock(&priv->mutex);
1315}
1316
Sujithabd984e2010-05-18 15:26:04 +05301317static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1318 struct ieee80211_vif *vif,
1319 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301320{
1321 struct ath9k_htc_priv *priv = hw->priv;
1322 int ret;
1323
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301324 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301325 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301326 ret = ath9k_htc_add_station(priv, vif, sta);
1327 if (!ret)
1328 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301329 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301330 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301331
1332 return ret;
1333}
1334
1335static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1336 struct ieee80211_vif *vif,
1337 struct ieee80211_sta *sta)
1338{
1339 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301340 struct ath9k_htc_sta *ista;
Sujithabd984e2010-05-18 15:26:04 +05301341 int ret;
1342
1343 mutex_lock(&priv->mutex);
1344 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan84c9e1642011-04-13 11:26:11 +05301345 ista = (struct ath9k_htc_sta *) sta->drv_priv;
1346 htc_sta_drain(priv->htc, ista->index);
Sujithabd984e2010-05-18 15:26:04 +05301347 ret = ath9k_htc_remove_station(priv, vif, sta);
1348 ath9k_htc_ps_restore(priv);
1349 mutex_unlock(&priv->mutex);
1350
1351 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301352}
1353
Eliad Peller8a3a3c82011-10-02 10:15:52 +02001354static int ath9k_htc_conf_tx(struct ieee80211_hw *hw,
1355 struct ieee80211_vif *vif, u16 queue,
Sujithfb9987d2010-03-17 14:25:25 +05301356 const struct ieee80211_tx_queue_params *params)
1357{
1358 struct ath9k_htc_priv *priv = hw->priv;
1359 struct ath_common *common = ath9k_hw_common(priv->ah);
1360 struct ath9k_tx_queue_info qi;
1361 int ret = 0, qnum;
1362
1363 if (queue >= WME_NUM_AC)
1364 return 0;
1365
1366 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301367 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301368
1369 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1370
1371 qi.tqi_aifs = params->aifs;
1372 qi.tqi_cwmin = params->cw_min;
1373 qi.tqi_cwmax = params->cw_max;
1374 qi.tqi_burstTime = params->txop;
1375
1376 qnum = get_hw_qnum(queue, priv->hwq_map);
1377
Joe Perchesd2182b62011-12-15 14:55:53 -08001378 ath_dbg(common, CONFIG,
Joe Perches226afe62010-12-02 19:12:37 -08001379 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1380 queue, qnum, params->aifs, params->cw_min,
1381 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301382
Sujithe1572c52010-03-24 13:42:13 +05301383 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301384 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001385 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301386 goto out;
1387 }
Sujithfb9987d2010-03-17 14:25:25 +05301388
Sujith764580f2010-06-01 15:14:19 +05301389 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001390 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301391 ath9k_htc_beaconq_config(priv);
1392out:
Sujithcb551df2010-06-01 15:14:12 +05301393 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301394 mutex_unlock(&priv->mutex);
1395
1396 return ret;
1397}
1398
1399static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1400 enum set_key_cmd cmd,
1401 struct ieee80211_vif *vif,
1402 struct ieee80211_sta *sta,
1403 struct ieee80211_key_conf *key)
1404{
1405 struct ath9k_htc_priv *priv = hw->priv;
1406 struct ath_common *common = ath9k_hw_common(priv->ah);
1407 int ret = 0;
1408
Sujithe1572c52010-03-24 13:42:13 +05301409 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301410 return -ENOSPC;
1411
Antonio Quartullid7d312c2012-01-17 23:13:30 +01001412 if ((vif->type == NL80211_IFTYPE_ADHOC ||
1413 vif->type == NL80211_IFTYPE_MESH_POINT) &&
1414 (key->cipher == WLAN_CIPHER_SUITE_TKIP ||
1415 key->cipher == WLAN_CIPHER_SUITE_CCMP) &&
1416 !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) {
1417 /*
1418 * For now, disable hw crypto for the RSN IBSS group keys. This
1419 * could be optimized in the future to use a modified key cache
1420 * design to support per-STA RX GTK, but until that gets
1421 * implemented, use of software crypto for group addressed
1422 * frames is a acceptable to allow RSN IBSS to be used.
1423 */
1424 return -EOPNOTSUPP;
1425 }
1426
Sujithfb9987d2010-03-17 14:25:25 +05301427 mutex_lock(&priv->mutex);
Joe Perchesd2182b62011-12-15 14:55:53 -08001428 ath_dbg(common, CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301429 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301430
1431 switch (cmd) {
1432 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001433 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301434 if (ret >= 0) {
1435 key->hw_key_idx = ret;
1436 /* push IV and Michael MIC generation to stack */
1437 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001438 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301439 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001440 if (priv->ah->sw_mgmt_crypto &&
1441 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301442 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1443 ret = 0;
1444 }
1445 break;
1446 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001447 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301448 break;
1449 default:
1450 ret = -EINVAL;
1451 }
1452
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301453 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301454 mutex_unlock(&priv->mutex);
1455
1456 return ret;
1457}
1458
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301459static void ath9k_htc_set_bssid(struct ath9k_htc_priv *priv)
1460{
1461 struct ath_common *common = ath9k_hw_common(priv->ah);
1462
1463 ath9k_hw_write_associd(priv->ah);
Joe Perchesd2182b62011-12-15 14:55:53 -08001464 ath_dbg(common, CONFIG, "BSSID: %pM aid: 0x%x\n",
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301465 common->curbssid, common->curaid);
1466}
1467
1468static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
1469{
1470 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
1471 struct ath_common *common = ath9k_hw_common(priv->ah);
1472 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
1473
1474 if ((vif->type == NL80211_IFTYPE_STATION) && bss_conf->assoc) {
1475 common->curaid = bss_conf->aid;
1476 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1477 }
1478}
1479
1480static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv)
1481{
1482 if (priv->num_sta_assoc_vif == 1) {
1483 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1484 ath9k_htc_bss_iter, priv);
1485 ath9k_htc_set_bssid(priv);
1486 }
1487}
1488
Sujithfb9987d2010-03-17 14:25:25 +05301489static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1490 struct ieee80211_vif *vif,
1491 struct ieee80211_bss_conf *bss_conf,
1492 u32 changed)
1493{
1494 struct ath9k_htc_priv *priv = hw->priv;
1495 struct ath_hw *ah = priv->ah;
1496 struct ath_common *common = ath9k_hw_common(ah);
1497
1498 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301499 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301500
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301501 if (changed & BSS_CHANGED_ASSOC) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001502 ath_dbg(common, CONFIG, "BSS Changed ASSOC %d\n",
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301503 bss_conf->assoc);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301504
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301505 bss_conf->assoc ?
1506 priv->num_sta_assoc_vif++ : priv->num_sta_assoc_vif--;
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301507
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301508 if (priv->ah->opmode == NL80211_IFTYPE_STATION) {
1509 if (bss_conf->assoc && (priv->num_sta_assoc_vif == 1))
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301510 ath9k_htc_start_ani(priv);
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301511 else if (priv->num_sta_assoc_vif == 0)
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301512 ath9k_htc_stop_ani(priv);
1513 }
Sujithfb9987d2010-03-17 14:25:25 +05301514 }
1515
1516 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301517 if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) {
1518 common->curaid = bss_conf->aid;
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301519 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
Sujith Manoharan0cd075d2011-05-17 12:42:03 +05301520 ath9k_htc_set_bssid(priv);
1521 } else if (priv->ah->opmode == NL80211_IFTYPE_STATION) {
1522 ath9k_htc_choose_set_bssid(priv);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301523 }
Sujithfb9987d2010-03-17 14:25:25 +05301524 }
1525
Sujith Manoharana5fae372011-02-21 07:49:53 +05301526 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001527 ath_dbg(common, CONFIG, "Beacon enabled for BSS: %pM\n",
1528 bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301529 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301530 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301531 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301532 }
1533
Sujith Manoharana5fae372011-02-21 07:49:53 +05301534 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1535 /*
1536 * Disable SWBA interrupt only if there are no
1537 * AP/IBSS interfaces.
1538 */
1539 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
Joe Perchesd2182b62011-12-15 14:55:53 -08001540 ath_dbg(common, CONFIG,
Sujith Manoharana5fae372011-02-21 07:49:53 +05301541 "Beacon disabled for BSS: %pM\n",
1542 bss_conf->bssid);
1543 priv->op_flags &= ~OP_ENABLE_BEACON;
1544 ath9k_htc_beacon_config(priv, vif);
1545 }
1546 }
1547
1548 if (changed & BSS_CHANGED_BEACON_INT) {
1549 /*
1550 * Reset the HW TSF for the first AP interface.
1551 */
1552 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1553 (priv->nvifs == 1) &&
1554 (priv->num_ap_vif == 1) &&
1555 (vif->type == NL80211_IFTYPE_AP)) {
1556 priv->op_flags |= OP_TSF_RESET;
1557 }
Joe Perchesd2182b62011-12-15 14:55:53 -08001558 ath_dbg(common, CONFIG,
Sujith Manoharana5fae372011-02-21 07:49:53 +05301559 "Beacon interval changed for BSS: %pM\n",
1560 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301561 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301562 }
1563
Sujithfb9987d2010-03-17 14:25:25 +05301564 if (changed & BSS_CHANGED_ERP_SLOT) {
1565 if (bss_conf->use_short_slot)
1566 ah->slottime = 9;
1567 else
1568 ah->slottime = 20;
1569
1570 ath9k_hw_init_global_settings(ah);
1571 }
1572
Sujith2c76ef82010-05-17 12:01:18 +05301573 if (changed & BSS_CHANGED_HT)
1574 ath9k_htc_update_rate(priv, vif, bss_conf);
1575
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301576 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301577 mutex_unlock(&priv->mutex);
1578}
1579
Eliad Peller37a41b42011-09-21 14:06:11 +03001580static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw,
1581 struct ieee80211_vif *vif)
Sujithfb9987d2010-03-17 14:25:25 +05301582{
1583 struct ath9k_htc_priv *priv = hw->priv;
1584 u64 tsf;
1585
1586 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301587 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301588 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301589 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301590 mutex_unlock(&priv->mutex);
1591
1592 return tsf;
1593}
1594
Eliad Peller37a41b42011-09-21 14:06:11 +03001595static void ath9k_htc_set_tsf(struct ieee80211_hw *hw,
1596 struct ieee80211_vif *vif, u64 tsf)
Sujithfb9987d2010-03-17 14:25:25 +05301597{
1598 struct ath9k_htc_priv *priv = hw->priv;
1599
1600 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301601 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301602 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301603 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301604 mutex_unlock(&priv->mutex);
1605}
1606
Eliad Peller37a41b42011-09-21 14:06:11 +03001607static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw,
1608 struct ieee80211_vif *vif)
Sujithfb9987d2010-03-17 14:25:25 +05301609{
1610 struct ath9k_htc_priv *priv = hw->priv;
1611
1612 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301613 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301614 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301615 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301616 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301617}
1618
1619static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1620 struct ieee80211_vif *vif,
1621 enum ieee80211_ampdu_mlme_action action,
1622 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001623 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301624{
1625 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301626 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301627 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301628
Sujith Manoharan87df8952011-02-21 07:49:08 +05301629 mutex_lock(&priv->mutex);
Sujith Manoharanc58ca5b2011-04-20 11:01:10 +05301630 ath9k_htc_ps_wakeup(priv);
Sujith Manoharan87df8952011-02-21 07:49:08 +05301631
Sujithfb9987d2010-03-17 14:25:25 +05301632 switch (action) {
1633 case IEEE80211_AMPDU_RX_START:
1634 break;
1635 case IEEE80211_AMPDU_RX_STOP:
1636 break;
1637 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301638 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1639 if (!ret)
1640 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1641 break;
Sujithfb9987d2010-03-17 14:25:25 +05301642 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301643 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1644 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301645 break;
1646 case IEEE80211_AMPDU_TX_OPERATIONAL:
1647 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301648 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301649 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301650 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301651 break;
1652 default:
Joe Perches38002762010-12-02 19:12:36 -08001653 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301654 }
1655
Sujith Manoharanc58ca5b2011-04-20 11:01:10 +05301656 ath9k_htc_ps_restore(priv);
Sujith Manoharan87df8952011-02-21 07:49:08 +05301657 mutex_unlock(&priv->mutex);
1658
Sujithd7ca2132010-06-15 10:24:37 +05301659 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301660}
1661
1662static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1663{
1664 struct ath9k_htc_priv *priv = hw->priv;
1665
1666 mutex_lock(&priv->mutex);
1667 spin_lock_bh(&priv->beacon_lock);
1668 priv->op_flags |= OP_SCANNING;
1669 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301670 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301671 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301672 mutex_unlock(&priv->mutex);
1673}
1674
1675static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1676{
1677 struct ath9k_htc_priv *priv = hw->priv;
1678
1679 mutex_lock(&priv->mutex);
1680 spin_lock_bh(&priv->beacon_lock);
1681 priv->op_flags &= ~OP_SCANNING;
1682 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301683 ath9k_htc_ps_wakeup(priv);
1684 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301685 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301686 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301687}
1688
1689static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1690{
1691 return 0;
1692}
1693
1694static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1695 u8 coverage_class)
1696{
1697 struct ath9k_htc_priv *priv = hw->priv;
1698
1699 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301700 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301701 priv->ah->coverage_class = coverage_class;
1702 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301703 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301704 mutex_unlock(&priv->mutex);
1705}
1706
Sujith Manoharane2186b72011-04-27 17:13:40 +05301707/*
1708 * Currently, this is used only for selecting the minimum rate
1709 * for management frames, rate selection for data frames remain
1710 * unaffected.
1711 */
1712static int ath9k_htc_set_bitrate_mask(struct ieee80211_hw *hw,
1713 struct ieee80211_vif *vif,
1714 const struct cfg80211_bitrate_mask *mask)
1715{
1716 struct ath9k_htc_priv *priv = hw->priv;
1717 struct ath_common *common = ath9k_hw_common(priv->ah);
1718 struct ath9k_htc_target_rate_mask tmask;
1719 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1720 int ret = 0;
1721 u8 cmd_rsp;
1722
1723 memset(&tmask, 0, sizeof(struct ath9k_htc_target_rate_mask));
1724
1725 tmask.vif_index = avp->index;
1726 tmask.band = IEEE80211_BAND_2GHZ;
1727 tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_2GHZ].legacy);
1728
1729 WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask);
1730 if (ret) {
1731 ath_err(common,
1732 "Unable to set 2G rate mask for "
1733 "interface at idx: %d\n", avp->index);
1734 goto out;
1735 }
1736
1737 tmask.band = IEEE80211_BAND_5GHZ;
1738 tmask.mask = cpu_to_be32(mask->control[IEEE80211_BAND_5GHZ].legacy);
1739
1740 WMI_CMD_BUF(WMI_BITRATE_MASK_CMDID, &tmask);
1741 if (ret) {
1742 ath_err(common,
1743 "Unable to set 5G rate mask for "
1744 "interface at idx: %d\n", avp->index);
1745 goto out;
1746 }
1747
Joe Perchesd2182b62011-12-15 14:55:53 -08001748 ath_dbg(common, CONFIG, "Set bitrate masks: 0x%x, 0x%x\n",
Sujith Manoharane2186b72011-04-27 17:13:40 +05301749 mask->control[IEEE80211_BAND_2GHZ].legacy,
1750 mask->control[IEEE80211_BAND_5GHZ].legacy);
1751out:
1752 return ret;
1753}
1754
Mohammed Shafi Shajakhan5fa71982011-08-25 01:01:22 +05301755
1756static int ath9k_htc_get_stats(struct ieee80211_hw *hw,
1757 struct ieee80211_low_level_stats *stats)
1758{
1759 struct ath9k_htc_priv *priv = hw->priv;
1760 struct ath_hw *ah = priv->ah;
1761 struct ath9k_mib_stats *mib_stats = &ah->ah_mibStats;
1762
1763 stats->dot11ACKFailureCount = mib_stats->ackrcv_bad;
1764 stats->dot11RTSFailureCount = mib_stats->rts_bad;
1765 stats->dot11FCSErrorCount = mib_stats->fcs_bad;
1766 stats->dot11RTSSuccessCount = mib_stats->rts_good;
1767
1768 return 0;
1769}
1770
Sujithfb9987d2010-03-17 14:25:25 +05301771struct ieee80211_ops ath9k_htc_ops = {
1772 .tx = ath9k_htc_tx,
1773 .start = ath9k_htc_start,
1774 .stop = ath9k_htc_stop,
1775 .add_interface = ath9k_htc_add_interface,
1776 .remove_interface = ath9k_htc_remove_interface,
1777 .config = ath9k_htc_config,
1778 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301779 .sta_add = ath9k_htc_sta_add,
1780 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301781 .conf_tx = ath9k_htc_conf_tx,
1782 .bss_info_changed = ath9k_htc_bss_info_changed,
1783 .set_key = ath9k_htc_set_key,
1784 .get_tsf = ath9k_htc_get_tsf,
1785 .set_tsf = ath9k_htc_set_tsf,
1786 .reset_tsf = ath9k_htc_reset_tsf,
1787 .ampdu_action = ath9k_htc_ampdu_action,
1788 .sw_scan_start = ath9k_htc_sw_scan_start,
1789 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1790 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1791 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1792 .set_coverage_class = ath9k_htc_set_coverage_class,
Sujith Manoharane2186b72011-04-27 17:13:40 +05301793 .set_bitrate_mask = ath9k_htc_set_bitrate_mask,
Mohammed Shafi Shajakhan5fa71982011-08-25 01:01:22 +05301794 .get_stats = ath9k_htc_get_stats,
Sujithfb9987d2010-03-17 14:25:25 +05301795};