blob: f4d576bc3ccdbce3bef81d4702a756e9b900a34d [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;
124 struct ath9k_hw_cal_data *caldata;
125 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
142 caldata = &priv->caldata[channel->hw_value];
143 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
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200205 caldata = &priv->caldata[channel->hw_value];
206 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530207 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800208 ath_err(common,
209 "Unable to reset channel (%u Mhz) reset status %d\n",
210 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530211 goto err;
212 }
213
214 ath_update_txpow(priv);
215
216 WMI_CMD(WMI_START_RECV_CMDID);
217 if (ret)
218 goto err;
219
220 ath9k_host_rx_init(priv);
221
222 mode = ath9k_htc_get_curmode(priv, hchan);
223 htc_mode = cpu_to_be16(mode);
224 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
225 if (ret)
226 goto err;
227
228 WMI_CMD(WMI_ENABLE_INTR_CMDID);
229 if (ret)
230 goto err;
231
232 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530233err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530234 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530235 return ret;
236}
237
Sujith Manoharancc721282011-01-03 21:22:18 +0530238static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530239{
240 struct ath_common *common = ath9k_hw_common(priv->ah);
241 struct ath9k_htc_target_vif hvif;
242 int ret = 0;
243 u8 cmd_rsp;
244
Sujith Manoharancc721282011-01-03 21:22:18 +0530245 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
246 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
247 hvif.index = 0; /* Should do for now */
248 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
249 priv->nvifs--;
250}
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;
257 int ret = 0;
258 u8 cmd_rsp;
259
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530260 if (priv->nvifs > 0)
261 return -ENOBUFS;
262
Sujith Manoharancc721282011-01-03 21:22:18 +0530263 if (priv->nstations >= ATH9K_HTC_MAX_STA)
264 return -ENOBUFS;
265
266 /*
267 * Add an interface.
268 */
269
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530270 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
271 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
272
273 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
274 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
275 hvif.index = priv->nvifs;
276
277 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
278 if (ret)
279 return ret;
280
281 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530282
283 /*
284 * Associate a station with the interface for packet injection.
285 */
286
287 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
288
289 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
290
291 tsta.is_vif_sta = 1;
292 tsta.sta_index = priv->nstations;
293 tsta.vif_index = hvif.index;
294 tsta.maxampdu = 0xffff;
295
296 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
297 if (ret) {
298 ath_err(common, "Unable to add station entry for monitor mode\n");
299 goto err_vif;
300 }
301
302 priv->nstations++;
303
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530304 /*
305 * Set chainmask etc. on the target.
306 */
307 ret = ath9k_htc_update_cap_target(priv);
308 if (ret)
309 ath_dbg(common, ATH_DBG_CONFIG,
310 "Failed to update capability in target\n");
311
312 priv->ah->is_monitoring = true;
313
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530314 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530315
316err_vif:
317 /*
318 * Remove the interface from the target.
319 */
320 __ath9k_htc_remove_monitor_interface(priv);
321 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530322}
323
324static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
325{
326 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530327 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530328 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530329
Sujith Manoharancc721282011-01-03 21:22:18 +0530330 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530331
Sujith Manoharancc721282011-01-03 21:22:18 +0530332 sta_idx = 0; /* Only single interface, for now */
333
334 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
335 if (ret) {
336 ath_err(common, "Unable to remove station entry for monitor mode\n");
337 return ret;
338 }
339
340 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530341 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530342
343 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530344}
345
Sujithfb9987d2010-03-17 14:25:25 +0530346static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
347 struct ieee80211_vif *vif,
348 struct ieee80211_sta *sta)
349{
350 struct ath_common *common = ath9k_hw_common(priv->ah);
351 struct ath9k_htc_target_sta tsta;
352 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
353 struct ath9k_htc_sta *ista;
354 int ret;
355 u8 cmd_rsp;
356
357 if (priv->nstations >= ATH9K_HTC_MAX_STA)
358 return -ENOBUFS;
359
360 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
361
362 if (sta) {
363 ista = (struct ath9k_htc_sta *) sta->drv_priv;
364 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
365 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
366 tsta.associd = common->curaid;
367 tsta.is_vif_sta = 0;
368 tsta.valid = true;
369 ista->index = priv->nstations;
370 } else {
371 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
372 tsta.is_vif_sta = 1;
373 }
374
375 tsta.sta_index = priv->nstations;
376 tsta.vif_index = avp->index;
377 tsta.maxampdu = 0xffff;
378 if (sta && sta->ht_cap.ht_supported)
379 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
380
381 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
382 if (ret) {
383 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800384 ath_err(common,
385 "Unable to add station entry for: %pM\n",
386 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530387 return ret;
388 }
389
390 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800391 ath_dbg(common, ATH_DBG_CONFIG,
392 "Added a station entry for: %pM (idx: %d)\n",
393 sta->addr, tsta.sta_index);
Sujithfb9987d2010-03-17 14:25:25 +0530394
395 priv->nstations++;
396 return 0;
397}
398
399static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
400 struct ieee80211_vif *vif,
401 struct ieee80211_sta *sta)
402{
403 struct ath_common *common = ath9k_hw_common(priv->ah);
404 struct ath9k_htc_sta *ista;
405 int ret;
406 u8 cmd_rsp, sta_idx;
407
408 if (sta) {
409 ista = (struct ath9k_htc_sta *) sta->drv_priv;
410 sta_idx = ista->index;
411 } else {
412 sta_idx = 0;
413 }
414
415 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
416 if (ret) {
417 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800418 ath_err(common,
419 "Unable to remove station entry for: %pM\n",
420 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530421 return ret;
422 }
423
424 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800425 ath_dbg(common, ATH_DBG_CONFIG,
426 "Removed a station entry for: %pM (idx: %d)\n",
427 sta->addr, sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530428
429 priv->nstations--;
430 return 0;
431}
432
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530433int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530434{
435 struct ath9k_htc_cap_target tcap;
436 int ret;
437 u8 cmd_rsp;
438
439 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
440
441 /* FIXME: Values are hardcoded */
442 tcap.flags = 0x240c40;
443 tcap.flags_ext = 0x80601000;
444 tcap.ampdu_limit = 0xffff0000;
445 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530446 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530447 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530448 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530449
450 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
451
452 return ret;
453}
454
Sujith0d425a72010-05-17 12:01:16 +0530455static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
456 struct ieee80211_sta *sta,
457 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530458{
Sujithfb9987d2010-03-17 14:25:25 +0530459 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
460 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530461 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530462 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530463
Sujithea46e642010-06-02 15:53:50 +0530464 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530465
466 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
467 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530468 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530469 = (sband->bitrates[i].bitrate * 2) / 10;
470 j++;
471 }
472 }
Sujith0d425a72010-05-17 12:01:16 +0530473 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530474
475 if (sta->ht_cap.ht_supported) {
476 for (i = 0, j = 0; i < 77; i++) {
477 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530478 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530479 if (j == ATH_HTC_RATE_MAX)
480 break;
481 }
Sujith0d425a72010-05-17 12:01:16 +0530482 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530483
484 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200485 if (sta->ht_cap.mcs.rx_mask[1])
486 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530487 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
488 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530489 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530490 if (conf_is_ht40(&priv->hw->conf) &&
491 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530492 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530493 else if (conf_is_ht20(&priv->hw->conf) &&
494 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
495 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530496 }
497
Sujith0d425a72010-05-17 12:01:16 +0530498 trate->sta_index = ista->index;
499 trate->isnew = 1;
500 trate->capflags = cpu_to_be32(caps);
501}
Sujithfb9987d2010-03-17 14:25:25 +0530502
Sujith0d425a72010-05-17 12:01:16 +0530503static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
504 struct ath9k_htc_target_rate *trate)
505{
506 struct ath_common *common = ath9k_hw_common(priv->ah);
507 int ret;
508 u8 cmd_rsp;
509
510 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530511 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800512 ath_err(common,
513 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530514 }
515
Sujith0d425a72010-05-17 12:01:16 +0530516 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530517}
518
Sujith0d425a72010-05-17 12:01:16 +0530519static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
520 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530521{
Sujithfb9987d2010-03-17 14:25:25 +0530522 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530523 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530524 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530525
Sujith0d425a72010-05-17 12:01:16 +0530526 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
527 ath9k_htc_setup_rate(priv, sta, &trate);
528 ret = ath9k_htc_send_rate_cmd(priv, &trate);
529 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800530 ath_dbg(common, ATH_DBG_CONFIG,
531 "Updated target sta: %pM, rate caps: 0x%X\n",
532 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530533}
534
Sujith2c76ef82010-05-17 12:01:18 +0530535static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
536 struct ieee80211_vif *vif,
537 struct ieee80211_bss_conf *bss_conf)
538{
539 struct ath_common *common = ath9k_hw_common(priv->ah);
540 struct ath9k_htc_target_rate trate;
541 struct ieee80211_sta *sta;
542 int ret;
543
544 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
545
546 rcu_read_lock();
547 sta = ieee80211_find_sta(vif, bss_conf->bssid);
548 if (!sta) {
549 rcu_read_unlock();
550 return;
551 }
552 ath9k_htc_setup_rate(priv, sta, &trate);
553 rcu_read_unlock();
554
555 ret = ath9k_htc_send_rate_cmd(priv, &trate);
556 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800557 ath_dbg(common, ATH_DBG_CONFIG,
558 "Updated target sta: %pM, rate caps: 0x%X\n",
559 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530560}
561
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400562static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
563 struct ieee80211_vif *vif,
564 struct ieee80211_sta *sta,
565 enum ieee80211_ampdu_mlme_action action,
566 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530567{
568 struct ath_common *common = ath9k_hw_common(priv->ah);
569 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200570 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530571 int ret = 0;
572 u8 cmd_rsp;
573
Dan Carpenter0730d112010-05-08 18:24:02 +0200574 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530575 return -EINVAL;
576
Sujithfb9987d2010-03-17 14:25:25 +0530577 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530578 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530579
Sujithef98c3c2010-03-29 16:07:11 +0530580 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530581 aggr.tidno = tid & 0xf;
582 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530583
Sujithfb9987d2010-03-17 14:25:25 +0530584 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
585 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800586 ath_dbg(common, ATH_DBG_CONFIG,
587 "Unable to %s TX aggregation for (%pM, %d)\n",
588 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530589 else
Joe Perches226afe62010-12-02 19:12:37 -0800590 ath_dbg(common, ATH_DBG_CONFIG,
591 "%s TX aggregation for (%pM, %d)\n",
592 (aggr.aggr_enable) ? "Starting" : "Stopping",
593 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530594
595 spin_lock_bh(&priv->tx_lock);
596 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
597 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530598
599 return ret;
600}
601
Sujithfb9987d2010-03-17 14:25:25 +0530602/*********/
603/* DEBUG */
604/*********/
605
606#ifdef CONFIG_ATH9K_HTC_DEBUGFS
607
608static int ath9k_debugfs_open(struct inode *inode, struct file *file)
609{
610 file->private_data = inode->i_private;
611 return 0;
612}
613
614static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
615 size_t count, loff_t *ppos)
616{
Joe Perches57674302010-07-12 13:50:06 -0700617 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530618 struct ath9k_htc_target_stats cmd_rsp;
619 char buf[512];
620 unsigned int len = 0;
621 int ret = 0;
622
623 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
624
625 WMI_CMD(WMI_TGT_STATS_CMDID);
626 if (ret)
627 return -EINVAL;
628
629
630 len += snprintf(buf + len, sizeof(buf) - len,
631 "%19s : %10u\n", "TX Short Retries",
632 be32_to_cpu(cmd_rsp.tx_shortretry));
633 len += snprintf(buf + len, sizeof(buf) - len,
634 "%19s : %10u\n", "TX Long Retries",
635 be32_to_cpu(cmd_rsp.tx_longretry));
636 len += snprintf(buf + len, sizeof(buf) - len,
637 "%19s : %10u\n", "TX Xretries",
638 be32_to_cpu(cmd_rsp.tx_xretries));
639 len += snprintf(buf + len, sizeof(buf) - len,
640 "%19s : %10u\n", "TX Unaggr. Xretries",
641 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
642 len += snprintf(buf + len, sizeof(buf) - len,
643 "%19s : %10u\n", "TX Xretries (HT)",
644 be32_to_cpu(cmd_rsp.ht_tx_xretries));
645 len += snprintf(buf + len, sizeof(buf) - len,
646 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
647
Dan Carpenter97460102010-07-22 10:50:28 +0200648 if (len > sizeof(buf))
649 len = sizeof(buf);
650
Sujithfb9987d2010-03-17 14:25:25 +0530651 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
652}
653
654static const struct file_operations fops_tgt_stats = {
655 .read = read_file_tgt_stats,
656 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200657 .owner = THIS_MODULE,
658 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530659};
660
661static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
662 size_t count, loff_t *ppos)
663{
Joe Perches57674302010-07-12 13:50:06 -0700664 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530665 char buf[512];
666 unsigned int len = 0;
667
668 len += snprintf(buf + len, sizeof(buf) - len,
669 "%20s : %10u\n", "Buffers queued",
670 priv->debug.tx_stats.buf_queued);
671 len += snprintf(buf + len, sizeof(buf) - len,
672 "%20s : %10u\n", "Buffers completed",
673 priv->debug.tx_stats.buf_completed);
674 len += snprintf(buf + len, sizeof(buf) - len,
675 "%20s : %10u\n", "SKBs queued",
676 priv->debug.tx_stats.skb_queued);
677 len += snprintf(buf + len, sizeof(buf) - len,
678 "%20s : %10u\n", "SKBs completed",
679 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530680 len += snprintf(buf + len, sizeof(buf) - len,
681 "%20s : %10u\n", "SKBs dropped",
682 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530683
Sujith2edb4582010-05-14 11:18:54 +0530684 len += snprintf(buf + len, sizeof(buf) - len,
685 "%20s : %10u\n", "BE queued",
686 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
687 len += snprintf(buf + len, sizeof(buf) - len,
688 "%20s : %10u\n", "BK queued",
689 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
690 len += snprintf(buf + len, sizeof(buf) - len,
691 "%20s : %10u\n", "VI queued",
692 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
693 len += snprintf(buf + len, sizeof(buf) - len,
694 "%20s : %10u\n", "VO queued",
695 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
696
Dan Carpenter97460102010-07-22 10:50:28 +0200697 if (len > sizeof(buf))
698 len = sizeof(buf);
699
Sujithfb9987d2010-03-17 14:25:25 +0530700 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
701}
702
703static const struct file_operations fops_xmit = {
704 .read = read_file_xmit,
705 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200706 .owner = THIS_MODULE,
707 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530708};
709
710static ssize_t read_file_recv(struct file *file, char __user *user_buf,
711 size_t count, loff_t *ppos)
712{
Joe Perches57674302010-07-12 13:50:06 -0700713 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530714 char buf[512];
715 unsigned int len = 0;
716
717 len += snprintf(buf + len, sizeof(buf) - len,
718 "%20s : %10u\n", "SKBs allocated",
719 priv->debug.rx_stats.skb_allocated);
720 len += snprintf(buf + len, sizeof(buf) - len,
721 "%20s : %10u\n", "SKBs completed",
722 priv->debug.rx_stats.skb_completed);
723 len += snprintf(buf + len, sizeof(buf) - len,
724 "%20s : %10u\n", "SKBs Dropped",
725 priv->debug.rx_stats.skb_dropped);
726
Dan Carpenter97460102010-07-22 10:50:28 +0200727 if (len > sizeof(buf))
728 len = sizeof(buf);
729
Sujithfb9987d2010-03-17 14:25:25 +0530730 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
731}
732
733static const struct file_operations fops_recv = {
734 .read = read_file_recv,
735 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200736 .owner = THIS_MODULE,
737 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530738};
739
Sujithe1572c52010-03-24 13:42:13 +0530740int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530741{
742 struct ath_common *common = ath9k_hw_common(ah);
743 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
744
745 if (!ath9k_debugfs_root)
746 return -ENOENT;
747
748 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
749 ath9k_debugfs_root);
750 if (!priv->debug.debugfs_phy)
751 goto err;
752
753 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
754 priv->debug.debugfs_phy,
755 priv, &fops_tgt_stats);
756 if (!priv->debug.debugfs_tgt_stats)
757 goto err;
758
759
760 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
761 priv->debug.debugfs_phy,
762 priv, &fops_xmit);
763 if (!priv->debug.debugfs_xmit)
764 goto err;
765
766 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
767 priv->debug.debugfs_phy,
768 priv, &fops_recv);
769 if (!priv->debug.debugfs_recv)
770 goto err;
771
772 return 0;
773
774err:
Sujithe1572c52010-03-24 13:42:13 +0530775 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530776 return -ENOMEM;
777}
778
Sujithe1572c52010-03-24 13:42:13 +0530779void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530780{
781 struct ath_common *common = ath9k_hw_common(ah);
782 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
783
784 debugfs_remove(priv->debug.debugfs_recv);
785 debugfs_remove(priv->debug.debugfs_xmit);
786 debugfs_remove(priv->debug.debugfs_tgt_stats);
787 debugfs_remove(priv->debug.debugfs_phy);
788}
789
Sujithe1572c52010-03-24 13:42:13 +0530790int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530791{
792 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
793 if (!ath9k_debugfs_root)
794 return -ENOENT;
795
796 return 0;
797}
798
Sujithe1572c52010-03-24 13:42:13 +0530799void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530800{
801 debugfs_remove(ath9k_debugfs_root);
802 ath9k_debugfs_root = NULL;
803}
804
805#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
806
807/*******/
808/* ANI */
809/*******/
810
Sujith Manoharan73908672010-12-28 14:28:27 +0530811void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530812{
813 struct ath_common *common = ath9k_hw_common(priv->ah);
814 unsigned long timestamp = jiffies_to_msecs(jiffies);
815
816 common->ani.longcal_timer = timestamp;
817 common->ani.shortcal_timer = timestamp;
818 common->ani.checkani_timer = timestamp;
819
820 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
821 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
822}
823
824void ath9k_ani_work(struct work_struct *work)
825{
826 struct ath9k_htc_priv *priv =
827 container_of(work, struct ath9k_htc_priv,
828 ath9k_ani_work.work);
829 struct ath_hw *ah = priv->ah;
830 struct ath_common *common = ath9k_hw_common(ah);
831 bool longcal = false;
832 bool shortcal = false;
833 bool aniflag = false;
834 unsigned int timestamp = jiffies_to_msecs(jiffies);
835 u32 cal_interval, short_cal_interval;
836
837 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
838
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530839 /* Only calibrate if awake */
840 if (ah->power_mode != ATH9K_PM_AWAKE)
841 goto set_timer;
842
Sujithfb9987d2010-03-17 14:25:25 +0530843 /* Long calibration runs independently of short calibration. */
844 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
845 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800846 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530847 common->ani.longcal_timer = timestamp;
848 }
849
850 /* Short calibration applies only while caldone is false */
851 if (!common->ani.caldone) {
852 if ((timestamp - common->ani.shortcal_timer) >=
853 short_cal_interval) {
854 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800855 ath_dbg(common, ATH_DBG_ANI,
856 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530857 common->ani.shortcal_timer = timestamp;
858 common->ani.resetcal_timer = timestamp;
859 }
860 } else {
861 if ((timestamp - common->ani.resetcal_timer) >=
862 ATH_RESTART_CALINTERVAL) {
863 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
864 if (common->ani.caldone)
865 common->ani.resetcal_timer = timestamp;
866 }
867 }
868
869 /* Verify whether we must check ANI */
870 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
871 aniflag = true;
872 common->ani.checkani_timer = timestamp;
873 }
874
875 /* Skip all processing if there's nothing to do. */
876 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530877
878 ath9k_htc_ps_wakeup(priv);
879
Sujithfb9987d2010-03-17 14:25:25 +0530880 /* Call ANI routine if necessary */
881 if (aniflag)
882 ath9k_hw_ani_monitor(ah, ah->curchan);
883
884 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200885 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530886 common->ani.caldone =
887 ath9k_hw_calibrate(ah, ah->curchan,
888 common->rx_chainmask,
889 longcal);
890
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530891 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530892 }
893
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530894set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530895 /*
896 * Set timer interval based on previous results.
897 * The interval must be the shortest necessary to satisfy ANI,
898 * short calibration and long calibration.
899 */
900 cal_interval = ATH_LONG_CALINTERVAL;
901 if (priv->ah->config.enable_ani)
902 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
903 if (!common->ani.caldone)
904 cal_interval = min(cal_interval, (u32)short_cal_interval);
905
906 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
907 msecs_to_jiffies(cal_interval));
908}
909
Sujithfb9987d2010-03-17 14:25:25 +0530910/**********************/
911/* mac80211 Callbacks */
912/**********************/
913
914static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
915{
916 struct ieee80211_hdr *hdr;
917 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +0530918 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +0530919
920 hdr = (struct ieee80211_hdr *) skb->data;
921
922 /* Add the padding after the header if this is not already done */
923 padpos = ath9k_cmn_padpos(hdr->frame_control);
924 padsize = padpos & 3;
925 if (padsize && skb->len > padpos) {
926 if (skb_headroom(skb) < padsize)
927 return -1;
928 skb_push(skb, padsize);
929 memmove(skb->data, skb->data + padsize, padpos);
930 }
931
Sujith7757dfe2010-03-29 16:07:17 +0530932 ret = ath9k_htc_tx_start(priv, skb);
933 if (ret != 0) {
934 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -0800935 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
936 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +0530937 ieee80211_stop_queues(hw);
938 spin_lock_bh(&priv->tx_lock);
939 priv->tx_queues_stop = true;
940 spin_unlock_bh(&priv->tx_lock);
941 } else {
Joe Perches226afe62010-12-02 19:12:37 -0800942 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
943 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +0530944 }
Sujithfb9987d2010-03-17 14:25:25 +0530945 goto fail_tx;
946 }
947
948 return 0;
949
950fail_tx:
951 dev_kfree_skb_any(skb);
952 return 0;
953}
954
Sujith881ac6a2010-06-01 15:14:11 +0530955static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530956{
957 struct ath9k_htc_priv *priv = hw->priv;
958 struct ath_hw *ah = priv->ah;
959 struct ath_common *common = ath9k_hw_common(ah);
960 struct ieee80211_channel *curchan = hw->conf.channel;
961 struct ath9k_channel *init_channel;
962 int ret = 0;
963 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530964 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530965 u8 cmd_rsp;
966
Sujith881ac6a2010-06-01 15:14:11 +0530967 mutex_lock(&priv->mutex);
968
Joe Perches226afe62010-12-02 19:12:37 -0800969 ath_dbg(common, ATH_DBG_CONFIG,
970 "Starting driver with initial channel: %d MHz\n",
971 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530972
Sujith21d51302010-06-01 15:14:18 +0530973 /* Ensure that HW is awake before flushing RX */
974 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
975 WMI_CMD(WMI_FLUSH_RECV_CMDID);
976
Sujithfb9987d2010-03-17 14:25:25 +0530977 /* setup initial channel */
978 init_channel = ath9k_cmn_get_curchannel(hw, ah);
979
Sujithfb9987d2010-03-17 14:25:25 +0530980 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200981 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530982 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800983 ath_err(common,
984 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
985 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530986 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530987 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530988 }
989
990 ath_update_txpow(priv);
991
992 mode = ath9k_htc_get_curmode(priv, init_channel);
993 htc_mode = cpu_to_be16(mode);
994 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530995 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530996 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530997
998 ath9k_host_rx_init(priv);
999
1000 priv->op_flags &= ~OP_INVALID;
1001 htc_start(priv->htc);
1002
Sujith7757dfe2010-03-29 16:07:17 +05301003 spin_lock_bh(&priv->tx_lock);
1004 priv->tx_queues_stop = false;
1005 spin_unlock_bh(&priv->tx_lock);
1006
1007 ieee80211_wake_queues(hw);
1008
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301009 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1010 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1011 AR_STOMP_LOW_WLAN_WGHT);
1012 ath9k_hw_btcoex_enable(ah);
1013 ath_htc_resume_btcoex_work(priv);
1014 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301015 mutex_unlock(&priv->mutex);
1016
1017 return ret;
1018}
1019
Sujith881ac6a2010-06-01 15:14:11 +05301020static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301021{
1022 struct ath9k_htc_priv *priv = hw->priv;
1023 struct ath_hw *ah = priv->ah;
1024 struct ath_common *common = ath9k_hw_common(ah);
1025 int ret = 0;
1026 u8 cmd_rsp;
1027
Sujith Manoharan66e35472010-12-28 14:28:14 +05301028 /* Cancel all the running timers/work .. */
Sujith Manoharan73908672010-12-28 14:28:27 +05301029 cancel_work_sync(&priv->fatal_work);
Sujith Manoharan66e35472010-12-28 14:28:14 +05301030 cancel_work_sync(&priv->ps_work);
1031 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1032 ath9k_led_stop_brightness(priv);
1033
Sujith881ac6a2010-06-01 15:14:11 +05301034 mutex_lock(&priv->mutex);
1035
Sujithfb9987d2010-03-17 14:25:25 +05301036 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001037 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301038 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301039 return;
1040 }
1041
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301042 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301043 htc_stop(priv->htc);
1044 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1045 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1046 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301047 skb_queue_purge(&priv->tx_queue);
1048
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301049 /* Remove monitor interface here */
1050 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1051 if (ath9k_htc_remove_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001052 ath_err(common, "Unable to remove monitor interface\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301053 else
Joe Perches226afe62010-12-02 19:12:37 -08001054 ath_dbg(common, ATH_DBG_CONFIG,
1055 "Monitor interface removed\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301056 }
1057
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301058 if (ah->btcoex_hw.enabled) {
1059 ath9k_hw_btcoex_disable(ah);
1060 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1061 ath_htc_cancel_btcoex_work(priv);
1062 }
1063
Sujithe9201f02010-06-01 15:14:17 +05301064 ath9k_hw_phy_disable(ah);
1065 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301066 ath9k_htc_ps_restore(priv);
1067 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1068
Sujithfb9987d2010-03-17 14:25:25 +05301069 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301070
Joe Perches226afe62010-12-02 19:12:37 -08001071 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301072 mutex_unlock(&priv->mutex);
1073}
1074
Sujithfb9987d2010-03-17 14:25:25 +05301075static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1076 struct ieee80211_vif *vif)
1077{
1078 struct ath9k_htc_priv *priv = hw->priv;
1079 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1080 struct ath_common *common = ath9k_hw_common(priv->ah);
1081 struct ath9k_htc_target_vif hvif;
1082 int ret = 0;
1083 u8 cmd_rsp;
1084
1085 mutex_lock(&priv->mutex);
1086
1087 /* Only one interface for now */
1088 if (priv->nvifs > 0) {
1089 ret = -ENOBUFS;
1090 goto out;
1091 }
1092
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301093 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301094 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1095 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1096
1097 switch (vif->type) {
1098 case NL80211_IFTYPE_STATION:
1099 hvif.opmode = cpu_to_be32(HTC_M_STA);
1100 break;
1101 case NL80211_IFTYPE_ADHOC:
1102 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1103 break;
1104 default:
Joe Perches38002762010-12-02 19:12:36 -08001105 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301106 "Interface type %d not yet supported\n", vif->type);
1107 ret = -EOPNOTSUPP;
1108 goto out;
1109 }
1110
Joe Perches226afe62010-12-02 19:12:37 -08001111 ath_dbg(common, ATH_DBG_CONFIG,
1112 "Attach a VIF of type: %d\n", vif->type);
Sujithfb9987d2010-03-17 14:25:25 +05301113
1114 priv->ah->opmode = vif->type;
1115
1116 /* Index starts from zero on the target */
1117 avp->index = hvif.index = priv->nvifs;
1118 hvif.rtsthreshold = cpu_to_be16(2304);
1119 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1120 if (ret)
1121 goto out;
1122
1123 priv->nvifs++;
1124
1125 /*
1126 * We need a node in target to tx mgmt frames
1127 * before association.
1128 */
1129 ret = ath9k_htc_add_station(priv, vif, NULL);
1130 if (ret)
1131 goto out;
1132
1133 ret = ath9k_htc_update_cap_target(priv);
1134 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -08001135 ath_dbg(common, ATH_DBG_CONFIG,
1136 "Failed to update capability in target\n");
Sujithfb9987d2010-03-17 14:25:25 +05301137
1138 priv->vif = vif;
1139out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301140 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301141 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301142
Sujithfb9987d2010-03-17 14:25:25 +05301143 return ret;
1144}
1145
1146static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1147 struct ieee80211_vif *vif)
1148{
1149 struct ath9k_htc_priv *priv = hw->priv;
1150 struct ath_common *common = ath9k_hw_common(priv->ah);
1151 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1152 struct ath9k_htc_target_vif hvif;
1153 int ret = 0;
1154 u8 cmd_rsp;
1155
Joe Perches226afe62010-12-02 19:12:37 -08001156 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
Sujithfb9987d2010-03-17 14:25:25 +05301157
1158 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301159 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301160
1161 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1162 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1163 hvif.index = avp->index;
1164 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1165 priv->nvifs--;
1166
1167 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301168 priv->vif = NULL;
1169
Sujithcb551df2010-06-01 15:14:12 +05301170 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301171 mutex_unlock(&priv->mutex);
1172}
1173
1174static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1175{
1176 struct ath9k_htc_priv *priv = hw->priv;
1177 struct ath_common *common = ath9k_hw_common(priv->ah);
1178 struct ieee80211_conf *conf = &hw->conf;
1179
1180 mutex_lock(&priv->mutex);
1181
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301182 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1183 bool enable_radio = false;
1184 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1185
Sujith23367762010-06-01 15:14:16 +05301186 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301187 if (!idle && priv->ps_idle)
1188 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301189 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301190 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301191
1192 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001193 ath_dbg(common, ATH_DBG_CONFIG,
1194 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301195 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1196 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301197 }
1198 }
1199
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301200 /*
1201 * Monitor interface should be added before
1202 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1203 */
1204 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1205 if (conf->flags & IEEE80211_CONF_MONITOR) {
1206 if (ath9k_htc_add_monitor_interface(priv))
1207 ath_err(common, "Failed to set monitor mode\n");
1208 else
1209 ath_dbg(common, ATH_DBG_CONFIG,
1210 "HW opmode set to Monitor mode\n");
1211 }
1212 }
1213
Sujithfb9987d2010-03-17 14:25:25 +05301214 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1215 struct ieee80211_channel *curchan = hw->conf.channel;
1216 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301217
Joe Perches226afe62010-12-02 19:12:37 -08001218 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1219 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301220
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001221 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1222 hw->conf.channel,
1223 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301224
1225 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001226 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301227 mutex_unlock(&priv->mutex);
1228 return -EINVAL;
1229 }
1230
1231 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301232
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301233 if (changed & IEEE80211_CONF_CHANGE_PS) {
1234 if (conf->flags & IEEE80211_CONF_PS) {
1235 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1236 priv->ps_enabled = true;
1237 } else {
1238 priv->ps_enabled = false;
1239 cancel_work_sync(&priv->ps_work);
1240 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1241 }
1242 }
Sujithfb9987d2010-03-17 14:25:25 +05301243
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301244 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1245 priv->txpowlimit = 2 * conf->power_level;
1246 ath_update_txpow(priv);
1247 }
1248
Sujith23367762010-06-01 15:14:16 +05301249 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1250 mutex_lock(&priv->htc_pm_lock);
1251 if (!priv->ps_idle) {
1252 mutex_unlock(&priv->htc_pm_lock);
1253 goto out;
1254 }
1255 mutex_unlock(&priv->htc_pm_lock);
1256
Joe Perches226afe62010-12-02 19:12:37 -08001257 ath_dbg(common, ATH_DBG_CONFIG,
1258 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301259 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301260 }
1261
Sujith23367762010-06-01 15:14:16 +05301262out:
Sujithfb9987d2010-03-17 14:25:25 +05301263 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301264 return 0;
1265}
1266
1267#define SUPPORTED_FILTERS \
1268 (FIF_PROMISC_IN_BSS | \
1269 FIF_ALLMULTI | \
1270 FIF_CONTROL | \
1271 FIF_PSPOLL | \
1272 FIF_OTHER_BSS | \
1273 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301274 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301275 FIF_FCSFAIL)
1276
1277static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1278 unsigned int changed_flags,
1279 unsigned int *total_flags,
1280 u64 multicast)
1281{
1282 struct ath9k_htc_priv *priv = hw->priv;
1283 u32 rfilt;
1284
1285 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301286 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301287
Sujithfb9987d2010-03-17 14:25:25 +05301288 changed_flags &= SUPPORTED_FILTERS;
1289 *total_flags &= SUPPORTED_FILTERS;
1290
1291 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301292 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301293 ath9k_hw_setrxfilter(priv->ah, rfilt);
1294
Joe Perches226afe62010-12-02 19:12:37 -08001295 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1296 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301297
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301298 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301299 mutex_unlock(&priv->mutex);
1300}
1301
Sujithabd984e2010-05-18 15:26:04 +05301302static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1303 struct ieee80211_vif *vif,
1304 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301305{
1306 struct ath9k_htc_priv *priv = hw->priv;
1307 int ret;
1308
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301309 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301310 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301311 ret = ath9k_htc_add_station(priv, vif, sta);
1312 if (!ret)
1313 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301314 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301315 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301316
1317 return ret;
1318}
1319
1320static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1321 struct ieee80211_vif *vif,
1322 struct ieee80211_sta *sta)
1323{
1324 struct ath9k_htc_priv *priv = hw->priv;
1325 int ret;
1326
1327 mutex_lock(&priv->mutex);
1328 ath9k_htc_ps_wakeup(priv);
1329 ret = ath9k_htc_remove_station(priv, vif, sta);
1330 ath9k_htc_ps_restore(priv);
1331 mutex_unlock(&priv->mutex);
1332
1333 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301334}
1335
1336static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1337 const struct ieee80211_tx_queue_params *params)
1338{
1339 struct ath9k_htc_priv *priv = hw->priv;
1340 struct ath_common *common = ath9k_hw_common(priv->ah);
1341 struct ath9k_tx_queue_info qi;
1342 int ret = 0, qnum;
1343
1344 if (queue >= WME_NUM_AC)
1345 return 0;
1346
1347 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301348 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301349
1350 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1351
1352 qi.tqi_aifs = params->aifs;
1353 qi.tqi_cwmin = params->cw_min;
1354 qi.tqi_cwmax = params->cw_max;
1355 qi.tqi_burstTime = params->txop;
1356
1357 qnum = get_hw_qnum(queue, priv->hwq_map);
1358
Joe Perches226afe62010-12-02 19:12:37 -08001359 ath_dbg(common, ATH_DBG_CONFIG,
1360 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1361 queue, qnum, params->aifs, params->cw_min,
1362 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301363
Sujithe1572c52010-03-24 13:42:13 +05301364 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301365 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001366 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301367 goto out;
1368 }
Sujithfb9987d2010-03-17 14:25:25 +05301369
Sujith764580f2010-06-01 15:14:19 +05301370 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001371 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301372 ath9k_htc_beaconq_config(priv);
1373out:
Sujithcb551df2010-06-01 15:14:12 +05301374 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301375 mutex_unlock(&priv->mutex);
1376
1377 return ret;
1378}
1379
1380static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1381 enum set_key_cmd cmd,
1382 struct ieee80211_vif *vif,
1383 struct ieee80211_sta *sta,
1384 struct ieee80211_key_conf *key)
1385{
1386 struct ath9k_htc_priv *priv = hw->priv;
1387 struct ath_common *common = ath9k_hw_common(priv->ah);
1388 int ret = 0;
1389
Sujithe1572c52010-03-24 13:42:13 +05301390 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301391 return -ENOSPC;
1392
1393 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001394 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301395 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301396
1397 switch (cmd) {
1398 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001399 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301400 if (ret >= 0) {
1401 key->hw_key_idx = ret;
1402 /* push IV and Michael MIC generation to stack */
1403 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001404 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301405 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001406 if (priv->ah->sw_mgmt_crypto &&
1407 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301408 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1409 ret = 0;
1410 }
1411 break;
1412 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001413 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301414 break;
1415 default:
1416 ret = -EINVAL;
1417 }
1418
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301419 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301420 mutex_unlock(&priv->mutex);
1421
1422 return ret;
1423}
1424
1425static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1426 struct ieee80211_vif *vif,
1427 struct ieee80211_bss_conf *bss_conf,
1428 u32 changed)
1429{
1430 struct ath9k_htc_priv *priv = hw->priv;
1431 struct ath_hw *ah = priv->ah;
1432 struct ath_common *common = ath9k_hw_common(ah);
1433
1434 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301435 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301436
1437 if (changed & BSS_CHANGED_ASSOC) {
1438 common->curaid = bss_conf->assoc ?
1439 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001440 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301441 bss_conf->assoc);
1442
1443 if (bss_conf->assoc) {
1444 priv->op_flags |= OP_ASSOCIATED;
1445 ath_start_ani(priv);
1446 } else {
1447 priv->op_flags &= ~OP_ASSOCIATED;
1448 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1449 }
1450 }
1451
1452 if (changed & BSS_CHANGED_BSSID) {
1453 /* Set BSSID */
1454 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1455 ath9k_hw_write_associd(ah);
1456
Joe Perches226afe62010-12-02 19:12:37 -08001457 ath_dbg(common, ATH_DBG_CONFIG,
1458 "BSSID: %pM aid: 0x%x\n",
1459 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301460 }
1461
1462 if ((changed & BSS_CHANGED_BEACON_INT) ||
1463 (changed & BSS_CHANGED_BEACON) ||
1464 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1465 bss_conf->enable_beacon)) {
1466 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301467 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301468 }
1469
Sujithfb9987d2010-03-17 14:25:25 +05301470 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1471 !bss_conf->enable_beacon) {
1472 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301473 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301474 }
1475
1476 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Joe Perches226afe62010-12-02 19:12:37 -08001477 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1478 bss_conf->use_short_preamble);
Sujithfb9987d2010-03-17 14:25:25 +05301479 if (bss_conf->use_short_preamble)
1480 priv->op_flags |= OP_PREAMBLE_SHORT;
1481 else
1482 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1483 }
1484
1485 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Joe Perches226afe62010-12-02 19:12:37 -08001486 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1487 bss_conf->use_cts_prot);
Sujithfb9987d2010-03-17 14:25:25 +05301488 if (bss_conf->use_cts_prot &&
1489 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1490 priv->op_flags |= OP_PROTECT_ENABLE;
1491 else
1492 priv->op_flags &= ~OP_PROTECT_ENABLE;
1493 }
1494
1495 if (changed & BSS_CHANGED_ERP_SLOT) {
1496 if (bss_conf->use_short_slot)
1497 ah->slottime = 9;
1498 else
1499 ah->slottime = 20;
1500
1501 ath9k_hw_init_global_settings(ah);
1502 }
1503
Sujith2c76ef82010-05-17 12:01:18 +05301504 if (changed & BSS_CHANGED_HT)
1505 ath9k_htc_update_rate(priv, vif, bss_conf);
1506
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301507 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301508 mutex_unlock(&priv->mutex);
1509}
1510
1511static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1512{
1513 struct ath9k_htc_priv *priv = hw->priv;
1514 u64 tsf;
1515
1516 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301517 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301518 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301519 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301520 mutex_unlock(&priv->mutex);
1521
1522 return tsf;
1523}
1524
1525static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1526{
1527 struct ath9k_htc_priv *priv = hw->priv;
1528
1529 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301530 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301531 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301532 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301533 mutex_unlock(&priv->mutex);
1534}
1535
1536static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1537{
1538 struct ath9k_htc_priv *priv = hw->priv;
1539
1540 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301541 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301542 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301543 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301544 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301545}
1546
1547static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1548 struct ieee80211_vif *vif,
1549 enum ieee80211_ampdu_mlme_action action,
1550 struct ieee80211_sta *sta,
1551 u16 tid, u16 *ssn)
1552{
1553 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301554 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301555 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301556
1557 switch (action) {
1558 case IEEE80211_AMPDU_RX_START:
1559 break;
1560 case IEEE80211_AMPDU_RX_STOP:
1561 break;
1562 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301563 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1564 if (!ret)
1565 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1566 break;
Sujithfb9987d2010-03-17 14:25:25 +05301567 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301568 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1569 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301570 break;
1571 case IEEE80211_AMPDU_TX_OPERATIONAL:
1572 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301573 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301574 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301575 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301576 break;
1577 default:
Joe Perches38002762010-12-02 19:12:36 -08001578 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301579 }
1580
Sujithd7ca2132010-06-15 10:24:37 +05301581 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301582}
1583
1584static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1585{
1586 struct ath9k_htc_priv *priv = hw->priv;
1587
1588 mutex_lock(&priv->mutex);
1589 spin_lock_bh(&priv->beacon_lock);
1590 priv->op_flags |= OP_SCANNING;
1591 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301592 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301593 if (priv->op_flags & OP_ASSOCIATED)
1594 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301595 mutex_unlock(&priv->mutex);
1596}
1597
1598static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1599{
1600 struct ath9k_htc_priv *priv = hw->priv;
1601
1602 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301603 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301604 spin_lock_bh(&priv->beacon_lock);
1605 priv->op_flags &= ~OP_SCANNING;
1606 spin_unlock_bh(&priv->beacon_lock);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301607 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301608 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301609 ath_start_ani(priv);
1610 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301611 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301612 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301613}
1614
1615static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1616{
1617 return 0;
1618}
1619
1620static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1621 u8 coverage_class)
1622{
1623 struct ath9k_htc_priv *priv = hw->priv;
1624
1625 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301626 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301627 priv->ah->coverage_class = coverage_class;
1628 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301629 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301630 mutex_unlock(&priv->mutex);
1631}
1632
1633struct ieee80211_ops ath9k_htc_ops = {
1634 .tx = ath9k_htc_tx,
1635 .start = ath9k_htc_start,
1636 .stop = ath9k_htc_stop,
1637 .add_interface = ath9k_htc_add_interface,
1638 .remove_interface = ath9k_htc_remove_interface,
1639 .config = ath9k_htc_config,
1640 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301641 .sta_add = ath9k_htc_sta_add,
1642 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301643 .conf_tx = ath9k_htc_conf_tx,
1644 .bss_info_changed = ath9k_htc_bss_info_changed,
1645 .set_key = ath9k_htc_set_key,
1646 .get_tsf = ath9k_htc_get_tsf,
1647 .set_tsf = ath9k_htc_set_tsf,
1648 .reset_tsf = ath9k_htc_reset_tsf,
1649 .ampdu_action = ath9k_htc_ampdu_action,
1650 .sw_scan_start = ath9k_htc_sw_scan_start,
1651 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1652 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1653 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1654 .set_coverage_class = ath9k_htc_set_coverage_class,
1655};