blob: 7367d6c1c6492647fa7951b8c1e18194c81117d9 [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
Sujith Manoharana5fae372011-02-21 07:49:53 +0530113 if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
114 priv->reconfig_beacon = true;
115
Sujith Manoharan7c277342011-02-21 07:48:39 +0530116 if (bss_conf->assoc) {
117 priv->rearm_ani = true;
118 priv->reconfig_beacon = true;
119 }
120}
121
122static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
123{
124 priv->rearm_ani = false;
125 priv->reconfig_beacon = false;
126
127 ieee80211_iterate_active_interfaces_atomic(priv->hw,
128 ath9k_htc_vif_iter, priv);
129 if (priv->rearm_ani)
Sujith Manoharana2362542011-02-21 07:49:38 +0530130 ath9k_htc_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530131
132 if (priv->reconfig_beacon) {
133 ath9k_htc_ps_wakeup(priv);
134 ath9k_htc_beacon_reconfig(priv);
135 ath9k_htc_ps_restore(priv);
136 }
137}
138
Sujith Manoharan585895c2011-02-21 07:48:46 +0530139static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
140{
141 struct ath9k_vif_iter_data *iter_data = data;
142 int i;
143
144 for (i = 0; i < ETH_ALEN; i++)
145 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
146}
147
148static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
149 struct ieee80211_vif *vif)
150{
151 struct ath_common *common = ath9k_hw_common(priv->ah);
152 struct ath9k_vif_iter_data iter_data;
153
154 /*
155 * Use the hardware MAC address as reference, the hardware uses it
156 * together with the BSSID mask when matching addresses.
157 */
158 iter_data.hw_macaddr = common->macaddr;
159 memset(&iter_data.mask, 0xff, ETH_ALEN);
160
161 if (vif)
162 ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
163
164 /* Get list of all active MAC addresses */
165 ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
166 &iter_data);
167
168 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
169 ath_hw_setbssidmask(common);
170}
171
Sujith Manoharanffbe7c82011-02-21 07:49:31 +0530172static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
173{
174 if (priv->num_ibss_vif)
175 priv->ah->opmode = NL80211_IFTYPE_ADHOC;
176 else if (priv->num_ap_vif)
177 priv->ah->opmode = NL80211_IFTYPE_AP;
178 else
179 priv->ah->opmode = NL80211_IFTYPE_STATION;
180
181 ath9k_hw_setopmode(priv->ah);
182}
183
Sujith Manoharan73908672010-12-28 14:28:27 +0530184void ath9k_htc_reset(struct ath9k_htc_priv *priv)
185{
186 struct ath_hw *ah = priv->ah;
187 struct ath_common *common = ath9k_hw_common(ah);
188 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530189 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530190 enum htc_phymode mode;
191 __be16 htc_mode;
192 u8 cmd_rsp;
193 int ret;
194
195 mutex_lock(&priv->mutex);
196 ath9k_htc_ps_wakeup(priv);
197
Sujith Manoharana2362542011-02-21 07:49:38 +0530198 ath9k_htc_stop_ani(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530199 ieee80211_stop_queues(priv->hw);
200 htc_stop(priv->htc);
201 WMI_CMD(WMI_DISABLE_INTR_CMDID);
202 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
203 WMI_CMD(WMI_STOP_RECV_CMDID);
204
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530205 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530206 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
207 if (ret) {
208 ath_err(common,
209 "Unable to reset device (%u Mhz) reset status %d\n",
210 channel->center_freq, ret);
211 }
212
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530213 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
214 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530215
216 WMI_CMD(WMI_START_RECV_CMDID);
217 ath9k_host_rx_init(priv);
218
219 mode = ath9k_htc_get_curmode(priv, ah->curchan);
220 htc_mode = cpu_to_be16(mode);
221 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
222
223 WMI_CMD(WMI_ENABLE_INTR_CMDID);
224 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530225 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530226 ieee80211_wake_queues(priv->hw);
227
228 ath9k_htc_ps_restore(priv);
229 mutex_unlock(&priv->mutex);
230}
231
Sujithfb9987d2010-03-17 14:25:25 +0530232static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
233 struct ieee80211_hw *hw,
234 struct ath9k_channel *hchan)
235{
236 struct ath_hw *ah = priv->ah;
237 struct ath_common *common = ath9k_hw_common(ah);
238 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530239 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530240 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530241 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530242 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530243 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530244 u8 cmd_rsp;
245 int ret;
246
247 if (priv->op_flags & OP_INVALID)
248 return -EIO;
249
Sujith Manoharan039a0722010-12-28 14:28:37 +0530250 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530251
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530252 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530253 htc_stop(priv->htc);
254 WMI_CMD(WMI_DISABLE_INTR_CMDID);
255 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
256 WMI_CMD(WMI_STOP_RECV_CMDID);
257
Joe Perches226afe62010-12-02 19:12:37 -0800258 ath_dbg(common, ATH_DBG_CONFIG,
259 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
260 priv->ah->curchan->channel,
261 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
262 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530263
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530264 if (!fastcc)
265 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200266 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530267 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800268 ath_err(common,
269 "Unable to reset channel (%u Mhz) reset status %d\n",
270 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530271 goto err;
272 }
273
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530274 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
275 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530276
277 WMI_CMD(WMI_START_RECV_CMDID);
278 if (ret)
279 goto err;
280
281 ath9k_host_rx_init(priv);
282
283 mode = ath9k_htc_get_curmode(priv, hchan);
284 htc_mode = cpu_to_be16(mode);
285 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
286 if (ret)
287 goto err;
288
289 WMI_CMD(WMI_ENABLE_INTR_CMDID);
290 if (ret)
291 goto err;
292
293 htc_start(priv->htc);
Sujith Manoharana5fae372011-02-21 07:49:53 +0530294
295 if (!(priv->op_flags & OP_SCANNING) &&
296 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
297 ath9k_htc_vif_reconfig(priv);
298
Sujithfb9987d2010-03-17 14:25:25 +0530299err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530300 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530301 return ret;
302}
303
Sujith Manoharana97b4782011-02-21 07:48:00 +0530304/*
305 * Monitor mode handling is a tad complicated because the firmware requires
306 * an interface to be created exclusively, while mac80211 doesn't associate
307 * an interface with the mode.
308 *
309 * So, for now, only one monitor interface can be configured.
310 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530311static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530312{
313 struct ath_common *common = ath9k_hw_common(priv->ah);
314 struct ath9k_htc_target_vif hvif;
315 int ret = 0;
316 u8 cmd_rsp;
317
Sujith Manoharancc721282011-01-03 21:22:18 +0530318 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
319 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530320 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530321 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
322 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530323 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530324}
325
326static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
327{
328 struct ath_common *common = ath9k_hw_common(priv->ah);
329 struct ath9k_htc_target_vif hvif;
330 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530331 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530332 u8 cmd_rsp;
333
Sujith Manoharana97b4782011-02-21 07:48:00 +0530334 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
335 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
336 ret = -ENOBUFS;
337 goto err_vif;
338 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530339
Sujith Manoharana97b4782011-02-21 07:48:00 +0530340 sta_idx = ffz(priv->sta_slot);
341 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
342 ret = -ENOBUFS;
343 goto err_vif;
344 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530345
346 /*
347 * Add an interface.
348 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530349 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
350 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
351
352 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530353 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530354
355 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
356 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530357 goto err_vif;
358
359 /*
360 * Assign the monitor interface index as a special case here.
361 * This is needed when the interface is brought down.
362 */
363 priv->mon_vif_idx = hvif.index;
364 priv->vif_slot |= (1 << hvif.index);
365
366 /*
367 * Set the hardware mode to monitor only if there are no
368 * other interfaces.
369 */
370 if (!priv->nvifs)
371 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530372
373 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530374
375 /*
376 * Associate a station with the interface for packet injection.
377 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530378 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
379
380 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
381
382 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530383 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530384 tsta.vif_index = hvif.index;
385 tsta.maxampdu = 0xffff;
386
387 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
388 if (ret) {
389 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530390 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530391 }
392
Sujith Manoharana97b4782011-02-21 07:48:00 +0530393 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530394 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530395 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530396 priv->ah->is_monitoring = true;
397
Sujith Manoharana97b4782011-02-21 07:48:00 +0530398 ath_dbg(common, ATH_DBG_CONFIG,
399 "Attached a monitor interface at idx: %d, sta idx: %d\n",
400 priv->mon_vif_idx, sta_idx);
401
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530402 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530403
Sujith Manoharana97b4782011-02-21 07:48:00 +0530404err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530405 /*
406 * Remove the interface from the target.
407 */
408 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530409err_vif:
410 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
411
Sujith Manoharancc721282011-01-03 21:22:18 +0530412 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530413}
414
415static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
416{
417 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530418 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530419 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530420
Sujith Manoharancc721282011-01-03 21:22:18 +0530421 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530422
Sujith Manoharana97b4782011-02-21 07:48:00 +0530423 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530424
425 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
426 if (ret) {
427 ath_err(common, "Unable to remove station entry for monitor mode\n");
428 return ret;
429 }
430
Sujith Manoharana97b4782011-02-21 07:48:00 +0530431 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530432 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530433 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530434
Sujith Manoharana97b4782011-02-21 07:48:00 +0530435 ath_dbg(common, ATH_DBG_CONFIG,
436 "Removed a monitor interface at idx: %d, sta idx: %d\n",
437 priv->mon_vif_idx, sta_idx);
438
Sujith Manoharancc721282011-01-03 21:22:18 +0530439 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530440}
441
Sujithfb9987d2010-03-17 14:25:25 +0530442static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
443 struct ieee80211_vif *vif,
444 struct ieee80211_sta *sta)
445{
446 struct ath_common *common = ath9k_hw_common(priv->ah);
447 struct ath9k_htc_target_sta tsta;
448 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
449 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530450 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530451 u8 cmd_rsp;
452
453 if (priv->nstations >= ATH9K_HTC_MAX_STA)
454 return -ENOBUFS;
455
Sujith Manoharana97b4782011-02-21 07:48:00 +0530456 sta_idx = ffz(priv->sta_slot);
457 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
458 return -ENOBUFS;
459
Sujithfb9987d2010-03-17 14:25:25 +0530460 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
461
462 if (sta) {
463 ista = (struct ath9k_htc_sta *) sta->drv_priv;
464 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
465 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
466 tsta.associd = common->curaid;
467 tsta.is_vif_sta = 0;
468 tsta.valid = true;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530469 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530470 } else {
471 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
472 tsta.is_vif_sta = 1;
473 }
474
Sujith Manoharana97b4782011-02-21 07:48:00 +0530475 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530476 tsta.vif_index = avp->index;
477 tsta.maxampdu = 0xffff;
478 if (sta && sta->ht_cap.ht_supported)
479 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
480
481 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
482 if (ret) {
483 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800484 ath_err(common,
485 "Unable to add station entry for: %pM\n",
486 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530487 return ret;
488 }
489
Sujith Manoharana97b4782011-02-21 07:48:00 +0530490 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800491 ath_dbg(common, ATH_DBG_CONFIG,
492 "Added a station entry for: %pM (idx: %d)\n",
493 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530494 } else {
495 ath_dbg(common, ATH_DBG_CONFIG,
496 "Added a station entry for VIF %d (idx: %d)\n",
497 avp->index, tsta.sta_index);
498 }
Sujithfb9987d2010-03-17 14:25:25 +0530499
Sujith Manoharana97b4782011-02-21 07:48:00 +0530500 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530501 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530502 if (!sta)
503 priv->vif_sta_pos[avp->index] = sta_idx;
504
Sujithfb9987d2010-03-17 14:25:25 +0530505 return 0;
506}
507
508static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
509 struct ieee80211_vif *vif,
510 struct ieee80211_sta *sta)
511{
512 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530513 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530514 struct ath9k_htc_sta *ista;
515 int ret;
516 u8 cmd_rsp, sta_idx;
517
518 if (sta) {
519 ista = (struct ath9k_htc_sta *) sta->drv_priv;
520 sta_idx = ista->index;
521 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530522 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530523 }
524
525 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
526 if (ret) {
527 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800528 ath_err(common,
529 "Unable to remove station entry for: %pM\n",
530 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530531 return ret;
532 }
533
Sujith Manoharana97b4782011-02-21 07:48:00 +0530534 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800535 ath_dbg(common, ATH_DBG_CONFIG,
536 "Removed a station entry for: %pM (idx: %d)\n",
537 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530538 } else {
539 ath_dbg(common, ATH_DBG_CONFIG,
540 "Removed a station entry for VIF %d (idx: %d)\n",
541 avp->index, sta_idx);
542 }
Sujithfb9987d2010-03-17 14:25:25 +0530543
Sujith Manoharana97b4782011-02-21 07:48:00 +0530544 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530545 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530546
Sujithfb9987d2010-03-17 14:25:25 +0530547 return 0;
548}
549
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530550int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530551{
552 struct ath9k_htc_cap_target tcap;
553 int ret;
554 u8 cmd_rsp;
555
556 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
557
558 /* FIXME: Values are hardcoded */
559 tcap.flags = 0x240c40;
560 tcap.flags_ext = 0x80601000;
561 tcap.ampdu_limit = 0xffff0000;
562 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530563 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530564 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530565 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530566
567 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
568
569 return ret;
570}
571
Sujith0d425a72010-05-17 12:01:16 +0530572static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
573 struct ieee80211_sta *sta,
574 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530575{
Sujithfb9987d2010-03-17 14:25:25 +0530576 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
577 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530578 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530579 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530580
Sujithea46e642010-06-02 15:53:50 +0530581 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530582
583 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
584 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530585 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530586 = (sband->bitrates[i].bitrate * 2) / 10;
587 j++;
588 }
589 }
Sujith0d425a72010-05-17 12:01:16 +0530590 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530591
592 if (sta->ht_cap.ht_supported) {
593 for (i = 0, j = 0; i < 77; i++) {
594 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530595 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530596 if (j == ATH_HTC_RATE_MAX)
597 break;
598 }
Sujith0d425a72010-05-17 12:01:16 +0530599 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530600
601 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200602 if (sta->ht_cap.mcs.rx_mask[1])
603 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530604 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
605 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530606 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530607 if (conf_is_ht40(&priv->hw->conf) &&
608 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530609 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530610 else if (conf_is_ht20(&priv->hw->conf) &&
611 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
612 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530613 }
614
Sujith0d425a72010-05-17 12:01:16 +0530615 trate->sta_index = ista->index;
616 trate->isnew = 1;
617 trate->capflags = cpu_to_be32(caps);
618}
Sujithfb9987d2010-03-17 14:25:25 +0530619
Sujith0d425a72010-05-17 12:01:16 +0530620static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
621 struct ath9k_htc_target_rate *trate)
622{
623 struct ath_common *common = ath9k_hw_common(priv->ah);
624 int ret;
625 u8 cmd_rsp;
626
627 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530628 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800629 ath_err(common,
630 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530631 }
632
Sujith0d425a72010-05-17 12:01:16 +0530633 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530634}
635
Sujith0d425a72010-05-17 12:01:16 +0530636static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
637 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530638{
Sujithfb9987d2010-03-17 14:25:25 +0530639 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530640 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530641 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530642
Sujith0d425a72010-05-17 12:01:16 +0530643 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
644 ath9k_htc_setup_rate(priv, sta, &trate);
645 ret = ath9k_htc_send_rate_cmd(priv, &trate);
646 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800647 ath_dbg(common, ATH_DBG_CONFIG,
648 "Updated target sta: %pM, rate caps: 0x%X\n",
649 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530650}
651
Sujith2c76ef82010-05-17 12:01:18 +0530652static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
653 struct ieee80211_vif *vif,
654 struct ieee80211_bss_conf *bss_conf)
655{
656 struct ath_common *common = ath9k_hw_common(priv->ah);
657 struct ath9k_htc_target_rate trate;
658 struct ieee80211_sta *sta;
659 int ret;
660
661 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
662
663 rcu_read_lock();
664 sta = ieee80211_find_sta(vif, bss_conf->bssid);
665 if (!sta) {
666 rcu_read_unlock();
667 return;
668 }
669 ath9k_htc_setup_rate(priv, sta, &trate);
670 rcu_read_unlock();
671
672 ret = ath9k_htc_send_rate_cmd(priv, &trate);
673 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800674 ath_dbg(common, ATH_DBG_CONFIG,
675 "Updated target sta: %pM, rate caps: 0x%X\n",
676 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530677}
678
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400679static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
680 struct ieee80211_vif *vif,
681 struct ieee80211_sta *sta,
682 enum ieee80211_ampdu_mlme_action action,
683 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530684{
685 struct ath_common *common = ath9k_hw_common(priv->ah);
686 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200687 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530688 int ret = 0;
689 u8 cmd_rsp;
690
Dan Carpenter0730d112010-05-08 18:24:02 +0200691 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530692 return -EINVAL;
693
Sujithfb9987d2010-03-17 14:25:25 +0530694 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530695 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530696
Sujithef98c3c2010-03-29 16:07:11 +0530697 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530698 aggr.tidno = tid & 0xf;
699 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530700
Sujithfb9987d2010-03-17 14:25:25 +0530701 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
702 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800703 ath_dbg(common, ATH_DBG_CONFIG,
704 "Unable to %s TX aggregation for (%pM, %d)\n",
705 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530706 else
Joe Perches226afe62010-12-02 19:12:37 -0800707 ath_dbg(common, ATH_DBG_CONFIG,
708 "%s TX aggregation for (%pM, %d)\n",
709 (aggr.aggr_enable) ? "Starting" : "Stopping",
710 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530711
712 spin_lock_bh(&priv->tx_lock);
713 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
714 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530715
716 return ret;
717}
718
Sujithfb9987d2010-03-17 14:25:25 +0530719/*********/
720/* DEBUG */
721/*********/
722
723#ifdef CONFIG_ATH9K_HTC_DEBUGFS
724
725static int ath9k_debugfs_open(struct inode *inode, struct file *file)
726{
727 file->private_data = inode->i_private;
728 return 0;
729}
730
731static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
732 size_t count, loff_t *ppos)
733{
Joe Perches57674302010-07-12 13:50:06 -0700734 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530735 struct ath9k_htc_target_stats cmd_rsp;
736 char buf[512];
737 unsigned int len = 0;
738 int ret = 0;
739
740 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
741
742 WMI_CMD(WMI_TGT_STATS_CMDID);
743 if (ret)
744 return -EINVAL;
745
746
747 len += snprintf(buf + len, sizeof(buf) - len,
748 "%19s : %10u\n", "TX Short Retries",
749 be32_to_cpu(cmd_rsp.tx_shortretry));
750 len += snprintf(buf + len, sizeof(buf) - len,
751 "%19s : %10u\n", "TX Long Retries",
752 be32_to_cpu(cmd_rsp.tx_longretry));
753 len += snprintf(buf + len, sizeof(buf) - len,
754 "%19s : %10u\n", "TX Xretries",
755 be32_to_cpu(cmd_rsp.tx_xretries));
756 len += snprintf(buf + len, sizeof(buf) - len,
757 "%19s : %10u\n", "TX Unaggr. Xretries",
758 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
759 len += snprintf(buf + len, sizeof(buf) - len,
760 "%19s : %10u\n", "TX Xretries (HT)",
761 be32_to_cpu(cmd_rsp.ht_tx_xretries));
762 len += snprintf(buf + len, sizeof(buf) - len,
763 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
764
Dan Carpenter97460102010-07-22 10:50:28 +0200765 if (len > sizeof(buf))
766 len = sizeof(buf);
767
Sujithfb9987d2010-03-17 14:25:25 +0530768 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
769}
770
771static const struct file_operations fops_tgt_stats = {
772 .read = read_file_tgt_stats,
773 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200774 .owner = THIS_MODULE,
775 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530776};
777
778static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
779 size_t count, loff_t *ppos)
780{
Joe Perches57674302010-07-12 13:50:06 -0700781 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530782 char buf[512];
783 unsigned int len = 0;
784
785 len += snprintf(buf + len, sizeof(buf) - len,
786 "%20s : %10u\n", "Buffers queued",
787 priv->debug.tx_stats.buf_queued);
788 len += snprintf(buf + len, sizeof(buf) - len,
789 "%20s : %10u\n", "Buffers completed",
790 priv->debug.tx_stats.buf_completed);
791 len += snprintf(buf + len, sizeof(buf) - len,
792 "%20s : %10u\n", "SKBs queued",
793 priv->debug.tx_stats.skb_queued);
794 len += snprintf(buf + len, sizeof(buf) - len,
795 "%20s : %10u\n", "SKBs completed",
796 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530797 len += snprintf(buf + len, sizeof(buf) - len,
798 "%20s : %10u\n", "SKBs dropped",
799 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530800
Sujith2edb4582010-05-14 11:18:54 +0530801 len += snprintf(buf + len, sizeof(buf) - len,
802 "%20s : %10u\n", "BE queued",
803 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
804 len += snprintf(buf + len, sizeof(buf) - len,
805 "%20s : %10u\n", "BK queued",
806 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
807 len += snprintf(buf + len, sizeof(buf) - len,
808 "%20s : %10u\n", "VI queued",
809 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
810 len += snprintf(buf + len, sizeof(buf) - len,
811 "%20s : %10u\n", "VO queued",
812 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
813
Dan Carpenter97460102010-07-22 10:50:28 +0200814 if (len > sizeof(buf))
815 len = sizeof(buf);
816
Sujithfb9987d2010-03-17 14:25:25 +0530817 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
818}
819
820static const struct file_operations fops_xmit = {
821 .read = read_file_xmit,
822 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200823 .owner = THIS_MODULE,
824 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530825};
826
827static ssize_t read_file_recv(struct file *file, char __user *user_buf,
828 size_t count, loff_t *ppos)
829{
Joe Perches57674302010-07-12 13:50:06 -0700830 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530831 char buf[512];
832 unsigned int len = 0;
833
834 len += snprintf(buf + len, sizeof(buf) - len,
835 "%20s : %10u\n", "SKBs allocated",
836 priv->debug.rx_stats.skb_allocated);
837 len += snprintf(buf + len, sizeof(buf) - len,
838 "%20s : %10u\n", "SKBs completed",
839 priv->debug.rx_stats.skb_completed);
840 len += snprintf(buf + len, sizeof(buf) - len,
841 "%20s : %10u\n", "SKBs Dropped",
842 priv->debug.rx_stats.skb_dropped);
843
Dan Carpenter97460102010-07-22 10:50:28 +0200844 if (len > sizeof(buf))
845 len = sizeof(buf);
846
Sujithfb9987d2010-03-17 14:25:25 +0530847 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
848}
849
850static const struct file_operations fops_recv = {
851 .read = read_file_recv,
852 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200853 .owner = THIS_MODULE,
854 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530855};
856
Sujithe1572c52010-03-24 13:42:13 +0530857int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530858{
859 struct ath_common *common = ath9k_hw_common(ah);
860 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
861
862 if (!ath9k_debugfs_root)
863 return -ENOENT;
864
865 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
866 ath9k_debugfs_root);
867 if (!priv->debug.debugfs_phy)
868 goto err;
869
870 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
871 priv->debug.debugfs_phy,
872 priv, &fops_tgt_stats);
873 if (!priv->debug.debugfs_tgt_stats)
874 goto err;
875
876
877 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
878 priv->debug.debugfs_phy,
879 priv, &fops_xmit);
880 if (!priv->debug.debugfs_xmit)
881 goto err;
882
883 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
884 priv->debug.debugfs_phy,
885 priv, &fops_recv);
886 if (!priv->debug.debugfs_recv)
887 goto err;
888
889 return 0;
890
891err:
Sujithe1572c52010-03-24 13:42:13 +0530892 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530893 return -ENOMEM;
894}
895
Sujithe1572c52010-03-24 13:42:13 +0530896void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530897{
898 struct ath_common *common = ath9k_hw_common(ah);
899 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
900
901 debugfs_remove(priv->debug.debugfs_recv);
902 debugfs_remove(priv->debug.debugfs_xmit);
903 debugfs_remove(priv->debug.debugfs_tgt_stats);
904 debugfs_remove(priv->debug.debugfs_phy);
905}
906
Sujithe1572c52010-03-24 13:42:13 +0530907int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530908{
909 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
910 if (!ath9k_debugfs_root)
911 return -ENOENT;
912
913 return 0;
914}
915
Sujithe1572c52010-03-24 13:42:13 +0530916void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530917{
918 debugfs_remove(ath9k_debugfs_root);
919 ath9k_debugfs_root = NULL;
920}
921
922#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
923
924/*******/
925/* ANI */
926/*******/
927
Sujith Manoharana2362542011-02-21 07:49:38 +0530928void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530929{
930 struct ath_common *common = ath9k_hw_common(priv->ah);
931 unsigned long timestamp = jiffies_to_msecs(jiffies);
932
933 common->ani.longcal_timer = timestamp;
934 common->ani.shortcal_timer = timestamp;
935 common->ani.checkani_timer = timestamp;
936
Sujith Manoharana2362542011-02-21 07:49:38 +0530937 priv->op_flags |= OP_ANI_RUNNING;
938
939 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530940 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
941}
942
Sujith Manoharana2362542011-02-21 07:49:38 +0530943void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
944{
945 cancel_delayed_work_sync(&priv->ani_work);
946 priv->op_flags &= ~OP_ANI_RUNNING;
947}
948
949void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530950{
951 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530952 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530953 struct ath_hw *ah = priv->ah;
954 struct ath_common *common = ath9k_hw_common(ah);
955 bool longcal = false;
956 bool shortcal = false;
957 bool aniflag = false;
958 unsigned int timestamp = jiffies_to_msecs(jiffies);
959 u32 cal_interval, short_cal_interval;
960
Sujith Manoharana2362542011-02-21 07:49:38 +0530961 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
962 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530963
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530964 /* Only calibrate if awake */
965 if (ah->power_mode != ATH9K_PM_AWAKE)
966 goto set_timer;
967
Sujithfb9987d2010-03-17 14:25:25 +0530968 /* Long calibration runs independently of short calibration. */
969 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
970 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800971 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530972 common->ani.longcal_timer = timestamp;
973 }
974
975 /* Short calibration applies only while caldone is false */
976 if (!common->ani.caldone) {
977 if ((timestamp - common->ani.shortcal_timer) >=
978 short_cal_interval) {
979 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800980 ath_dbg(common, ATH_DBG_ANI,
981 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530982 common->ani.shortcal_timer = timestamp;
983 common->ani.resetcal_timer = timestamp;
984 }
985 } else {
986 if ((timestamp - common->ani.resetcal_timer) >=
987 ATH_RESTART_CALINTERVAL) {
988 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
989 if (common->ani.caldone)
990 common->ani.resetcal_timer = timestamp;
991 }
992 }
993
994 /* Verify whether we must check ANI */
995 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
996 aniflag = true;
997 common->ani.checkani_timer = timestamp;
998 }
999
1000 /* Skip all processing if there's nothing to do. */
1001 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301002
1003 ath9k_htc_ps_wakeup(priv);
1004
Sujithfb9987d2010-03-17 14:25:25 +05301005 /* Call ANI routine if necessary */
1006 if (aniflag)
1007 ath9k_hw_ani_monitor(ah, ah->curchan);
1008
1009 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +02001010 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +05301011 common->ani.caldone =
1012 ath9k_hw_calibrate(ah, ah->curchan,
1013 common->rx_chainmask,
1014 longcal);
1015
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301016 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301017 }
1018
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301019set_timer:
Sujithfb9987d2010-03-17 14:25:25 +05301020 /*
1021 * Set timer interval based on previous results.
1022 * The interval must be the shortest necessary to satisfy ANI,
1023 * short calibration and long calibration.
1024 */
1025 cal_interval = ATH_LONG_CALINTERVAL;
1026 if (priv->ah->config.enable_ani)
1027 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
1028 if (!common->ani.caldone)
1029 cal_interval = min(cal_interval, (u32)short_cal_interval);
1030
Sujith Manoharana2362542011-02-21 07:49:38 +05301031 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +05301032 msecs_to_jiffies(cal_interval));
1033}
1034
Sujithfb9987d2010-03-17 14:25:25 +05301035/**********************/
1036/* mac80211 Callbacks */
1037/**********************/
1038
1039static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1040{
1041 struct ieee80211_hdr *hdr;
1042 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301043 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301044
1045 hdr = (struct ieee80211_hdr *) skb->data;
1046
1047 /* Add the padding after the header if this is not already done */
1048 padpos = ath9k_cmn_padpos(hdr->frame_control);
1049 padsize = padpos & 3;
1050 if (padsize && skb->len > padpos) {
1051 if (skb_headroom(skb) < padsize)
1052 return -1;
1053 skb_push(skb, padsize);
1054 memmove(skb->data, skb->data + padsize, padpos);
1055 }
1056
Sujith7757dfe2010-03-29 16:07:17 +05301057 ret = ath9k_htc_tx_start(priv, skb);
1058 if (ret != 0) {
1059 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -08001060 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1061 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +05301062 ieee80211_stop_queues(hw);
1063 spin_lock_bh(&priv->tx_lock);
1064 priv->tx_queues_stop = true;
1065 spin_unlock_bh(&priv->tx_lock);
1066 } else {
Joe Perches226afe62010-12-02 19:12:37 -08001067 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1068 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +05301069 }
Sujithfb9987d2010-03-17 14:25:25 +05301070 goto fail_tx;
1071 }
1072
1073 return 0;
1074
1075fail_tx:
1076 dev_kfree_skb_any(skb);
1077 return 0;
1078}
1079
Sujith881ac6a2010-06-01 15:14:11 +05301080static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301081{
1082 struct ath9k_htc_priv *priv = hw->priv;
1083 struct ath_hw *ah = priv->ah;
1084 struct ath_common *common = ath9k_hw_common(ah);
1085 struct ieee80211_channel *curchan = hw->conf.channel;
1086 struct ath9k_channel *init_channel;
1087 int ret = 0;
1088 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301089 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301090 u8 cmd_rsp;
1091
Sujith881ac6a2010-06-01 15:14:11 +05301092 mutex_lock(&priv->mutex);
1093
Joe Perches226afe62010-12-02 19:12:37 -08001094 ath_dbg(common, ATH_DBG_CONFIG,
1095 "Starting driver with initial channel: %d MHz\n",
1096 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301097
Sujith21d51302010-06-01 15:14:18 +05301098 /* Ensure that HW is awake before flushing RX */
1099 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1100 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1101
Sujithfb9987d2010-03-17 14:25:25 +05301102 /* setup initial channel */
1103 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1104
Sujithfb9987d2010-03-17 14:25:25 +05301105 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001106 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301107 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001108 ath_err(common,
1109 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1110 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301111 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301112 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301113 }
1114
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301115 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1116 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +05301117
1118 mode = ath9k_htc_get_curmode(priv, init_channel);
1119 htc_mode = cpu_to_be16(mode);
1120 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301121 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301122 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301123
1124 ath9k_host_rx_init(priv);
1125
Sujith Manoharan1057b752011-02-21 07:48:09 +05301126 ret = ath9k_htc_update_cap_target(priv);
1127 if (ret)
1128 ath_dbg(common, ATH_DBG_CONFIG,
1129 "Failed to update capability in target\n");
1130
Sujithfb9987d2010-03-17 14:25:25 +05301131 priv->op_flags &= ~OP_INVALID;
1132 htc_start(priv->htc);
1133
Sujith7757dfe2010-03-29 16:07:17 +05301134 spin_lock_bh(&priv->tx_lock);
1135 priv->tx_queues_stop = false;
1136 spin_unlock_bh(&priv->tx_lock);
1137
1138 ieee80211_wake_queues(hw);
1139
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301140 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1141 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1142 AR_STOMP_LOW_WLAN_WGHT);
1143 ath9k_hw_btcoex_enable(ah);
1144 ath_htc_resume_btcoex_work(priv);
1145 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301146 mutex_unlock(&priv->mutex);
1147
1148 return ret;
1149}
1150
Sujith881ac6a2010-06-01 15:14:11 +05301151static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301152{
1153 struct ath9k_htc_priv *priv = hw->priv;
1154 struct ath_hw *ah = priv->ah;
1155 struct ath_common *common = ath9k_hw_common(ah);
1156 int ret = 0;
1157 u8 cmd_rsp;
1158
Sujith881ac6a2010-06-01 15:14:11 +05301159 mutex_lock(&priv->mutex);
1160
Sujithfb9987d2010-03-17 14:25:25 +05301161 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001162 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301163 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301164 return;
1165 }
1166
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301167 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301168 htc_stop(priv->htc);
1169 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1170 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1171 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001172
1173 tasklet_kill(&priv->swba_tasklet);
1174 tasklet_kill(&priv->rx_tasklet);
1175 tasklet_kill(&priv->tx_tasklet);
1176
Sujithfb9987d2010-03-17 14:25:25 +05301177 skb_queue_purge(&priv->tx_queue);
1178
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001179 mutex_unlock(&priv->mutex);
1180
1181 /* Cancel all the running timers/work .. */
1182 cancel_work_sync(&priv->fatal_work);
1183 cancel_work_sync(&priv->ps_work);
1184 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301185 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001186 ath9k_led_stop_brightness(priv);
1187
1188 mutex_lock(&priv->mutex);
1189
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301190 if (ah->btcoex_hw.enabled) {
1191 ath9k_hw_btcoex_disable(ah);
1192 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1193 ath_htc_cancel_btcoex_work(priv);
1194 }
1195
Sujith Manoharana97b4782011-02-21 07:48:00 +05301196 /* Remove a monitor interface if it's present. */
1197 if (priv->ah->is_monitoring)
1198 ath9k_htc_remove_monitor_interface(priv);
1199
Sujithe9201f02010-06-01 15:14:17 +05301200 ath9k_hw_phy_disable(ah);
1201 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301202 ath9k_htc_ps_restore(priv);
1203 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1204
Sujithfb9987d2010-03-17 14:25:25 +05301205 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301206
Joe Perches226afe62010-12-02 19:12:37 -08001207 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301208 mutex_unlock(&priv->mutex);
1209}
1210
Sujithfb9987d2010-03-17 14:25:25 +05301211static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1212 struct ieee80211_vif *vif)
1213{
1214 struct ath9k_htc_priv *priv = hw->priv;
1215 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1216 struct ath_common *common = ath9k_hw_common(priv->ah);
1217 struct ath9k_htc_target_vif hvif;
1218 int ret = 0;
1219 u8 cmd_rsp;
1220
1221 mutex_lock(&priv->mutex);
1222
Sujith Manoharana97b4782011-02-21 07:48:00 +05301223 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301224 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301225 return -ENOBUFS;
1226 }
1227
1228 if (priv->num_ibss_vif ||
1229 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1230 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1231 mutex_unlock(&priv->mutex);
1232 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301233 }
1234
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301235 if (((vif->type == NL80211_IFTYPE_AP) ||
1236 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1237 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1238 ath_err(common, "Max. number of beaconing interfaces reached\n");
1239 mutex_unlock(&priv->mutex);
1240 return -ENOBUFS;
1241 }
1242
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301243 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301244 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1245 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1246
1247 switch (vif->type) {
1248 case NL80211_IFTYPE_STATION:
1249 hvif.opmode = cpu_to_be32(HTC_M_STA);
1250 break;
1251 case NL80211_IFTYPE_ADHOC:
1252 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1253 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301254 case NL80211_IFTYPE_AP:
1255 hvif.opmode = cpu_to_be32(HTC_M_HOSTAP);
1256 break;
Sujithfb9987d2010-03-17 14:25:25 +05301257 default:
Joe Perches38002762010-12-02 19:12:36 -08001258 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301259 "Interface type %d not yet supported\n", vif->type);
1260 ret = -EOPNOTSUPP;
1261 goto out;
1262 }
1263
Sujithfb9987d2010-03-17 14:25:25 +05301264 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301265 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301266 hvif.rtsthreshold = cpu_to_be16(2304);
1267 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1268 if (ret)
1269 goto out;
1270
Sujithfb9987d2010-03-17 14:25:25 +05301271 /*
1272 * We need a node in target to tx mgmt frames
1273 * before association.
1274 */
1275 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301276 if (ret) {
1277 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301278 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301279 }
Sujithfb9987d2010-03-17 14:25:25 +05301280
Sujith Manoharan585895c2011-02-21 07:48:46 +05301281 ath9k_htc_set_bssid_mask(priv, vif);
1282
Sujith Manoharana97b4782011-02-21 07:48:00 +05301283 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301284 priv->nvifs++;
Sujithfb9987d2010-03-17 14:25:25 +05301285 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301286
Sujith Manoharan0df83592011-02-21 07:49:15 +05301287 INC_VIF(priv, vif->type);
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301288 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301289
Sujith Manoharana2362542011-02-21 07:49:38 +05301290 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1291 !(priv->op_flags & OP_ANI_RUNNING))
1292 ath9k_htc_start_ani(priv);
1293
Sujith Manoharana97b4782011-02-21 07:48:00 +05301294 ath_dbg(common, ATH_DBG_CONFIG,
1295 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1296
Sujithfb9987d2010-03-17 14:25:25 +05301297out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301298 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301299 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301300
Sujithfb9987d2010-03-17 14:25:25 +05301301 return ret;
1302}
1303
1304static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1305 struct ieee80211_vif *vif)
1306{
1307 struct ath9k_htc_priv *priv = hw->priv;
1308 struct ath_common *common = ath9k_hw_common(priv->ah);
1309 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1310 struct ath9k_htc_target_vif hvif;
1311 int ret = 0;
1312 u8 cmd_rsp;
1313
Sujithfb9987d2010-03-17 14:25:25 +05301314 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301315 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301316
1317 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1318 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1319 hvif.index = avp->index;
1320 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1321 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301322 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301323
1324 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301325 priv->vif = NULL;
1326
Sujith Manoharan0df83592011-02-21 07:49:15 +05301327 DEC_VIF(priv, vif->type);
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301328 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301329
Sujith Manoharana2362542011-02-21 07:49:38 +05301330 /*
1331 * Stop ANI only if there are no associated station interfaces.
1332 */
1333 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1334 priv->rearm_ani = false;
1335 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1336 ath9k_htc_vif_iter, priv);
1337 if (!priv->rearm_ani)
1338 ath9k_htc_stop_ani(priv);
1339 }
1340
Sujith Manoharana97b4782011-02-21 07:48:00 +05301341 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1342
Sujithcb551df2010-06-01 15:14:12 +05301343 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301344 mutex_unlock(&priv->mutex);
1345}
1346
1347static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1348{
1349 struct ath9k_htc_priv *priv = hw->priv;
1350 struct ath_common *common = ath9k_hw_common(priv->ah);
1351 struct ieee80211_conf *conf = &hw->conf;
1352
1353 mutex_lock(&priv->mutex);
1354
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301355 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1356 bool enable_radio = false;
1357 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1358
Sujith23367762010-06-01 15:14:16 +05301359 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301360 if (!idle && priv->ps_idle)
1361 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301362 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301363 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301364
1365 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001366 ath_dbg(common, ATH_DBG_CONFIG,
1367 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301368 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1369 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301370 }
1371 }
1372
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301373 /*
1374 * Monitor interface should be added before
1375 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1376 */
1377 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301378 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1379 !priv->ah->is_monitoring)
1380 ath9k_htc_add_monitor_interface(priv);
1381 else if (priv->ah->is_monitoring)
1382 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301383 }
1384
Sujithfb9987d2010-03-17 14:25:25 +05301385 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1386 struct ieee80211_channel *curchan = hw->conf.channel;
1387 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301388
Joe Perches226afe62010-12-02 19:12:37 -08001389 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1390 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301391
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001392 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1393 hw->conf.channel,
1394 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301395
1396 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001397 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301398 mutex_unlock(&priv->mutex);
1399 return -EINVAL;
1400 }
1401
1402 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301403
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301404 if (changed & IEEE80211_CONF_CHANGE_PS) {
1405 if (conf->flags & IEEE80211_CONF_PS) {
1406 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1407 priv->ps_enabled = true;
1408 } else {
1409 priv->ps_enabled = false;
1410 cancel_work_sync(&priv->ps_work);
1411 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1412 }
1413 }
Sujithfb9987d2010-03-17 14:25:25 +05301414
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301415 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1416 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301417 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1418 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301419 }
1420
Sujith23367762010-06-01 15:14:16 +05301421 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1422 mutex_lock(&priv->htc_pm_lock);
1423 if (!priv->ps_idle) {
1424 mutex_unlock(&priv->htc_pm_lock);
1425 goto out;
1426 }
1427 mutex_unlock(&priv->htc_pm_lock);
1428
Joe Perches226afe62010-12-02 19:12:37 -08001429 ath_dbg(common, ATH_DBG_CONFIG,
1430 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301431 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301432 }
1433
Sujith23367762010-06-01 15:14:16 +05301434out:
Sujithfb9987d2010-03-17 14:25:25 +05301435 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301436 return 0;
1437}
1438
1439#define SUPPORTED_FILTERS \
1440 (FIF_PROMISC_IN_BSS | \
1441 FIF_ALLMULTI | \
1442 FIF_CONTROL | \
1443 FIF_PSPOLL | \
1444 FIF_OTHER_BSS | \
1445 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301446 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301447 FIF_FCSFAIL)
1448
1449static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1450 unsigned int changed_flags,
1451 unsigned int *total_flags,
1452 u64 multicast)
1453{
1454 struct ath9k_htc_priv *priv = hw->priv;
1455 u32 rfilt;
1456
1457 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301458 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301459
Sujithfb9987d2010-03-17 14:25:25 +05301460 changed_flags &= SUPPORTED_FILTERS;
1461 *total_flags &= SUPPORTED_FILTERS;
1462
1463 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301464 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301465 ath9k_hw_setrxfilter(priv->ah, rfilt);
1466
Joe Perches226afe62010-12-02 19:12:37 -08001467 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1468 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301469
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301470 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301471 mutex_unlock(&priv->mutex);
1472}
1473
Sujithabd984e2010-05-18 15:26:04 +05301474static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1475 struct ieee80211_vif *vif,
1476 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301477{
1478 struct ath9k_htc_priv *priv = hw->priv;
1479 int ret;
1480
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301481 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301482 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301483 ret = ath9k_htc_add_station(priv, vif, sta);
1484 if (!ret)
1485 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301486 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301487 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301488
1489 return ret;
1490}
1491
1492static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1493 struct ieee80211_vif *vif,
1494 struct ieee80211_sta *sta)
1495{
1496 struct ath9k_htc_priv *priv = hw->priv;
1497 int ret;
1498
1499 mutex_lock(&priv->mutex);
1500 ath9k_htc_ps_wakeup(priv);
1501 ret = ath9k_htc_remove_station(priv, vif, sta);
1502 ath9k_htc_ps_restore(priv);
1503 mutex_unlock(&priv->mutex);
1504
1505 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301506}
1507
1508static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1509 const struct ieee80211_tx_queue_params *params)
1510{
1511 struct ath9k_htc_priv *priv = hw->priv;
1512 struct ath_common *common = ath9k_hw_common(priv->ah);
1513 struct ath9k_tx_queue_info qi;
1514 int ret = 0, qnum;
1515
1516 if (queue >= WME_NUM_AC)
1517 return 0;
1518
1519 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301520 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301521
1522 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1523
1524 qi.tqi_aifs = params->aifs;
1525 qi.tqi_cwmin = params->cw_min;
1526 qi.tqi_cwmax = params->cw_max;
1527 qi.tqi_burstTime = params->txop;
1528
1529 qnum = get_hw_qnum(queue, priv->hwq_map);
1530
Joe Perches226afe62010-12-02 19:12:37 -08001531 ath_dbg(common, ATH_DBG_CONFIG,
1532 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1533 queue, qnum, params->aifs, params->cw_min,
1534 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301535
Sujithe1572c52010-03-24 13:42:13 +05301536 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301537 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001538 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301539 goto out;
1540 }
Sujithfb9987d2010-03-17 14:25:25 +05301541
Sujith764580f2010-06-01 15:14:19 +05301542 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001543 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301544 ath9k_htc_beaconq_config(priv);
1545out:
Sujithcb551df2010-06-01 15:14:12 +05301546 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301547 mutex_unlock(&priv->mutex);
1548
1549 return ret;
1550}
1551
1552static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1553 enum set_key_cmd cmd,
1554 struct ieee80211_vif *vif,
1555 struct ieee80211_sta *sta,
1556 struct ieee80211_key_conf *key)
1557{
1558 struct ath9k_htc_priv *priv = hw->priv;
1559 struct ath_common *common = ath9k_hw_common(priv->ah);
1560 int ret = 0;
1561
Sujithe1572c52010-03-24 13:42:13 +05301562 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301563 return -ENOSPC;
1564
1565 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001566 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301567 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301568
1569 switch (cmd) {
1570 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001571 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301572 if (ret >= 0) {
1573 key->hw_key_idx = ret;
1574 /* push IV and Michael MIC generation to stack */
1575 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001576 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301577 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001578 if (priv->ah->sw_mgmt_crypto &&
1579 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301580 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1581 ret = 0;
1582 }
1583 break;
1584 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001585 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301586 break;
1587 default:
1588 ret = -EINVAL;
1589 }
1590
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301591 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301592 mutex_unlock(&priv->mutex);
1593
1594 return ret;
1595}
1596
1597static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1598 struct ieee80211_vif *vif,
1599 struct ieee80211_bss_conf *bss_conf,
1600 u32 changed)
1601{
1602 struct ath9k_htc_priv *priv = hw->priv;
1603 struct ath_hw *ah = priv->ah;
1604 struct ath_common *common = ath9k_hw_common(ah);
1605
1606 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301607 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301608
1609 if (changed & BSS_CHANGED_ASSOC) {
1610 common->curaid = bss_conf->assoc ?
1611 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001612 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301613 bss_conf->assoc);
1614
Sujith Manoharan7c277342011-02-21 07:48:39 +05301615 if (bss_conf->assoc)
Sujith Manoharana2362542011-02-21 07:49:38 +05301616 ath9k_htc_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301617 else
Sujith Manoharana2362542011-02-21 07:49:38 +05301618 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301619 }
1620
1621 if (changed & BSS_CHANGED_BSSID) {
1622 /* Set BSSID */
1623 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1624 ath9k_hw_write_associd(ah);
1625
Joe Perches226afe62010-12-02 19:12:37 -08001626 ath_dbg(common, ATH_DBG_CONFIG,
1627 "BSSID: %pM aid: 0x%x\n",
1628 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301629 }
1630
Sujith Manoharana5fae372011-02-21 07:49:53 +05301631 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1632 ath_dbg(common, ATH_DBG_CONFIG,
1633 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujithfb9987d2010-03-17 14:25:25 +05301634 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301635 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301636 }
1637
Sujith Manoharana5fae372011-02-21 07:49:53 +05301638 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1639 /*
1640 * Disable SWBA interrupt only if there are no
1641 * AP/IBSS interfaces.
1642 */
1643 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1644 ath_dbg(common, ATH_DBG_CONFIG,
1645 "Beacon disabled for BSS: %pM\n",
1646 bss_conf->bssid);
1647 priv->op_flags &= ~OP_ENABLE_BEACON;
1648 ath9k_htc_beacon_config(priv, vif);
1649 }
1650 }
1651
1652 if (changed & BSS_CHANGED_BEACON_INT) {
1653 /*
1654 * Reset the HW TSF for the first AP interface.
1655 */
1656 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1657 (priv->nvifs == 1) &&
1658 (priv->num_ap_vif == 1) &&
1659 (vif->type == NL80211_IFTYPE_AP)) {
1660 priv->op_flags |= OP_TSF_RESET;
1661 }
1662 ath_dbg(common, ATH_DBG_CONFIG,
1663 "Beacon interval changed for BSS: %pM\n",
1664 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301665 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301666 }
1667
Sujithfb9987d2010-03-17 14:25:25 +05301668 if (changed & BSS_CHANGED_ERP_SLOT) {
1669 if (bss_conf->use_short_slot)
1670 ah->slottime = 9;
1671 else
1672 ah->slottime = 20;
1673
1674 ath9k_hw_init_global_settings(ah);
1675 }
1676
Sujith2c76ef82010-05-17 12:01:18 +05301677 if (changed & BSS_CHANGED_HT)
1678 ath9k_htc_update_rate(priv, vif, bss_conf);
1679
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301680 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301681 mutex_unlock(&priv->mutex);
1682}
1683
1684static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1685{
1686 struct ath9k_htc_priv *priv = hw->priv;
1687 u64 tsf;
1688
1689 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301690 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301691 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301692 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301693 mutex_unlock(&priv->mutex);
1694
1695 return tsf;
1696}
1697
1698static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1699{
1700 struct ath9k_htc_priv *priv = hw->priv;
1701
1702 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301703 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301704 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301705 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301706 mutex_unlock(&priv->mutex);
1707}
1708
1709static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1710{
1711 struct ath9k_htc_priv *priv = hw->priv;
1712
1713 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301714 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301715 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301716 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301717 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301718}
1719
1720static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1721 struct ieee80211_vif *vif,
1722 enum ieee80211_ampdu_mlme_action action,
1723 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001724 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301725{
1726 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301727 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301728 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301729
Sujith Manoharan87df8952011-02-21 07:49:08 +05301730 mutex_lock(&priv->mutex);
1731
Sujithfb9987d2010-03-17 14:25:25 +05301732 switch (action) {
1733 case IEEE80211_AMPDU_RX_START:
1734 break;
1735 case IEEE80211_AMPDU_RX_STOP:
1736 break;
1737 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301738 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1739 if (!ret)
1740 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1741 break;
Sujithfb9987d2010-03-17 14:25:25 +05301742 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301743 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1744 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301745 break;
1746 case IEEE80211_AMPDU_TX_OPERATIONAL:
1747 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301748 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301749 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301750 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301751 break;
1752 default:
Joe Perches38002762010-12-02 19:12:36 -08001753 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301754 }
1755
Sujith Manoharan87df8952011-02-21 07:49:08 +05301756 mutex_unlock(&priv->mutex);
1757
Sujithd7ca2132010-06-15 10:24:37 +05301758 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301759}
1760
1761static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1762{
1763 struct ath9k_htc_priv *priv = hw->priv;
1764
1765 mutex_lock(&priv->mutex);
1766 spin_lock_bh(&priv->beacon_lock);
1767 priv->op_flags |= OP_SCANNING;
1768 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301769 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301770 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301771 mutex_unlock(&priv->mutex);
1772}
1773
1774static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1775{
1776 struct ath9k_htc_priv *priv = hw->priv;
1777
1778 mutex_lock(&priv->mutex);
1779 spin_lock_bh(&priv->beacon_lock);
1780 priv->op_flags &= ~OP_SCANNING;
1781 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301782 ath9k_htc_ps_wakeup(priv);
1783 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301784 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301785 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301786}
1787
1788static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1789{
1790 return 0;
1791}
1792
1793static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1794 u8 coverage_class)
1795{
1796 struct ath9k_htc_priv *priv = hw->priv;
1797
1798 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301799 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301800 priv->ah->coverage_class = coverage_class;
1801 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301802 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301803 mutex_unlock(&priv->mutex);
1804}
1805
1806struct ieee80211_ops ath9k_htc_ops = {
1807 .tx = ath9k_htc_tx,
1808 .start = ath9k_htc_start,
1809 .stop = ath9k_htc_stop,
1810 .add_interface = ath9k_htc_add_interface,
1811 .remove_interface = ath9k_htc_remove_interface,
1812 .config = ath9k_htc_config,
1813 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301814 .sta_add = ath9k_htc_sta_add,
1815 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301816 .conf_tx = ath9k_htc_conf_tx,
1817 .bss_info_changed = ath9k_htc_bss_info_changed,
1818 .set_key = ath9k_htc_set_key,
1819 .get_tsf = ath9k_htc_get_tsf,
1820 .set_tsf = ath9k_htc_set_tsf,
1821 .reset_tsf = ath9k_htc_reset_tsf,
1822 .ampdu_action = ath9k_htc_ampdu_action,
1823 .sw_scan_start = ath9k_htc_sw_scan_start,
1824 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1825 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1826 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1827 .set_coverage_class = ath9k_htc_set_coverage_class,
1828};