blob: db8c0c044e9e8498a97dc4788906624b7087c531 [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
Johannes Berg7bb45682011-02-24 14:42:06 +01001039static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +05301040{
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)
Johannes Berg7bb45682011-02-24 14:42:06 +01001052 goto fail_tx;
Sujithfb9987d2010-03-17 14:25:25 +05301053 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
Johannes Berg7bb45682011-02-24 14:42:06 +01001073 return;
Sujithfb9987d2010-03-17 14:25:25 +05301074
1075fail_tx:
1076 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +05301077}
1078
Sujith881ac6a2010-06-01 15:14:11 +05301079static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301080{
1081 struct ath9k_htc_priv *priv = hw->priv;
1082 struct ath_hw *ah = priv->ah;
1083 struct ath_common *common = ath9k_hw_common(ah);
1084 struct ieee80211_channel *curchan = hw->conf.channel;
1085 struct ath9k_channel *init_channel;
1086 int ret = 0;
1087 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301088 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301089 u8 cmd_rsp;
1090
Sujith881ac6a2010-06-01 15:14:11 +05301091 mutex_lock(&priv->mutex);
1092
Joe Perches226afe62010-12-02 19:12:37 -08001093 ath_dbg(common, ATH_DBG_CONFIG,
1094 "Starting driver with initial channel: %d MHz\n",
1095 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301096
Sujith21d51302010-06-01 15:14:18 +05301097 /* Ensure that HW is awake before flushing RX */
1098 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1099 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1100
Sujithfb9987d2010-03-17 14:25:25 +05301101 /* setup initial channel */
1102 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1103
Sujithfb9987d2010-03-17 14:25:25 +05301104 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001105 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301106 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001107 ath_err(common,
1108 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1109 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301110 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301111 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301112 }
1113
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301114 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1115 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +05301116
1117 mode = ath9k_htc_get_curmode(priv, init_channel);
1118 htc_mode = cpu_to_be16(mode);
1119 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301120 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301121 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301122
1123 ath9k_host_rx_init(priv);
1124
Sujith Manoharan1057b752011-02-21 07:48:09 +05301125 ret = ath9k_htc_update_cap_target(priv);
1126 if (ret)
1127 ath_dbg(common, ATH_DBG_CONFIG,
1128 "Failed to update capability in target\n");
1129
Sujithfb9987d2010-03-17 14:25:25 +05301130 priv->op_flags &= ~OP_INVALID;
1131 htc_start(priv->htc);
1132
Sujith7757dfe2010-03-29 16:07:17 +05301133 spin_lock_bh(&priv->tx_lock);
1134 priv->tx_queues_stop = false;
1135 spin_unlock_bh(&priv->tx_lock);
1136
1137 ieee80211_wake_queues(hw);
1138
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301139 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1140 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1141 AR_STOMP_LOW_WLAN_WGHT);
1142 ath9k_hw_btcoex_enable(ah);
1143 ath_htc_resume_btcoex_work(priv);
1144 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301145 mutex_unlock(&priv->mutex);
1146
1147 return ret;
1148}
1149
Sujith881ac6a2010-06-01 15:14:11 +05301150static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301151{
1152 struct ath9k_htc_priv *priv = hw->priv;
1153 struct ath_hw *ah = priv->ah;
1154 struct ath_common *common = ath9k_hw_common(ah);
1155 int ret = 0;
1156 u8 cmd_rsp;
1157
Sujith881ac6a2010-06-01 15:14:11 +05301158 mutex_lock(&priv->mutex);
1159
Sujithfb9987d2010-03-17 14:25:25 +05301160 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001161 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301162 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301163 return;
1164 }
1165
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301166 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301167 htc_stop(priv->htc);
1168 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1169 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1170 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001171
1172 tasklet_kill(&priv->swba_tasklet);
1173 tasklet_kill(&priv->rx_tasklet);
1174 tasklet_kill(&priv->tx_tasklet);
1175
Sujithfb9987d2010-03-17 14:25:25 +05301176 skb_queue_purge(&priv->tx_queue);
1177
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001178 mutex_unlock(&priv->mutex);
1179
1180 /* Cancel all the running timers/work .. */
1181 cancel_work_sync(&priv->fatal_work);
1182 cancel_work_sync(&priv->ps_work);
1183 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301184 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001185 ath9k_led_stop_brightness(priv);
1186
1187 mutex_lock(&priv->mutex);
1188
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301189 if (ah->btcoex_hw.enabled) {
1190 ath9k_hw_btcoex_disable(ah);
1191 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1192 ath_htc_cancel_btcoex_work(priv);
1193 }
1194
Sujith Manoharana97b4782011-02-21 07:48:00 +05301195 /* Remove a monitor interface if it's present. */
1196 if (priv->ah->is_monitoring)
1197 ath9k_htc_remove_monitor_interface(priv);
1198
Sujithe9201f02010-06-01 15:14:17 +05301199 ath9k_hw_phy_disable(ah);
1200 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301201 ath9k_htc_ps_restore(priv);
1202 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1203
Sujithfb9987d2010-03-17 14:25:25 +05301204 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301205
Joe Perches226afe62010-12-02 19:12:37 -08001206 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301207 mutex_unlock(&priv->mutex);
1208}
1209
Sujithfb9987d2010-03-17 14:25:25 +05301210static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1211 struct ieee80211_vif *vif)
1212{
1213 struct ath9k_htc_priv *priv = hw->priv;
1214 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1215 struct ath_common *common = ath9k_hw_common(priv->ah);
1216 struct ath9k_htc_target_vif hvif;
1217 int ret = 0;
1218 u8 cmd_rsp;
1219
1220 mutex_lock(&priv->mutex);
1221
Sujith Manoharana97b4782011-02-21 07:48:00 +05301222 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301223 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301224 return -ENOBUFS;
1225 }
1226
1227 if (priv->num_ibss_vif ||
1228 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1229 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1230 mutex_unlock(&priv->mutex);
1231 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301232 }
1233
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301234 if (((vif->type == NL80211_IFTYPE_AP) ||
1235 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1236 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1237 ath_err(common, "Max. number of beaconing interfaces reached\n");
1238 mutex_unlock(&priv->mutex);
1239 return -ENOBUFS;
1240 }
1241
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301242 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301243 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1244 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1245
1246 switch (vif->type) {
1247 case NL80211_IFTYPE_STATION:
1248 hvif.opmode = cpu_to_be32(HTC_M_STA);
1249 break;
1250 case NL80211_IFTYPE_ADHOC:
1251 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1252 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301253 case NL80211_IFTYPE_AP:
1254 hvif.opmode = cpu_to_be32(HTC_M_HOSTAP);
1255 break;
Sujithfb9987d2010-03-17 14:25:25 +05301256 default:
Joe Perches38002762010-12-02 19:12:36 -08001257 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301258 "Interface type %d not yet supported\n", vif->type);
1259 ret = -EOPNOTSUPP;
1260 goto out;
1261 }
1262
Sujithfb9987d2010-03-17 14:25:25 +05301263 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301264 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301265 hvif.rtsthreshold = cpu_to_be16(2304);
1266 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1267 if (ret)
1268 goto out;
1269
Sujithfb9987d2010-03-17 14:25:25 +05301270 /*
1271 * We need a node in target to tx mgmt frames
1272 * before association.
1273 */
1274 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301275 if (ret) {
1276 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301277 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301278 }
Sujithfb9987d2010-03-17 14:25:25 +05301279
Sujith Manoharan585895c2011-02-21 07:48:46 +05301280 ath9k_htc_set_bssid_mask(priv, vif);
1281
Sujith Manoharana97b4782011-02-21 07:48:00 +05301282 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301283 priv->nvifs++;
Sujithfb9987d2010-03-17 14:25:25 +05301284 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301285
Sujith Manoharan0df83592011-02-21 07:49:15 +05301286 INC_VIF(priv, vif->type);
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301287 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301288
Sujith Manoharana2362542011-02-21 07:49:38 +05301289 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1290 !(priv->op_flags & OP_ANI_RUNNING))
1291 ath9k_htc_start_ani(priv);
1292
Sujith Manoharana97b4782011-02-21 07:48:00 +05301293 ath_dbg(common, ATH_DBG_CONFIG,
1294 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1295
Sujithfb9987d2010-03-17 14:25:25 +05301296out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301297 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301298 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301299
Sujithfb9987d2010-03-17 14:25:25 +05301300 return ret;
1301}
1302
1303static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1304 struct ieee80211_vif *vif)
1305{
1306 struct ath9k_htc_priv *priv = hw->priv;
1307 struct ath_common *common = ath9k_hw_common(priv->ah);
1308 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1309 struct ath9k_htc_target_vif hvif;
1310 int ret = 0;
1311 u8 cmd_rsp;
1312
Sujithfb9987d2010-03-17 14:25:25 +05301313 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301314 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301315
1316 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1317 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1318 hvif.index = avp->index;
1319 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1320 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301321 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301322
1323 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301324 priv->vif = NULL;
1325
Sujith Manoharan0df83592011-02-21 07:49:15 +05301326 DEC_VIF(priv, vif->type);
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301327 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301328
Sujith Manoharana2362542011-02-21 07:49:38 +05301329 /*
1330 * Stop ANI only if there are no associated station interfaces.
1331 */
1332 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1333 priv->rearm_ani = false;
1334 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1335 ath9k_htc_vif_iter, priv);
1336 if (!priv->rearm_ani)
1337 ath9k_htc_stop_ani(priv);
1338 }
1339
Sujith Manoharana97b4782011-02-21 07:48:00 +05301340 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1341
Sujithcb551df2010-06-01 15:14:12 +05301342 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301343 mutex_unlock(&priv->mutex);
1344}
1345
1346static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1347{
1348 struct ath9k_htc_priv *priv = hw->priv;
1349 struct ath_common *common = ath9k_hw_common(priv->ah);
1350 struct ieee80211_conf *conf = &hw->conf;
1351
1352 mutex_lock(&priv->mutex);
1353
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301354 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1355 bool enable_radio = false;
1356 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1357
Sujith23367762010-06-01 15:14:16 +05301358 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301359 if (!idle && priv->ps_idle)
1360 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301361 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301362 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301363
1364 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001365 ath_dbg(common, ATH_DBG_CONFIG,
1366 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301367 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1368 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301369 }
1370 }
1371
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301372 /*
1373 * Monitor interface should be added before
1374 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1375 */
1376 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301377 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1378 !priv->ah->is_monitoring)
1379 ath9k_htc_add_monitor_interface(priv);
1380 else if (priv->ah->is_monitoring)
1381 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301382 }
1383
Sujithfb9987d2010-03-17 14:25:25 +05301384 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1385 struct ieee80211_channel *curchan = hw->conf.channel;
1386 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301387
Joe Perches226afe62010-12-02 19:12:37 -08001388 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1389 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301390
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001391 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1392 hw->conf.channel,
1393 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301394
1395 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001396 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301397 mutex_unlock(&priv->mutex);
1398 return -EINVAL;
1399 }
1400
1401 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301402
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301403 if (changed & IEEE80211_CONF_CHANGE_PS) {
1404 if (conf->flags & IEEE80211_CONF_PS) {
1405 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1406 priv->ps_enabled = true;
1407 } else {
1408 priv->ps_enabled = false;
1409 cancel_work_sync(&priv->ps_work);
1410 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1411 }
1412 }
Sujithfb9987d2010-03-17 14:25:25 +05301413
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301414 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1415 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301416 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1417 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301418 }
1419
Sujith23367762010-06-01 15:14:16 +05301420 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1421 mutex_lock(&priv->htc_pm_lock);
1422 if (!priv->ps_idle) {
1423 mutex_unlock(&priv->htc_pm_lock);
1424 goto out;
1425 }
1426 mutex_unlock(&priv->htc_pm_lock);
1427
Joe Perches226afe62010-12-02 19:12:37 -08001428 ath_dbg(common, ATH_DBG_CONFIG,
1429 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301430 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301431 }
1432
Sujith23367762010-06-01 15:14:16 +05301433out:
Sujithfb9987d2010-03-17 14:25:25 +05301434 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301435 return 0;
1436}
1437
1438#define SUPPORTED_FILTERS \
1439 (FIF_PROMISC_IN_BSS | \
1440 FIF_ALLMULTI | \
1441 FIF_CONTROL | \
1442 FIF_PSPOLL | \
1443 FIF_OTHER_BSS | \
1444 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301445 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301446 FIF_FCSFAIL)
1447
1448static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1449 unsigned int changed_flags,
1450 unsigned int *total_flags,
1451 u64 multicast)
1452{
1453 struct ath9k_htc_priv *priv = hw->priv;
1454 u32 rfilt;
1455
1456 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301457 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301458
Sujithfb9987d2010-03-17 14:25:25 +05301459 changed_flags &= SUPPORTED_FILTERS;
1460 *total_flags &= SUPPORTED_FILTERS;
1461
1462 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301463 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301464 ath9k_hw_setrxfilter(priv->ah, rfilt);
1465
Joe Perches226afe62010-12-02 19:12:37 -08001466 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1467 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301468
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301469 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301470 mutex_unlock(&priv->mutex);
1471}
1472
Sujithabd984e2010-05-18 15:26:04 +05301473static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1474 struct ieee80211_vif *vif,
1475 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301476{
1477 struct ath9k_htc_priv *priv = hw->priv;
1478 int ret;
1479
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301480 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301481 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301482 ret = ath9k_htc_add_station(priv, vif, sta);
1483 if (!ret)
1484 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301485 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301486 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301487
1488 return ret;
1489}
1490
1491static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1492 struct ieee80211_vif *vif,
1493 struct ieee80211_sta *sta)
1494{
1495 struct ath9k_htc_priv *priv = hw->priv;
1496 int ret;
1497
1498 mutex_lock(&priv->mutex);
1499 ath9k_htc_ps_wakeup(priv);
1500 ret = ath9k_htc_remove_station(priv, vif, sta);
1501 ath9k_htc_ps_restore(priv);
1502 mutex_unlock(&priv->mutex);
1503
1504 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301505}
1506
1507static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1508 const struct ieee80211_tx_queue_params *params)
1509{
1510 struct ath9k_htc_priv *priv = hw->priv;
1511 struct ath_common *common = ath9k_hw_common(priv->ah);
1512 struct ath9k_tx_queue_info qi;
1513 int ret = 0, qnum;
1514
1515 if (queue >= WME_NUM_AC)
1516 return 0;
1517
1518 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301519 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301520
1521 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1522
1523 qi.tqi_aifs = params->aifs;
1524 qi.tqi_cwmin = params->cw_min;
1525 qi.tqi_cwmax = params->cw_max;
1526 qi.tqi_burstTime = params->txop;
1527
1528 qnum = get_hw_qnum(queue, priv->hwq_map);
1529
Joe Perches226afe62010-12-02 19:12:37 -08001530 ath_dbg(common, ATH_DBG_CONFIG,
1531 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1532 queue, qnum, params->aifs, params->cw_min,
1533 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301534
Sujithe1572c52010-03-24 13:42:13 +05301535 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301536 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001537 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301538 goto out;
1539 }
Sujithfb9987d2010-03-17 14:25:25 +05301540
Sujith764580f2010-06-01 15:14:19 +05301541 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001542 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301543 ath9k_htc_beaconq_config(priv);
1544out:
Sujithcb551df2010-06-01 15:14:12 +05301545 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301546 mutex_unlock(&priv->mutex);
1547
1548 return ret;
1549}
1550
1551static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1552 enum set_key_cmd cmd,
1553 struct ieee80211_vif *vif,
1554 struct ieee80211_sta *sta,
1555 struct ieee80211_key_conf *key)
1556{
1557 struct ath9k_htc_priv *priv = hw->priv;
1558 struct ath_common *common = ath9k_hw_common(priv->ah);
1559 int ret = 0;
1560
Sujithe1572c52010-03-24 13:42:13 +05301561 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301562 return -ENOSPC;
1563
1564 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001565 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301566 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301567
1568 switch (cmd) {
1569 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001570 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301571 if (ret >= 0) {
1572 key->hw_key_idx = ret;
1573 /* push IV and Michael MIC generation to stack */
1574 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001575 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301576 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001577 if (priv->ah->sw_mgmt_crypto &&
1578 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301579 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1580 ret = 0;
1581 }
1582 break;
1583 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001584 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301585 break;
1586 default:
1587 ret = -EINVAL;
1588 }
1589
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301590 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301591 mutex_unlock(&priv->mutex);
1592
1593 return ret;
1594}
1595
1596static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1597 struct ieee80211_vif *vif,
1598 struct ieee80211_bss_conf *bss_conf,
1599 u32 changed)
1600{
1601 struct ath9k_htc_priv *priv = hw->priv;
1602 struct ath_hw *ah = priv->ah;
1603 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301604 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301605
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
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301609 /*
1610 * Set the HW AID/BSSID only for the first station interface
1611 * or in IBSS mode.
1612 */
1613 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1614 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1615 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301616
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301617
1618 if (changed & BSS_CHANGED_ASSOC) {
1619 if (set_assoc) {
1620 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1621 bss_conf->assoc);
1622
1623 common->curaid = bss_conf->assoc ?
1624 bss_conf->aid : 0;
1625
1626 if (bss_conf->assoc)
1627 ath9k_htc_start_ani(priv);
1628 else
1629 ath9k_htc_stop_ani(priv);
1630 }
Sujithfb9987d2010-03-17 14:25:25 +05301631 }
1632
1633 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301634 if (set_assoc) {
1635 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1636 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301637
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301638 ath_dbg(common, ATH_DBG_CONFIG,
1639 "BSSID: %pM aid: 0x%x\n",
1640 common->curbssid, common->curaid);
1641 }
Sujithfb9987d2010-03-17 14:25:25 +05301642 }
1643
Sujith Manoharana5fae372011-02-21 07:49:53 +05301644 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1645 ath_dbg(common, ATH_DBG_CONFIG,
1646 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujithfb9987d2010-03-17 14:25:25 +05301647 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301648 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301649 }
1650
Sujith Manoharana5fae372011-02-21 07:49:53 +05301651 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1652 /*
1653 * Disable SWBA interrupt only if there are no
1654 * AP/IBSS interfaces.
1655 */
1656 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1657 ath_dbg(common, ATH_DBG_CONFIG,
1658 "Beacon disabled for BSS: %pM\n",
1659 bss_conf->bssid);
1660 priv->op_flags &= ~OP_ENABLE_BEACON;
1661 ath9k_htc_beacon_config(priv, vif);
1662 }
1663 }
1664
1665 if (changed & BSS_CHANGED_BEACON_INT) {
1666 /*
1667 * Reset the HW TSF for the first AP interface.
1668 */
1669 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1670 (priv->nvifs == 1) &&
1671 (priv->num_ap_vif == 1) &&
1672 (vif->type == NL80211_IFTYPE_AP)) {
1673 priv->op_flags |= OP_TSF_RESET;
1674 }
1675 ath_dbg(common, ATH_DBG_CONFIG,
1676 "Beacon interval changed for BSS: %pM\n",
1677 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301678 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301679 }
1680
Sujithfb9987d2010-03-17 14:25:25 +05301681 if (changed & BSS_CHANGED_ERP_SLOT) {
1682 if (bss_conf->use_short_slot)
1683 ah->slottime = 9;
1684 else
1685 ah->slottime = 20;
1686
1687 ath9k_hw_init_global_settings(ah);
1688 }
1689
Sujith2c76ef82010-05-17 12:01:18 +05301690 if (changed & BSS_CHANGED_HT)
1691 ath9k_htc_update_rate(priv, vif, bss_conf);
1692
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301693 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301694 mutex_unlock(&priv->mutex);
1695}
1696
1697static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1698{
1699 struct ath9k_htc_priv *priv = hw->priv;
1700 u64 tsf;
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 tsf = ath9k_hw_gettsf64(priv->ah);
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 return tsf;
1709}
1710
1711static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1712{
1713 struct ath9k_htc_priv *priv = hw->priv;
1714
1715 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301716 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301717 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301718 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301719 mutex_unlock(&priv->mutex);
1720}
1721
1722static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1723{
1724 struct ath9k_htc_priv *priv = hw->priv;
1725
1726 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301727 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301728 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301729 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301730 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301731}
1732
1733static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1734 struct ieee80211_vif *vif,
1735 enum ieee80211_ampdu_mlme_action action,
1736 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001737 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301738{
1739 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301740 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301741 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301742
Sujith Manoharan87df8952011-02-21 07:49:08 +05301743 mutex_lock(&priv->mutex);
1744
Sujithfb9987d2010-03-17 14:25:25 +05301745 switch (action) {
1746 case IEEE80211_AMPDU_RX_START:
1747 break;
1748 case IEEE80211_AMPDU_RX_STOP:
1749 break;
1750 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301751 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1752 if (!ret)
1753 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1754 break;
Sujithfb9987d2010-03-17 14:25:25 +05301755 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301756 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1757 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301758 break;
1759 case IEEE80211_AMPDU_TX_OPERATIONAL:
1760 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301761 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301762 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301763 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301764 break;
1765 default:
Joe Perches38002762010-12-02 19:12:36 -08001766 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301767 }
1768
Sujith Manoharan87df8952011-02-21 07:49:08 +05301769 mutex_unlock(&priv->mutex);
1770
Sujithd7ca2132010-06-15 10:24:37 +05301771 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301772}
1773
1774static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1775{
1776 struct ath9k_htc_priv *priv = hw->priv;
1777
1778 mutex_lock(&priv->mutex);
1779 spin_lock_bh(&priv->beacon_lock);
1780 priv->op_flags |= OP_SCANNING;
1781 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301782 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301783 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301784 mutex_unlock(&priv->mutex);
1785}
1786
1787static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1788{
1789 struct ath9k_htc_priv *priv = hw->priv;
1790
1791 mutex_lock(&priv->mutex);
1792 spin_lock_bh(&priv->beacon_lock);
1793 priv->op_flags &= ~OP_SCANNING;
1794 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301795 ath9k_htc_ps_wakeup(priv);
1796 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301797 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301798 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301799}
1800
1801static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1802{
1803 return 0;
1804}
1805
1806static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1807 u8 coverage_class)
1808{
1809 struct ath9k_htc_priv *priv = hw->priv;
1810
1811 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301812 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301813 priv->ah->coverage_class = coverage_class;
1814 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301815 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301816 mutex_unlock(&priv->mutex);
1817}
1818
1819struct ieee80211_ops ath9k_htc_ops = {
1820 .tx = ath9k_htc_tx,
1821 .start = ath9k_htc_start,
1822 .stop = ath9k_htc_stop,
1823 .add_interface = ath9k_htc_add_interface,
1824 .remove_interface = ath9k_htc_remove_interface,
1825 .config = ath9k_htc_config,
1826 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301827 .sta_add = ath9k_htc_sta_add,
1828 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301829 .conf_tx = ath9k_htc_conf_tx,
1830 .bss_info_changed = ath9k_htc_bss_info_changed,
1831 .set_key = ath9k_htc_set_key,
1832 .get_tsf = ath9k_htc_get_tsf,
1833 .set_tsf = ath9k_htc_set_tsf,
1834 .reset_tsf = ath9k_htc_reset_tsf,
1835 .ampdu_action = ath9k_htc_ampdu_action,
1836 .sw_scan_start = ath9k_htc_sw_scan_start,
1837 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1838 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1839 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1840 .set_coverage_class = ath9k_htc_set_coverage_class,
1841};