blob: 9733580579a91123207b71d9fd721598caa0e047 [file] [log] [blame]
Sujithfb9987d2010-03-17 14:25:25 +05301/*
2 * Copyright (c) 2010 Atheros Communications Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "htc.h"
18
19#ifdef CONFIG_ATH9K_HTC_DEBUGFS
20static struct dentry *ath9k_debugfs_root;
21#endif
22
23/*************/
24/* Utilities */
25/*************/
26
Sujithfb9987d2010-03-17 14:25:25 +053027/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
28static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
29 struct ath9k_channel *ichan)
30{
31 enum htc_phymode mode;
32
33 mode = HTC_MODE_AUTO;
34
35 switch (ichan->chanmode) {
36 case CHANNEL_G:
37 case CHANNEL_G_HT20:
38 case CHANNEL_G_HT40PLUS:
39 case CHANNEL_G_HT40MINUS:
40 mode = HTC_MODE_11NG;
41 break;
42 case CHANNEL_A:
43 case CHANNEL_A_HT20:
44 case CHANNEL_A_HT40PLUS:
45 case CHANNEL_A_HT40MINUS:
46 mode = HTC_MODE_11NA;
47 break;
48 default:
49 break;
50 }
51
52 return mode;
53}
54
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053055bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
56 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053057{
58 bool ret;
59
60 mutex_lock(&priv->htc_pm_lock);
61 ret = ath9k_hw_setpower(priv->ah, mode);
62 mutex_unlock(&priv->htc_pm_lock);
63
64 return ret;
65}
66
67void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
68{
69 mutex_lock(&priv->htc_pm_lock);
70 if (++priv->ps_usecount != 1)
71 goto unlock;
72 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
73
74unlock:
75 mutex_unlock(&priv->htc_pm_lock);
76}
77
78void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
79{
80 mutex_lock(&priv->htc_pm_lock);
81 if (--priv->ps_usecount != 0)
82 goto unlock;
83
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053084 if (priv->ps_idle)
85 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
86 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053087 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053088
Vivek Natarajanbde748a2010-04-05 14:48:05 +053089unlock:
90 mutex_unlock(&priv->htc_pm_lock);
91}
92
93void ath9k_ps_work(struct work_struct *work)
94{
95 struct ath9k_htc_priv *priv =
96 container_of(work, struct ath9k_htc_priv,
97 ps_work);
98 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
99
100 /* The chip wakes up after receiving the first beacon
101 while network sleep is enabled. For the driver to
102 be in sync with the hw, set the chip to awake and
103 only then set it to sleep.
104 */
105 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
106}
107
Sujith Manoharan7c277342011-02-21 07:48:39 +0530108static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
109{
110 struct ath9k_htc_priv *priv = data;
111 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
112
113 if (bss_conf->assoc) {
114 priv->rearm_ani = true;
115 priv->reconfig_beacon = true;
116 }
117}
118
119static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
120{
121 priv->rearm_ani = false;
122 priv->reconfig_beacon = false;
123
124 ieee80211_iterate_active_interfaces_atomic(priv->hw,
125 ath9k_htc_vif_iter, priv);
126 if (priv->rearm_ani)
127 ath_start_ani(priv);
128
129 if (priv->reconfig_beacon) {
130 ath9k_htc_ps_wakeup(priv);
131 ath9k_htc_beacon_reconfig(priv);
132 ath9k_htc_ps_restore(priv);
133 }
134}
135
Sujith Manoharan585895c2011-02-21 07:48:46 +0530136static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
137{
138 struct ath9k_vif_iter_data *iter_data = data;
139 int i;
140
141 for (i = 0; i < ETH_ALEN; i++)
142 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
143}
144
145static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
146 struct ieee80211_vif *vif)
147{
148 struct ath_common *common = ath9k_hw_common(priv->ah);
149 struct ath9k_vif_iter_data iter_data;
150
151 /*
152 * Use the hardware MAC address as reference, the hardware uses it
153 * together with the BSSID mask when matching addresses.
154 */
155 iter_data.hw_macaddr = common->macaddr;
156 memset(&iter_data.mask, 0xff, ETH_ALEN);
157
158 if (vif)
159 ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
160
161 /* Get list of all active MAC addresses */
162 ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
163 &iter_data);
164
165 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
166 ath_hw_setbssidmask(common);
167}
168
Sujith Manoharanffbe7c82011-02-21 07:49:31 +0530169static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
170{
171 if (priv->num_ibss_vif)
172 priv->ah->opmode = NL80211_IFTYPE_ADHOC;
173 else if (priv->num_ap_vif)
174 priv->ah->opmode = NL80211_IFTYPE_AP;
175 else
176 priv->ah->opmode = NL80211_IFTYPE_STATION;
177
178 ath9k_hw_setopmode(priv->ah);
179}
180
Sujith Manoharan73908672010-12-28 14:28:27 +0530181void ath9k_htc_reset(struct ath9k_htc_priv *priv)
182{
183 struct ath_hw *ah = priv->ah;
184 struct ath_common *common = ath9k_hw_common(ah);
185 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530186 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530187 enum htc_phymode mode;
188 __be16 htc_mode;
189 u8 cmd_rsp;
190 int ret;
191
192 mutex_lock(&priv->mutex);
193 ath9k_htc_ps_wakeup(priv);
194
Sujith Manoharan7c277342011-02-21 07:48:39 +0530195 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujith Manoharan73908672010-12-28 14:28:27 +0530196 ieee80211_stop_queues(priv->hw);
197 htc_stop(priv->htc);
198 WMI_CMD(WMI_DISABLE_INTR_CMDID);
199 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
200 WMI_CMD(WMI_STOP_RECV_CMDID);
201
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530202 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530203 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
204 if (ret) {
205 ath_err(common,
206 "Unable to reset device (%u Mhz) reset status %d\n",
207 channel->center_freq, ret);
208 }
209
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530210 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
211 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530212
213 WMI_CMD(WMI_START_RECV_CMDID);
214 ath9k_host_rx_init(priv);
215
216 mode = ath9k_htc_get_curmode(priv, ah->curchan);
217 htc_mode = cpu_to_be16(mode);
218 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
219
220 WMI_CMD(WMI_ENABLE_INTR_CMDID);
221 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530222 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530223 ieee80211_wake_queues(priv->hw);
224
225 ath9k_htc_ps_restore(priv);
226 mutex_unlock(&priv->mutex);
227}
228
Sujithfb9987d2010-03-17 14:25:25 +0530229static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
230 struct ieee80211_hw *hw,
231 struct ath9k_channel *hchan)
232{
233 struct ath_hw *ah = priv->ah;
234 struct ath_common *common = ath9k_hw_common(ah);
235 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530236 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530237 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530238 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530239 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530240 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530241 u8 cmd_rsp;
242 int ret;
243
244 if (priv->op_flags & OP_INVALID)
245 return -EIO;
246
Sujith Manoharan039a0722010-12-28 14:28:37 +0530247 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530248
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530249 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530250 htc_stop(priv->htc);
251 WMI_CMD(WMI_DISABLE_INTR_CMDID);
252 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
253 WMI_CMD(WMI_STOP_RECV_CMDID);
254
Joe Perches226afe62010-12-02 19:12:37 -0800255 ath_dbg(common, ATH_DBG_CONFIG,
256 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
257 priv->ah->curchan->channel,
258 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
259 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530260
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530261 if (!fastcc)
262 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200263 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530264 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800265 ath_err(common,
266 "Unable to reset channel (%u Mhz) reset status %d\n",
267 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530268 goto err;
269 }
270
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530271 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
272 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530273
274 WMI_CMD(WMI_START_RECV_CMDID);
275 if (ret)
276 goto err;
277
278 ath9k_host_rx_init(priv);
279
280 mode = ath9k_htc_get_curmode(priv, hchan);
281 htc_mode = cpu_to_be16(mode);
282 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
283 if (ret)
284 goto err;
285
286 WMI_CMD(WMI_ENABLE_INTR_CMDID);
287 if (ret)
288 goto err;
289
290 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530291err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530292 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530293 return ret;
294}
295
Sujith Manoharana97b4782011-02-21 07:48:00 +0530296/*
297 * Monitor mode handling is a tad complicated because the firmware requires
298 * an interface to be created exclusively, while mac80211 doesn't associate
299 * an interface with the mode.
300 *
301 * So, for now, only one monitor interface can be configured.
302 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530303static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530304{
305 struct ath_common *common = ath9k_hw_common(priv->ah);
306 struct ath9k_htc_target_vif hvif;
307 int ret = 0;
308 u8 cmd_rsp;
309
Sujith Manoharancc721282011-01-03 21:22:18 +0530310 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
311 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530312 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530313 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
314 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530315 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530316}
317
318static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
319{
320 struct ath_common *common = ath9k_hw_common(priv->ah);
321 struct ath9k_htc_target_vif hvif;
322 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530323 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530324 u8 cmd_rsp;
325
Sujith Manoharana97b4782011-02-21 07:48:00 +0530326 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
327 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
328 ret = -ENOBUFS;
329 goto err_vif;
330 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530331
Sujith Manoharana97b4782011-02-21 07:48:00 +0530332 sta_idx = ffz(priv->sta_slot);
333 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
334 ret = -ENOBUFS;
335 goto err_vif;
336 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530337
338 /*
339 * Add an interface.
340 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530341 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
342 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
343
344 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530345 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530346
347 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
348 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530349 goto err_vif;
350
351 /*
352 * Assign the monitor interface index as a special case here.
353 * This is needed when the interface is brought down.
354 */
355 priv->mon_vif_idx = hvif.index;
356 priv->vif_slot |= (1 << hvif.index);
357
358 /*
359 * Set the hardware mode to monitor only if there are no
360 * other interfaces.
361 */
362 if (!priv->nvifs)
363 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530364
365 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530366
367 /*
368 * Associate a station with the interface for packet injection.
369 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530370 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
371
372 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
373
374 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530375 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530376 tsta.vif_index = hvif.index;
377 tsta.maxampdu = 0xffff;
378
379 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
380 if (ret) {
381 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530382 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530383 }
384
Sujith Manoharana97b4782011-02-21 07:48:00 +0530385 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530386 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530387 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530388 priv->ah->is_monitoring = true;
389
Sujith Manoharana97b4782011-02-21 07:48:00 +0530390 ath_dbg(common, ATH_DBG_CONFIG,
391 "Attached a monitor interface at idx: %d, sta idx: %d\n",
392 priv->mon_vif_idx, sta_idx);
393
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530394 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530395
Sujith Manoharana97b4782011-02-21 07:48:00 +0530396err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530397 /*
398 * Remove the interface from the target.
399 */
400 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530401err_vif:
402 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
403
Sujith Manoharancc721282011-01-03 21:22:18 +0530404 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530405}
406
407static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
408{
409 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530410 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530411 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530412
Sujith Manoharancc721282011-01-03 21:22:18 +0530413 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530414
Sujith Manoharana97b4782011-02-21 07:48:00 +0530415 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530416
417 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
418 if (ret) {
419 ath_err(common, "Unable to remove station entry for monitor mode\n");
420 return ret;
421 }
422
Sujith Manoharana97b4782011-02-21 07:48:00 +0530423 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530424 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530425 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530426
Sujith Manoharana97b4782011-02-21 07:48:00 +0530427 ath_dbg(common, ATH_DBG_CONFIG,
428 "Removed a monitor interface at idx: %d, sta idx: %d\n",
429 priv->mon_vif_idx, sta_idx);
430
Sujith Manoharancc721282011-01-03 21:22:18 +0530431 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530432}
433
Sujithfb9987d2010-03-17 14:25:25 +0530434static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
435 struct ieee80211_vif *vif,
436 struct ieee80211_sta *sta)
437{
438 struct ath_common *common = ath9k_hw_common(priv->ah);
439 struct ath9k_htc_target_sta tsta;
440 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
441 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530442 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530443 u8 cmd_rsp;
444
445 if (priv->nstations >= ATH9K_HTC_MAX_STA)
446 return -ENOBUFS;
447
Sujith Manoharana97b4782011-02-21 07:48:00 +0530448 sta_idx = ffz(priv->sta_slot);
449 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
450 return -ENOBUFS;
451
Sujithfb9987d2010-03-17 14:25:25 +0530452 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
453
454 if (sta) {
455 ista = (struct ath9k_htc_sta *) sta->drv_priv;
456 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
457 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
458 tsta.associd = common->curaid;
459 tsta.is_vif_sta = 0;
460 tsta.valid = true;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530461 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530462 } else {
463 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
464 tsta.is_vif_sta = 1;
465 }
466
Sujith Manoharana97b4782011-02-21 07:48:00 +0530467 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530468 tsta.vif_index = avp->index;
469 tsta.maxampdu = 0xffff;
470 if (sta && sta->ht_cap.ht_supported)
471 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
472
473 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
474 if (ret) {
475 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800476 ath_err(common,
477 "Unable to add station entry for: %pM\n",
478 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530479 return ret;
480 }
481
Sujith Manoharana97b4782011-02-21 07:48:00 +0530482 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800483 ath_dbg(common, ATH_DBG_CONFIG,
484 "Added a station entry for: %pM (idx: %d)\n",
485 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530486 } else {
487 ath_dbg(common, ATH_DBG_CONFIG,
488 "Added a station entry for VIF %d (idx: %d)\n",
489 avp->index, tsta.sta_index);
490 }
Sujithfb9987d2010-03-17 14:25:25 +0530491
Sujith Manoharana97b4782011-02-21 07:48:00 +0530492 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530493 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530494 if (!sta)
495 priv->vif_sta_pos[avp->index] = sta_idx;
496
Sujithfb9987d2010-03-17 14:25:25 +0530497 return 0;
498}
499
500static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
501 struct ieee80211_vif *vif,
502 struct ieee80211_sta *sta)
503{
504 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530505 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530506 struct ath9k_htc_sta *ista;
507 int ret;
508 u8 cmd_rsp, sta_idx;
509
510 if (sta) {
511 ista = (struct ath9k_htc_sta *) sta->drv_priv;
512 sta_idx = ista->index;
513 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530514 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530515 }
516
517 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
518 if (ret) {
519 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800520 ath_err(common,
521 "Unable to remove station entry for: %pM\n",
522 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530523 return ret;
524 }
525
Sujith Manoharana97b4782011-02-21 07:48:00 +0530526 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800527 ath_dbg(common, ATH_DBG_CONFIG,
528 "Removed a station entry for: %pM (idx: %d)\n",
529 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530530 } else {
531 ath_dbg(common, ATH_DBG_CONFIG,
532 "Removed a station entry for VIF %d (idx: %d)\n",
533 avp->index, sta_idx);
534 }
Sujithfb9987d2010-03-17 14:25:25 +0530535
Sujith Manoharana97b4782011-02-21 07:48:00 +0530536 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530537 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530538
Sujithfb9987d2010-03-17 14:25:25 +0530539 return 0;
540}
541
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530542int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530543{
544 struct ath9k_htc_cap_target tcap;
545 int ret;
546 u8 cmd_rsp;
547
548 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
549
550 /* FIXME: Values are hardcoded */
551 tcap.flags = 0x240c40;
552 tcap.flags_ext = 0x80601000;
553 tcap.ampdu_limit = 0xffff0000;
554 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530555 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530556 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530557 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530558
559 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
560
561 return ret;
562}
563
Sujith0d425a72010-05-17 12:01:16 +0530564static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
565 struct ieee80211_sta *sta,
566 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530567{
Sujithfb9987d2010-03-17 14:25:25 +0530568 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
569 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530570 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530571 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530572
Sujithea46e642010-06-02 15:53:50 +0530573 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530574
575 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
576 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530577 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530578 = (sband->bitrates[i].bitrate * 2) / 10;
579 j++;
580 }
581 }
Sujith0d425a72010-05-17 12:01:16 +0530582 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530583
584 if (sta->ht_cap.ht_supported) {
585 for (i = 0, j = 0; i < 77; i++) {
586 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530587 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530588 if (j == ATH_HTC_RATE_MAX)
589 break;
590 }
Sujith0d425a72010-05-17 12:01:16 +0530591 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530592
593 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200594 if (sta->ht_cap.mcs.rx_mask[1])
595 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530596 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
597 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530598 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530599 if (conf_is_ht40(&priv->hw->conf) &&
600 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530601 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530602 else if (conf_is_ht20(&priv->hw->conf) &&
603 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
604 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530605 }
606
Sujith0d425a72010-05-17 12:01:16 +0530607 trate->sta_index = ista->index;
608 trate->isnew = 1;
609 trate->capflags = cpu_to_be32(caps);
610}
Sujithfb9987d2010-03-17 14:25:25 +0530611
Sujith0d425a72010-05-17 12:01:16 +0530612static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
613 struct ath9k_htc_target_rate *trate)
614{
615 struct ath_common *common = ath9k_hw_common(priv->ah);
616 int ret;
617 u8 cmd_rsp;
618
619 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530620 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800621 ath_err(common,
622 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530623 }
624
Sujith0d425a72010-05-17 12:01:16 +0530625 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530626}
627
Sujith0d425a72010-05-17 12:01:16 +0530628static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
629 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530630{
Sujithfb9987d2010-03-17 14:25:25 +0530631 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530632 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530633 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530634
Sujith0d425a72010-05-17 12:01:16 +0530635 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
636 ath9k_htc_setup_rate(priv, sta, &trate);
637 ret = ath9k_htc_send_rate_cmd(priv, &trate);
638 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800639 ath_dbg(common, ATH_DBG_CONFIG,
640 "Updated target sta: %pM, rate caps: 0x%X\n",
641 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530642}
643
Sujith2c76ef82010-05-17 12:01:18 +0530644static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
645 struct ieee80211_vif *vif,
646 struct ieee80211_bss_conf *bss_conf)
647{
648 struct ath_common *common = ath9k_hw_common(priv->ah);
649 struct ath9k_htc_target_rate trate;
650 struct ieee80211_sta *sta;
651 int ret;
652
653 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
654
655 rcu_read_lock();
656 sta = ieee80211_find_sta(vif, bss_conf->bssid);
657 if (!sta) {
658 rcu_read_unlock();
659 return;
660 }
661 ath9k_htc_setup_rate(priv, sta, &trate);
662 rcu_read_unlock();
663
664 ret = ath9k_htc_send_rate_cmd(priv, &trate);
665 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800666 ath_dbg(common, ATH_DBG_CONFIG,
667 "Updated target sta: %pM, rate caps: 0x%X\n",
668 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530669}
670
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400671static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
672 struct ieee80211_vif *vif,
673 struct ieee80211_sta *sta,
674 enum ieee80211_ampdu_mlme_action action,
675 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530676{
677 struct ath_common *common = ath9k_hw_common(priv->ah);
678 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200679 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530680 int ret = 0;
681 u8 cmd_rsp;
682
Dan Carpenter0730d112010-05-08 18:24:02 +0200683 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530684 return -EINVAL;
685
Sujithfb9987d2010-03-17 14:25:25 +0530686 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530687 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530688
Sujithef98c3c2010-03-29 16:07:11 +0530689 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530690 aggr.tidno = tid & 0xf;
691 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530692
Sujithfb9987d2010-03-17 14:25:25 +0530693 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
694 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800695 ath_dbg(common, ATH_DBG_CONFIG,
696 "Unable to %s TX aggregation for (%pM, %d)\n",
697 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530698 else
Joe Perches226afe62010-12-02 19:12:37 -0800699 ath_dbg(common, ATH_DBG_CONFIG,
700 "%s TX aggregation for (%pM, %d)\n",
701 (aggr.aggr_enable) ? "Starting" : "Stopping",
702 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530703
704 spin_lock_bh(&priv->tx_lock);
705 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
706 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530707
708 return ret;
709}
710
Sujithfb9987d2010-03-17 14:25:25 +0530711/*********/
712/* DEBUG */
713/*********/
714
715#ifdef CONFIG_ATH9K_HTC_DEBUGFS
716
717static int ath9k_debugfs_open(struct inode *inode, struct file *file)
718{
719 file->private_data = inode->i_private;
720 return 0;
721}
722
723static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
724 size_t count, loff_t *ppos)
725{
Joe Perches57674302010-07-12 13:50:06 -0700726 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530727 struct ath9k_htc_target_stats cmd_rsp;
728 char buf[512];
729 unsigned int len = 0;
730 int ret = 0;
731
732 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
733
734 WMI_CMD(WMI_TGT_STATS_CMDID);
735 if (ret)
736 return -EINVAL;
737
738
739 len += snprintf(buf + len, sizeof(buf) - len,
740 "%19s : %10u\n", "TX Short Retries",
741 be32_to_cpu(cmd_rsp.tx_shortretry));
742 len += snprintf(buf + len, sizeof(buf) - len,
743 "%19s : %10u\n", "TX Long Retries",
744 be32_to_cpu(cmd_rsp.tx_longretry));
745 len += snprintf(buf + len, sizeof(buf) - len,
746 "%19s : %10u\n", "TX Xretries",
747 be32_to_cpu(cmd_rsp.tx_xretries));
748 len += snprintf(buf + len, sizeof(buf) - len,
749 "%19s : %10u\n", "TX Unaggr. Xretries",
750 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
751 len += snprintf(buf + len, sizeof(buf) - len,
752 "%19s : %10u\n", "TX Xretries (HT)",
753 be32_to_cpu(cmd_rsp.ht_tx_xretries));
754 len += snprintf(buf + len, sizeof(buf) - len,
755 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
756
Dan Carpenter97460102010-07-22 10:50:28 +0200757 if (len > sizeof(buf))
758 len = sizeof(buf);
759
Sujithfb9987d2010-03-17 14:25:25 +0530760 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
761}
762
763static const struct file_operations fops_tgt_stats = {
764 .read = read_file_tgt_stats,
765 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200766 .owner = THIS_MODULE,
767 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530768};
769
770static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
771 size_t count, loff_t *ppos)
772{
Joe Perches57674302010-07-12 13:50:06 -0700773 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530774 char buf[512];
775 unsigned int len = 0;
776
777 len += snprintf(buf + len, sizeof(buf) - len,
778 "%20s : %10u\n", "Buffers queued",
779 priv->debug.tx_stats.buf_queued);
780 len += snprintf(buf + len, sizeof(buf) - len,
781 "%20s : %10u\n", "Buffers completed",
782 priv->debug.tx_stats.buf_completed);
783 len += snprintf(buf + len, sizeof(buf) - len,
784 "%20s : %10u\n", "SKBs queued",
785 priv->debug.tx_stats.skb_queued);
786 len += snprintf(buf + len, sizeof(buf) - len,
787 "%20s : %10u\n", "SKBs completed",
788 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530789 len += snprintf(buf + len, sizeof(buf) - len,
790 "%20s : %10u\n", "SKBs dropped",
791 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530792
Sujith2edb4582010-05-14 11:18:54 +0530793 len += snprintf(buf + len, sizeof(buf) - len,
794 "%20s : %10u\n", "BE queued",
795 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
796 len += snprintf(buf + len, sizeof(buf) - len,
797 "%20s : %10u\n", "BK queued",
798 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
799 len += snprintf(buf + len, sizeof(buf) - len,
800 "%20s : %10u\n", "VI queued",
801 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
802 len += snprintf(buf + len, sizeof(buf) - len,
803 "%20s : %10u\n", "VO queued",
804 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
805
Dan Carpenter97460102010-07-22 10:50:28 +0200806 if (len > sizeof(buf))
807 len = sizeof(buf);
808
Sujithfb9987d2010-03-17 14:25:25 +0530809 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
810}
811
812static const struct file_operations fops_xmit = {
813 .read = read_file_xmit,
814 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200815 .owner = THIS_MODULE,
816 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530817};
818
819static ssize_t read_file_recv(struct file *file, char __user *user_buf,
820 size_t count, loff_t *ppos)
821{
Joe Perches57674302010-07-12 13:50:06 -0700822 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530823 char buf[512];
824 unsigned int len = 0;
825
826 len += snprintf(buf + len, sizeof(buf) - len,
827 "%20s : %10u\n", "SKBs allocated",
828 priv->debug.rx_stats.skb_allocated);
829 len += snprintf(buf + len, sizeof(buf) - len,
830 "%20s : %10u\n", "SKBs completed",
831 priv->debug.rx_stats.skb_completed);
832 len += snprintf(buf + len, sizeof(buf) - len,
833 "%20s : %10u\n", "SKBs Dropped",
834 priv->debug.rx_stats.skb_dropped);
835
Dan Carpenter97460102010-07-22 10:50:28 +0200836 if (len > sizeof(buf))
837 len = sizeof(buf);
838
Sujithfb9987d2010-03-17 14:25:25 +0530839 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
840}
841
842static const struct file_operations fops_recv = {
843 .read = read_file_recv,
844 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200845 .owner = THIS_MODULE,
846 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530847};
848
Sujithe1572c52010-03-24 13:42:13 +0530849int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530850{
851 struct ath_common *common = ath9k_hw_common(ah);
852 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
853
854 if (!ath9k_debugfs_root)
855 return -ENOENT;
856
857 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
858 ath9k_debugfs_root);
859 if (!priv->debug.debugfs_phy)
860 goto err;
861
862 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
863 priv->debug.debugfs_phy,
864 priv, &fops_tgt_stats);
865 if (!priv->debug.debugfs_tgt_stats)
866 goto err;
867
868
869 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
870 priv->debug.debugfs_phy,
871 priv, &fops_xmit);
872 if (!priv->debug.debugfs_xmit)
873 goto err;
874
875 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
876 priv->debug.debugfs_phy,
877 priv, &fops_recv);
878 if (!priv->debug.debugfs_recv)
879 goto err;
880
881 return 0;
882
883err:
Sujithe1572c52010-03-24 13:42:13 +0530884 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530885 return -ENOMEM;
886}
887
Sujithe1572c52010-03-24 13:42:13 +0530888void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530889{
890 struct ath_common *common = ath9k_hw_common(ah);
891 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
892
893 debugfs_remove(priv->debug.debugfs_recv);
894 debugfs_remove(priv->debug.debugfs_xmit);
895 debugfs_remove(priv->debug.debugfs_tgt_stats);
896 debugfs_remove(priv->debug.debugfs_phy);
897}
898
Sujithe1572c52010-03-24 13:42:13 +0530899int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530900{
901 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
902 if (!ath9k_debugfs_root)
903 return -ENOENT;
904
905 return 0;
906}
907
Sujithe1572c52010-03-24 13:42:13 +0530908void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530909{
910 debugfs_remove(ath9k_debugfs_root);
911 ath9k_debugfs_root = NULL;
912}
913
914#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
915
916/*******/
917/* ANI */
918/*******/
919
Sujith Manoharan73908672010-12-28 14:28:27 +0530920void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530921{
922 struct ath_common *common = ath9k_hw_common(priv->ah);
923 unsigned long timestamp = jiffies_to_msecs(jiffies);
924
925 common->ani.longcal_timer = timestamp;
926 common->ani.shortcal_timer = timestamp;
927 common->ani.checkani_timer = timestamp;
928
929 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
930 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
931}
932
933void ath9k_ani_work(struct work_struct *work)
934{
935 struct ath9k_htc_priv *priv =
936 container_of(work, struct ath9k_htc_priv,
937 ath9k_ani_work.work);
938 struct ath_hw *ah = priv->ah;
939 struct ath_common *common = ath9k_hw_common(ah);
940 bool longcal = false;
941 bool shortcal = false;
942 bool aniflag = false;
943 unsigned int timestamp = jiffies_to_msecs(jiffies);
944 u32 cal_interval, short_cal_interval;
945
946 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
947
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530948 /* Only calibrate if awake */
949 if (ah->power_mode != ATH9K_PM_AWAKE)
950 goto set_timer;
951
Sujithfb9987d2010-03-17 14:25:25 +0530952 /* Long calibration runs independently of short calibration. */
953 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
954 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800955 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530956 common->ani.longcal_timer = timestamp;
957 }
958
959 /* Short calibration applies only while caldone is false */
960 if (!common->ani.caldone) {
961 if ((timestamp - common->ani.shortcal_timer) >=
962 short_cal_interval) {
963 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800964 ath_dbg(common, ATH_DBG_ANI,
965 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530966 common->ani.shortcal_timer = timestamp;
967 common->ani.resetcal_timer = timestamp;
968 }
969 } else {
970 if ((timestamp - common->ani.resetcal_timer) >=
971 ATH_RESTART_CALINTERVAL) {
972 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
973 if (common->ani.caldone)
974 common->ani.resetcal_timer = timestamp;
975 }
976 }
977
978 /* Verify whether we must check ANI */
979 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
980 aniflag = true;
981 common->ani.checkani_timer = timestamp;
982 }
983
984 /* Skip all processing if there's nothing to do. */
985 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530986
987 ath9k_htc_ps_wakeup(priv);
988
Sujithfb9987d2010-03-17 14:25:25 +0530989 /* Call ANI routine if necessary */
990 if (aniflag)
991 ath9k_hw_ani_monitor(ah, ah->curchan);
992
993 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200994 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530995 common->ani.caldone =
996 ath9k_hw_calibrate(ah, ah->curchan,
997 common->rx_chainmask,
998 longcal);
999
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301000 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301001 }
1002
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301003set_timer:
Sujithfb9987d2010-03-17 14:25:25 +05301004 /*
1005 * Set timer interval based on previous results.
1006 * The interval must be the shortest necessary to satisfy ANI,
1007 * short calibration and long calibration.
1008 */
1009 cal_interval = ATH_LONG_CALINTERVAL;
1010 if (priv->ah->config.enable_ani)
1011 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
1012 if (!common->ani.caldone)
1013 cal_interval = min(cal_interval, (u32)short_cal_interval);
1014
1015 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
1016 msecs_to_jiffies(cal_interval));
1017}
1018
Sujithfb9987d2010-03-17 14:25:25 +05301019/**********************/
1020/* mac80211 Callbacks */
1021/**********************/
1022
1023static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1024{
1025 struct ieee80211_hdr *hdr;
1026 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301027 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301028
1029 hdr = (struct ieee80211_hdr *) skb->data;
1030
1031 /* Add the padding after the header if this is not already done */
1032 padpos = ath9k_cmn_padpos(hdr->frame_control);
1033 padsize = padpos & 3;
1034 if (padsize && skb->len > padpos) {
1035 if (skb_headroom(skb) < padsize)
1036 return -1;
1037 skb_push(skb, padsize);
1038 memmove(skb->data, skb->data + padsize, padpos);
1039 }
1040
Sujith7757dfe2010-03-29 16:07:17 +05301041 ret = ath9k_htc_tx_start(priv, skb);
1042 if (ret != 0) {
1043 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -08001044 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1045 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +05301046 ieee80211_stop_queues(hw);
1047 spin_lock_bh(&priv->tx_lock);
1048 priv->tx_queues_stop = true;
1049 spin_unlock_bh(&priv->tx_lock);
1050 } else {
Joe Perches226afe62010-12-02 19:12:37 -08001051 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1052 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +05301053 }
Sujithfb9987d2010-03-17 14:25:25 +05301054 goto fail_tx;
1055 }
1056
1057 return 0;
1058
1059fail_tx:
1060 dev_kfree_skb_any(skb);
1061 return 0;
1062}
1063
Sujith881ac6a2010-06-01 15:14:11 +05301064static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301065{
1066 struct ath9k_htc_priv *priv = hw->priv;
1067 struct ath_hw *ah = priv->ah;
1068 struct ath_common *common = ath9k_hw_common(ah);
1069 struct ieee80211_channel *curchan = hw->conf.channel;
1070 struct ath9k_channel *init_channel;
1071 int ret = 0;
1072 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301073 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301074 u8 cmd_rsp;
1075
Sujith881ac6a2010-06-01 15:14:11 +05301076 mutex_lock(&priv->mutex);
1077
Joe Perches226afe62010-12-02 19:12:37 -08001078 ath_dbg(common, ATH_DBG_CONFIG,
1079 "Starting driver with initial channel: %d MHz\n",
1080 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301081
Sujith21d51302010-06-01 15:14:18 +05301082 /* Ensure that HW is awake before flushing RX */
1083 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1084 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1085
Sujithfb9987d2010-03-17 14:25:25 +05301086 /* setup initial channel */
1087 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1088
Sujithfb9987d2010-03-17 14:25:25 +05301089 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001090 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301091 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001092 ath_err(common,
1093 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1094 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301095 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301096 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301097 }
1098
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301099 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1100 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +05301101
1102 mode = ath9k_htc_get_curmode(priv, init_channel);
1103 htc_mode = cpu_to_be16(mode);
1104 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301105 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301106 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301107
1108 ath9k_host_rx_init(priv);
1109
Sujith Manoharan1057b752011-02-21 07:48:09 +05301110 ret = ath9k_htc_update_cap_target(priv);
1111 if (ret)
1112 ath_dbg(common, ATH_DBG_CONFIG,
1113 "Failed to update capability in target\n");
1114
Sujithfb9987d2010-03-17 14:25:25 +05301115 priv->op_flags &= ~OP_INVALID;
1116 htc_start(priv->htc);
1117
Sujith7757dfe2010-03-29 16:07:17 +05301118 spin_lock_bh(&priv->tx_lock);
1119 priv->tx_queues_stop = false;
1120 spin_unlock_bh(&priv->tx_lock);
1121
1122 ieee80211_wake_queues(hw);
1123
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301124 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1125 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1126 AR_STOMP_LOW_WLAN_WGHT);
1127 ath9k_hw_btcoex_enable(ah);
1128 ath_htc_resume_btcoex_work(priv);
1129 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301130 mutex_unlock(&priv->mutex);
1131
1132 return ret;
1133}
1134
Sujith881ac6a2010-06-01 15:14:11 +05301135static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301136{
1137 struct ath9k_htc_priv *priv = hw->priv;
1138 struct ath_hw *ah = priv->ah;
1139 struct ath_common *common = ath9k_hw_common(ah);
1140 int ret = 0;
1141 u8 cmd_rsp;
1142
Sujith881ac6a2010-06-01 15:14:11 +05301143 mutex_lock(&priv->mutex);
1144
Sujithfb9987d2010-03-17 14:25:25 +05301145 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001146 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301147 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301148 return;
1149 }
1150
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301151 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301152 htc_stop(priv->htc);
1153 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1154 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1155 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001156
1157 tasklet_kill(&priv->swba_tasklet);
1158 tasklet_kill(&priv->rx_tasklet);
1159 tasklet_kill(&priv->tx_tasklet);
1160
Sujithfb9987d2010-03-17 14:25:25 +05301161 skb_queue_purge(&priv->tx_queue);
1162
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001163 mutex_unlock(&priv->mutex);
1164
1165 /* Cancel all the running timers/work .. */
1166 cancel_work_sync(&priv->fatal_work);
1167 cancel_work_sync(&priv->ps_work);
1168 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Rajkumar Manoharan45655ba2011-01-31 23:47:42 +05301169 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001170 ath9k_led_stop_brightness(priv);
1171
1172 mutex_lock(&priv->mutex);
1173
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301174 if (ah->btcoex_hw.enabled) {
1175 ath9k_hw_btcoex_disable(ah);
1176 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1177 ath_htc_cancel_btcoex_work(priv);
1178 }
1179
Sujith Manoharana97b4782011-02-21 07:48:00 +05301180 /* Remove a monitor interface if it's present. */
1181 if (priv->ah->is_monitoring)
1182 ath9k_htc_remove_monitor_interface(priv);
1183
Sujithe9201f02010-06-01 15:14:17 +05301184 ath9k_hw_phy_disable(ah);
1185 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301186 ath9k_htc_ps_restore(priv);
1187 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1188
Sujithfb9987d2010-03-17 14:25:25 +05301189 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301190
Joe Perches226afe62010-12-02 19:12:37 -08001191 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301192 mutex_unlock(&priv->mutex);
1193}
1194
Sujithfb9987d2010-03-17 14:25:25 +05301195static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1196 struct ieee80211_vif *vif)
1197{
1198 struct ath9k_htc_priv *priv = hw->priv;
1199 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1200 struct ath_common *common = ath9k_hw_common(priv->ah);
1201 struct ath9k_htc_target_vif hvif;
1202 int ret = 0;
1203 u8 cmd_rsp;
1204
1205 mutex_lock(&priv->mutex);
1206
Sujith Manoharana97b4782011-02-21 07:48:00 +05301207 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301208 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301209 return -ENOBUFS;
1210 }
1211
1212 if (priv->num_ibss_vif ||
1213 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1214 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1215 mutex_unlock(&priv->mutex);
1216 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301217 }
1218
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301219 if (((vif->type == NL80211_IFTYPE_AP) ||
1220 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1221 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1222 ath_err(common, "Max. number of beaconing interfaces reached\n");
1223 mutex_unlock(&priv->mutex);
1224 return -ENOBUFS;
1225 }
1226
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301227 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301228 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1229 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1230
1231 switch (vif->type) {
1232 case NL80211_IFTYPE_STATION:
1233 hvif.opmode = cpu_to_be32(HTC_M_STA);
1234 break;
1235 case NL80211_IFTYPE_ADHOC:
1236 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1237 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301238 case NL80211_IFTYPE_AP:
1239 hvif.opmode = cpu_to_be32(HTC_M_HOSTAP);
1240 break;
Sujithfb9987d2010-03-17 14:25:25 +05301241 default:
Joe Perches38002762010-12-02 19:12:36 -08001242 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301243 "Interface type %d not yet supported\n", vif->type);
1244 ret = -EOPNOTSUPP;
1245 goto out;
1246 }
1247
Sujithfb9987d2010-03-17 14:25:25 +05301248 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301249 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301250 hvif.rtsthreshold = cpu_to_be16(2304);
1251 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1252 if (ret)
1253 goto out;
1254
Sujithfb9987d2010-03-17 14:25:25 +05301255 /*
1256 * We need a node in target to tx mgmt frames
1257 * before association.
1258 */
1259 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301260 if (ret) {
1261 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301262 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301263 }
Sujithfb9987d2010-03-17 14:25:25 +05301264
Sujith Manoharan585895c2011-02-21 07:48:46 +05301265 ath9k_htc_set_bssid_mask(priv, vif);
1266
Sujith Manoharana97b4782011-02-21 07:48:00 +05301267 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301268 priv->nvifs++;
Sujithfb9987d2010-03-17 14:25:25 +05301269 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301270
Sujith Manoharan0df83592011-02-21 07:49:15 +05301271 INC_VIF(priv, vif->type);
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301272 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301273
Sujith Manoharana97b4782011-02-21 07:48:00 +05301274 ath_dbg(common, ATH_DBG_CONFIG,
1275 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1276
Sujithfb9987d2010-03-17 14:25:25 +05301277out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301278 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301279 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301280
Sujithfb9987d2010-03-17 14:25:25 +05301281 return ret;
1282}
1283
1284static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1285 struct ieee80211_vif *vif)
1286{
1287 struct ath9k_htc_priv *priv = hw->priv;
1288 struct ath_common *common = ath9k_hw_common(priv->ah);
1289 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1290 struct ath9k_htc_target_vif hvif;
1291 int ret = 0;
1292 u8 cmd_rsp;
1293
Sujithfb9987d2010-03-17 14:25:25 +05301294 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301295 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301296
1297 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1298 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1299 hvif.index = avp->index;
1300 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1301 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301302 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301303
1304 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301305 priv->vif = NULL;
1306
Sujith Manoharan0df83592011-02-21 07:49:15 +05301307 DEC_VIF(priv, vif->type);
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301308 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301309
Sujith Manoharana97b4782011-02-21 07:48:00 +05301310 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1311
Sujithcb551df2010-06-01 15:14:12 +05301312 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301313 mutex_unlock(&priv->mutex);
1314}
1315
1316static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1317{
1318 struct ath9k_htc_priv *priv = hw->priv;
1319 struct ath_common *common = ath9k_hw_common(priv->ah);
1320 struct ieee80211_conf *conf = &hw->conf;
1321
1322 mutex_lock(&priv->mutex);
1323
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301324 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1325 bool enable_radio = false;
1326 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1327
Sujith23367762010-06-01 15:14:16 +05301328 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301329 if (!idle && priv->ps_idle)
1330 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301331 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301332 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301333
1334 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001335 ath_dbg(common, ATH_DBG_CONFIG,
1336 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301337 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1338 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301339 }
1340 }
1341
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301342 /*
1343 * Monitor interface should be added before
1344 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1345 */
1346 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301347 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1348 !priv->ah->is_monitoring)
1349 ath9k_htc_add_monitor_interface(priv);
1350 else if (priv->ah->is_monitoring)
1351 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301352 }
1353
Sujithfb9987d2010-03-17 14:25:25 +05301354 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1355 struct ieee80211_channel *curchan = hw->conf.channel;
1356 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301357
Joe Perches226afe62010-12-02 19:12:37 -08001358 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1359 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301360
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001361 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1362 hw->conf.channel,
1363 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301364
1365 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001366 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301367 mutex_unlock(&priv->mutex);
1368 return -EINVAL;
1369 }
1370
1371 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301372
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301373 if (changed & IEEE80211_CONF_CHANGE_PS) {
1374 if (conf->flags & IEEE80211_CONF_PS) {
1375 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1376 priv->ps_enabled = true;
1377 } else {
1378 priv->ps_enabled = false;
1379 cancel_work_sync(&priv->ps_work);
1380 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1381 }
1382 }
Sujithfb9987d2010-03-17 14:25:25 +05301383
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301384 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1385 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301386 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1387 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301388 }
1389
Sujith23367762010-06-01 15:14:16 +05301390 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1391 mutex_lock(&priv->htc_pm_lock);
1392 if (!priv->ps_idle) {
1393 mutex_unlock(&priv->htc_pm_lock);
1394 goto out;
1395 }
1396 mutex_unlock(&priv->htc_pm_lock);
1397
Joe Perches226afe62010-12-02 19:12:37 -08001398 ath_dbg(common, ATH_DBG_CONFIG,
1399 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301400 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301401 }
1402
Sujith23367762010-06-01 15:14:16 +05301403out:
Sujithfb9987d2010-03-17 14:25:25 +05301404 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301405 return 0;
1406}
1407
1408#define SUPPORTED_FILTERS \
1409 (FIF_PROMISC_IN_BSS | \
1410 FIF_ALLMULTI | \
1411 FIF_CONTROL | \
1412 FIF_PSPOLL | \
1413 FIF_OTHER_BSS | \
1414 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301415 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301416 FIF_FCSFAIL)
1417
1418static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1419 unsigned int changed_flags,
1420 unsigned int *total_flags,
1421 u64 multicast)
1422{
1423 struct ath9k_htc_priv *priv = hw->priv;
1424 u32 rfilt;
1425
1426 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301427 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301428
Sujithfb9987d2010-03-17 14:25:25 +05301429 changed_flags &= SUPPORTED_FILTERS;
1430 *total_flags &= SUPPORTED_FILTERS;
1431
1432 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301433 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301434 ath9k_hw_setrxfilter(priv->ah, rfilt);
1435
Joe Perches226afe62010-12-02 19:12:37 -08001436 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1437 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301438
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301439 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301440 mutex_unlock(&priv->mutex);
1441}
1442
Sujithabd984e2010-05-18 15:26:04 +05301443static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1444 struct ieee80211_vif *vif,
1445 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301446{
1447 struct ath9k_htc_priv *priv = hw->priv;
1448 int ret;
1449
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301450 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301451 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301452 ret = ath9k_htc_add_station(priv, vif, sta);
1453 if (!ret)
1454 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301455 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301456 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301457
1458 return ret;
1459}
1460
1461static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1462 struct ieee80211_vif *vif,
1463 struct ieee80211_sta *sta)
1464{
1465 struct ath9k_htc_priv *priv = hw->priv;
1466 int ret;
1467
1468 mutex_lock(&priv->mutex);
1469 ath9k_htc_ps_wakeup(priv);
1470 ret = ath9k_htc_remove_station(priv, vif, sta);
1471 ath9k_htc_ps_restore(priv);
1472 mutex_unlock(&priv->mutex);
1473
1474 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301475}
1476
1477static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1478 const struct ieee80211_tx_queue_params *params)
1479{
1480 struct ath9k_htc_priv *priv = hw->priv;
1481 struct ath_common *common = ath9k_hw_common(priv->ah);
1482 struct ath9k_tx_queue_info qi;
1483 int ret = 0, qnum;
1484
1485 if (queue >= WME_NUM_AC)
1486 return 0;
1487
1488 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301489 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301490
1491 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1492
1493 qi.tqi_aifs = params->aifs;
1494 qi.tqi_cwmin = params->cw_min;
1495 qi.tqi_cwmax = params->cw_max;
1496 qi.tqi_burstTime = params->txop;
1497
1498 qnum = get_hw_qnum(queue, priv->hwq_map);
1499
Joe Perches226afe62010-12-02 19:12:37 -08001500 ath_dbg(common, ATH_DBG_CONFIG,
1501 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1502 queue, qnum, params->aifs, params->cw_min,
1503 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301504
Sujithe1572c52010-03-24 13:42:13 +05301505 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301506 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001507 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301508 goto out;
1509 }
Sujithfb9987d2010-03-17 14:25:25 +05301510
Sujith764580f2010-06-01 15:14:19 +05301511 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001512 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301513 ath9k_htc_beaconq_config(priv);
1514out:
Sujithcb551df2010-06-01 15:14:12 +05301515 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301516 mutex_unlock(&priv->mutex);
1517
1518 return ret;
1519}
1520
1521static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1522 enum set_key_cmd cmd,
1523 struct ieee80211_vif *vif,
1524 struct ieee80211_sta *sta,
1525 struct ieee80211_key_conf *key)
1526{
1527 struct ath9k_htc_priv *priv = hw->priv;
1528 struct ath_common *common = ath9k_hw_common(priv->ah);
1529 int ret = 0;
1530
Sujithe1572c52010-03-24 13:42:13 +05301531 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301532 return -ENOSPC;
1533
1534 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001535 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301536 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301537
1538 switch (cmd) {
1539 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001540 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301541 if (ret >= 0) {
1542 key->hw_key_idx = ret;
1543 /* push IV and Michael MIC generation to stack */
1544 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001545 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301546 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001547 if (priv->ah->sw_mgmt_crypto &&
1548 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301549 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1550 ret = 0;
1551 }
1552 break;
1553 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001554 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301555 break;
1556 default:
1557 ret = -EINVAL;
1558 }
1559
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301560 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301561 mutex_unlock(&priv->mutex);
1562
1563 return ret;
1564}
1565
1566static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1567 struct ieee80211_vif *vif,
1568 struct ieee80211_bss_conf *bss_conf,
1569 u32 changed)
1570{
1571 struct ath9k_htc_priv *priv = hw->priv;
1572 struct ath_hw *ah = priv->ah;
1573 struct ath_common *common = ath9k_hw_common(ah);
1574
1575 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301576 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301577
1578 if (changed & BSS_CHANGED_ASSOC) {
1579 common->curaid = bss_conf->assoc ?
1580 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001581 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301582 bss_conf->assoc);
1583
Sujith Manoharan7c277342011-02-21 07:48:39 +05301584 if (bss_conf->assoc)
Sujithfb9987d2010-03-17 14:25:25 +05301585 ath_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301586 else
Sujithfb9987d2010-03-17 14:25:25 +05301587 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301588 }
1589
1590 if (changed & BSS_CHANGED_BSSID) {
1591 /* Set BSSID */
1592 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1593 ath9k_hw_write_associd(ah);
1594
Joe Perches226afe62010-12-02 19:12:37 -08001595 ath_dbg(common, ATH_DBG_CONFIG,
1596 "BSSID: %pM aid: 0x%x\n",
1597 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301598 }
1599
1600 if ((changed & BSS_CHANGED_BEACON_INT) ||
1601 (changed & BSS_CHANGED_BEACON) ||
1602 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1603 bss_conf->enable_beacon)) {
1604 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301605 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301606 }
1607
Sujithfb9987d2010-03-17 14:25:25 +05301608 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1609 !bss_conf->enable_beacon) {
1610 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301611 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301612 }
1613
Sujithfb9987d2010-03-17 14:25:25 +05301614 if (changed & BSS_CHANGED_ERP_SLOT) {
1615 if (bss_conf->use_short_slot)
1616 ah->slottime = 9;
1617 else
1618 ah->slottime = 20;
1619
1620 ath9k_hw_init_global_settings(ah);
1621 }
1622
Sujith2c76ef82010-05-17 12:01:18 +05301623 if (changed & BSS_CHANGED_HT)
1624 ath9k_htc_update_rate(priv, vif, bss_conf);
1625
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301626 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301627 mutex_unlock(&priv->mutex);
1628}
1629
1630static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1631{
1632 struct ath9k_htc_priv *priv = hw->priv;
1633 u64 tsf;
1634
1635 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301636 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301637 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301638 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301639 mutex_unlock(&priv->mutex);
1640
1641 return tsf;
1642}
1643
1644static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1645{
1646 struct ath9k_htc_priv *priv = hw->priv;
1647
1648 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301649 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301650 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301651 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301652 mutex_unlock(&priv->mutex);
1653}
1654
1655static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1656{
1657 struct ath9k_htc_priv *priv = hw->priv;
1658
1659 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301660 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301661 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301662 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301663 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301664}
1665
1666static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1667 struct ieee80211_vif *vif,
1668 enum ieee80211_ampdu_mlme_action action,
1669 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001670 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301671{
1672 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301673 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301674 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301675
Sujith Manoharan87df8952011-02-21 07:49:08 +05301676 mutex_lock(&priv->mutex);
1677
Sujithfb9987d2010-03-17 14:25:25 +05301678 switch (action) {
1679 case IEEE80211_AMPDU_RX_START:
1680 break;
1681 case IEEE80211_AMPDU_RX_STOP:
1682 break;
1683 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301684 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1685 if (!ret)
1686 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1687 break;
Sujithfb9987d2010-03-17 14:25:25 +05301688 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301689 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1690 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301691 break;
1692 case IEEE80211_AMPDU_TX_OPERATIONAL:
1693 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301694 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301695 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301696 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301697 break;
1698 default:
Joe Perches38002762010-12-02 19:12:36 -08001699 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301700 }
1701
Sujith Manoharan87df8952011-02-21 07:49:08 +05301702 mutex_unlock(&priv->mutex);
1703
Sujithd7ca2132010-06-15 10:24:37 +05301704 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301705}
1706
1707static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1708{
1709 struct ath9k_htc_priv *priv = hw->priv;
1710
1711 mutex_lock(&priv->mutex);
1712 spin_lock_bh(&priv->beacon_lock);
1713 priv->op_flags |= OP_SCANNING;
1714 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301715 cancel_work_sync(&priv->ps_work);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301716 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301717 mutex_unlock(&priv->mutex);
1718}
1719
1720static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1721{
1722 struct ath9k_htc_priv *priv = hw->priv;
1723
1724 mutex_lock(&priv->mutex);
1725 spin_lock_bh(&priv->beacon_lock);
1726 priv->op_flags &= ~OP_SCANNING;
1727 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301728 ath9k_htc_ps_wakeup(priv);
1729 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301730 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301731 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301732}
1733
1734static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1735{
1736 return 0;
1737}
1738
1739static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1740 u8 coverage_class)
1741{
1742 struct ath9k_htc_priv *priv = hw->priv;
1743
1744 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301745 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301746 priv->ah->coverage_class = coverage_class;
1747 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301748 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301749 mutex_unlock(&priv->mutex);
1750}
1751
1752struct ieee80211_ops ath9k_htc_ops = {
1753 .tx = ath9k_htc_tx,
1754 .start = ath9k_htc_start,
1755 .stop = ath9k_htc_stop,
1756 .add_interface = ath9k_htc_add_interface,
1757 .remove_interface = ath9k_htc_remove_interface,
1758 .config = ath9k_htc_config,
1759 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301760 .sta_add = ath9k_htc_sta_add,
1761 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301762 .conf_tx = ath9k_htc_conf_tx,
1763 .bss_info_changed = ath9k_htc_bss_info_changed,
1764 .set_key = ath9k_htc_set_key,
1765 .get_tsf = ath9k_htc_get_tsf,
1766 .set_tsf = ath9k_htc_set_tsf,
1767 .reset_tsf = ath9k_htc_reset_tsf,
1768 .ampdu_action = ath9k_htc_ampdu_action,
1769 .sw_scan_start = ath9k_htc_sw_scan_start,
1770 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1771 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1772 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1773 .set_coverage_class = ath9k_htc_set_coverage_class,
1774};