blob: 5ef076978181d7a59d6eccde3f7cbd6da55a4801 [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 Manoharan7c277342011-02-21 07:48:39 +0530108static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
109{
110 struct ath9k_htc_priv *priv = data;
111 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
112
113 if (bss_conf->assoc) {
114 priv->rearm_ani = true;
115 priv->reconfig_beacon = true;
116 }
117}
118
119static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
120{
121 priv->rearm_ani = false;
122 priv->reconfig_beacon = false;
123
124 ieee80211_iterate_active_interfaces_atomic(priv->hw,
125 ath9k_htc_vif_iter, priv);
126 if (priv->rearm_ani)
127 ath_start_ani(priv);
128
129 if (priv->reconfig_beacon) {
130 ath9k_htc_ps_wakeup(priv);
131 ath9k_htc_beacon_reconfig(priv);
132 ath9k_htc_ps_restore(priv);
133 }
134}
135
Sujith Manoharan73908672010-12-28 14:28:27 +0530136void ath9k_htc_reset(struct ath9k_htc_priv *priv)
137{
138 struct ath_hw *ah = priv->ah;
139 struct ath_common *common = ath9k_hw_common(ah);
140 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530141 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530142 enum htc_phymode mode;
143 __be16 htc_mode;
144 u8 cmd_rsp;
145 int ret;
146
147 mutex_lock(&priv->mutex);
148 ath9k_htc_ps_wakeup(priv);
149
Sujith Manoharan7c277342011-02-21 07:48:39 +0530150 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujith Manoharan73908672010-12-28 14:28:27 +0530151 ieee80211_stop_queues(priv->hw);
152 htc_stop(priv->htc);
153 WMI_CMD(WMI_DISABLE_INTR_CMDID);
154 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
155 WMI_CMD(WMI_STOP_RECV_CMDID);
156
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530157 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530158 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
159 if (ret) {
160 ath_err(common,
161 "Unable to reset device (%u Mhz) reset status %d\n",
162 channel->center_freq, ret);
163 }
164
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530165 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
166 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530167
168 WMI_CMD(WMI_START_RECV_CMDID);
169 ath9k_host_rx_init(priv);
170
171 mode = ath9k_htc_get_curmode(priv, ah->curchan);
172 htc_mode = cpu_to_be16(mode);
173 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
174
175 WMI_CMD(WMI_ENABLE_INTR_CMDID);
176 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530177 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530178 ieee80211_wake_queues(priv->hw);
179
180 ath9k_htc_ps_restore(priv);
181 mutex_unlock(&priv->mutex);
182}
183
Sujithfb9987d2010-03-17 14:25:25 +0530184static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
185 struct ieee80211_hw *hw,
186 struct ath9k_channel *hchan)
187{
188 struct ath_hw *ah = priv->ah;
189 struct ath_common *common = ath9k_hw_common(ah);
190 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530191 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530192 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530193 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530194 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530195 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530196 u8 cmd_rsp;
197 int ret;
198
199 if (priv->op_flags & OP_INVALID)
200 return -EIO;
201
Sujith Manoharan039a0722010-12-28 14:28:37 +0530202 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530203
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530204 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530205 htc_stop(priv->htc);
206 WMI_CMD(WMI_DISABLE_INTR_CMDID);
207 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
208 WMI_CMD(WMI_STOP_RECV_CMDID);
209
Joe Perches226afe62010-12-02 19:12:37 -0800210 ath_dbg(common, ATH_DBG_CONFIG,
211 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
212 priv->ah->curchan->channel,
213 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
214 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530215
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530216 if (!fastcc)
217 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200218 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530219 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800220 ath_err(common,
221 "Unable to reset channel (%u Mhz) reset status %d\n",
222 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530223 goto err;
224 }
225
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530226 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
227 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530228
229 WMI_CMD(WMI_START_RECV_CMDID);
230 if (ret)
231 goto err;
232
233 ath9k_host_rx_init(priv);
234
235 mode = ath9k_htc_get_curmode(priv, hchan);
236 htc_mode = cpu_to_be16(mode);
237 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
238 if (ret)
239 goto err;
240
241 WMI_CMD(WMI_ENABLE_INTR_CMDID);
242 if (ret)
243 goto err;
244
245 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530246err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530247 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530248 return ret;
249}
250
Sujith Manoharana97b4782011-02-21 07:48:00 +0530251/*
252 * Monitor mode handling is a tad complicated because the firmware requires
253 * an interface to be created exclusively, while mac80211 doesn't associate
254 * an interface with the mode.
255 *
256 * So, for now, only one monitor interface can be configured.
257 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530258static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530259{
260 struct ath_common *common = ath9k_hw_common(priv->ah);
261 struct ath9k_htc_target_vif hvif;
262 int ret = 0;
263 u8 cmd_rsp;
264
Sujith Manoharancc721282011-01-03 21:22:18 +0530265 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
266 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530267 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530268 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
269 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530270 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530271}
272
273static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
274{
275 struct ath_common *common = ath9k_hw_common(priv->ah);
276 struct ath9k_htc_target_vif hvif;
277 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530278 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530279 u8 cmd_rsp;
280
Sujith Manoharana97b4782011-02-21 07:48:00 +0530281 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
282 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
283 ret = -ENOBUFS;
284 goto err_vif;
285 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530286
Sujith Manoharana97b4782011-02-21 07:48:00 +0530287 sta_idx = ffz(priv->sta_slot);
288 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
289 ret = -ENOBUFS;
290 goto err_vif;
291 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530292
293 /*
294 * Add an interface.
295 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530296 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
297 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
298
299 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530300 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530301
302 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
303 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530304 goto err_vif;
305
306 /*
307 * Assign the monitor interface index as a special case here.
308 * This is needed when the interface is brought down.
309 */
310 priv->mon_vif_idx = hvif.index;
311 priv->vif_slot |= (1 << hvif.index);
312
313 /*
314 * Set the hardware mode to monitor only if there are no
315 * other interfaces.
316 */
317 if (!priv->nvifs)
318 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530319
320 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530321
322 /*
323 * Associate a station with the interface for packet injection.
324 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530325 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
326
327 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
328
329 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530330 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530331 tsta.vif_index = hvif.index;
332 tsta.maxampdu = 0xffff;
333
334 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
335 if (ret) {
336 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530337 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530338 }
339
Sujith Manoharana97b4782011-02-21 07:48:00 +0530340 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530341 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530342 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530343 priv->ah->is_monitoring = true;
344
Sujith Manoharana97b4782011-02-21 07:48:00 +0530345 ath_dbg(common, ATH_DBG_CONFIG,
346 "Attached a monitor interface at idx: %d, sta idx: %d\n",
347 priv->mon_vif_idx, sta_idx);
348
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530349 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530350
Sujith Manoharana97b4782011-02-21 07:48:00 +0530351err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530352 /*
353 * Remove the interface from the target.
354 */
355 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530356err_vif:
357 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
358
Sujith Manoharancc721282011-01-03 21:22:18 +0530359 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530360}
361
362static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
363{
364 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530365 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530366 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530367
Sujith Manoharancc721282011-01-03 21:22:18 +0530368 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530369
Sujith Manoharana97b4782011-02-21 07:48:00 +0530370 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530371
372 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
373 if (ret) {
374 ath_err(common, "Unable to remove station entry for monitor mode\n");
375 return ret;
376 }
377
Sujith Manoharana97b4782011-02-21 07:48:00 +0530378 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530379 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530380 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530381
Sujith Manoharana97b4782011-02-21 07:48:00 +0530382 ath_dbg(common, ATH_DBG_CONFIG,
383 "Removed a monitor interface at idx: %d, sta idx: %d\n",
384 priv->mon_vif_idx, sta_idx);
385
Sujith Manoharancc721282011-01-03 21:22:18 +0530386 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530387}
388
Sujithfb9987d2010-03-17 14:25:25 +0530389static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
390 struct ieee80211_vif *vif,
391 struct ieee80211_sta *sta)
392{
393 struct ath_common *common = ath9k_hw_common(priv->ah);
394 struct ath9k_htc_target_sta tsta;
395 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
396 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530397 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530398 u8 cmd_rsp;
399
400 if (priv->nstations >= ATH9K_HTC_MAX_STA)
401 return -ENOBUFS;
402
Sujith Manoharana97b4782011-02-21 07:48:00 +0530403 sta_idx = ffz(priv->sta_slot);
404 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
405 return -ENOBUFS;
406
Sujithfb9987d2010-03-17 14:25:25 +0530407 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
408
409 if (sta) {
410 ista = (struct ath9k_htc_sta *) sta->drv_priv;
411 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
412 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
413 tsta.associd = common->curaid;
414 tsta.is_vif_sta = 0;
415 tsta.valid = true;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530416 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530417 } else {
418 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
419 tsta.is_vif_sta = 1;
420 }
421
Sujith Manoharana97b4782011-02-21 07:48:00 +0530422 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530423 tsta.vif_index = avp->index;
424 tsta.maxampdu = 0xffff;
425 if (sta && sta->ht_cap.ht_supported)
426 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
427
428 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
429 if (ret) {
430 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800431 ath_err(common,
432 "Unable to add station entry for: %pM\n",
433 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530434 return ret;
435 }
436
Sujith Manoharana97b4782011-02-21 07:48:00 +0530437 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800438 ath_dbg(common, ATH_DBG_CONFIG,
439 "Added a station entry for: %pM (idx: %d)\n",
440 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530441 } else {
442 ath_dbg(common, ATH_DBG_CONFIG,
443 "Added a station entry for VIF %d (idx: %d)\n",
444 avp->index, tsta.sta_index);
445 }
Sujithfb9987d2010-03-17 14:25:25 +0530446
Sujith Manoharana97b4782011-02-21 07:48:00 +0530447 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530448 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530449 if (!sta)
450 priv->vif_sta_pos[avp->index] = sta_idx;
451
Sujithfb9987d2010-03-17 14:25:25 +0530452 return 0;
453}
454
455static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
456 struct ieee80211_vif *vif,
457 struct ieee80211_sta *sta)
458{
459 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530460 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530461 struct ath9k_htc_sta *ista;
462 int ret;
463 u8 cmd_rsp, sta_idx;
464
465 if (sta) {
466 ista = (struct ath9k_htc_sta *) sta->drv_priv;
467 sta_idx = ista->index;
468 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530469 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530470 }
471
472 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
473 if (ret) {
474 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800475 ath_err(common,
476 "Unable to remove station entry for: %pM\n",
477 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530478 return ret;
479 }
480
Sujith Manoharana97b4782011-02-21 07:48:00 +0530481 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800482 ath_dbg(common, ATH_DBG_CONFIG,
483 "Removed a station entry for: %pM (idx: %d)\n",
484 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530485 } else {
486 ath_dbg(common, ATH_DBG_CONFIG,
487 "Removed a station entry for VIF %d (idx: %d)\n",
488 avp->index, sta_idx);
489 }
Sujithfb9987d2010-03-17 14:25:25 +0530490
Sujith Manoharana97b4782011-02-21 07:48:00 +0530491 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530492 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530493
Sujithfb9987d2010-03-17 14:25:25 +0530494 return 0;
495}
496
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530497int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530498{
499 struct ath9k_htc_cap_target tcap;
500 int ret;
501 u8 cmd_rsp;
502
503 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
504
505 /* FIXME: Values are hardcoded */
506 tcap.flags = 0x240c40;
507 tcap.flags_ext = 0x80601000;
508 tcap.ampdu_limit = 0xffff0000;
509 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530510 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530511 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530512 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530513
514 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
515
516 return ret;
517}
518
Sujith0d425a72010-05-17 12:01:16 +0530519static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
520 struct ieee80211_sta *sta,
521 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530522{
Sujithfb9987d2010-03-17 14:25:25 +0530523 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
524 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530525 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530526 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530527
Sujithea46e642010-06-02 15:53:50 +0530528 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530529
530 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
531 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530532 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530533 = (sband->bitrates[i].bitrate * 2) / 10;
534 j++;
535 }
536 }
Sujith0d425a72010-05-17 12:01:16 +0530537 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530538
539 if (sta->ht_cap.ht_supported) {
540 for (i = 0, j = 0; i < 77; i++) {
541 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530542 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530543 if (j == ATH_HTC_RATE_MAX)
544 break;
545 }
Sujith0d425a72010-05-17 12:01:16 +0530546 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530547
548 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200549 if (sta->ht_cap.mcs.rx_mask[1])
550 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530551 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
552 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530553 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530554 if (conf_is_ht40(&priv->hw->conf) &&
555 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530556 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530557 else if (conf_is_ht20(&priv->hw->conf) &&
558 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
559 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530560 }
561
Sujith0d425a72010-05-17 12:01:16 +0530562 trate->sta_index = ista->index;
563 trate->isnew = 1;
564 trate->capflags = cpu_to_be32(caps);
565}
Sujithfb9987d2010-03-17 14:25:25 +0530566
Sujith0d425a72010-05-17 12:01:16 +0530567static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
568 struct ath9k_htc_target_rate *trate)
569{
570 struct ath_common *common = ath9k_hw_common(priv->ah);
571 int ret;
572 u8 cmd_rsp;
573
574 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530575 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800576 ath_err(common,
577 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530578 }
579
Sujith0d425a72010-05-17 12:01:16 +0530580 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530581}
582
Sujith0d425a72010-05-17 12:01:16 +0530583static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
584 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530585{
Sujithfb9987d2010-03-17 14:25:25 +0530586 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530587 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530588 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530589
Sujith0d425a72010-05-17 12:01:16 +0530590 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
591 ath9k_htc_setup_rate(priv, sta, &trate);
592 ret = ath9k_htc_send_rate_cmd(priv, &trate);
593 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800594 ath_dbg(common, ATH_DBG_CONFIG,
595 "Updated target sta: %pM, rate caps: 0x%X\n",
596 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530597}
598
Sujith2c76ef82010-05-17 12:01:18 +0530599static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
600 struct ieee80211_vif *vif,
601 struct ieee80211_bss_conf *bss_conf)
602{
603 struct ath_common *common = ath9k_hw_common(priv->ah);
604 struct ath9k_htc_target_rate trate;
605 struct ieee80211_sta *sta;
606 int ret;
607
608 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
609
610 rcu_read_lock();
611 sta = ieee80211_find_sta(vif, bss_conf->bssid);
612 if (!sta) {
613 rcu_read_unlock();
614 return;
615 }
616 ath9k_htc_setup_rate(priv, sta, &trate);
617 rcu_read_unlock();
618
619 ret = ath9k_htc_send_rate_cmd(priv, &trate);
620 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800621 ath_dbg(common, ATH_DBG_CONFIG,
622 "Updated target sta: %pM, rate caps: 0x%X\n",
623 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530624}
625
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400626static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
627 struct ieee80211_vif *vif,
628 struct ieee80211_sta *sta,
629 enum ieee80211_ampdu_mlme_action action,
630 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530631{
632 struct ath_common *common = ath9k_hw_common(priv->ah);
633 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200634 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530635 int ret = 0;
636 u8 cmd_rsp;
637
Dan Carpenter0730d112010-05-08 18:24:02 +0200638 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530639 return -EINVAL;
640
Sujithfb9987d2010-03-17 14:25:25 +0530641 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530642 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530643
Sujithef98c3c2010-03-29 16:07:11 +0530644 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530645 aggr.tidno = tid & 0xf;
646 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530647
Sujithfb9987d2010-03-17 14:25:25 +0530648 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
649 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800650 ath_dbg(common, ATH_DBG_CONFIG,
651 "Unable to %s TX aggregation for (%pM, %d)\n",
652 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530653 else
Joe Perches226afe62010-12-02 19:12:37 -0800654 ath_dbg(common, ATH_DBG_CONFIG,
655 "%s TX aggregation for (%pM, %d)\n",
656 (aggr.aggr_enable) ? "Starting" : "Stopping",
657 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530658
659 spin_lock_bh(&priv->tx_lock);
660 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
661 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530662
663 return ret;
664}
665
Sujithfb9987d2010-03-17 14:25:25 +0530666/*********/
667/* DEBUG */
668/*********/
669
670#ifdef CONFIG_ATH9K_HTC_DEBUGFS
671
672static int ath9k_debugfs_open(struct inode *inode, struct file *file)
673{
674 file->private_data = inode->i_private;
675 return 0;
676}
677
678static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
679 size_t count, loff_t *ppos)
680{
Joe Perches57674302010-07-12 13:50:06 -0700681 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530682 struct ath9k_htc_target_stats cmd_rsp;
683 char buf[512];
684 unsigned int len = 0;
685 int ret = 0;
686
687 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
688
689 WMI_CMD(WMI_TGT_STATS_CMDID);
690 if (ret)
691 return -EINVAL;
692
693
694 len += snprintf(buf + len, sizeof(buf) - len,
695 "%19s : %10u\n", "TX Short Retries",
696 be32_to_cpu(cmd_rsp.tx_shortretry));
697 len += snprintf(buf + len, sizeof(buf) - len,
698 "%19s : %10u\n", "TX Long Retries",
699 be32_to_cpu(cmd_rsp.tx_longretry));
700 len += snprintf(buf + len, sizeof(buf) - len,
701 "%19s : %10u\n", "TX Xretries",
702 be32_to_cpu(cmd_rsp.tx_xretries));
703 len += snprintf(buf + len, sizeof(buf) - len,
704 "%19s : %10u\n", "TX Unaggr. Xretries",
705 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
706 len += snprintf(buf + len, sizeof(buf) - len,
707 "%19s : %10u\n", "TX Xretries (HT)",
708 be32_to_cpu(cmd_rsp.ht_tx_xretries));
709 len += snprintf(buf + len, sizeof(buf) - len,
710 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
711
Dan Carpenter97460102010-07-22 10:50:28 +0200712 if (len > sizeof(buf))
713 len = sizeof(buf);
714
Sujithfb9987d2010-03-17 14:25:25 +0530715 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
716}
717
718static const struct file_operations fops_tgt_stats = {
719 .read = read_file_tgt_stats,
720 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200721 .owner = THIS_MODULE,
722 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530723};
724
725static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
726 size_t count, loff_t *ppos)
727{
Joe Perches57674302010-07-12 13:50:06 -0700728 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530729 char buf[512];
730 unsigned int len = 0;
731
732 len += snprintf(buf + len, sizeof(buf) - len,
733 "%20s : %10u\n", "Buffers queued",
734 priv->debug.tx_stats.buf_queued);
735 len += snprintf(buf + len, sizeof(buf) - len,
736 "%20s : %10u\n", "Buffers completed",
737 priv->debug.tx_stats.buf_completed);
738 len += snprintf(buf + len, sizeof(buf) - len,
739 "%20s : %10u\n", "SKBs queued",
740 priv->debug.tx_stats.skb_queued);
741 len += snprintf(buf + len, sizeof(buf) - len,
742 "%20s : %10u\n", "SKBs completed",
743 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530744 len += snprintf(buf + len, sizeof(buf) - len,
745 "%20s : %10u\n", "SKBs dropped",
746 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530747
Sujith2edb4582010-05-14 11:18:54 +0530748 len += snprintf(buf + len, sizeof(buf) - len,
749 "%20s : %10u\n", "BE queued",
750 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
751 len += snprintf(buf + len, sizeof(buf) - len,
752 "%20s : %10u\n", "BK queued",
753 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
754 len += snprintf(buf + len, sizeof(buf) - len,
755 "%20s : %10u\n", "VI queued",
756 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
757 len += snprintf(buf + len, sizeof(buf) - len,
758 "%20s : %10u\n", "VO queued",
759 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
760
Dan Carpenter97460102010-07-22 10:50:28 +0200761 if (len > sizeof(buf))
762 len = sizeof(buf);
763
Sujithfb9987d2010-03-17 14:25:25 +0530764 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
765}
766
767static const struct file_operations fops_xmit = {
768 .read = read_file_xmit,
769 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200770 .owner = THIS_MODULE,
771 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530772};
773
774static ssize_t read_file_recv(struct file *file, char __user *user_buf,
775 size_t count, loff_t *ppos)
776{
Joe Perches57674302010-07-12 13:50:06 -0700777 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530778 char buf[512];
779 unsigned int len = 0;
780
781 len += snprintf(buf + len, sizeof(buf) - len,
782 "%20s : %10u\n", "SKBs allocated",
783 priv->debug.rx_stats.skb_allocated);
784 len += snprintf(buf + len, sizeof(buf) - len,
785 "%20s : %10u\n", "SKBs completed",
786 priv->debug.rx_stats.skb_completed);
787 len += snprintf(buf + len, sizeof(buf) - len,
788 "%20s : %10u\n", "SKBs Dropped",
789 priv->debug.rx_stats.skb_dropped);
790
Dan Carpenter97460102010-07-22 10:50:28 +0200791 if (len > sizeof(buf))
792 len = sizeof(buf);
793
Sujithfb9987d2010-03-17 14:25:25 +0530794 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
795}
796
797static const struct file_operations fops_recv = {
798 .read = read_file_recv,
799 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200800 .owner = THIS_MODULE,
801 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530802};
803
Sujithe1572c52010-03-24 13:42:13 +0530804int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530805{
806 struct ath_common *common = ath9k_hw_common(ah);
807 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
808
809 if (!ath9k_debugfs_root)
810 return -ENOENT;
811
812 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
813 ath9k_debugfs_root);
814 if (!priv->debug.debugfs_phy)
815 goto err;
816
817 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
818 priv->debug.debugfs_phy,
819 priv, &fops_tgt_stats);
820 if (!priv->debug.debugfs_tgt_stats)
821 goto err;
822
823
824 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
825 priv->debug.debugfs_phy,
826 priv, &fops_xmit);
827 if (!priv->debug.debugfs_xmit)
828 goto err;
829
830 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
831 priv->debug.debugfs_phy,
832 priv, &fops_recv);
833 if (!priv->debug.debugfs_recv)
834 goto err;
835
836 return 0;
837
838err:
Sujithe1572c52010-03-24 13:42:13 +0530839 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530840 return -ENOMEM;
841}
842
Sujithe1572c52010-03-24 13:42:13 +0530843void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530844{
845 struct ath_common *common = ath9k_hw_common(ah);
846 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
847
848 debugfs_remove(priv->debug.debugfs_recv);
849 debugfs_remove(priv->debug.debugfs_xmit);
850 debugfs_remove(priv->debug.debugfs_tgt_stats);
851 debugfs_remove(priv->debug.debugfs_phy);
852}
853
Sujithe1572c52010-03-24 13:42:13 +0530854int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530855{
856 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
857 if (!ath9k_debugfs_root)
858 return -ENOENT;
859
860 return 0;
861}
862
Sujithe1572c52010-03-24 13:42:13 +0530863void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530864{
865 debugfs_remove(ath9k_debugfs_root);
866 ath9k_debugfs_root = NULL;
867}
868
869#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
870
871/*******/
872/* ANI */
873/*******/
874
Sujith Manoharan73908672010-12-28 14:28:27 +0530875void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530876{
877 struct ath_common *common = ath9k_hw_common(priv->ah);
878 unsigned long timestamp = jiffies_to_msecs(jiffies);
879
880 common->ani.longcal_timer = timestamp;
881 common->ani.shortcal_timer = timestamp;
882 common->ani.checkani_timer = timestamp;
883
884 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
885 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
886}
887
888void ath9k_ani_work(struct work_struct *work)
889{
890 struct ath9k_htc_priv *priv =
891 container_of(work, struct ath9k_htc_priv,
892 ath9k_ani_work.work);
893 struct ath_hw *ah = priv->ah;
894 struct ath_common *common = ath9k_hw_common(ah);
895 bool longcal = false;
896 bool shortcal = false;
897 bool aniflag = false;
898 unsigned int timestamp = jiffies_to_msecs(jiffies);
899 u32 cal_interval, short_cal_interval;
900
901 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
902
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530903 /* Only calibrate if awake */
904 if (ah->power_mode != ATH9K_PM_AWAKE)
905 goto set_timer;
906
Sujithfb9987d2010-03-17 14:25:25 +0530907 /* Long calibration runs independently of short calibration. */
908 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
909 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800910 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530911 common->ani.longcal_timer = timestamp;
912 }
913
914 /* Short calibration applies only while caldone is false */
915 if (!common->ani.caldone) {
916 if ((timestamp - common->ani.shortcal_timer) >=
917 short_cal_interval) {
918 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800919 ath_dbg(common, ATH_DBG_ANI,
920 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530921 common->ani.shortcal_timer = timestamp;
922 common->ani.resetcal_timer = timestamp;
923 }
924 } else {
925 if ((timestamp - common->ani.resetcal_timer) >=
926 ATH_RESTART_CALINTERVAL) {
927 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
928 if (common->ani.caldone)
929 common->ani.resetcal_timer = timestamp;
930 }
931 }
932
933 /* Verify whether we must check ANI */
934 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
935 aniflag = true;
936 common->ani.checkani_timer = timestamp;
937 }
938
939 /* Skip all processing if there's nothing to do. */
940 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530941
942 ath9k_htc_ps_wakeup(priv);
943
Sujithfb9987d2010-03-17 14:25:25 +0530944 /* Call ANI routine if necessary */
945 if (aniflag)
946 ath9k_hw_ani_monitor(ah, ah->curchan);
947
948 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200949 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530950 common->ani.caldone =
951 ath9k_hw_calibrate(ah, ah->curchan,
952 common->rx_chainmask,
953 longcal);
954
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530955 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530956 }
957
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530958set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530959 /*
960 * Set timer interval based on previous results.
961 * The interval must be the shortest necessary to satisfy ANI,
962 * short calibration and long calibration.
963 */
964 cal_interval = ATH_LONG_CALINTERVAL;
965 if (priv->ah->config.enable_ani)
966 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
967 if (!common->ani.caldone)
968 cal_interval = min(cal_interval, (u32)short_cal_interval);
969
970 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
971 msecs_to_jiffies(cal_interval));
972}
973
Sujithfb9987d2010-03-17 14:25:25 +0530974/**********************/
975/* mac80211 Callbacks */
976/**********************/
977
978static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
979{
980 struct ieee80211_hdr *hdr;
981 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +0530982 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +0530983
984 hdr = (struct ieee80211_hdr *) skb->data;
985
986 /* Add the padding after the header if this is not already done */
987 padpos = ath9k_cmn_padpos(hdr->frame_control);
988 padsize = padpos & 3;
989 if (padsize && skb->len > padpos) {
990 if (skb_headroom(skb) < padsize)
991 return -1;
992 skb_push(skb, padsize);
993 memmove(skb->data, skb->data + padsize, padpos);
994 }
995
Sujith7757dfe2010-03-29 16:07:17 +0530996 ret = ath9k_htc_tx_start(priv, skb);
997 if (ret != 0) {
998 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -0800999 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1000 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +05301001 ieee80211_stop_queues(hw);
1002 spin_lock_bh(&priv->tx_lock);
1003 priv->tx_queues_stop = true;
1004 spin_unlock_bh(&priv->tx_lock);
1005 } else {
Joe Perches226afe62010-12-02 19:12:37 -08001006 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1007 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +05301008 }
Sujithfb9987d2010-03-17 14:25:25 +05301009 goto fail_tx;
1010 }
1011
1012 return 0;
1013
1014fail_tx:
1015 dev_kfree_skb_any(skb);
1016 return 0;
1017}
1018
Sujith881ac6a2010-06-01 15:14:11 +05301019static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301020{
1021 struct ath9k_htc_priv *priv = hw->priv;
1022 struct ath_hw *ah = priv->ah;
1023 struct ath_common *common = ath9k_hw_common(ah);
1024 struct ieee80211_channel *curchan = hw->conf.channel;
1025 struct ath9k_channel *init_channel;
1026 int ret = 0;
1027 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301028 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301029 u8 cmd_rsp;
1030
Sujith881ac6a2010-06-01 15:14:11 +05301031 mutex_lock(&priv->mutex);
1032
Joe Perches226afe62010-12-02 19:12:37 -08001033 ath_dbg(common, ATH_DBG_CONFIG,
1034 "Starting driver with initial channel: %d MHz\n",
1035 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301036
Sujith21d51302010-06-01 15:14:18 +05301037 /* Ensure that HW is awake before flushing RX */
1038 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1039 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1040
Sujithfb9987d2010-03-17 14:25:25 +05301041 /* setup initial channel */
1042 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1043
Sujithfb9987d2010-03-17 14:25:25 +05301044 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001045 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301046 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001047 ath_err(common,
1048 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1049 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301050 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301051 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301052 }
1053
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301054 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1055 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +05301056
1057 mode = ath9k_htc_get_curmode(priv, init_channel);
1058 htc_mode = cpu_to_be16(mode);
1059 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301060 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301061 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301062
1063 ath9k_host_rx_init(priv);
1064
Sujith Manoharan1057b752011-02-21 07:48:09 +05301065 ret = ath9k_htc_update_cap_target(priv);
1066 if (ret)
1067 ath_dbg(common, ATH_DBG_CONFIG,
1068 "Failed to update capability in target\n");
1069
Sujithfb9987d2010-03-17 14:25:25 +05301070 priv->op_flags &= ~OP_INVALID;
1071 htc_start(priv->htc);
1072
Sujith7757dfe2010-03-29 16:07:17 +05301073 spin_lock_bh(&priv->tx_lock);
1074 priv->tx_queues_stop = false;
1075 spin_unlock_bh(&priv->tx_lock);
1076
1077 ieee80211_wake_queues(hw);
1078
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301079 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1080 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1081 AR_STOMP_LOW_WLAN_WGHT);
1082 ath9k_hw_btcoex_enable(ah);
1083 ath_htc_resume_btcoex_work(priv);
1084 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301085 mutex_unlock(&priv->mutex);
1086
1087 return ret;
1088}
1089
Sujith881ac6a2010-06-01 15:14:11 +05301090static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301091{
1092 struct ath9k_htc_priv *priv = hw->priv;
1093 struct ath_hw *ah = priv->ah;
1094 struct ath_common *common = ath9k_hw_common(ah);
1095 int ret = 0;
1096 u8 cmd_rsp;
1097
Sujith881ac6a2010-06-01 15:14:11 +05301098 mutex_lock(&priv->mutex);
1099
Sujithfb9987d2010-03-17 14:25:25 +05301100 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001101 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301102 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301103 return;
1104 }
1105
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301106 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301107 htc_stop(priv->htc);
1108 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1109 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1110 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001111
1112 tasklet_kill(&priv->swba_tasklet);
1113 tasklet_kill(&priv->rx_tasklet);
1114 tasklet_kill(&priv->tx_tasklet);
1115
Sujithfb9987d2010-03-17 14:25:25 +05301116 skb_queue_purge(&priv->tx_queue);
1117
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001118 mutex_unlock(&priv->mutex);
1119
1120 /* Cancel all the running timers/work .. */
1121 cancel_work_sync(&priv->fatal_work);
1122 cancel_work_sync(&priv->ps_work);
1123 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Rajkumar Manoharan45655ba2011-01-31 23:47:42 +05301124 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001125 ath9k_led_stop_brightness(priv);
1126
1127 mutex_lock(&priv->mutex);
1128
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301129 if (ah->btcoex_hw.enabled) {
1130 ath9k_hw_btcoex_disable(ah);
1131 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1132 ath_htc_cancel_btcoex_work(priv);
1133 }
1134
Sujith Manoharana97b4782011-02-21 07:48:00 +05301135 /* Remove a monitor interface if it's present. */
1136 if (priv->ah->is_monitoring)
1137 ath9k_htc_remove_monitor_interface(priv);
1138
Sujithe9201f02010-06-01 15:14:17 +05301139 ath9k_hw_phy_disable(ah);
1140 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301141 ath9k_htc_ps_restore(priv);
1142 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1143
Sujithfb9987d2010-03-17 14:25:25 +05301144 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301145
Joe Perches226afe62010-12-02 19:12:37 -08001146 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301147 mutex_unlock(&priv->mutex);
1148}
1149
Sujithfb9987d2010-03-17 14:25:25 +05301150static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1151 struct ieee80211_vif *vif)
1152{
1153 struct ath9k_htc_priv *priv = hw->priv;
1154 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1155 struct ath_common *common = ath9k_hw_common(priv->ah);
1156 struct ath9k_htc_target_vif hvif;
1157 int ret = 0;
1158 u8 cmd_rsp;
1159
1160 mutex_lock(&priv->mutex);
1161
Sujith Manoharana97b4782011-02-21 07:48:00 +05301162 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujithfb9987d2010-03-17 14:25:25 +05301163 ret = -ENOBUFS;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301164 mutex_unlock(&priv->mutex);
1165 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301166 }
1167
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301168 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301169 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1170 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1171
1172 switch (vif->type) {
1173 case NL80211_IFTYPE_STATION:
1174 hvif.opmode = cpu_to_be32(HTC_M_STA);
1175 break;
1176 case NL80211_IFTYPE_ADHOC:
1177 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1178 break;
1179 default:
Joe Perches38002762010-12-02 19:12:36 -08001180 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301181 "Interface type %d not yet supported\n", vif->type);
1182 ret = -EOPNOTSUPP;
1183 goto out;
1184 }
1185
Sujithfb9987d2010-03-17 14:25:25 +05301186 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301187 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301188 hvif.rtsthreshold = cpu_to_be16(2304);
1189 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1190 if (ret)
1191 goto out;
1192
Sujithfb9987d2010-03-17 14:25:25 +05301193 /*
1194 * We need a node in target to tx mgmt frames
1195 * before association.
1196 */
1197 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301198 if (ret) {
1199 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301200 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301201 }
Sujithfb9987d2010-03-17 14:25:25 +05301202
Sujith Manoharana97b4782011-02-21 07:48:00 +05301203 priv->ah->opmode = vif->type;
1204 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301205 priv->nvifs++;
Sujithfb9987d2010-03-17 14:25:25 +05301206 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301207
1208 ath_dbg(common, ATH_DBG_CONFIG,
1209 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1210
Sujithfb9987d2010-03-17 14:25:25 +05301211out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301212 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301213 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301214
Sujithfb9987d2010-03-17 14:25:25 +05301215 return ret;
1216}
1217
1218static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1219 struct ieee80211_vif *vif)
1220{
1221 struct ath9k_htc_priv *priv = hw->priv;
1222 struct ath_common *common = ath9k_hw_common(priv->ah);
1223 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1224 struct ath9k_htc_target_vif hvif;
1225 int ret = 0;
1226 u8 cmd_rsp;
1227
Sujithfb9987d2010-03-17 14:25:25 +05301228 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301229 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301230
1231 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1232 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1233 hvif.index = avp->index;
1234 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1235 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301236 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301237
1238 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301239 priv->vif = NULL;
1240
Sujith Manoharana97b4782011-02-21 07:48:00 +05301241 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1242
Sujithcb551df2010-06-01 15:14:12 +05301243 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301244 mutex_unlock(&priv->mutex);
1245}
1246
1247static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1248{
1249 struct ath9k_htc_priv *priv = hw->priv;
1250 struct ath_common *common = ath9k_hw_common(priv->ah);
1251 struct ieee80211_conf *conf = &hw->conf;
1252
1253 mutex_lock(&priv->mutex);
1254
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301255 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1256 bool enable_radio = false;
1257 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1258
Sujith23367762010-06-01 15:14:16 +05301259 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301260 if (!idle && priv->ps_idle)
1261 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301262 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301263 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301264
1265 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001266 ath_dbg(common, ATH_DBG_CONFIG,
1267 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301268 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1269 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301270 }
1271 }
1272
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301273 /*
1274 * Monitor interface should be added before
1275 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1276 */
1277 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301278 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1279 !priv->ah->is_monitoring)
1280 ath9k_htc_add_monitor_interface(priv);
1281 else if (priv->ah->is_monitoring)
1282 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301283 }
1284
Sujithfb9987d2010-03-17 14:25:25 +05301285 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1286 struct ieee80211_channel *curchan = hw->conf.channel;
1287 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301288
Joe Perches226afe62010-12-02 19:12:37 -08001289 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1290 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301291
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001292 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1293 hw->conf.channel,
1294 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301295
1296 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001297 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301298 mutex_unlock(&priv->mutex);
1299 return -EINVAL;
1300 }
1301
1302 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301303
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301304 if (changed & IEEE80211_CONF_CHANGE_PS) {
1305 if (conf->flags & IEEE80211_CONF_PS) {
1306 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1307 priv->ps_enabled = true;
1308 } else {
1309 priv->ps_enabled = false;
1310 cancel_work_sync(&priv->ps_work);
1311 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1312 }
1313 }
Sujithfb9987d2010-03-17 14:25:25 +05301314
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301315 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1316 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301317 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1318 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301319 }
1320
Sujith23367762010-06-01 15:14:16 +05301321 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1322 mutex_lock(&priv->htc_pm_lock);
1323 if (!priv->ps_idle) {
1324 mutex_unlock(&priv->htc_pm_lock);
1325 goto out;
1326 }
1327 mutex_unlock(&priv->htc_pm_lock);
1328
Joe Perches226afe62010-12-02 19:12:37 -08001329 ath_dbg(common, ATH_DBG_CONFIG,
1330 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301331 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301332 }
1333
Sujith23367762010-06-01 15:14:16 +05301334out:
Sujithfb9987d2010-03-17 14:25:25 +05301335 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301336 return 0;
1337}
1338
1339#define SUPPORTED_FILTERS \
1340 (FIF_PROMISC_IN_BSS | \
1341 FIF_ALLMULTI | \
1342 FIF_CONTROL | \
1343 FIF_PSPOLL | \
1344 FIF_OTHER_BSS | \
1345 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301346 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301347 FIF_FCSFAIL)
1348
1349static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1350 unsigned int changed_flags,
1351 unsigned int *total_flags,
1352 u64 multicast)
1353{
1354 struct ath9k_htc_priv *priv = hw->priv;
1355 u32 rfilt;
1356
1357 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301358 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301359
Sujithfb9987d2010-03-17 14:25:25 +05301360 changed_flags &= SUPPORTED_FILTERS;
1361 *total_flags &= SUPPORTED_FILTERS;
1362
1363 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301364 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301365 ath9k_hw_setrxfilter(priv->ah, rfilt);
1366
Joe Perches226afe62010-12-02 19:12:37 -08001367 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1368 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301369
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301370 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301371 mutex_unlock(&priv->mutex);
1372}
1373
Sujithabd984e2010-05-18 15:26:04 +05301374static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1375 struct ieee80211_vif *vif,
1376 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301377{
1378 struct ath9k_htc_priv *priv = hw->priv;
1379 int ret;
1380
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301381 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301382 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301383 ret = ath9k_htc_add_station(priv, vif, sta);
1384 if (!ret)
1385 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301386 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301387 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301388
1389 return ret;
1390}
1391
1392static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1393 struct ieee80211_vif *vif,
1394 struct ieee80211_sta *sta)
1395{
1396 struct ath9k_htc_priv *priv = hw->priv;
1397 int ret;
1398
1399 mutex_lock(&priv->mutex);
1400 ath9k_htc_ps_wakeup(priv);
1401 ret = ath9k_htc_remove_station(priv, vif, sta);
1402 ath9k_htc_ps_restore(priv);
1403 mutex_unlock(&priv->mutex);
1404
1405 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301406}
1407
1408static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1409 const struct ieee80211_tx_queue_params *params)
1410{
1411 struct ath9k_htc_priv *priv = hw->priv;
1412 struct ath_common *common = ath9k_hw_common(priv->ah);
1413 struct ath9k_tx_queue_info qi;
1414 int ret = 0, qnum;
1415
1416 if (queue >= WME_NUM_AC)
1417 return 0;
1418
1419 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301420 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301421
1422 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1423
1424 qi.tqi_aifs = params->aifs;
1425 qi.tqi_cwmin = params->cw_min;
1426 qi.tqi_cwmax = params->cw_max;
1427 qi.tqi_burstTime = params->txop;
1428
1429 qnum = get_hw_qnum(queue, priv->hwq_map);
1430
Joe Perches226afe62010-12-02 19:12:37 -08001431 ath_dbg(common, ATH_DBG_CONFIG,
1432 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1433 queue, qnum, params->aifs, params->cw_min,
1434 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301435
Sujithe1572c52010-03-24 13:42:13 +05301436 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301437 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001438 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301439 goto out;
1440 }
Sujithfb9987d2010-03-17 14:25:25 +05301441
Sujith764580f2010-06-01 15:14:19 +05301442 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001443 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301444 ath9k_htc_beaconq_config(priv);
1445out:
Sujithcb551df2010-06-01 15:14:12 +05301446 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301447 mutex_unlock(&priv->mutex);
1448
1449 return ret;
1450}
1451
1452static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1453 enum set_key_cmd cmd,
1454 struct ieee80211_vif *vif,
1455 struct ieee80211_sta *sta,
1456 struct ieee80211_key_conf *key)
1457{
1458 struct ath9k_htc_priv *priv = hw->priv;
1459 struct ath_common *common = ath9k_hw_common(priv->ah);
1460 int ret = 0;
1461
Sujithe1572c52010-03-24 13:42:13 +05301462 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301463 return -ENOSPC;
1464
1465 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001466 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301467 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301468
1469 switch (cmd) {
1470 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001471 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301472 if (ret >= 0) {
1473 key->hw_key_idx = ret;
1474 /* push IV and Michael MIC generation to stack */
1475 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001476 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301477 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001478 if (priv->ah->sw_mgmt_crypto &&
1479 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301480 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1481 ret = 0;
1482 }
1483 break;
1484 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001485 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301486 break;
1487 default:
1488 ret = -EINVAL;
1489 }
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 return ret;
1495}
1496
1497static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1498 struct ieee80211_vif *vif,
1499 struct ieee80211_bss_conf *bss_conf,
1500 u32 changed)
1501{
1502 struct ath9k_htc_priv *priv = hw->priv;
1503 struct ath_hw *ah = priv->ah;
1504 struct ath_common *common = ath9k_hw_common(ah);
1505
1506 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301507 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301508
1509 if (changed & BSS_CHANGED_ASSOC) {
1510 common->curaid = bss_conf->assoc ?
1511 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001512 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301513 bss_conf->assoc);
1514
Sujith Manoharan7c277342011-02-21 07:48:39 +05301515 if (bss_conf->assoc)
Sujithfb9987d2010-03-17 14:25:25 +05301516 ath_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301517 else
Sujithfb9987d2010-03-17 14:25:25 +05301518 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301519 }
1520
1521 if (changed & BSS_CHANGED_BSSID) {
1522 /* Set BSSID */
1523 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1524 ath9k_hw_write_associd(ah);
1525
Joe Perches226afe62010-12-02 19:12:37 -08001526 ath_dbg(common, ATH_DBG_CONFIG,
1527 "BSSID: %pM aid: 0x%x\n",
1528 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301529 }
1530
1531 if ((changed & BSS_CHANGED_BEACON_INT) ||
1532 (changed & BSS_CHANGED_BEACON) ||
1533 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1534 bss_conf->enable_beacon)) {
1535 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301536 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301537 }
1538
Sujithfb9987d2010-03-17 14:25:25 +05301539 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1540 !bss_conf->enable_beacon) {
1541 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301542 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301543 }
1544
Sujithfb9987d2010-03-17 14:25:25 +05301545 if (changed & BSS_CHANGED_ERP_SLOT) {
1546 if (bss_conf->use_short_slot)
1547 ah->slottime = 9;
1548 else
1549 ah->slottime = 20;
1550
1551 ath9k_hw_init_global_settings(ah);
1552 }
1553
Sujith2c76ef82010-05-17 12:01:18 +05301554 if (changed & BSS_CHANGED_HT)
1555 ath9k_htc_update_rate(priv, vif, bss_conf);
1556
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301557 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301558 mutex_unlock(&priv->mutex);
1559}
1560
1561static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1562{
1563 struct ath9k_htc_priv *priv = hw->priv;
1564 u64 tsf;
1565
1566 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301567 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301568 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301569 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301570 mutex_unlock(&priv->mutex);
1571
1572 return tsf;
1573}
1574
1575static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1576{
1577 struct ath9k_htc_priv *priv = hw->priv;
1578
1579 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301580 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301581 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301582 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301583 mutex_unlock(&priv->mutex);
1584}
1585
1586static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1587{
1588 struct ath9k_htc_priv *priv = hw->priv;
1589
1590 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301591 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301592 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301593 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301594 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301595}
1596
1597static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1598 struct ieee80211_vif *vif,
1599 enum ieee80211_ampdu_mlme_action action,
1600 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001601 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301602{
1603 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301604 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301605 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301606
1607 switch (action) {
1608 case IEEE80211_AMPDU_RX_START:
1609 break;
1610 case IEEE80211_AMPDU_RX_STOP:
1611 break;
1612 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301613 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1614 if (!ret)
1615 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1616 break;
Sujithfb9987d2010-03-17 14:25:25 +05301617 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301618 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1619 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301620 break;
1621 case IEEE80211_AMPDU_TX_OPERATIONAL:
1622 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301623 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301624 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301625 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301626 break;
1627 default:
Joe Perches38002762010-12-02 19:12:36 -08001628 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301629 }
1630
Sujithd7ca2132010-06-15 10:24:37 +05301631 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301632}
1633
1634static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1635{
1636 struct ath9k_htc_priv *priv = hw->priv;
1637
1638 mutex_lock(&priv->mutex);
1639 spin_lock_bh(&priv->beacon_lock);
1640 priv->op_flags |= OP_SCANNING;
1641 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301642 cancel_work_sync(&priv->ps_work);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301643 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301644 mutex_unlock(&priv->mutex);
1645}
1646
1647static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1648{
1649 struct ath9k_htc_priv *priv = hw->priv;
1650
1651 mutex_lock(&priv->mutex);
1652 spin_lock_bh(&priv->beacon_lock);
1653 priv->op_flags &= ~OP_SCANNING;
1654 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301655 ath9k_htc_ps_wakeup(priv);
1656 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301657 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301658 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301659}
1660
1661static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1662{
1663 return 0;
1664}
1665
1666static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1667 u8 coverage_class)
1668{
1669 struct ath9k_htc_priv *priv = hw->priv;
1670
1671 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301672 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301673 priv->ah->coverage_class = coverage_class;
1674 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301675 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301676 mutex_unlock(&priv->mutex);
1677}
1678
1679struct ieee80211_ops ath9k_htc_ops = {
1680 .tx = ath9k_htc_tx,
1681 .start = ath9k_htc_start,
1682 .stop = ath9k_htc_stop,
1683 .add_interface = ath9k_htc_add_interface,
1684 .remove_interface = ath9k_htc_remove_interface,
1685 .config = ath9k_htc_config,
1686 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301687 .sta_add = ath9k_htc_sta_add,
1688 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301689 .conf_tx = ath9k_htc_conf_tx,
1690 .bss_info_changed = ath9k_htc_bss_info_changed,
1691 .set_key = ath9k_htc_set_key,
1692 .get_tsf = ath9k_htc_get_tsf,
1693 .set_tsf = ath9k_htc_set_tsf,
1694 .reset_tsf = ath9k_htc_reset_tsf,
1695 .ampdu_action = ath9k_htc_ampdu_action,
1696 .sw_scan_start = ath9k_htc_sw_scan_start,
1697 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1698 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1699 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1700 .set_coverage_class = ath9k_htc_set_coverage_class,
1701};