blob: 5e318cb662c665deda93364e3eebc37c00c39fad [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
27static void ath_update_txpow(struct ath9k_htc_priv *priv)
28{
29 struct ath_hw *ah = priv->ah;
Sujithfb9987d2010-03-17 14:25:25 +053030
31 if (priv->curtxpow != priv->txpowlimit) {
32 ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit);
33 /* 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
Vivek Natarajanbde748a2010-04-05 14:48:05 +053066static bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
67 enum ath9k_power_mode mode)
68{
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
Sujithfb9987d2010-03-17 14:25:25 +0530119static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
120 struct ieee80211_hw *hw,
121 struct ath9k_channel *hchan)
122{
123 struct ath_hw *ah = priv->ah;
124 struct ath_common *common = ath9k_hw_common(ah);
125 struct ieee80211_conf *conf = &common->hw->conf;
126 bool fastcc = true;
127 struct ieee80211_channel *channel = hw->conf.channel;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200128 struct ath9k_hw_cal_data *caldata;
Sujithfb9987d2010-03-17 14:25:25 +0530129 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530130 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530131 u8 cmd_rsp;
132 int ret;
133
134 if (priv->op_flags & OP_INVALID)
135 return -EIO;
136
137 if (priv->op_flags & OP_FULL_RESET)
138 fastcc = false;
139
140 /* Fiddle around with fastcc later on, for now just use full reset */
141 fastcc = false;
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530142 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530143 htc_stop(priv->htc);
144 WMI_CMD(WMI_DISABLE_INTR_CMDID);
145 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
146 WMI_CMD(WMI_STOP_RECV_CMDID);
147
148 ath_print(common, ATH_DBG_CONFIG,
149 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n",
150 priv->ah->curchan->channel,
151 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf));
152
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200153 caldata = &priv->caldata[channel->hw_value];
154 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530155 if (ret) {
156 ath_print(common, ATH_DBG_FATAL,
157 "Unable to reset channel (%u Mhz) "
158 "reset status %d\n", channel->center_freq, ret);
159 goto err;
160 }
161
162 ath_update_txpow(priv);
163
164 WMI_CMD(WMI_START_RECV_CMDID);
165 if (ret)
166 goto err;
167
168 ath9k_host_rx_init(priv);
169
170 mode = ath9k_htc_get_curmode(priv, hchan);
171 htc_mode = cpu_to_be16(mode);
172 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
173 if (ret)
174 goto err;
175
176 WMI_CMD(WMI_ENABLE_INTR_CMDID);
177 if (ret)
178 goto err;
179
180 htc_start(priv->htc);
181
182 priv->op_flags &= ~OP_FULL_RESET;
183err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530184 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530185 return ret;
186}
187
188static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
189{
190 struct ath_common *common = ath9k_hw_common(priv->ah);
191 struct ath9k_htc_target_vif hvif;
192 int ret = 0;
193 u8 cmd_rsp;
194
195 if (priv->nvifs > 0)
196 return -ENOBUFS;
197
198 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
199 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
200
201 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
202 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
203 hvif.index = priv->nvifs;
204
205 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
206 if (ret)
207 return ret;
208
209 priv->nvifs++;
210 return 0;
211}
212
213static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
214{
215 struct ath_common *common = ath9k_hw_common(priv->ah);
216 struct ath9k_htc_target_vif hvif;
217 int ret = 0;
218 u8 cmd_rsp;
219
220 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
221 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
222 hvif.index = 0; /* Should do for now */
223 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
224 priv->nvifs--;
225
226 return ret;
227}
228
229static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
230 struct ieee80211_vif *vif,
231 struct ieee80211_sta *sta)
232{
233 struct ath_common *common = ath9k_hw_common(priv->ah);
234 struct ath9k_htc_target_sta tsta;
235 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
236 struct ath9k_htc_sta *ista;
237 int ret;
238 u8 cmd_rsp;
239
240 if (priv->nstations >= ATH9K_HTC_MAX_STA)
241 return -ENOBUFS;
242
243 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
244
245 if (sta) {
246 ista = (struct ath9k_htc_sta *) sta->drv_priv;
247 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
248 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
249 tsta.associd = common->curaid;
250 tsta.is_vif_sta = 0;
251 tsta.valid = true;
252 ista->index = priv->nstations;
253 } else {
254 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
255 tsta.is_vif_sta = 1;
256 }
257
258 tsta.sta_index = priv->nstations;
259 tsta.vif_index = avp->index;
260 tsta.maxampdu = 0xffff;
261 if (sta && sta->ht_cap.ht_supported)
262 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
263
264 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
265 if (ret) {
266 if (sta)
267 ath_print(common, ATH_DBG_FATAL,
268 "Unable to add station entry for: %pM\n", sta->addr);
269 return ret;
270 }
271
272 if (sta)
273 ath_print(common, ATH_DBG_CONFIG,
274 "Added a station entry for: %pM (idx: %d)\n",
275 sta->addr, tsta.sta_index);
276
277 priv->nstations++;
278 return 0;
279}
280
281static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
282 struct ieee80211_vif *vif,
283 struct ieee80211_sta *sta)
284{
285 struct ath_common *common = ath9k_hw_common(priv->ah);
286 struct ath9k_htc_sta *ista;
287 int ret;
288 u8 cmd_rsp, sta_idx;
289
290 if (sta) {
291 ista = (struct ath9k_htc_sta *) sta->drv_priv;
292 sta_idx = ista->index;
293 } else {
294 sta_idx = 0;
295 }
296
297 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
298 if (ret) {
299 if (sta)
300 ath_print(common, ATH_DBG_FATAL,
301 "Unable to remove station entry for: %pM\n",
302 sta->addr);
303 return ret;
304 }
305
306 if (sta)
307 ath_print(common, ATH_DBG_CONFIG,
308 "Removed a station entry for: %pM (idx: %d)\n",
309 sta->addr, sta_idx);
310
311 priv->nstations--;
312 return 0;
313}
314
315static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
316{
317 struct ath9k_htc_cap_target tcap;
318 int ret;
319 u8 cmd_rsp;
320
321 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
322
323 /* FIXME: Values are hardcoded */
324 tcap.flags = 0x240c40;
325 tcap.flags_ext = 0x80601000;
326 tcap.ampdu_limit = 0xffff0000;
327 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530328 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530329 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530330 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530331
332 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
333
334 return ret;
335}
336
Sujith0d425a72010-05-17 12:01:16 +0530337static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
338 struct ieee80211_sta *sta,
339 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530340{
Sujithfb9987d2010-03-17 14:25:25 +0530341 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
342 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530343 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530344 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530345
Sujithea46e642010-06-02 15:53:50 +0530346 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530347
348 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
349 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530350 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530351 = (sband->bitrates[i].bitrate * 2) / 10;
352 j++;
353 }
354 }
Sujith0d425a72010-05-17 12:01:16 +0530355 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530356
357 if (sta->ht_cap.ht_supported) {
358 for (i = 0, j = 0; i < 77; i++) {
359 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530360 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530361 if (j == ATH_HTC_RATE_MAX)
362 break;
363 }
Sujith0d425a72010-05-17 12:01:16 +0530364 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530365
366 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200367 if (sta->ht_cap.mcs.rx_mask[1])
368 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530369 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
370 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530371 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530372 if (conf_is_ht40(&priv->hw->conf) &&
373 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530374 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530375 else if (conf_is_ht20(&priv->hw->conf) &&
376 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
377 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530378 }
379
Sujith0d425a72010-05-17 12:01:16 +0530380 trate->sta_index = ista->index;
381 trate->isnew = 1;
382 trate->capflags = cpu_to_be32(caps);
383}
Sujithfb9987d2010-03-17 14:25:25 +0530384
Sujith0d425a72010-05-17 12:01:16 +0530385static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
386 struct ath9k_htc_target_rate *trate)
387{
388 struct ath_common *common = ath9k_hw_common(priv->ah);
389 int ret;
390 u8 cmd_rsp;
391
392 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530393 if (ret) {
394 ath_print(common, ATH_DBG_FATAL,
395 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530396 }
397
Sujith0d425a72010-05-17 12:01:16 +0530398 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530399}
400
Sujith0d425a72010-05-17 12:01:16 +0530401static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
402 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530403{
Sujithfb9987d2010-03-17 14:25:25 +0530404 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530405 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530406 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530407
Sujith0d425a72010-05-17 12:01:16 +0530408 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
409 ath9k_htc_setup_rate(priv, sta, &trate);
410 ret = ath9k_htc_send_rate_cmd(priv, &trate);
411 if (!ret)
412 ath_print(common, ATH_DBG_CONFIG,
413 "Updated target sta: %pM, rate caps: 0x%X\n",
414 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530415}
416
Sujith2c76ef82010-05-17 12:01:18 +0530417static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
418 struct ieee80211_vif *vif,
419 struct ieee80211_bss_conf *bss_conf)
420{
421 struct ath_common *common = ath9k_hw_common(priv->ah);
422 struct ath9k_htc_target_rate trate;
423 struct ieee80211_sta *sta;
424 int ret;
425
426 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
427
428 rcu_read_lock();
429 sta = ieee80211_find_sta(vif, bss_conf->bssid);
430 if (!sta) {
431 rcu_read_unlock();
432 return;
433 }
434 ath9k_htc_setup_rate(priv, sta, &trate);
435 rcu_read_unlock();
436
437 ret = ath9k_htc_send_rate_cmd(priv, &trate);
438 if (!ret)
439 ath_print(common, ATH_DBG_CONFIG,
440 "Updated target sta: %pM, rate caps: 0x%X\n",
441 bss_conf->bssid, be32_to_cpu(trate.capflags));
442}
443
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400444static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
445 struct ieee80211_vif *vif,
446 struct ieee80211_sta *sta,
447 enum ieee80211_ampdu_mlme_action action,
448 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530449{
450 struct ath_common *common = ath9k_hw_common(priv->ah);
451 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200452 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530453 int ret = 0;
454 u8 cmd_rsp;
455
Dan Carpenter0730d112010-05-08 18:24:02 +0200456 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530457 return -EINVAL;
458
Sujithfb9987d2010-03-17 14:25:25 +0530459 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530460 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530461
Sujithef98c3c2010-03-29 16:07:11 +0530462 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530463 aggr.tidno = tid & 0xf;
464 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530465
Sujithfb9987d2010-03-17 14:25:25 +0530466 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
467 if (ret)
468 ath_print(common, ATH_DBG_CONFIG,
469 "Unable to %s TX aggregation for (%pM, %d)\n",
Sujithd7ca2132010-06-15 10:24:37 +0530470 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530471 else
472 ath_print(common, ATH_DBG_CONFIG,
Sujithd7ca2132010-06-15 10:24:37 +0530473 "%s TX aggregation for (%pM, %d)\n",
474 (aggr.aggr_enable) ? "Starting" : "Stopping",
475 sta->addr, tid);
476
477 spin_lock_bh(&priv->tx_lock);
478 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
479 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530480
481 return ret;
482}
483
Sujithfb9987d2010-03-17 14:25:25 +0530484/*********/
485/* DEBUG */
486/*********/
487
488#ifdef CONFIG_ATH9K_HTC_DEBUGFS
489
490static int ath9k_debugfs_open(struct inode *inode, struct file *file)
491{
492 file->private_data = inode->i_private;
493 return 0;
494}
495
496static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
497 size_t count, loff_t *ppos)
498{
Joe Perches57674302010-07-12 13:50:06 -0700499 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530500 struct ath9k_htc_target_stats cmd_rsp;
501 char buf[512];
502 unsigned int len = 0;
503 int ret = 0;
504
505 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
506
507 WMI_CMD(WMI_TGT_STATS_CMDID);
508 if (ret)
509 return -EINVAL;
510
511
512 len += snprintf(buf + len, sizeof(buf) - len,
513 "%19s : %10u\n", "TX Short Retries",
514 be32_to_cpu(cmd_rsp.tx_shortretry));
515 len += snprintf(buf + len, sizeof(buf) - len,
516 "%19s : %10u\n", "TX Long Retries",
517 be32_to_cpu(cmd_rsp.tx_longretry));
518 len += snprintf(buf + len, sizeof(buf) - len,
519 "%19s : %10u\n", "TX Xretries",
520 be32_to_cpu(cmd_rsp.tx_xretries));
521 len += snprintf(buf + len, sizeof(buf) - len,
522 "%19s : %10u\n", "TX Unaggr. Xretries",
523 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
524 len += snprintf(buf + len, sizeof(buf) - len,
525 "%19s : %10u\n", "TX Xretries (HT)",
526 be32_to_cpu(cmd_rsp.ht_tx_xretries));
527 len += snprintf(buf + len, sizeof(buf) - len,
528 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
529
Dan Carpenter97460102010-07-22 10:50:28 +0200530 if (len > sizeof(buf))
531 len = sizeof(buf);
532
Sujithfb9987d2010-03-17 14:25:25 +0530533 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
534}
535
536static const struct file_operations fops_tgt_stats = {
537 .read = read_file_tgt_stats,
538 .open = ath9k_debugfs_open,
539 .owner = THIS_MODULE
540};
541
542static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
543 size_t count, loff_t *ppos)
544{
Joe Perches57674302010-07-12 13:50:06 -0700545 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530546 char buf[512];
547 unsigned int len = 0;
548
549 len += snprintf(buf + len, sizeof(buf) - len,
550 "%20s : %10u\n", "Buffers queued",
551 priv->debug.tx_stats.buf_queued);
552 len += snprintf(buf + len, sizeof(buf) - len,
553 "%20s : %10u\n", "Buffers completed",
554 priv->debug.tx_stats.buf_completed);
555 len += snprintf(buf + len, sizeof(buf) - len,
556 "%20s : %10u\n", "SKBs queued",
557 priv->debug.tx_stats.skb_queued);
558 len += snprintf(buf + len, sizeof(buf) - len,
559 "%20s : %10u\n", "SKBs completed",
560 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530561 len += snprintf(buf + len, sizeof(buf) - len,
562 "%20s : %10u\n", "SKBs dropped",
563 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530564
Sujith2edb4582010-05-14 11:18:54 +0530565 len += snprintf(buf + len, sizeof(buf) - len,
566 "%20s : %10u\n", "BE queued",
567 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
568 len += snprintf(buf + len, sizeof(buf) - len,
569 "%20s : %10u\n", "BK queued",
570 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
571 len += snprintf(buf + len, sizeof(buf) - len,
572 "%20s : %10u\n", "VI queued",
573 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
574 len += snprintf(buf + len, sizeof(buf) - len,
575 "%20s : %10u\n", "VO queued",
576 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
577
Dan Carpenter97460102010-07-22 10:50:28 +0200578 if (len > sizeof(buf))
579 len = sizeof(buf);
580
Sujithfb9987d2010-03-17 14:25:25 +0530581 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
582}
583
584static const struct file_operations fops_xmit = {
585 .read = read_file_xmit,
586 .open = ath9k_debugfs_open,
587 .owner = THIS_MODULE
588};
589
590static ssize_t read_file_recv(struct file *file, char __user *user_buf,
591 size_t count, loff_t *ppos)
592{
Joe Perches57674302010-07-12 13:50:06 -0700593 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530594 char buf[512];
595 unsigned int len = 0;
596
597 len += snprintf(buf + len, sizeof(buf) - len,
598 "%20s : %10u\n", "SKBs allocated",
599 priv->debug.rx_stats.skb_allocated);
600 len += snprintf(buf + len, sizeof(buf) - len,
601 "%20s : %10u\n", "SKBs completed",
602 priv->debug.rx_stats.skb_completed);
603 len += snprintf(buf + len, sizeof(buf) - len,
604 "%20s : %10u\n", "SKBs Dropped",
605 priv->debug.rx_stats.skb_dropped);
606
Dan Carpenter97460102010-07-22 10:50:28 +0200607 if (len > sizeof(buf))
608 len = sizeof(buf);
609
Sujithfb9987d2010-03-17 14:25:25 +0530610 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
611}
612
613static const struct file_operations fops_recv = {
614 .read = read_file_recv,
615 .open = ath9k_debugfs_open,
616 .owner = THIS_MODULE
617};
618
Sujithe1572c52010-03-24 13:42:13 +0530619int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530620{
621 struct ath_common *common = ath9k_hw_common(ah);
622 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
623
624 if (!ath9k_debugfs_root)
625 return -ENOENT;
626
627 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
628 ath9k_debugfs_root);
629 if (!priv->debug.debugfs_phy)
630 goto err;
631
632 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
633 priv->debug.debugfs_phy,
634 priv, &fops_tgt_stats);
635 if (!priv->debug.debugfs_tgt_stats)
636 goto err;
637
638
639 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
640 priv->debug.debugfs_phy,
641 priv, &fops_xmit);
642 if (!priv->debug.debugfs_xmit)
643 goto err;
644
645 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
646 priv->debug.debugfs_phy,
647 priv, &fops_recv);
648 if (!priv->debug.debugfs_recv)
649 goto err;
650
651 return 0;
652
653err:
Sujithe1572c52010-03-24 13:42:13 +0530654 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530655 return -ENOMEM;
656}
657
Sujithe1572c52010-03-24 13:42:13 +0530658void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530659{
660 struct ath_common *common = ath9k_hw_common(ah);
661 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
662
663 debugfs_remove(priv->debug.debugfs_recv);
664 debugfs_remove(priv->debug.debugfs_xmit);
665 debugfs_remove(priv->debug.debugfs_tgt_stats);
666 debugfs_remove(priv->debug.debugfs_phy);
667}
668
Sujithe1572c52010-03-24 13:42:13 +0530669int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530670{
671 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
672 if (!ath9k_debugfs_root)
673 return -ENOENT;
674
675 return 0;
676}
677
Sujithe1572c52010-03-24 13:42:13 +0530678void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530679{
680 debugfs_remove(ath9k_debugfs_root);
681 ath9k_debugfs_root = NULL;
682}
683
684#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
685
686/*******/
687/* ANI */
688/*******/
689
690static void ath_start_ani(struct ath9k_htc_priv *priv)
691{
692 struct ath_common *common = ath9k_hw_common(priv->ah);
693 unsigned long timestamp = jiffies_to_msecs(jiffies);
694
695 common->ani.longcal_timer = timestamp;
696 common->ani.shortcal_timer = timestamp;
697 common->ani.checkani_timer = timestamp;
698
699 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
700 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
701}
702
703void ath9k_ani_work(struct work_struct *work)
704{
705 struct ath9k_htc_priv *priv =
706 container_of(work, struct ath9k_htc_priv,
707 ath9k_ani_work.work);
708 struct ath_hw *ah = priv->ah;
709 struct ath_common *common = ath9k_hw_common(ah);
710 bool longcal = false;
711 bool shortcal = false;
712 bool aniflag = false;
713 unsigned int timestamp = jiffies_to_msecs(jiffies);
714 u32 cal_interval, short_cal_interval;
715
716 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
717
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530718 /* Only calibrate if awake */
719 if (ah->power_mode != ATH9K_PM_AWAKE)
720 goto set_timer;
721
Sujithfb9987d2010-03-17 14:25:25 +0530722 /* Long calibration runs independently of short calibration. */
723 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
724 longcal = true;
725 ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
726 common->ani.longcal_timer = timestamp;
727 }
728
729 /* Short calibration applies only while caldone is false */
730 if (!common->ani.caldone) {
731 if ((timestamp - common->ani.shortcal_timer) >=
732 short_cal_interval) {
733 shortcal = true;
734 ath_print(common, ATH_DBG_ANI,
735 "shortcal @%lu\n", jiffies);
736 common->ani.shortcal_timer = timestamp;
737 common->ani.resetcal_timer = timestamp;
738 }
739 } else {
740 if ((timestamp - common->ani.resetcal_timer) >=
741 ATH_RESTART_CALINTERVAL) {
742 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
743 if (common->ani.caldone)
744 common->ani.resetcal_timer = timestamp;
745 }
746 }
747
748 /* Verify whether we must check ANI */
749 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
750 aniflag = true;
751 common->ani.checkani_timer = timestamp;
752 }
753
754 /* Skip all processing if there's nothing to do. */
755 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530756
757 ath9k_htc_ps_wakeup(priv);
758
Sujithfb9987d2010-03-17 14:25:25 +0530759 /* Call ANI routine if necessary */
760 if (aniflag)
761 ath9k_hw_ani_monitor(ah, ah->curchan);
762
763 /* Perform calibration if necessary */
764 if (longcal || shortcal) {
765 common->ani.caldone =
766 ath9k_hw_calibrate(ah, ah->curchan,
767 common->rx_chainmask,
768 longcal);
769
770 if (longcal)
771 common->ani.noise_floor =
772 ath9k_hw_getchan_noise(ah, ah->curchan);
773
774 ath_print(common, ATH_DBG_ANI,
775 " calibrate chan %u/%x nf: %d\n",
776 ah->curchan->channel,
777 ah->curchan->channelFlags,
778 common->ani.noise_floor);
779 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530780
781 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530782 }
783
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530784set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530785 /*
786 * Set timer interval based on previous results.
787 * The interval must be the shortest necessary to satisfy ANI,
788 * short calibration and long calibration.
789 */
790 cal_interval = ATH_LONG_CALINTERVAL;
791 if (priv->ah->config.enable_ani)
792 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
793 if (!common->ani.caldone)
794 cal_interval = min(cal_interval, (u32)short_cal_interval);
795
796 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
797 msecs_to_jiffies(cal_interval));
798}
799
800/*******/
801/* LED */
802/*******/
803
804static void ath9k_led_blink_work(struct work_struct *work)
805{
806 struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
807 ath9k_led_blink_work.work);
808
809 if (!(priv->op_flags & OP_LED_ASSOCIATED))
810 return;
811
812 if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
813 (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
814 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
815 else
816 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
817 (priv->op_flags & OP_LED_ON) ? 1 : 0);
818
819 ieee80211_queue_delayed_work(priv->hw,
820 &priv->ath9k_led_blink_work,
821 (priv->op_flags & OP_LED_ON) ?
822 msecs_to_jiffies(priv->led_off_duration) :
823 msecs_to_jiffies(priv->led_on_duration));
824
825 priv->led_on_duration = priv->led_on_cnt ?
826 max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) :
827 ATH_LED_ON_DURATION_IDLE;
828 priv->led_off_duration = priv->led_off_cnt ?
829 max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) :
830 ATH_LED_OFF_DURATION_IDLE;
831 priv->led_on_cnt = priv->led_off_cnt = 0;
832
833 if (priv->op_flags & OP_LED_ON)
834 priv->op_flags &= ~OP_LED_ON;
835 else
836 priv->op_flags |= OP_LED_ON;
837}
838
839static void ath9k_led_brightness_work(struct work_struct *work)
840{
841 struct ath_led *led = container_of(work, struct ath_led,
842 brightness_work.work);
843 struct ath9k_htc_priv *priv = led->priv;
844
845 switch (led->brightness) {
846 case LED_OFF:
847 if (led->led_type == ATH_LED_ASSOC ||
848 led->led_type == ATH_LED_RADIO) {
849 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
850 (led->led_type == ATH_LED_RADIO));
851 priv->op_flags &= ~OP_LED_ASSOCIATED;
852 if (led->led_type == ATH_LED_RADIO)
853 priv->op_flags &= ~OP_LED_ON;
854 } else {
855 priv->led_off_cnt++;
856 }
857 break;
858 case LED_FULL:
859 if (led->led_type == ATH_LED_ASSOC) {
860 priv->op_flags |= OP_LED_ASSOCIATED;
861 ieee80211_queue_delayed_work(priv->hw,
862 &priv->ath9k_led_blink_work, 0);
863 } else if (led->led_type == ATH_LED_RADIO) {
864 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
865 priv->op_flags |= OP_LED_ON;
866 } else {
867 priv->led_on_cnt++;
868 }
869 break;
870 default:
871 break;
872 }
873}
874
875static void ath9k_led_brightness(struct led_classdev *led_cdev,
876 enum led_brightness brightness)
877{
878 struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
879 struct ath9k_htc_priv *priv = led->priv;
880
881 led->brightness = brightness;
882 if (!(priv->op_flags & OP_LED_DEINIT))
883 ieee80211_queue_delayed_work(priv->hw,
884 &led->brightness_work, 0);
885}
886
887static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv)
888{
889 cancel_delayed_work_sync(&priv->radio_led.brightness_work);
890 cancel_delayed_work_sync(&priv->assoc_led.brightness_work);
891 cancel_delayed_work_sync(&priv->tx_led.brightness_work);
892 cancel_delayed_work_sync(&priv->rx_led.brightness_work);
893}
894
895static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led,
896 char *trigger)
897{
898 int ret;
899
900 led->priv = priv;
901 led->led_cdev.name = led->name;
902 led->led_cdev.default_trigger = trigger;
903 led->led_cdev.brightness_set = ath9k_led_brightness;
904
905 ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev);
906 if (ret)
907 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
908 "Failed to register led:%s", led->name);
909 else
910 led->registered = 1;
911
912 INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work);
913
914 return ret;
915}
916
917static void ath9k_unregister_led(struct ath_led *led)
918{
919 if (led->registered) {
920 led_classdev_unregister(&led->led_cdev);
921 led->registered = 0;
922 }
923}
924
925void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
926{
927 priv->op_flags |= OP_LED_DEINIT;
928 ath9k_unregister_led(&priv->assoc_led);
929 priv->op_flags &= ~OP_LED_ASSOCIATED;
930 ath9k_unregister_led(&priv->tx_led);
931 ath9k_unregister_led(&priv->rx_led);
932 ath9k_unregister_led(&priv->radio_led);
Sujithfb9987d2010-03-17 14:25:25 +0530933}
934
935void ath9k_init_leds(struct ath9k_htc_priv *priv)
936{
937 char *trigger;
938 int ret;
939
940 if (AR_SREV_9287(priv->ah))
941 priv->ah->led_pin = ATH_LED_PIN_9287;
942 else if (AR_SREV_9271(priv->ah))
943 priv->ah->led_pin = ATH_LED_PIN_9271;
Sujith88c1f4f2010-06-30 14:46:31 +0530944 else if (AR_DEVID_7010(priv->ah))
945 priv->ah->led_pin = ATH_LED_PIN_7010;
Sujithfb9987d2010-03-17 14:25:25 +0530946 else
947 priv->ah->led_pin = ATH_LED_PIN_DEF;
948
949 /* Configure gpio 1 for output */
950 ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
951 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
952 /* LED off, active low */
953 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
954
955 INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work);
956
957 trigger = ieee80211_get_radio_led_name(priv->hw);
958 snprintf(priv->radio_led.name, sizeof(priv->radio_led.name),
959 "ath9k-%s::radio", wiphy_name(priv->hw->wiphy));
960 ret = ath9k_register_led(priv, &priv->radio_led, trigger);
961 priv->radio_led.led_type = ATH_LED_RADIO;
962 if (ret)
963 goto fail;
964
965 trigger = ieee80211_get_assoc_led_name(priv->hw);
966 snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name),
967 "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy));
968 ret = ath9k_register_led(priv, &priv->assoc_led, trigger);
969 priv->assoc_led.led_type = ATH_LED_ASSOC;
970 if (ret)
971 goto fail;
972
973 trigger = ieee80211_get_tx_led_name(priv->hw);
974 snprintf(priv->tx_led.name, sizeof(priv->tx_led.name),
975 "ath9k-%s::tx", wiphy_name(priv->hw->wiphy));
976 ret = ath9k_register_led(priv, &priv->tx_led, trigger);
977 priv->tx_led.led_type = ATH_LED_TX;
978 if (ret)
979 goto fail;
980
981 trigger = ieee80211_get_rx_led_name(priv->hw);
982 snprintf(priv->rx_led.name, sizeof(priv->rx_led.name),
983 "ath9k-%s::rx", wiphy_name(priv->hw->wiphy));
984 ret = ath9k_register_led(priv, &priv->rx_led, trigger);
985 priv->rx_led.led_type = ATH_LED_RX;
986 if (ret)
987 goto fail;
988
989 priv->op_flags &= ~OP_LED_DEINIT;
990
991 return;
992
993fail:
994 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
995 ath9k_deinit_leds(priv);
996}
997
998/*******************/
999/* Rfkill */
1000/*******************/
1001
1002static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
1003{
1004 return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
1005 priv->ah->rfkill_polarity;
1006}
1007
1008static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
1009{
1010 struct ath9k_htc_priv *priv = hw->priv;
1011 bool blocked = !!ath_is_rfkill_set(priv);
1012
1013 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
1014}
1015
1016void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv)
1017{
1018 if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1019 wiphy_rfkill_start_polling(priv->hw->wiphy);
1020}
1021
Sujith881ac6a2010-06-01 15:14:11 +05301022static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
1023{
1024 struct ath9k_htc_priv *priv = hw->priv;
1025 struct ath_hw *ah = priv->ah;
1026 struct ath_common *common = ath9k_hw_common(ah);
1027 int ret;
1028 u8 cmd_rsp;
1029
1030 if (!ah->curchan)
1031 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1032
1033 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001034 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301035 if (ret) {
1036 ath_print(common, ATH_DBG_FATAL,
1037 "Unable to reset hardware; reset status %d "
1038 "(freq %u MHz)\n", ret, ah->curchan->channel);
1039 }
1040
1041 ath_update_txpow(priv);
1042
1043 /* Start RX */
1044 WMI_CMD(WMI_START_RECV_CMDID);
1045 ath9k_host_rx_init(priv);
1046
1047 /* Start TX */
1048 htc_start(priv->htc);
1049 spin_lock_bh(&priv->tx_lock);
1050 priv->tx_queues_stop = false;
1051 spin_unlock_bh(&priv->tx_lock);
1052 ieee80211_wake_queues(hw);
1053
1054 WMI_CMD(WMI_ENABLE_INTR_CMDID);
1055
1056 /* Enable LED */
1057 ath9k_hw_cfg_output(ah, ah->led_pin,
1058 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1059 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
1060}
1061
1062static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
1063{
1064 struct ath9k_htc_priv *priv = hw->priv;
1065 struct ath_hw *ah = priv->ah;
1066 struct ath_common *common = ath9k_hw_common(ah);
1067 int ret;
1068 u8 cmd_rsp;
1069
1070 ath9k_htc_ps_wakeup(priv);
1071
1072 /* Disable LED */
1073 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1074 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
1075
1076 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1077
1078 /* Stop TX */
1079 ieee80211_stop_queues(hw);
1080 htc_stop(priv->htc);
1081 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1082 skb_queue_purge(&priv->tx_queue);
1083
1084 /* Stop RX */
1085 WMI_CMD(WMI_STOP_RECV_CMDID);
1086
Sujith21d51302010-06-01 15:14:18 +05301087 /*
1088 * The MIB counters have to be disabled here,
1089 * since the target doesn't do it.
1090 */
1091 ath9k_hw_disable_mib_counters(ah);
1092
Sujith881ac6a2010-06-01 15:14:11 +05301093 if (!ah->curchan)
1094 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1095
1096 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001097 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301098 if (ret) {
1099 ath_print(common, ATH_DBG_FATAL,
1100 "Unable to reset hardware; reset status %d "
1101 "(freq %u MHz)\n", ret, ah->curchan->channel);
1102 }
1103
1104 /* Disable the PHY */
1105 ath9k_hw_phy_disable(ah);
1106
1107 ath9k_htc_ps_restore(priv);
1108 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1109}
1110
Sujithfb9987d2010-03-17 14:25:25 +05301111/**********************/
1112/* mac80211 Callbacks */
1113/**********************/
1114
1115static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1116{
1117 struct ieee80211_hdr *hdr;
1118 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301119 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301120
1121 hdr = (struct ieee80211_hdr *) skb->data;
1122
1123 /* Add the padding after the header if this is not already done */
1124 padpos = ath9k_cmn_padpos(hdr->frame_control);
1125 padsize = padpos & 3;
1126 if (padsize && skb->len > padpos) {
1127 if (skb_headroom(skb) < padsize)
1128 return -1;
1129 skb_push(skb, padsize);
1130 memmove(skb->data, skb->data + padsize, padpos);
1131 }
1132
Sujith7757dfe2010-03-29 16:07:17 +05301133 ret = ath9k_htc_tx_start(priv, skb);
1134 if (ret != 0) {
1135 if (ret == -ENOMEM) {
1136 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1137 "Stopping TX queues\n");
1138 ieee80211_stop_queues(hw);
1139 spin_lock_bh(&priv->tx_lock);
1140 priv->tx_queues_stop = true;
1141 spin_unlock_bh(&priv->tx_lock);
1142 } else {
1143 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1144 "Tx failed");
1145 }
Sujithfb9987d2010-03-17 14:25:25 +05301146 goto fail_tx;
1147 }
1148
1149 return 0;
1150
1151fail_tx:
1152 dev_kfree_skb_any(skb);
1153 return 0;
1154}
1155
Sujith881ac6a2010-06-01 15:14:11 +05301156static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301157{
1158 struct ath9k_htc_priv *priv = hw->priv;
1159 struct ath_hw *ah = priv->ah;
1160 struct ath_common *common = ath9k_hw_common(ah);
1161 struct ieee80211_channel *curchan = hw->conf.channel;
1162 struct ath9k_channel *init_channel;
1163 int ret = 0;
1164 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301165 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301166 u8 cmd_rsp;
1167
Sujith881ac6a2010-06-01 15:14:11 +05301168 mutex_lock(&priv->mutex);
1169
Sujithfb9987d2010-03-17 14:25:25 +05301170 ath_print(common, ATH_DBG_CONFIG,
1171 "Starting driver with initial channel: %d MHz\n",
1172 curchan->center_freq);
1173
Sujith21d51302010-06-01 15:14:18 +05301174 /* Ensure that HW is awake before flushing RX */
1175 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1176 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1177
Sujithfb9987d2010-03-17 14:25:25 +05301178 /* setup initial channel */
1179 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1180
1181 /* Reset SERDES registers */
1182 ath9k_hw_configpcipowersave(ah, 0, 0);
1183
1184 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001185 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301186 if (ret) {
1187 ath_print(common, ATH_DBG_FATAL,
1188 "Unable to reset hardware; reset status %d "
1189 "(freq %u MHz)\n", ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301190 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301191 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301192 }
1193
1194 ath_update_txpow(priv);
1195
1196 mode = ath9k_htc_get_curmode(priv, init_channel);
1197 htc_mode = cpu_to_be16(mode);
1198 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301199 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301200 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301201
1202 ath9k_host_rx_init(priv);
1203
1204 priv->op_flags &= ~OP_INVALID;
1205 htc_start(priv->htc);
1206
Sujith7757dfe2010-03-29 16:07:17 +05301207 spin_lock_bh(&priv->tx_lock);
1208 priv->tx_queues_stop = false;
1209 spin_unlock_bh(&priv->tx_lock);
1210
1211 ieee80211_wake_queues(hw);
1212
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301213 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1214 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1215 AR_STOMP_LOW_WLAN_WGHT);
1216 ath9k_hw_btcoex_enable(ah);
1217 ath_htc_resume_btcoex_work(priv);
1218 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301219 mutex_unlock(&priv->mutex);
1220
1221 return ret;
1222}
1223
Sujith881ac6a2010-06-01 15:14:11 +05301224static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301225{
1226 struct ath9k_htc_priv *priv = hw->priv;
1227 struct ath_hw *ah = priv->ah;
1228 struct ath_common *common = ath9k_hw_common(ah);
1229 int ret = 0;
1230 u8 cmd_rsp;
1231
Sujith881ac6a2010-06-01 15:14:11 +05301232 mutex_lock(&priv->mutex);
1233
Sujithfb9987d2010-03-17 14:25:25 +05301234 if (priv->op_flags & OP_INVALID) {
1235 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301236 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301237 return;
1238 }
1239
Sujith7073daa2010-04-23 10:28:13 +05301240 /* Cancel all the running timers/work .. */
1241 cancel_work_sync(&priv->ps_work);
1242 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujith7073daa2010-04-23 10:28:13 +05301243 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1244 ath9k_led_stop_brightness(priv);
1245
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301246 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301247 htc_stop(priv->htc);
1248 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1249 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1250 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301251 skb_queue_purge(&priv->tx_queue);
1252
1253 /* Remove monitor interface here */
1254 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1255 if (ath9k_htc_remove_monitor_interface(priv))
1256 ath_print(common, ATH_DBG_FATAL,
1257 "Unable to remove monitor interface\n");
1258 else
1259 ath_print(common, ATH_DBG_CONFIG,
1260 "Monitor interface removed\n");
1261 }
1262
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301263 if (ah->btcoex_hw.enabled) {
1264 ath9k_hw_btcoex_disable(ah);
1265 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1266 ath_htc_cancel_btcoex_work(priv);
1267 }
1268
Sujithe9201f02010-06-01 15:14:17 +05301269 ath9k_hw_phy_disable(ah);
1270 ath9k_hw_disable(ah);
1271 ath9k_hw_configpcipowersave(ah, 1, 1);
1272 ath9k_htc_ps_restore(priv);
1273 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1274
Sujithfb9987d2010-03-17 14:25:25 +05301275 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301276
1277 ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301278 mutex_unlock(&priv->mutex);
1279}
1280
Sujithfb9987d2010-03-17 14:25:25 +05301281static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1282 struct ieee80211_vif *vif)
1283{
1284 struct ath9k_htc_priv *priv = hw->priv;
1285 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1286 struct ath_common *common = ath9k_hw_common(priv->ah);
1287 struct ath9k_htc_target_vif hvif;
1288 int ret = 0;
1289 u8 cmd_rsp;
1290
1291 mutex_lock(&priv->mutex);
1292
1293 /* Only one interface for now */
1294 if (priv->nvifs > 0) {
1295 ret = -ENOBUFS;
1296 goto out;
1297 }
1298
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301299 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301300 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1301 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1302
1303 switch (vif->type) {
1304 case NL80211_IFTYPE_STATION:
1305 hvif.opmode = cpu_to_be32(HTC_M_STA);
1306 break;
1307 case NL80211_IFTYPE_ADHOC:
1308 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1309 break;
1310 default:
1311 ath_print(common, ATH_DBG_FATAL,
1312 "Interface type %d not yet supported\n", vif->type);
1313 ret = -EOPNOTSUPP;
1314 goto out;
1315 }
1316
1317 ath_print(common, ATH_DBG_CONFIG,
1318 "Attach a VIF of type: %d\n", vif->type);
1319
1320 priv->ah->opmode = vif->type;
1321
1322 /* Index starts from zero on the target */
1323 avp->index = hvif.index = priv->nvifs;
1324 hvif.rtsthreshold = cpu_to_be16(2304);
1325 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1326 if (ret)
1327 goto out;
1328
1329 priv->nvifs++;
1330
1331 /*
1332 * We need a node in target to tx mgmt frames
1333 * before association.
1334 */
1335 ret = ath9k_htc_add_station(priv, vif, NULL);
1336 if (ret)
1337 goto out;
1338
1339 ret = ath9k_htc_update_cap_target(priv);
1340 if (ret)
1341 ath_print(common, ATH_DBG_CONFIG, "Failed to update"
1342 " capability in target \n");
1343
1344 priv->vif = vif;
1345out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301346 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301347 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301348
Sujithfb9987d2010-03-17 14:25:25 +05301349 return ret;
1350}
1351
1352static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1353 struct ieee80211_vif *vif)
1354{
1355 struct ath9k_htc_priv *priv = hw->priv;
1356 struct ath_common *common = ath9k_hw_common(priv->ah);
1357 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1358 struct ath9k_htc_target_vif hvif;
1359 int ret = 0;
1360 u8 cmd_rsp;
1361
1362 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
1363
1364 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301365 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301366
1367 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1368 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1369 hvif.index = avp->index;
1370 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1371 priv->nvifs--;
1372
1373 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301374 priv->vif = NULL;
1375
Sujithcb551df2010-06-01 15:14:12 +05301376 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301377 mutex_unlock(&priv->mutex);
1378}
1379
1380static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1381{
1382 struct ath9k_htc_priv *priv = hw->priv;
1383 struct ath_common *common = ath9k_hw_common(priv->ah);
1384 struct ieee80211_conf *conf = &hw->conf;
1385
1386 mutex_lock(&priv->mutex);
1387
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301388 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1389 bool enable_radio = false;
1390 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1391
Sujith23367762010-06-01 15:14:16 +05301392 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301393 if (!idle && priv->ps_idle)
1394 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301395 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301396 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301397
1398 if (enable_radio) {
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301399 ath_print(common, ATH_DBG_CONFIG,
1400 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301401 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1402 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301403 }
1404 }
1405
Sujithfb9987d2010-03-17 14:25:25 +05301406 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1407 struct ieee80211_channel *curchan = hw->conf.channel;
1408 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301409
1410 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1411 curchan->center_freq);
1412
Sujithfb9987d2010-03-17 14:25:25 +05301413 ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]);
1414
1415 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
1416 ath_print(common, ATH_DBG_FATAL,
1417 "Unable to set channel\n");
1418 mutex_unlock(&priv->mutex);
1419 return -EINVAL;
1420 }
1421
1422 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301423 if (changed & IEEE80211_CONF_CHANGE_PS) {
1424 if (conf->flags & IEEE80211_CONF_PS) {
1425 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1426 priv->ps_enabled = true;
1427 } else {
1428 priv->ps_enabled = false;
1429 cancel_work_sync(&priv->ps_work);
1430 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1431 }
1432 }
Sujithfb9987d2010-03-17 14:25:25 +05301433
1434 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1435 if (conf->flags & IEEE80211_CONF_MONITOR) {
1436 if (ath9k_htc_add_monitor_interface(priv))
1437 ath_print(common, ATH_DBG_FATAL,
1438 "Failed to set monitor mode\n");
1439 else
1440 ath_print(common, ATH_DBG_CONFIG,
1441 "HW opmode set to Monitor mode\n");
1442 }
1443 }
1444
Sujith23367762010-06-01 15:14:16 +05301445 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1446 mutex_lock(&priv->htc_pm_lock);
1447 if (!priv->ps_idle) {
1448 mutex_unlock(&priv->htc_pm_lock);
1449 goto out;
1450 }
1451 mutex_unlock(&priv->htc_pm_lock);
1452
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301453 ath_print(common, ATH_DBG_CONFIG,
1454 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301455 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301456 }
1457
Sujith23367762010-06-01 15:14:16 +05301458out:
Sujithfb9987d2010-03-17 14:25:25 +05301459 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301460 return 0;
1461}
1462
1463#define SUPPORTED_FILTERS \
1464 (FIF_PROMISC_IN_BSS | \
1465 FIF_ALLMULTI | \
1466 FIF_CONTROL | \
1467 FIF_PSPOLL | \
1468 FIF_OTHER_BSS | \
1469 FIF_BCN_PRBRESP_PROMISC | \
1470 FIF_FCSFAIL)
1471
1472static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1473 unsigned int changed_flags,
1474 unsigned int *total_flags,
1475 u64 multicast)
1476{
1477 struct ath9k_htc_priv *priv = hw->priv;
1478 u32 rfilt;
1479
1480 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301481 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301482
Sujithfb9987d2010-03-17 14:25:25 +05301483 changed_flags &= SUPPORTED_FILTERS;
1484 *total_flags &= SUPPORTED_FILTERS;
1485
1486 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301487 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301488 ath9k_hw_setrxfilter(priv->ah, rfilt);
1489
1490 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1491 "Set HW RX filter: 0x%x\n", rfilt);
1492
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301493 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301494 mutex_unlock(&priv->mutex);
1495}
1496
Sujithabd984e2010-05-18 15:26:04 +05301497static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1498 struct ieee80211_vif *vif,
1499 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301500{
1501 struct ath9k_htc_priv *priv = hw->priv;
1502 int ret;
1503
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301504 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301505 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301506 ret = ath9k_htc_add_station(priv, vif, sta);
1507 if (!ret)
1508 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301509 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301510 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301511
1512 return ret;
1513}
1514
1515static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1516 struct ieee80211_vif *vif,
1517 struct ieee80211_sta *sta)
1518{
1519 struct ath9k_htc_priv *priv = hw->priv;
1520 int ret;
1521
1522 mutex_lock(&priv->mutex);
1523 ath9k_htc_ps_wakeup(priv);
1524 ret = ath9k_htc_remove_station(priv, vif, sta);
1525 ath9k_htc_ps_restore(priv);
1526 mutex_unlock(&priv->mutex);
1527
1528 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301529}
1530
1531static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1532 const struct ieee80211_tx_queue_params *params)
1533{
1534 struct ath9k_htc_priv *priv = hw->priv;
1535 struct ath_common *common = ath9k_hw_common(priv->ah);
1536 struct ath9k_tx_queue_info qi;
1537 int ret = 0, qnum;
1538
1539 if (queue >= WME_NUM_AC)
1540 return 0;
1541
1542 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301543 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301544
1545 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1546
1547 qi.tqi_aifs = params->aifs;
1548 qi.tqi_cwmin = params->cw_min;
1549 qi.tqi_cwmax = params->cw_max;
1550 qi.tqi_burstTime = params->txop;
1551
1552 qnum = get_hw_qnum(queue, priv->hwq_map);
1553
1554 ath_print(common, ATH_DBG_CONFIG,
1555 "Configure tx [queue/hwq] [%d/%d], "
1556 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1557 queue, qnum, params->aifs, params->cw_min,
1558 params->cw_max, params->txop);
1559
Sujithe1572c52010-03-24 13:42:13 +05301560 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301561 if (ret) {
Sujithfb9987d2010-03-17 14:25:25 +05301562 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301563 goto out;
1564 }
Sujithfb9987d2010-03-17 14:25:25 +05301565
Sujith764580f2010-06-01 15:14:19 +05301566 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001567 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301568 ath9k_htc_beaconq_config(priv);
1569out:
Sujithcb551df2010-06-01 15:14:12 +05301570 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301571 mutex_unlock(&priv->mutex);
1572
1573 return ret;
1574}
1575
1576static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1577 enum set_key_cmd cmd,
1578 struct ieee80211_vif *vif,
1579 struct ieee80211_sta *sta,
1580 struct ieee80211_key_conf *key)
1581{
1582 struct ath9k_htc_priv *priv = hw->priv;
1583 struct ath_common *common = ath9k_hw_common(priv->ah);
1584 int ret = 0;
1585
Sujithe1572c52010-03-24 13:42:13 +05301586 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301587 return -ENOSPC;
1588
1589 mutex_lock(&priv->mutex);
1590 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301591 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301592
1593 switch (cmd) {
1594 case SET_KEY:
1595 ret = ath9k_cmn_key_config(common, vif, sta, key);
1596 if (ret >= 0) {
1597 key->hw_key_idx = ret;
1598 /* push IV and Michael MIC generation to stack */
1599 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001600 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301601 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001602 if (priv->ah->sw_mgmt_crypto &&
1603 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301604 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1605 ret = 0;
1606 }
1607 break;
1608 case DISABLE_KEY:
1609 ath9k_cmn_key_delete(common, key);
1610 break;
1611 default:
1612 ret = -EINVAL;
1613 }
1614
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301615 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301616 mutex_unlock(&priv->mutex);
1617
1618 return ret;
1619}
1620
1621static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1622 struct ieee80211_vif *vif,
1623 struct ieee80211_bss_conf *bss_conf,
1624 u32 changed)
1625{
1626 struct ath9k_htc_priv *priv = hw->priv;
1627 struct ath_hw *ah = priv->ah;
1628 struct ath_common *common = ath9k_hw_common(ah);
1629
1630 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301631 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301632
1633 if (changed & BSS_CHANGED_ASSOC) {
1634 common->curaid = bss_conf->assoc ?
1635 bss_conf->aid : 0;
1636 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1637 bss_conf->assoc);
1638
1639 if (bss_conf->assoc) {
1640 priv->op_flags |= OP_ASSOCIATED;
1641 ath_start_ani(priv);
1642 } else {
1643 priv->op_flags &= ~OP_ASSOCIATED;
1644 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1645 }
1646 }
1647
1648 if (changed & BSS_CHANGED_BSSID) {
1649 /* Set BSSID */
1650 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1651 ath9k_hw_write_associd(ah);
1652
1653 ath_print(common, ATH_DBG_CONFIG,
1654 "BSSID: %pM aid: 0x%x\n",
1655 common->curbssid, common->curaid);
1656 }
1657
1658 if ((changed & BSS_CHANGED_BEACON_INT) ||
1659 (changed & BSS_CHANGED_BEACON) ||
1660 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1661 bss_conf->enable_beacon)) {
1662 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301663 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301664 }
1665
Sujithfb9987d2010-03-17 14:25:25 +05301666 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1667 !bss_conf->enable_beacon) {
1668 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301669 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301670 }
1671
1672 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1673 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1674 bss_conf->use_short_preamble);
1675 if (bss_conf->use_short_preamble)
1676 priv->op_flags |= OP_PREAMBLE_SHORT;
1677 else
1678 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1679 }
1680
1681 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1682 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1683 bss_conf->use_cts_prot);
1684 if (bss_conf->use_cts_prot &&
1685 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1686 priv->op_flags |= OP_PROTECT_ENABLE;
1687 else
1688 priv->op_flags &= ~OP_PROTECT_ENABLE;
1689 }
1690
1691 if (changed & BSS_CHANGED_ERP_SLOT) {
1692 if (bss_conf->use_short_slot)
1693 ah->slottime = 9;
1694 else
1695 ah->slottime = 20;
1696
1697 ath9k_hw_init_global_settings(ah);
1698 }
1699
Sujith2c76ef82010-05-17 12:01:18 +05301700 if (changed & BSS_CHANGED_HT)
1701 ath9k_htc_update_rate(priv, vif, bss_conf);
1702
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301703 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301704 mutex_unlock(&priv->mutex);
1705}
1706
1707static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1708{
1709 struct ath9k_htc_priv *priv = hw->priv;
1710 u64 tsf;
1711
1712 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301713 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301714 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301715 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301716 mutex_unlock(&priv->mutex);
1717
1718 return tsf;
1719}
1720
1721static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1722{
1723 struct ath9k_htc_priv *priv = hw->priv;
1724
1725 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301726 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301727 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301728 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301729 mutex_unlock(&priv->mutex);
1730}
1731
1732static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1733{
1734 struct ath9k_htc_priv *priv = hw->priv;
1735
1736 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301737 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301738 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301739 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301740 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301741}
1742
1743static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1744 struct ieee80211_vif *vif,
1745 enum ieee80211_ampdu_mlme_action action,
1746 struct ieee80211_sta *sta,
1747 u16 tid, u16 *ssn)
1748{
1749 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301750 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301751 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301752
1753 switch (action) {
1754 case IEEE80211_AMPDU_RX_START:
1755 break;
1756 case IEEE80211_AMPDU_RX_STOP:
1757 break;
1758 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301759 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1760 if (!ret)
1761 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1762 break;
Sujithfb9987d2010-03-17 14:25:25 +05301763 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301764 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1765 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301766 break;
1767 case IEEE80211_AMPDU_TX_OPERATIONAL:
1768 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301769 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301770 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301771 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301772 break;
1773 default:
1774 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
1775 "Unknown AMPDU action\n");
1776 }
1777
Sujithd7ca2132010-06-15 10:24:37 +05301778 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301779}
1780
1781static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1782{
1783 struct ath9k_htc_priv *priv = hw->priv;
1784
1785 mutex_lock(&priv->mutex);
1786 spin_lock_bh(&priv->beacon_lock);
1787 priv->op_flags |= OP_SCANNING;
1788 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301789 cancel_work_sync(&priv->ps_work);
Sujithfb9987d2010-03-17 14:25:25 +05301790 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1791 mutex_unlock(&priv->mutex);
1792}
1793
1794static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1795{
1796 struct ath9k_htc_priv *priv = hw->priv;
1797
1798 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301799 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301800 spin_lock_bh(&priv->beacon_lock);
1801 priv->op_flags &= ~OP_SCANNING;
1802 spin_unlock_bh(&priv->beacon_lock);
1803 priv->op_flags |= OP_FULL_RESET;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301804 if (priv->op_flags & OP_ASSOCIATED)
Sujithfcb93922010-04-16 11:53:48 +05301805 ath9k_htc_beacon_config(priv, priv->vif);
Sujithfb9987d2010-03-17 14:25:25 +05301806 ath_start_ani(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301807 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301808 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301809}
1810
1811static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1812{
1813 return 0;
1814}
1815
1816static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1817 u8 coverage_class)
1818{
1819 struct ath9k_htc_priv *priv = hw->priv;
1820
1821 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301822 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301823 priv->ah->coverage_class = coverage_class;
1824 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301825 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301826 mutex_unlock(&priv->mutex);
1827}
1828
1829struct ieee80211_ops ath9k_htc_ops = {
1830 .tx = ath9k_htc_tx,
1831 .start = ath9k_htc_start,
1832 .stop = ath9k_htc_stop,
1833 .add_interface = ath9k_htc_add_interface,
1834 .remove_interface = ath9k_htc_remove_interface,
1835 .config = ath9k_htc_config,
1836 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301837 .sta_add = ath9k_htc_sta_add,
1838 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301839 .conf_tx = ath9k_htc_conf_tx,
1840 .bss_info_changed = ath9k_htc_bss_info_changed,
1841 .set_key = ath9k_htc_set_key,
1842 .get_tsf = ath9k_htc_get_tsf,
1843 .set_tsf = ath9k_htc_set_tsf,
1844 .reset_tsf = ath9k_htc_reset_tsf,
1845 .ampdu_action = ath9k_htc_ampdu_action,
1846 .sw_scan_start = ath9k_htc_sw_scan_start,
1847 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1848 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1849 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1850 .set_coverage_class = ath9k_htc_set_coverage_class,
1851};