blob: 618670d318c5010ca7114d0c4b91a0adb1edf3ea [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
19#ifdef CONFIG_ATH9K_HTC_DEBUGFS
20static struct dentry *ath9k_debugfs_root;
21#endif
22
23/*************/
24/* Utilities */
25/*************/
26
Sujithfb9987d2010-03-17 14:25:25 +053027/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
28static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
29 struct ath9k_channel *ichan)
30{
31 enum htc_phymode mode;
32
33 mode = HTC_MODE_AUTO;
34
35 switch (ichan->chanmode) {
36 case CHANNEL_G:
37 case CHANNEL_G_HT20:
38 case CHANNEL_G_HT40PLUS:
39 case CHANNEL_G_HT40MINUS:
40 mode = HTC_MODE_11NG;
41 break;
42 case CHANNEL_A:
43 case CHANNEL_A_HT20:
44 case CHANNEL_A_HT40PLUS:
45 case CHANNEL_A_HT40MINUS:
46 mode = HTC_MODE_11NA;
47 break;
48 default:
49 break;
50 }
51
52 return mode;
53}
54
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053055bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
56 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053057{
58 bool ret;
59
60 mutex_lock(&priv->htc_pm_lock);
61 ret = ath9k_hw_setpower(priv->ah, mode);
62 mutex_unlock(&priv->htc_pm_lock);
63
64 return ret;
65}
66
67void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
68{
69 mutex_lock(&priv->htc_pm_lock);
70 if (++priv->ps_usecount != 1)
71 goto unlock;
72 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
73
74unlock:
75 mutex_unlock(&priv->htc_pm_lock);
76}
77
78void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
79{
80 mutex_lock(&priv->htc_pm_lock);
81 if (--priv->ps_usecount != 0)
82 goto unlock;
83
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053084 if (priv->ps_idle)
85 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
86 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053087 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053088
Vivek Natarajanbde748a2010-04-05 14:48:05 +053089unlock:
90 mutex_unlock(&priv->htc_pm_lock);
91}
92
93void ath9k_ps_work(struct work_struct *work)
94{
95 struct ath9k_htc_priv *priv =
96 container_of(work, struct ath9k_htc_priv,
97 ps_work);
98 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
99
100 /* The chip wakes up after receiving the first beacon
101 while network sleep is enabled. For the driver to
102 be in sync with the hw, set the chip to awake and
103 only then set it to sleep.
104 */
105 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
106}
107
Sujith Manoharan73908672010-12-28 14:28:27 +0530108void ath9k_htc_reset(struct ath9k_htc_priv *priv)
109{
110 struct ath_hw *ah = priv->ah;
111 struct ath_common *common = ath9k_hw_common(ah);
112 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530113 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530114 enum htc_phymode mode;
115 __be16 htc_mode;
116 u8 cmd_rsp;
117 int ret;
118
119 mutex_lock(&priv->mutex);
120 ath9k_htc_ps_wakeup(priv);
121
122 if (priv->op_flags & OP_ASSOCIATED)
123 cancel_delayed_work_sync(&priv->ath9k_ani_work);
124
125 ieee80211_stop_queues(priv->hw);
126 htc_stop(priv->htc);
127 WMI_CMD(WMI_DISABLE_INTR_CMDID);
128 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
129 WMI_CMD(WMI_STOP_RECV_CMDID);
130
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530131 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530132 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
133 if (ret) {
134 ath_err(common,
135 "Unable to reset device (%u Mhz) reset status %d\n",
136 channel->center_freq, ret);
137 }
138
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530139 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
140 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530141
142 WMI_CMD(WMI_START_RECV_CMDID);
143 ath9k_host_rx_init(priv);
144
145 mode = ath9k_htc_get_curmode(priv, ah->curchan);
146 htc_mode = cpu_to_be16(mode);
147 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
148
149 WMI_CMD(WMI_ENABLE_INTR_CMDID);
150 htc_start(priv->htc);
151
152 if (priv->op_flags & OP_ASSOCIATED) {
153 ath9k_htc_beacon_config(priv, priv->vif);
154 ath_start_ani(priv);
155 }
156
157 ieee80211_wake_queues(priv->hw);
158
159 ath9k_htc_ps_restore(priv);
160 mutex_unlock(&priv->mutex);
161}
162
Sujithfb9987d2010-03-17 14:25:25 +0530163static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
164 struct ieee80211_hw *hw,
165 struct ath9k_channel *hchan)
166{
167 struct ath_hw *ah = priv->ah;
168 struct ath_common *common = ath9k_hw_common(ah);
169 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530170 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530171 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530172 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530173 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530174 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530175 u8 cmd_rsp;
176 int ret;
177
178 if (priv->op_flags & OP_INVALID)
179 return -EIO;
180
Sujith Manoharan039a0722010-12-28 14:28:37 +0530181 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530182
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530183 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530184 htc_stop(priv->htc);
185 WMI_CMD(WMI_DISABLE_INTR_CMDID);
186 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
187 WMI_CMD(WMI_STOP_RECV_CMDID);
188
Joe Perches226afe62010-12-02 19:12:37 -0800189 ath_dbg(common, ATH_DBG_CONFIG,
190 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
191 priv->ah->curchan->channel,
192 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
193 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530194
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530195 if (!fastcc)
196 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200197 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530198 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800199 ath_err(common,
200 "Unable to reset channel (%u Mhz) reset status %d\n",
201 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530202 goto err;
203 }
204
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530205 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
206 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530207
208 WMI_CMD(WMI_START_RECV_CMDID);
209 if (ret)
210 goto err;
211
212 ath9k_host_rx_init(priv);
213
214 mode = ath9k_htc_get_curmode(priv, hchan);
215 htc_mode = cpu_to_be16(mode);
216 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
217 if (ret)
218 goto err;
219
220 WMI_CMD(WMI_ENABLE_INTR_CMDID);
221 if (ret)
222 goto err;
223
224 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530225err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530226 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530227 return ret;
228}
229
Sujith Manoharana97b4782011-02-21 07:48:00 +0530230/*
231 * Monitor mode handling is a tad complicated because the firmware requires
232 * an interface to be created exclusively, while mac80211 doesn't associate
233 * an interface with the mode.
234 *
235 * So, for now, only one monitor interface can be configured.
236 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530237static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530238{
239 struct ath_common *common = ath9k_hw_common(priv->ah);
240 struct ath9k_htc_target_vif hvif;
241 int ret = 0;
242 u8 cmd_rsp;
243
Sujith Manoharancc721282011-01-03 21:22:18 +0530244 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
245 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530246 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530247 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
248 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530249 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530250}
251
252static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
253{
254 struct ath_common *common = ath9k_hw_common(priv->ah);
255 struct ath9k_htc_target_vif hvif;
256 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530257 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530258 u8 cmd_rsp;
259
Sujith Manoharana97b4782011-02-21 07:48:00 +0530260 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
261 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
262 ret = -ENOBUFS;
263 goto err_vif;
264 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530265
Sujith Manoharana97b4782011-02-21 07:48:00 +0530266 sta_idx = ffz(priv->sta_slot);
267 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
268 ret = -ENOBUFS;
269 goto err_vif;
270 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530271
272 /*
273 * Add an interface.
274 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530275 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
276 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
277
278 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530279 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530280
281 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
282 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530283 goto err_vif;
284
285 /*
286 * Assign the monitor interface index as a special case here.
287 * This is needed when the interface is brought down.
288 */
289 priv->mon_vif_idx = hvif.index;
290 priv->vif_slot |= (1 << hvif.index);
291
292 /*
293 * Set the hardware mode to monitor only if there are no
294 * other interfaces.
295 */
296 if (!priv->nvifs)
297 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530298
299 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530300
301 /*
302 * Associate a station with the interface for packet injection.
303 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530304 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
305
306 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
307
308 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530309 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530310 tsta.vif_index = hvif.index;
311 tsta.maxampdu = 0xffff;
312
313 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
314 if (ret) {
315 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530316 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530317 }
318
Sujith Manoharana97b4782011-02-21 07:48:00 +0530319 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530320 priv->nstations++;
321
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530322 /*
323 * Set chainmask etc. on the target.
324 */
325 ret = ath9k_htc_update_cap_target(priv);
326 if (ret)
327 ath_dbg(common, ATH_DBG_CONFIG,
328 "Failed to update capability in target\n");
329
Sujith Manoharana97b4782011-02-21 07:48:00 +0530330 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530331 priv->ah->is_monitoring = true;
332
Sujith Manoharana97b4782011-02-21 07:48:00 +0530333 ath_dbg(common, ATH_DBG_CONFIG,
334 "Attached a monitor interface at idx: %d, sta idx: %d\n",
335 priv->mon_vif_idx, sta_idx);
336
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530337 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530338
Sujith Manoharana97b4782011-02-21 07:48:00 +0530339err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530340 /*
341 * Remove the interface from the target.
342 */
343 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530344err_vif:
345 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
346
Sujith Manoharancc721282011-01-03 21:22:18 +0530347 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530348}
349
350static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
351{
352 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530353 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530354 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530355
Sujith Manoharancc721282011-01-03 21:22:18 +0530356 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530357
Sujith Manoharana97b4782011-02-21 07:48:00 +0530358 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530359
360 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
361 if (ret) {
362 ath_err(common, "Unable to remove station entry for monitor mode\n");
363 return ret;
364 }
365
Sujith Manoharana97b4782011-02-21 07:48:00 +0530366 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530367 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530368 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530369
Sujith Manoharana97b4782011-02-21 07:48:00 +0530370 ath_dbg(common, ATH_DBG_CONFIG,
371 "Removed a monitor interface at idx: %d, sta idx: %d\n",
372 priv->mon_vif_idx, sta_idx);
373
Sujith Manoharancc721282011-01-03 21:22:18 +0530374 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530375}
376
Sujithfb9987d2010-03-17 14:25:25 +0530377static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
378 struct ieee80211_vif *vif,
379 struct ieee80211_sta *sta)
380{
381 struct ath_common *common = ath9k_hw_common(priv->ah);
382 struct ath9k_htc_target_sta tsta;
383 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
384 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530385 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530386 u8 cmd_rsp;
387
388 if (priv->nstations >= ATH9K_HTC_MAX_STA)
389 return -ENOBUFS;
390
Sujith Manoharana97b4782011-02-21 07:48:00 +0530391 sta_idx = ffz(priv->sta_slot);
392 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
393 return -ENOBUFS;
394
Sujithfb9987d2010-03-17 14:25:25 +0530395 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
396
397 if (sta) {
398 ista = (struct ath9k_htc_sta *) sta->drv_priv;
399 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
400 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
401 tsta.associd = common->curaid;
402 tsta.is_vif_sta = 0;
403 tsta.valid = true;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530404 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530405 } else {
406 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
407 tsta.is_vif_sta = 1;
408 }
409
Sujith Manoharana97b4782011-02-21 07:48:00 +0530410 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530411 tsta.vif_index = avp->index;
412 tsta.maxampdu = 0xffff;
413 if (sta && sta->ht_cap.ht_supported)
414 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
415
416 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
417 if (ret) {
418 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800419 ath_err(common,
420 "Unable to add station entry for: %pM\n",
421 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530422 return ret;
423 }
424
Sujith Manoharana97b4782011-02-21 07:48:00 +0530425 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800426 ath_dbg(common, ATH_DBG_CONFIG,
427 "Added a station entry for: %pM (idx: %d)\n",
428 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530429 } else {
430 ath_dbg(common, ATH_DBG_CONFIG,
431 "Added a station entry for VIF %d (idx: %d)\n",
432 avp->index, tsta.sta_index);
433 }
Sujithfb9987d2010-03-17 14:25:25 +0530434
Sujith Manoharana97b4782011-02-21 07:48:00 +0530435 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530436 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530437 if (!sta)
438 priv->vif_sta_pos[avp->index] = sta_idx;
439
Sujithfb9987d2010-03-17 14:25:25 +0530440 return 0;
441}
442
443static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
444 struct ieee80211_vif *vif,
445 struct ieee80211_sta *sta)
446{
447 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530448 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530449 struct ath9k_htc_sta *ista;
450 int ret;
451 u8 cmd_rsp, sta_idx;
452
453 if (sta) {
454 ista = (struct ath9k_htc_sta *) sta->drv_priv;
455 sta_idx = ista->index;
456 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530457 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530458 }
459
460 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
461 if (ret) {
462 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800463 ath_err(common,
464 "Unable to remove station entry for: %pM\n",
465 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530466 return ret;
467 }
468
Sujith Manoharana97b4782011-02-21 07:48:00 +0530469 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800470 ath_dbg(common, ATH_DBG_CONFIG,
471 "Removed a station entry for: %pM (idx: %d)\n",
472 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530473 } else {
474 ath_dbg(common, ATH_DBG_CONFIG,
475 "Removed a station entry for VIF %d (idx: %d)\n",
476 avp->index, sta_idx);
477 }
Sujithfb9987d2010-03-17 14:25:25 +0530478
Sujith Manoharana97b4782011-02-21 07:48:00 +0530479 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530480 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530481
Sujithfb9987d2010-03-17 14:25:25 +0530482 return 0;
483}
484
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530485int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530486{
487 struct ath9k_htc_cap_target tcap;
488 int ret;
489 u8 cmd_rsp;
490
491 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
492
493 /* FIXME: Values are hardcoded */
494 tcap.flags = 0x240c40;
495 tcap.flags_ext = 0x80601000;
496 tcap.ampdu_limit = 0xffff0000;
497 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530498 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530499 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530500 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530501
502 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
503
504 return ret;
505}
506
Sujith0d425a72010-05-17 12:01:16 +0530507static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
508 struct ieee80211_sta *sta,
509 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530510{
Sujithfb9987d2010-03-17 14:25:25 +0530511 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
512 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530513 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530514 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530515
Sujithea46e642010-06-02 15:53:50 +0530516 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530517
518 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
519 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530520 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530521 = (sband->bitrates[i].bitrate * 2) / 10;
522 j++;
523 }
524 }
Sujith0d425a72010-05-17 12:01:16 +0530525 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530526
527 if (sta->ht_cap.ht_supported) {
528 for (i = 0, j = 0; i < 77; i++) {
529 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530530 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530531 if (j == ATH_HTC_RATE_MAX)
532 break;
533 }
Sujith0d425a72010-05-17 12:01:16 +0530534 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530535
536 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200537 if (sta->ht_cap.mcs.rx_mask[1])
538 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530539 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
540 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530541 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530542 if (conf_is_ht40(&priv->hw->conf) &&
543 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530544 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530545 else if (conf_is_ht20(&priv->hw->conf) &&
546 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
547 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530548 }
549
Sujith0d425a72010-05-17 12:01:16 +0530550 trate->sta_index = ista->index;
551 trate->isnew = 1;
552 trate->capflags = cpu_to_be32(caps);
553}
Sujithfb9987d2010-03-17 14:25:25 +0530554
Sujith0d425a72010-05-17 12:01:16 +0530555static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
556 struct ath9k_htc_target_rate *trate)
557{
558 struct ath_common *common = ath9k_hw_common(priv->ah);
559 int ret;
560 u8 cmd_rsp;
561
562 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530563 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800564 ath_err(common,
565 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530566 }
567
Sujith0d425a72010-05-17 12:01:16 +0530568 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530569}
570
Sujith0d425a72010-05-17 12:01:16 +0530571static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
572 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530573{
Sujithfb9987d2010-03-17 14:25:25 +0530574 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530575 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530576 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530577
Sujith0d425a72010-05-17 12:01:16 +0530578 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
579 ath9k_htc_setup_rate(priv, sta, &trate);
580 ret = ath9k_htc_send_rate_cmd(priv, &trate);
581 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800582 ath_dbg(common, ATH_DBG_CONFIG,
583 "Updated target sta: %pM, rate caps: 0x%X\n",
584 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530585}
586
Sujith2c76ef82010-05-17 12:01:18 +0530587static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
588 struct ieee80211_vif *vif,
589 struct ieee80211_bss_conf *bss_conf)
590{
591 struct ath_common *common = ath9k_hw_common(priv->ah);
592 struct ath9k_htc_target_rate trate;
593 struct ieee80211_sta *sta;
594 int ret;
595
596 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
597
598 rcu_read_lock();
599 sta = ieee80211_find_sta(vif, bss_conf->bssid);
600 if (!sta) {
601 rcu_read_unlock();
602 return;
603 }
604 ath9k_htc_setup_rate(priv, sta, &trate);
605 rcu_read_unlock();
606
607 ret = ath9k_htc_send_rate_cmd(priv, &trate);
608 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800609 ath_dbg(common, ATH_DBG_CONFIG,
610 "Updated target sta: %pM, rate caps: 0x%X\n",
611 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530612}
613
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400614static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
615 struct ieee80211_vif *vif,
616 struct ieee80211_sta *sta,
617 enum ieee80211_ampdu_mlme_action action,
618 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530619{
620 struct ath_common *common = ath9k_hw_common(priv->ah);
621 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200622 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530623 int ret = 0;
624 u8 cmd_rsp;
625
Dan Carpenter0730d112010-05-08 18:24:02 +0200626 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530627 return -EINVAL;
628
Sujithfb9987d2010-03-17 14:25:25 +0530629 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530630 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530631
Sujithef98c3c2010-03-29 16:07:11 +0530632 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530633 aggr.tidno = tid & 0xf;
634 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530635
Sujithfb9987d2010-03-17 14:25:25 +0530636 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
637 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800638 ath_dbg(common, ATH_DBG_CONFIG,
639 "Unable to %s TX aggregation for (%pM, %d)\n",
640 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530641 else
Joe Perches226afe62010-12-02 19:12:37 -0800642 ath_dbg(common, ATH_DBG_CONFIG,
643 "%s TX aggregation for (%pM, %d)\n",
644 (aggr.aggr_enable) ? "Starting" : "Stopping",
645 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530646
647 spin_lock_bh(&priv->tx_lock);
648 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
649 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530650
651 return ret;
652}
653
Sujithfb9987d2010-03-17 14:25:25 +0530654/*********/
655/* DEBUG */
656/*********/
657
658#ifdef CONFIG_ATH9K_HTC_DEBUGFS
659
660static int ath9k_debugfs_open(struct inode *inode, struct file *file)
661{
662 file->private_data = inode->i_private;
663 return 0;
664}
665
666static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
667 size_t count, loff_t *ppos)
668{
Joe Perches57674302010-07-12 13:50:06 -0700669 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530670 struct ath9k_htc_target_stats cmd_rsp;
671 char buf[512];
672 unsigned int len = 0;
673 int ret = 0;
674
675 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
676
677 WMI_CMD(WMI_TGT_STATS_CMDID);
678 if (ret)
679 return -EINVAL;
680
681
682 len += snprintf(buf + len, sizeof(buf) - len,
683 "%19s : %10u\n", "TX Short Retries",
684 be32_to_cpu(cmd_rsp.tx_shortretry));
685 len += snprintf(buf + len, sizeof(buf) - len,
686 "%19s : %10u\n", "TX Long Retries",
687 be32_to_cpu(cmd_rsp.tx_longretry));
688 len += snprintf(buf + len, sizeof(buf) - len,
689 "%19s : %10u\n", "TX Xretries",
690 be32_to_cpu(cmd_rsp.tx_xretries));
691 len += snprintf(buf + len, sizeof(buf) - len,
692 "%19s : %10u\n", "TX Unaggr. Xretries",
693 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
694 len += snprintf(buf + len, sizeof(buf) - len,
695 "%19s : %10u\n", "TX Xretries (HT)",
696 be32_to_cpu(cmd_rsp.ht_tx_xretries));
697 len += snprintf(buf + len, sizeof(buf) - len,
698 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
699
Dan Carpenter97460102010-07-22 10:50:28 +0200700 if (len > sizeof(buf))
701 len = sizeof(buf);
702
Sujithfb9987d2010-03-17 14:25:25 +0530703 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
704}
705
706static const struct file_operations fops_tgt_stats = {
707 .read = read_file_tgt_stats,
708 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200709 .owner = THIS_MODULE,
710 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530711};
712
713static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
714 size_t count, loff_t *ppos)
715{
Joe Perches57674302010-07-12 13:50:06 -0700716 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530717 char buf[512];
718 unsigned int len = 0;
719
720 len += snprintf(buf + len, sizeof(buf) - len,
721 "%20s : %10u\n", "Buffers queued",
722 priv->debug.tx_stats.buf_queued);
723 len += snprintf(buf + len, sizeof(buf) - len,
724 "%20s : %10u\n", "Buffers completed",
725 priv->debug.tx_stats.buf_completed);
726 len += snprintf(buf + len, sizeof(buf) - len,
727 "%20s : %10u\n", "SKBs queued",
728 priv->debug.tx_stats.skb_queued);
729 len += snprintf(buf + len, sizeof(buf) - len,
730 "%20s : %10u\n", "SKBs completed",
731 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530732 len += snprintf(buf + len, sizeof(buf) - len,
733 "%20s : %10u\n", "SKBs dropped",
734 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530735
Sujith2edb4582010-05-14 11:18:54 +0530736 len += snprintf(buf + len, sizeof(buf) - len,
737 "%20s : %10u\n", "BE queued",
738 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
739 len += snprintf(buf + len, sizeof(buf) - len,
740 "%20s : %10u\n", "BK queued",
741 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
742 len += snprintf(buf + len, sizeof(buf) - len,
743 "%20s : %10u\n", "VI queued",
744 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
745 len += snprintf(buf + len, sizeof(buf) - len,
746 "%20s : %10u\n", "VO queued",
747 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
748
Dan Carpenter97460102010-07-22 10:50:28 +0200749 if (len > sizeof(buf))
750 len = sizeof(buf);
751
Sujithfb9987d2010-03-17 14:25:25 +0530752 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
753}
754
755static const struct file_operations fops_xmit = {
756 .read = read_file_xmit,
757 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200758 .owner = THIS_MODULE,
759 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530760};
761
762static ssize_t read_file_recv(struct file *file, char __user *user_buf,
763 size_t count, loff_t *ppos)
764{
Joe Perches57674302010-07-12 13:50:06 -0700765 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530766 char buf[512];
767 unsigned int len = 0;
768
769 len += snprintf(buf + len, sizeof(buf) - len,
770 "%20s : %10u\n", "SKBs allocated",
771 priv->debug.rx_stats.skb_allocated);
772 len += snprintf(buf + len, sizeof(buf) - len,
773 "%20s : %10u\n", "SKBs completed",
774 priv->debug.rx_stats.skb_completed);
775 len += snprintf(buf + len, sizeof(buf) - len,
776 "%20s : %10u\n", "SKBs Dropped",
777 priv->debug.rx_stats.skb_dropped);
778
Dan Carpenter97460102010-07-22 10:50:28 +0200779 if (len > sizeof(buf))
780 len = sizeof(buf);
781
Sujithfb9987d2010-03-17 14:25:25 +0530782 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
783}
784
785static const struct file_operations fops_recv = {
786 .read = read_file_recv,
787 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200788 .owner = THIS_MODULE,
789 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530790};
791
Sujithe1572c52010-03-24 13:42:13 +0530792int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530793{
794 struct ath_common *common = ath9k_hw_common(ah);
795 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
796
797 if (!ath9k_debugfs_root)
798 return -ENOENT;
799
800 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
801 ath9k_debugfs_root);
802 if (!priv->debug.debugfs_phy)
803 goto err;
804
805 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
806 priv->debug.debugfs_phy,
807 priv, &fops_tgt_stats);
808 if (!priv->debug.debugfs_tgt_stats)
809 goto err;
810
811
812 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
813 priv->debug.debugfs_phy,
814 priv, &fops_xmit);
815 if (!priv->debug.debugfs_xmit)
816 goto err;
817
818 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
819 priv->debug.debugfs_phy,
820 priv, &fops_recv);
821 if (!priv->debug.debugfs_recv)
822 goto err;
823
824 return 0;
825
826err:
Sujithe1572c52010-03-24 13:42:13 +0530827 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530828 return -ENOMEM;
829}
830
Sujithe1572c52010-03-24 13:42:13 +0530831void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530832{
833 struct ath_common *common = ath9k_hw_common(ah);
834 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
835
836 debugfs_remove(priv->debug.debugfs_recv);
837 debugfs_remove(priv->debug.debugfs_xmit);
838 debugfs_remove(priv->debug.debugfs_tgt_stats);
839 debugfs_remove(priv->debug.debugfs_phy);
840}
841
Sujithe1572c52010-03-24 13:42:13 +0530842int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530843{
844 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
845 if (!ath9k_debugfs_root)
846 return -ENOENT;
847
848 return 0;
849}
850
Sujithe1572c52010-03-24 13:42:13 +0530851void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530852{
853 debugfs_remove(ath9k_debugfs_root);
854 ath9k_debugfs_root = NULL;
855}
856
857#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
858
859/*******/
860/* ANI */
861/*******/
862
Sujith Manoharan73908672010-12-28 14:28:27 +0530863void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530864{
865 struct ath_common *common = ath9k_hw_common(priv->ah);
866 unsigned long timestamp = jiffies_to_msecs(jiffies);
867
868 common->ani.longcal_timer = timestamp;
869 common->ani.shortcal_timer = timestamp;
870 common->ani.checkani_timer = timestamp;
871
872 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
873 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
874}
875
876void ath9k_ani_work(struct work_struct *work)
877{
878 struct ath9k_htc_priv *priv =
879 container_of(work, struct ath9k_htc_priv,
880 ath9k_ani_work.work);
881 struct ath_hw *ah = priv->ah;
882 struct ath_common *common = ath9k_hw_common(ah);
883 bool longcal = false;
884 bool shortcal = false;
885 bool aniflag = false;
886 unsigned int timestamp = jiffies_to_msecs(jiffies);
887 u32 cal_interval, short_cal_interval;
888
889 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
890
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530891 /* Only calibrate if awake */
892 if (ah->power_mode != ATH9K_PM_AWAKE)
893 goto set_timer;
894
Sujithfb9987d2010-03-17 14:25:25 +0530895 /* Long calibration runs independently of short calibration. */
896 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
897 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800898 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530899 common->ani.longcal_timer = timestamp;
900 }
901
902 /* Short calibration applies only while caldone is false */
903 if (!common->ani.caldone) {
904 if ((timestamp - common->ani.shortcal_timer) >=
905 short_cal_interval) {
906 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800907 ath_dbg(common, ATH_DBG_ANI,
908 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530909 common->ani.shortcal_timer = timestamp;
910 common->ani.resetcal_timer = timestamp;
911 }
912 } else {
913 if ((timestamp - common->ani.resetcal_timer) >=
914 ATH_RESTART_CALINTERVAL) {
915 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
916 if (common->ani.caldone)
917 common->ani.resetcal_timer = timestamp;
918 }
919 }
920
921 /* Verify whether we must check ANI */
922 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
923 aniflag = true;
924 common->ani.checkani_timer = timestamp;
925 }
926
927 /* Skip all processing if there's nothing to do. */
928 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530929
930 ath9k_htc_ps_wakeup(priv);
931
Sujithfb9987d2010-03-17 14:25:25 +0530932 /* Call ANI routine if necessary */
933 if (aniflag)
934 ath9k_hw_ani_monitor(ah, ah->curchan);
935
936 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200937 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530938 common->ani.caldone =
939 ath9k_hw_calibrate(ah, ah->curchan,
940 common->rx_chainmask,
941 longcal);
942
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530943 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530944 }
945
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530946set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530947 /*
948 * Set timer interval based on previous results.
949 * The interval must be the shortest necessary to satisfy ANI,
950 * short calibration and long calibration.
951 */
952 cal_interval = ATH_LONG_CALINTERVAL;
953 if (priv->ah->config.enable_ani)
954 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
955 if (!common->ani.caldone)
956 cal_interval = min(cal_interval, (u32)short_cal_interval);
957
958 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
959 msecs_to_jiffies(cal_interval));
960}
961
Sujithfb9987d2010-03-17 14:25:25 +0530962/**********************/
963/* mac80211 Callbacks */
964/**********************/
965
966static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
967{
968 struct ieee80211_hdr *hdr;
969 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +0530970 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +0530971
972 hdr = (struct ieee80211_hdr *) skb->data;
973
974 /* Add the padding after the header if this is not already done */
975 padpos = ath9k_cmn_padpos(hdr->frame_control);
976 padsize = padpos & 3;
977 if (padsize && skb->len > padpos) {
978 if (skb_headroom(skb) < padsize)
979 return -1;
980 skb_push(skb, padsize);
981 memmove(skb->data, skb->data + padsize, padpos);
982 }
983
Sujith7757dfe2010-03-29 16:07:17 +0530984 ret = ath9k_htc_tx_start(priv, skb);
985 if (ret != 0) {
986 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -0800987 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
988 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +0530989 ieee80211_stop_queues(hw);
990 spin_lock_bh(&priv->tx_lock);
991 priv->tx_queues_stop = true;
992 spin_unlock_bh(&priv->tx_lock);
993 } else {
Joe Perches226afe62010-12-02 19:12:37 -0800994 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
995 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +0530996 }
Sujithfb9987d2010-03-17 14:25:25 +0530997 goto fail_tx;
998 }
999
1000 return 0;
1001
1002fail_tx:
1003 dev_kfree_skb_any(skb);
1004 return 0;
1005}
1006
Sujith881ac6a2010-06-01 15:14:11 +05301007static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301008{
1009 struct ath9k_htc_priv *priv = hw->priv;
1010 struct ath_hw *ah = priv->ah;
1011 struct ath_common *common = ath9k_hw_common(ah);
1012 struct ieee80211_channel *curchan = hw->conf.channel;
1013 struct ath9k_channel *init_channel;
1014 int ret = 0;
1015 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301016 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301017 u8 cmd_rsp;
1018
Sujith881ac6a2010-06-01 15:14:11 +05301019 mutex_lock(&priv->mutex);
1020
Joe Perches226afe62010-12-02 19:12:37 -08001021 ath_dbg(common, ATH_DBG_CONFIG,
1022 "Starting driver with initial channel: %d MHz\n",
1023 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301024
Sujith21d51302010-06-01 15:14:18 +05301025 /* Ensure that HW is awake before flushing RX */
1026 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1027 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1028
Sujithfb9987d2010-03-17 14:25:25 +05301029 /* setup initial channel */
1030 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1031
Sujithfb9987d2010-03-17 14:25:25 +05301032 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001033 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301034 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001035 ath_err(common,
1036 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1037 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301038 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301039 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301040 }
1041
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301042 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1043 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +05301044
1045 mode = ath9k_htc_get_curmode(priv, init_channel);
1046 htc_mode = cpu_to_be16(mode);
1047 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301048 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301049 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301050
1051 ath9k_host_rx_init(priv);
1052
1053 priv->op_flags &= ~OP_INVALID;
1054 htc_start(priv->htc);
1055
Sujith7757dfe2010-03-29 16:07:17 +05301056 spin_lock_bh(&priv->tx_lock);
1057 priv->tx_queues_stop = false;
1058 spin_unlock_bh(&priv->tx_lock);
1059
1060 ieee80211_wake_queues(hw);
1061
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301062 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1063 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1064 AR_STOMP_LOW_WLAN_WGHT);
1065 ath9k_hw_btcoex_enable(ah);
1066 ath_htc_resume_btcoex_work(priv);
1067 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301068 mutex_unlock(&priv->mutex);
1069
1070 return ret;
1071}
1072
Sujith881ac6a2010-06-01 15:14:11 +05301073static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301074{
1075 struct ath9k_htc_priv *priv = hw->priv;
1076 struct ath_hw *ah = priv->ah;
1077 struct ath_common *common = ath9k_hw_common(ah);
1078 int ret = 0;
1079 u8 cmd_rsp;
1080
Sujith881ac6a2010-06-01 15:14:11 +05301081 mutex_lock(&priv->mutex);
1082
Sujithfb9987d2010-03-17 14:25:25 +05301083 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001084 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301085 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301086 return;
1087 }
1088
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301089 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301090 htc_stop(priv->htc);
1091 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1092 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1093 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001094
1095 tasklet_kill(&priv->swba_tasklet);
1096 tasklet_kill(&priv->rx_tasklet);
1097 tasklet_kill(&priv->tx_tasklet);
1098
Sujithfb9987d2010-03-17 14:25:25 +05301099 skb_queue_purge(&priv->tx_queue);
1100
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001101 mutex_unlock(&priv->mutex);
1102
1103 /* Cancel all the running timers/work .. */
1104 cancel_work_sync(&priv->fatal_work);
1105 cancel_work_sync(&priv->ps_work);
1106 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Rajkumar Manoharan45655ba2011-01-31 23:47:42 +05301107 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001108 ath9k_led_stop_brightness(priv);
1109
1110 mutex_lock(&priv->mutex);
1111
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301112 if (ah->btcoex_hw.enabled) {
1113 ath9k_hw_btcoex_disable(ah);
1114 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1115 ath_htc_cancel_btcoex_work(priv);
1116 }
1117
Sujith Manoharana97b4782011-02-21 07:48:00 +05301118 /* Remove a monitor interface if it's present. */
1119 if (priv->ah->is_monitoring)
1120 ath9k_htc_remove_monitor_interface(priv);
1121
Sujithe9201f02010-06-01 15:14:17 +05301122 ath9k_hw_phy_disable(ah);
1123 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301124 ath9k_htc_ps_restore(priv);
1125 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1126
Sujithfb9987d2010-03-17 14:25:25 +05301127 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301128
Joe Perches226afe62010-12-02 19:12:37 -08001129 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301130 mutex_unlock(&priv->mutex);
1131}
1132
Sujithfb9987d2010-03-17 14:25:25 +05301133static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1134 struct ieee80211_vif *vif)
1135{
1136 struct ath9k_htc_priv *priv = hw->priv;
1137 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1138 struct ath_common *common = ath9k_hw_common(priv->ah);
1139 struct ath9k_htc_target_vif hvif;
1140 int ret = 0;
1141 u8 cmd_rsp;
1142
1143 mutex_lock(&priv->mutex);
1144
Sujith Manoharana97b4782011-02-21 07:48:00 +05301145 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujithfb9987d2010-03-17 14:25:25 +05301146 ret = -ENOBUFS;
1147 goto out;
1148 }
1149
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301150 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301151 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1152 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1153
1154 switch (vif->type) {
1155 case NL80211_IFTYPE_STATION:
1156 hvif.opmode = cpu_to_be32(HTC_M_STA);
1157 break;
1158 case NL80211_IFTYPE_ADHOC:
1159 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1160 break;
1161 default:
Joe Perches38002762010-12-02 19:12:36 -08001162 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301163 "Interface type %d not yet supported\n", vif->type);
1164 ret = -EOPNOTSUPP;
1165 goto out;
1166 }
1167
Sujithfb9987d2010-03-17 14:25:25 +05301168 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301169 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301170 hvif.rtsthreshold = cpu_to_be16(2304);
1171 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1172 if (ret)
1173 goto out;
1174
1175 priv->nvifs++;
1176
1177 /*
1178 * We need a node in target to tx mgmt frames
1179 * before association.
1180 */
1181 ret = ath9k_htc_add_station(priv, vif, NULL);
1182 if (ret)
1183 goto out;
1184
1185 ret = ath9k_htc_update_cap_target(priv);
1186 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -08001187 ath_dbg(common, ATH_DBG_CONFIG,
1188 "Failed to update capability in target\n");
Sujithfb9987d2010-03-17 14:25:25 +05301189
Sujith Manoharana97b4782011-02-21 07:48:00 +05301190 priv->ah->opmode = vif->type;
1191 priv->vif_slot |= (1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301192 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301193
1194 ath_dbg(common, ATH_DBG_CONFIG,
1195 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1196
Sujithfb9987d2010-03-17 14:25:25 +05301197out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301198 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301199 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301200
Sujithfb9987d2010-03-17 14:25:25 +05301201 return ret;
1202}
1203
1204static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1205 struct ieee80211_vif *vif)
1206{
1207 struct ath9k_htc_priv *priv = hw->priv;
1208 struct ath_common *common = ath9k_hw_common(priv->ah);
1209 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1210 struct ath9k_htc_target_vif hvif;
1211 int ret = 0;
1212 u8 cmd_rsp;
1213
Sujithfb9987d2010-03-17 14:25:25 +05301214 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301215 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301216
1217 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1218 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1219 hvif.index = avp->index;
1220 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1221 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301222 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301223
1224 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301225 priv->vif = NULL;
1226
Sujith Manoharana97b4782011-02-21 07:48:00 +05301227 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1228
Sujithcb551df2010-06-01 15:14:12 +05301229 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301230 mutex_unlock(&priv->mutex);
1231}
1232
1233static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1234{
1235 struct ath9k_htc_priv *priv = hw->priv;
1236 struct ath_common *common = ath9k_hw_common(priv->ah);
1237 struct ieee80211_conf *conf = &hw->conf;
1238
1239 mutex_lock(&priv->mutex);
1240
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301241 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1242 bool enable_radio = false;
1243 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1244
Sujith23367762010-06-01 15:14:16 +05301245 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301246 if (!idle && priv->ps_idle)
1247 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301248 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301249 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301250
1251 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001252 ath_dbg(common, ATH_DBG_CONFIG,
1253 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301254 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1255 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301256 }
1257 }
1258
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301259 /*
1260 * Monitor interface should be added before
1261 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1262 */
1263 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301264 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1265 !priv->ah->is_monitoring)
1266 ath9k_htc_add_monitor_interface(priv);
1267 else if (priv->ah->is_monitoring)
1268 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301269 }
1270
Sujithfb9987d2010-03-17 14:25:25 +05301271 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1272 struct ieee80211_channel *curchan = hw->conf.channel;
1273 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301274
Joe Perches226afe62010-12-02 19:12:37 -08001275 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1276 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301277
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001278 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1279 hw->conf.channel,
1280 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301281
1282 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001283 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301284 mutex_unlock(&priv->mutex);
1285 return -EINVAL;
1286 }
1287
1288 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301289
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301290 if (changed & IEEE80211_CONF_CHANGE_PS) {
1291 if (conf->flags & IEEE80211_CONF_PS) {
1292 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1293 priv->ps_enabled = true;
1294 } else {
1295 priv->ps_enabled = false;
1296 cancel_work_sync(&priv->ps_work);
1297 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1298 }
1299 }
Sujithfb9987d2010-03-17 14:25:25 +05301300
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301301 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1302 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301303 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1304 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301305 }
1306
Sujith23367762010-06-01 15:14:16 +05301307 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1308 mutex_lock(&priv->htc_pm_lock);
1309 if (!priv->ps_idle) {
1310 mutex_unlock(&priv->htc_pm_lock);
1311 goto out;
1312 }
1313 mutex_unlock(&priv->htc_pm_lock);
1314
Joe Perches226afe62010-12-02 19:12:37 -08001315 ath_dbg(common, ATH_DBG_CONFIG,
1316 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301317 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301318 }
1319
Sujith23367762010-06-01 15:14:16 +05301320out:
Sujithfb9987d2010-03-17 14:25:25 +05301321 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301322 return 0;
1323}
1324
1325#define SUPPORTED_FILTERS \
1326 (FIF_PROMISC_IN_BSS | \
1327 FIF_ALLMULTI | \
1328 FIF_CONTROL | \
1329 FIF_PSPOLL | \
1330 FIF_OTHER_BSS | \
1331 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301332 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301333 FIF_FCSFAIL)
1334
1335static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1336 unsigned int changed_flags,
1337 unsigned int *total_flags,
1338 u64 multicast)
1339{
1340 struct ath9k_htc_priv *priv = hw->priv;
1341 u32 rfilt;
1342
1343 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301344 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301345
Sujithfb9987d2010-03-17 14:25:25 +05301346 changed_flags &= SUPPORTED_FILTERS;
1347 *total_flags &= SUPPORTED_FILTERS;
1348
1349 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301350 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301351 ath9k_hw_setrxfilter(priv->ah, rfilt);
1352
Joe Perches226afe62010-12-02 19:12:37 -08001353 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1354 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301355
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301356 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301357 mutex_unlock(&priv->mutex);
1358}
1359
Sujithabd984e2010-05-18 15:26:04 +05301360static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1361 struct ieee80211_vif *vif,
1362 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301363{
1364 struct ath9k_htc_priv *priv = hw->priv;
1365 int ret;
1366
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301367 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301368 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301369 ret = ath9k_htc_add_station(priv, vif, sta);
1370 if (!ret)
1371 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301372 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301373 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301374
1375 return ret;
1376}
1377
1378static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1379 struct ieee80211_vif *vif,
1380 struct ieee80211_sta *sta)
1381{
1382 struct ath9k_htc_priv *priv = hw->priv;
1383 int ret;
1384
1385 mutex_lock(&priv->mutex);
1386 ath9k_htc_ps_wakeup(priv);
1387 ret = ath9k_htc_remove_station(priv, vif, sta);
1388 ath9k_htc_ps_restore(priv);
1389 mutex_unlock(&priv->mutex);
1390
1391 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301392}
1393
1394static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1395 const struct ieee80211_tx_queue_params *params)
1396{
1397 struct ath9k_htc_priv *priv = hw->priv;
1398 struct ath_common *common = ath9k_hw_common(priv->ah);
1399 struct ath9k_tx_queue_info qi;
1400 int ret = 0, qnum;
1401
1402 if (queue >= WME_NUM_AC)
1403 return 0;
1404
1405 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301406 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301407
1408 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1409
1410 qi.tqi_aifs = params->aifs;
1411 qi.tqi_cwmin = params->cw_min;
1412 qi.tqi_cwmax = params->cw_max;
1413 qi.tqi_burstTime = params->txop;
1414
1415 qnum = get_hw_qnum(queue, priv->hwq_map);
1416
Joe Perches226afe62010-12-02 19:12:37 -08001417 ath_dbg(common, ATH_DBG_CONFIG,
1418 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1419 queue, qnum, params->aifs, params->cw_min,
1420 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301421
Sujithe1572c52010-03-24 13:42:13 +05301422 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301423 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001424 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301425 goto out;
1426 }
Sujithfb9987d2010-03-17 14:25:25 +05301427
Sujith764580f2010-06-01 15:14:19 +05301428 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001429 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301430 ath9k_htc_beaconq_config(priv);
1431out:
Sujithcb551df2010-06-01 15:14:12 +05301432 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301433 mutex_unlock(&priv->mutex);
1434
1435 return ret;
1436}
1437
1438static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1439 enum set_key_cmd cmd,
1440 struct ieee80211_vif *vif,
1441 struct ieee80211_sta *sta,
1442 struct ieee80211_key_conf *key)
1443{
1444 struct ath9k_htc_priv *priv = hw->priv;
1445 struct ath_common *common = ath9k_hw_common(priv->ah);
1446 int ret = 0;
1447
Sujithe1572c52010-03-24 13:42:13 +05301448 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301449 return -ENOSPC;
1450
1451 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001452 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301453 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301454
1455 switch (cmd) {
1456 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001457 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301458 if (ret >= 0) {
1459 key->hw_key_idx = ret;
1460 /* push IV and Michael MIC generation to stack */
1461 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001462 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301463 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001464 if (priv->ah->sw_mgmt_crypto &&
1465 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301466 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1467 ret = 0;
1468 }
1469 break;
1470 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001471 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301472 break;
1473 default:
1474 ret = -EINVAL;
1475 }
1476
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301477 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301478 mutex_unlock(&priv->mutex);
1479
1480 return ret;
1481}
1482
1483static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1484 struct ieee80211_vif *vif,
1485 struct ieee80211_bss_conf *bss_conf,
1486 u32 changed)
1487{
1488 struct ath9k_htc_priv *priv = hw->priv;
1489 struct ath_hw *ah = priv->ah;
1490 struct ath_common *common = ath9k_hw_common(ah);
1491
1492 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301493 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301494
1495 if (changed & BSS_CHANGED_ASSOC) {
1496 common->curaid = bss_conf->assoc ?
1497 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001498 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301499 bss_conf->assoc);
1500
1501 if (bss_conf->assoc) {
1502 priv->op_flags |= OP_ASSOCIATED;
1503 ath_start_ani(priv);
1504 } else {
1505 priv->op_flags &= ~OP_ASSOCIATED;
1506 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1507 }
1508 }
1509
1510 if (changed & BSS_CHANGED_BSSID) {
1511 /* Set BSSID */
1512 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1513 ath9k_hw_write_associd(ah);
1514
Joe Perches226afe62010-12-02 19:12:37 -08001515 ath_dbg(common, ATH_DBG_CONFIG,
1516 "BSSID: %pM aid: 0x%x\n",
1517 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301518 }
1519
1520 if ((changed & BSS_CHANGED_BEACON_INT) ||
1521 (changed & BSS_CHANGED_BEACON) ||
1522 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1523 bss_conf->enable_beacon)) {
1524 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301525 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301526 }
1527
Sujithfb9987d2010-03-17 14:25:25 +05301528 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1529 !bss_conf->enable_beacon) {
1530 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
1534 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Joe Perches226afe62010-12-02 19:12:37 -08001535 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1536 bss_conf->use_short_preamble);
Sujithfb9987d2010-03-17 14:25:25 +05301537 if (bss_conf->use_short_preamble)
1538 priv->op_flags |= OP_PREAMBLE_SHORT;
1539 else
1540 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1541 }
1542
1543 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Joe Perches226afe62010-12-02 19:12:37 -08001544 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1545 bss_conf->use_cts_prot);
Sujithfb9987d2010-03-17 14:25:25 +05301546 if (bss_conf->use_cts_prot &&
1547 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1548 priv->op_flags |= OP_PROTECT_ENABLE;
1549 else
1550 priv->op_flags &= ~OP_PROTECT_ENABLE;
1551 }
1552
1553 if (changed & BSS_CHANGED_ERP_SLOT) {
1554 if (bss_conf->use_short_slot)
1555 ah->slottime = 9;
1556 else
1557 ah->slottime = 20;
1558
1559 ath9k_hw_init_global_settings(ah);
1560 }
1561
Sujith2c76ef82010-05-17 12:01:18 +05301562 if (changed & BSS_CHANGED_HT)
1563 ath9k_htc_update_rate(priv, vif, bss_conf);
1564
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301565 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301566 mutex_unlock(&priv->mutex);
1567}
1568
1569static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1570{
1571 struct ath9k_htc_priv *priv = hw->priv;
1572 u64 tsf;
1573
1574 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301575 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301576 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301577 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301578 mutex_unlock(&priv->mutex);
1579
1580 return tsf;
1581}
1582
1583static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1584{
1585 struct ath9k_htc_priv *priv = hw->priv;
1586
1587 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301588 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301589 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301590 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301591 mutex_unlock(&priv->mutex);
1592}
1593
1594static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1595{
1596 struct ath9k_htc_priv *priv = hw->priv;
1597
1598 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301599 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301600 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301601 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301602 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301603}
1604
1605static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1606 struct ieee80211_vif *vif,
1607 enum ieee80211_ampdu_mlme_action action,
1608 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001609 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301610{
1611 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301612 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301613 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301614
1615 switch (action) {
1616 case IEEE80211_AMPDU_RX_START:
1617 break;
1618 case IEEE80211_AMPDU_RX_STOP:
1619 break;
1620 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301621 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1622 if (!ret)
1623 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1624 break;
Sujithfb9987d2010-03-17 14:25:25 +05301625 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301626 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1627 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301628 break;
1629 case IEEE80211_AMPDU_TX_OPERATIONAL:
1630 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301631 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301632 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301633 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301634 break;
1635 default:
Joe Perches38002762010-12-02 19:12:36 -08001636 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301637 }
1638
Sujithd7ca2132010-06-15 10:24:37 +05301639 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301640}
1641
1642static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1643{
1644 struct ath9k_htc_priv *priv = hw->priv;
1645
1646 mutex_lock(&priv->mutex);
1647 spin_lock_bh(&priv->beacon_lock);
1648 priv->op_flags |= OP_SCANNING;
1649 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301650 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301651 if (priv->op_flags & OP_ASSOCIATED)
1652 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301653 mutex_unlock(&priv->mutex);
1654}
1655
1656static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1657{
1658 struct ath9k_htc_priv *priv = hw->priv;
1659
1660 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301661 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301662 spin_lock_bh(&priv->beacon_lock);
1663 priv->op_flags &= ~OP_SCANNING;
1664 spin_unlock_bh(&priv->beacon_lock);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301665 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301666 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301667 ath_start_ani(priv);
1668 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301669 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301670 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301671}
1672
1673static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1674{
1675 return 0;
1676}
1677
1678static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1679 u8 coverage_class)
1680{
1681 struct ath9k_htc_priv *priv = hw->priv;
1682
1683 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301684 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301685 priv->ah->coverage_class = coverage_class;
1686 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301687 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301688 mutex_unlock(&priv->mutex);
1689}
1690
1691struct ieee80211_ops ath9k_htc_ops = {
1692 .tx = ath9k_htc_tx,
1693 .start = ath9k_htc_start,
1694 .stop = ath9k_htc_stop,
1695 .add_interface = ath9k_htc_add_interface,
1696 .remove_interface = ath9k_htc_remove_interface,
1697 .config = ath9k_htc_config,
1698 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301699 .sta_add = ath9k_htc_sta_add,
1700 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301701 .conf_tx = ath9k_htc_conf_tx,
1702 .bss_info_changed = ath9k_htc_bss_info_changed,
1703 .set_key = ath9k_htc_set_key,
1704 .get_tsf = ath9k_htc_get_tsf,
1705 .set_tsf = ath9k_htc_set_tsf,
1706 .reset_tsf = ath9k_htc_reset_tsf,
1707 .ampdu_action = ath9k_htc_ampdu_action,
1708 .sw_scan_start = ath9k_htc_sw_scan_start,
1709 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1710 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1711 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1712 .set_coverage_class = ath9k_htc_set_coverage_class,
1713};