blob: 04cb243416ae1261a2ad233519f6784130d91c9c [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) {
Sujithfb9987d2010-03-17 14:25:25 +05301196 ret = -ENOBUFS;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301197 mutex_unlock(&priv->mutex);
1198 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301199 }
1200
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301201 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301202 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1203 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1204
1205 switch (vif->type) {
1206 case NL80211_IFTYPE_STATION:
1207 hvif.opmode = cpu_to_be32(HTC_M_STA);
1208 break;
1209 case NL80211_IFTYPE_ADHOC:
1210 hvif.opmode = cpu_to_be32(HTC_M_IBSS);
1211 break;
1212 default:
Joe Perches38002762010-12-02 19:12:36 -08001213 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301214 "Interface type %d not yet supported\n", vif->type);
1215 ret = -EOPNOTSUPP;
1216 goto out;
1217 }
1218
Sujithfb9987d2010-03-17 14:25:25 +05301219 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301220 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301221 hvif.rtsthreshold = cpu_to_be16(2304);
1222 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1223 if (ret)
1224 goto out;
1225
Sujithfb9987d2010-03-17 14:25:25 +05301226 /*
1227 * We need a node in target to tx mgmt frames
1228 * before association.
1229 */
1230 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301231 if (ret) {
1232 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301233 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301234 }
Sujithfb9987d2010-03-17 14:25:25 +05301235
Sujith Manoharan585895c2011-02-21 07:48:46 +05301236 ath9k_htc_set_bssid_mask(priv, vif);
1237
Sujith Manoharana97b4782011-02-21 07:48:00 +05301238 priv->ah->opmode = vif->type;
1239 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301240 priv->nvifs++;
Sujithfb9987d2010-03-17 14:25:25 +05301241 priv->vif = vif;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301242
1243 ath_dbg(common, ATH_DBG_CONFIG,
1244 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1245
Sujithfb9987d2010-03-17 14:25:25 +05301246out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301247 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301248 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301249
Sujithfb9987d2010-03-17 14:25:25 +05301250 return ret;
1251}
1252
1253static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1254 struct ieee80211_vif *vif)
1255{
1256 struct ath9k_htc_priv *priv = hw->priv;
1257 struct ath_common *common = ath9k_hw_common(priv->ah);
1258 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1259 struct ath9k_htc_target_vif hvif;
1260 int ret = 0;
1261 u8 cmd_rsp;
1262
Sujithfb9987d2010-03-17 14:25:25 +05301263 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301264 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301265
1266 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1267 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1268 hvif.index = avp->index;
1269 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1270 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301271 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301272
1273 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301274 priv->vif = NULL;
1275
Sujith Manoharana97b4782011-02-21 07:48:00 +05301276 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1277
Sujithcb551df2010-06-01 15:14:12 +05301278 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301279 mutex_unlock(&priv->mutex);
1280}
1281
1282static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1283{
1284 struct ath9k_htc_priv *priv = hw->priv;
1285 struct ath_common *common = ath9k_hw_common(priv->ah);
1286 struct ieee80211_conf *conf = &hw->conf;
1287
1288 mutex_lock(&priv->mutex);
1289
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301290 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1291 bool enable_radio = false;
1292 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1293
Sujith23367762010-06-01 15:14:16 +05301294 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301295 if (!idle && priv->ps_idle)
1296 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301297 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301298 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301299
1300 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001301 ath_dbg(common, ATH_DBG_CONFIG,
1302 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301303 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1304 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301305 }
1306 }
1307
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301308 /*
1309 * Monitor interface should be added before
1310 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1311 */
1312 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301313 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1314 !priv->ah->is_monitoring)
1315 ath9k_htc_add_monitor_interface(priv);
1316 else if (priv->ah->is_monitoring)
1317 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301318 }
1319
Sujithfb9987d2010-03-17 14:25:25 +05301320 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1321 struct ieee80211_channel *curchan = hw->conf.channel;
1322 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301323
Joe Perches226afe62010-12-02 19:12:37 -08001324 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1325 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301326
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001327 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1328 hw->conf.channel,
1329 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301330
1331 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001332 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301333 mutex_unlock(&priv->mutex);
1334 return -EINVAL;
1335 }
1336
1337 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301338
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301339 if (changed & IEEE80211_CONF_CHANGE_PS) {
1340 if (conf->flags & IEEE80211_CONF_PS) {
1341 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1342 priv->ps_enabled = true;
1343 } else {
1344 priv->ps_enabled = false;
1345 cancel_work_sync(&priv->ps_work);
1346 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1347 }
1348 }
Sujithfb9987d2010-03-17 14:25:25 +05301349
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301350 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1351 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301352 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1353 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301354 }
1355
Sujith23367762010-06-01 15:14:16 +05301356 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1357 mutex_lock(&priv->htc_pm_lock);
1358 if (!priv->ps_idle) {
1359 mutex_unlock(&priv->htc_pm_lock);
1360 goto out;
1361 }
1362 mutex_unlock(&priv->htc_pm_lock);
1363
Joe Perches226afe62010-12-02 19:12:37 -08001364 ath_dbg(common, ATH_DBG_CONFIG,
1365 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301366 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301367 }
1368
Sujith23367762010-06-01 15:14:16 +05301369out:
Sujithfb9987d2010-03-17 14:25:25 +05301370 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301371 return 0;
1372}
1373
1374#define SUPPORTED_FILTERS \
1375 (FIF_PROMISC_IN_BSS | \
1376 FIF_ALLMULTI | \
1377 FIF_CONTROL | \
1378 FIF_PSPOLL | \
1379 FIF_OTHER_BSS | \
1380 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301381 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301382 FIF_FCSFAIL)
1383
1384static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1385 unsigned int changed_flags,
1386 unsigned int *total_flags,
1387 u64 multicast)
1388{
1389 struct ath9k_htc_priv *priv = hw->priv;
1390 u32 rfilt;
1391
1392 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301393 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301394
Sujithfb9987d2010-03-17 14:25:25 +05301395 changed_flags &= SUPPORTED_FILTERS;
1396 *total_flags &= SUPPORTED_FILTERS;
1397
1398 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301399 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301400 ath9k_hw_setrxfilter(priv->ah, rfilt);
1401
Joe Perches226afe62010-12-02 19:12:37 -08001402 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1403 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301404
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301405 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301406 mutex_unlock(&priv->mutex);
1407}
1408
Sujithabd984e2010-05-18 15:26:04 +05301409static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1410 struct ieee80211_vif *vif,
1411 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301412{
1413 struct ath9k_htc_priv *priv = hw->priv;
1414 int ret;
1415
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301416 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301417 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301418 ret = ath9k_htc_add_station(priv, vif, sta);
1419 if (!ret)
1420 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301421 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301422 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301423
1424 return ret;
1425}
1426
1427static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1428 struct ieee80211_vif *vif,
1429 struct ieee80211_sta *sta)
1430{
1431 struct ath9k_htc_priv *priv = hw->priv;
1432 int ret;
1433
1434 mutex_lock(&priv->mutex);
1435 ath9k_htc_ps_wakeup(priv);
1436 ret = ath9k_htc_remove_station(priv, vif, sta);
1437 ath9k_htc_ps_restore(priv);
1438 mutex_unlock(&priv->mutex);
1439
1440 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301441}
1442
1443static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1444 const struct ieee80211_tx_queue_params *params)
1445{
1446 struct ath9k_htc_priv *priv = hw->priv;
1447 struct ath_common *common = ath9k_hw_common(priv->ah);
1448 struct ath9k_tx_queue_info qi;
1449 int ret = 0, qnum;
1450
1451 if (queue >= WME_NUM_AC)
1452 return 0;
1453
1454 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301455 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301456
1457 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1458
1459 qi.tqi_aifs = params->aifs;
1460 qi.tqi_cwmin = params->cw_min;
1461 qi.tqi_cwmax = params->cw_max;
1462 qi.tqi_burstTime = params->txop;
1463
1464 qnum = get_hw_qnum(queue, priv->hwq_map);
1465
Joe Perches226afe62010-12-02 19:12:37 -08001466 ath_dbg(common, ATH_DBG_CONFIG,
1467 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1468 queue, qnum, params->aifs, params->cw_min,
1469 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301470
Sujithe1572c52010-03-24 13:42:13 +05301471 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301472 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001473 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301474 goto out;
1475 }
Sujithfb9987d2010-03-17 14:25:25 +05301476
Sujith764580f2010-06-01 15:14:19 +05301477 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001478 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301479 ath9k_htc_beaconq_config(priv);
1480out:
Sujithcb551df2010-06-01 15:14:12 +05301481 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301482 mutex_unlock(&priv->mutex);
1483
1484 return ret;
1485}
1486
1487static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1488 enum set_key_cmd cmd,
1489 struct ieee80211_vif *vif,
1490 struct ieee80211_sta *sta,
1491 struct ieee80211_key_conf *key)
1492{
1493 struct ath9k_htc_priv *priv = hw->priv;
1494 struct ath_common *common = ath9k_hw_common(priv->ah);
1495 int ret = 0;
1496
Sujithe1572c52010-03-24 13:42:13 +05301497 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301498 return -ENOSPC;
1499
1500 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001501 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301502 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301503
1504 switch (cmd) {
1505 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001506 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301507 if (ret >= 0) {
1508 key->hw_key_idx = ret;
1509 /* push IV and Michael MIC generation to stack */
1510 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001511 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301512 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001513 if (priv->ah->sw_mgmt_crypto &&
1514 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301515 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1516 ret = 0;
1517 }
1518 break;
1519 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001520 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301521 break;
1522 default:
1523 ret = -EINVAL;
1524 }
1525
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301526 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301527 mutex_unlock(&priv->mutex);
1528
1529 return ret;
1530}
1531
1532static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1533 struct ieee80211_vif *vif,
1534 struct ieee80211_bss_conf *bss_conf,
1535 u32 changed)
1536{
1537 struct ath9k_htc_priv *priv = hw->priv;
1538 struct ath_hw *ah = priv->ah;
1539 struct ath_common *common = ath9k_hw_common(ah);
1540
1541 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301542 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301543
1544 if (changed & BSS_CHANGED_ASSOC) {
1545 common->curaid = bss_conf->assoc ?
1546 bss_conf->aid : 0;
Joe Perches226afe62010-12-02 19:12:37 -08001547 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
Sujithfb9987d2010-03-17 14:25:25 +05301548 bss_conf->assoc);
1549
Sujith Manoharan7c277342011-02-21 07:48:39 +05301550 if (bss_conf->assoc)
Sujithfb9987d2010-03-17 14:25:25 +05301551 ath_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301552 else
Sujithfb9987d2010-03-17 14:25:25 +05301553 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301554 }
1555
1556 if (changed & BSS_CHANGED_BSSID) {
1557 /* Set BSSID */
1558 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1559 ath9k_hw_write_associd(ah);
1560
Joe Perches226afe62010-12-02 19:12:37 -08001561 ath_dbg(common, ATH_DBG_CONFIG,
1562 "BSSID: %pM aid: 0x%x\n",
1563 common->curbssid, common->curaid);
Sujithfb9987d2010-03-17 14:25:25 +05301564 }
1565
1566 if ((changed & BSS_CHANGED_BEACON_INT) ||
1567 (changed & BSS_CHANGED_BEACON) ||
1568 ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1569 bss_conf->enable_beacon)) {
1570 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301571 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301572 }
1573
Sujithfb9987d2010-03-17 14:25:25 +05301574 if ((changed & BSS_CHANGED_BEACON_ENABLED) &&
1575 !bss_conf->enable_beacon) {
1576 priv->op_flags &= ~OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301577 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301578 }
1579
Sujithfb9987d2010-03-17 14:25:25 +05301580 if (changed & BSS_CHANGED_ERP_SLOT) {
1581 if (bss_conf->use_short_slot)
1582 ah->slottime = 9;
1583 else
1584 ah->slottime = 20;
1585
1586 ath9k_hw_init_global_settings(ah);
1587 }
1588
Sujith2c76ef82010-05-17 12:01:18 +05301589 if (changed & BSS_CHANGED_HT)
1590 ath9k_htc_update_rate(priv, vif, bss_conf);
1591
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301592 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301593 mutex_unlock(&priv->mutex);
1594}
1595
1596static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1597{
1598 struct ath9k_htc_priv *priv = hw->priv;
1599 u64 tsf;
1600
1601 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301602 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301603 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301604 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301605 mutex_unlock(&priv->mutex);
1606
1607 return tsf;
1608}
1609
1610static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1611{
1612 struct ath9k_htc_priv *priv = hw->priv;
1613
1614 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301615 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301616 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301617 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301618 mutex_unlock(&priv->mutex);
1619}
1620
1621static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1622{
1623 struct ath9k_htc_priv *priv = hw->priv;
1624
1625 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301626 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301627 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301628 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301629 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301630}
1631
1632static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1633 struct ieee80211_vif *vif,
1634 enum ieee80211_ampdu_mlme_action action,
1635 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001636 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301637{
1638 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301639 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301640 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301641
Sujith Manoharan87df8952011-02-21 07:49:08 +05301642 mutex_lock(&priv->mutex);
1643
Sujithfb9987d2010-03-17 14:25:25 +05301644 switch (action) {
1645 case IEEE80211_AMPDU_RX_START:
1646 break;
1647 case IEEE80211_AMPDU_RX_STOP:
1648 break;
1649 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301650 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1651 if (!ret)
1652 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1653 break;
Sujithfb9987d2010-03-17 14:25:25 +05301654 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301655 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1656 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301657 break;
1658 case IEEE80211_AMPDU_TX_OPERATIONAL:
1659 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithd7ca2132010-06-15 10:24:37 +05301660 spin_lock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301661 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujithd7ca2132010-06-15 10:24:37 +05301662 spin_unlock_bh(&priv->tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301663 break;
1664 default:
Joe Perches38002762010-12-02 19:12:36 -08001665 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301666 }
1667
Sujith Manoharan87df8952011-02-21 07:49:08 +05301668 mutex_unlock(&priv->mutex);
1669
Sujithd7ca2132010-06-15 10:24:37 +05301670 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301671}
1672
1673static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1674{
1675 struct ath9k_htc_priv *priv = hw->priv;
1676
1677 mutex_lock(&priv->mutex);
1678 spin_lock_bh(&priv->beacon_lock);
1679 priv->op_flags |= OP_SCANNING;
1680 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301681 cancel_work_sync(&priv->ps_work);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301682 cancel_delayed_work_sync(&priv->ath9k_ani_work);
Sujithfb9987d2010-03-17 14:25:25 +05301683 mutex_unlock(&priv->mutex);
1684}
1685
1686static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1687{
1688 struct ath9k_htc_priv *priv = hw->priv;
1689
1690 mutex_lock(&priv->mutex);
1691 spin_lock_bh(&priv->beacon_lock);
1692 priv->op_flags &= ~OP_SCANNING;
1693 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301694 ath9k_htc_ps_wakeup(priv);
1695 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301696 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301697 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301698}
1699
1700static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1701{
1702 return 0;
1703}
1704
1705static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1706 u8 coverage_class)
1707{
1708 struct ath9k_htc_priv *priv = hw->priv;
1709
1710 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301711 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301712 priv->ah->coverage_class = coverage_class;
1713 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301714 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301715 mutex_unlock(&priv->mutex);
1716}
1717
1718struct ieee80211_ops ath9k_htc_ops = {
1719 .tx = ath9k_htc_tx,
1720 .start = ath9k_htc_start,
1721 .stop = ath9k_htc_stop,
1722 .add_interface = ath9k_htc_add_interface,
1723 .remove_interface = ath9k_htc_remove_interface,
1724 .config = ath9k_htc_config,
1725 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301726 .sta_add = ath9k_htc_sta_add,
1727 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301728 .conf_tx = ath9k_htc_conf_tx,
1729 .bss_info_changed = ath9k_htc_bss_info_changed,
1730 .set_key = ath9k_htc_set_key,
1731 .get_tsf = ath9k_htc_get_tsf,
1732 .set_tsf = ath9k_htc_set_tsf,
1733 .reset_tsf = ath9k_htc_reset_tsf,
1734 .ampdu_action = ath9k_htc_ampdu_action,
1735 .sw_scan_start = ath9k_htc_sw_scan_start,
1736 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1737 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1738 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1739 .set_coverage_class = ath9k_htc_set_coverage_class,
1740};