blob: fe82e5e30d82ed2849cb86b02cf1174687c7a202 [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) {
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
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
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530140 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530141 htc_stop(priv->htc);
142 WMI_CMD(WMI_DISABLE_INTR_CMDID);
143 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
144 WMI_CMD(WMI_STOP_RECV_CMDID);
145
Joe Perches226afe62010-12-02 19:12:37 -0800146 ath_dbg(common, ATH_DBG_CONFIG,
147 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
148 priv->ah->curchan->channel,
149 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
150 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530151
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200152 caldata = &priv->caldata[channel->hw_value];
153 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530154 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800155 ath_err(common,
156 "Unable to reset channel (%u Mhz) reset status %d\n",
157 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530158 goto err;
159 }
160
161 ath_update_txpow(priv);
162
163 WMI_CMD(WMI_START_RECV_CMDID);
164 if (ret)
165 goto err;
166
167 ath9k_host_rx_init(priv);
168
169 mode = ath9k_htc_get_curmode(priv, hchan);
170 htc_mode = cpu_to_be16(mode);
171 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
172 if (ret)
173 goto err;
174
175 WMI_CMD(WMI_ENABLE_INTR_CMDID);
176 if (ret)
177 goto err;
178
179 htc_start(priv->htc);
180
181 priv->op_flags &= ~OP_FULL_RESET;
182err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530183 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530184 return ret;
185}
186
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530187static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
188{
189 struct ath_common *common = ath9k_hw_common(priv->ah);
190 struct ath9k_htc_target_vif hvif;
191 int ret = 0;
192 u8 cmd_rsp;
193
194 if (priv->nvifs > 0)
195 return -ENOBUFS;
196
197 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
198 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
199
200 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
201 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
202 hvif.index = priv->nvifs;
203
204 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
205 if (ret)
206 return ret;
207
208 priv->nvifs++;
209 return 0;
210}
211
212static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
213{
214 struct ath_common *common = ath9k_hw_common(priv->ah);
215 struct ath9k_htc_target_vif hvif;
216 int ret = 0;
217 u8 cmd_rsp;
218
219 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
220 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
221 hvif.index = 0; /* Should do for now */
222 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
223 priv->nvifs--;
224
225 return ret;
226}
227
Sujithfb9987d2010-03-17 14:25:25 +0530228static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
229 struct ieee80211_vif *vif,
230 struct ieee80211_sta *sta)
231{
232 struct ath_common *common = ath9k_hw_common(priv->ah);
233 struct ath9k_htc_target_sta tsta;
234 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
235 struct ath9k_htc_sta *ista;
236 int ret;
237 u8 cmd_rsp;
238
239 if (priv->nstations >= ATH9K_HTC_MAX_STA)
240 return -ENOBUFS;
241
242 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
243
244 if (sta) {
245 ista = (struct ath9k_htc_sta *) sta->drv_priv;
246 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
247 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
248 tsta.associd = common->curaid;
249 tsta.is_vif_sta = 0;
250 tsta.valid = true;
251 ista->index = priv->nstations;
252 } else {
253 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
254 tsta.is_vif_sta = 1;
255 }
256
257 tsta.sta_index = priv->nstations;
258 tsta.vif_index = avp->index;
259 tsta.maxampdu = 0xffff;
260 if (sta && sta->ht_cap.ht_supported)
261 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
262
263 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
264 if (ret) {
265 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800266 ath_err(common,
267 "Unable to add station entry for: %pM\n",
268 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530269 return ret;
270 }
271
272 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800273 ath_dbg(common, ATH_DBG_CONFIG,
274 "Added a station entry for: %pM (idx: %d)\n",
275 sta->addr, tsta.sta_index);
Sujithfb9987d2010-03-17 14:25:25 +0530276
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)
Joe Perches38002762010-12-02 19:12:36 -0800300 ath_err(common,
301 "Unable to remove station entry for: %pM\n",
302 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530303 return ret;
304 }
305
306 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800307 ath_dbg(common, ATH_DBG_CONFIG,
308 "Removed a station entry for: %pM (idx: %d)\n",
309 sta->addr, sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530310
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) {
Joe Perches38002762010-12-02 19:12:36 -0800394 ath_err(common,
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)
Joe Perches226afe62010-12-02 19:12:37 -0800412 ath_dbg(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)
Joe Perches226afe62010-12-02 19:12:37 -0800439 ath_dbg(common, ATH_DBG_CONFIG,
440 "Updated target sta: %pM, rate caps: 0x%X\n",
441 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530442}
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)
Joe Perches226afe62010-12-02 19:12:37 -0800468 ath_dbg(common, ATH_DBG_CONFIG,
469 "Unable to %s TX aggregation for (%pM, %d)\n",
470 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530471 else
Joe Perches226afe62010-12-02 19:12:37 -0800472 ath_dbg(common, ATH_DBG_CONFIG,
473 "%s TX aggregation for (%pM, %d)\n",
474 (aggr.aggr_enable) ? "Starting" : "Stopping",
475 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530476
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,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200539 .owner = THIS_MODULE,
540 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530541};
542
543static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
544 size_t count, loff_t *ppos)
545{
Joe Perches57674302010-07-12 13:50:06 -0700546 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530547 char buf[512];
548 unsigned int len = 0;
549
550 len += snprintf(buf + len, sizeof(buf) - len,
551 "%20s : %10u\n", "Buffers queued",
552 priv->debug.tx_stats.buf_queued);
553 len += snprintf(buf + len, sizeof(buf) - len,
554 "%20s : %10u\n", "Buffers completed",
555 priv->debug.tx_stats.buf_completed);
556 len += snprintf(buf + len, sizeof(buf) - len,
557 "%20s : %10u\n", "SKBs queued",
558 priv->debug.tx_stats.skb_queued);
559 len += snprintf(buf + len, sizeof(buf) - len,
560 "%20s : %10u\n", "SKBs completed",
561 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530562 len += snprintf(buf + len, sizeof(buf) - len,
563 "%20s : %10u\n", "SKBs dropped",
564 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530565
Sujith2edb4582010-05-14 11:18:54 +0530566 len += snprintf(buf + len, sizeof(buf) - len,
567 "%20s : %10u\n", "BE queued",
568 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
569 len += snprintf(buf + len, sizeof(buf) - len,
570 "%20s : %10u\n", "BK queued",
571 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
572 len += snprintf(buf + len, sizeof(buf) - len,
573 "%20s : %10u\n", "VI queued",
574 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
575 len += snprintf(buf + len, sizeof(buf) - len,
576 "%20s : %10u\n", "VO queued",
577 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
578
Dan Carpenter97460102010-07-22 10:50:28 +0200579 if (len > sizeof(buf))
580 len = sizeof(buf);
581
Sujithfb9987d2010-03-17 14:25:25 +0530582 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
583}
584
585static const struct file_operations fops_xmit = {
586 .read = read_file_xmit,
587 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200588 .owner = THIS_MODULE,
589 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530590};
591
592static ssize_t read_file_recv(struct file *file, char __user *user_buf,
593 size_t count, loff_t *ppos)
594{
Joe Perches57674302010-07-12 13:50:06 -0700595 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530596 char buf[512];
597 unsigned int len = 0;
598
599 len += snprintf(buf + len, sizeof(buf) - len,
600 "%20s : %10u\n", "SKBs allocated",
601 priv->debug.rx_stats.skb_allocated);
602 len += snprintf(buf + len, sizeof(buf) - len,
603 "%20s : %10u\n", "SKBs completed",
604 priv->debug.rx_stats.skb_completed);
605 len += snprintf(buf + len, sizeof(buf) - len,
606 "%20s : %10u\n", "SKBs Dropped",
607 priv->debug.rx_stats.skb_dropped);
608
Dan Carpenter97460102010-07-22 10:50:28 +0200609 if (len > sizeof(buf))
610 len = sizeof(buf);
611
Sujithfb9987d2010-03-17 14:25:25 +0530612 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
613}
614
615static const struct file_operations fops_recv = {
616 .read = read_file_recv,
617 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200618 .owner = THIS_MODULE,
619 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530620};
621
Sujithe1572c52010-03-24 13:42:13 +0530622int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530623{
624 struct ath_common *common = ath9k_hw_common(ah);
625 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
626
627 if (!ath9k_debugfs_root)
628 return -ENOENT;
629
630 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
631 ath9k_debugfs_root);
632 if (!priv->debug.debugfs_phy)
633 goto err;
634
635 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
636 priv->debug.debugfs_phy,
637 priv, &fops_tgt_stats);
638 if (!priv->debug.debugfs_tgt_stats)
639 goto err;
640
641
642 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
643 priv->debug.debugfs_phy,
644 priv, &fops_xmit);
645 if (!priv->debug.debugfs_xmit)
646 goto err;
647
648 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
649 priv->debug.debugfs_phy,
650 priv, &fops_recv);
651 if (!priv->debug.debugfs_recv)
652 goto err;
653
654 return 0;
655
656err:
Sujithe1572c52010-03-24 13:42:13 +0530657 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530658 return -ENOMEM;
659}
660
Sujithe1572c52010-03-24 13:42:13 +0530661void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530662{
663 struct ath_common *common = ath9k_hw_common(ah);
664 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
665
666 debugfs_remove(priv->debug.debugfs_recv);
667 debugfs_remove(priv->debug.debugfs_xmit);
668 debugfs_remove(priv->debug.debugfs_tgt_stats);
669 debugfs_remove(priv->debug.debugfs_phy);
670}
671
Sujithe1572c52010-03-24 13:42:13 +0530672int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530673{
674 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
675 if (!ath9k_debugfs_root)
676 return -ENOENT;
677
678 return 0;
679}
680
Sujithe1572c52010-03-24 13:42:13 +0530681void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530682{
683 debugfs_remove(ath9k_debugfs_root);
684 ath9k_debugfs_root = NULL;
685}
686
687#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
688
689/*******/
690/* ANI */
691/*******/
692
693static void ath_start_ani(struct ath9k_htc_priv *priv)
694{
695 struct ath_common *common = ath9k_hw_common(priv->ah);
696 unsigned long timestamp = jiffies_to_msecs(jiffies);
697
698 common->ani.longcal_timer = timestamp;
699 common->ani.shortcal_timer = timestamp;
700 common->ani.checkani_timer = timestamp;
701
702 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
703 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
704}
705
706void ath9k_ani_work(struct work_struct *work)
707{
708 struct ath9k_htc_priv *priv =
709 container_of(work, struct ath9k_htc_priv,
710 ath9k_ani_work.work);
711 struct ath_hw *ah = priv->ah;
712 struct ath_common *common = ath9k_hw_common(ah);
713 bool longcal = false;
714 bool shortcal = false;
715 bool aniflag = false;
716 unsigned int timestamp = jiffies_to_msecs(jiffies);
717 u32 cal_interval, short_cal_interval;
718
719 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
720
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530721 /* Only calibrate if awake */
722 if (ah->power_mode != ATH9K_PM_AWAKE)
723 goto set_timer;
724
Sujithfb9987d2010-03-17 14:25:25 +0530725 /* Long calibration runs independently of short calibration. */
726 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
727 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800728 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530729 common->ani.longcal_timer = timestamp;
730 }
731
732 /* Short calibration applies only while caldone is false */
733 if (!common->ani.caldone) {
734 if ((timestamp - common->ani.shortcal_timer) >=
735 short_cal_interval) {
736 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800737 ath_dbg(common, ATH_DBG_ANI,
738 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530739 common->ani.shortcal_timer = timestamp;
740 common->ani.resetcal_timer = timestamp;
741 }
742 } else {
743 if ((timestamp - common->ani.resetcal_timer) >=
744 ATH_RESTART_CALINTERVAL) {
745 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
746 if (common->ani.caldone)
747 common->ani.resetcal_timer = timestamp;
748 }
749 }
750
751 /* Verify whether we must check ANI */
752 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
753 aniflag = true;
754 common->ani.checkani_timer = timestamp;
755 }
756
757 /* Skip all processing if there's nothing to do. */
758 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530759
760 ath9k_htc_ps_wakeup(priv);
761
Sujithfb9987d2010-03-17 14:25:25 +0530762 /* Call ANI routine if necessary */
763 if (aniflag)
764 ath9k_hw_ani_monitor(ah, ah->curchan);
765
766 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200767 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530768 common->ani.caldone =
769 ath9k_hw_calibrate(ah, ah->curchan,
770 common->rx_chainmask,
771 longcal);
772
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530773 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530774 }
775
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530776set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530777 /*
778 * Set timer interval based on previous results.
779 * The interval must be the shortest necessary to satisfy ANI,
780 * short calibration and long calibration.
781 */
782 cal_interval = ATH_LONG_CALINTERVAL;
783 if (priv->ah->config.enable_ani)
784 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
785 if (!common->ani.caldone)
786 cal_interval = min(cal_interval, (u32)short_cal_interval);
787
788 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
789 msecs_to_jiffies(cal_interval));
790}
791
792/*******/
793/* LED */
794/*******/
795
796static void ath9k_led_blink_work(struct work_struct *work)
797{
798 struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
799 ath9k_led_blink_work.work);
800
801 if (!(priv->op_flags & OP_LED_ASSOCIATED))
802 return;
803
804 if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
805 (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
806 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
807 else
808 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
809 (priv->op_flags & OP_LED_ON) ? 1 : 0);
810
811 ieee80211_queue_delayed_work(priv->hw,
812 &priv->ath9k_led_blink_work,
813 (priv->op_flags & OP_LED_ON) ?
814 msecs_to_jiffies(priv->led_off_duration) :
815 msecs_to_jiffies(priv->led_on_duration));
816
817 priv->led_on_duration = priv->led_on_cnt ?
818 max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) :
819 ATH_LED_ON_DURATION_IDLE;
820 priv->led_off_duration = priv->led_off_cnt ?
821 max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) :
822 ATH_LED_OFF_DURATION_IDLE;
823 priv->led_on_cnt = priv->led_off_cnt = 0;
824
825 if (priv->op_flags & OP_LED_ON)
826 priv->op_flags &= ~OP_LED_ON;
827 else
828 priv->op_flags |= OP_LED_ON;
829}
830
831static void ath9k_led_brightness_work(struct work_struct *work)
832{
833 struct ath_led *led = container_of(work, struct ath_led,
834 brightness_work.work);
835 struct ath9k_htc_priv *priv = led->priv;
836
837 switch (led->brightness) {
838 case LED_OFF:
839 if (led->led_type == ATH_LED_ASSOC ||
840 led->led_type == ATH_LED_RADIO) {
841 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
842 (led->led_type == ATH_LED_RADIO));
843 priv->op_flags &= ~OP_LED_ASSOCIATED;
844 if (led->led_type == ATH_LED_RADIO)
845 priv->op_flags &= ~OP_LED_ON;
846 } else {
847 priv->led_off_cnt++;
848 }
849 break;
850 case LED_FULL:
851 if (led->led_type == ATH_LED_ASSOC) {
852 priv->op_flags |= OP_LED_ASSOCIATED;
853 ieee80211_queue_delayed_work(priv->hw,
854 &priv->ath9k_led_blink_work, 0);
855 } else if (led->led_type == ATH_LED_RADIO) {
856 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
857 priv->op_flags |= OP_LED_ON;
858 } else {
859 priv->led_on_cnt++;
860 }
861 break;
862 default:
863 break;
864 }
865}
866
867static void ath9k_led_brightness(struct led_classdev *led_cdev,
868 enum led_brightness brightness)
869{
870 struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
871 struct ath9k_htc_priv *priv = led->priv;
872
873 led->brightness = brightness;
874 if (!(priv->op_flags & OP_LED_DEINIT))
875 ieee80211_queue_delayed_work(priv->hw,
876 &led->brightness_work, 0);
877}
878
879static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv)
880{
881 cancel_delayed_work_sync(&priv->radio_led.brightness_work);
882 cancel_delayed_work_sync(&priv->assoc_led.brightness_work);
883 cancel_delayed_work_sync(&priv->tx_led.brightness_work);
884 cancel_delayed_work_sync(&priv->rx_led.brightness_work);
885}
886
887static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led,
888 char *trigger)
889{
890 int ret;
891
892 led->priv = priv;
893 led->led_cdev.name = led->name;
894 led->led_cdev.default_trigger = trigger;
895 led->led_cdev.brightness_set = ath9k_led_brightness;
896
897 ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev);
898 if (ret)
Joe Perches38002762010-12-02 19:12:36 -0800899 ath_err(ath9k_hw_common(priv->ah),
900 "Failed to register led:%s", led->name);
Sujithfb9987d2010-03-17 14:25:25 +0530901 else
902 led->registered = 1;
903
904 INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work);
905
906 return ret;
907}
908
909static void ath9k_unregister_led(struct ath_led *led)
910{
911 if (led->registered) {
912 led_classdev_unregister(&led->led_cdev);
913 led->registered = 0;
914 }
915}
916
917void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
918{
919 priv->op_flags |= OP_LED_DEINIT;
920 ath9k_unregister_led(&priv->assoc_led);
921 priv->op_flags &= ~OP_LED_ASSOCIATED;
922 ath9k_unregister_led(&priv->tx_led);
923 ath9k_unregister_led(&priv->rx_led);
924 ath9k_unregister_led(&priv->radio_led);
Sujithfb9987d2010-03-17 14:25:25 +0530925}
926
927void ath9k_init_leds(struct ath9k_htc_priv *priv)
928{
929 char *trigger;
930 int ret;
931
932 if (AR_SREV_9287(priv->ah))
933 priv->ah->led_pin = ATH_LED_PIN_9287;
934 else if (AR_SREV_9271(priv->ah))
935 priv->ah->led_pin = ATH_LED_PIN_9271;
Sujith88c1f4f2010-06-30 14:46:31 +0530936 else if (AR_DEVID_7010(priv->ah))
937 priv->ah->led_pin = ATH_LED_PIN_7010;
Sujithfb9987d2010-03-17 14:25:25 +0530938 else
939 priv->ah->led_pin = ATH_LED_PIN_DEF;
940
941 /* Configure gpio 1 for output */
942 ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
943 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
944 /* LED off, active low */
945 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
946
947 INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work);
948
949 trigger = ieee80211_get_radio_led_name(priv->hw);
950 snprintf(priv->radio_led.name, sizeof(priv->radio_led.name),
951 "ath9k-%s::radio", wiphy_name(priv->hw->wiphy));
952 ret = ath9k_register_led(priv, &priv->radio_led, trigger);
953 priv->radio_led.led_type = ATH_LED_RADIO;
954 if (ret)
955 goto fail;
956
957 trigger = ieee80211_get_assoc_led_name(priv->hw);
958 snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name),
959 "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy));
960 ret = ath9k_register_led(priv, &priv->assoc_led, trigger);
961 priv->assoc_led.led_type = ATH_LED_ASSOC;
962 if (ret)
963 goto fail;
964
965 trigger = ieee80211_get_tx_led_name(priv->hw);
966 snprintf(priv->tx_led.name, sizeof(priv->tx_led.name),
967 "ath9k-%s::tx", wiphy_name(priv->hw->wiphy));
968 ret = ath9k_register_led(priv, &priv->tx_led, trigger);
969 priv->tx_led.led_type = ATH_LED_TX;
970 if (ret)
971 goto fail;
972
973 trigger = ieee80211_get_rx_led_name(priv->hw);
974 snprintf(priv->rx_led.name, sizeof(priv->rx_led.name),
975 "ath9k-%s::rx", wiphy_name(priv->hw->wiphy));
976 ret = ath9k_register_led(priv, &priv->rx_led, trigger);
977 priv->rx_led.led_type = ATH_LED_RX;
978 if (ret)
979 goto fail;
980
981 priv->op_flags &= ~OP_LED_DEINIT;
982
983 return;
984
985fail:
986 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
987 ath9k_deinit_leds(priv);
988}
989
990/*******************/
991/* Rfkill */
992/*******************/
993
994static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
995{
996 return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
997 priv->ah->rfkill_polarity;
998}
999
1000static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
1001{
1002 struct ath9k_htc_priv *priv = hw->priv;
1003 bool blocked = !!ath_is_rfkill_set(priv);
1004
1005 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
1006}
1007
1008void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv)
1009{
1010 if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1011 wiphy_rfkill_start_polling(priv->hw->wiphy);
1012}
1013
Sujith881ac6a2010-06-01 15:14:11 +05301014static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
1015{
1016 struct ath9k_htc_priv *priv = hw->priv;
1017 struct ath_hw *ah = priv->ah;
1018 struct ath_common *common = ath9k_hw_common(ah);
1019 int ret;
1020 u8 cmd_rsp;
1021
1022 if (!ah->curchan)
1023 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1024
1025 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001026 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301027 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001028 ath_err(common,
1029 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1030 ret, ah->curchan->channel);
Sujith881ac6a2010-06-01 15:14:11 +05301031 }
1032
1033 ath_update_txpow(priv);
1034
1035 /* Start RX */
1036 WMI_CMD(WMI_START_RECV_CMDID);
1037 ath9k_host_rx_init(priv);
1038
1039 /* Start TX */
1040 htc_start(priv->htc);
1041 spin_lock_bh(&priv->tx_lock);
1042 priv->tx_queues_stop = false;
1043 spin_unlock_bh(&priv->tx_lock);
1044 ieee80211_wake_queues(hw);
1045
1046 WMI_CMD(WMI_ENABLE_INTR_CMDID);
1047
1048 /* Enable LED */
1049 ath9k_hw_cfg_output(ah, ah->led_pin,
1050 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1051 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
1052}
1053
1054static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
1055{
1056 struct ath9k_htc_priv *priv = hw->priv;
1057 struct ath_hw *ah = priv->ah;
1058 struct ath_common *common = ath9k_hw_common(ah);
1059 int ret;
1060 u8 cmd_rsp;
1061
1062 ath9k_htc_ps_wakeup(priv);
1063
1064 /* Disable LED */
1065 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1066 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
1067
1068 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1069
1070 /* Stop TX */
1071 ieee80211_stop_queues(hw);
1072 htc_stop(priv->htc);
1073 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1074 skb_queue_purge(&priv->tx_queue);
1075
1076 /* Stop RX */
1077 WMI_CMD(WMI_STOP_RECV_CMDID);
1078
Sujith21d51302010-06-01 15:14:18 +05301079 /*
1080 * The MIB counters have to be disabled here,
1081 * since the target doesn't do it.
1082 */
1083 ath9k_hw_disable_mib_counters(ah);
1084
Sujith881ac6a2010-06-01 15:14:11 +05301085 if (!ah->curchan)
1086 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1087
1088 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001089 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301090 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001091 ath_err(common,
1092 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1093 ret, ah->curchan->channel);
Sujith881ac6a2010-06-01 15:14:11 +05301094 }
1095
1096 /* Disable the PHY */
1097 ath9k_hw_phy_disable(ah);
1098
1099 ath9k_htc_ps_restore(priv);
1100 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1101}
1102
Sujithfb9987d2010-03-17 14:25:25 +05301103/**********************/
1104/* mac80211 Callbacks */
1105/**********************/
1106
1107static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1108{
1109 struct ieee80211_hdr *hdr;
1110 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301111 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301112
1113 hdr = (struct ieee80211_hdr *) skb->data;
1114
1115 /* Add the padding after the header if this is not already done */
1116 padpos = ath9k_cmn_padpos(hdr->frame_control);
1117 padsize = padpos & 3;
1118 if (padsize && skb->len > padpos) {
1119 if (skb_headroom(skb) < padsize)
1120 return -1;
1121 skb_push(skb, padsize);
1122 memmove(skb->data, skb->data + padsize, padpos);
1123 }
1124
Sujith7757dfe2010-03-29 16:07:17 +05301125 ret = ath9k_htc_tx_start(priv, skb);
1126 if (ret != 0) {
1127 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -08001128 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1129 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +05301130 ieee80211_stop_queues(hw);
1131 spin_lock_bh(&priv->tx_lock);
1132 priv->tx_queues_stop = true;
1133 spin_unlock_bh(&priv->tx_lock);
1134 } else {
Joe Perches226afe62010-12-02 19:12:37 -08001135 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1136 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +05301137 }
Sujithfb9987d2010-03-17 14:25:25 +05301138 goto fail_tx;
1139 }
1140
1141 return 0;
1142
1143fail_tx:
1144 dev_kfree_skb_any(skb);
1145 return 0;
1146}
1147
Sujith881ac6a2010-06-01 15:14:11 +05301148static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301149{
1150 struct ath9k_htc_priv *priv = hw->priv;
1151 struct ath_hw *ah = priv->ah;
1152 struct ath_common *common = ath9k_hw_common(ah);
1153 struct ieee80211_channel *curchan = hw->conf.channel;
1154 struct ath9k_channel *init_channel;
1155 int ret = 0;
1156 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301157 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301158 u8 cmd_rsp;
1159
Sujith881ac6a2010-06-01 15:14:11 +05301160 mutex_lock(&priv->mutex);
1161
Joe Perches226afe62010-12-02 19:12:37 -08001162 ath_dbg(common, ATH_DBG_CONFIG,
1163 "Starting driver with initial channel: %d MHz\n",
1164 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301165
Sujith21d51302010-06-01 15:14:18 +05301166 /* Ensure that HW is awake before flushing RX */
1167 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1168 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1169
Sujithfb9987d2010-03-17 14:25:25 +05301170 /* setup initial channel */
1171 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1172
1173 /* Reset SERDES registers */
1174 ath9k_hw_configpcipowersave(ah, 0, 0);
1175
1176 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001177 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301178 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001179 ath_err(common,
1180 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1181 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301182 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301183 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301184 }
1185
1186 ath_update_txpow(priv);
1187
1188 mode = ath9k_htc_get_curmode(priv, init_channel);
1189 htc_mode = cpu_to_be16(mode);
1190 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301191 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301192 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301193
1194 ath9k_host_rx_init(priv);
1195
1196 priv->op_flags &= ~OP_INVALID;
1197 htc_start(priv->htc);
1198
Sujith7757dfe2010-03-29 16:07:17 +05301199 spin_lock_bh(&priv->tx_lock);
1200 priv->tx_queues_stop = false;
1201 spin_unlock_bh(&priv->tx_lock);
1202
1203 ieee80211_wake_queues(hw);
1204
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301205 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1206 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1207 AR_STOMP_LOW_WLAN_WGHT);
1208 ath9k_hw_btcoex_enable(ah);
1209 ath_htc_resume_btcoex_work(priv);
1210 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301211 mutex_unlock(&priv->mutex);
1212
1213 return ret;
1214}
1215
Sujith881ac6a2010-06-01 15:14:11 +05301216static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301217{
1218 struct ath9k_htc_priv *priv = hw->priv;
1219 struct ath_hw *ah = priv->ah;
1220 struct ath_common *common = ath9k_hw_common(ah);
1221 int ret = 0;
1222 u8 cmd_rsp;
1223
Sujith881ac6a2010-06-01 15:14:11 +05301224 mutex_lock(&priv->mutex);
1225
Sujithfb9987d2010-03-17 14:25:25 +05301226 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001227 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301228 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301229 return;
1230 }
1231
Sujith7073daa2010-04-23 10:28:13 +05301232 /* Cancel all the running timers/work .. */
1233 cancel_work_sync(&priv->ps_work);
Sujith7073daa2010-04-23 10:28:13 +05301234 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1235 ath9k_led_stop_brightness(priv);
1236
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301237 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301238 htc_stop(priv->htc);
1239 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1240 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1241 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301242 skb_queue_purge(&priv->tx_queue);
1243
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301244 /* Remove monitor interface here */
1245 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1246 if (ath9k_htc_remove_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001247 ath_err(common, "Unable to remove monitor interface\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301248 else
Joe Perches226afe62010-12-02 19:12:37 -08001249 ath_dbg(common, ATH_DBG_CONFIG,
1250 "Monitor interface removed\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301251 }
1252
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301253 if (ah->btcoex_hw.enabled) {
1254 ath9k_hw_btcoex_disable(ah);
1255 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1256 ath_htc_cancel_btcoex_work(priv);
1257 }
1258
Sujithe9201f02010-06-01 15:14:17 +05301259 ath9k_hw_phy_disable(ah);
1260 ath9k_hw_disable(ah);
1261 ath9k_hw_configpcipowersave(ah, 1, 1);
1262 ath9k_htc_ps_restore(priv);
1263 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1264
Sujithfb9987d2010-03-17 14:25:25 +05301265 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301266
Joe Perches226afe62010-12-02 19:12:37 -08001267 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301268 mutex_unlock(&priv->mutex);
1269}
1270
Sujithfb9987d2010-03-17 14:25:25 +05301271static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1272 struct ieee80211_vif *vif)
1273{
1274 struct ath9k_htc_priv *priv = hw->priv;
1275 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1276 struct ath_common *common = ath9k_hw_common(priv->ah);
1277 struct ath9k_htc_target_vif hvif;
1278 int ret = 0;
1279 u8 cmd_rsp;
1280
1281 mutex_lock(&priv->mutex);
1282
1283 /* Only one interface for now */
1284 if (priv->nvifs > 0) {
1285 ret = -ENOBUFS;
1286 goto out;
1287 }
1288
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301289 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301290 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1291 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1292
1293 switch (vif->type) {
1294 case NL80211_IFTYPE_STATION:
1295 hvif.opmode = cpu_to_be32(HTC_M_STA);
1296 break;
1297 case NL80211_IFTYPE_ADHOC:
1298 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1299 break;
1300 default:
Joe Perches38002762010-12-02 19:12:36 -08001301 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301302 "Interface type %d not yet supported\n", vif->type);
1303 ret = -EOPNOTSUPP;
1304 goto out;
1305 }
1306
Joe Perches226afe62010-12-02 19:12:37 -08001307 ath_dbg(common, ATH_DBG_CONFIG,
1308 "Attach a VIF of type: %d\n", vif->type);
Sujithfb9987d2010-03-17 14:25:25 +05301309
1310 priv->ah->opmode = vif->type;
1311
1312 /* Index starts from zero on the target */
1313 avp->index = hvif.index = priv->nvifs;
1314 hvif.rtsthreshold = cpu_to_be16(2304);
1315 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1316 if (ret)
1317 goto out;
1318
1319 priv->nvifs++;
1320
1321 /*
1322 * We need a node in target to tx mgmt frames
1323 * before association.
1324 */
1325 ret = ath9k_htc_add_station(priv, vif, NULL);
1326 if (ret)
1327 goto out;
1328
1329 ret = ath9k_htc_update_cap_target(priv);
1330 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -08001331 ath_dbg(common, ATH_DBG_CONFIG,
1332 "Failed to update capability in target\n");
Sujithfb9987d2010-03-17 14:25:25 +05301333
1334 priv->vif = vif;
1335out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301336 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301337 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301338
Sujithfb9987d2010-03-17 14:25:25 +05301339 return ret;
1340}
1341
1342static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1343 struct ieee80211_vif *vif)
1344{
1345 struct ath9k_htc_priv *priv = hw->priv;
1346 struct ath_common *common = ath9k_hw_common(priv->ah);
1347 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1348 struct ath9k_htc_target_vif hvif;
1349 int ret = 0;
1350 u8 cmd_rsp;
1351
Joe Perches226afe62010-12-02 19:12:37 -08001352 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
Sujithfb9987d2010-03-17 14:25:25 +05301353
1354 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301355 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301356
1357 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1358 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1359 hvif.index = avp->index;
1360 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1361 priv->nvifs--;
1362
1363 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301364 priv->vif = NULL;
1365
Sujithcb551df2010-06-01 15:14:12 +05301366 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301367 mutex_unlock(&priv->mutex);
1368}
1369
1370static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1371{
1372 struct ath9k_htc_priv *priv = hw->priv;
1373 struct ath_common *common = ath9k_hw_common(priv->ah);
1374 struct ieee80211_conf *conf = &hw->conf;
1375
1376 mutex_lock(&priv->mutex);
1377
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301378 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1379 bool enable_radio = false;
1380 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1381
Sujith23367762010-06-01 15:14:16 +05301382 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301383 if (!idle && priv->ps_idle)
1384 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301385 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301386 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301387
1388 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001389 ath_dbg(common, ATH_DBG_CONFIG,
1390 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301391 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1392 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301393 }
1394 }
1395
Sujithfb9987d2010-03-17 14:25:25 +05301396 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1397 struct ieee80211_channel *curchan = hw->conf.channel;
1398 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301399
Joe Perches226afe62010-12-02 19:12:37 -08001400 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1401 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301402
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001403 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1404 hw->conf.channel,
1405 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301406
1407 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001408 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301409 mutex_unlock(&priv->mutex);
1410 return -EINVAL;
1411 }
1412
1413 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301414
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301415 if (changed & IEEE80211_CONF_CHANGE_PS) {
1416 if (conf->flags & IEEE80211_CONF_PS) {
1417 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1418 priv->ps_enabled = true;
1419 } else {
1420 priv->ps_enabled = false;
1421 cancel_work_sync(&priv->ps_work);
1422 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1423 }
1424 }
Sujithfb9987d2010-03-17 14:25:25 +05301425
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301426 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1427 priv->txpowlimit = 2 * conf->power_level;
1428 ath_update_txpow(priv);
1429 }
1430
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301431 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujithfb9987d2010-03-17 14:25:25 +05301432 if (conf->flags & IEEE80211_CONF_MONITOR) {
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301433 if (ath9k_htc_add_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001434 ath_err(common, "Failed to set monitor mode\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301435 else
Joe Perches226afe62010-12-02 19:12:37 -08001436 ath_dbg(common, ATH_DBG_CONFIG,
1437 "HW opmode set to Monitor mode\n");
Sujithfb9987d2010-03-17 14:25:25 +05301438 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301439 }
Sujithfb9987d2010-03-17 14:25:25 +05301440
Sujith23367762010-06-01 15:14:16 +05301441 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1442 mutex_lock(&priv->htc_pm_lock);
1443 if (!priv->ps_idle) {
1444 mutex_unlock(&priv->htc_pm_lock);
1445 goto out;
1446 }
1447 mutex_unlock(&priv->htc_pm_lock);
1448
Joe Perches226afe62010-12-02 19:12:37 -08001449 ath_dbg(common, ATH_DBG_CONFIG,
1450 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301451 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301452 }
1453
Sujith23367762010-06-01 15:14:16 +05301454out:
Sujithfb9987d2010-03-17 14:25:25 +05301455 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301456 return 0;
1457}
1458
1459#define SUPPORTED_FILTERS \
1460 (FIF_PROMISC_IN_BSS | \
1461 FIF_ALLMULTI | \
1462 FIF_CONTROL | \
1463 FIF_PSPOLL | \
1464 FIF_OTHER_BSS | \
1465 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301466 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301467 FIF_FCSFAIL)
1468
1469static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1470 unsigned int changed_flags,
1471 unsigned int *total_flags,
1472 u64 multicast)
1473{
1474 struct ath9k_htc_priv *priv = hw->priv;
1475 u32 rfilt;
1476
1477 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301478 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301479
Sujithfb9987d2010-03-17 14:25:25 +05301480 changed_flags &= SUPPORTED_FILTERS;
1481 *total_flags &= SUPPORTED_FILTERS;
1482
1483 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301484 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301485 ath9k_hw_setrxfilter(priv->ah, rfilt);
1486
Joe Perches226afe62010-12-02 19:12:37 -08001487 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1488 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301489
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301490 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301491 mutex_unlock(&priv->mutex);
1492}
1493
Sujithabd984e2010-05-18 15:26:04 +05301494static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1495 struct ieee80211_vif *vif,
1496 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301497{
1498 struct ath9k_htc_priv *priv = hw->priv;
1499 int ret;
1500
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301501 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301502 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301503 ret = ath9k_htc_add_station(priv, vif, sta);
1504 if (!ret)
1505 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301506 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301507 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301508
1509 return ret;
1510}
1511
1512static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1513 struct ieee80211_vif *vif,
1514 struct ieee80211_sta *sta)
1515{
1516 struct ath9k_htc_priv *priv = hw->priv;
1517 int ret;
1518
1519 mutex_lock(&priv->mutex);
1520 ath9k_htc_ps_wakeup(priv);
1521 ret = ath9k_htc_remove_station(priv, vif, sta);
1522 ath9k_htc_ps_restore(priv);
1523 mutex_unlock(&priv->mutex);
1524
1525 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301526}
1527
1528static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1529 const struct ieee80211_tx_queue_params *params)
1530{
1531 struct ath9k_htc_priv *priv = hw->priv;
1532 struct ath_common *common = ath9k_hw_common(priv->ah);
1533 struct ath9k_tx_queue_info qi;
1534 int ret = 0, qnum;
1535
1536 if (queue >= WME_NUM_AC)
1537 return 0;
1538
1539 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301540 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301541
1542 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1543
1544 qi.tqi_aifs = params->aifs;
1545 qi.tqi_cwmin = params->cw_min;
1546 qi.tqi_cwmax = params->cw_max;
1547 qi.tqi_burstTime = params->txop;
1548
1549 qnum = get_hw_qnum(queue, priv->hwq_map);
1550
Joe Perches226afe62010-12-02 19:12:37 -08001551 ath_dbg(common, ATH_DBG_CONFIG,
1552 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1553 queue, qnum, params->aifs, params->cw_min,
1554 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301555
Sujithe1572c52010-03-24 13:42:13 +05301556 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301557 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001558 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301559 goto out;
1560 }
Sujithfb9987d2010-03-17 14:25:25 +05301561
Sujith764580f2010-06-01 15:14:19 +05301562 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001563 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301564 ath9k_htc_beaconq_config(priv);
1565out:
Sujithcb551df2010-06-01 15:14:12 +05301566 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301567 mutex_unlock(&priv->mutex);
1568
1569 return ret;
1570}
1571
1572static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1573 enum set_key_cmd cmd,
1574 struct ieee80211_vif *vif,
1575 struct ieee80211_sta *sta,
1576 struct ieee80211_key_conf *key)
1577{
1578 struct ath9k_htc_priv *priv = hw->priv;
1579 struct ath_common *common = ath9k_hw_common(priv->ah);
1580 int ret = 0;
1581
Sujithe1572c52010-03-24 13:42:13 +05301582 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301583 return -ENOSPC;
1584
1585 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001586 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301587 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301588
1589 switch (cmd) {
1590 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001591 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301592 if (ret >= 0) {
1593 key->hw_key_idx = ret;
1594 /* push IV and Michael MIC generation to stack */
1595 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001596 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301597 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001598 if (priv->ah->sw_mgmt_crypto &&
1599 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301600 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1601 ret = 0;
1602 }
1603 break;
1604 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001605 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301606 break;
1607 default:
1608 ret = -EINVAL;
1609 }
1610
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301611 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301612 mutex_unlock(&priv->mutex);
1613
1614 return ret;
1615}
1616
1617static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1618 struct ieee80211_vif *vif,
1619 struct ieee80211_bss_conf *bss_conf,
1620 u32 changed)
1621{
1622 struct ath9k_htc_priv *priv = hw->priv;
1623 struct ath_hw *ah = priv->ah;
1624 struct ath_common *common = ath9k_hw_common(ah);
1625
1626 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301627 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301628
1629 if (changed & BSS_CHANGED_ASSOC) {
1630 common->curaid = bss_conf->assoc ?
1631 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001632 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301633 bss_conf->assoc);
1634
1635 if (bss_conf->assoc) {
1636 priv->op_flags |= OP_ASSOCIATED;
1637 ath_start_ani(priv);
1638 } else {
1639 priv->op_flags &= ~OP_ASSOCIATED;
1640 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1641 }
1642 }
1643
1644 if (changed & BSS_CHANGED_BSSID) {
1645 /* Set BSSID */
1646 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1647 ath9k_hw_write_associd(ah);
1648
Joe Perches226afe62010-12-02 19:12:37 -08001649 ath_dbg(common, ATH_DBG_CONFIG,
1650 "BSSID: %pM aid: 0x%x\n",
1651 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301652 }
1653
1654 if ((changed & BSS_CHANGED_BEACON_INT) ||
1655 (changed & BSS_CHANGED_BEACON) ||
1656 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1657 bss_conf->enable_beacon)) {
1658 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301659 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301660 }
1661
Sujithfb9987d2010-03-17 14:25:25 +05301662 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1663 !bss_conf->enable_beacon) {
1664 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301665 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301666 }
1667
1668 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Joe Perches226afe62010-12-02 19:12:37 -08001669 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1670 bss_conf->use_short_preamble);
Sujithfb9987d2010-03-17 14:25:25 +05301671 if (bss_conf->use_short_preamble)
1672 priv->op_flags |= OP_PREAMBLE_SHORT;
1673 else
1674 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1675 }
1676
1677 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Joe Perches226afe62010-12-02 19:12:37 -08001678 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1679 bss_conf->use_cts_prot);
Sujithfb9987d2010-03-17 14:25:25 +05301680 if (bss_conf->use_cts_prot &&
1681 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1682 priv->op_flags |= OP_PROTECT_ENABLE;
1683 else
1684 priv->op_flags &= ~OP_PROTECT_ENABLE;
1685 }
1686
1687 if (changed & BSS_CHANGED_ERP_SLOT) {
1688 if (bss_conf->use_short_slot)
1689 ah->slottime = 9;
1690 else
1691 ah->slottime = 20;
1692
1693 ath9k_hw_init_global_settings(ah);
1694 }
1695
Sujith2c76ef82010-05-17 12:01:18 +05301696 if (changed & BSS_CHANGED_HT)
1697 ath9k_htc_update_rate(priv, vif, bss_conf);
1698
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301699 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301700 mutex_unlock(&priv->mutex);
1701}
1702
1703static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1704{
1705 struct ath9k_htc_priv *priv = hw->priv;
1706 u64 tsf;
1707
1708 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301709 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301710 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301711 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301712 mutex_unlock(&priv->mutex);
1713
1714 return tsf;
1715}
1716
1717static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1718{
1719 struct ath9k_htc_priv *priv = hw->priv;
1720
1721 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301722 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301723 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301724 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301725 mutex_unlock(&priv->mutex);
1726}
1727
1728static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1729{
1730 struct ath9k_htc_priv *priv = hw->priv;
1731
1732 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301733 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301734 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301735 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301736 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301737}
1738
1739static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1740 struct ieee80211_vif *vif,
1741 enum ieee80211_ampdu_mlme_action action,
1742 struct ieee80211_sta *sta,
1743 u16 tid, u16 *ssn)
1744{
1745 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301746 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301747 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301748
1749 switch (action) {
1750 case IEEE80211_AMPDU_RX_START:
1751 break;
1752 case IEEE80211_AMPDU_RX_STOP:
1753 break;
1754 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301755 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1756 if (!ret)
1757 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1758 break;
Sujithfb9987d2010-03-17 14:25:25 +05301759 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301760 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1761 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301762 break;
1763 case IEEE80211_AMPDU_TX_OPERATIONAL:
1764 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301765 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301766 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301767 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301768 break;
1769 default:
Joe Perches38002762010-12-02 19:12:36 -08001770 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301771 }
1772
Sujithd7ca2132010-06-15 10:24:37 +05301773 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301774}
1775
1776static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1777{
1778 struct ath9k_htc_priv *priv = hw->priv;
1779
1780 mutex_lock(&priv->mutex);
1781 spin_lock_bh(&priv->beacon_lock);
1782 priv->op_flags |= OP_SCANNING;
1783 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301784 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301785 if (priv->op_flags & OP_ASSOCIATED)
1786 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301787 mutex_unlock(&priv->mutex);
1788}
1789
1790static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1791{
1792 struct ath9k_htc_priv *priv = hw->priv;
1793
1794 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301795 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301796 spin_lock_bh(&priv->beacon_lock);
1797 priv->op_flags &= ~OP_SCANNING;
1798 spin_unlock_bh(&priv->beacon_lock);
1799 priv->op_flags |= OP_FULL_RESET;
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301800 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301801 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301802 ath_start_ani(priv);
1803 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301804 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301805 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301806}
1807
1808static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1809{
1810 return 0;
1811}
1812
1813static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1814 u8 coverage_class)
1815{
1816 struct ath9k_htc_priv *priv = hw->priv;
1817
1818 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301819 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301820 priv->ah->coverage_class = coverage_class;
1821 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301822 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301823 mutex_unlock(&priv->mutex);
1824}
1825
1826struct ieee80211_ops ath9k_htc_ops = {
1827 .tx = ath9k_htc_tx,
1828 .start = ath9k_htc_start,
1829 .stop = ath9k_htc_stop,
1830 .add_interface = ath9k_htc_add_interface,
1831 .remove_interface = ath9k_htc_remove_interface,
1832 .config = ath9k_htc_config,
1833 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301834 .sta_add = ath9k_htc_sta_add,
1835 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301836 .conf_tx = ath9k_htc_conf_tx,
1837 .bss_info_changed = ath9k_htc_bss_info_changed,
1838 .set_key = ath9k_htc_set_key,
1839 .get_tsf = ath9k_htc_get_tsf,
1840 .set_tsf = ath9k_htc_set_tsf,
1841 .reset_tsf = ath9k_htc_reset_tsf,
1842 .ampdu_action = ath9k_htc_ampdu_action,
1843 .sw_scan_start = ath9k_htc_sw_scan_start,
1844 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1845 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1846 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1847 .set_coverage_class = ath9k_htc_set_coverage_class,
1848};