blob: 5124d04b240b5d09eb21ad35f2122905dda55afc [file] [log] [blame]
Sujithfb9987d2010-03-17 14:25:25 +05301/*
2 * Copyright (c) 2010 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "htc.h"
18
19#ifdef CONFIG_ATH9K_HTC_DEBUGFS
20static struct dentry *ath9k_debugfs_root;
21#endif
22
23/*************/
24/* Utilities */
25/*************/
26
27static void ath_update_txpow(struct ath9k_htc_priv *priv)
28{
29 struct ath_hw *ah = priv->ah;
Sujithfb9987d2010-03-17 14:25:25 +053030
31 if (priv->curtxpow != priv->txpowlimit) {
32 ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit);
33 /* read back in case value is clamped */
Felix Fietkau9cc32712010-06-12 17:22:29 +020034 priv->curtxpow = ath9k_hw_regulatory(ah)->power_limit;
Sujithfb9987d2010-03-17 14:25:25 +053035 }
36}
37
38/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
39static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
40 struct ath9k_channel *ichan)
41{
42 enum htc_phymode mode;
43
44 mode = HTC_MODE_AUTO;
45
46 switch (ichan->chanmode) {
47 case CHANNEL_G:
48 case CHANNEL_G_HT20:
49 case CHANNEL_G_HT40PLUS:
50 case CHANNEL_G_HT40MINUS:
51 mode = HTC_MODE_11NG;
52 break;
53 case CHANNEL_A:
54 case CHANNEL_A_HT20:
55 case CHANNEL_A_HT40PLUS:
56 case CHANNEL_A_HT40MINUS:
57 mode = HTC_MODE_11NA;
58 break;
59 default:
60 break;
61 }
62
63 return mode;
64}
65
Vivek Natarajanbde748a2010-04-05 14:48:05 +053066static bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
67 enum ath9k_power_mode mode)
68{
69 bool ret;
70
71 mutex_lock(&priv->htc_pm_lock);
72 ret = ath9k_hw_setpower(priv->ah, mode);
73 mutex_unlock(&priv->htc_pm_lock);
74
75 return ret;
76}
77
78void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
79{
80 mutex_lock(&priv->htc_pm_lock);
81 if (++priv->ps_usecount != 1)
82 goto unlock;
83 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
84
85unlock:
86 mutex_unlock(&priv->htc_pm_lock);
87}
88
89void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
90{
91 mutex_lock(&priv->htc_pm_lock);
92 if (--priv->ps_usecount != 0)
93 goto unlock;
94
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053095 if (priv->ps_idle)
96 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
97 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053098 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053099
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530100unlock:
101 mutex_unlock(&priv->htc_pm_lock);
102}
103
104void ath9k_ps_work(struct work_struct *work)
105{
106 struct ath9k_htc_priv *priv =
107 container_of(work, struct ath9k_htc_priv,
108 ps_work);
109 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
110
111 /* The chip wakes up after receiving the first beacon
112 while network sleep is enabled. For the driver to
113 be in sync with the hw, set the chip to awake and
114 only then set it to sleep.
115 */
116 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
117}
118
Sujithfb9987d2010-03-17 14:25:25 +0530119static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
120 struct ieee80211_hw *hw,
121 struct ath9k_channel *hchan)
122{
123 struct ath_hw *ah = priv->ah;
124 struct ath_common *common = ath9k_hw_common(ah);
125 struct ieee80211_conf *conf = &common->hw->conf;
126 bool fastcc = true;
127 struct ieee80211_channel *channel = hw->conf.channel;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200128 struct ath9k_hw_cal_data *caldata;
Sujithfb9987d2010-03-17 14:25:25 +0530129 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530130 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530131 u8 cmd_rsp;
132 int ret;
133
134 if (priv->op_flags & OP_INVALID)
135 return -EIO;
136
137 if (priv->op_flags & OP_FULL_RESET)
138 fastcc = false;
139
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,
538 .owner = THIS_MODULE
539};
540
541static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
542 size_t count, loff_t *ppos)
543{
Joe Perches57674302010-07-12 13:50:06 -0700544 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530545 char buf[512];
546 unsigned int len = 0;
547
548 len += snprintf(buf + len, sizeof(buf) - len,
549 "%20s : %10u\n", "Buffers queued",
550 priv->debug.tx_stats.buf_queued);
551 len += snprintf(buf + len, sizeof(buf) - len,
552 "%20s : %10u\n", "Buffers completed",
553 priv->debug.tx_stats.buf_completed);
554 len += snprintf(buf + len, sizeof(buf) - len,
555 "%20s : %10u\n", "SKBs queued",
556 priv->debug.tx_stats.skb_queued);
557 len += snprintf(buf + len, sizeof(buf) - len,
558 "%20s : %10u\n", "SKBs completed",
559 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530560 len += snprintf(buf + len, sizeof(buf) - len,
561 "%20s : %10u\n", "SKBs dropped",
562 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530563
Sujith2edb4582010-05-14 11:18:54 +0530564 len += snprintf(buf + len, sizeof(buf) - len,
565 "%20s : %10u\n", "BE queued",
566 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
567 len += snprintf(buf + len, sizeof(buf) - len,
568 "%20s : %10u\n", "BK queued",
569 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
570 len += snprintf(buf + len, sizeof(buf) - len,
571 "%20s : %10u\n", "VI queued",
572 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
573 len += snprintf(buf + len, sizeof(buf) - len,
574 "%20s : %10u\n", "VO queued",
575 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
576
Dan Carpenter97460102010-07-22 10:50:28 +0200577 if (len > sizeof(buf))
578 len = sizeof(buf);
579
Sujithfb9987d2010-03-17 14:25:25 +0530580 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
581}
582
583static const struct file_operations fops_xmit = {
584 .read = read_file_xmit,
585 .open = ath9k_debugfs_open,
586 .owner = THIS_MODULE
587};
588
589static ssize_t read_file_recv(struct file *file, char __user *user_buf,
590 size_t count, loff_t *ppos)
591{
Joe Perches57674302010-07-12 13:50:06 -0700592 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530593 char buf[512];
594 unsigned int len = 0;
595
596 len += snprintf(buf + len, sizeof(buf) - len,
597 "%20s : %10u\n", "SKBs allocated",
598 priv->debug.rx_stats.skb_allocated);
599 len += snprintf(buf + len, sizeof(buf) - len,
600 "%20s : %10u\n", "SKBs completed",
601 priv->debug.rx_stats.skb_completed);
602 len += snprintf(buf + len, sizeof(buf) - len,
603 "%20s : %10u\n", "SKBs Dropped",
604 priv->debug.rx_stats.skb_dropped);
605
Dan Carpenter97460102010-07-22 10:50:28 +0200606 if (len > sizeof(buf))
607 len = sizeof(buf);
608
Sujithfb9987d2010-03-17 14:25:25 +0530609 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
610}
611
612static const struct file_operations fops_recv = {
613 .read = read_file_recv,
614 .open = ath9k_debugfs_open,
615 .owner = THIS_MODULE
616};
617
Sujithe1572c52010-03-24 13:42:13 +0530618int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530619{
620 struct ath_common *common = ath9k_hw_common(ah);
621 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
622
623 if (!ath9k_debugfs_root)
624 return -ENOENT;
625
626 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
627 ath9k_debugfs_root);
628 if (!priv->debug.debugfs_phy)
629 goto err;
630
631 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
632 priv->debug.debugfs_phy,
633 priv, &fops_tgt_stats);
634 if (!priv->debug.debugfs_tgt_stats)
635 goto err;
636
637
638 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
639 priv->debug.debugfs_phy,
640 priv, &fops_xmit);
641 if (!priv->debug.debugfs_xmit)
642 goto err;
643
644 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
645 priv->debug.debugfs_phy,
646 priv, &fops_recv);
647 if (!priv->debug.debugfs_recv)
648 goto err;
649
650 return 0;
651
652err:
Sujithe1572c52010-03-24 13:42:13 +0530653 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530654 return -ENOMEM;
655}
656
Sujithe1572c52010-03-24 13:42:13 +0530657void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530658{
659 struct ath_common *common = ath9k_hw_common(ah);
660 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
661
662 debugfs_remove(priv->debug.debugfs_recv);
663 debugfs_remove(priv->debug.debugfs_xmit);
664 debugfs_remove(priv->debug.debugfs_tgt_stats);
665 debugfs_remove(priv->debug.debugfs_phy);
666}
667
Sujithe1572c52010-03-24 13:42:13 +0530668int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530669{
670 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
671 if (!ath9k_debugfs_root)
672 return -ENOENT;
673
674 return 0;
675}
676
Sujithe1572c52010-03-24 13:42:13 +0530677void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530678{
679 debugfs_remove(ath9k_debugfs_root);
680 ath9k_debugfs_root = NULL;
681}
682
683#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
684
685/*******/
686/* ANI */
687/*******/
688
689static void ath_start_ani(struct ath9k_htc_priv *priv)
690{
691 struct ath_common *common = ath9k_hw_common(priv->ah);
692 unsigned long timestamp = jiffies_to_msecs(jiffies);
693
694 common->ani.longcal_timer = timestamp;
695 common->ani.shortcal_timer = timestamp;
696 common->ani.checkani_timer = timestamp;
697
698 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
699 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
700}
701
702void ath9k_ani_work(struct work_struct *work)
703{
704 struct ath9k_htc_priv *priv =
705 container_of(work, struct ath9k_htc_priv,
706 ath9k_ani_work.work);
707 struct ath_hw *ah = priv->ah;
708 struct ath_common *common = ath9k_hw_common(ah);
709 bool longcal = false;
710 bool shortcal = false;
711 bool aniflag = false;
712 unsigned int timestamp = jiffies_to_msecs(jiffies);
713 u32 cal_interval, short_cal_interval;
714
715 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
716
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530717 /* Only calibrate if awake */
718 if (ah->power_mode != ATH9K_PM_AWAKE)
719 goto set_timer;
720
Sujithfb9987d2010-03-17 14:25:25 +0530721 /* Long calibration runs independently of short calibration. */
722 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
723 longcal = true;
724 ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
725 common->ani.longcal_timer = timestamp;
726 }
727
728 /* Short calibration applies only while caldone is false */
729 if (!common->ani.caldone) {
730 if ((timestamp - common->ani.shortcal_timer) >=
731 short_cal_interval) {
732 shortcal = true;
733 ath_print(common, ATH_DBG_ANI,
734 "shortcal @%lu\n", jiffies);
735 common->ani.shortcal_timer = timestamp;
736 common->ani.resetcal_timer = timestamp;
737 }
738 } else {
739 if ((timestamp - common->ani.resetcal_timer) >=
740 ATH_RESTART_CALINTERVAL) {
741 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
742 if (common->ani.caldone)
743 common->ani.resetcal_timer = timestamp;
744 }
745 }
746
747 /* Verify whether we must check ANI */
748 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
749 aniflag = true;
750 common->ani.checkani_timer = timestamp;
751 }
752
753 /* Skip all processing if there's nothing to do. */
754 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530755
756 ath9k_htc_ps_wakeup(priv);
757
Sujithfb9987d2010-03-17 14:25:25 +0530758 /* Call ANI routine if necessary */
759 if (aniflag)
760 ath9k_hw_ani_monitor(ah, ah->curchan);
761
762 /* Perform calibration if necessary */
763 if (longcal || shortcal) {
764 common->ani.caldone =
765 ath9k_hw_calibrate(ah, ah->curchan,
766 common->rx_chainmask,
767 longcal);
768
769 if (longcal)
770 common->ani.noise_floor =
771 ath9k_hw_getchan_noise(ah, ah->curchan);
772
773 ath_print(common, ATH_DBG_ANI,
774 " calibrate chan %u/%x nf: %d\n",
775 ah->curchan->channel,
776 ah->curchan->channelFlags,
777 common->ani.noise_floor);
778 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530779
780 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530781 }
782
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530783set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530784 /*
785 * Set timer interval based on previous results.
786 * The interval must be the shortest necessary to satisfy ANI,
787 * short calibration and long calibration.
788 */
789 cal_interval = ATH_LONG_CALINTERVAL;
790 if (priv->ah->config.enable_ani)
791 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
792 if (!common->ani.caldone)
793 cal_interval = min(cal_interval, (u32)short_cal_interval);
794
795 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
796 msecs_to_jiffies(cal_interval));
797}
798
799/*******/
800/* LED */
801/*******/
802
803static void ath9k_led_blink_work(struct work_struct *work)
804{
805 struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
806 ath9k_led_blink_work.work);
807
808 if (!(priv->op_flags & OP_LED_ASSOCIATED))
809 return;
810
811 if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) ||
812 (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE))
813 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
814 else
815 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
816 (priv->op_flags & OP_LED_ON) ? 1 : 0);
817
818 ieee80211_queue_delayed_work(priv->hw,
819 &priv->ath9k_led_blink_work,
820 (priv->op_flags & OP_LED_ON) ?
821 msecs_to_jiffies(priv->led_off_duration) :
822 msecs_to_jiffies(priv->led_on_duration));
823
824 priv->led_on_duration = priv->led_on_cnt ?
825 max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) :
826 ATH_LED_ON_DURATION_IDLE;
827 priv->led_off_duration = priv->led_off_cnt ?
828 max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) :
829 ATH_LED_OFF_DURATION_IDLE;
830 priv->led_on_cnt = priv->led_off_cnt = 0;
831
832 if (priv->op_flags & OP_LED_ON)
833 priv->op_flags &= ~OP_LED_ON;
834 else
835 priv->op_flags |= OP_LED_ON;
836}
837
838static void ath9k_led_brightness_work(struct work_struct *work)
839{
840 struct ath_led *led = container_of(work, struct ath_led,
841 brightness_work.work);
842 struct ath9k_htc_priv *priv = led->priv;
843
844 switch (led->brightness) {
845 case LED_OFF:
846 if (led->led_type == ATH_LED_ASSOC ||
847 led->led_type == ATH_LED_RADIO) {
848 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin,
849 (led->led_type == ATH_LED_RADIO));
850 priv->op_flags &= ~OP_LED_ASSOCIATED;
851 if (led->led_type == ATH_LED_RADIO)
852 priv->op_flags &= ~OP_LED_ON;
853 } else {
854 priv->led_off_cnt++;
855 }
856 break;
857 case LED_FULL:
858 if (led->led_type == ATH_LED_ASSOC) {
859 priv->op_flags |= OP_LED_ASSOCIATED;
860 ieee80211_queue_delayed_work(priv->hw,
861 &priv->ath9k_led_blink_work, 0);
862 } else if (led->led_type == ATH_LED_RADIO) {
863 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0);
864 priv->op_flags |= OP_LED_ON;
865 } else {
866 priv->led_on_cnt++;
867 }
868 break;
869 default:
870 break;
871 }
872}
873
874static void ath9k_led_brightness(struct led_classdev *led_cdev,
875 enum led_brightness brightness)
876{
877 struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev);
878 struct ath9k_htc_priv *priv = led->priv;
879
880 led->brightness = brightness;
881 if (!(priv->op_flags & OP_LED_DEINIT))
882 ieee80211_queue_delayed_work(priv->hw,
883 &led->brightness_work, 0);
884}
885
886static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv)
887{
888 cancel_delayed_work_sync(&priv->radio_led.brightness_work);
889 cancel_delayed_work_sync(&priv->assoc_led.brightness_work);
890 cancel_delayed_work_sync(&priv->tx_led.brightness_work);
891 cancel_delayed_work_sync(&priv->rx_led.brightness_work);
892}
893
894static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led,
895 char *trigger)
896{
897 int ret;
898
899 led->priv = priv;
900 led->led_cdev.name = led->name;
901 led->led_cdev.default_trigger = trigger;
902 led->led_cdev.brightness_set = ath9k_led_brightness;
903
904 ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev);
905 if (ret)
906 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
907 "Failed to register led:%s", led->name);
908 else
909 led->registered = 1;
910
911 INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work);
912
913 return ret;
914}
915
916static void ath9k_unregister_led(struct ath_led *led)
917{
918 if (led->registered) {
919 led_classdev_unregister(&led->led_cdev);
920 led->registered = 0;
921 }
922}
923
924void ath9k_deinit_leds(struct ath9k_htc_priv *priv)
925{
926 priv->op_flags |= OP_LED_DEINIT;
927 ath9k_unregister_led(&priv->assoc_led);
928 priv->op_flags &= ~OP_LED_ASSOCIATED;
929 ath9k_unregister_led(&priv->tx_led);
930 ath9k_unregister_led(&priv->rx_led);
931 ath9k_unregister_led(&priv->radio_led);
Sujithfb9987d2010-03-17 14:25:25 +0530932}
933
934void ath9k_init_leds(struct ath9k_htc_priv *priv)
935{
936 char *trigger;
937 int ret;
938
939 if (AR_SREV_9287(priv->ah))
940 priv->ah->led_pin = ATH_LED_PIN_9287;
941 else if (AR_SREV_9271(priv->ah))
942 priv->ah->led_pin = ATH_LED_PIN_9271;
Sujith88c1f4f2010-06-30 14:46:31 +0530943 else if (AR_DEVID_7010(priv->ah))
944 priv->ah->led_pin = ATH_LED_PIN_7010;
Sujithfb9987d2010-03-17 14:25:25 +0530945 else
946 priv->ah->led_pin = ATH_LED_PIN_DEF;
947
948 /* Configure gpio 1 for output */
949 ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin,
950 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
951 /* LED off, active low */
952 ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1);
953
954 INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work);
955
956 trigger = ieee80211_get_radio_led_name(priv->hw);
957 snprintf(priv->radio_led.name, sizeof(priv->radio_led.name),
958 "ath9k-%s::radio", wiphy_name(priv->hw->wiphy));
959 ret = ath9k_register_led(priv, &priv->radio_led, trigger);
960 priv->radio_led.led_type = ATH_LED_RADIO;
961 if (ret)
962 goto fail;
963
964 trigger = ieee80211_get_assoc_led_name(priv->hw);
965 snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name),
966 "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy));
967 ret = ath9k_register_led(priv, &priv->assoc_led, trigger);
968 priv->assoc_led.led_type = ATH_LED_ASSOC;
969 if (ret)
970 goto fail;
971
972 trigger = ieee80211_get_tx_led_name(priv->hw);
973 snprintf(priv->tx_led.name, sizeof(priv->tx_led.name),
974 "ath9k-%s::tx", wiphy_name(priv->hw->wiphy));
975 ret = ath9k_register_led(priv, &priv->tx_led, trigger);
976 priv->tx_led.led_type = ATH_LED_TX;
977 if (ret)
978 goto fail;
979
980 trigger = ieee80211_get_rx_led_name(priv->hw);
981 snprintf(priv->rx_led.name, sizeof(priv->rx_led.name),
982 "ath9k-%s::rx", wiphy_name(priv->hw->wiphy));
983 ret = ath9k_register_led(priv, &priv->rx_led, trigger);
984 priv->rx_led.led_type = ATH_LED_RX;
985 if (ret)
986 goto fail;
987
988 priv->op_flags &= ~OP_LED_DEINIT;
989
990 return;
991
992fail:
993 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
994 ath9k_deinit_leds(priv);
995}
996
997/*******************/
998/* Rfkill */
999/*******************/
1000
1001static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv)
1002{
1003 return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) ==
1004 priv->ah->rfkill_polarity;
1005}
1006
1007static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw)
1008{
1009 struct ath9k_htc_priv *priv = hw->priv;
1010 bool blocked = !!ath_is_rfkill_set(priv);
1011
1012 wiphy_rfkill_set_hw_state(hw->wiphy, blocked);
1013}
1014
1015void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv)
1016{
1017 if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT)
1018 wiphy_rfkill_start_polling(priv->hw->wiphy);
1019}
1020
Sujith881ac6a2010-06-01 15:14:11 +05301021static void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
1022{
1023 struct ath9k_htc_priv *priv = hw->priv;
1024 struct ath_hw *ah = priv->ah;
1025 struct ath_common *common = ath9k_hw_common(ah);
1026 int ret;
1027 u8 cmd_rsp;
1028
1029 if (!ah->curchan)
1030 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1031
1032 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001033 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301034 if (ret) {
1035 ath_print(common, ATH_DBG_FATAL,
1036 "Unable to reset hardware; reset status %d "
1037 "(freq %u MHz)\n", ret, ah->curchan->channel);
1038 }
1039
1040 ath_update_txpow(priv);
1041
1042 /* Start RX */
1043 WMI_CMD(WMI_START_RECV_CMDID);
1044 ath9k_host_rx_init(priv);
1045
1046 /* Start TX */
1047 htc_start(priv->htc);
1048 spin_lock_bh(&priv->tx_lock);
1049 priv->tx_queues_stop = false;
1050 spin_unlock_bh(&priv->tx_lock);
1051 ieee80211_wake_queues(hw);
1052
1053 WMI_CMD(WMI_ENABLE_INTR_CMDID);
1054
1055 /* Enable LED */
1056 ath9k_hw_cfg_output(ah, ah->led_pin,
1057 AR_GPIO_OUTPUT_MUX_AS_OUTPUT);
1058 ath9k_hw_set_gpio(ah, ah->led_pin, 0);
1059}
1060
1061static void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
1062{
1063 struct ath9k_htc_priv *priv = hw->priv;
1064 struct ath_hw *ah = priv->ah;
1065 struct ath_common *common = ath9k_hw_common(ah);
1066 int ret;
1067 u8 cmd_rsp;
1068
1069 ath9k_htc_ps_wakeup(priv);
1070
1071 /* Disable LED */
1072 ath9k_hw_set_gpio(ah, ah->led_pin, 1);
1073 ath9k_hw_cfg_gpio_input(ah, ah->led_pin);
1074
1075 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1076
1077 /* Stop TX */
1078 ieee80211_stop_queues(hw);
1079 htc_stop(priv->htc);
1080 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1081 skb_queue_purge(&priv->tx_queue);
1082
1083 /* Stop RX */
1084 WMI_CMD(WMI_STOP_RECV_CMDID);
1085
Sujith21d51302010-06-01 15:14:18 +05301086 /*
1087 * The MIB counters have to be disabled here,
1088 * since the target doesn't do it.
1089 */
1090 ath9k_hw_disable_mib_counters(ah);
1091
Sujith881ac6a2010-06-01 15:14:11 +05301092 if (!ah->curchan)
1093 ah->curchan = ath9k_cmn_get_curchannel(hw, ah);
1094
1095 /* Reset the HW */
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001096 ret = ath9k_hw_reset(ah, ah->curchan, ah->caldata, false);
Sujith881ac6a2010-06-01 15:14:11 +05301097 if (ret) {
1098 ath_print(common, ATH_DBG_FATAL,
1099 "Unable to reset hardware; reset status %d "
1100 "(freq %u MHz)\n", ret, ah->curchan->channel);
1101 }
1102
1103 /* Disable the PHY */
1104 ath9k_hw_phy_disable(ah);
1105
1106 ath9k_htc_ps_restore(priv);
1107 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1108}
1109
Sujithfb9987d2010-03-17 14:25:25 +05301110/**********************/
1111/* mac80211 Callbacks */
1112/**********************/
1113
1114static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1115{
1116 struct ieee80211_hdr *hdr;
1117 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301118 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301119
1120 hdr = (struct ieee80211_hdr *) skb->data;
1121
1122 /* Add the padding after the header if this is not already done */
1123 padpos = ath9k_cmn_padpos(hdr->frame_control);
1124 padsize = padpos & 3;
1125 if (padsize && skb->len > padpos) {
1126 if (skb_headroom(skb) < padsize)
1127 return -1;
1128 skb_push(skb, padsize);
1129 memmove(skb->data, skb->data + padsize, padpos);
1130 }
1131
Sujith7757dfe2010-03-29 16:07:17 +05301132 ret = ath9k_htc_tx_start(priv, skb);
1133 if (ret != 0) {
1134 if (ret == -ENOMEM) {
1135 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1136 "Stopping TX queues\n");
1137 ieee80211_stop_queues(hw);
1138 spin_lock_bh(&priv->tx_lock);
1139 priv->tx_queues_stop = true;
1140 spin_unlock_bh(&priv->tx_lock);
1141 } else {
1142 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1143 "Tx failed");
1144 }
Sujithfb9987d2010-03-17 14:25:25 +05301145 goto fail_tx;
1146 }
1147
1148 return 0;
1149
1150fail_tx:
1151 dev_kfree_skb_any(skb);
1152 return 0;
1153}
1154
Sujith881ac6a2010-06-01 15:14:11 +05301155static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301156{
1157 struct ath9k_htc_priv *priv = hw->priv;
1158 struct ath_hw *ah = priv->ah;
1159 struct ath_common *common = ath9k_hw_common(ah);
1160 struct ieee80211_channel *curchan = hw->conf.channel;
1161 struct ath9k_channel *init_channel;
1162 int ret = 0;
1163 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301164 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301165 u8 cmd_rsp;
1166
Sujith881ac6a2010-06-01 15:14:11 +05301167 mutex_lock(&priv->mutex);
1168
Sujithfb9987d2010-03-17 14:25:25 +05301169 ath_print(common, ATH_DBG_CONFIG,
1170 "Starting driver with initial channel: %d MHz\n",
1171 curchan->center_freq);
1172
Sujith21d51302010-06-01 15:14:18 +05301173 /* Ensure that HW is awake before flushing RX */
1174 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1175 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1176
Sujithfb9987d2010-03-17 14:25:25 +05301177 /* setup initial channel */
1178 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1179
1180 /* Reset SERDES registers */
1181 ath9k_hw_configpcipowersave(ah, 0, 0);
1182
1183 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001184 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301185 if (ret) {
1186 ath_print(common, ATH_DBG_FATAL,
1187 "Unable to reset hardware; reset status %d "
1188 "(freq %u MHz)\n", ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301189 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301190 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301191 }
1192
1193 ath_update_txpow(priv);
1194
1195 mode = ath9k_htc_get_curmode(priv, init_channel);
1196 htc_mode = cpu_to_be16(mode);
1197 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301198 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301199 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301200
1201 ath9k_host_rx_init(priv);
1202
1203 priv->op_flags &= ~OP_INVALID;
1204 htc_start(priv->htc);
1205
Sujith7757dfe2010-03-29 16:07:17 +05301206 spin_lock_bh(&priv->tx_lock);
1207 priv->tx_queues_stop = false;
1208 spin_unlock_bh(&priv->tx_lock);
1209
1210 ieee80211_wake_queues(hw);
1211
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301212 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1213 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1214 AR_STOMP_LOW_WLAN_WGHT);
1215 ath9k_hw_btcoex_enable(ah);
1216 ath_htc_resume_btcoex_work(priv);
1217 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301218 mutex_unlock(&priv->mutex);
1219
1220 return ret;
1221}
1222
Sujith881ac6a2010-06-01 15:14:11 +05301223static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301224{
1225 struct ath9k_htc_priv *priv = hw->priv;
1226 struct ath_hw *ah = priv->ah;
1227 struct ath_common *common = ath9k_hw_common(ah);
1228 int ret = 0;
1229 u8 cmd_rsp;
1230
Sujith881ac6a2010-06-01 15:14:11 +05301231 mutex_lock(&priv->mutex);
1232
Sujithfb9987d2010-03-17 14:25:25 +05301233 if (priv->op_flags & OP_INVALID) {
1234 ath_print(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301235 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301236 return;
1237 }
1238
Sujith7073daa2010-04-23 10:28:13 +05301239 /* Cancel all the running timers/work .. */
1240 cancel_work_sync(&priv->ps_work);
Sujith7073daa2010-04-23 10:28:13 +05301241 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
1242 ath9k_led_stop_brightness(priv);
1243
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301244 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301245 htc_stop(priv->htc);
1246 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1247 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1248 WMI_CMD(WMI_STOP_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301249 skb_queue_purge(&priv->tx_queue);
1250
1251 /* Remove monitor interface here */
1252 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1253 if (ath9k_htc_remove_monitor_interface(priv))
1254 ath_print(common, ATH_DBG_FATAL,
1255 "Unable to remove monitor interface\n");
1256 else
1257 ath_print(common, ATH_DBG_CONFIG,
1258 "Monitor interface removed\n");
1259 }
1260
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301261 if (ah->btcoex_hw.enabled) {
1262 ath9k_hw_btcoex_disable(ah);
1263 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1264 ath_htc_cancel_btcoex_work(priv);
1265 }
1266
Sujithe9201f02010-06-01 15:14:17 +05301267 ath9k_hw_phy_disable(ah);
1268 ath9k_hw_disable(ah);
1269 ath9k_hw_configpcipowersave(ah, 1, 1);
1270 ath9k_htc_ps_restore(priv);
1271 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1272
Sujithfb9987d2010-03-17 14:25:25 +05301273 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301274
1275 ath_print(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301276 mutex_unlock(&priv->mutex);
1277}
1278
Sujithfb9987d2010-03-17 14:25:25 +05301279static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1280 struct ieee80211_vif *vif)
1281{
1282 struct ath9k_htc_priv *priv = hw->priv;
1283 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1284 struct ath_common *common = ath9k_hw_common(priv->ah);
1285 struct ath9k_htc_target_vif hvif;
1286 int ret = 0;
1287 u8 cmd_rsp;
1288
1289 mutex_lock(&priv->mutex);
1290
1291 /* Only one interface for now */
1292 if (priv->nvifs > 0) {
1293 ret = -ENOBUFS;
1294 goto out;
1295 }
1296
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301297 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301298 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1299 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1300
1301 switch (vif->type) {
1302 case NL80211_IFTYPE_STATION:
1303 hvif.opmode = cpu_to_be32(HTC_M_STA);
1304 break;
1305 case NL80211_IFTYPE_ADHOC:
1306 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1307 break;
1308 default:
1309 ath_print(common, ATH_DBG_FATAL,
1310 "Interface type %d not yet supported\n", vif->type);
1311 ret = -EOPNOTSUPP;
1312 goto out;
1313 }
1314
1315 ath_print(common, ATH_DBG_CONFIG,
1316 "Attach a VIF of type: %d\n", vif->type);
1317
1318 priv->ah->opmode = vif->type;
1319
1320 /* Index starts from zero on the target */
1321 avp->index = hvif.index = priv->nvifs;
1322 hvif.rtsthreshold = cpu_to_be16(2304);
1323 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1324 if (ret)
1325 goto out;
1326
1327 priv->nvifs++;
1328
1329 /*
1330 * We need a node in target to tx mgmt frames
1331 * before association.
1332 */
1333 ret = ath9k_htc_add_station(priv, vif, NULL);
1334 if (ret)
1335 goto out;
1336
1337 ret = ath9k_htc_update_cap_target(priv);
1338 if (ret)
1339 ath_print(common, ATH_DBG_CONFIG, "Failed to update"
1340 " capability in target \n");
1341
1342 priv->vif = vif;
1343out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301344 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301345 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301346
Sujithfb9987d2010-03-17 14:25:25 +05301347 return ret;
1348}
1349
1350static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1351 struct ieee80211_vif *vif)
1352{
1353 struct ath9k_htc_priv *priv = hw->priv;
1354 struct ath_common *common = ath9k_hw_common(priv->ah);
1355 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1356 struct ath9k_htc_target_vif hvif;
1357 int ret = 0;
1358 u8 cmd_rsp;
1359
1360 ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n");
1361
1362 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301363 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301364
1365 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1366 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1367 hvif.index = avp->index;
1368 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1369 priv->nvifs--;
1370
1371 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301372 priv->vif = NULL;
1373
Sujithcb551df2010-06-01 15:14:12 +05301374 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301375 mutex_unlock(&priv->mutex);
1376}
1377
1378static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1379{
1380 struct ath9k_htc_priv *priv = hw->priv;
1381 struct ath_common *common = ath9k_hw_common(priv->ah);
1382 struct ieee80211_conf *conf = &hw->conf;
1383
1384 mutex_lock(&priv->mutex);
1385
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301386 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1387 bool enable_radio = false;
1388 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1389
Sujith23367762010-06-01 15:14:16 +05301390 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301391 if (!idle && priv->ps_idle)
1392 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301393 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301394 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301395
1396 if (enable_radio) {
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301397 ath_print(common, ATH_DBG_CONFIG,
1398 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301399 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1400 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301401 }
1402 }
1403
Sujithfb9987d2010-03-17 14:25:25 +05301404 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1405 struct ieee80211_channel *curchan = hw->conf.channel;
1406 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301407
1408 ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1409 curchan->center_freq);
1410
Sujithfb9987d2010-03-17 14:25:25 +05301411 ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]);
1412
1413 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
1414 ath_print(common, ATH_DBG_FATAL,
1415 "Unable to set channel\n");
1416 mutex_unlock(&priv->mutex);
1417 return -EINVAL;
1418 }
1419
1420 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301421 if (changed & IEEE80211_CONF_CHANGE_PS) {
1422 if (conf->flags & IEEE80211_CONF_PS) {
1423 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1424 priv->ps_enabled = true;
1425 } else {
1426 priv->ps_enabled = false;
1427 cancel_work_sync(&priv->ps_work);
1428 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1429 }
1430 }
Sujithfb9987d2010-03-17 14:25:25 +05301431
1432 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1433 if (conf->flags & IEEE80211_CONF_MONITOR) {
1434 if (ath9k_htc_add_monitor_interface(priv))
1435 ath_print(common, ATH_DBG_FATAL,
1436 "Failed to set monitor mode\n");
1437 else
1438 ath_print(common, ATH_DBG_CONFIG,
1439 "HW opmode set to Monitor mode\n");
1440 }
1441 }
1442
Sujith23367762010-06-01 15:14:16 +05301443 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1444 mutex_lock(&priv->htc_pm_lock);
1445 if (!priv->ps_idle) {
1446 mutex_unlock(&priv->htc_pm_lock);
1447 goto out;
1448 }
1449 mutex_unlock(&priv->htc_pm_lock);
1450
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301451 ath_print(common, ATH_DBG_CONFIG,
1452 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301453 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301454 }
1455
Sujith23367762010-06-01 15:14:16 +05301456out:
Sujithfb9987d2010-03-17 14:25:25 +05301457 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301458 return 0;
1459}
1460
1461#define SUPPORTED_FILTERS \
1462 (FIF_PROMISC_IN_BSS | \
1463 FIF_ALLMULTI | \
1464 FIF_CONTROL | \
1465 FIF_PSPOLL | \
1466 FIF_OTHER_BSS | \
1467 FIF_BCN_PRBRESP_PROMISC | \
1468 FIF_FCSFAIL)
1469
1470static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1471 unsigned int changed_flags,
1472 unsigned int *total_flags,
1473 u64 multicast)
1474{
1475 struct ath9k_htc_priv *priv = hw->priv;
1476 u32 rfilt;
1477
1478 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301479 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301480
Sujithfb9987d2010-03-17 14:25:25 +05301481 changed_flags &= SUPPORTED_FILTERS;
1482 *total_flags &= SUPPORTED_FILTERS;
1483
1484 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301485 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301486 ath9k_hw_setrxfilter(priv->ah, rfilt);
1487
1488 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1489 "Set HW RX filter: 0x%x\n", rfilt);
1490
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301491 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301492 mutex_unlock(&priv->mutex);
1493}
1494
Sujithabd984e2010-05-18 15:26:04 +05301495static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1496 struct ieee80211_vif *vif,
1497 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301498{
1499 struct ath9k_htc_priv *priv = hw->priv;
1500 int ret;
1501
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301502 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301503 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301504 ret = ath9k_htc_add_station(priv, vif, sta);
1505 if (!ret)
1506 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301507 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301508 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301509
1510 return ret;
1511}
1512
1513static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1514 struct ieee80211_vif *vif,
1515 struct ieee80211_sta *sta)
1516{
1517 struct ath9k_htc_priv *priv = hw->priv;
1518 int ret;
1519
1520 mutex_lock(&priv->mutex);
1521 ath9k_htc_ps_wakeup(priv);
1522 ret = ath9k_htc_remove_station(priv, vif, sta);
1523 ath9k_htc_ps_restore(priv);
1524 mutex_unlock(&priv->mutex);
1525
1526 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301527}
1528
1529static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1530 const struct ieee80211_tx_queue_params *params)
1531{
1532 struct ath9k_htc_priv *priv = hw->priv;
1533 struct ath_common *common = ath9k_hw_common(priv->ah);
1534 struct ath9k_tx_queue_info qi;
1535 int ret = 0, qnum;
1536
1537 if (queue >= WME_NUM_AC)
1538 return 0;
1539
1540 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301541 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301542
1543 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1544
1545 qi.tqi_aifs = params->aifs;
1546 qi.tqi_cwmin = params->cw_min;
1547 qi.tqi_cwmax = params->cw_max;
1548 qi.tqi_burstTime = params->txop;
1549
1550 qnum = get_hw_qnum(queue, priv->hwq_map);
1551
1552 ath_print(common, ATH_DBG_CONFIG,
1553 "Configure tx [queue/hwq] [%d/%d], "
1554 "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1555 queue, qnum, params->aifs, params->cw_min,
1556 params->cw_max, params->txop);
1557
Sujithe1572c52010-03-24 13:42:13 +05301558 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301559 if (ret) {
Sujithfb9987d2010-03-17 14:25:25 +05301560 ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301561 goto out;
1562 }
Sujithfb9987d2010-03-17 14:25:25 +05301563
Sujith764580f2010-06-01 15:14:19 +05301564 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001565 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301566 ath9k_htc_beaconq_config(priv);
1567out:
Sujithcb551df2010-06-01 15:14:12 +05301568 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301569 mutex_unlock(&priv->mutex);
1570
1571 return ret;
1572}
1573
1574static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1575 enum set_key_cmd cmd,
1576 struct ieee80211_vif *vif,
1577 struct ieee80211_sta *sta,
1578 struct ieee80211_key_conf *key)
1579{
1580 struct ath9k_htc_priv *priv = hw->priv;
1581 struct ath_common *common = ath9k_hw_common(priv->ah);
1582 int ret = 0;
1583
Sujithe1572c52010-03-24 13:42:13 +05301584 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301585 return -ENOSPC;
1586
1587 mutex_lock(&priv->mutex);
1588 ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301589 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301590
1591 switch (cmd) {
1592 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001593 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301594 if (ret >= 0) {
1595 key->hw_key_idx = ret;
1596 /* push IV and Michael MIC generation to stack */
1597 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001598 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301599 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001600 if (priv->ah->sw_mgmt_crypto &&
1601 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301602 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1603 ret = 0;
1604 }
1605 break;
1606 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001607 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301608 break;
1609 default:
1610 ret = -EINVAL;
1611 }
1612
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301613 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301614 mutex_unlock(&priv->mutex);
1615
1616 return ret;
1617}
1618
1619static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1620 struct ieee80211_vif *vif,
1621 struct ieee80211_bss_conf *bss_conf,
1622 u32 changed)
1623{
1624 struct ath9k_htc_priv *priv = hw->priv;
1625 struct ath_hw *ah = priv->ah;
1626 struct ath_common *common = ath9k_hw_common(ah);
1627
1628 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301629 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301630
1631 if (changed & BSS_CHANGED_ASSOC) {
1632 common->curaid = bss_conf->assoc ?
1633 bss_conf->aid : 0;
1634 ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1635 bss_conf->assoc);
1636
1637 if (bss_conf->assoc) {
1638 priv->op_flags |= OP_ASSOCIATED;
1639 ath_start_ani(priv);
1640 } else {
1641 priv->op_flags &= ~OP_ASSOCIATED;
1642 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1643 }
1644 }
1645
1646 if (changed & BSS_CHANGED_BSSID) {
1647 /* Set BSSID */
1648 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1649 ath9k_hw_write_associd(ah);
1650
1651 ath_print(common, ATH_DBG_CONFIG,
1652 "BSSID: %pM aid: 0x%x\n",
1653 common->curbssid, common->curaid);
1654 }
1655
1656 if ((changed & BSS_CHANGED_BEACON_INT) ||
1657 (changed & BSS_CHANGED_BEACON) ||
1658 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1659 bss_conf->enable_beacon)) {
1660 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301661 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301662 }
1663
Sujithfb9987d2010-03-17 14:25:25 +05301664 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1665 !bss_conf->enable_beacon) {
1666 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301667 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301668 }
1669
1670 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
1671 ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1672 bss_conf->use_short_preamble);
1673 if (bss_conf->use_short_preamble)
1674 priv->op_flags |= OP_PREAMBLE_SHORT;
1675 else
1676 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1677 }
1678
1679 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
1680 ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1681 bss_conf->use_cts_prot);
1682 if (bss_conf->use_cts_prot &&
1683 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1684 priv->op_flags |= OP_PROTECT_ENABLE;
1685 else
1686 priv->op_flags &= ~OP_PROTECT_ENABLE;
1687 }
1688
1689 if (changed & BSS_CHANGED_ERP_SLOT) {
1690 if (bss_conf->use_short_slot)
1691 ah->slottime = 9;
1692 else
1693 ah->slottime = 20;
1694
1695 ath9k_hw_init_global_settings(ah);
1696 }
1697
Sujith2c76ef82010-05-17 12:01:18 +05301698 if (changed & BSS_CHANGED_HT)
1699 ath9k_htc_update_rate(priv, vif, bss_conf);
1700
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301701 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301702 mutex_unlock(&priv->mutex);
1703}
1704
1705static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1706{
1707 struct ath9k_htc_priv *priv = hw->priv;
1708 u64 tsf;
1709
1710 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301711 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301712 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301713 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301714 mutex_unlock(&priv->mutex);
1715
1716 return tsf;
1717}
1718
1719static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1720{
1721 struct ath9k_htc_priv *priv = hw->priv;
1722
1723 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301724 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301725 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301726 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301727 mutex_unlock(&priv->mutex);
1728}
1729
1730static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1731{
1732 struct ath9k_htc_priv *priv = hw->priv;
1733
1734 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301735 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301736 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301737 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301738 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301739}
1740
1741static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1742 struct ieee80211_vif *vif,
1743 enum ieee80211_ampdu_mlme_action action,
1744 struct ieee80211_sta *sta,
1745 u16 tid, u16 *ssn)
1746{
1747 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301748 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301749 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301750
1751 switch (action) {
1752 case IEEE80211_AMPDU_RX_START:
1753 break;
1754 case IEEE80211_AMPDU_RX_STOP:
1755 break;
1756 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301757 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1758 if (!ret)
1759 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1760 break;
Sujithfb9987d2010-03-17 14:25:25 +05301761 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301762 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1763 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301764 break;
1765 case IEEE80211_AMPDU_TX_OPERATIONAL:
1766 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301767 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301768 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301769 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301770 break;
1771 default:
1772 ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL,
1773 "Unknown AMPDU action\n");
1774 }
1775
Sujithd7ca2132010-06-15 10:24:37 +05301776 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301777}
1778
1779static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1780{
1781 struct ath9k_htc_priv *priv = hw->priv;
1782
1783 mutex_lock(&priv->mutex);
1784 spin_lock_bh(&priv->beacon_lock);
1785 priv->op_flags |= OP_SCANNING;
1786 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301787 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301788 if (priv->op_flags & OP_ASSOCIATED)
1789 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301790 mutex_unlock(&priv->mutex);
1791}
1792
1793static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1794{
1795 struct ath9k_htc_priv *priv = hw->priv;
1796
1797 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301798 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301799 spin_lock_bh(&priv->beacon_lock);
1800 priv->op_flags &= ~OP_SCANNING;
1801 spin_unlock_bh(&priv->beacon_lock);
1802 priv->op_flags |= OP_FULL_RESET;
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301803 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301804 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301805 ath_start_ani(priv);
1806 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301807 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301808 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301809}
1810
1811static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1812{
1813 return 0;
1814}
1815
1816static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1817 u8 coverage_class)
1818{
1819 struct ath9k_htc_priv *priv = hw->priv;
1820
1821 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301822 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301823 priv->ah->coverage_class = coverage_class;
1824 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301825 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301826 mutex_unlock(&priv->mutex);
1827}
1828
1829struct ieee80211_ops ath9k_htc_ops = {
1830 .tx = ath9k_htc_tx,
1831 .start = ath9k_htc_start,
1832 .stop = ath9k_htc_stop,
1833 .add_interface = ath9k_htc_add_interface,
1834 .remove_interface = ath9k_htc_remove_interface,
1835 .config = ath9k_htc_config,
1836 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301837 .sta_add = ath9k_htc_sta_add,
1838 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301839 .conf_tx = ath9k_htc_conf_tx,
1840 .bss_info_changed = ath9k_htc_bss_info_changed,
1841 .set_key = ath9k_htc_set_key,
1842 .get_tsf = ath9k_htc_get_tsf,
1843 .set_tsf = ath9k_htc_set_tsf,
1844 .reset_tsf = ath9k_htc_reset_tsf,
1845 .ampdu_action = ath9k_htc_ampdu_action,
1846 .sw_scan_start = ath9k_htc_sw_scan_start,
1847 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1848 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1849 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1850 .set_coverage_class = ath9k_htc_set_coverage_class,
1851};