blob: f14f37d29f45fd06ed1490a9a14f8f314b2fe09b [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
Sujith Manoharan1e1f4ad2010-12-28 14:28:52 +053027void ath_update_txpow(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +053028{
29 struct ath_hw *ah = priv->ah;
Sujithfb9987d2010-03-17 14:25:25 +053030
31 if (priv->curtxpow != priv->txpowlimit) {
Felix Fietkaude40f312010-10-20 03:08:53 +020032 ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit, false);
Sujithfb9987d2010-03-17 14:25:25 +053033 /* read back in case value is clamped */
Felix Fietkau9cc32712010-06-12 17:22:29 +020034 priv->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
Sujithfb9987d2010-03-17 14:25:25 +053035 }
36}
37
38/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
39static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
40 struct ath9k_channel *ichan)
41{
42 enum htc_phymode mode;
43
44 mode = HTC_MODE_AUTO;
45
46 switch (ichan->chanmode) {
47 case CHANNEL_G:
48 case CHANNEL_G_HT20:
49 case CHANNEL_G_HT40PLUS:
50 case CHANNEL_G_HT40MINUS:
51 mode = HTC_MODE_11NG;
52 break;
53 case CHANNEL_A:
54 case CHANNEL_A_HT20:
55 case CHANNEL_A_HT40PLUS:
56 case CHANNEL_A_HT40MINUS:
57 mode = HTC_MODE_11NA;
58 break;
59 default:
60 break;
61 }
62
63 return mode;
64}
65
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053066bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
67 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053068{
69 bool ret;
70
71 mutex_lock(&priv->htc_pm_lock);
72 ret = ath9k_hw_setpower(priv->ah, mode);
73 mutex_unlock(&priv->htc_pm_lock);
74
75 return ret;
76}
77
78void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
79{
80 mutex_lock(&priv->htc_pm_lock);
81 if (++priv->ps_usecount != 1)
82 goto unlock;
83 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
84
85unlock:
86 mutex_unlock(&priv->htc_pm_lock);
87}
88
89void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
90{
91 mutex_lock(&priv->htc_pm_lock);
92 if (--priv->ps_usecount != 0)
93 goto unlock;
94
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053095 if (priv->ps_idle)
96 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
97 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053098 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053099
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530100unlock:
101 mutex_unlock(&priv->htc_pm_lock);
102}
103
104void ath9k_ps_work(struct work_struct *work)
105{
106 struct ath9k_htc_priv *priv =
107 container_of(work, struct ath9k_htc_priv,
108 ps_work);
109 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
110
111 /* The chip wakes up after receiving the first beacon
112 while network sleep is enabled. For the driver to
113 be in sync with the hw, set the chip to awake and
114 only then set it to sleep.
115 */
116 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
117}
118
Sujith Manoharan73908672010-12-28 14:28:27 +0530119void ath9k_htc_reset(struct ath9k_htc_priv *priv)
120{
121 struct ath_hw *ah = priv->ah;
122 struct ath_common *common = ath9k_hw_common(ah);
123 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530124 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530125 enum htc_phymode mode;
126 __be16 htc_mode;
127 u8 cmd_rsp;
128 int ret;
129
130 mutex_lock(&priv->mutex);
131 ath9k_htc_ps_wakeup(priv);
132
133 if (priv->op_flags & OP_ASSOCIATED)
134 cancel_delayed_work_sync(&priv->ath9k_ani_work);
135
136 ieee80211_stop_queues(priv->hw);
137 htc_stop(priv->htc);
138 WMI_CMD(WMI_DISABLE_INTR_CMDID);
139 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
140 WMI_CMD(WMI_STOP_RECV_CMDID);
141
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530142 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530143 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
144 if (ret) {
145 ath_err(common,
146 "Unable to reset device (%u Mhz) reset status %d\n",
147 channel->center_freq, ret);
148 }
149
150 ath_update_txpow(priv);
151
152 WMI_CMD(WMI_START_RECV_CMDID);
153 ath9k_host_rx_init(priv);
154
155 mode = ath9k_htc_get_curmode(priv, ah->curchan);
156 htc_mode = cpu_to_be16(mode);
157 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
158
159 WMI_CMD(WMI_ENABLE_INTR_CMDID);
160 htc_start(priv->htc);
161
162 if (priv->op_flags & OP_ASSOCIATED) {
163 ath9k_htc_beacon_config(priv, priv->vif);
164 ath_start_ani(priv);
165 }
166
167 ieee80211_wake_queues(priv->hw);
168
169 ath9k_htc_ps_restore(priv);
170 mutex_unlock(&priv->mutex);
171}
172
Sujithfb9987d2010-03-17 14:25:25 +0530173static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
174 struct ieee80211_hw *hw,
175 struct ath9k_channel *hchan)
176{
177 struct ath_hw *ah = priv->ah;
178 struct ath_common *common = ath9k_hw_common(ah);
179 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530180 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530181 struct ieee80211_channel *channel = hw->conf.channel;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200182 struct ath9k_hw_cal_data *caldata;
Sujithfb9987d2010-03-17 14:25:25 +0530183 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530184 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530185 u8 cmd_rsp;
186 int ret;
187
188 if (priv->op_flags & OP_INVALID)
189 return -EIO;
190
Sujith Manoharan039a0722010-12-28 14:28:37 +0530191 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530192
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530193 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530194 htc_stop(priv->htc);
195 WMI_CMD(WMI_DISABLE_INTR_CMDID);
196 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
197 WMI_CMD(WMI_STOP_RECV_CMDID);
198
Joe Perches226afe62010-12-02 19:12:37 -0800199 ath_dbg(common, ATH_DBG_CONFIG,
200 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
201 priv->ah->curchan->channel,
202 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
203 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530204
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530205 if (!fastcc)
206 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200207 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530208 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800209 ath_err(common,
210 "Unable to reset channel (%u Mhz) reset status %d\n",
211 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530212 goto err;
213 }
214
215 ath_update_txpow(priv);
216
217 WMI_CMD(WMI_START_RECV_CMDID);
218 if (ret)
219 goto err;
220
221 ath9k_host_rx_init(priv);
222
223 mode = ath9k_htc_get_curmode(priv, hchan);
224 htc_mode = cpu_to_be16(mode);
225 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
226 if (ret)
227 goto err;
228
229 WMI_CMD(WMI_ENABLE_INTR_CMDID);
230 if (ret)
231 goto err;
232
233 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530234err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530235 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530236 return ret;
237}
238
Sujith Manoharancc721282011-01-03 21:22:18 +0530239static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530240{
241 struct ath_common *common = ath9k_hw_common(priv->ah);
242 struct ath9k_htc_target_vif hvif;
243 int ret = 0;
244 u8 cmd_rsp;
245
Sujith Manoharancc721282011-01-03 21:22:18 +0530246 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
247 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
248 hvif.index = 0; /* Should do for now */
249 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
250 priv->nvifs--;
251}
252
253static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
254{
255 struct ath_common *common = ath9k_hw_common(priv->ah);
256 struct ath9k_htc_target_vif hvif;
257 struct ath9k_htc_target_sta tsta;
258 int ret = 0;
259 u8 cmd_rsp;
260
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530261 if (priv->nvifs > 0)
262 return -ENOBUFS;
263
Sujith Manoharancc721282011-01-03 21:22:18 +0530264 if (priv->nstations >= ATH9K_HTC_MAX_STA)
265 return -ENOBUFS;
266
267 /*
268 * Add an interface.
269 */
270
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530271 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
272 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
273
274 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
275 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
276 hvif.index = priv->nvifs;
277
278 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
279 if (ret)
280 return ret;
281
282 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530283
284 /*
285 * Associate a station with the interface for packet injection.
286 */
287
288 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
289
290 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
291
292 tsta.is_vif_sta = 1;
293 tsta.sta_index = priv->nstations;
294 tsta.vif_index = hvif.index;
295 tsta.maxampdu = 0xffff;
296
297 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
298 if (ret) {
299 ath_err(common, "Unable to add station entry for monitor mode\n");
300 goto err_vif;
301 }
302
303 priv->nstations++;
304
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530305 /*
306 * Set chainmask etc. on the target.
307 */
308 ret = ath9k_htc_update_cap_target(priv);
309 if (ret)
310 ath_dbg(common, ATH_DBG_CONFIG,
311 "Failed to update capability in target\n");
312
313 priv->ah->is_monitoring = true;
314
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530315 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530316
317err_vif:
318 /*
319 * Remove the interface from the target.
320 */
321 __ath9k_htc_remove_monitor_interface(priv);
322 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530323}
324
325static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
326{
327 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530328 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530329 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530330
Sujith Manoharancc721282011-01-03 21:22:18 +0530331 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530332
Sujith Manoharancc721282011-01-03 21:22:18 +0530333 sta_idx = 0; /* Only single interface, for now */
334
335 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
336 if (ret) {
337 ath_err(common, "Unable to remove station entry for monitor mode\n");
338 return ret;
339 }
340
341 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530342 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530343
344 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530345}
346
Sujithfb9987d2010-03-17 14:25:25 +0530347static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
348 struct ieee80211_vif *vif,
349 struct ieee80211_sta *sta)
350{
351 struct ath_common *common = ath9k_hw_common(priv->ah);
352 struct ath9k_htc_target_sta tsta;
353 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
354 struct ath9k_htc_sta *ista;
355 int ret;
356 u8 cmd_rsp;
357
358 if (priv->nstations >= ATH9K_HTC_MAX_STA)
359 return -ENOBUFS;
360
361 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
362
363 if (sta) {
364 ista = (struct ath9k_htc_sta *) sta->drv_priv;
365 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
366 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
367 tsta.associd = common->curaid;
368 tsta.is_vif_sta = 0;
369 tsta.valid = true;
370 ista->index = priv->nstations;
371 } else {
372 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
373 tsta.is_vif_sta = 1;
374 }
375
376 tsta.sta_index = priv->nstations;
377 tsta.vif_index = avp->index;
378 tsta.maxampdu = 0xffff;
379 if (sta && sta->ht_cap.ht_supported)
380 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
381
382 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
383 if (ret) {
384 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800385 ath_err(common,
386 "Unable to add station entry for: %pM\n",
387 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530388 return ret;
389 }
390
391 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800392 ath_dbg(common, ATH_DBG_CONFIG,
393 "Added a station entry for: %pM (idx: %d)\n",
394 sta->addr, tsta.sta_index);
Sujithfb9987d2010-03-17 14:25:25 +0530395
396 priv->nstations++;
397 return 0;
398}
399
400static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
401 struct ieee80211_vif *vif,
402 struct ieee80211_sta *sta)
403{
404 struct ath_common *common = ath9k_hw_common(priv->ah);
405 struct ath9k_htc_sta *ista;
406 int ret;
407 u8 cmd_rsp, sta_idx;
408
409 if (sta) {
410 ista = (struct ath9k_htc_sta *) sta->drv_priv;
411 sta_idx = ista->index;
412 } else {
413 sta_idx = 0;
414 }
415
416 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
417 if (ret) {
418 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800419 ath_err(common,
420 "Unable to remove station entry for: %pM\n",
421 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530422 return ret;
423 }
424
425 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800426 ath_dbg(common, ATH_DBG_CONFIG,
427 "Removed a station entry for: %pM (idx: %d)\n",
428 sta->addr, sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530429
430 priv->nstations--;
431 return 0;
432}
433
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530434int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530435{
436 struct ath9k_htc_cap_target tcap;
437 int ret;
438 u8 cmd_rsp;
439
440 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
441
442 /* FIXME: Values are hardcoded */
443 tcap.flags = 0x240c40;
444 tcap.flags_ext = 0x80601000;
445 tcap.ampdu_limit = 0xffff0000;
446 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530447 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530448 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530449 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530450
451 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
452
453 return ret;
454}
455
Sujith0d425a72010-05-17 12:01:16 +0530456static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
457 struct ieee80211_sta *sta,
458 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530459{
Sujithfb9987d2010-03-17 14:25:25 +0530460 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
461 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530462 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530463 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530464
Sujithea46e642010-06-02 15:53:50 +0530465 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530466
467 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
468 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530469 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530470 = (sband->bitrates[i].bitrate * 2) / 10;
471 j++;
472 }
473 }
Sujith0d425a72010-05-17 12:01:16 +0530474 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530475
476 if (sta->ht_cap.ht_supported) {
477 for (i = 0, j = 0; i < 77; i++) {
478 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530479 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530480 if (j == ATH_HTC_RATE_MAX)
481 break;
482 }
Sujith0d425a72010-05-17 12:01:16 +0530483 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530484
485 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200486 if (sta->ht_cap.mcs.rx_mask[1])
487 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530488 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
489 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530490 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530491 if (conf_is_ht40(&priv->hw->conf) &&
492 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530493 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530494 else if (conf_is_ht20(&priv->hw->conf) &&
495 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
496 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530497 }
498
Sujith0d425a72010-05-17 12:01:16 +0530499 trate->sta_index = ista->index;
500 trate->isnew = 1;
501 trate->capflags = cpu_to_be32(caps);
502}
Sujithfb9987d2010-03-17 14:25:25 +0530503
Sujith0d425a72010-05-17 12:01:16 +0530504static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
505 struct ath9k_htc_target_rate *trate)
506{
507 struct ath_common *common = ath9k_hw_common(priv->ah);
508 int ret;
509 u8 cmd_rsp;
510
511 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530512 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800513 ath_err(common,
514 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530515 }
516
Sujith0d425a72010-05-17 12:01:16 +0530517 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530518}
519
Sujith0d425a72010-05-17 12:01:16 +0530520static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
521 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530522{
Sujithfb9987d2010-03-17 14:25:25 +0530523 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530524 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530525 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530526
Sujith0d425a72010-05-17 12:01:16 +0530527 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
528 ath9k_htc_setup_rate(priv, sta, &trate);
529 ret = ath9k_htc_send_rate_cmd(priv, &trate);
530 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800531 ath_dbg(common, ATH_DBG_CONFIG,
532 "Updated target sta: %pM, rate caps: 0x%X\n",
533 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530534}
535
Sujith2c76ef82010-05-17 12:01:18 +0530536static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
537 struct ieee80211_vif *vif,
538 struct ieee80211_bss_conf *bss_conf)
539{
540 struct ath_common *common = ath9k_hw_common(priv->ah);
541 struct ath9k_htc_target_rate trate;
542 struct ieee80211_sta *sta;
543 int ret;
544
545 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
546
547 rcu_read_lock();
548 sta = ieee80211_find_sta(vif, bss_conf->bssid);
549 if (!sta) {
550 rcu_read_unlock();
551 return;
552 }
553 ath9k_htc_setup_rate(priv, sta, &trate);
554 rcu_read_unlock();
555
556 ret = ath9k_htc_send_rate_cmd(priv, &trate);
557 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800558 ath_dbg(common, ATH_DBG_CONFIG,
559 "Updated target sta: %pM, rate caps: 0x%X\n",
560 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530561}
562
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400563static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
564 struct ieee80211_vif *vif,
565 struct ieee80211_sta *sta,
566 enum ieee80211_ampdu_mlme_action action,
567 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530568{
569 struct ath_common *common = ath9k_hw_common(priv->ah);
570 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200571 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530572 int ret = 0;
573 u8 cmd_rsp;
574
Dan Carpenter0730d112010-05-08 18:24:02 +0200575 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530576 return -EINVAL;
577
Sujithfb9987d2010-03-17 14:25:25 +0530578 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530579 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530580
Sujithef98c3c2010-03-29 16:07:11 +0530581 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530582 aggr.tidno = tid & 0xf;
583 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530584
Sujithfb9987d2010-03-17 14:25:25 +0530585 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
586 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800587 ath_dbg(common, ATH_DBG_CONFIG,
588 "Unable to %s TX aggregation for (%pM, %d)\n",
589 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530590 else
Joe Perches226afe62010-12-02 19:12:37 -0800591 ath_dbg(common, ATH_DBG_CONFIG,
592 "%s TX aggregation for (%pM, %d)\n",
593 (aggr.aggr_enable) ? "Starting" : "Stopping",
594 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530595
596 spin_lock_bh(&priv->tx_lock);
597 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
598 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530599
600 return ret;
601}
602
Sujithfb9987d2010-03-17 14:25:25 +0530603/*********/
604/* DEBUG */
605/*********/
606
607#ifdef CONFIG_ATH9K_HTC_DEBUGFS
608
609static int ath9k_debugfs_open(struct inode *inode, struct file *file)
610{
611 file->private_data = inode->i_private;
612 return 0;
613}
614
615static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
616 size_t count, loff_t *ppos)
617{
Joe Perches57674302010-07-12 13:50:06 -0700618 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530619 struct ath9k_htc_target_stats cmd_rsp;
620 char buf[512];
621 unsigned int len = 0;
622 int ret = 0;
623
624 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
625
626 WMI_CMD(WMI_TGT_STATS_CMDID);
627 if (ret)
628 return -EINVAL;
629
630
631 len += snprintf(buf + len, sizeof(buf) - len,
632 "%19s : %10u\n", "TX Short Retries",
633 be32_to_cpu(cmd_rsp.tx_shortretry));
634 len += snprintf(buf + len, sizeof(buf) - len,
635 "%19s : %10u\n", "TX Long Retries",
636 be32_to_cpu(cmd_rsp.tx_longretry));
637 len += snprintf(buf + len, sizeof(buf) - len,
638 "%19s : %10u\n", "TX Xretries",
639 be32_to_cpu(cmd_rsp.tx_xretries));
640 len += snprintf(buf + len, sizeof(buf) - len,
641 "%19s : %10u\n", "TX Unaggr. Xretries",
642 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
643 len += snprintf(buf + len, sizeof(buf) - len,
644 "%19s : %10u\n", "TX Xretries (HT)",
645 be32_to_cpu(cmd_rsp.ht_tx_xretries));
646 len += snprintf(buf + len, sizeof(buf) - len,
647 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
648
Dan Carpenter97460102010-07-22 10:50:28 +0200649 if (len > sizeof(buf))
650 len = sizeof(buf);
651
Sujithfb9987d2010-03-17 14:25:25 +0530652 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
653}
654
655static const struct file_operations fops_tgt_stats = {
656 .read = read_file_tgt_stats,
657 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200658 .owner = THIS_MODULE,
659 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530660};
661
662static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
663 size_t count, loff_t *ppos)
664{
Joe Perches57674302010-07-12 13:50:06 -0700665 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530666 char buf[512];
667 unsigned int len = 0;
668
669 len += snprintf(buf + len, sizeof(buf) - len,
670 "%20s : %10u\n", "Buffers queued",
671 priv->debug.tx_stats.buf_queued);
672 len += snprintf(buf + len, sizeof(buf) - len,
673 "%20s : %10u\n", "Buffers completed",
674 priv->debug.tx_stats.buf_completed);
675 len += snprintf(buf + len, sizeof(buf) - len,
676 "%20s : %10u\n", "SKBs queued",
677 priv->debug.tx_stats.skb_queued);
678 len += snprintf(buf + len, sizeof(buf) - len,
679 "%20s : %10u\n", "SKBs completed",
680 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530681 len += snprintf(buf + len, sizeof(buf) - len,
682 "%20s : %10u\n", "SKBs dropped",
683 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530684
Sujith2edb4582010-05-14 11:18:54 +0530685 len += snprintf(buf + len, sizeof(buf) - len,
686 "%20s : %10u\n", "BE queued",
687 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
688 len += snprintf(buf + len, sizeof(buf) - len,
689 "%20s : %10u\n", "BK queued",
690 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
691 len += snprintf(buf + len, sizeof(buf) - len,
692 "%20s : %10u\n", "VI queued",
693 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
694 len += snprintf(buf + len, sizeof(buf) - len,
695 "%20s : %10u\n", "VO queued",
696 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
697
Dan Carpenter97460102010-07-22 10:50:28 +0200698 if (len > sizeof(buf))
699 len = sizeof(buf);
700
Sujithfb9987d2010-03-17 14:25:25 +0530701 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
702}
703
704static const struct file_operations fops_xmit = {
705 .read = read_file_xmit,
706 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200707 .owner = THIS_MODULE,
708 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530709};
710
711static ssize_t read_file_recv(struct file *file, char __user *user_buf,
712 size_t count, loff_t *ppos)
713{
Joe Perches57674302010-07-12 13:50:06 -0700714 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530715 char buf[512];
716 unsigned int len = 0;
717
718 len += snprintf(buf + len, sizeof(buf) - len,
719 "%20s : %10u\n", "SKBs allocated",
720 priv->debug.rx_stats.skb_allocated);
721 len += snprintf(buf + len, sizeof(buf) - len,
722 "%20s : %10u\n", "SKBs completed",
723 priv->debug.rx_stats.skb_completed);
724 len += snprintf(buf + len, sizeof(buf) - len,
725 "%20s : %10u\n", "SKBs Dropped",
726 priv->debug.rx_stats.skb_dropped);
727
Dan Carpenter97460102010-07-22 10:50:28 +0200728 if (len > sizeof(buf))
729 len = sizeof(buf);
730
Sujithfb9987d2010-03-17 14:25:25 +0530731 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
732}
733
734static const struct file_operations fops_recv = {
735 .read = read_file_recv,
736 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200737 .owner = THIS_MODULE,
738 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530739};
740
Sujithe1572c52010-03-24 13:42:13 +0530741int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530742{
743 struct ath_common *common = ath9k_hw_common(ah);
744 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
745
746 if (!ath9k_debugfs_root)
747 return -ENOENT;
748
749 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
750 ath9k_debugfs_root);
751 if (!priv->debug.debugfs_phy)
752 goto err;
753
754 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
755 priv->debug.debugfs_phy,
756 priv, &fops_tgt_stats);
757 if (!priv->debug.debugfs_tgt_stats)
758 goto err;
759
760
761 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
762 priv->debug.debugfs_phy,
763 priv, &fops_xmit);
764 if (!priv->debug.debugfs_xmit)
765 goto err;
766
767 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
768 priv->debug.debugfs_phy,
769 priv, &fops_recv);
770 if (!priv->debug.debugfs_recv)
771 goto err;
772
773 return 0;
774
775err:
Sujithe1572c52010-03-24 13:42:13 +0530776 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530777 return -ENOMEM;
778}
779
Sujithe1572c52010-03-24 13:42:13 +0530780void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530781{
782 struct ath_common *common = ath9k_hw_common(ah);
783 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
784
785 debugfs_remove(priv->debug.debugfs_recv);
786 debugfs_remove(priv->debug.debugfs_xmit);
787 debugfs_remove(priv->debug.debugfs_tgt_stats);
788 debugfs_remove(priv->debug.debugfs_phy);
789}
790
Sujithe1572c52010-03-24 13:42:13 +0530791int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530792{
793 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
794 if (!ath9k_debugfs_root)
795 return -ENOENT;
796
797 return 0;
798}
799
Sujithe1572c52010-03-24 13:42:13 +0530800void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530801{
802 debugfs_remove(ath9k_debugfs_root);
803 ath9k_debugfs_root = NULL;
804}
805
806#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
807
808/*******/
809/* ANI */
810/*******/
811
Sujith Manoharan73908672010-12-28 14:28:27 +0530812void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530813{
814 struct ath_common *common = ath9k_hw_common(priv->ah);
815 unsigned long timestamp = jiffies_to_msecs(jiffies);
816
817 common->ani.longcal_timer = timestamp;
818 common->ani.shortcal_timer = timestamp;
819 common->ani.checkani_timer = timestamp;
820
821 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
822 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
823}
824
825void ath9k_ani_work(struct work_struct *work)
826{
827 struct ath9k_htc_priv *priv =
828 container_of(work, struct ath9k_htc_priv,
829 ath9k_ani_work.work);
830 struct ath_hw *ah = priv->ah;
831 struct ath_common *common = ath9k_hw_common(ah);
832 bool longcal = false;
833 bool shortcal = false;
834 bool aniflag = false;
835 unsigned int timestamp = jiffies_to_msecs(jiffies);
836 u32 cal_interval, short_cal_interval;
837
838 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
839
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530840 /* Only calibrate if awake */
841 if (ah->power_mode != ATH9K_PM_AWAKE)
842 goto set_timer;
843
Sujithfb9987d2010-03-17 14:25:25 +0530844 /* Long calibration runs independently of short calibration. */
845 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
846 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800847 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530848 common->ani.longcal_timer = timestamp;
849 }
850
851 /* Short calibration applies only while caldone is false */
852 if (!common->ani.caldone) {
853 if ((timestamp - common->ani.shortcal_timer) >=
854 short_cal_interval) {
855 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800856 ath_dbg(common, ATH_DBG_ANI,
857 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530858 common->ani.shortcal_timer = timestamp;
859 common->ani.resetcal_timer = timestamp;
860 }
861 } else {
862 if ((timestamp - common->ani.resetcal_timer) >=
863 ATH_RESTART_CALINTERVAL) {
864 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
865 if (common->ani.caldone)
866 common->ani.resetcal_timer = timestamp;
867 }
868 }
869
870 /* Verify whether we must check ANI */
871 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
872 aniflag = true;
873 common->ani.checkani_timer = timestamp;
874 }
875
876 /* Skip all processing if there's nothing to do. */
877 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530878
879 ath9k_htc_ps_wakeup(priv);
880
Sujithfb9987d2010-03-17 14:25:25 +0530881 /* Call ANI routine if necessary */
882 if (aniflag)
883 ath9k_hw_ani_monitor(ah, ah->curchan);
884
885 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200886 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530887 common->ani.caldone =
888 ath9k_hw_calibrate(ah, ah->curchan,
889 common->rx_chainmask,
890 longcal);
891
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530892 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530893 }
894
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530895set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530896 /*
897 * Set timer interval based on previous results.
898 * The interval must be the shortest necessary to satisfy ANI,
899 * short calibration and long calibration.
900 */
901 cal_interval = ATH_LONG_CALINTERVAL;
902 if (priv->ah->config.enable_ani)
903 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
904 if (!common->ani.caldone)
905 cal_interval = min(cal_interval, (u32)short_cal_interval);
906
907 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
908 msecs_to_jiffies(cal_interval));
909}
910
Sujithfb9987d2010-03-17 14:25:25 +0530911/**********************/
912/* mac80211 Callbacks */
913/**********************/
914
915static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
916{
917 struct ieee80211_hdr *hdr;
918 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +0530919 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +0530920
921 hdr = (struct ieee80211_hdr *) skb->data;
922
923 /* Add the padding after the header if this is not already done */
924 padpos = ath9k_cmn_padpos(hdr->frame_control);
925 padsize = padpos & 3;
926 if (padsize && skb->len > padpos) {
927 if (skb_headroom(skb) < padsize)
928 return -1;
929 skb_push(skb, padsize);
930 memmove(skb->data, skb->data + padsize, padpos);
931 }
932
Sujith7757dfe2010-03-29 16:07:17 +0530933 ret = ath9k_htc_tx_start(priv, skb);
934 if (ret != 0) {
935 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -0800936 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
937 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +0530938 ieee80211_stop_queues(hw);
939 spin_lock_bh(&priv->tx_lock);
940 priv->tx_queues_stop = true;
941 spin_unlock_bh(&priv->tx_lock);
942 } else {
Joe Perches226afe62010-12-02 19:12:37 -0800943 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
944 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +0530945 }
Sujithfb9987d2010-03-17 14:25:25 +0530946 goto fail_tx;
947 }
948
949 return 0;
950
951fail_tx:
952 dev_kfree_skb_any(skb);
953 return 0;
954}
955
Sujith881ac6a2010-06-01 15:14:11 +0530956static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530957{
958 struct ath9k_htc_priv *priv = hw->priv;
959 struct ath_hw *ah = priv->ah;
960 struct ath_common *common = ath9k_hw_common(ah);
961 struct ieee80211_channel *curchan = hw->conf.channel;
962 struct ath9k_channel *init_channel;
963 int ret = 0;
964 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530965 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530966 u8 cmd_rsp;
967
Sujith881ac6a2010-06-01 15:14:11 +0530968 mutex_lock(&priv->mutex);
969
Joe Perches226afe62010-12-02 19:12:37 -0800970 ath_dbg(common, ATH_DBG_CONFIG,
971 "Starting driver with initial channel: %d MHz\n",
972 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530973
Sujith21d51302010-06-01 15:14:18 +0530974 /* Ensure that HW is awake before flushing RX */
975 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
976 WMI_CMD(WMI_FLUSH_RECV_CMDID);
977
Sujithfb9987d2010-03-17 14:25:25 +0530978 /* setup initial channel */
979 init_channel = ath9k_cmn_get_curchannel(hw, ah);
980
Sujithfb9987d2010-03-17 14:25:25 +0530981 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200982 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530983 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800984 ath_err(common,
985 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
986 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530987 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530988 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530989 }
990
991 ath_update_txpow(priv);
992
993 mode = ath9k_htc_get_curmode(priv, init_channel);
994 htc_mode = cpu_to_be16(mode);
995 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530996 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530997 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530998
999 ath9k_host_rx_init(priv);
1000
1001 priv->op_flags &= ~OP_INVALID;
1002 htc_start(priv->htc);
1003
Sujith7757dfe2010-03-29 16:07:17 +05301004 spin_lock_bh(&priv->tx_lock);
1005 priv->tx_queues_stop = false;
1006 spin_unlock_bh(&priv->tx_lock);
1007
1008 ieee80211_wake_queues(hw);
1009
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301010 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1011 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1012 AR_STOMP_LOW_WLAN_WGHT);
1013 ath9k_hw_btcoex_enable(ah);
1014 ath_htc_resume_btcoex_work(priv);
1015 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301016 mutex_unlock(&priv->mutex);
1017
1018 return ret;
1019}
1020
Sujith881ac6a2010-06-01 15:14:11 +05301021static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301022{
1023 struct ath9k_htc_priv *priv = hw->priv;
1024 struct ath_hw *ah = priv->ah;
1025 struct ath_common *common = ath9k_hw_common(ah);
1026 int ret = 0;
1027 u8 cmd_rsp;
1028
Sujith Manoharan66e35472010-12-28 14:28:14 +05301029 /* Cancel all the running timers/work .. */
Sujith Manoharan73908672010-12-28 14:28:27 +05301030 cancel_work_sync(&priv->fatal_work);
Sujith Manoharan66e35472010-12-28 14:28:14 +05301031 cancel_work_sync(&priv->ps_work);
1032 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1033 ath9k_led_stop_brightness(priv);
1034
Sujith881ac6a2010-06-01 15:14:11 +05301035 mutex_lock(&priv->mutex);
1036
Sujithfb9987d2010-03-17 14:25:25 +05301037 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001038 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301039 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301040 return;
1041 }
1042
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301043 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301044 htc_stop(priv->htc);
1045 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1046 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1047 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301048 skb_queue_purge(&priv->tx_queue);
1049
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301050 /* Remove monitor interface here */
1051 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1052 if (ath9k_htc_remove_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001053 ath_err(common, "Unable to remove monitor interface\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301054 else
Joe Perches226afe62010-12-02 19:12:37 -08001055 ath_dbg(common, ATH_DBG_CONFIG,
1056 "Monitor interface removed\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301057 }
1058
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301059 if (ah->btcoex_hw.enabled) {
1060 ath9k_hw_btcoex_disable(ah);
1061 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1062 ath_htc_cancel_btcoex_work(priv);
1063 }
1064
Sujithe9201f02010-06-01 15:14:17 +05301065 ath9k_hw_phy_disable(ah);
1066 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301067 ath9k_htc_ps_restore(priv);
1068 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1069
Sujithfb9987d2010-03-17 14:25:25 +05301070 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301071
Joe Perches226afe62010-12-02 19:12:37 -08001072 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301073 mutex_unlock(&priv->mutex);
1074}
1075
Sujithfb9987d2010-03-17 14:25:25 +05301076static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1077 struct ieee80211_vif *vif)
1078{
1079 struct ath9k_htc_priv *priv = hw->priv;
1080 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1081 struct ath_common *common = ath9k_hw_common(priv->ah);
1082 struct ath9k_htc_target_vif hvif;
1083 int ret = 0;
1084 u8 cmd_rsp;
1085
1086 mutex_lock(&priv->mutex);
1087
1088 /* Only one interface for now */
1089 if (priv->nvifs > 0) {
1090 ret = -ENOBUFS;
1091 goto out;
1092 }
1093
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301094 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301095 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1096 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1097
1098 switch (vif->type) {
1099 case NL80211_IFTYPE_STATION:
1100 hvif.opmode = cpu_to_be32(HTC_M_STA);
1101 break;
1102 case NL80211_IFTYPE_ADHOC:
1103 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1104 break;
1105 default:
Joe Perches38002762010-12-02 19:12:36 -08001106 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301107 "Interface type %d not yet supported\n", vif->type);
1108 ret = -EOPNOTSUPP;
1109 goto out;
1110 }
1111
Joe Perches226afe62010-12-02 19:12:37 -08001112 ath_dbg(common, ATH_DBG_CONFIG,
1113 "Attach a VIF of type: %d\n", vif->type);
Sujithfb9987d2010-03-17 14:25:25 +05301114
1115 priv->ah->opmode = vif->type;
1116
1117 /* Index starts from zero on the target */
1118 avp->index = hvif.index = priv->nvifs;
1119 hvif.rtsthreshold = cpu_to_be16(2304);
1120 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1121 if (ret)
1122 goto out;
1123
1124 priv->nvifs++;
1125
1126 /*
1127 * We need a node in target to tx mgmt frames
1128 * before association.
1129 */
1130 ret = ath9k_htc_add_station(priv, vif, NULL);
1131 if (ret)
1132 goto out;
1133
1134 ret = ath9k_htc_update_cap_target(priv);
1135 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -08001136 ath_dbg(common, ATH_DBG_CONFIG,
1137 "Failed to update capability in target\n");
Sujithfb9987d2010-03-17 14:25:25 +05301138
1139 priv->vif = vif;
1140out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301141 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301142 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301143
Sujithfb9987d2010-03-17 14:25:25 +05301144 return ret;
1145}
1146
1147static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1148 struct ieee80211_vif *vif)
1149{
1150 struct ath9k_htc_priv *priv = hw->priv;
1151 struct ath_common *common = ath9k_hw_common(priv->ah);
1152 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1153 struct ath9k_htc_target_vif hvif;
1154 int ret = 0;
1155 u8 cmd_rsp;
1156
Joe Perches226afe62010-12-02 19:12:37 -08001157 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
Sujithfb9987d2010-03-17 14:25:25 +05301158
1159 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301160 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301161
1162 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1163 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1164 hvif.index = avp->index;
1165 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1166 priv->nvifs--;
1167
1168 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301169 priv->vif = NULL;
1170
Sujithcb551df2010-06-01 15:14:12 +05301171 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301172 mutex_unlock(&priv->mutex);
1173}
1174
1175static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1176{
1177 struct ath9k_htc_priv *priv = hw->priv;
1178 struct ath_common *common = ath9k_hw_common(priv->ah);
1179 struct ieee80211_conf *conf = &hw->conf;
1180
1181 mutex_lock(&priv->mutex);
1182
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301183 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1184 bool enable_radio = false;
1185 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1186
Sujith23367762010-06-01 15:14:16 +05301187 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301188 if (!idle && priv->ps_idle)
1189 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301190 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301191 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301192
1193 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001194 ath_dbg(common, ATH_DBG_CONFIG,
1195 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301196 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1197 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301198 }
1199 }
1200
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301201 /*
1202 * Monitor interface should be added before
1203 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1204 */
1205 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1206 if (conf->flags & IEEE80211_CONF_MONITOR) {
1207 if (ath9k_htc_add_monitor_interface(priv))
1208 ath_err(common, "Failed to set monitor mode\n");
1209 else
1210 ath_dbg(common, ATH_DBG_CONFIG,
1211 "HW opmode set to Monitor mode\n");
1212 }
1213 }
1214
Sujithfb9987d2010-03-17 14:25:25 +05301215 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1216 struct ieee80211_channel *curchan = hw->conf.channel;
1217 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301218
Joe Perches226afe62010-12-02 19:12:37 -08001219 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1220 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301221
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001222 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1223 hw->conf.channel,
1224 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301225
1226 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001227 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301228 mutex_unlock(&priv->mutex);
1229 return -EINVAL;
1230 }
1231
1232 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301233
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301234 if (changed & IEEE80211_CONF_CHANGE_PS) {
1235 if (conf->flags & IEEE80211_CONF_PS) {
1236 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1237 priv->ps_enabled = true;
1238 } else {
1239 priv->ps_enabled = false;
1240 cancel_work_sync(&priv->ps_work);
1241 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1242 }
1243 }
Sujithfb9987d2010-03-17 14:25:25 +05301244
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301245 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1246 priv->txpowlimit = 2 * conf->power_level;
1247 ath_update_txpow(priv);
1248 }
1249
Sujith23367762010-06-01 15:14:16 +05301250 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1251 mutex_lock(&priv->htc_pm_lock);
1252 if (!priv->ps_idle) {
1253 mutex_unlock(&priv->htc_pm_lock);
1254 goto out;
1255 }
1256 mutex_unlock(&priv->htc_pm_lock);
1257
Joe Perches226afe62010-12-02 19:12:37 -08001258 ath_dbg(common, ATH_DBG_CONFIG,
1259 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301260 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301261 }
1262
Sujith23367762010-06-01 15:14:16 +05301263out:
Sujithfb9987d2010-03-17 14:25:25 +05301264 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301265 return 0;
1266}
1267
1268#define SUPPORTED_FILTERS \
1269 (FIF_PROMISC_IN_BSS | \
1270 FIF_ALLMULTI | \
1271 FIF_CONTROL | \
1272 FIF_PSPOLL | \
1273 FIF_OTHER_BSS | \
1274 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301275 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301276 FIF_FCSFAIL)
1277
1278static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1279 unsigned int changed_flags,
1280 unsigned int *total_flags,
1281 u64 multicast)
1282{
1283 struct ath9k_htc_priv *priv = hw->priv;
1284 u32 rfilt;
1285
1286 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301287 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301288
Sujithfb9987d2010-03-17 14:25:25 +05301289 changed_flags &= SUPPORTED_FILTERS;
1290 *total_flags &= SUPPORTED_FILTERS;
1291
1292 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301293 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301294 ath9k_hw_setrxfilter(priv->ah, rfilt);
1295
Joe Perches226afe62010-12-02 19:12:37 -08001296 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1297 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301298
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301299 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301300 mutex_unlock(&priv->mutex);
1301}
1302
Sujithabd984e2010-05-18 15:26:04 +05301303static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1304 struct ieee80211_vif *vif,
1305 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301306{
1307 struct ath9k_htc_priv *priv = hw->priv;
1308 int ret;
1309
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301310 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301311 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301312 ret = ath9k_htc_add_station(priv, vif, sta);
1313 if (!ret)
1314 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301315 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301316 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301317
1318 return ret;
1319}
1320
1321static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1322 struct ieee80211_vif *vif,
1323 struct ieee80211_sta *sta)
1324{
1325 struct ath9k_htc_priv *priv = hw->priv;
1326 int ret;
1327
1328 mutex_lock(&priv->mutex);
1329 ath9k_htc_ps_wakeup(priv);
1330 ret = ath9k_htc_remove_station(priv, vif, sta);
1331 ath9k_htc_ps_restore(priv);
1332 mutex_unlock(&priv->mutex);
1333
1334 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301335}
1336
1337static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1338 const struct ieee80211_tx_queue_params *params)
1339{
1340 struct ath9k_htc_priv *priv = hw->priv;
1341 struct ath_common *common = ath9k_hw_common(priv->ah);
1342 struct ath9k_tx_queue_info qi;
1343 int ret = 0, qnum;
1344
1345 if (queue >= WME_NUM_AC)
1346 return 0;
1347
1348 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301349 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301350
1351 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1352
1353 qi.tqi_aifs = params->aifs;
1354 qi.tqi_cwmin = params->cw_min;
1355 qi.tqi_cwmax = params->cw_max;
1356 qi.tqi_burstTime = params->txop;
1357
1358 qnum = get_hw_qnum(queue, priv->hwq_map);
1359
Joe Perches226afe62010-12-02 19:12:37 -08001360 ath_dbg(common, ATH_DBG_CONFIG,
1361 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1362 queue, qnum, params->aifs, params->cw_min,
1363 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301364
Sujithe1572c52010-03-24 13:42:13 +05301365 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301366 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001367 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301368 goto out;
1369 }
Sujithfb9987d2010-03-17 14:25:25 +05301370
Sujith764580f2010-06-01 15:14:19 +05301371 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001372 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301373 ath9k_htc_beaconq_config(priv);
1374out:
Sujithcb551df2010-06-01 15:14:12 +05301375 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301376 mutex_unlock(&priv->mutex);
1377
1378 return ret;
1379}
1380
1381static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1382 enum set_key_cmd cmd,
1383 struct ieee80211_vif *vif,
1384 struct ieee80211_sta *sta,
1385 struct ieee80211_key_conf *key)
1386{
1387 struct ath9k_htc_priv *priv = hw->priv;
1388 struct ath_common *common = ath9k_hw_common(priv->ah);
1389 int ret = 0;
1390
Sujithe1572c52010-03-24 13:42:13 +05301391 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301392 return -ENOSPC;
1393
1394 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001395 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301396 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301397
1398 switch (cmd) {
1399 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001400 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301401 if (ret >= 0) {
1402 key->hw_key_idx = ret;
1403 /* push IV and Michael MIC generation to stack */
1404 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001405 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301406 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001407 if (priv->ah->sw_mgmt_crypto &&
1408 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301409 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1410 ret = 0;
1411 }
1412 break;
1413 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001414 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301415 break;
1416 default:
1417 ret = -EINVAL;
1418 }
1419
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301420 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301421 mutex_unlock(&priv->mutex);
1422
1423 return ret;
1424}
1425
1426static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1427 struct ieee80211_vif *vif,
1428 struct ieee80211_bss_conf *bss_conf,
1429 u32 changed)
1430{
1431 struct ath9k_htc_priv *priv = hw->priv;
1432 struct ath_hw *ah = priv->ah;
1433 struct ath_common *common = ath9k_hw_common(ah);
1434
1435 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301436 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301437
1438 if (changed & BSS_CHANGED_ASSOC) {
1439 common->curaid = bss_conf->assoc ?
1440 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001441 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301442 bss_conf->assoc);
1443
1444 if (bss_conf->assoc) {
1445 priv->op_flags |= OP_ASSOCIATED;
1446 ath_start_ani(priv);
1447 } else {
1448 priv->op_flags &= ~OP_ASSOCIATED;
1449 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1450 }
1451 }
1452
1453 if (changed & BSS_CHANGED_BSSID) {
1454 /* Set BSSID */
1455 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1456 ath9k_hw_write_associd(ah);
1457
Joe Perches226afe62010-12-02 19:12:37 -08001458 ath_dbg(common, ATH_DBG_CONFIG,
1459 "BSSID: %pM aid: 0x%x\n",
1460 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301461 }
1462
1463 if ((changed & BSS_CHANGED_BEACON_INT) ||
1464 (changed & BSS_CHANGED_BEACON) ||
1465 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1466 bss_conf->enable_beacon)) {
1467 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301468 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301469 }
1470
Sujithfb9987d2010-03-17 14:25:25 +05301471 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1472 !bss_conf->enable_beacon) {
1473 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301474 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301475 }
1476
1477 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Joe Perches226afe62010-12-02 19:12:37 -08001478 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1479 bss_conf->use_short_preamble);
Sujithfb9987d2010-03-17 14:25:25 +05301480 if (bss_conf->use_short_preamble)
1481 priv->op_flags |= OP_PREAMBLE_SHORT;
1482 else
1483 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1484 }
1485
1486 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Joe Perches226afe62010-12-02 19:12:37 -08001487 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1488 bss_conf->use_cts_prot);
Sujithfb9987d2010-03-17 14:25:25 +05301489 if (bss_conf->use_cts_prot &&
1490 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1491 priv->op_flags |= OP_PROTECT_ENABLE;
1492 else
1493 priv->op_flags &= ~OP_PROTECT_ENABLE;
1494 }
1495
1496 if (changed & BSS_CHANGED_ERP_SLOT) {
1497 if (bss_conf->use_short_slot)
1498 ah->slottime = 9;
1499 else
1500 ah->slottime = 20;
1501
1502 ath9k_hw_init_global_settings(ah);
1503 }
1504
Sujith2c76ef82010-05-17 12:01:18 +05301505 if (changed & BSS_CHANGED_HT)
1506 ath9k_htc_update_rate(priv, vif, bss_conf);
1507
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301508 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301509 mutex_unlock(&priv->mutex);
1510}
1511
1512static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1513{
1514 struct ath9k_htc_priv *priv = hw->priv;
1515 u64 tsf;
1516
1517 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301518 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301519 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301520 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301521 mutex_unlock(&priv->mutex);
1522
1523 return tsf;
1524}
1525
1526static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1527{
1528 struct ath9k_htc_priv *priv = hw->priv;
1529
1530 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301531 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301532 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301533 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301534 mutex_unlock(&priv->mutex);
1535}
1536
1537static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1538{
1539 struct ath9k_htc_priv *priv = hw->priv;
1540
1541 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301542 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301543 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301544 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301545 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301546}
1547
1548static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1549 struct ieee80211_vif *vif,
1550 enum ieee80211_ampdu_mlme_action action,
1551 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001552 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301553{
1554 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301555 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301556 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301557
1558 switch (action) {
1559 case IEEE80211_AMPDU_RX_START:
1560 break;
1561 case IEEE80211_AMPDU_RX_STOP:
1562 break;
1563 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301564 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1565 if (!ret)
1566 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1567 break;
Sujithfb9987d2010-03-17 14:25:25 +05301568 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301569 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1570 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301571 break;
1572 case IEEE80211_AMPDU_TX_OPERATIONAL:
1573 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301574 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301575 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301576 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301577 break;
1578 default:
Joe Perches38002762010-12-02 19:12:36 -08001579 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301580 }
1581
Sujithd7ca2132010-06-15 10:24:37 +05301582 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301583}
1584
1585static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1586{
1587 struct ath9k_htc_priv *priv = hw->priv;
1588
1589 mutex_lock(&priv->mutex);
1590 spin_lock_bh(&priv->beacon_lock);
1591 priv->op_flags |= OP_SCANNING;
1592 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301593 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301594 if (priv->op_flags & OP_ASSOCIATED)
1595 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301596 mutex_unlock(&priv->mutex);
1597}
1598
1599static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1600{
1601 struct ath9k_htc_priv *priv = hw->priv;
1602
1603 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301604 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301605 spin_lock_bh(&priv->beacon_lock);
1606 priv->op_flags &= ~OP_SCANNING;
1607 spin_unlock_bh(&priv->beacon_lock);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301608 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301609 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301610 ath_start_ani(priv);
1611 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301612 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301613 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301614}
1615
1616static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1617{
1618 return 0;
1619}
1620
1621static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1622 u8 coverage_class)
1623{
1624 struct ath9k_htc_priv *priv = hw->priv;
1625
1626 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301627 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301628 priv->ah->coverage_class = coverage_class;
1629 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301630 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301631 mutex_unlock(&priv->mutex);
1632}
1633
1634struct ieee80211_ops ath9k_htc_ops = {
1635 .tx = ath9k_htc_tx,
1636 .start = ath9k_htc_start,
1637 .stop = ath9k_htc_stop,
1638 .add_interface = ath9k_htc_add_interface,
1639 .remove_interface = ath9k_htc_remove_interface,
1640 .config = ath9k_htc_config,
1641 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301642 .sta_add = ath9k_htc_sta_add,
1643 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301644 .conf_tx = ath9k_htc_conf_tx,
1645 .bss_info_changed = ath9k_htc_bss_info_changed,
1646 .set_key = ath9k_htc_set_key,
1647 .get_tsf = ath9k_htc_get_tsf,
1648 .set_tsf = ath9k_htc_set_tsf,
1649 .reset_tsf = ath9k_htc_reset_tsf,
1650 .ampdu_action = ath9k_htc_ampdu_action,
1651 .sw_scan_start = ath9k_htc_sw_scan_start,
1652 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1653 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1654 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1655 .set_coverage_class = ath9k_htc_set_coverage_class,
1656};