blob: 51a8c51510e812ac388d40d295eae4a9449a6465 [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 Manoharan73908672010-12-28 14:28:27 +0530169void ath9k_htc_reset(struct ath9k_htc_priv *priv)
170{
171 struct ath_hw *ah = priv->ah;
172 struct ath_common *common = ath9k_hw_common(ah);
173 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530174 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530175 enum htc_phymode mode;
176 __be16 htc_mode;
177 u8 cmd_rsp;
178 int ret;
179
180 mutex_lock(&priv->mutex);
181 ath9k_htc_ps_wakeup(priv);
182
Sujith Manoharan7c277342011-02-21 07:48:39 +0530183 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujith Manoharan73908672010-12-28 14:28:27 +0530184 ieee80211_stop_queues(priv->hw);
185 htc_stop(priv->htc);
186 WMI_CMD(WMI_DISABLE_INTR_CMDID);
187 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
188 WMI_CMD(WMI_STOP_RECV_CMDID);
189
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530190 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530191 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
192 if (ret) {
193 ath_err(common,
194 "Unable to reset device (%u Mhz) reset status %d\n",
195 channel->center_freq, ret);
196 }
197
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530198 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
199 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530200
201 WMI_CMD(WMI_START_RECV_CMDID);
202 ath9k_host_rx_init(priv);
203
204 mode = ath9k_htc_get_curmode(priv, ah->curchan);
205 htc_mode = cpu_to_be16(mode);
206 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
207
208 WMI_CMD(WMI_ENABLE_INTR_CMDID);
209 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530210 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530211 ieee80211_wake_queues(priv->hw);
212
213 ath9k_htc_ps_restore(priv);
214 mutex_unlock(&priv->mutex);
215}
216
Sujithfb9987d2010-03-17 14:25:25 +0530217static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
218 struct ieee80211_hw *hw,
219 struct ath9k_channel *hchan)
220{
221 struct ath_hw *ah = priv->ah;
222 struct ath_common *common = ath9k_hw_common(ah);
223 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530224 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530225 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530226 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530227 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530228 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530229 u8 cmd_rsp;
230 int ret;
231
232 if (priv->op_flags & OP_INVALID)
233 return -EIO;
234
Sujith Manoharan039a0722010-12-28 14:28:37 +0530235 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530236
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530237 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530238 htc_stop(priv->htc);
239 WMI_CMD(WMI_DISABLE_INTR_CMDID);
240 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
241 WMI_CMD(WMI_STOP_RECV_CMDID);
242
Joe Perches226afe62010-12-02 19:12:37 -0800243 ath_dbg(common, ATH_DBG_CONFIG,
244 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
245 priv->ah->curchan->channel,
246 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
247 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530248
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530249 if (!fastcc)
250 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200251 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530252 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800253 ath_err(common,
254 "Unable to reset channel (%u Mhz) reset status %d\n",
255 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530256 goto err;
257 }
258
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530259 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
260 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530261
262 WMI_CMD(WMI_START_RECV_CMDID);
263 if (ret)
264 goto err;
265
266 ath9k_host_rx_init(priv);
267
268 mode = ath9k_htc_get_curmode(priv, hchan);
269 htc_mode = cpu_to_be16(mode);
270 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
271 if (ret)
272 goto err;
273
274 WMI_CMD(WMI_ENABLE_INTR_CMDID);
275 if (ret)
276 goto err;
277
278 htc_start(priv->htc);
Sujithfb9987d2010-03-17 14:25:25 +0530279err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530280 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530281 return ret;
282}
283
Sujith Manoharana97b4782011-02-21 07:48:00 +0530284/*
285 * Monitor mode handling is a tad complicated because the firmware requires
286 * an interface to be created exclusively, while mac80211 doesn't associate
287 * an interface with the mode.
288 *
289 * So, for now, only one monitor interface can be configured.
290 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530291static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530292{
293 struct ath_common *common = ath9k_hw_common(priv->ah);
294 struct ath9k_htc_target_vif hvif;
295 int ret = 0;
296 u8 cmd_rsp;
297
Sujith Manoharancc721282011-01-03 21:22:18 +0530298 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
299 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530300 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530301 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
302 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530303 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530304}
305
306static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
307{
308 struct ath_common *common = ath9k_hw_common(priv->ah);
309 struct ath9k_htc_target_vif hvif;
310 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530311 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530312 u8 cmd_rsp;
313
Sujith Manoharana97b4782011-02-21 07:48:00 +0530314 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
315 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
316 ret = -ENOBUFS;
317 goto err_vif;
318 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530319
Sujith Manoharana97b4782011-02-21 07:48:00 +0530320 sta_idx = ffz(priv->sta_slot);
321 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
322 ret = -ENOBUFS;
323 goto err_vif;
324 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530325
326 /*
327 * Add an interface.
328 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530329 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
330 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
331
332 hvif.opmode = cpu_to_be32(HTC_M_MONITOR);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530333 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530334
335 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
336 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530337 goto err_vif;
338
339 /*
340 * Assign the monitor interface index as a special case here.
341 * This is needed when the interface is brought down.
342 */
343 priv->mon_vif_idx = hvif.index;
344 priv->vif_slot |= (1 << hvif.index);
345
346 /*
347 * Set the hardware mode to monitor only if there are no
348 * other interfaces.
349 */
350 if (!priv->nvifs)
351 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530352
353 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530354
355 /*
356 * Associate a station with the interface for packet injection.
357 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530358 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
359
360 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
361
362 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530363 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530364 tsta.vif_index = hvif.index;
365 tsta.maxampdu = 0xffff;
366
367 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
368 if (ret) {
369 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530370 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530371 }
372
Sujith Manoharana97b4782011-02-21 07:48:00 +0530373 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530374 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530375 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530376 priv->ah->is_monitoring = true;
377
Sujith Manoharana97b4782011-02-21 07:48:00 +0530378 ath_dbg(common, ATH_DBG_CONFIG,
379 "Attached a monitor interface at idx: %d, sta idx: %d\n",
380 priv->mon_vif_idx, sta_idx);
381
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530382 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530383
Sujith Manoharana97b4782011-02-21 07:48:00 +0530384err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530385 /*
386 * Remove the interface from the target.
387 */
388 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530389err_vif:
390 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
391
Sujith Manoharancc721282011-01-03 21:22:18 +0530392 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530393}
394
395static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
396{
397 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530398 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530399 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530400
Sujith Manoharancc721282011-01-03 21:22:18 +0530401 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530402
Sujith Manoharana97b4782011-02-21 07:48:00 +0530403 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530404
405 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
406 if (ret) {
407 ath_err(common, "Unable to remove station entry for monitor mode\n");
408 return ret;
409 }
410
Sujith Manoharana97b4782011-02-21 07:48:00 +0530411 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530412 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530413 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530414
Sujith Manoharana97b4782011-02-21 07:48:00 +0530415 ath_dbg(common, ATH_DBG_CONFIG,
416 "Removed a monitor interface at idx: %d, sta idx: %d\n",
417 priv->mon_vif_idx, sta_idx);
418
Sujith Manoharancc721282011-01-03 21:22:18 +0530419 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530420}
421
Sujithfb9987d2010-03-17 14:25:25 +0530422static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
423 struct ieee80211_vif *vif,
424 struct ieee80211_sta *sta)
425{
426 struct ath_common *common = ath9k_hw_common(priv->ah);
427 struct ath9k_htc_target_sta tsta;
428 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
429 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530430 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530431 u8 cmd_rsp;
432
433 if (priv->nstations >= ATH9K_HTC_MAX_STA)
434 return -ENOBUFS;
435
Sujith Manoharana97b4782011-02-21 07:48:00 +0530436 sta_idx = ffz(priv->sta_slot);
437 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
438 return -ENOBUFS;
439
Sujithfb9987d2010-03-17 14:25:25 +0530440 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
441
442 if (sta) {
443 ista = (struct ath9k_htc_sta *) sta->drv_priv;
444 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
445 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
446 tsta.associd = common->curaid;
447 tsta.is_vif_sta = 0;
448 tsta.valid = true;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530449 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530450 } else {
451 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
452 tsta.is_vif_sta = 1;
453 }
454
Sujith Manoharana97b4782011-02-21 07:48:00 +0530455 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530456 tsta.vif_index = avp->index;
457 tsta.maxampdu = 0xffff;
458 if (sta && sta->ht_cap.ht_supported)
459 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
460
461 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
462 if (ret) {
463 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800464 ath_err(common,
465 "Unable to add station entry for: %pM\n",
466 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530467 return ret;
468 }
469
Sujith Manoharana97b4782011-02-21 07:48:00 +0530470 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800471 ath_dbg(common, ATH_DBG_CONFIG,
472 "Added a station entry for: %pM (idx: %d)\n",
473 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530474 } else {
475 ath_dbg(common, ATH_DBG_CONFIG,
476 "Added a station entry for VIF %d (idx: %d)\n",
477 avp->index, tsta.sta_index);
478 }
Sujithfb9987d2010-03-17 14:25:25 +0530479
Sujith Manoharana97b4782011-02-21 07:48:00 +0530480 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530481 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530482 if (!sta)
483 priv->vif_sta_pos[avp->index] = sta_idx;
484
Sujithfb9987d2010-03-17 14:25:25 +0530485 return 0;
486}
487
488static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
489 struct ieee80211_vif *vif,
490 struct ieee80211_sta *sta)
491{
492 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530493 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530494 struct ath9k_htc_sta *ista;
495 int ret;
496 u8 cmd_rsp, sta_idx;
497
498 if (sta) {
499 ista = (struct ath9k_htc_sta *) sta->drv_priv;
500 sta_idx = ista->index;
501 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530502 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530503 }
504
505 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
506 if (ret) {
507 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800508 ath_err(common,
509 "Unable to remove station entry for: %pM\n",
510 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530511 return ret;
512 }
513
Sujith Manoharana97b4782011-02-21 07:48:00 +0530514 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800515 ath_dbg(common, ATH_DBG_CONFIG,
516 "Removed a station entry for: %pM (idx: %d)\n",
517 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530518 } else {
519 ath_dbg(common, ATH_DBG_CONFIG,
520 "Removed a station entry for VIF %d (idx: %d)\n",
521 avp->index, sta_idx);
522 }
Sujithfb9987d2010-03-17 14:25:25 +0530523
Sujith Manoharana97b4782011-02-21 07:48:00 +0530524 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530525 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530526
Sujithfb9987d2010-03-17 14:25:25 +0530527 return 0;
528}
529
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530530int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530531{
532 struct ath9k_htc_cap_target tcap;
533 int ret;
534 u8 cmd_rsp;
535
536 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
537
538 /* FIXME: Values are hardcoded */
539 tcap.flags = 0x240c40;
540 tcap.flags_ext = 0x80601000;
541 tcap.ampdu_limit = 0xffff0000;
542 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530543 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530544 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530545 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530546
547 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
548
549 return ret;
550}
551
Sujith0d425a72010-05-17 12:01:16 +0530552static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
553 struct ieee80211_sta *sta,
554 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530555{
Sujithfb9987d2010-03-17 14:25:25 +0530556 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
557 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530558 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530559 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530560
Sujithea46e642010-06-02 15:53:50 +0530561 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530562
563 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
564 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530565 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530566 = (sband->bitrates[i].bitrate * 2) / 10;
567 j++;
568 }
569 }
Sujith0d425a72010-05-17 12:01:16 +0530570 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530571
572 if (sta->ht_cap.ht_supported) {
573 for (i = 0, j = 0; i < 77; i++) {
574 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530575 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530576 if (j == ATH_HTC_RATE_MAX)
577 break;
578 }
Sujith0d425a72010-05-17 12:01:16 +0530579 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530580
581 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200582 if (sta->ht_cap.mcs.rx_mask[1])
583 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530584 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
585 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530586 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530587 if (conf_is_ht40(&priv->hw->conf) &&
588 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530589 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530590 else if (conf_is_ht20(&priv->hw->conf) &&
591 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
592 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530593 }
594
Sujith0d425a72010-05-17 12:01:16 +0530595 trate->sta_index = ista->index;
596 trate->isnew = 1;
597 trate->capflags = cpu_to_be32(caps);
598}
Sujithfb9987d2010-03-17 14:25:25 +0530599
Sujith0d425a72010-05-17 12:01:16 +0530600static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
601 struct ath9k_htc_target_rate *trate)
602{
603 struct ath_common *common = ath9k_hw_common(priv->ah);
604 int ret;
605 u8 cmd_rsp;
606
607 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530608 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800609 ath_err(common,
610 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530611 }
612
Sujith0d425a72010-05-17 12:01:16 +0530613 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530614}
615
Sujith0d425a72010-05-17 12:01:16 +0530616static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
617 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530618{
Sujithfb9987d2010-03-17 14:25:25 +0530619 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530620 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530621 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530622
Sujith0d425a72010-05-17 12:01:16 +0530623 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
624 ath9k_htc_setup_rate(priv, sta, &trate);
625 ret = ath9k_htc_send_rate_cmd(priv, &trate);
626 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800627 ath_dbg(common, ATH_DBG_CONFIG,
628 "Updated target sta: %pM, rate caps: 0x%X\n",
629 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530630}
631
Sujith2c76ef82010-05-17 12:01:18 +0530632static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
633 struct ieee80211_vif *vif,
634 struct ieee80211_bss_conf *bss_conf)
635{
636 struct ath_common *common = ath9k_hw_common(priv->ah);
637 struct ath9k_htc_target_rate trate;
638 struct ieee80211_sta *sta;
639 int ret;
640
641 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
642
643 rcu_read_lock();
644 sta = ieee80211_find_sta(vif, bss_conf->bssid);
645 if (!sta) {
646 rcu_read_unlock();
647 return;
648 }
649 ath9k_htc_setup_rate(priv, sta, &trate);
650 rcu_read_unlock();
651
652 ret = ath9k_htc_send_rate_cmd(priv, &trate);
653 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800654 ath_dbg(common, ATH_DBG_CONFIG,
655 "Updated target sta: %pM, rate caps: 0x%X\n",
656 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530657}
658
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400659static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
660 struct ieee80211_vif *vif,
661 struct ieee80211_sta *sta,
662 enum ieee80211_ampdu_mlme_action action,
663 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530664{
665 struct ath_common *common = ath9k_hw_common(priv->ah);
666 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200667 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530668 int ret = 0;
669 u8 cmd_rsp;
670
Dan Carpenter0730d112010-05-08 18:24:02 +0200671 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530672 return -EINVAL;
673
Sujithfb9987d2010-03-17 14:25:25 +0530674 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530675 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530676
Sujithef98c3c2010-03-29 16:07:11 +0530677 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530678 aggr.tidno = tid & 0xf;
679 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530680
Sujithfb9987d2010-03-17 14:25:25 +0530681 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
682 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800683 ath_dbg(common, ATH_DBG_CONFIG,
684 "Unable to %s TX aggregation for (%pM, %d)\n",
685 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530686 else
Joe Perches226afe62010-12-02 19:12:37 -0800687 ath_dbg(common, ATH_DBG_CONFIG,
688 "%s TX aggregation for (%pM, %d)\n",
689 (aggr.aggr_enable) ? "Starting" : "Stopping",
690 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530691
692 spin_lock_bh(&priv->tx_lock);
693 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
694 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530695
696 return ret;
697}
698
Sujithfb9987d2010-03-17 14:25:25 +0530699/*********/
700/* DEBUG */
701/*********/
702
703#ifdef CONFIG_ATH9K_HTC_DEBUGFS
704
705static int ath9k_debugfs_open(struct inode *inode, struct file *file)
706{
707 file->private_data = inode->i_private;
708 return 0;
709}
710
711static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf,
712 size_t count, loff_t *ppos)
713{
Joe Perches57674302010-07-12 13:50:06 -0700714 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530715 struct ath9k_htc_target_stats cmd_rsp;
716 char buf[512];
717 unsigned int len = 0;
718 int ret = 0;
719
720 memset(&cmd_rsp, 0, sizeof(cmd_rsp));
721
722 WMI_CMD(WMI_TGT_STATS_CMDID);
723 if (ret)
724 return -EINVAL;
725
726
727 len += snprintf(buf + len, sizeof(buf) - len,
728 "%19s : %10u\n", "TX Short Retries",
729 be32_to_cpu(cmd_rsp.tx_shortretry));
730 len += snprintf(buf + len, sizeof(buf) - len,
731 "%19s : %10u\n", "TX Long Retries",
732 be32_to_cpu(cmd_rsp.tx_longretry));
733 len += snprintf(buf + len, sizeof(buf) - len,
734 "%19s : %10u\n", "TX Xretries",
735 be32_to_cpu(cmd_rsp.tx_xretries));
736 len += snprintf(buf + len, sizeof(buf) - len,
737 "%19s : %10u\n", "TX Unaggr. Xretries",
738 be32_to_cpu(cmd_rsp.ht_txunaggr_xretry));
739 len += snprintf(buf + len, sizeof(buf) - len,
740 "%19s : %10u\n", "TX Xretries (HT)",
741 be32_to_cpu(cmd_rsp.ht_tx_xretries));
742 len += snprintf(buf + len, sizeof(buf) - len,
743 "%19s : %10u\n", "TX Rate", priv->debug.txrate);
744
Dan Carpenter97460102010-07-22 10:50:28 +0200745 if (len > sizeof(buf))
746 len = sizeof(buf);
747
Sujithfb9987d2010-03-17 14:25:25 +0530748 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
749}
750
751static const struct file_operations fops_tgt_stats = {
752 .read = read_file_tgt_stats,
753 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200754 .owner = THIS_MODULE,
755 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530756};
757
758static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
759 size_t count, loff_t *ppos)
760{
Joe Perches57674302010-07-12 13:50:06 -0700761 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530762 char buf[512];
763 unsigned int len = 0;
764
765 len += snprintf(buf + len, sizeof(buf) - len,
766 "%20s : %10u\n", "Buffers queued",
767 priv->debug.tx_stats.buf_queued);
768 len += snprintf(buf + len, sizeof(buf) - len,
769 "%20s : %10u\n", "Buffers completed",
770 priv->debug.tx_stats.buf_completed);
771 len += snprintf(buf + len, sizeof(buf) - len,
772 "%20s : %10u\n", "SKBs queued",
773 priv->debug.tx_stats.skb_queued);
774 len += snprintf(buf + len, sizeof(buf) - len,
775 "%20s : %10u\n", "SKBs completed",
776 priv->debug.tx_stats.skb_completed);
Sujitheac8e382010-04-16 11:54:00 +0530777 len += snprintf(buf + len, sizeof(buf) - len,
778 "%20s : %10u\n", "SKBs dropped",
779 priv->debug.tx_stats.skb_dropped);
Sujithfb9987d2010-03-17 14:25:25 +0530780
Sujith2edb4582010-05-14 11:18:54 +0530781 len += snprintf(buf + len, sizeof(buf) - len,
782 "%20s : %10u\n", "BE queued",
783 priv->debug.tx_stats.queue_stats[WME_AC_BE]);
784 len += snprintf(buf + len, sizeof(buf) - len,
785 "%20s : %10u\n", "BK queued",
786 priv->debug.tx_stats.queue_stats[WME_AC_BK]);
787 len += snprintf(buf + len, sizeof(buf) - len,
788 "%20s : %10u\n", "VI queued",
789 priv->debug.tx_stats.queue_stats[WME_AC_VI]);
790 len += snprintf(buf + len, sizeof(buf) - len,
791 "%20s : %10u\n", "VO queued",
792 priv->debug.tx_stats.queue_stats[WME_AC_VO]);
793
Dan Carpenter97460102010-07-22 10:50:28 +0200794 if (len > sizeof(buf))
795 len = sizeof(buf);
796
Sujithfb9987d2010-03-17 14:25:25 +0530797 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
798}
799
800static const struct file_operations fops_xmit = {
801 .read = read_file_xmit,
802 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200803 .owner = THIS_MODULE,
804 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530805};
806
807static ssize_t read_file_recv(struct file *file, char __user *user_buf,
808 size_t count, loff_t *ppos)
809{
Joe Perches57674302010-07-12 13:50:06 -0700810 struct ath9k_htc_priv *priv = file->private_data;
Sujithfb9987d2010-03-17 14:25:25 +0530811 char buf[512];
812 unsigned int len = 0;
813
814 len += snprintf(buf + len, sizeof(buf) - len,
815 "%20s : %10u\n", "SKBs allocated",
816 priv->debug.rx_stats.skb_allocated);
817 len += snprintf(buf + len, sizeof(buf) - len,
818 "%20s : %10u\n", "SKBs completed",
819 priv->debug.rx_stats.skb_completed);
820 len += snprintf(buf + len, sizeof(buf) - len,
821 "%20s : %10u\n", "SKBs Dropped",
822 priv->debug.rx_stats.skb_dropped);
823
Dan Carpenter97460102010-07-22 10:50:28 +0200824 if (len > sizeof(buf))
825 len = sizeof(buf);
826
Sujithfb9987d2010-03-17 14:25:25 +0530827 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
828}
829
830static const struct file_operations fops_recv = {
831 .read = read_file_recv,
832 .open = ath9k_debugfs_open,
Arnd Bergmann6038f372010-08-15 18:52:59 +0200833 .owner = THIS_MODULE,
834 .llseek = default_llseek,
Sujithfb9987d2010-03-17 14:25:25 +0530835};
836
Sujithe1572c52010-03-24 13:42:13 +0530837int ath9k_htc_init_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530838{
839 struct ath_common *common = ath9k_hw_common(ah);
840 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
841
842 if (!ath9k_debugfs_root)
843 return -ENOENT;
844
845 priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy),
846 ath9k_debugfs_root);
847 if (!priv->debug.debugfs_phy)
848 goto err;
849
850 priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR,
851 priv->debug.debugfs_phy,
852 priv, &fops_tgt_stats);
853 if (!priv->debug.debugfs_tgt_stats)
854 goto err;
855
856
857 priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR,
858 priv->debug.debugfs_phy,
859 priv, &fops_xmit);
860 if (!priv->debug.debugfs_xmit)
861 goto err;
862
863 priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR,
864 priv->debug.debugfs_phy,
865 priv, &fops_recv);
866 if (!priv->debug.debugfs_recv)
867 goto err;
868
869 return 0;
870
871err:
Sujithe1572c52010-03-24 13:42:13 +0530872 ath9k_htc_exit_debug(ah);
Sujithfb9987d2010-03-17 14:25:25 +0530873 return -ENOMEM;
874}
875
Sujithe1572c52010-03-24 13:42:13 +0530876void ath9k_htc_exit_debug(struct ath_hw *ah)
Sujithfb9987d2010-03-17 14:25:25 +0530877{
878 struct ath_common *common = ath9k_hw_common(ah);
879 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv;
880
881 debugfs_remove(priv->debug.debugfs_recv);
882 debugfs_remove(priv->debug.debugfs_xmit);
883 debugfs_remove(priv->debug.debugfs_tgt_stats);
884 debugfs_remove(priv->debug.debugfs_phy);
885}
886
Sujithe1572c52010-03-24 13:42:13 +0530887int ath9k_htc_debug_create_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530888{
889 ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL);
890 if (!ath9k_debugfs_root)
891 return -ENOENT;
892
893 return 0;
894}
895
Sujithe1572c52010-03-24 13:42:13 +0530896void ath9k_htc_debug_remove_root(void)
Sujithfb9987d2010-03-17 14:25:25 +0530897{
898 debugfs_remove(ath9k_debugfs_root);
899 ath9k_debugfs_root = NULL;
900}
901
902#endif /* CONFIG_ATH9K_HTC_DEBUGFS */
903
904/*******/
905/* ANI */
906/*******/
907
Sujith Manoharan73908672010-12-28 14:28:27 +0530908void ath_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530909{
910 struct ath_common *common = ath9k_hw_common(priv->ah);
911 unsigned long timestamp = jiffies_to_msecs(jiffies);
912
913 common->ani.longcal_timer = timestamp;
914 common->ani.shortcal_timer = timestamp;
915 common->ani.checkani_timer = timestamp;
916
917 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
918 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
919}
920
921void ath9k_ani_work(struct work_struct *work)
922{
923 struct ath9k_htc_priv *priv =
924 container_of(work, struct ath9k_htc_priv,
925 ath9k_ani_work.work);
926 struct ath_hw *ah = priv->ah;
927 struct ath_common *common = ath9k_hw_common(ah);
928 bool longcal = false;
929 bool shortcal = false;
930 bool aniflag = false;
931 unsigned int timestamp = jiffies_to_msecs(jiffies);
932 u32 cal_interval, short_cal_interval;
933
934 short_cal_interval = ATH_STA_SHORT_CALINTERVAL;
935
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530936 /* Only calibrate if awake */
937 if (ah->power_mode != ATH9K_PM_AWAKE)
938 goto set_timer;
939
Sujithfb9987d2010-03-17 14:25:25 +0530940 /* Long calibration runs independently of short calibration. */
941 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
942 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800943 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530944 common->ani.longcal_timer = timestamp;
945 }
946
947 /* Short calibration applies only while caldone is false */
948 if (!common->ani.caldone) {
949 if ((timestamp - common->ani.shortcal_timer) >=
950 short_cal_interval) {
951 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800952 ath_dbg(common, ATH_DBG_ANI,
953 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530954 common->ani.shortcal_timer = timestamp;
955 common->ani.resetcal_timer = timestamp;
956 }
957 } else {
958 if ((timestamp - common->ani.resetcal_timer) >=
959 ATH_RESTART_CALINTERVAL) {
960 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
961 if (common->ani.caldone)
962 common->ani.resetcal_timer = timestamp;
963 }
964 }
965
966 /* Verify whether we must check ANI */
967 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
968 aniflag = true;
969 common->ani.checkani_timer = timestamp;
970 }
971
972 /* Skip all processing if there's nothing to do. */
973 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530974
975 ath9k_htc_ps_wakeup(priv);
976
Sujithfb9987d2010-03-17 14:25:25 +0530977 /* Call ANI routine if necessary */
978 if (aniflag)
979 ath9k_hw_ani_monitor(ah, ah->curchan);
980
981 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200982 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530983 common->ani.caldone =
984 ath9k_hw_calibrate(ah, ah->curchan,
985 common->rx_chainmask,
986 longcal);
987
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530988 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530989 }
990
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530991set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530992 /*
993 * Set timer interval based on previous results.
994 * The interval must be the shortest necessary to satisfy ANI,
995 * short calibration and long calibration.
996 */
997 cal_interval = ATH_LONG_CALINTERVAL;
998 if (priv->ah->config.enable_ani)
999 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
1000 if (!common->ani.caldone)
1001 cal_interval = min(cal_interval, (u32)short_cal_interval);
1002
1003 ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work,
1004 msecs_to_jiffies(cal_interval));
1005}
1006
Sujithfb9987d2010-03-17 14:25:25 +05301007/**********************/
1008/* mac80211 Callbacks */
1009/**********************/
1010
1011static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
1012{
1013 struct ieee80211_hdr *hdr;
1014 struct ath9k_htc_priv *priv = hw->priv;
Sujith7757dfe2010-03-29 16:07:17 +05301015 int padpos, padsize, ret;
Sujithfb9987d2010-03-17 14:25:25 +05301016
1017 hdr = (struct ieee80211_hdr *) skb->data;
1018
1019 /* Add the padding after the header if this is not already done */
1020 padpos = ath9k_cmn_padpos(hdr->frame_control);
1021 padsize = padpos & 3;
1022 if (padsize && skb->len > padpos) {
1023 if (skb_headroom(skb) < padsize)
1024 return -1;
1025 skb_push(skb, padsize);
1026 memmove(skb->data, skb->data + padsize, padpos);
1027 }
1028
Sujith7757dfe2010-03-29 16:07:17 +05301029 ret = ath9k_htc_tx_start(priv, skb);
1030 if (ret != 0) {
1031 if (ret == -ENOMEM) {
Joe Perches226afe62010-12-02 19:12:37 -08001032 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1033 "Stopping TX queues\n");
Sujith7757dfe2010-03-29 16:07:17 +05301034 ieee80211_stop_queues(hw);
1035 spin_lock_bh(&priv->tx_lock);
1036 priv->tx_queues_stop = true;
1037 spin_unlock_bh(&priv->tx_lock);
1038 } else {
Joe Perches226afe62010-12-02 19:12:37 -08001039 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
1040 "Tx failed\n");
Sujith7757dfe2010-03-29 16:07:17 +05301041 }
Sujithfb9987d2010-03-17 14:25:25 +05301042 goto fail_tx;
1043 }
1044
1045 return 0;
1046
1047fail_tx:
1048 dev_kfree_skb_any(skb);
1049 return 0;
1050}
1051
Sujith881ac6a2010-06-01 15:14:11 +05301052static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301053{
1054 struct ath9k_htc_priv *priv = hw->priv;
1055 struct ath_hw *ah = priv->ah;
1056 struct ath_common *common = ath9k_hw_common(ah);
1057 struct ieee80211_channel *curchan = hw->conf.channel;
1058 struct ath9k_channel *init_channel;
1059 int ret = 0;
1060 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +05301061 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +05301062 u8 cmd_rsp;
1063
Sujith881ac6a2010-06-01 15:14:11 +05301064 mutex_lock(&priv->mutex);
1065
Joe Perches226afe62010-12-02 19:12:37 -08001066 ath_dbg(common, ATH_DBG_CONFIG,
1067 "Starting driver with initial channel: %d MHz\n",
1068 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301069
Sujith21d51302010-06-01 15:14:18 +05301070 /* Ensure that HW is awake before flushing RX */
1071 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1072 WMI_CMD(WMI_FLUSH_RECV_CMDID);
1073
Sujithfb9987d2010-03-17 14:25:25 +05301074 /* setup initial channel */
1075 init_channel = ath9k_cmn_get_curchannel(hw, ah);
1076
Sujithfb9987d2010-03-17 14:25:25 +05301077 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +02001078 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +05301079 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001080 ath_err(common,
1081 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
1082 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +05301083 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301084 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301085 }
1086
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301087 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
1088 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +05301089
1090 mode = ath9k_htc_get_curmode(priv, init_channel);
1091 htc_mode = cpu_to_be16(mode);
1092 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +05301093 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301094 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +05301095
1096 ath9k_host_rx_init(priv);
1097
Sujith Manoharan1057b752011-02-21 07:48:09 +05301098 ret = ath9k_htc_update_cap_target(priv);
1099 if (ret)
1100 ath_dbg(common, ATH_DBG_CONFIG,
1101 "Failed to update capability in target\n");
1102
Sujithfb9987d2010-03-17 14:25:25 +05301103 priv->op_flags &= ~OP_INVALID;
1104 htc_start(priv->htc);
1105
Sujith7757dfe2010-03-29 16:07:17 +05301106 spin_lock_bh(&priv->tx_lock);
1107 priv->tx_queues_stop = false;
1108 spin_unlock_bh(&priv->tx_lock);
1109
1110 ieee80211_wake_queues(hw);
1111
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301112 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
1113 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
1114 AR_STOMP_LOW_WLAN_WGHT);
1115 ath9k_hw_btcoex_enable(ah);
1116 ath_htc_resume_btcoex_work(priv);
1117 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301118 mutex_unlock(&priv->mutex);
1119
1120 return ret;
1121}
1122
Sujith881ac6a2010-06-01 15:14:11 +05301123static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +05301124{
1125 struct ath9k_htc_priv *priv = hw->priv;
1126 struct ath_hw *ah = priv->ah;
1127 struct ath_common *common = ath9k_hw_common(ah);
1128 int ret = 0;
1129 u8 cmd_rsp;
1130
Sujith881ac6a2010-06-01 15:14:11 +05301131 mutex_lock(&priv->mutex);
1132
Sujithfb9987d2010-03-17 14:25:25 +05301133 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -08001134 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +05301135 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301136 return;
1137 }
1138
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301139 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301140 htc_stop(priv->htc);
1141 WMI_CMD(WMI_DISABLE_INTR_CMDID);
1142 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
1143 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001144
1145 tasklet_kill(&priv->swba_tasklet);
1146 tasklet_kill(&priv->rx_tasklet);
1147 tasklet_kill(&priv->tx_tasklet);
1148
Sujithfb9987d2010-03-17 14:25:25 +05301149 skb_queue_purge(&priv->tx_queue);
1150
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001151 mutex_unlock(&priv->mutex);
1152
1153 /* Cancel all the running timers/work .. */
1154 cancel_work_sync(&priv->fatal_work);
1155 cancel_work_sync(&priv->ps_work);
1156 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Rajkumar Manoharan45655ba2011-01-31 23:47:42 +05301157 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +01001158 ath9k_led_stop_brightness(priv);
1159
1160 mutex_lock(&priv->mutex);
1161
Vivek Natarajan21cb9872010-08-18 19:57:49 +05301162 if (ah->btcoex_hw.enabled) {
1163 ath9k_hw_btcoex_disable(ah);
1164 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
1165 ath_htc_cancel_btcoex_work(priv);
1166 }
1167
Sujith Manoharana97b4782011-02-21 07:48:00 +05301168 /* Remove a monitor interface if it's present. */
1169 if (priv->ah->is_monitoring)
1170 ath9k_htc_remove_monitor_interface(priv);
1171
Sujithe9201f02010-06-01 15:14:17 +05301172 ath9k_hw_phy_disable(ah);
1173 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301174 ath9k_htc_ps_restore(priv);
1175 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1176
Sujithfb9987d2010-03-17 14:25:25 +05301177 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301178
Joe Perches226afe62010-12-02 19:12:37 -08001179 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301180 mutex_unlock(&priv->mutex);
1181}
1182
Sujithfb9987d2010-03-17 14:25:25 +05301183static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1184 struct ieee80211_vif *vif)
1185{
1186 struct ath9k_htc_priv *priv = hw->priv;
1187 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1188 struct ath_common *common = ath9k_hw_common(priv->ah);
1189 struct ath9k_htc_target_vif hvif;
1190 int ret = 0;
1191 u8 cmd_rsp;
1192
1193 mutex_lock(&priv->mutex);
1194
Sujith Manoharana97b4782011-02-21 07:48:00 +05301195 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301196 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301197 return -ENOBUFS;
1198 }
1199
1200 if (priv->num_ibss_vif ||
1201 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1202 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1203 mutex_unlock(&priv->mutex);
1204 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301205 }
1206
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301207 if (((vif->type == NL80211_IFTYPE_AP) ||
1208 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1209 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1210 ath_err(common, "Max. number of beaconing interfaces reached\n");
1211 mutex_unlock(&priv->mutex);
1212 return -ENOBUFS;
1213 }
1214
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301215 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301216 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1217 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1218
1219 switch (vif->type) {
1220 case NL80211_IFTYPE_STATION:
1221 hvif.opmode = cpu_to_be32(HTC_M_STA);
1222 break;
1223 case NL80211_IFTYPE_ADHOC:
1224 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1225 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301226 case NL80211_IFTYPE_AP:
1227 hvif.opmode = cpu_to_be32(HTC_M_HOSTAP);
1228 break;
Sujithfb9987d2010-03-17 14:25:25 +05301229 default:
Joe Perches38002762010-12-02 19:12:36 -08001230 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301231 "Interface type %d not yet supported\n", vif->type);
1232 ret = -EOPNOTSUPP;
1233 goto out;
1234 }
1235
Sujithfb9987d2010-03-17 14:25:25 +05301236 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301237 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301238 hvif.rtsthreshold = cpu_to_be16(2304);
1239 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1240 if (ret)
1241 goto out;
1242
Sujithfb9987d2010-03-17 14:25:25 +05301243 /*
1244 * We need a node in target to tx mgmt frames
1245 * before association.
1246 */
1247 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301248 if (ret) {
1249 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301250 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301251 }
Sujithfb9987d2010-03-17 14:25:25 +05301252
Sujith Manoharan585895c2011-02-21 07:48:46 +05301253 ath9k_htc_set_bssid_mask(priv, vif);
1254
Sujith Manoharana97b4782011-02-21 07:48:00 +05301255 priv->ah->opmode = vif->type;
1256 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301257 priv->nvifs++;
Sujithfb9987d2010-03-17 14:25:25 +05301258 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301259
Sujith Manoharan0df83592011-02-21 07:49:15 +05301260 INC_VIF(priv, vif->type);
1261
Sujith Manoharana97b4782011-02-21 07:48:00 +05301262 ath_dbg(common, ATH_DBG_CONFIG,
1263 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1264
Sujithfb9987d2010-03-17 14:25:25 +05301265out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301266 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301267 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301268
Sujithfb9987d2010-03-17 14:25:25 +05301269 return ret;
1270}
1271
1272static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1273 struct ieee80211_vif *vif)
1274{
1275 struct ath9k_htc_priv *priv = hw->priv;
1276 struct ath_common *common = ath9k_hw_common(priv->ah);
1277 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1278 struct ath9k_htc_target_vif hvif;
1279 int ret = 0;
1280 u8 cmd_rsp;
1281
Sujithfb9987d2010-03-17 14:25:25 +05301282 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301283 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301284
1285 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1286 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1287 hvif.index = avp->index;
1288 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1289 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301290 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301291
1292 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301293 priv->vif = NULL;
1294
Sujith Manoharan0df83592011-02-21 07:49:15 +05301295 DEC_VIF(priv, vif->type);
1296
Sujith Manoharana97b4782011-02-21 07:48:00 +05301297 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1298
Sujithcb551df2010-06-01 15:14:12 +05301299 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301300 mutex_unlock(&priv->mutex);
1301}
1302
1303static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1304{
1305 struct ath9k_htc_priv *priv = hw->priv;
1306 struct ath_common *common = ath9k_hw_common(priv->ah);
1307 struct ieee80211_conf *conf = &hw->conf;
1308
1309 mutex_lock(&priv->mutex);
1310
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301311 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1312 bool enable_radio = false;
1313 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1314
Sujith23367762010-06-01 15:14:16 +05301315 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301316 if (!idle && priv->ps_idle)
1317 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301318 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301319 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301320
1321 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001322 ath_dbg(common, ATH_DBG_CONFIG,
1323 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301324 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1325 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301326 }
1327 }
1328
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301329 /*
1330 * Monitor interface should be added before
1331 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1332 */
1333 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301334 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1335 !priv->ah->is_monitoring)
1336 ath9k_htc_add_monitor_interface(priv);
1337 else if (priv->ah->is_monitoring)
1338 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301339 }
1340
Sujithfb9987d2010-03-17 14:25:25 +05301341 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1342 struct ieee80211_channel *curchan = hw->conf.channel;
1343 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301344
Joe Perches226afe62010-12-02 19:12:37 -08001345 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1346 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301347
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001348 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1349 hw->conf.channel,
1350 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301351
1352 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001353 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301354 mutex_unlock(&priv->mutex);
1355 return -EINVAL;
1356 }
1357
1358 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301359
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301360 if (changed & IEEE80211_CONF_CHANGE_PS) {
1361 if (conf->flags & IEEE80211_CONF_PS) {
1362 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1363 priv->ps_enabled = true;
1364 } else {
1365 priv->ps_enabled = false;
1366 cancel_work_sync(&priv->ps_work);
1367 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1368 }
1369 }
Sujithfb9987d2010-03-17 14:25:25 +05301370
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301371 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1372 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301373 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1374 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301375 }
1376
Sujith23367762010-06-01 15:14:16 +05301377 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1378 mutex_lock(&priv->htc_pm_lock);
1379 if (!priv->ps_idle) {
1380 mutex_unlock(&priv->htc_pm_lock);
1381 goto out;
1382 }
1383 mutex_unlock(&priv->htc_pm_lock);
1384
Joe Perches226afe62010-12-02 19:12:37 -08001385 ath_dbg(common, ATH_DBG_CONFIG,
1386 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301387 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301388 }
1389
Sujith23367762010-06-01 15:14:16 +05301390out:
Sujithfb9987d2010-03-17 14:25:25 +05301391 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301392 return 0;
1393}
1394
1395#define SUPPORTED_FILTERS \
1396 (FIF_PROMISC_IN_BSS | \
1397 FIF_ALLMULTI | \
1398 FIF_CONTROL | \
1399 FIF_PSPOLL | \
1400 FIF_OTHER_BSS | \
1401 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301402 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301403 FIF_FCSFAIL)
1404
1405static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1406 unsigned int changed_flags,
1407 unsigned int *total_flags,
1408 u64 multicast)
1409{
1410 struct ath9k_htc_priv *priv = hw->priv;
1411 u32 rfilt;
1412
1413 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301414 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301415
Sujithfb9987d2010-03-17 14:25:25 +05301416 changed_flags &= SUPPORTED_FILTERS;
1417 *total_flags &= SUPPORTED_FILTERS;
1418
1419 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301420 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301421 ath9k_hw_setrxfilter(priv->ah, rfilt);
1422
Joe Perches226afe62010-12-02 19:12:37 -08001423 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1424 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301425
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301426 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301427 mutex_unlock(&priv->mutex);
1428}
1429
Sujithabd984e2010-05-18 15:26:04 +05301430static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1431 struct ieee80211_vif *vif,
1432 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301433{
1434 struct ath9k_htc_priv *priv = hw->priv;
1435 int ret;
1436
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301437 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301438 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301439 ret = ath9k_htc_add_station(priv, vif, sta);
1440 if (!ret)
1441 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301442 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301443 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301444
1445 return ret;
1446}
1447
1448static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1449 struct ieee80211_vif *vif,
1450 struct ieee80211_sta *sta)
1451{
1452 struct ath9k_htc_priv *priv = hw->priv;
1453 int ret;
1454
1455 mutex_lock(&priv->mutex);
1456 ath9k_htc_ps_wakeup(priv);
1457 ret = ath9k_htc_remove_station(priv, vif, sta);
1458 ath9k_htc_ps_restore(priv);
1459 mutex_unlock(&priv->mutex);
1460
1461 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301462}
1463
1464static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1465 const struct ieee80211_tx_queue_params *params)
1466{
1467 struct ath9k_htc_priv *priv = hw->priv;
1468 struct ath_common *common = ath9k_hw_common(priv->ah);
1469 struct ath9k_tx_queue_info qi;
1470 int ret = 0, qnum;
1471
1472 if (queue >= WME_NUM_AC)
1473 return 0;
1474
1475 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301476 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301477
1478 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1479
1480 qi.tqi_aifs = params->aifs;
1481 qi.tqi_cwmin = params->cw_min;
1482 qi.tqi_cwmax = params->cw_max;
1483 qi.tqi_burstTime = params->txop;
1484
1485 qnum = get_hw_qnum(queue, priv->hwq_map);
1486
Joe Perches226afe62010-12-02 19:12:37 -08001487 ath_dbg(common, ATH_DBG_CONFIG,
1488 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1489 queue, qnum, params->aifs, params->cw_min,
1490 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301491
Sujithe1572c52010-03-24 13:42:13 +05301492 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301493 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001494 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301495 goto out;
1496 }
Sujithfb9987d2010-03-17 14:25:25 +05301497
Sujith764580f2010-06-01 15:14:19 +05301498 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001499 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301500 ath9k_htc_beaconq_config(priv);
1501out:
Sujithcb551df2010-06-01 15:14:12 +05301502 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301503 mutex_unlock(&priv->mutex);
1504
1505 return ret;
1506}
1507
1508static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1509 enum set_key_cmd cmd,
1510 struct ieee80211_vif *vif,
1511 struct ieee80211_sta *sta,
1512 struct ieee80211_key_conf *key)
1513{
1514 struct ath9k_htc_priv *priv = hw->priv;
1515 struct ath_common *common = ath9k_hw_common(priv->ah);
1516 int ret = 0;
1517
Sujithe1572c52010-03-24 13:42:13 +05301518 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301519 return -ENOSPC;
1520
1521 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001522 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301523 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301524
1525 switch (cmd) {
1526 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001527 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301528 if (ret >= 0) {
1529 key->hw_key_idx = ret;
1530 /* push IV and Michael MIC generation to stack */
1531 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001532 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301533 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001534 if (priv->ah->sw_mgmt_crypto &&
1535 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301536 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1537 ret = 0;
1538 }
1539 break;
1540 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001541 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301542 break;
1543 default:
1544 ret = -EINVAL;
1545 }
1546
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301547 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301548 mutex_unlock(&priv->mutex);
1549
1550 return ret;
1551}
1552
1553static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1554 struct ieee80211_vif *vif,
1555 struct ieee80211_bss_conf *bss_conf,
1556 u32 changed)
1557{
1558 struct ath9k_htc_priv *priv = hw->priv;
1559 struct ath_hw *ah = priv->ah;
1560 struct ath_common *common = ath9k_hw_common(ah);
1561
1562 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301563 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301564
1565 if (changed & BSS_CHANGED_ASSOC) {
1566 common->curaid = bss_conf->assoc ?
1567 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001568 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301569 bss_conf->assoc);
1570
Sujith Manoharan7c277342011-02-21 07:48:39 +05301571 if (bss_conf->assoc)
Sujithfb9987d2010-03-17 14:25:25 +05301572 ath_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301573 else
Sujithfb9987d2010-03-17 14:25:25 +05301574 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301575 }
1576
1577 if (changed & BSS_CHANGED_BSSID) {
1578 /* Set BSSID */
1579 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1580 ath9k_hw_write_associd(ah);
1581
Joe Perches226afe62010-12-02 19:12:37 -08001582 ath_dbg(common, ATH_DBG_CONFIG,
1583 "BSSID: %pM aid: 0x%x\n",
1584 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301585 }
1586
1587 if ((changed & BSS_CHANGED_BEACON_INT) ||
1588 (changed & BSS_CHANGED_BEACON) ||
1589 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1590 bss_conf->enable_beacon)) {
1591 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301592 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301593 }
1594
Sujithfb9987d2010-03-17 14:25:25 +05301595 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1596 !bss_conf->enable_beacon) {
1597 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301598 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301599 }
1600
Sujithfb9987d2010-03-17 14:25:25 +05301601 if (changed & BSS_CHANGED_ERP_SLOT) {
1602 if (bss_conf->use_short_slot)
1603 ah->slottime = 9;
1604 else
1605 ah->slottime = 20;
1606
1607 ath9k_hw_init_global_settings(ah);
1608 }
1609
Sujith2c76ef82010-05-17 12:01:18 +05301610 if (changed & BSS_CHANGED_HT)
1611 ath9k_htc_update_rate(priv, vif, bss_conf);
1612
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301613 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301614 mutex_unlock(&priv->mutex);
1615}
1616
1617static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1618{
1619 struct ath9k_htc_priv *priv = hw->priv;
1620 u64 tsf;
1621
1622 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301623 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301624 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301625 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301626 mutex_unlock(&priv->mutex);
1627
1628 return tsf;
1629}
1630
1631static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1632{
1633 struct ath9k_htc_priv *priv = hw->priv;
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 ath9k_hw_settsf64(priv->ah, tsf);
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
1642static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1643{
1644 struct ath9k_htc_priv *priv = hw->priv;
1645
1646 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301647 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301648 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301649 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301650 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301651}
1652
1653static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1654 struct ieee80211_vif *vif,
1655 enum ieee80211_ampdu_mlme_action action,
1656 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001657 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301658{
1659 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301660 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301661 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301662
Sujith Manoharan87df8952011-02-21 07:49:08 +05301663 mutex_lock(&priv->mutex);
1664
Sujithfb9987d2010-03-17 14:25:25 +05301665 switch (action) {
1666 case IEEE80211_AMPDU_RX_START:
1667 break;
1668 case IEEE80211_AMPDU_RX_STOP:
1669 break;
1670 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301671 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1672 if (!ret)
1673 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1674 break;
Sujithfb9987d2010-03-17 14:25:25 +05301675 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301676 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1677 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301678 break;
1679 case IEEE80211_AMPDU_TX_OPERATIONAL:
1680 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301681 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301682 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301683 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301684 break;
1685 default:
Joe Perches38002762010-12-02 19:12:36 -08001686 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301687 }
1688
Sujith Manoharan87df8952011-02-21 07:49:08 +05301689 mutex_unlock(&priv->mutex);
1690
Sujithd7ca2132010-06-15 10:24:37 +05301691 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301692}
1693
1694static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1695{
1696 struct ath9k_htc_priv *priv = hw->priv;
1697
1698 mutex_lock(&priv->mutex);
1699 spin_lock_bh(&priv->beacon_lock);
1700 priv->op_flags |= OP_SCANNING;
1701 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301702 cancel_work_sync(&priv->ps_work);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301703 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301704 mutex_unlock(&priv->mutex);
1705}
1706
1707static void ath9k_htc_sw_scan_complete(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);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301715 ath9k_htc_ps_wakeup(priv);
1716 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301717 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301718 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301719}
1720
1721static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1722{
1723 return 0;
1724}
1725
1726static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1727 u8 coverage_class)
1728{
1729 struct ath9k_htc_priv *priv = hw->priv;
1730
1731 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301732 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301733 priv->ah->coverage_class = coverage_class;
1734 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301735 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301736 mutex_unlock(&priv->mutex);
1737}
1738
1739struct ieee80211_ops ath9k_htc_ops = {
1740 .tx = ath9k_htc_tx,
1741 .start = ath9k_htc_start,
1742 .stop = ath9k_htc_stop,
1743 .add_interface = ath9k_htc_add_interface,
1744 .remove_interface = ath9k_htc_remove_interface,
1745 .config = ath9k_htc_config,
1746 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301747 .sta_add = ath9k_htc_sta_add,
1748 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301749 .conf_tx = ath9k_htc_conf_tx,
1750 .bss_info_changed = ath9k_htc_bss_info_changed,
1751 .set_key = ath9k_htc_set_key,
1752 .get_tsf = ath9k_htc_get_tsf,
1753 .set_tsf = ath9k_htc_set_tsf,
1754 .reset_tsf = ath9k_htc_reset_tsf,
1755 .ampdu_action = ath9k_htc_ampdu_action,
1756 .sw_scan_start = ath9k_htc_sw_scan_start,
1757 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1758 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1759 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1760 .set_coverage_class = ath9k_htc_set_coverage_class,
1761};