blob: 845b4c938d166090efc045ea11083e1cf0ce0789 [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
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530304 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530305
306err_vif:
307 /*
308 * Remove the interface from the target.
309 */
310 __ath9k_htc_remove_monitor_interface(priv);
311 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530312}
313
314static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
315{
316 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530317 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530318 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530319
Sujith Manoharancc721282011-01-03 21:22:18 +0530320 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530321
Sujith Manoharancc721282011-01-03 21:22:18 +0530322 sta_idx = 0; /* Only single interface, for now */
323
324 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
325 if (ret) {
326 ath_err(common, "Unable to remove station entry for monitor mode\n");
327 return ret;
328 }
329
330 priv->nstations--;
331
332 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530333}
334
Sujithfb9987d2010-03-17 14:25:25 +0530335static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
336 struct ieee80211_vif *vif,
337 struct ieee80211_sta *sta)
338{
339 struct ath_common *common = ath9k_hw_common(priv->ah);
340 struct ath9k_htc_target_sta tsta;
341 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
342 struct ath9k_htc_sta *ista;
343 int ret;
344 u8 cmd_rsp;
345
346 if (priv->nstations >= ATH9K_HTC_MAX_STA)
347 return -ENOBUFS;
348
349 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
350
351 if (sta) {
352 ista = (struct ath9k_htc_sta *) sta->drv_priv;
353 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
354 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
355 tsta.associd = common->curaid;
356 tsta.is_vif_sta = 0;
357 tsta.valid = true;
358 ista->index = priv->nstations;
359 } else {
360 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
361 tsta.is_vif_sta = 1;
362 }
363
364 tsta.sta_index = priv->nstations;
365 tsta.vif_index = avp->index;
366 tsta.maxampdu = 0xffff;
367 if (sta && sta->ht_cap.ht_supported)
368 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
369
370 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
371 if (ret) {
372 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800373 ath_err(common,
374 "Unable to add station entry for: %pM\n",
375 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530376 return ret;
377 }
378
379 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800380 ath_dbg(common, ATH_DBG_CONFIG,
381 "Added a station entry for: %pM (idx: %d)\n",
382 sta->addr, tsta.sta_index);
Sujithfb9987d2010-03-17 14:25:25 +0530383
384 priv->nstations++;
385 return 0;
386}
387
388static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
389 struct ieee80211_vif *vif,
390 struct ieee80211_sta *sta)
391{
392 struct ath_common *common = ath9k_hw_common(priv->ah);
393 struct ath9k_htc_sta *ista;
394 int ret;
395 u8 cmd_rsp, sta_idx;
396
397 if (sta) {
398 ista = (struct ath9k_htc_sta *) sta->drv_priv;
399 sta_idx = ista->index;
400 } else {
401 sta_idx = 0;
402 }
403
404 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
405 if (ret) {
406 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800407 ath_err(common,
408 "Unable to remove station entry for: %pM\n",
409 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530410 return ret;
411 }
412
413 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800414 ath_dbg(common, ATH_DBG_CONFIG,
415 "Removed a station entry for: %pM (idx: %d)\n",
416 sta->addr, sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530417
418 priv->nstations--;
419 return 0;
420}
421
422static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
423{
424 struct ath9k_htc_cap_target tcap;
425 int ret;
426 u8 cmd_rsp;
427
428 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
429
430 /* FIXME: Values are hardcoded */
431 tcap.flags = 0x240c40;
432 tcap.flags_ext = 0x80601000;
433 tcap.ampdu_limit = 0xffff0000;
434 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530435 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530436 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530437 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530438
439 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
440
441 return ret;
442}
443
Sujith0d425a72010-05-17 12:01:16 +0530444static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
445 struct ieee80211_sta *sta,
446 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530447{
Sujithfb9987d2010-03-17 14:25:25 +0530448 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
449 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530450 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530451 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530452
Sujithea46e642010-06-02 15:53:50 +0530453 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530454
455 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
456 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530457 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530458 = (sband->bitrates[i].bitrate * 2) / 10;
459 j++;
460 }
461 }
Sujith0d425a72010-05-17 12:01:16 +0530462 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530463
464 if (sta->ht_cap.ht_supported) {
465 for (i = 0, j = 0; i < 77; i++) {
466 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530467 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530468 if (j == ATH_HTC_RATE_MAX)
469 break;
470 }
Sujith0d425a72010-05-17 12:01:16 +0530471 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530472
473 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200474 if (sta->ht_cap.mcs.rx_mask[1])
475 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530476 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
477 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530478 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530479 if (conf_is_ht40(&priv->hw->conf) &&
480 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530481 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530482 else if (conf_is_ht20(&priv->hw->conf) &&
483 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
484 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530485 }
486
Sujith0d425a72010-05-17 12:01:16 +0530487 trate->sta_index = ista->index;
488 trate->isnew = 1;
489 trate->capflags = cpu_to_be32(caps);
490}
Sujithfb9987d2010-03-17 14:25:25 +0530491
Sujith0d425a72010-05-17 12:01:16 +0530492static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
493 struct ath9k_htc_target_rate *trate)
494{
495 struct ath_common *common = ath9k_hw_common(priv->ah);
496 int ret;
497 u8 cmd_rsp;
498
499 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530500 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800501 ath_err(common,
502 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530503 }
504
Sujith0d425a72010-05-17 12:01:16 +0530505 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530506}
507
Sujith0d425a72010-05-17 12:01:16 +0530508static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
509 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530510{
Sujithfb9987d2010-03-17 14:25:25 +0530511 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530512 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530513 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530514
Sujith0d425a72010-05-17 12:01:16 +0530515 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
516 ath9k_htc_setup_rate(priv, sta, &trate);
517 ret = ath9k_htc_send_rate_cmd(priv, &trate);
518 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800519 ath_dbg(common, ATH_DBG_CONFIG,
520 "Updated target sta: %pM, rate caps: 0x%X\n",
521 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530522}
523
Sujith2c76ef82010-05-17 12:01:18 +0530524static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
525 struct ieee80211_vif *vif,
526 struct ieee80211_bss_conf *bss_conf)
527{
528 struct ath_common *common = ath9k_hw_common(priv->ah);
529 struct ath9k_htc_target_rate trate;
530 struct ieee80211_sta *sta;
531 int ret;
532
533 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
534
535 rcu_read_lock();
536 sta = ieee80211_find_sta(vif, bss_conf->bssid);
537 if (!sta) {
538 rcu_read_unlock();
539 return;
540 }
541 ath9k_htc_setup_rate(priv, sta, &trate);
542 rcu_read_unlock();
543
544 ret = ath9k_htc_send_rate_cmd(priv, &trate);
545 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800546 ath_dbg(common, ATH_DBG_CONFIG,
547 "Updated target sta: %pM, rate caps: 0x%X\n",
548 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530549}
550
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400551static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
552 struct ieee80211_vif *vif,
553 struct ieee80211_sta *sta,
554 enum ieee80211_ampdu_mlme_action action,
555 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530556{
557 struct ath_common *common = ath9k_hw_common(priv->ah);
558 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200559 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530560 int ret = 0;
561 u8 cmd_rsp;
562
Dan Carpenter0730d112010-05-08 18:24:02 +0200563 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530564 return -EINVAL;
565
Sujithfb9987d2010-03-17 14:25:25 +0530566 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530567 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530568
Sujithef98c3c2010-03-29 16:07:11 +0530569 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530570 aggr.tidno = tid & 0xf;
571 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530572
Sujithfb9987d2010-03-17 14:25:25 +0530573 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
574 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800575 ath_dbg(common, ATH_DBG_CONFIG,
576 "Unable to %s TX aggregation for (%pM, %d)\n",
577 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530578 else
Joe Perches226afe62010-12-02 19:12:37 -0800579 ath_dbg(common, ATH_DBG_CONFIG,
580 "%s TX aggregation for (%pM, %d)\n",
581 (aggr.aggr_enable) ? "Starting" : "Stopping",
582 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530583
584 spin_lock_bh(&priv->tx_lock);
585 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
586 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530587
588 return ret;
589}
590
Sujithfb9987d2010-03-17 14:25:25 +0530591/*********/
592/* DEBUG */
593/*********/
594
595#ifdef CONFIG_ATH9K_HTC_DEBUGFS
596
597static int ath9k_debugfs_open(struct inode *inode, struct file *file)
598{
599 file->private_data = inode->i_private;
600 return 0;
601}
602
603static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
604 size_t count, loff_t *ppos)
605{
Joe Perches57674302010-07-12 13:50:06 -0700606 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530607 struct ath9k_htc_target_stats cmd_rsp;
608 char buf[512];
609 unsigned int len = 0;
610 int ret = 0;
611
612 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
613
614 WMI_CMD(WMI_TGT_STATS_CMDID);
615 if (ret)
616 return -EINVAL;
617
618
619 len += snprintf(buf + len, sizeof(buf) - len,
620 "%19s : %10u\n", "TX Short Retries",
621 be32_to_cpu(cmd_rsp.tx_shortretry));
622 len += snprintf(buf + len, sizeof(buf) - len,
623 "%19s : %10u\n", "TX Long Retries",
624 be32_to_cpu(cmd_rsp.tx_longretry));
625 len += snprintf(buf + len, sizeof(buf) - len,
626 "%19s : %10u\n", "TX Xretries",
627 be32_to_cpu(cmd_rsp.tx_xretries));
628 len += snprintf(buf + len, sizeof(buf) - len,
629 "%19s : %10u\n", "TX Unaggr. Xretries",
630 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
631 len += snprintf(buf + len, sizeof(buf) - len,
632 "%19s : %10u\n", "TX Xretries (HT)",
633 be32_to_cpu(cmd_rsp.ht_tx_xretries));
634 len += snprintf(buf + len, sizeof(buf) - len,
635 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
636
Dan Carpenter97460102010-07-22 10:50:28 +0200637 if (len > sizeof(buf))
638 len = sizeof(buf);
639
Sujithfb9987d2010-03-17 14:25:25 +0530640 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
641}
642
643static const struct file_operations fops_tgt_stats = {
644 .read = read_file_tgt_stats,
645 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200646 .owner = THIS_MODULE,
647 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530648};
649
650static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
651 size_t count, loff_t *ppos)
652{
Joe Perches57674302010-07-12 13:50:06 -0700653 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530654 char buf[512];
655 unsigned int len = 0;
656
657 len += snprintf(buf + len, sizeof(buf) - len,
658 "%20s : %10u\n", "Buffers queued",
659 priv->debug.tx_stats.buf_queued);
660 len += snprintf(buf + len, sizeof(buf) - len,
661 "%20s : %10u\n", "Buffers completed",
662 priv->debug.tx_stats.buf_completed);
663 len += snprintf(buf + len, sizeof(buf) - len,
664 "%20s : %10u\n", "SKBs queued",
665 priv->debug.tx_stats.skb_queued);
666 len += snprintf(buf + len, sizeof(buf) - len,
667 "%20s : %10u\n", "SKBs completed",
668 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530669 len += snprintf(buf + len, sizeof(buf) - len,
670 "%20s : %10u\n", "SKBs dropped",
671 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530672
Sujith2edb4582010-05-14 11:18:54 +0530673 len += snprintf(buf + len, sizeof(buf) - len,
674 "%20s : %10u\n", "BE queued",
675 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
676 len += snprintf(buf + len, sizeof(buf) - len,
677 "%20s : %10u\n", "BK queued",
678 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
679 len += snprintf(buf + len, sizeof(buf) - len,
680 "%20s : %10u\n", "VI queued",
681 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
682 len += snprintf(buf + len, sizeof(buf) - len,
683 "%20s : %10u\n", "VO queued",
684 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
685
Dan Carpenter97460102010-07-22 10:50:28 +0200686 if (len > sizeof(buf))
687 len = sizeof(buf);
688
Sujithfb9987d2010-03-17 14:25:25 +0530689 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
690}
691
692static const struct file_operations fops_xmit = {
693 .read = read_file_xmit,
694 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200695 .owner = THIS_MODULE,
696 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530697};
698
699static ssize_t read_file_recv(struct file *file, char __user *user_buf,
700 size_t count, loff_t *ppos)
701{
Joe Perches57674302010-07-12 13:50:06 -0700702 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530703 char buf[512];
704 unsigned int len = 0;
705
706 len += snprintf(buf + len, sizeof(buf) - len,
707 "%20s : %10u\n", "SKBs allocated",
708 priv->debug.rx_stats.skb_allocated);
709 len += snprintf(buf + len, sizeof(buf) - len,
710 "%20s : %10u\n", "SKBs completed",
711 priv->debug.rx_stats.skb_completed);
712 len += snprintf(buf + len, sizeof(buf) - len,
713 "%20s : %10u\n", "SKBs Dropped",
714 priv->debug.rx_stats.skb_dropped);
715
Dan Carpenter97460102010-07-22 10:50:28 +0200716 if (len > sizeof(buf))
717 len = sizeof(buf);
718
Sujithfb9987d2010-03-17 14:25:25 +0530719 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
720}
721
722static const struct file_operations fops_recv = {
723 .read = read_file_recv,
724 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200725 .owner = THIS_MODULE,
726 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530727};
728
Sujithe1572c52010-03-24 13:42:13 +0530729int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530730{
731 struct ath_common *common = ath9k_hw_common(ah);
732 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
733
734 if (!ath9k_debugfs_root)
735 return -ENOENT;
736
737 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
738 ath9k_debugfs_root);
739 if (!priv->debug.debugfs_phy)
740 goto err;
741
742 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
743 priv->debug.debugfs_phy,
744 priv, &fops_tgt_stats);
745 if (!priv->debug.debugfs_tgt_stats)
746 goto err;
747
748
749 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
750 priv->debug.debugfs_phy,
751 priv, &fops_xmit);
752 if (!priv->debug.debugfs_xmit)
753 goto err;
754
755 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
756 priv->debug.debugfs_phy,
757 priv, &fops_recv);
758 if (!priv->debug.debugfs_recv)
759 goto err;
760
761 return 0;
762
763err:
Sujithe1572c52010-03-24 13:42:13 +0530764 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530765 return -ENOMEM;
766}
767
Sujithe1572c52010-03-24 13:42:13 +0530768void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530769{
770 struct ath_common *common = ath9k_hw_common(ah);
771 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
772
773 debugfs_remove(priv->debug.debugfs_recv);
774 debugfs_remove(priv->debug.debugfs_xmit);
775 debugfs_remove(priv->debug.debugfs_tgt_stats);
776 debugfs_remove(priv->debug.debugfs_phy);
777}
778
Sujithe1572c52010-03-24 13:42:13 +0530779int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530780{
781 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
782 if (!ath9k_debugfs_root)
783 return -ENOENT;
784
785 return 0;
786}
787
Sujithe1572c52010-03-24 13:42:13 +0530788void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530789{
790 debugfs_remove(ath9k_debugfs_root);
791 ath9k_debugfs_root = NULL;
792}
793
794#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
795
796/*******/
797/* ANI */
798/*******/
799
Sujith Manoharan73908672010-12-28 14:28:27 +0530800void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530801{
802 struct ath_common *common = ath9k_hw_common(priv->ah);
803 unsigned long timestamp = jiffies_to_msecs(jiffies);
804
805 common->ani.longcal_timer = timestamp;
806 common->ani.shortcal_timer = timestamp;
807 common->ani.checkani_timer = timestamp;
808
809 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
810 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
811}
812
813void ath9k_ani_work(struct work_struct *work)
814{
815 struct ath9k_htc_priv *priv =
816 container_of(work, struct ath9k_htc_priv,
817 ath9k_ani_work.work);
818 struct ath_hw *ah = priv->ah;
819 struct ath_common *common = ath9k_hw_common(ah);
820 bool longcal = false;
821 bool shortcal = false;
822 bool aniflag = false;
823 unsigned int timestamp = jiffies_to_msecs(jiffies);
824 u32 cal_interval, short_cal_interval;
825
826 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
827
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530828 /* Only calibrate if awake */
829 if (ah->power_mode != ATH9K_PM_AWAKE)
830 goto set_timer;
831
Sujithfb9987d2010-03-17 14:25:25 +0530832 /* Long calibration runs independently of short calibration. */
833 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
834 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800835 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530836 common->ani.longcal_timer = timestamp;
837 }
838
839 /* Short calibration applies only while caldone is false */
840 if (!common->ani.caldone) {
841 if ((timestamp - common->ani.shortcal_timer) >=
842 short_cal_interval) {
843 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800844 ath_dbg(common, ATH_DBG_ANI,
845 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530846 common->ani.shortcal_timer = timestamp;
847 common->ani.resetcal_timer = timestamp;
848 }
849 } else {
850 if ((timestamp - common->ani.resetcal_timer) >=
851 ATH_RESTART_CALINTERVAL) {
852 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
853 if (common->ani.caldone)
854 common->ani.resetcal_timer = timestamp;
855 }
856 }
857
858 /* Verify whether we must check ANI */
859 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
860 aniflag = true;
861 common->ani.checkani_timer = timestamp;
862 }
863
864 /* Skip all processing if there's nothing to do. */
865 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530866
867 ath9k_htc_ps_wakeup(priv);
868
Sujithfb9987d2010-03-17 14:25:25 +0530869 /* Call ANI routine if necessary */
870 if (aniflag)
871 ath9k_hw_ani_monitor(ah, ah->curchan);
872
873 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200874 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530875 common->ani.caldone =
876 ath9k_hw_calibrate(ah, ah->curchan,
877 common->rx_chainmask,
878 longcal);
879
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530880 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530881 }
882
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530883set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530884 /*
885 * Set timer interval based on previous results.
886 * The interval must be the shortest necessary to satisfy ANI,
887 * short calibration and long calibration.
888 */
889 cal_interval = ATH_LONG_CALINTERVAL;
890 if (priv->ah->config.enable_ani)
891 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
892 if (!common->ani.caldone)
893 cal_interval = min(cal_interval, (u32)short_cal_interval);
894
895 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
896 msecs_to_jiffies(cal_interval));
897}
898
Sujithfb9987d2010-03-17 14:25:25 +0530899/**********************/
900/* mac80211 Callbacks */
901/**********************/
902
903static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
904{
905 struct ieee80211_hdr *hdr;
906 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +0530907 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +0530908
909 hdr = (struct ieee80211_hdr *) skb->data;
910
911 /* Add the padding after the header if this is not already done */
912 padpos = ath9k_cmn_padpos(hdr->frame_control);
913 padsize = padpos & 3;
914 if (padsize && skb->len > padpos) {
915 if (skb_headroom(skb) < padsize)
916 return -1;
917 skb_push(skb, padsize);
918 memmove(skb->data, skb->data + padsize, padpos);
919 }
920
Sujith7757dfe2010-03-29 16:07:17 +0530921 ret = ath9k_htc_tx_start(priv, skb);
922 if (ret != 0) {
923 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -0800924 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
925 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +0530926 ieee80211_stop_queues(hw);
927 spin_lock_bh(&priv->tx_lock);
928 priv->tx_queues_stop = true;
929 spin_unlock_bh(&priv->tx_lock);
930 } else {
Joe Perches226afe62010-12-02 19:12:37 -0800931 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
932 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +0530933 }
Sujithfb9987d2010-03-17 14:25:25 +0530934 goto fail_tx;
935 }
936
937 return 0;
938
939fail_tx:
940 dev_kfree_skb_any(skb);
941 return 0;
942}
943
Sujith881ac6a2010-06-01 15:14:11 +0530944static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530945{
946 struct ath9k_htc_priv *priv = hw->priv;
947 struct ath_hw *ah = priv->ah;
948 struct ath_common *common = ath9k_hw_common(ah);
949 struct ieee80211_channel *curchan = hw->conf.channel;
950 struct ath9k_channel *init_channel;
951 int ret = 0;
952 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530953 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530954 u8 cmd_rsp;
955
Sujith881ac6a2010-06-01 15:14:11 +0530956 mutex_lock(&priv->mutex);
957
Joe Perches226afe62010-12-02 19:12:37 -0800958 ath_dbg(common, ATH_DBG_CONFIG,
959 "Starting driver with initial channel: %d MHz\n",
960 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530961
Sujith21d51302010-06-01 15:14:18 +0530962 /* Ensure that HW is awake before flushing RX */
963 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
964 WMI_CMD(WMI_FLUSH_RECV_CMDID);
965
Sujithfb9987d2010-03-17 14:25:25 +0530966 /* setup initial channel */
967 init_channel = ath9k_cmn_get_curchannel(hw, ah);
968
Sujithfb9987d2010-03-17 14:25:25 +0530969 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200970 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530971 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800972 ath_err(common,
973 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
974 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530975 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530976 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530977 }
978
979 ath_update_txpow(priv);
980
981 mode = ath9k_htc_get_curmode(priv, init_channel);
982 htc_mode = cpu_to_be16(mode);
983 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530984 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530985 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530986
987 ath9k_host_rx_init(priv);
988
989 priv->op_flags &= ~OP_INVALID;
990 htc_start(priv->htc);
991
Sujith7757dfe2010-03-29 16:07:17 +0530992 spin_lock_bh(&priv->tx_lock);
993 priv->tx_queues_stop = false;
994 spin_unlock_bh(&priv->tx_lock);
995
996 ieee80211_wake_queues(hw);
997
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530998 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
999 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1000 AR_STOMP_LOW_WLAN_WGHT);
1001 ath9k_hw_btcoex_enable(ah);
1002 ath_htc_resume_btcoex_work(priv);
1003 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301004 mutex_unlock(&priv->mutex);
1005
1006 return ret;
1007}
1008
Sujith881ac6a2010-06-01 15:14:11 +05301009static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301010{
1011 struct ath9k_htc_priv *priv = hw->priv;
1012 struct ath_hw *ah = priv->ah;
1013 struct ath_common *common = ath9k_hw_common(ah);
1014 int ret = 0;
1015 u8 cmd_rsp;
1016
Sujith Manoharan66e35472010-12-28 14:28:14 +05301017 /* Cancel all the running timers/work .. */
Sujith Manoharan73908672010-12-28 14:28:27 +05301018 cancel_work_sync(&priv->fatal_work);
Sujith Manoharan66e35472010-12-28 14:28:14 +05301019 cancel_work_sync(&priv->ps_work);
1020 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1021 ath9k_led_stop_brightness(priv);
1022
Sujith881ac6a2010-06-01 15:14:11 +05301023 mutex_lock(&priv->mutex);
1024
Sujithfb9987d2010-03-17 14:25:25 +05301025 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001026 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301027 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301028 return;
1029 }
1030
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301031 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301032 htc_stop(priv->htc);
1033 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1034 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1035 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301036 skb_queue_purge(&priv->tx_queue);
1037
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301038 /* Remove monitor interface here */
1039 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1040 if (ath9k_htc_remove_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001041 ath_err(common, "Unable to remove monitor interface\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301042 else
Joe Perches226afe62010-12-02 19:12:37 -08001043 ath_dbg(common, ATH_DBG_CONFIG,
1044 "Monitor interface removed\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301045 }
1046
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301047 if (ah->btcoex_hw.enabled) {
1048 ath9k_hw_btcoex_disable(ah);
1049 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1050 ath_htc_cancel_btcoex_work(priv);
1051 }
1052
Sujithe9201f02010-06-01 15:14:17 +05301053 ath9k_hw_phy_disable(ah);
1054 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301055 ath9k_htc_ps_restore(priv);
1056 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1057
Sujithfb9987d2010-03-17 14:25:25 +05301058 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301059
Joe Perches226afe62010-12-02 19:12:37 -08001060 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301061 mutex_unlock(&priv->mutex);
1062}
1063
Sujithfb9987d2010-03-17 14:25:25 +05301064static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1065 struct ieee80211_vif *vif)
1066{
1067 struct ath9k_htc_priv *priv = hw->priv;
1068 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1069 struct ath_common *common = ath9k_hw_common(priv->ah);
1070 struct ath9k_htc_target_vif hvif;
1071 int ret = 0;
1072 u8 cmd_rsp;
1073
1074 mutex_lock(&priv->mutex);
1075
1076 /* Only one interface for now */
1077 if (priv->nvifs > 0) {
1078 ret = -ENOBUFS;
1079 goto out;
1080 }
1081
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301082 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301083 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1084 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1085
1086 switch (vif->type) {
1087 case NL80211_IFTYPE_STATION:
1088 hvif.opmode = cpu_to_be32(HTC_M_STA);
1089 break;
1090 case NL80211_IFTYPE_ADHOC:
1091 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1092 break;
1093 default:
Joe Perches38002762010-12-02 19:12:36 -08001094 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301095 "Interface type %d not yet supported\n", vif->type);
1096 ret = -EOPNOTSUPP;
1097 goto out;
1098 }
1099
Joe Perches226afe62010-12-02 19:12:37 -08001100 ath_dbg(common, ATH_DBG_CONFIG,
1101 "Attach a VIF of type: %d\n", vif->type);
Sujithfb9987d2010-03-17 14:25:25 +05301102
1103 priv->ah->opmode = vif->type;
1104
1105 /* Index starts from zero on the target */
1106 avp->index = hvif.index = priv->nvifs;
1107 hvif.rtsthreshold = cpu_to_be16(2304);
1108 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1109 if (ret)
1110 goto out;
1111
1112 priv->nvifs++;
1113
1114 /*
1115 * We need a node in target to tx mgmt frames
1116 * before association.
1117 */
1118 ret = ath9k_htc_add_station(priv, vif, NULL);
1119 if (ret)
1120 goto out;
1121
1122 ret = ath9k_htc_update_cap_target(priv);
1123 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -08001124 ath_dbg(common, ATH_DBG_CONFIG,
1125 "Failed to update capability in target\n");
Sujithfb9987d2010-03-17 14:25:25 +05301126
1127 priv->vif = vif;
1128out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301129 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301130 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301131
Sujithfb9987d2010-03-17 14:25:25 +05301132 return ret;
1133}
1134
1135static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1136 struct ieee80211_vif *vif)
1137{
1138 struct ath9k_htc_priv *priv = hw->priv;
1139 struct ath_common *common = ath9k_hw_common(priv->ah);
1140 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1141 struct ath9k_htc_target_vif hvif;
1142 int ret = 0;
1143 u8 cmd_rsp;
1144
Joe Perches226afe62010-12-02 19:12:37 -08001145 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
Sujithfb9987d2010-03-17 14:25:25 +05301146
1147 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301148 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301149
1150 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1151 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1152 hvif.index = avp->index;
1153 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1154 priv->nvifs--;
1155
1156 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301157 priv->vif = NULL;
1158
Sujithcb551df2010-06-01 15:14:12 +05301159 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301160 mutex_unlock(&priv->mutex);
1161}
1162
1163static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1164{
1165 struct ath9k_htc_priv *priv = hw->priv;
1166 struct ath_common *common = ath9k_hw_common(priv->ah);
1167 struct ieee80211_conf *conf = &hw->conf;
1168
1169 mutex_lock(&priv->mutex);
1170
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301171 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1172 bool enable_radio = false;
1173 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1174
Sujith23367762010-06-01 15:14:16 +05301175 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301176 if (!idle && priv->ps_idle)
1177 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301178 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301179 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301180
1181 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001182 ath_dbg(common, ATH_DBG_CONFIG,
1183 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301184 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1185 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301186 }
1187 }
1188
Sujithfb9987d2010-03-17 14:25:25 +05301189 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1190 struct ieee80211_channel *curchan = hw->conf.channel;
1191 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301192
Joe Perches226afe62010-12-02 19:12:37 -08001193 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1194 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301195
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001196 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1197 hw->conf.channel,
1198 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301199
1200 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001201 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301202 mutex_unlock(&priv->mutex);
1203 return -EINVAL;
1204 }
1205
1206 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301207
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301208 if (changed & IEEE80211_CONF_CHANGE_PS) {
1209 if (conf->flags & IEEE80211_CONF_PS) {
1210 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1211 priv->ps_enabled = true;
1212 } else {
1213 priv->ps_enabled = false;
1214 cancel_work_sync(&priv->ps_work);
1215 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1216 }
1217 }
Sujithfb9987d2010-03-17 14:25:25 +05301218
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301219 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1220 priv->txpowlimit = 2 * conf->power_level;
1221 ath_update_txpow(priv);
1222 }
1223
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301224 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujithfb9987d2010-03-17 14:25:25 +05301225 if (conf->flags & IEEE80211_CONF_MONITOR) {
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301226 if (ath9k_htc_add_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001227 ath_err(common, "Failed to set monitor mode\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301228 else
Joe Perches226afe62010-12-02 19:12:37 -08001229 ath_dbg(common, ATH_DBG_CONFIG,
1230 "HW opmode set to Monitor mode\n");
Sujithfb9987d2010-03-17 14:25:25 +05301231 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301232 }
Sujithfb9987d2010-03-17 14:25:25 +05301233
Sujith23367762010-06-01 15:14:16 +05301234 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1235 mutex_lock(&priv->htc_pm_lock);
1236 if (!priv->ps_idle) {
1237 mutex_unlock(&priv->htc_pm_lock);
1238 goto out;
1239 }
1240 mutex_unlock(&priv->htc_pm_lock);
1241
Joe Perches226afe62010-12-02 19:12:37 -08001242 ath_dbg(common, ATH_DBG_CONFIG,
1243 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301244 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301245 }
1246
Sujith23367762010-06-01 15:14:16 +05301247out:
Sujithfb9987d2010-03-17 14:25:25 +05301248 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301249 return 0;
1250}
1251
1252#define SUPPORTED_FILTERS \
1253 (FIF_PROMISC_IN_BSS | \
1254 FIF_ALLMULTI | \
1255 FIF_CONTROL | \
1256 FIF_PSPOLL | \
1257 FIF_OTHER_BSS | \
1258 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301259 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301260 FIF_FCSFAIL)
1261
1262static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1263 unsigned int changed_flags,
1264 unsigned int *total_flags,
1265 u64 multicast)
1266{
1267 struct ath9k_htc_priv *priv = hw->priv;
1268 u32 rfilt;
1269
1270 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301271 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301272
Sujithfb9987d2010-03-17 14:25:25 +05301273 changed_flags &= SUPPORTED_FILTERS;
1274 *total_flags &= SUPPORTED_FILTERS;
1275
1276 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301277 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301278 ath9k_hw_setrxfilter(priv->ah, rfilt);
1279
Joe Perches226afe62010-12-02 19:12:37 -08001280 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1281 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301282
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301283 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301284 mutex_unlock(&priv->mutex);
1285}
1286
Sujithabd984e2010-05-18 15:26:04 +05301287static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1288 struct ieee80211_vif *vif,
1289 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301290{
1291 struct ath9k_htc_priv *priv = hw->priv;
1292 int ret;
1293
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301294 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301295 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301296 ret = ath9k_htc_add_station(priv, vif, sta);
1297 if (!ret)
1298 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301299 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301300 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301301
1302 return ret;
1303}
1304
1305static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1306 struct ieee80211_vif *vif,
1307 struct ieee80211_sta *sta)
1308{
1309 struct ath9k_htc_priv *priv = hw->priv;
1310 int ret;
1311
1312 mutex_lock(&priv->mutex);
1313 ath9k_htc_ps_wakeup(priv);
1314 ret = ath9k_htc_remove_station(priv, vif, sta);
1315 ath9k_htc_ps_restore(priv);
1316 mutex_unlock(&priv->mutex);
1317
1318 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301319}
1320
1321static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1322 const struct ieee80211_tx_queue_params *params)
1323{
1324 struct ath9k_htc_priv *priv = hw->priv;
1325 struct ath_common *common = ath9k_hw_common(priv->ah);
1326 struct ath9k_tx_queue_info qi;
1327 int ret = 0, qnum;
1328
1329 if (queue >= WME_NUM_AC)
1330 return 0;
1331
1332 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301333 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301334
1335 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1336
1337 qi.tqi_aifs = params->aifs;
1338 qi.tqi_cwmin = params->cw_min;
1339 qi.tqi_cwmax = params->cw_max;
1340 qi.tqi_burstTime = params->txop;
1341
1342 qnum = get_hw_qnum(queue, priv->hwq_map);
1343
Joe Perches226afe62010-12-02 19:12:37 -08001344 ath_dbg(common, ATH_DBG_CONFIG,
1345 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1346 queue, qnum, params->aifs, params->cw_min,
1347 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301348
Sujithe1572c52010-03-24 13:42:13 +05301349 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301350 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001351 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301352 goto out;
1353 }
Sujithfb9987d2010-03-17 14:25:25 +05301354
Sujith764580f2010-06-01 15:14:19 +05301355 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001356 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301357 ath9k_htc_beaconq_config(priv);
1358out:
Sujithcb551df2010-06-01 15:14:12 +05301359 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301360 mutex_unlock(&priv->mutex);
1361
1362 return ret;
1363}
1364
1365static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1366 enum set_key_cmd cmd,
1367 struct ieee80211_vif *vif,
1368 struct ieee80211_sta *sta,
1369 struct ieee80211_key_conf *key)
1370{
1371 struct ath9k_htc_priv *priv = hw->priv;
1372 struct ath_common *common = ath9k_hw_common(priv->ah);
1373 int ret = 0;
1374
Sujithe1572c52010-03-24 13:42:13 +05301375 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301376 return -ENOSPC;
1377
1378 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001379 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301380 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301381
1382 switch (cmd) {
1383 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001384 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301385 if (ret >= 0) {
1386 key->hw_key_idx = ret;
1387 /* push IV and Michael MIC generation to stack */
1388 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001389 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301390 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001391 if (priv->ah->sw_mgmt_crypto &&
1392 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301393 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1394 ret = 0;
1395 }
1396 break;
1397 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001398 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301399 break;
1400 default:
1401 ret = -EINVAL;
1402 }
1403
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301404 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301405 mutex_unlock(&priv->mutex);
1406
1407 return ret;
1408}
1409
1410static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1411 struct ieee80211_vif *vif,
1412 struct ieee80211_bss_conf *bss_conf,
1413 u32 changed)
1414{
1415 struct ath9k_htc_priv *priv = hw->priv;
1416 struct ath_hw *ah = priv->ah;
1417 struct ath_common *common = ath9k_hw_common(ah);
1418
1419 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301420 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301421
1422 if (changed & BSS_CHANGED_ASSOC) {
1423 common->curaid = bss_conf->assoc ?
1424 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001425 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301426 bss_conf->assoc);
1427
1428 if (bss_conf->assoc) {
1429 priv->op_flags |= OP_ASSOCIATED;
1430 ath_start_ani(priv);
1431 } else {
1432 priv->op_flags &= ~OP_ASSOCIATED;
1433 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1434 }
1435 }
1436
1437 if (changed & BSS_CHANGED_BSSID) {
1438 /* Set BSSID */
1439 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1440 ath9k_hw_write_associd(ah);
1441
Joe Perches226afe62010-12-02 19:12:37 -08001442 ath_dbg(common, ATH_DBG_CONFIG,
1443 "BSSID: %pM aid: 0x%x\n",
1444 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301445 }
1446
1447 if ((changed & BSS_CHANGED_BEACON_INT) ||
1448 (changed & BSS_CHANGED_BEACON) ||
1449 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1450 bss_conf->enable_beacon)) {
1451 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301452 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301453 }
1454
Sujithfb9987d2010-03-17 14:25:25 +05301455 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1456 !bss_conf->enable_beacon) {
1457 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301458 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301459 }
1460
1461 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Joe Perches226afe62010-12-02 19:12:37 -08001462 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1463 bss_conf->use_short_preamble);
Sujithfb9987d2010-03-17 14:25:25 +05301464 if (bss_conf->use_short_preamble)
1465 priv->op_flags |= OP_PREAMBLE_SHORT;
1466 else
1467 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1468 }
1469
1470 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Joe Perches226afe62010-12-02 19:12:37 -08001471 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1472 bss_conf->use_cts_prot);
Sujithfb9987d2010-03-17 14:25:25 +05301473 if (bss_conf->use_cts_prot &&
1474 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1475 priv->op_flags |= OP_PROTECT_ENABLE;
1476 else
1477 priv->op_flags &= ~OP_PROTECT_ENABLE;
1478 }
1479
1480 if (changed & BSS_CHANGED_ERP_SLOT) {
1481 if (bss_conf->use_short_slot)
1482 ah->slottime = 9;
1483 else
1484 ah->slottime = 20;
1485
1486 ath9k_hw_init_global_settings(ah);
1487 }
1488
Sujith2c76ef82010-05-17 12:01:18 +05301489 if (changed & BSS_CHANGED_HT)
1490 ath9k_htc_update_rate(priv, vif, bss_conf);
1491
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301492 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301493 mutex_unlock(&priv->mutex);
1494}
1495
1496static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1497{
1498 struct ath9k_htc_priv *priv = hw->priv;
1499 u64 tsf;
1500
1501 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301502 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301503 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301504 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301505 mutex_unlock(&priv->mutex);
1506
1507 return tsf;
1508}
1509
1510static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1511{
1512 struct ath9k_htc_priv *priv = hw->priv;
1513
1514 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301515 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301516 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301517 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301518 mutex_unlock(&priv->mutex);
1519}
1520
1521static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1522{
1523 struct ath9k_htc_priv *priv = hw->priv;
1524
1525 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301526 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301527 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301528 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301529 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301530}
1531
1532static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1533 struct ieee80211_vif *vif,
1534 enum ieee80211_ampdu_mlme_action action,
1535 struct ieee80211_sta *sta,
1536 u16 tid, u16 *ssn)
1537{
1538 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301539 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301540 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301541
1542 switch (action) {
1543 case IEEE80211_AMPDU_RX_START:
1544 break;
1545 case IEEE80211_AMPDU_RX_STOP:
1546 break;
1547 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301548 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1549 if (!ret)
1550 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1551 break;
Sujithfb9987d2010-03-17 14:25:25 +05301552 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301553 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1554 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301555 break;
1556 case IEEE80211_AMPDU_TX_OPERATIONAL:
1557 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301558 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301559 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301560 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301561 break;
1562 default:
Joe Perches38002762010-12-02 19:12:36 -08001563 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301564 }
1565
Sujithd7ca2132010-06-15 10:24:37 +05301566 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301567}
1568
1569static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1570{
1571 struct ath9k_htc_priv *priv = hw->priv;
1572
1573 mutex_lock(&priv->mutex);
1574 spin_lock_bh(&priv->beacon_lock);
1575 priv->op_flags |= OP_SCANNING;
1576 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301577 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301578 if (priv->op_flags & OP_ASSOCIATED)
1579 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301580 mutex_unlock(&priv->mutex);
1581}
1582
1583static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1584{
1585 struct ath9k_htc_priv *priv = hw->priv;
1586
1587 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301588 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301589 spin_lock_bh(&priv->beacon_lock);
1590 priv->op_flags &= ~OP_SCANNING;
1591 spin_unlock_bh(&priv->beacon_lock);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301592 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301593 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301594 ath_start_ani(priv);
1595 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301596 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301597 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301598}
1599
1600static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1601{
1602 return 0;
1603}
1604
1605static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1606 u8 coverage_class)
1607{
1608 struct ath9k_htc_priv *priv = hw->priv;
1609
1610 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301611 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301612 priv->ah->coverage_class = coverage_class;
1613 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301614 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301615 mutex_unlock(&priv->mutex);
1616}
1617
1618struct ieee80211_ops ath9k_htc_ops = {
1619 .tx = ath9k_htc_tx,
1620 .start = ath9k_htc_start,
1621 .stop = ath9k_htc_stop,
1622 .add_interface = ath9k_htc_add_interface,
1623 .remove_interface = ath9k_htc_remove_interface,
1624 .config = ath9k_htc_config,
1625 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301626 .sta_add = ath9k_htc_sta_add,
1627 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301628 .conf_tx = ath9k_htc_conf_tx,
1629 .bss_info_changed = ath9k_htc_bss_info_changed,
1630 .set_key = ath9k_htc_set_key,
1631 .get_tsf = ath9k_htc_get_tsf,
1632 .set_tsf = ath9k_htc_set_tsf,
1633 .reset_tsf = ath9k_htc_reset_tsf,
1634 .ampdu_action = ath9k_htc_ampdu_action,
1635 .sw_scan_start = ath9k_htc_sw_scan_start,
1636 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1637 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1638 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1639 .set_coverage_class = ath9k_htc_set_coverage_class,
1640};