blob: 8266ce1f02e324a7f1d511b51d6a916d9977d538 [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
146 ath_print(common, ATH_DBG_CONFIG,
Rajkumar Manoharan7cf1f2d2010-09-03 12:41:28 +0530147 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
Sujithfb9987d2010-03-17 14:25:25 +0530148 priv->ah->curchan->channel,
Rajkumar Manoharan7cf1f2d2010-09-03 12:41:28 +0530149 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) {
155 ath_print(common, ATH_DBG_FATAL,
156 "Unable to reset channel (%u Mhz) "
157 "reset status %d\n", channel->center_freq, ret);
158 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
187static 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
228static 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)
266 ath_print(common, ATH_DBG_FATAL,
267 "Unable to add station entry for: %pM\n", sta->addr);
268 return ret;
269 }
270
271 if (sta)
272 ath_print(common, ATH_DBG_CONFIG,
273 "Added a station entry for: %pM (idx: %d)\n",
274 sta->addr, tsta.sta_index);
275
276 priv->nstations++;
277 return 0;
278}
279
280static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
281 struct ieee80211_vif *vif,
282 struct ieee80211_sta *sta)
283{
284 struct ath_common *common = ath9k_hw_common(priv->ah);
285 struct ath9k_htc_sta *ista;
286 int ret;
287 u8 cmd_rsp, sta_idx;
288
289 if (sta) {
290 ista = (struct ath9k_htc_sta *) sta->drv_priv;
291 sta_idx = ista->index;
292 } else {
293 sta_idx = 0;
294 }
295
296 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
297 if (ret) {
298 if (sta)
299 ath_print(common, ATH_DBG_FATAL,
300 "Unable to remove station entry for: %pM\n",
301 sta->addr);
302 return ret;
303 }
304
305 if (sta)
306 ath_print(common, ATH_DBG_CONFIG,
307 "Removed a station entry for: %pM (idx: %d)\n",
308 sta->addr, sta_idx);
309
310 priv->nstations--;
311 return 0;
312}
313
314static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
315{
316 struct ath9k_htc_cap_target tcap;
317 int ret;
318 u8 cmd_rsp;
319
320 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
321
322 /* FIXME: Values are hardcoded */
323 tcap.flags = 0x240c40;
324 tcap.flags_ext = 0x80601000;
325 tcap.ampdu_limit = 0xffff0000;
326 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530327 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530328 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530329 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530330
331 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
332
333 return ret;
334}
335
Sujith0d425a72010-05-17 12:01:16 +0530336static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
337 struct ieee80211_sta *sta,
338 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530339{
Sujithfb9987d2010-03-17 14:25:25 +0530340 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
341 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530342 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530343 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530344
Sujithea46e642010-06-02 15:53:50 +0530345 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530346
347 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
348 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530349 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530350 = (sband->bitrates[i].bitrate * 2) / 10;
351 j++;
352 }
353 }
Sujith0d425a72010-05-17 12:01:16 +0530354 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530355
356 if (sta->ht_cap.ht_supported) {
357 for (i = 0, j = 0; i < 77; i++) {
358 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530359 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530360 if (j == ATH_HTC_RATE_MAX)
361 break;
362 }
Sujith0d425a72010-05-17 12:01:16 +0530363 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530364
365 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200366 if (sta->ht_cap.mcs.rx_mask[1])
367 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530368 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
369 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530370 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530371 if (conf_is_ht40(&priv->hw->conf) &&
372 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530373 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530374 else if (conf_is_ht20(&priv->hw->conf) &&
375 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
376 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530377 }
378
Sujith0d425a72010-05-17 12:01:16 +0530379 trate->sta_index = ista->index;
380 trate->isnew = 1;
381 trate->capflags = cpu_to_be32(caps);
382}
Sujithfb9987d2010-03-17 14:25:25 +0530383
Sujith0d425a72010-05-17 12:01:16 +0530384static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
385 struct ath9k_htc_target_rate *trate)
386{
387 struct ath_common *common = ath9k_hw_common(priv->ah);
388 int ret;
389 u8 cmd_rsp;
390
391 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530392 if (ret) {
393 ath_print(common, ATH_DBG_FATAL,
394 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530395 }
396
Sujith0d425a72010-05-17 12:01:16 +0530397 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530398}
399
Sujith0d425a72010-05-17 12:01:16 +0530400static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
401 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530402{
Sujithfb9987d2010-03-17 14:25:25 +0530403 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530404 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530405 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530406
Sujith0d425a72010-05-17 12:01:16 +0530407 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
408 ath9k_htc_setup_rate(priv, sta, &trate);
409 ret = ath9k_htc_send_rate_cmd(priv, &trate);
410 if (!ret)
411 ath_print(common, ATH_DBG_CONFIG,
412 "Updated target sta: %pM, rate caps: 0x%X\n",
413 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530414}
415
Sujith2c76ef82010-05-17 12:01:18 +0530416static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
417 struct ieee80211_vif *vif,
418 struct ieee80211_bss_conf *bss_conf)
419{
420 struct ath_common *common = ath9k_hw_common(priv->ah);
421 struct ath9k_htc_target_rate trate;
422 struct ieee80211_sta *sta;
423 int ret;
424
425 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
426
427 rcu_read_lock();
428 sta = ieee80211_find_sta(vif, bss_conf->bssid);
429 if (!sta) {
430 rcu_read_unlock();
431 return;
432 }
433 ath9k_htc_setup_rate(priv, sta, &trate);
434 rcu_read_unlock();
435
436 ret = ath9k_htc_send_rate_cmd(priv, &trate);
437 if (!ret)
438 ath_print(common, ATH_DBG_CONFIG,
439 "Updated target sta: %pM, rate caps: 0x%X\n",
440 bss_conf->bssid, be32_to_cpu(trate.capflags));
441}
442
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400443static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
444 struct ieee80211_vif *vif,
445 struct ieee80211_sta *sta,
446 enum ieee80211_ampdu_mlme_action action,
447 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530448{
449 struct ath_common *common = ath9k_hw_common(priv->ah);
450 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200451 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530452 int ret = 0;
453 u8 cmd_rsp;
454
Dan Carpenter0730d112010-05-08 18:24:02 +0200455 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530456 return -EINVAL;
457
Sujithfb9987d2010-03-17 14:25:25 +0530458 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530459 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530460
Sujithef98c3c2010-03-29 16:07:11 +0530461 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530462 aggr.tidno = tid & 0xf;
463 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530464
Sujithfb9987d2010-03-17 14:25:25 +0530465 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
466 if (ret)
467 ath_print(common, ATH_DBG_CONFIG,
468 "Unable to %s TX aggregation for (%pM, %d)\n",
Sujithd7ca2132010-06-15 10:24:37 +0530469 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530470 else
471 ath_print(common, ATH_DBG_CONFIG,
Sujithd7ca2132010-06-15 10:24:37 +0530472 "%s TX aggregation for (%pM, %d)\n",
473 (aggr.aggr_enable) ? "Starting" : "Stopping",
474 sta->addr, tid);
475
476 spin_lock_bh(&priv->tx_lock);
477 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
478 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530479
480 return ret;
481}
482
Sujithfb9987d2010-03-17 14:25:25 +0530483/*********/
484/* DEBUG */
485/*********/
486
487#ifdef CONFIG_ATH9K_HTC_DEBUGFS
488
489static int ath9k_debugfs_open(struct inode *inode, struct file *file)
490{
491 file->private_data = inode->i_private;
492 return 0;
493}
494
495static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
496 size_t count, loff_t *ppos)
497{
Joe Perches57674302010-07-12 13:50:06 -0700498 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530499 struct ath9k_htc_target_stats cmd_rsp;
500 char buf[512];
501 unsigned int len = 0;
502 int ret = 0;
503
504 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
505
506 WMI_CMD(WMI_TGT_STATS_CMDID);
507 if (ret)
508 return -EINVAL;
509
510
511 len += snprintf(buf + len, sizeof(buf) - len,
512 "%19s : %10u\n", "TX Short Retries",
513 be32_to_cpu(cmd_rsp.tx_shortretry));
514 len += snprintf(buf + len, sizeof(buf) - len,
515 "%19s : %10u\n", "TX Long Retries",
516 be32_to_cpu(cmd_rsp.tx_longretry));
517 len += snprintf(buf + len, sizeof(buf) - len,
518 "%19s : %10u\n", "TX Xretries",
519 be32_to_cpu(cmd_rsp.tx_xretries));
520 len += snprintf(buf + len, sizeof(buf) - len,
521 "%19s : %10u\n", "TX Unaggr. Xretries",
522 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
523 len += snprintf(buf + len, sizeof(buf) - len,
524 "%19s : %10u\n", "TX Xretries (HT)",
525 be32_to_cpu(cmd_rsp.ht_tx_xretries));
526 len += snprintf(buf + len, sizeof(buf) - len,
527 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
528
Dan Carpenter97460102010-07-22 10:50:28 +0200529 if (len > sizeof(buf))
530 len = sizeof(buf);
531
Sujithfb9987d2010-03-17 14:25:25 +0530532 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
533}
534
535static const struct file_operations fops_tgt_stats = {
536 .read = read_file_tgt_stats,
537 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200538 .owner = THIS_MODULE,
539 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530540};
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,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200587 .owner = THIS_MODULE,
588 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530589};
590
591static ssize_t read_file_recv(struct file *file, char __user *user_buf,
592 size_t count, loff_t *ppos)
593{
Joe Perches57674302010-07-12 13:50:06 -0700594 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530595 char buf[512];
596 unsigned int len = 0;
597
598 len += snprintf(buf + len, sizeof(buf) - len,
599 "%20s : %10u\n", "SKBs allocated",
600 priv->debug.rx_stats.skb_allocated);
601 len += snprintf(buf + len, sizeof(buf) - len,
602 "%20s : %10u\n", "SKBs completed",
603 priv->debug.rx_stats.skb_completed);
604 len += snprintf(buf + len, sizeof(buf) - len,
605 "%20s : %10u\n", "SKBs Dropped",
606 priv->debug.rx_stats.skb_dropped);
607
Dan Carpenter97460102010-07-22 10:50:28 +0200608 if (len > sizeof(buf))
609 len = sizeof(buf);
610
Sujithfb9987d2010-03-17 14:25:25 +0530611 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
612}
613
614static const struct file_operations fops_recv = {
615 .read = read_file_recv,
616 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200617 .owner = THIS_MODULE,
618 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530619};
620
Sujithe1572c52010-03-24 13:42:13 +0530621int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530622{
623 struct ath_common *common = ath9k_hw_common(ah);
624 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
625
626 if (!ath9k_debugfs_root)
627 return -ENOENT;
628
629 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
630 ath9k_debugfs_root);
631 if (!priv->debug.debugfs_phy)
632 goto err;
633
634 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
635 priv->debug.debugfs_phy,
636 priv, &fops_tgt_stats);
637 if (!priv->debug.debugfs_tgt_stats)
638 goto err;
639
640
641 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
642 priv->debug.debugfs_phy,
643 priv, &fops_xmit);
644 if (!priv->debug.debugfs_xmit)
645 goto err;
646
647 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
648 priv->debug.debugfs_phy,
649 priv, &fops_recv);
650 if (!priv->debug.debugfs_recv)
651 goto err;
652
653 return 0;
654
655err:
Sujithe1572c52010-03-24 13:42:13 +0530656 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530657 return -ENOMEM;
658}
659
Sujithe1572c52010-03-24 13:42:13 +0530660void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530661{
662 struct ath_common *common = ath9k_hw_common(ah);
663 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
664
665 debugfs_remove(priv->debug.debugfs_recv);
666 debugfs_remove(priv->debug.debugfs_xmit);
667 debugfs_remove(priv->debug.debugfs_tgt_stats);
668 debugfs_remove(priv->debug.debugfs_phy);
669}
670
Sujithe1572c52010-03-24 13:42:13 +0530671int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530672{
673 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
674 if (!ath9k_debugfs_root)
675 return -ENOENT;
676
677 return 0;
678}
679
Sujithe1572c52010-03-24 13:42:13 +0530680void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530681{
682 debugfs_remove(ath9k_debugfs_root);
683 ath9k_debugfs_root = NULL;
684}
685
686#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
687
688/*******/
689/* ANI */
690/*******/
691
692static void ath_start_ani(struct ath9k_htc_priv *priv)
693{
694 struct ath_common *common = ath9k_hw_common(priv->ah);
695 unsigned long timestamp = jiffies_to_msecs(jiffies);
696
697 common->ani.longcal_timer = timestamp;
698 common->ani.shortcal_timer = timestamp;
699 common->ani.checkani_timer = timestamp;
700
701 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
702 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
703}
704
705void ath9k_ani_work(struct work_struct *work)
706{
707 struct ath9k_htc_priv *priv =
708 container_of(work, struct ath9k_htc_priv,
709 ath9k_ani_work.work);
710 struct ath_hw *ah = priv->ah;
711 struct ath_common *common = ath9k_hw_common(ah);
712 bool longcal = false;
713 bool shortcal = false;
714 bool aniflag = false;
715 unsigned int timestamp = jiffies_to_msecs(jiffies);
716 u32 cal_interval, short_cal_interval;
717
718 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
719
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530720 /* Only calibrate if awake */
721 if (ah->power_mode != ATH9K_PM_AWAKE)
722 goto set_timer;
723
Sujithfb9987d2010-03-17 14:25:25 +0530724 /* Long calibration runs independently of short calibration. */
725 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
726 longcal = true;
727 ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
728 common->ani.longcal_timer = timestamp;
729 }
730
731 /* Short calibration applies only while caldone is false */
732 if (!common->ani.caldone) {
733 if ((timestamp - common->ani.shortcal_timer) >=
734 short_cal_interval) {
735 shortcal = true;
736 ath_print(common, ATH_DBG_ANI,
737 "shortcal @%lu\n", jiffies);
738 common->ani.shortcal_timer = timestamp;
739 common->ani.resetcal_timer = timestamp;
740 }
741 } else {
742 if ((timestamp - common->ani.resetcal_timer) >=
743 ATH_RESTART_CALINTERVAL) {
744 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
745 if (common->ani.caldone)
746 common->ani.resetcal_timer = timestamp;
747 }
748 }
749
750 /* Verify whether we must check ANI */
751 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
752 aniflag = true;
753 common->ani.checkani_timer = timestamp;
754 }
755
756 /* Skip all processing if there's nothing to do. */
757 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530758
759 ath9k_htc_ps_wakeup(priv);
760
Sujithfb9987d2010-03-17 14:25:25 +0530761 /* Call ANI routine if necessary */
762 if (aniflag)
763 ath9k_hw_ani_monitor(ah, ah->curchan);
764
765 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200766 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530767 common->ani.caldone =
768 ath9k_hw_calibrate(ah, ah->curchan,
769 common->rx_chainmask,
770 longcal);
771
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530772 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530773 }
774
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530775set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530776 /*
777 * Set timer interval based on previous results.
778 * The interval must be the shortest necessary to satisfy ANI,
779 * short calibration and long calibration.
780 */
781 cal_interval = ATH_LONG_CALINTERVAL;
782 if (priv->ah->config.enable_ani)
783 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
784 if (!common->ani.caldone)
785 cal_interval = min(cal_interval, (u32)short_cal_interval);
786
787 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
788 msecs_to_jiffies(cal_interval));
789}
790
791/*******/
792/* LED */
793/*******/
794
795static void ath9k_led_blink_work(struct work_struct *work)
796{
797 struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
798 ath9k_led_blink_work.work);
799
800 if (!(priv->op_flags & OP_LED_ASSOCIATED))
801 return;
802
803 if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
804 (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
805 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
806 else
807 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
808 (priv->op_flags & OP_LED_ON) ? 1 : 0);
809
810 ieee80211_queue_delayed_work(priv->hw,
811 &priv->ath9k_led_blink_work,
812 (priv->op_flags & OP_LED_ON) ?
813 msecs_to_jiffies(priv->led_off_duration) :
814 msecs_to_jiffies(priv->led_on_duration));
815
816 priv->led_on_duration = priv->led_on_cnt ?
817 max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) :
818 ATH_LED_ON_DURATION_IDLE;
819 priv->led_off_duration = priv->led_off_cnt ?
820 max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) :
821 ATH_LED_OFF_DURATION_IDLE;
822 priv->led_on_cnt = priv->led_off_cnt = 0;
823
824 if (priv->op_flags & OP_LED_ON)
825 priv->op_flags &= ~OP_LED_ON;
826 else
827 priv->op_flags |= OP_LED_ON;
828}
829
830static void ath9k_led_brightness_work(struct work_struct *work)
831{
832 struct ath_led *led = container_of(work, struct ath_led,
833 brightness_work.work);
834 struct ath9k_htc_priv *priv = led->priv;
835
836 switch (led->brightness) {
837 case LED_OFF:
838 if (led->led_type == ATH_LED_ASSOC ||
839 led->led_type == ATH_LED_RADIO) {
840 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
841 (led->led_type == ATH_LED_RADIO));
842 priv->op_flags &= ~OP_LED_ASSOCIATED;
843 if (led->led_type == ATH_LED_RADIO)
844 priv->op_flags &= ~OP_LED_ON;
845 } else {
846 priv->led_off_cnt++;
847 }
848 break;
849 case LED_FULL:
850 if (led->led_type == ATH_LED_ASSOC) {
851 priv->op_flags |= OP_LED_ASSOCIATED;
852 ieee80211_queue_delayed_work(priv->hw,
853 &priv->ath9k_led_blink_work, 0);
854 } else if (led->led_type == ATH_LED_RADIO) {
855 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
856 priv->op_flags |= OP_LED_ON;
857 } else {
858 priv->led_on_cnt++;
859 }
860 break;
861 default:
862 break;
863 }
864}
865
866static void ath9k_led_brightness(struct led_classdev *led_cdev,
867 enum led_brightness brightness)
868{
869 struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
870 struct ath9k_htc_priv *priv = led->priv;
871
872 led->brightness = brightness;
873 if (!(priv->op_flags & OP_LED_DEINIT))
874 ieee80211_queue_delayed_work(priv->hw,
875 &led->brightness_work, 0);
876}
877
878static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv)
879{
880 cancel_delayed_work_sync(&priv->radio_led.brightness_work);
881 cancel_delayed_work_sync(&priv->assoc_led.brightness_work);
882 cancel_delayed_work_sync(&priv->tx_led.brightness_work);
883 cancel_delayed_work_sync(&priv->rx_led.brightness_work);
884}
885
886static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led,
887 char *trigger)
888{
889 int ret;
890
891 led->priv = priv;
892 led->led_cdev.name = led->name;
893 led->led_cdev.default_trigger = trigger;
894 led->led_cdev.brightness_set = ath9k_led_brightness;
895
896 ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev);
897 if (ret)
898 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
899 "Failed to register led:%s", led->name);
900 else
901 led->registered = 1;
902
903 INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work);
904
905 return ret;
906}
907
908static void ath9k_unregister_led(struct ath_led *led)
909{
910 if (led->registered) {
911 led_classdev_unregister(&led->led_cdev);
912 led->registered = 0;
913 }
914}
915
916void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
917{
918 priv->op_flags |= OP_LED_DEINIT;
919 ath9k_unregister_led(&priv->assoc_led);
920 priv->op_flags &= ~OP_LED_ASSOCIATED;
921 ath9k_unregister_led(&priv->tx_led);
922 ath9k_unregister_led(&priv->rx_led);
923 ath9k_unregister_led(&priv->radio_led);
Sujithfb9987d2010-03-17 14:25:25 +0530924}
925
926void ath9k_init_leds(struct ath9k_htc_priv *priv)
927{
928 char *trigger;
929 int ret;
930
931 if (AR_SREV_9287(priv->ah))
932 priv->ah->led_pin = ATH_LED_PIN_9287;
933 else if (AR_SREV_9271(priv->ah))
934 priv->ah->led_pin = ATH_LED_PIN_9271;
Sujith88c1f4f2010-06-30 14:46:31 +0530935 else if (AR_DEVID_7010(priv->ah))
936 priv->ah->led_pin = ATH_LED_PIN_7010;
Sujithfb9987d2010-03-17 14:25:25 +0530937 else
938 priv->ah->led_pin = ATH_LED_PIN_DEF;
939
940 /* Configure gpio 1 for output */
941 ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
942 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
943 /* LED off, active low */
944 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
945
946 INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work);
947
948 trigger = ieee80211_get_radio_led_name(priv->hw);
949 snprintf(priv->radio_led.name, sizeof(priv->radio_led.name),
950 "ath9k-%s::radio", wiphy_name(priv->hw->wiphy));
951 ret = ath9k_register_led(priv, &priv->radio_led, trigger);
952 priv->radio_led.led_type = ATH_LED_RADIO;
953 if (ret)
954 goto fail;
955
956 trigger = ieee80211_get_assoc_led_name(priv->hw);
957 snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name),
958 "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy));
959 ret = ath9k_register_led(priv, &priv->assoc_led, trigger);
960 priv->assoc_led.led_type = ATH_LED_ASSOC;
961 if (ret)
962 goto fail;
963
964 trigger = ieee80211_get_tx_led_name(priv->hw);
965 snprintf(priv->tx_led.name, sizeof(priv->tx_led.name),
966 "ath9k-%s::tx", wiphy_name(priv->hw->wiphy));
967 ret = ath9k_register_led(priv, &priv->tx_led, trigger);
968 priv->tx_led.led_type = ATH_LED_TX;
969 if (ret)
970 goto fail;
971
972 trigger = ieee80211_get_rx_led_name(priv->hw);
973 snprintf(priv->rx_led.name, sizeof(priv->rx_led.name),
974 "ath9k-%s::rx", wiphy_name(priv->hw->wiphy));
975 ret = ath9k_register_led(priv, &priv->rx_led, trigger);
976 priv->rx_led.led_type = ATH_LED_RX;
977 if (ret)
978 goto fail;
979
980 priv->op_flags &= ~OP_LED_DEINIT;
981
982 return;
983
984fail:
985 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
986 ath9k_deinit_leds(priv);
987}
988
989/*******************/
990/* Rfkill */
991/*******************/
992
993static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
994{
995 return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
996 priv->ah->rfkill_polarity;
997}
998
999static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
1000{
1001 struct ath9k_htc_priv *priv = hw->priv;
1002 bool blocked = !!ath_is_rfkill_set(priv);
1003
1004 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
1005}
1006
1007void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv)
1008{
1009 if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1010 wiphy_rfkill_start_polling(priv->hw->wiphy);
1011}
1012
Sujith881ac6a2010-06-01 15:14:11 +05301013static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
1014{
1015 struct ath9k_htc_priv *priv = hw->priv;
1016 struct ath_hw *ah = priv->ah;
1017 struct ath_common *common = ath9k_hw_common(ah);
1018 int ret;
1019 u8 cmd_rsp;
1020
1021 if (!ah->curchan)
1022 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1023
1024 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001025 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301026 if (ret) {
1027 ath_print(common, ATH_DBG_FATAL,
1028 "Unable to reset hardware; reset status %d "
1029 "(freq %u MHz)\n", ret, ah->curchan->channel);
1030 }
1031
1032 ath_update_txpow(priv);
1033
1034 /* Start RX */
1035 WMI_CMD(WMI_START_RECV_CMDID);
1036 ath9k_host_rx_init(priv);
1037
1038 /* Start TX */
1039 htc_start(priv->htc);
1040 spin_lock_bh(&priv->tx_lock);
1041 priv->tx_queues_stop = false;
1042 spin_unlock_bh(&priv->tx_lock);
1043 ieee80211_wake_queues(hw);
1044
1045 WMI_CMD(WMI_ENABLE_INTR_CMDID);
1046
1047 /* Enable LED */
1048 ath9k_hw_cfg_output(ah, ah->led_pin,
1049 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1050 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
1051}
1052
1053static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
1054{
1055 struct ath9k_htc_priv *priv = hw->priv;
1056 struct ath_hw *ah = priv->ah;
1057 struct ath_common *common = ath9k_hw_common(ah);
1058 int ret;
1059 u8 cmd_rsp;
1060
1061 ath9k_htc_ps_wakeup(priv);
1062
1063 /* Disable LED */
1064 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1065 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
1066
1067 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1068
1069 /* Stop TX */
1070 ieee80211_stop_queues(hw);
1071 htc_stop(priv->htc);
1072 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1073 skb_queue_purge(&priv->tx_queue);
1074
1075 /* Stop RX */
1076 WMI_CMD(WMI_STOP_RECV_CMDID);
1077
Sujith21d51302010-06-01 15:14:18 +05301078 /*
1079 * The MIB counters have to be disabled here,
1080 * since the target doesn't do it.
1081 */
1082 ath9k_hw_disable_mib_counters(ah);
1083
Sujith881ac6a2010-06-01 15:14:11 +05301084 if (!ah->curchan)
1085 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1086
1087 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001088 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301089 if (ret) {
1090 ath_print(common, ATH_DBG_FATAL,
1091 "Unable to reset hardware; reset status %d "
1092 "(freq %u MHz)\n", ret, ah->curchan->channel);
1093 }
1094
1095 /* Disable the PHY */
1096 ath9k_hw_phy_disable(ah);
1097
1098 ath9k_htc_ps_restore(priv);
1099 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1100}
1101
Sujithfb9987d2010-03-17 14:25:25 +05301102/**********************/
1103/* mac80211 Callbacks */
1104/**********************/
1105
1106static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1107{
1108 struct ieee80211_hdr *hdr;
1109 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301110 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301111
1112 hdr = (struct ieee80211_hdr *) skb->data;
1113
1114 /* Add the padding after the header if this is not already done */
1115 padpos = ath9k_cmn_padpos(hdr->frame_control);
1116 padsize = padpos & 3;
1117 if (padsize && skb->len > padpos) {
1118 if (skb_headroom(skb) < padsize)
1119 return -1;
1120 skb_push(skb, padsize);
1121 memmove(skb->data, skb->data + padsize, padpos);
1122 }
1123
Sujith7757dfe2010-03-29 16:07:17 +05301124 ret = ath9k_htc_tx_start(priv, skb);
1125 if (ret != 0) {
1126 if (ret == -ENOMEM) {
1127 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1128 "Stopping TX queues\n");
1129 ieee80211_stop_queues(hw);
1130 spin_lock_bh(&priv->tx_lock);
1131 priv->tx_queues_stop = true;
1132 spin_unlock_bh(&priv->tx_lock);
1133 } else {
1134 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1135 "Tx failed");
1136 }
Sujithfb9987d2010-03-17 14:25:25 +05301137 goto fail_tx;
1138 }
1139
1140 return 0;
1141
1142fail_tx:
1143 dev_kfree_skb_any(skb);
1144 return 0;
1145}
1146
Sujith881ac6a2010-06-01 15:14:11 +05301147static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301148{
1149 struct ath9k_htc_priv *priv = hw->priv;
1150 struct ath_hw *ah = priv->ah;
1151 struct ath_common *common = ath9k_hw_common(ah);
1152 struct ieee80211_channel *curchan = hw->conf.channel;
1153 struct ath9k_channel *init_channel;
1154 int ret = 0;
1155 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301156 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301157 u8 cmd_rsp;
1158
Sujith881ac6a2010-06-01 15:14:11 +05301159 mutex_lock(&priv->mutex);
1160
Sujithfb9987d2010-03-17 14:25:25 +05301161 ath_print(common, ATH_DBG_CONFIG,
1162 "Starting driver with initial channel: %d MHz\n",
1163 curchan->center_freq);
1164
Sujith21d51302010-06-01 15:14:18 +05301165 /* Ensure that HW is awake before flushing RX */
1166 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1167 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1168
Sujithfb9987d2010-03-17 14:25:25 +05301169 /* setup initial channel */
1170 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1171
1172 /* Reset SERDES registers */
1173 ath9k_hw_configpcipowersave(ah, 0, 0);
1174
1175 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001176 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301177 if (ret) {
1178 ath_print(common, ATH_DBG_FATAL,
1179 "Unable to reset hardware; reset status %d "
1180 "(freq %u MHz)\n", ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301181 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301182 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301183 }
1184
1185 ath_update_txpow(priv);
1186
1187 mode = ath9k_htc_get_curmode(priv, init_channel);
1188 htc_mode = cpu_to_be16(mode);
1189 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301190 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301191 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301192
1193 ath9k_host_rx_init(priv);
1194
1195 priv->op_flags &= ~OP_INVALID;
1196 htc_start(priv->htc);
1197
Sujith7757dfe2010-03-29 16:07:17 +05301198 spin_lock_bh(&priv->tx_lock);
1199 priv->tx_queues_stop = false;
1200 spin_unlock_bh(&priv->tx_lock);
1201
1202 ieee80211_wake_queues(hw);
1203
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301204 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1205 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1206 AR_STOMP_LOW_WLAN_WGHT);
1207 ath9k_hw_btcoex_enable(ah);
1208 ath_htc_resume_btcoex_work(priv);
1209 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301210 mutex_unlock(&priv->mutex);
1211
1212 return ret;
1213}
1214
Sujith881ac6a2010-06-01 15:14:11 +05301215static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301216{
1217 struct ath9k_htc_priv *priv = hw->priv;
1218 struct ath_hw *ah = priv->ah;
1219 struct ath_common *common = ath9k_hw_common(ah);
1220 int ret = 0;
1221 u8 cmd_rsp;
1222
Sujith881ac6a2010-06-01 15:14:11 +05301223 mutex_lock(&priv->mutex);
1224
Sujithfb9987d2010-03-17 14:25:25 +05301225 if (priv->op_flags & OP_INVALID) {
1226 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301227 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301228 return;
1229 }
1230
Sujith7073daa2010-04-23 10:28:13 +05301231 /* Cancel all the running timers/work .. */
1232 cancel_work_sync(&priv->ps_work);
Sujith7073daa2010-04-23 10:28:13 +05301233 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1234 ath9k_led_stop_brightness(priv);
1235
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301236 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301237 htc_stop(priv->htc);
1238 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1239 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1240 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301241 skb_queue_purge(&priv->tx_queue);
1242
1243 /* Remove monitor interface here */
1244 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1245 if (ath9k_htc_remove_monitor_interface(priv))
1246 ath_print(common, ATH_DBG_FATAL,
1247 "Unable to remove monitor interface\n");
1248 else
1249 ath_print(common, ATH_DBG_CONFIG,
1250 "Monitor interface removed\n");
1251 }
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
1267 ath_print(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:
1301 ath_print(common, ATH_DBG_FATAL,
1302 "Interface type %d not yet supported\n", vif->type);
1303 ret = -EOPNOTSUPP;
1304 goto out;
1305 }
1306
1307 ath_print(common, ATH_DBG_CONFIG,
1308 "Attach a VIF of type: %d\n", vif->type);
1309
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)
1331 ath_print(common, ATH_DBG_CONFIG, "Failed to update"
1332 " capability in target \n");
1333
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
1352 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
1353
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) {
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301389 ath_print(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
1400 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1401 curchan->center_freq);
1402
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) {
1408 ath_print(common, ATH_DBG_FATAL,
1409 "Unable to set channel\n");
1410 mutex_unlock(&priv->mutex);
1411 return -EINVAL;
1412 }
1413
1414 }
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
1426 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1427 if (conf->flags & IEEE80211_CONF_MONITOR) {
1428 if (ath9k_htc_add_monitor_interface(priv))
1429 ath_print(common, ATH_DBG_FATAL,
1430 "Failed to set monitor mode\n");
1431 else
1432 ath_print(common, ATH_DBG_CONFIG,
1433 "HW opmode set to Monitor mode\n");
1434 }
1435 }
1436
Sujith23367762010-06-01 15:14:16 +05301437 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1438 mutex_lock(&priv->htc_pm_lock);
1439 if (!priv->ps_idle) {
1440 mutex_unlock(&priv->htc_pm_lock);
1441 goto out;
1442 }
1443 mutex_unlock(&priv->htc_pm_lock);
1444
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301445 ath_print(common, ATH_DBG_CONFIG,
1446 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301447 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301448 }
1449
Sujith23367762010-06-01 15:14:16 +05301450out:
Sujithfb9987d2010-03-17 14:25:25 +05301451 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301452 return 0;
1453}
1454
1455#define SUPPORTED_FILTERS \
1456 (FIF_PROMISC_IN_BSS | \
1457 FIF_ALLMULTI | \
1458 FIF_CONTROL | \
1459 FIF_PSPOLL | \
1460 FIF_OTHER_BSS | \
1461 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301462 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301463 FIF_FCSFAIL)
1464
1465static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1466 unsigned int changed_flags,
1467 unsigned int *total_flags,
1468 u64 multicast)
1469{
1470 struct ath9k_htc_priv *priv = hw->priv;
1471 u32 rfilt;
1472
1473 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301474 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301475
Sujithfb9987d2010-03-17 14:25:25 +05301476 changed_flags &= SUPPORTED_FILTERS;
1477 *total_flags &= SUPPORTED_FILTERS;
1478
1479 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301480 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301481 ath9k_hw_setrxfilter(priv->ah, rfilt);
1482
1483 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1484 "Set HW RX filter: 0x%x\n", rfilt);
1485
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301486 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301487 mutex_unlock(&priv->mutex);
1488}
1489
Sujithabd984e2010-05-18 15:26:04 +05301490static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1491 struct ieee80211_vif *vif,
1492 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301493{
1494 struct ath9k_htc_priv *priv = hw->priv;
1495 int ret;
1496
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301497 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301498 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301499 ret = ath9k_htc_add_station(priv, vif, sta);
1500 if (!ret)
1501 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301502 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301503 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301504
1505 return ret;
1506}
1507
1508static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1509 struct ieee80211_vif *vif,
1510 struct ieee80211_sta *sta)
1511{
1512 struct ath9k_htc_priv *priv = hw->priv;
1513 int ret;
1514
1515 mutex_lock(&priv->mutex);
1516 ath9k_htc_ps_wakeup(priv);
1517 ret = ath9k_htc_remove_station(priv, vif, sta);
1518 ath9k_htc_ps_restore(priv);
1519 mutex_unlock(&priv->mutex);
1520
1521 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301522}
1523
1524static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1525 const struct ieee80211_tx_queue_params *params)
1526{
1527 struct ath9k_htc_priv *priv = hw->priv;
1528 struct ath_common *common = ath9k_hw_common(priv->ah);
1529 struct ath9k_tx_queue_info qi;
1530 int ret = 0, qnum;
1531
1532 if (queue >= WME_NUM_AC)
1533 return 0;
1534
1535 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301536 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301537
1538 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1539
1540 qi.tqi_aifs = params->aifs;
1541 qi.tqi_cwmin = params->cw_min;
1542 qi.tqi_cwmax = params->cw_max;
1543 qi.tqi_burstTime = params->txop;
1544
1545 qnum = get_hw_qnum(queue, priv->hwq_map);
1546
1547 ath_print(common, ATH_DBG_CONFIG,
1548 "Configure tx [queue/hwq] [%d/%d], "
1549 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1550 queue, qnum, params->aifs, params->cw_min,
1551 params->cw_max, params->txop);
1552
Sujithe1572c52010-03-24 13:42:13 +05301553 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301554 if (ret) {
Sujithfb9987d2010-03-17 14:25:25 +05301555 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301556 goto out;
1557 }
Sujithfb9987d2010-03-17 14:25:25 +05301558
Sujith764580f2010-06-01 15:14:19 +05301559 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001560 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301561 ath9k_htc_beaconq_config(priv);
1562out:
Sujithcb551df2010-06-01 15:14:12 +05301563 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301564 mutex_unlock(&priv->mutex);
1565
1566 return ret;
1567}
1568
1569static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1570 enum set_key_cmd cmd,
1571 struct ieee80211_vif *vif,
1572 struct ieee80211_sta *sta,
1573 struct ieee80211_key_conf *key)
1574{
1575 struct ath9k_htc_priv *priv = hw->priv;
1576 struct ath_common *common = ath9k_hw_common(priv->ah);
1577 int ret = 0;
1578
Sujithe1572c52010-03-24 13:42:13 +05301579 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301580 return -ENOSPC;
1581
1582 mutex_lock(&priv->mutex);
1583 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301584 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301585
1586 switch (cmd) {
1587 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001588 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301589 if (ret >= 0) {
1590 key->hw_key_idx = ret;
1591 /* push IV and Michael MIC generation to stack */
1592 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001593 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301594 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001595 if (priv->ah->sw_mgmt_crypto &&
1596 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301597 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1598 ret = 0;
1599 }
1600 break;
1601 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001602 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301603 break;
1604 default:
1605 ret = -EINVAL;
1606 }
1607
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301608 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301609 mutex_unlock(&priv->mutex);
1610
1611 return ret;
1612}
1613
1614static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1615 struct ieee80211_vif *vif,
1616 struct ieee80211_bss_conf *bss_conf,
1617 u32 changed)
1618{
1619 struct ath9k_htc_priv *priv = hw->priv;
1620 struct ath_hw *ah = priv->ah;
1621 struct ath_common *common = ath9k_hw_common(ah);
1622
1623 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301624 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301625
1626 if (changed & BSS_CHANGED_ASSOC) {
1627 common->curaid = bss_conf->assoc ?
1628 bss_conf->aid : 0;
1629 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1630 bss_conf->assoc);
1631
1632 if (bss_conf->assoc) {
1633 priv->op_flags |= OP_ASSOCIATED;
1634 ath_start_ani(priv);
1635 } else {
1636 priv->op_flags &= ~OP_ASSOCIATED;
1637 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1638 }
1639 }
1640
1641 if (changed & BSS_CHANGED_BSSID) {
1642 /* Set BSSID */
1643 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1644 ath9k_hw_write_associd(ah);
1645
1646 ath_print(common, ATH_DBG_CONFIG,
1647 "BSSID: %pM aid: 0x%x\n",
1648 common->curbssid, common->curaid);
1649 }
1650
1651 if ((changed & BSS_CHANGED_BEACON_INT) ||
1652 (changed & BSS_CHANGED_BEACON) ||
1653 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1654 bss_conf->enable_beacon)) {
1655 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301656 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301657 }
1658
Sujithfb9987d2010-03-17 14:25:25 +05301659 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1660 !bss_conf->enable_beacon) {
1661 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301662 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301663 }
1664
1665 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1666 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1667 bss_conf->use_short_preamble);
1668 if (bss_conf->use_short_preamble)
1669 priv->op_flags |= OP_PREAMBLE_SHORT;
1670 else
1671 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1672 }
1673
1674 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1675 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1676 bss_conf->use_cts_prot);
1677 if (bss_conf->use_cts_prot &&
1678 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1679 priv->op_flags |= OP_PROTECT_ENABLE;
1680 else
1681 priv->op_flags &= ~OP_PROTECT_ENABLE;
1682 }
1683
1684 if (changed & BSS_CHANGED_ERP_SLOT) {
1685 if (bss_conf->use_short_slot)
1686 ah->slottime = 9;
1687 else
1688 ah->slottime = 20;
1689
1690 ath9k_hw_init_global_settings(ah);
1691 }
1692
Sujith2c76ef82010-05-17 12:01:18 +05301693 if (changed & BSS_CHANGED_HT)
1694 ath9k_htc_update_rate(priv, vif, bss_conf);
1695
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301696 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301697 mutex_unlock(&priv->mutex);
1698}
1699
1700static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1701{
1702 struct ath9k_htc_priv *priv = hw->priv;
1703 u64 tsf;
1704
1705 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301706 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301707 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301708 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301709 mutex_unlock(&priv->mutex);
1710
1711 return tsf;
1712}
1713
1714static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1715{
1716 struct ath9k_htc_priv *priv = hw->priv;
1717
1718 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301719 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301720 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301721 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301722 mutex_unlock(&priv->mutex);
1723}
1724
1725static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1726{
1727 struct ath9k_htc_priv *priv = hw->priv;
1728
1729 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301730 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301731 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301732 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301733 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301734}
1735
1736static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1737 struct ieee80211_vif *vif,
1738 enum ieee80211_ampdu_mlme_action action,
1739 struct ieee80211_sta *sta,
1740 u16 tid, u16 *ssn)
1741{
1742 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301743 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301744 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301745
1746 switch (action) {
1747 case IEEE80211_AMPDU_RX_START:
1748 break;
1749 case IEEE80211_AMPDU_RX_STOP:
1750 break;
1751 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301752 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1753 if (!ret)
1754 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1755 break;
Sujithfb9987d2010-03-17 14:25:25 +05301756 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301757 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1758 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301759 break;
1760 case IEEE80211_AMPDU_TX_OPERATIONAL:
1761 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301762 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301763 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301764 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301765 break;
1766 default:
1767 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
1768 "Unknown AMPDU action\n");
1769 }
1770
Sujithd7ca2132010-06-15 10:24:37 +05301771 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301772}
1773
1774static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1775{
1776 struct ath9k_htc_priv *priv = hw->priv;
1777
1778 mutex_lock(&priv->mutex);
1779 spin_lock_bh(&priv->beacon_lock);
1780 priv->op_flags |= OP_SCANNING;
1781 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301782 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301783 if (priv->op_flags & OP_ASSOCIATED)
1784 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301785 mutex_unlock(&priv->mutex);
1786}
1787
1788static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1789{
1790 struct ath9k_htc_priv *priv = hw->priv;
1791
1792 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301793 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301794 spin_lock_bh(&priv->beacon_lock);
1795 priv->op_flags &= ~OP_SCANNING;
1796 spin_unlock_bh(&priv->beacon_lock);
1797 priv->op_flags |= OP_FULL_RESET;
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301798 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301799 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301800 ath_start_ani(priv);
1801 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301802 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301803 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301804}
1805
1806static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1807{
1808 return 0;
1809}
1810
1811static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1812 u8 coverage_class)
1813{
1814 struct ath9k_htc_priv *priv = hw->priv;
1815
1816 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301817 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301818 priv->ah->coverage_class = coverage_class;
1819 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301820 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301821 mutex_unlock(&priv->mutex);
1822}
1823
1824struct ieee80211_ops ath9k_htc_ops = {
1825 .tx = ath9k_htc_tx,
1826 .start = ath9k_htc_start,
1827 .stop = ath9k_htc_stop,
1828 .add_interface = ath9k_htc_add_interface,
1829 .remove_interface = ath9k_htc_remove_interface,
1830 .config = ath9k_htc_config,
1831 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301832 .sta_add = ath9k_htc_sta_add,
1833 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301834 .conf_tx = ath9k_htc_conf_tx,
1835 .bss_info_changed = ath9k_htc_bss_info_changed,
1836 .set_key = ath9k_htc_set_key,
1837 .get_tsf = ath9k_htc_get_tsf,
1838 .set_tsf = ath9k_htc_set_tsf,
1839 .reset_tsf = ath9k_htc_reset_tsf,
1840 .ampdu_action = ath9k_htc_ampdu_action,
1841 .sw_scan_start = ath9k_htc_sw_scan_start,
1842 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1843 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1844 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1845 .set_coverage_class = ath9k_htc_set_coverage_class,
1846};