blob: 953036a4ed53b41f58e93274c53009e5ceaae7e1 [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
Sujithfb9987d2010-03-17 14:25:25 +053027/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
28static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
29 struct ath9k_channel *ichan)
30{
31 enum htc_phymode mode;
32
33 mode = HTC_MODE_AUTO;
34
35 switch (ichan->chanmode) {
36 case CHANNEL_G:
37 case CHANNEL_G_HT20:
38 case CHANNEL_G_HT40PLUS:
39 case CHANNEL_G_HT40MINUS:
40 mode = HTC_MODE_11NG;
41 break;
42 case CHANNEL_A:
43 case CHANNEL_A_HT20:
44 case CHANNEL_A_HT40PLUS:
45 case CHANNEL_A_HT40MINUS:
46 mode = HTC_MODE_11NA;
47 break;
48 default:
49 break;
50 }
51
52 return mode;
53}
54
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053055bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
56 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053057{
58 bool ret;
59
60 mutex_lock(&priv->htc_pm_lock);
61 ret = ath9k_hw_setpower(priv->ah, mode);
62 mutex_unlock(&priv->htc_pm_lock);
63
64 return ret;
65}
66
67void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
68{
69 mutex_lock(&priv->htc_pm_lock);
70 if (++priv->ps_usecount != 1)
71 goto unlock;
72 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
73
74unlock:
75 mutex_unlock(&priv->htc_pm_lock);
76}
77
78void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
79{
80 mutex_lock(&priv->htc_pm_lock);
81 if (--priv->ps_usecount != 0)
82 goto unlock;
83
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053084 if (priv->ps_idle)
85 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
86 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053087 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053088
Vivek Natarajanbde748a2010-04-05 14:48:05 +053089unlock:
90 mutex_unlock(&priv->htc_pm_lock);
91}
92
93void ath9k_ps_work(struct work_struct *work)
94{
95 struct ath9k_htc_priv *priv =
96 container_of(work, struct ath9k_htc_priv,
97 ps_work);
98 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
99
100 /* The chip wakes up after receiving the first beacon
101 while network sleep is enabled. For the driver to
102 be in sync with the hw, set the chip to awake and
103 only then set it to sleep.
104 */
105 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
106}
107
Sujith Manoharan73908672010-12-28 14:28:27 +0530108void ath9k_htc_reset(struct ath9k_htc_priv *priv)
109{
110 struct ath_hw *ah = priv->ah;
111 struct ath_common *common = ath9k_hw_common(ah);
112 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530113 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530114 enum htc_phymode mode;
115 __be16 htc_mode;
116 u8 cmd_rsp;
117 int ret;
118
119 mutex_lock(&priv->mutex);
120 ath9k_htc_ps_wakeup(priv);
121
122 if (priv->op_flags & OP_ASSOCIATED)
123 cancel_delayed_work_sync(&priv->ath9k_ani_work);
124
125 ieee80211_stop_queues(priv->hw);
126 htc_stop(priv->htc);
127 WMI_CMD(WMI_DISABLE_INTR_CMDID);
128 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
129 WMI_CMD(WMI_STOP_RECV_CMDID);
130
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530131 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530132 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
133 if (ret) {
134 ath_err(common,
135 "Unable to reset device (%u Mhz) reset status %d\n",
136 channel->center_freq, ret);
137 }
138
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530139 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
140 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530141
142 WMI_CMD(WMI_START_RECV_CMDID);
143 ath9k_host_rx_init(priv);
144
145 mode = ath9k_htc_get_curmode(priv, ah->curchan);
146 htc_mode = cpu_to_be16(mode);
147 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
148
149 WMI_CMD(WMI_ENABLE_INTR_CMDID);
150 htc_start(priv->htc);
151
152 if (priv->op_flags & OP_ASSOCIATED) {
153 ath9k_htc_beacon_config(priv, priv->vif);
154 ath_start_ani(priv);
155 }
156
157 ieee80211_wake_queues(priv->hw);
158
159 ath9k_htc_ps_restore(priv);
160 mutex_unlock(&priv->mutex);
161}
162
Sujithfb9987d2010-03-17 14:25:25 +0530163static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
164 struct ieee80211_hw *hw,
165 struct ath9k_channel *hchan)
166{
167 struct ath_hw *ah = priv->ah;
168 struct ath_common *common = ath9k_hw_common(ah);
169 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530170 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530171 struct ieee80211_channel *channel = hw->conf.channel;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200172 struct ath9k_hw_cal_data *caldata;
Sujithfb9987d2010-03-17 14:25:25 +0530173 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530174 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530175 u8 cmd_rsp;
176 int ret;
177
178 if (priv->op_flags & OP_INVALID)
179 return -EIO;
180
Sujith Manoharan039a0722010-12-28 14:28:37 +0530181 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530182
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530183 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530184 htc_stop(priv->htc);
185 WMI_CMD(WMI_DISABLE_INTR_CMDID);
186 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
187 WMI_CMD(WMI_STOP_RECV_CMDID);
188
Joe Perches226afe62010-12-02 19:12:37 -0800189 ath_dbg(common, ATH_DBG_CONFIG,
190 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
191 priv->ah->curchan->channel,
192 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
193 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530194
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530195 if (!fastcc)
196 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200197 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530198 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800199 ath_err(common,
200 "Unable to reset channel (%u Mhz) reset status %d\n",
201 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530202 goto err;
203 }
204
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530205 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
206 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530207
208 WMI_CMD(WMI_START_RECV_CMDID);
209 if (ret)
210 goto err;
211
212 ath9k_host_rx_init(priv);
213
214 mode = ath9k_htc_get_curmode(priv, hchan);
215 htc_mode = cpu_to_be16(mode);
216 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
217 if (ret)
218 goto err;
219
220 WMI_CMD(WMI_ENABLE_INTR_CMDID);
221 if (ret)
222 goto err;
223
224 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530225err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530226 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530227 return ret;
228}
229
Sujith Manoharancc721282011-01-03 21:22:18 +0530230static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530231{
232 struct ath_common *common = ath9k_hw_common(priv->ah);
233 struct ath9k_htc_target_vif hvif;
234 int ret = 0;
235 u8 cmd_rsp;
236
Sujith Manoharancc721282011-01-03 21:22:18 +0530237 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
238 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
239 hvif.index = 0; /* Should do for now */
240 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
241 priv->nvifs--;
242}
243
244static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
245{
246 struct ath_common *common = ath9k_hw_common(priv->ah);
247 struct ath9k_htc_target_vif hvif;
248 struct ath9k_htc_target_sta tsta;
249 int ret = 0;
250 u8 cmd_rsp;
251
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530252 if (priv->nvifs > 0)
253 return -ENOBUFS;
254
Sujith Manoharancc721282011-01-03 21:22:18 +0530255 if (priv->nstations >= ATH9K_HTC_MAX_STA)
256 return -ENOBUFS;
257
258 /*
259 * Add an interface.
260 */
261
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530262 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
263 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
264
265 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
266 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
267 hvif.index = priv->nvifs;
268
269 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
270 if (ret)
271 return ret;
272
273 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530274
275 /*
276 * Associate a station with the interface for packet injection.
277 */
278
279 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
280
281 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
282
283 tsta.is_vif_sta = 1;
284 tsta.sta_index = priv->nstations;
285 tsta.vif_index = hvif.index;
286 tsta.maxampdu = 0xffff;
287
288 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
289 if (ret) {
290 ath_err(common, "Unable to add station entry for monitor mode\n");
291 goto err_vif;
292 }
293
294 priv->nstations++;
295
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530296 /*
297 * Set chainmask etc. on the target.
298 */
299 ret = ath9k_htc_update_cap_target(priv);
300 if (ret)
301 ath_dbg(common, ATH_DBG_CONFIG,
302 "Failed to update capability in target\n");
303
304 priv->ah->is_monitoring = true;
305
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530306 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530307
308err_vif:
309 /*
310 * Remove the interface from the target.
311 */
312 __ath9k_htc_remove_monitor_interface(priv);
313 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530314}
315
316static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
317{
318 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530319 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530320 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530321
Sujith Manoharancc721282011-01-03 21:22:18 +0530322 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530323
Sujith Manoharancc721282011-01-03 21:22:18 +0530324 sta_idx = 0; /* Only single interface, for now */
325
326 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
327 if (ret) {
328 ath_err(common, "Unable to remove station entry for monitor mode\n");
329 return ret;
330 }
331
332 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530333 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530334
335 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530336}
337
Sujithfb9987d2010-03-17 14:25:25 +0530338static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
339 struct ieee80211_vif *vif,
340 struct ieee80211_sta *sta)
341{
342 struct ath_common *common = ath9k_hw_common(priv->ah);
343 struct ath9k_htc_target_sta tsta;
344 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
345 struct ath9k_htc_sta *ista;
346 int ret;
347 u8 cmd_rsp;
348
349 if (priv->nstations >= ATH9K_HTC_MAX_STA)
350 return -ENOBUFS;
351
352 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
353
354 if (sta) {
355 ista = (struct ath9k_htc_sta *) sta->drv_priv;
356 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
357 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
358 tsta.associd = common->curaid;
359 tsta.is_vif_sta = 0;
360 tsta.valid = true;
361 ista->index = priv->nstations;
362 } else {
363 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
364 tsta.is_vif_sta = 1;
365 }
366
367 tsta.sta_index = priv->nstations;
368 tsta.vif_index = avp->index;
369 tsta.maxampdu = 0xffff;
370 if (sta && sta->ht_cap.ht_supported)
371 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
372
373 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
374 if (ret) {
375 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800376 ath_err(common,
377 "Unable to add station entry for: %pM\n",
378 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530379 return ret;
380 }
381
382 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800383 ath_dbg(common, ATH_DBG_CONFIG,
384 "Added a station entry for: %pM (idx: %d)\n",
385 sta->addr, tsta.sta_index);
Sujithfb9987d2010-03-17 14:25:25 +0530386
387 priv->nstations++;
388 return 0;
389}
390
391static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
392 struct ieee80211_vif *vif,
393 struct ieee80211_sta *sta)
394{
395 struct ath_common *common = ath9k_hw_common(priv->ah);
396 struct ath9k_htc_sta *ista;
397 int ret;
398 u8 cmd_rsp, sta_idx;
399
400 if (sta) {
401 ista = (struct ath9k_htc_sta *) sta->drv_priv;
402 sta_idx = ista->index;
403 } else {
404 sta_idx = 0;
405 }
406
407 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
408 if (ret) {
409 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800410 ath_err(common,
411 "Unable to remove station entry for: %pM\n",
412 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530413 return ret;
414 }
415
416 if (sta)
Joe Perches226afe62010-12-02 19:12:37 -0800417 ath_dbg(common, ATH_DBG_CONFIG,
418 "Removed a station entry for: %pM (idx: %d)\n",
419 sta->addr, sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530420
421 priv->nstations--;
422 return 0;
423}
424
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530425int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530426{
427 struct ath9k_htc_cap_target tcap;
428 int ret;
429 u8 cmd_rsp;
430
431 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
432
433 /* FIXME: Values are hardcoded */
434 tcap.flags = 0x240c40;
435 tcap.flags_ext = 0x80601000;
436 tcap.ampdu_limit = 0xffff0000;
437 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530438 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530439 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530440 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530441
442 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
443
444 return ret;
445}
446
Sujith0d425a72010-05-17 12:01:16 +0530447static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
448 struct ieee80211_sta *sta,
449 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530450{
Sujithfb9987d2010-03-17 14:25:25 +0530451 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
452 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530453 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530454 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530455
Sujithea46e642010-06-02 15:53:50 +0530456 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530457
458 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
459 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530460 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530461 = (sband->bitrates[i].bitrate * 2) / 10;
462 j++;
463 }
464 }
Sujith0d425a72010-05-17 12:01:16 +0530465 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530466
467 if (sta->ht_cap.ht_supported) {
468 for (i = 0, j = 0; i < 77; i++) {
469 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530470 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530471 if (j == ATH_HTC_RATE_MAX)
472 break;
473 }
Sujith0d425a72010-05-17 12:01:16 +0530474 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530475
476 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200477 if (sta->ht_cap.mcs.rx_mask[1])
478 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530479 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
480 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530481 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530482 if (conf_is_ht40(&priv->hw->conf) &&
483 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530484 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530485 else if (conf_is_ht20(&priv->hw->conf) &&
486 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
487 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530488 }
489
Sujith0d425a72010-05-17 12:01:16 +0530490 trate->sta_index = ista->index;
491 trate->isnew = 1;
492 trate->capflags = cpu_to_be32(caps);
493}
Sujithfb9987d2010-03-17 14:25:25 +0530494
Sujith0d425a72010-05-17 12:01:16 +0530495static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
496 struct ath9k_htc_target_rate *trate)
497{
498 struct ath_common *common = ath9k_hw_common(priv->ah);
499 int ret;
500 u8 cmd_rsp;
501
502 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530503 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800504 ath_err(common,
505 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530506 }
507
Sujith0d425a72010-05-17 12:01:16 +0530508 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530509}
510
Sujith0d425a72010-05-17 12:01:16 +0530511static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
512 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530513{
Sujithfb9987d2010-03-17 14:25:25 +0530514 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530515 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530516 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530517
Sujith0d425a72010-05-17 12:01:16 +0530518 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
519 ath9k_htc_setup_rate(priv, sta, &trate);
520 ret = ath9k_htc_send_rate_cmd(priv, &trate);
521 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800522 ath_dbg(common, ATH_DBG_CONFIG,
523 "Updated target sta: %pM, rate caps: 0x%X\n",
524 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530525}
526
Sujith2c76ef82010-05-17 12:01:18 +0530527static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
528 struct ieee80211_vif *vif,
529 struct ieee80211_bss_conf *bss_conf)
530{
531 struct ath_common *common = ath9k_hw_common(priv->ah);
532 struct ath9k_htc_target_rate trate;
533 struct ieee80211_sta *sta;
534 int ret;
535
536 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
537
538 rcu_read_lock();
539 sta = ieee80211_find_sta(vif, bss_conf->bssid);
540 if (!sta) {
541 rcu_read_unlock();
542 return;
543 }
544 ath9k_htc_setup_rate(priv, sta, &trate);
545 rcu_read_unlock();
546
547 ret = ath9k_htc_send_rate_cmd(priv, &trate);
548 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800549 ath_dbg(common, ATH_DBG_CONFIG,
550 "Updated target sta: %pM, rate caps: 0x%X\n",
551 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530552}
553
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400554static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
555 struct ieee80211_vif *vif,
556 struct ieee80211_sta *sta,
557 enum ieee80211_ampdu_mlme_action action,
558 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530559{
560 struct ath_common *common = ath9k_hw_common(priv->ah);
561 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200562 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530563 int ret = 0;
564 u8 cmd_rsp;
565
Dan Carpenter0730d112010-05-08 18:24:02 +0200566 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530567 return -EINVAL;
568
Sujithfb9987d2010-03-17 14:25:25 +0530569 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530570 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530571
Sujithef98c3c2010-03-29 16:07:11 +0530572 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530573 aggr.tidno = tid & 0xf;
574 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530575
Sujithfb9987d2010-03-17 14:25:25 +0530576 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
577 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800578 ath_dbg(common, ATH_DBG_CONFIG,
579 "Unable to %s TX aggregation for (%pM, %d)\n",
580 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530581 else
Joe Perches226afe62010-12-02 19:12:37 -0800582 ath_dbg(common, ATH_DBG_CONFIG,
583 "%s TX aggregation for (%pM, %d)\n",
584 (aggr.aggr_enable) ? "Starting" : "Stopping",
585 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530586
587 spin_lock_bh(&priv->tx_lock);
588 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
589 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530590
591 return ret;
592}
593
Sujithfb9987d2010-03-17 14:25:25 +0530594/*********/
595/* DEBUG */
596/*********/
597
598#ifdef CONFIG_ATH9K_HTC_DEBUGFS
599
600static int ath9k_debugfs_open(struct inode *inode, struct file *file)
601{
602 file->private_data = inode->i_private;
603 return 0;
604}
605
606static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
607 size_t count, loff_t *ppos)
608{
Joe Perches57674302010-07-12 13:50:06 -0700609 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530610 struct ath9k_htc_target_stats cmd_rsp;
611 char buf[512];
612 unsigned int len = 0;
613 int ret = 0;
614
615 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
616
617 WMI_CMD(WMI_TGT_STATS_CMDID);
618 if (ret)
619 return -EINVAL;
620
621
622 len += snprintf(buf + len, sizeof(buf) - len,
623 "%19s : %10u\n", "TX Short Retries",
624 be32_to_cpu(cmd_rsp.tx_shortretry));
625 len += snprintf(buf + len, sizeof(buf) - len,
626 "%19s : %10u\n", "TX Long Retries",
627 be32_to_cpu(cmd_rsp.tx_longretry));
628 len += snprintf(buf + len, sizeof(buf) - len,
629 "%19s : %10u\n", "TX Xretries",
630 be32_to_cpu(cmd_rsp.tx_xretries));
631 len += snprintf(buf + len, sizeof(buf) - len,
632 "%19s : %10u\n", "TX Unaggr. Xretries",
633 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
634 len += snprintf(buf + len, sizeof(buf) - len,
635 "%19s : %10u\n", "TX Xretries (HT)",
636 be32_to_cpu(cmd_rsp.ht_tx_xretries));
637 len += snprintf(buf + len, sizeof(buf) - len,
638 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
639
Dan Carpenter97460102010-07-22 10:50:28 +0200640 if (len > sizeof(buf))
641 len = sizeof(buf);
642
Sujithfb9987d2010-03-17 14:25:25 +0530643 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
644}
645
646static const struct file_operations fops_tgt_stats = {
647 .read = read_file_tgt_stats,
648 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200649 .owner = THIS_MODULE,
650 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530651};
652
653static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
654 size_t count, loff_t *ppos)
655{
Joe Perches57674302010-07-12 13:50:06 -0700656 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530657 char buf[512];
658 unsigned int len = 0;
659
660 len += snprintf(buf + len, sizeof(buf) - len,
661 "%20s : %10u\n", "Buffers queued",
662 priv->debug.tx_stats.buf_queued);
663 len += snprintf(buf + len, sizeof(buf) - len,
664 "%20s : %10u\n", "Buffers completed",
665 priv->debug.tx_stats.buf_completed);
666 len += snprintf(buf + len, sizeof(buf) - len,
667 "%20s : %10u\n", "SKBs queued",
668 priv->debug.tx_stats.skb_queued);
669 len += snprintf(buf + len, sizeof(buf) - len,
670 "%20s : %10u\n", "SKBs completed",
671 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530672 len += snprintf(buf + len, sizeof(buf) - len,
673 "%20s : %10u\n", "SKBs dropped",
674 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530675
Sujith2edb4582010-05-14 11:18:54 +0530676 len += snprintf(buf + len, sizeof(buf) - len,
677 "%20s : %10u\n", "BE queued",
678 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
679 len += snprintf(buf + len, sizeof(buf) - len,
680 "%20s : %10u\n", "BK queued",
681 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
682 len += snprintf(buf + len, sizeof(buf) - len,
683 "%20s : %10u\n", "VI queued",
684 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
685 len += snprintf(buf + len, sizeof(buf) - len,
686 "%20s : %10u\n", "VO queued",
687 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
688
Dan Carpenter97460102010-07-22 10:50:28 +0200689 if (len > sizeof(buf))
690 len = sizeof(buf);
691
Sujithfb9987d2010-03-17 14:25:25 +0530692 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
693}
694
695static const struct file_operations fops_xmit = {
696 .read = read_file_xmit,
697 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200698 .owner = THIS_MODULE,
699 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530700};
701
702static ssize_t read_file_recv(struct file *file, char __user *user_buf,
703 size_t count, loff_t *ppos)
704{
Joe Perches57674302010-07-12 13:50:06 -0700705 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530706 char buf[512];
707 unsigned int len = 0;
708
709 len += snprintf(buf + len, sizeof(buf) - len,
710 "%20s : %10u\n", "SKBs allocated",
711 priv->debug.rx_stats.skb_allocated);
712 len += snprintf(buf + len, sizeof(buf) - len,
713 "%20s : %10u\n", "SKBs completed",
714 priv->debug.rx_stats.skb_completed);
715 len += snprintf(buf + len, sizeof(buf) - len,
716 "%20s : %10u\n", "SKBs Dropped",
717 priv->debug.rx_stats.skb_dropped);
718
Dan Carpenter97460102010-07-22 10:50:28 +0200719 if (len > sizeof(buf))
720 len = sizeof(buf);
721
Sujithfb9987d2010-03-17 14:25:25 +0530722 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
723}
724
725static const struct file_operations fops_recv = {
726 .read = read_file_recv,
727 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200728 .owner = THIS_MODULE,
729 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530730};
731
Sujithe1572c52010-03-24 13:42:13 +0530732int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530733{
734 struct ath_common *common = ath9k_hw_common(ah);
735 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
736
737 if (!ath9k_debugfs_root)
738 return -ENOENT;
739
740 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
741 ath9k_debugfs_root);
742 if (!priv->debug.debugfs_phy)
743 goto err;
744
745 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
746 priv->debug.debugfs_phy,
747 priv, &fops_tgt_stats);
748 if (!priv->debug.debugfs_tgt_stats)
749 goto err;
750
751
752 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
753 priv->debug.debugfs_phy,
754 priv, &fops_xmit);
755 if (!priv->debug.debugfs_xmit)
756 goto err;
757
758 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
759 priv->debug.debugfs_phy,
760 priv, &fops_recv);
761 if (!priv->debug.debugfs_recv)
762 goto err;
763
764 return 0;
765
766err:
Sujithe1572c52010-03-24 13:42:13 +0530767 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530768 return -ENOMEM;
769}
770
Sujithe1572c52010-03-24 13:42:13 +0530771void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530772{
773 struct ath_common *common = ath9k_hw_common(ah);
774 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
775
776 debugfs_remove(priv->debug.debugfs_recv);
777 debugfs_remove(priv->debug.debugfs_xmit);
778 debugfs_remove(priv->debug.debugfs_tgt_stats);
779 debugfs_remove(priv->debug.debugfs_phy);
780}
781
Sujithe1572c52010-03-24 13:42:13 +0530782int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530783{
784 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
785 if (!ath9k_debugfs_root)
786 return -ENOENT;
787
788 return 0;
789}
790
Sujithe1572c52010-03-24 13:42:13 +0530791void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530792{
793 debugfs_remove(ath9k_debugfs_root);
794 ath9k_debugfs_root = NULL;
795}
796
797#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
798
799/*******/
800/* ANI */
801/*******/
802
Sujith Manoharan73908672010-12-28 14:28:27 +0530803void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530804{
805 struct ath_common *common = ath9k_hw_common(priv->ah);
806 unsigned long timestamp = jiffies_to_msecs(jiffies);
807
808 common->ani.longcal_timer = timestamp;
809 common->ani.shortcal_timer = timestamp;
810 common->ani.checkani_timer = timestamp;
811
812 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
813 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
814}
815
816void ath9k_ani_work(struct work_struct *work)
817{
818 struct ath9k_htc_priv *priv =
819 container_of(work, struct ath9k_htc_priv,
820 ath9k_ani_work.work);
821 struct ath_hw *ah = priv->ah;
822 struct ath_common *common = ath9k_hw_common(ah);
823 bool longcal = false;
824 bool shortcal = false;
825 bool aniflag = false;
826 unsigned int timestamp = jiffies_to_msecs(jiffies);
827 u32 cal_interval, short_cal_interval;
828
829 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
830
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530831 /* Only calibrate if awake */
832 if (ah->power_mode != ATH9K_PM_AWAKE)
833 goto set_timer;
834
Sujithfb9987d2010-03-17 14:25:25 +0530835 /* Long calibration runs independently of short calibration. */
836 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
837 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800838 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530839 common->ani.longcal_timer = timestamp;
840 }
841
842 /* Short calibration applies only while caldone is false */
843 if (!common->ani.caldone) {
844 if ((timestamp - common->ani.shortcal_timer) >=
845 short_cal_interval) {
846 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800847 ath_dbg(common, ATH_DBG_ANI,
848 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530849 common->ani.shortcal_timer = timestamp;
850 common->ani.resetcal_timer = timestamp;
851 }
852 } else {
853 if ((timestamp - common->ani.resetcal_timer) >=
854 ATH_RESTART_CALINTERVAL) {
855 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
856 if (common->ani.caldone)
857 common->ani.resetcal_timer = timestamp;
858 }
859 }
860
861 /* Verify whether we must check ANI */
862 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
863 aniflag = true;
864 common->ani.checkani_timer = timestamp;
865 }
866
867 /* Skip all processing if there's nothing to do. */
868 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530869
870 ath9k_htc_ps_wakeup(priv);
871
Sujithfb9987d2010-03-17 14:25:25 +0530872 /* Call ANI routine if necessary */
873 if (aniflag)
874 ath9k_hw_ani_monitor(ah, ah->curchan);
875
876 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200877 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530878 common->ani.caldone =
879 ath9k_hw_calibrate(ah, ah->curchan,
880 common->rx_chainmask,
881 longcal);
882
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530883 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530884 }
885
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530886set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530887 /*
888 * Set timer interval based on previous results.
889 * The interval must be the shortest necessary to satisfy ANI,
890 * short calibration and long calibration.
891 */
892 cal_interval = ATH_LONG_CALINTERVAL;
893 if (priv->ah->config.enable_ani)
894 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
895 if (!common->ani.caldone)
896 cal_interval = min(cal_interval, (u32)short_cal_interval);
897
898 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
899 msecs_to_jiffies(cal_interval));
900}
901
Sujithfb9987d2010-03-17 14:25:25 +0530902/**********************/
903/* mac80211 Callbacks */
904/**********************/
905
906static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
907{
908 struct ieee80211_hdr *hdr;
909 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +0530910 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +0530911
912 hdr = (struct ieee80211_hdr *) skb->data;
913
914 /* Add the padding after the header if this is not already done */
915 padpos = ath9k_cmn_padpos(hdr->frame_control);
916 padsize = padpos & 3;
917 if (padsize && skb->len > padpos) {
918 if (skb_headroom(skb) < padsize)
919 return -1;
920 skb_push(skb, padsize);
921 memmove(skb->data, skb->data + padsize, padpos);
922 }
923
Sujith7757dfe2010-03-29 16:07:17 +0530924 ret = ath9k_htc_tx_start(priv, skb);
925 if (ret != 0) {
926 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -0800927 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
928 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +0530929 ieee80211_stop_queues(hw);
930 spin_lock_bh(&priv->tx_lock);
931 priv->tx_queues_stop = true;
932 spin_unlock_bh(&priv->tx_lock);
933 } else {
Joe Perches226afe62010-12-02 19:12:37 -0800934 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
935 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +0530936 }
Sujithfb9987d2010-03-17 14:25:25 +0530937 goto fail_tx;
938 }
939
940 return 0;
941
942fail_tx:
943 dev_kfree_skb_any(skb);
944 return 0;
945}
946
Sujith881ac6a2010-06-01 15:14:11 +0530947static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530948{
949 struct ath9k_htc_priv *priv = hw->priv;
950 struct ath_hw *ah = priv->ah;
951 struct ath_common *common = ath9k_hw_common(ah);
952 struct ieee80211_channel *curchan = hw->conf.channel;
953 struct ath9k_channel *init_channel;
954 int ret = 0;
955 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530956 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530957 u8 cmd_rsp;
958
Sujith881ac6a2010-06-01 15:14:11 +0530959 mutex_lock(&priv->mutex);
960
Joe Perches226afe62010-12-02 19:12:37 -0800961 ath_dbg(common, ATH_DBG_CONFIG,
962 "Starting driver with initial channel: %d MHz\n",
963 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530964
Sujith21d51302010-06-01 15:14:18 +0530965 /* Ensure that HW is awake before flushing RX */
966 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
967 WMI_CMD(WMI_FLUSH_RECV_CMDID);
968
Sujithfb9987d2010-03-17 14:25:25 +0530969 /* setup initial channel */
970 init_channel = ath9k_cmn_get_curchannel(hw, ah);
971
Sujithfb9987d2010-03-17 14:25:25 +0530972 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200973 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530974 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800975 ath_err(common,
976 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
977 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530978 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530979 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530980 }
981
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530982 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
983 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530984
985 mode = ath9k_htc_get_curmode(priv, init_channel);
986 htc_mode = cpu_to_be16(mode);
987 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530988 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530989 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530990
991 ath9k_host_rx_init(priv);
992
993 priv->op_flags &= ~OP_INVALID;
994 htc_start(priv->htc);
995
Sujith7757dfe2010-03-29 16:07:17 +0530996 spin_lock_bh(&priv->tx_lock);
997 priv->tx_queues_stop = false;
998 spin_unlock_bh(&priv->tx_lock);
999
1000 ieee80211_wake_queues(hw);
1001
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301002 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1003 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1004 AR_STOMP_LOW_WLAN_WGHT);
1005 ath9k_hw_btcoex_enable(ah);
1006 ath_htc_resume_btcoex_work(priv);
1007 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301008 mutex_unlock(&priv->mutex);
1009
1010 return ret;
1011}
1012
Sujith881ac6a2010-06-01 15:14:11 +05301013static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301014{
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 = 0;
1019 u8 cmd_rsp;
1020
Sujith881ac6a2010-06-01 15:14:11 +05301021 mutex_lock(&priv->mutex);
1022
Sujithfb9987d2010-03-17 14:25:25 +05301023 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001024 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301025 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301026 return;
1027 }
1028
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301029 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301030 htc_stop(priv->htc);
1031 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1032 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1033 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001034
1035 tasklet_kill(&priv->swba_tasklet);
1036 tasklet_kill(&priv->rx_tasklet);
1037 tasklet_kill(&priv->tx_tasklet);
1038
Sujithfb9987d2010-03-17 14:25:25 +05301039 skb_queue_purge(&priv->tx_queue);
1040
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001041 mutex_unlock(&priv->mutex);
1042
1043 /* Cancel all the running timers/work .. */
1044 cancel_work_sync(&priv->fatal_work);
1045 cancel_work_sync(&priv->ps_work);
1046 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Rajkumar Manoharan45655ba2011-01-31 23:47:42 +05301047 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001048 ath9k_led_stop_brightness(priv);
1049
1050 mutex_lock(&priv->mutex);
1051
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301052 /* Remove monitor interface here */
1053 if (ah->opmode == NL80211_IFTYPE_MONITOR) {
1054 if (ath9k_htc_remove_monitor_interface(priv))
Joe Perches38002762010-12-02 19:12:36 -08001055 ath_err(common, "Unable to remove monitor interface\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301056 else
Joe Perches226afe62010-12-02 19:12:37 -08001057 ath_dbg(common, ATH_DBG_CONFIG,
1058 "Monitor interface removed\n");
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +05301059 }
1060
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301061 if (ah->btcoex_hw.enabled) {
1062 ath9k_hw_btcoex_disable(ah);
1063 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1064 ath_htc_cancel_btcoex_work(priv);
1065 }
1066
Sujithe9201f02010-06-01 15:14:17 +05301067 ath9k_hw_phy_disable(ah);
1068 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301069 ath9k_htc_ps_restore(priv);
1070 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1071
Sujithfb9987d2010-03-17 14:25:25 +05301072 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301073
Joe Perches226afe62010-12-02 19:12:37 -08001074 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301075 mutex_unlock(&priv->mutex);
1076}
1077
Sujithfb9987d2010-03-17 14:25:25 +05301078static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1079 struct ieee80211_vif *vif)
1080{
1081 struct ath9k_htc_priv *priv = hw->priv;
1082 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1083 struct ath_common *common = ath9k_hw_common(priv->ah);
1084 struct ath9k_htc_target_vif hvif;
1085 int ret = 0;
1086 u8 cmd_rsp;
1087
1088 mutex_lock(&priv->mutex);
1089
1090 /* Only one interface for now */
1091 if (priv->nvifs > 0) {
1092 ret = -ENOBUFS;
1093 goto out;
1094 }
1095
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301096 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301097 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1098 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1099
1100 switch (vif->type) {
1101 case NL80211_IFTYPE_STATION:
1102 hvif.opmode = cpu_to_be32(HTC_M_STA);
1103 break;
1104 case NL80211_IFTYPE_ADHOC:
1105 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1106 break;
1107 default:
Joe Perches38002762010-12-02 19:12:36 -08001108 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301109 "Interface type %d not yet supported\n", vif->type);
1110 ret = -EOPNOTSUPP;
1111 goto out;
1112 }
1113
Joe Perches226afe62010-12-02 19:12:37 -08001114 ath_dbg(common, ATH_DBG_CONFIG,
1115 "Attach a VIF of type: %d\n", vif->type);
Sujithfb9987d2010-03-17 14:25:25 +05301116
1117 priv->ah->opmode = vif->type;
1118
1119 /* Index starts from zero on the target */
1120 avp->index = hvif.index = priv->nvifs;
1121 hvif.rtsthreshold = cpu_to_be16(2304);
1122 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1123 if (ret)
1124 goto out;
1125
1126 priv->nvifs++;
1127
1128 /*
1129 * We need a node in target to tx mgmt frames
1130 * before association.
1131 */
1132 ret = ath9k_htc_add_station(priv, vif, NULL);
1133 if (ret)
1134 goto out;
1135
1136 ret = ath9k_htc_update_cap_target(priv);
1137 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -08001138 ath_dbg(common, ATH_DBG_CONFIG,
1139 "Failed to update capability in target\n");
Sujithfb9987d2010-03-17 14:25:25 +05301140
1141 priv->vif = vif;
1142out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301143 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301144 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301145
Sujithfb9987d2010-03-17 14:25:25 +05301146 return ret;
1147}
1148
1149static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1150 struct ieee80211_vif *vif)
1151{
1152 struct ath9k_htc_priv *priv = hw->priv;
1153 struct ath_common *common = ath9k_hw_common(priv->ah);
1154 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1155 struct ath9k_htc_target_vif hvif;
1156 int ret = 0;
1157 u8 cmd_rsp;
1158
Joe Perches226afe62010-12-02 19:12:37 -08001159 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n");
Sujithfb9987d2010-03-17 14:25:25 +05301160
1161 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301162 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301163
1164 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1165 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1166 hvif.index = avp->index;
1167 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1168 priv->nvifs--;
1169
1170 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301171 priv->vif = NULL;
1172
Sujithcb551df2010-06-01 15:14:12 +05301173 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301174 mutex_unlock(&priv->mutex);
1175}
1176
1177static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1178{
1179 struct ath9k_htc_priv *priv = hw->priv;
1180 struct ath_common *common = ath9k_hw_common(priv->ah);
1181 struct ieee80211_conf *conf = &hw->conf;
1182
1183 mutex_lock(&priv->mutex);
1184
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301185 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1186 bool enable_radio = false;
1187 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1188
Sujith23367762010-06-01 15:14:16 +05301189 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301190 if (!idle && priv->ps_idle)
1191 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301192 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301193 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301194
1195 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001196 ath_dbg(common, ATH_DBG_CONFIG,
1197 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301198 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1199 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301200 }
1201 }
1202
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301203 /*
1204 * Monitor interface should be added before
1205 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1206 */
1207 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
1208 if (conf->flags & IEEE80211_CONF_MONITOR) {
1209 if (ath9k_htc_add_monitor_interface(priv))
1210 ath_err(common, "Failed to set monitor mode\n");
1211 else
1212 ath_dbg(common, ATH_DBG_CONFIG,
1213 "HW opmode set to Monitor mode\n");
1214 }
1215 }
1216
Sujithfb9987d2010-03-17 14:25:25 +05301217 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1218 struct ieee80211_channel *curchan = hw->conf.channel;
1219 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301220
Joe Perches226afe62010-12-02 19:12:37 -08001221 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1222 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301223
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001224 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1225 hw->conf.channel,
1226 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301227
1228 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001229 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301230 mutex_unlock(&priv->mutex);
1231 return -EINVAL;
1232 }
1233
1234 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301235
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301236 if (changed & IEEE80211_CONF_CHANGE_PS) {
1237 if (conf->flags & IEEE80211_CONF_PS) {
1238 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1239 priv->ps_enabled = true;
1240 } else {
1241 priv->ps_enabled = false;
1242 cancel_work_sync(&priv->ps_work);
1243 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1244 }
1245 }
Sujithfb9987d2010-03-17 14:25:25 +05301246
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301247 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1248 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301249 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1250 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301251 }
1252
Sujith23367762010-06-01 15:14:16 +05301253 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1254 mutex_lock(&priv->htc_pm_lock);
1255 if (!priv->ps_idle) {
1256 mutex_unlock(&priv->htc_pm_lock);
1257 goto out;
1258 }
1259 mutex_unlock(&priv->htc_pm_lock);
1260
Joe Perches226afe62010-12-02 19:12:37 -08001261 ath_dbg(common, ATH_DBG_CONFIG,
1262 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301263 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301264 }
1265
Sujith23367762010-06-01 15:14:16 +05301266out:
Sujithfb9987d2010-03-17 14:25:25 +05301267 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301268 return 0;
1269}
1270
1271#define SUPPORTED_FILTERS \
1272 (FIF_PROMISC_IN_BSS | \
1273 FIF_ALLMULTI | \
1274 FIF_CONTROL | \
1275 FIF_PSPOLL | \
1276 FIF_OTHER_BSS | \
1277 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301278 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301279 FIF_FCSFAIL)
1280
1281static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1282 unsigned int changed_flags,
1283 unsigned int *total_flags,
1284 u64 multicast)
1285{
1286 struct ath9k_htc_priv *priv = hw->priv;
1287 u32 rfilt;
1288
1289 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301290 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301291
Sujithfb9987d2010-03-17 14:25:25 +05301292 changed_flags &= SUPPORTED_FILTERS;
1293 *total_flags &= SUPPORTED_FILTERS;
1294
1295 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301296 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301297 ath9k_hw_setrxfilter(priv->ah, rfilt);
1298
Joe Perches226afe62010-12-02 19:12:37 -08001299 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1300 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301301
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301302 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301303 mutex_unlock(&priv->mutex);
1304}
1305
Sujithabd984e2010-05-18 15:26:04 +05301306static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1307 struct ieee80211_vif *vif,
1308 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301309{
1310 struct ath9k_htc_priv *priv = hw->priv;
1311 int ret;
1312
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301313 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301314 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301315 ret = ath9k_htc_add_station(priv, vif, sta);
1316 if (!ret)
1317 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301318 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301319 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301320
1321 return ret;
1322}
1323
1324static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1325 struct ieee80211_vif *vif,
1326 struct ieee80211_sta *sta)
1327{
1328 struct ath9k_htc_priv *priv = hw->priv;
1329 int ret;
1330
1331 mutex_lock(&priv->mutex);
1332 ath9k_htc_ps_wakeup(priv);
1333 ret = ath9k_htc_remove_station(priv, vif, sta);
1334 ath9k_htc_ps_restore(priv);
1335 mutex_unlock(&priv->mutex);
1336
1337 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301338}
1339
1340static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1341 const struct ieee80211_tx_queue_params *params)
1342{
1343 struct ath9k_htc_priv *priv = hw->priv;
1344 struct ath_common *common = ath9k_hw_common(priv->ah);
1345 struct ath9k_tx_queue_info qi;
1346 int ret = 0, qnum;
1347
1348 if (queue >= WME_NUM_AC)
1349 return 0;
1350
1351 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301352 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301353
1354 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1355
1356 qi.tqi_aifs = params->aifs;
1357 qi.tqi_cwmin = params->cw_min;
1358 qi.tqi_cwmax = params->cw_max;
1359 qi.tqi_burstTime = params->txop;
1360
1361 qnum = get_hw_qnum(queue, priv->hwq_map);
1362
Joe Perches226afe62010-12-02 19:12:37 -08001363 ath_dbg(common, ATH_DBG_CONFIG,
1364 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1365 queue, qnum, params->aifs, params->cw_min,
1366 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301367
Sujithe1572c52010-03-24 13:42:13 +05301368 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301369 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001370 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301371 goto out;
1372 }
Sujithfb9987d2010-03-17 14:25:25 +05301373
Sujith764580f2010-06-01 15:14:19 +05301374 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001375 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301376 ath9k_htc_beaconq_config(priv);
1377out:
Sujithcb551df2010-06-01 15:14:12 +05301378 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301379 mutex_unlock(&priv->mutex);
1380
1381 return ret;
1382}
1383
1384static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1385 enum set_key_cmd cmd,
1386 struct ieee80211_vif *vif,
1387 struct ieee80211_sta *sta,
1388 struct ieee80211_key_conf *key)
1389{
1390 struct ath9k_htc_priv *priv = hw->priv;
1391 struct ath_common *common = ath9k_hw_common(priv->ah);
1392 int ret = 0;
1393
Sujithe1572c52010-03-24 13:42:13 +05301394 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301395 return -ENOSPC;
1396
1397 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001398 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301399 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301400
1401 switch (cmd) {
1402 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001403 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301404 if (ret >= 0) {
1405 key->hw_key_idx = ret;
1406 /* push IV and Michael MIC generation to stack */
1407 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001408 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301409 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001410 if (priv->ah->sw_mgmt_crypto &&
1411 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301412 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1413 ret = 0;
1414 }
1415 break;
1416 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001417 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301418 break;
1419 default:
1420 ret = -EINVAL;
1421 }
1422
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301423 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301424 mutex_unlock(&priv->mutex);
1425
1426 return ret;
1427}
1428
1429static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1430 struct ieee80211_vif *vif,
1431 struct ieee80211_bss_conf *bss_conf,
1432 u32 changed)
1433{
1434 struct ath9k_htc_priv *priv = hw->priv;
1435 struct ath_hw *ah = priv->ah;
1436 struct ath_common *common = ath9k_hw_common(ah);
1437
1438 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301439 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301440
1441 if (changed & BSS_CHANGED_ASSOC) {
1442 common->curaid = bss_conf->assoc ?
1443 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001444 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301445 bss_conf->assoc);
1446
1447 if (bss_conf->assoc) {
1448 priv->op_flags |= OP_ASSOCIATED;
1449 ath_start_ani(priv);
1450 } else {
1451 priv->op_flags &= ~OP_ASSOCIATED;
1452 cancel_delayed_work_sync(&priv->ath9k_ani_work);
1453 }
1454 }
1455
1456 if (changed & BSS_CHANGED_BSSID) {
1457 /* Set BSSID */
1458 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1459 ath9k_hw_write_associd(ah);
1460
Joe Perches226afe62010-12-02 19:12:37 -08001461 ath_dbg(common, ATH_DBG_CONFIG,
1462 "BSSID: %pM aid: 0x%x\n",
1463 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301464 }
1465
1466 if ((changed & BSS_CHANGED_BEACON_INT) ||
1467 (changed & BSS_CHANGED_BEACON) ||
1468 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1469 bss_conf->enable_beacon)) {
1470 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301471 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301472 }
1473
Sujithfb9987d2010-03-17 14:25:25 +05301474 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1475 !bss_conf->enable_beacon) {
1476 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301477 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301478 }
1479
1480 if (changed & BSS_CHANGED_ERP_PREAMBLE) {
Joe Perches226afe62010-12-02 19:12:37 -08001481 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n",
1482 bss_conf->use_short_preamble);
Sujithfb9987d2010-03-17 14:25:25 +05301483 if (bss_conf->use_short_preamble)
1484 priv->op_flags |= OP_PREAMBLE_SHORT;
1485 else
1486 priv->op_flags &= ~OP_PREAMBLE_SHORT;
1487 }
1488
1489 if (changed & BSS_CHANGED_ERP_CTS_PROT) {
Joe Perches226afe62010-12-02 19:12:37 -08001490 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n",
1491 bss_conf->use_cts_prot);
Sujithfb9987d2010-03-17 14:25:25 +05301492 if (bss_conf->use_cts_prot &&
1493 hw->conf.channel->band != IEEE80211_BAND_5GHZ)
1494 priv->op_flags |= OP_PROTECT_ENABLE;
1495 else
1496 priv->op_flags &= ~OP_PROTECT_ENABLE;
1497 }
1498
1499 if (changed & BSS_CHANGED_ERP_SLOT) {
1500 if (bss_conf->use_short_slot)
1501 ah->slottime = 9;
1502 else
1503 ah->slottime = 20;
1504
1505 ath9k_hw_init_global_settings(ah);
1506 }
1507
Sujith2c76ef82010-05-17 12:01:18 +05301508 if (changed & BSS_CHANGED_HT)
1509 ath9k_htc_update_rate(priv, vif, bss_conf);
1510
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301511 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301512 mutex_unlock(&priv->mutex);
1513}
1514
1515static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1516{
1517 struct ath9k_htc_priv *priv = hw->priv;
1518 u64 tsf;
1519
1520 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301521 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301522 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301523 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301524 mutex_unlock(&priv->mutex);
1525
1526 return tsf;
1527}
1528
1529static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1530{
1531 struct ath9k_htc_priv *priv = hw->priv;
1532
1533 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301534 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301535 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301536 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301537 mutex_unlock(&priv->mutex);
1538}
1539
1540static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1541{
1542 struct ath9k_htc_priv *priv = hw->priv;
1543
1544 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301545 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301546 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301547 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301548 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301549}
1550
1551static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1552 struct ieee80211_vif *vif,
1553 enum ieee80211_ampdu_mlme_action action,
1554 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001555 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301556{
1557 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301558 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301559 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301560
1561 switch (action) {
1562 case IEEE80211_AMPDU_RX_START:
1563 break;
1564 case IEEE80211_AMPDU_RX_STOP:
1565 break;
1566 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301567 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1568 if (!ret)
1569 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1570 break;
Sujithfb9987d2010-03-17 14:25:25 +05301571 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301572 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1573 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301574 break;
1575 case IEEE80211_AMPDU_TX_OPERATIONAL:
1576 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301577 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301578 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301579 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301580 break;
1581 default:
Joe Perches38002762010-12-02 19:12:36 -08001582 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301583 }
1584
Sujithd7ca2132010-06-15 10:24:37 +05301585 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301586}
1587
1588static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1589{
1590 struct ath9k_htc_priv *priv = hw->priv;
1591
1592 mutex_lock(&priv->mutex);
1593 spin_lock_bh(&priv->beacon_lock);
1594 priv->op_flags |= OP_SCANNING;
1595 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301596 cancel_work_sync(&priv->ps_work);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301597 if (priv->op_flags & OP_ASSOCIATED)
1598 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301599 mutex_unlock(&priv->mutex);
1600}
1601
1602static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1603{
1604 struct ath9k_htc_priv *priv = hw->priv;
1605
1606 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301607 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301608 spin_lock_bh(&priv->beacon_lock);
1609 priv->op_flags &= ~OP_SCANNING;
1610 spin_unlock_bh(&priv->beacon_lock);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301611 if (priv->op_flags & OP_ASSOCIATED) {
Sujithfcb93922010-04-16 11:53:48 +05301612 ath9k_htc_beacon_config(priv, priv->vif);
Rajkumar Manoharanfe674702010-08-27 12:09:00 +05301613 ath_start_ani(priv);
1614 }
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301615 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301616 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301617}
1618
1619static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1620{
1621 return 0;
1622}
1623
1624static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1625 u8 coverage_class)
1626{
1627 struct ath9k_htc_priv *priv = hw->priv;
1628
1629 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301630 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301631 priv->ah->coverage_class = coverage_class;
1632 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301633 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301634 mutex_unlock(&priv->mutex);
1635}
1636
1637struct ieee80211_ops ath9k_htc_ops = {
1638 .tx = ath9k_htc_tx,
1639 .start = ath9k_htc_start,
1640 .stop = ath9k_htc_stop,
1641 .add_interface = ath9k_htc_add_interface,
1642 .remove_interface = ath9k_htc_remove_interface,
1643 .config = ath9k_htc_config,
1644 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301645 .sta_add = ath9k_htc_sta_add,
1646 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301647 .conf_tx = ath9k_htc_conf_tx,
1648 .bss_info_changed = ath9k_htc_bss_info_changed,
1649 .set_key = ath9k_htc_set_key,
1650 .get_tsf = ath9k_htc_get_tsf,
1651 .set_tsf = ath9k_htc_set_tsf,
1652 .reset_tsf = ath9k_htc_reset_tsf,
1653 .ampdu_action = ath9k_htc_ampdu_action,
1654 .sw_scan_start = ath9k_htc_sw_scan_start,
1655 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1656 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1657 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1658 .set_coverage_class = ath9k_htc_set_coverage_class,
1659};