blob: fb9ff1188a0a51379715454449edaa4bd65c66fd [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
Sujithfb9987d2010-03-17 14:25:25 +053019/*************/
20/* Utilities */
21/*************/
22
Sujithfb9987d2010-03-17 14:25:25 +053023/* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */
24static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv,
25 struct ath9k_channel *ichan)
26{
27 enum htc_phymode mode;
28
29 mode = HTC_MODE_AUTO;
30
31 switch (ichan->chanmode) {
32 case CHANNEL_G:
33 case CHANNEL_G_HT20:
34 case CHANNEL_G_HT40PLUS:
35 case CHANNEL_G_HT40MINUS:
36 mode = HTC_MODE_11NG;
37 break;
38 case CHANNEL_A:
39 case CHANNEL_A_HT20:
40 case CHANNEL_A_HT40PLUS:
41 case CHANNEL_A_HT40MINUS:
42 mode = HTC_MODE_11NA;
43 break;
44 default:
45 break;
46 }
47
48 return mode;
49}
50
Sujith Manoharanf933ebe2010-12-01 12:30:27 +053051bool ath9k_htc_setpower(struct ath9k_htc_priv *priv,
52 enum ath9k_power_mode mode)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053053{
54 bool ret;
55
56 mutex_lock(&priv->htc_pm_lock);
57 ret = ath9k_hw_setpower(priv->ah, mode);
58 mutex_unlock(&priv->htc_pm_lock);
59
60 return ret;
61}
62
63void ath9k_htc_ps_wakeup(struct ath9k_htc_priv *priv)
64{
65 mutex_lock(&priv->htc_pm_lock);
66 if (++priv->ps_usecount != 1)
67 goto unlock;
68 ath9k_hw_setpower(priv->ah, ATH9K_PM_AWAKE);
69
70unlock:
71 mutex_unlock(&priv->htc_pm_lock);
72}
73
74void ath9k_htc_ps_restore(struct ath9k_htc_priv *priv)
75{
76 mutex_lock(&priv->htc_pm_lock);
77 if (--priv->ps_usecount != 0)
78 goto unlock;
79
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053080 if (priv->ps_idle)
81 ath9k_hw_setpower(priv->ah, ATH9K_PM_FULL_SLEEP);
82 else if (priv->ps_enabled)
Vivek Natarajanbde748a2010-04-05 14:48:05 +053083 ath9k_hw_setpower(priv->ah, ATH9K_PM_NETWORK_SLEEP);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +053084
Vivek Natarajanbde748a2010-04-05 14:48:05 +053085unlock:
86 mutex_unlock(&priv->htc_pm_lock);
87}
88
89void ath9k_ps_work(struct work_struct *work)
90{
91 struct ath9k_htc_priv *priv =
92 container_of(work, struct ath9k_htc_priv,
93 ps_work);
94 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
95
96 /* The chip wakes up after receiving the first beacon
97 while network sleep is enabled. For the driver to
98 be in sync with the hw, set the chip to awake and
99 only then set it to sleep.
100 */
101 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
102}
103
Sujith Manoharan7c277342011-02-21 07:48:39 +0530104static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
105{
106 struct ath9k_htc_priv *priv = data;
107 struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
108
Sujith Manoharana5fae372011-02-21 07:49:53 +0530109 if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon)
110 priv->reconfig_beacon = true;
111
Sujith Manoharan7c277342011-02-21 07:48:39 +0530112 if (bss_conf->assoc) {
113 priv->rearm_ani = true;
114 priv->reconfig_beacon = true;
115 }
116}
117
118static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv)
119{
120 priv->rearm_ani = false;
121 priv->reconfig_beacon = false;
122
123 ieee80211_iterate_active_interfaces_atomic(priv->hw,
124 ath9k_htc_vif_iter, priv);
125 if (priv->rearm_ani)
Sujith Manoharana2362542011-02-21 07:49:38 +0530126 ath9k_htc_start_ani(priv);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530127
128 if (priv->reconfig_beacon) {
129 ath9k_htc_ps_wakeup(priv);
130 ath9k_htc_beacon_reconfig(priv);
131 ath9k_htc_ps_restore(priv);
132 }
133}
134
Sujith Manoharan585895c2011-02-21 07:48:46 +0530135static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
136{
137 struct ath9k_vif_iter_data *iter_data = data;
138 int i;
139
140 for (i = 0; i < ETH_ALEN; i++)
141 iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]);
142}
143
144static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv,
145 struct ieee80211_vif *vif)
146{
147 struct ath_common *common = ath9k_hw_common(priv->ah);
148 struct ath9k_vif_iter_data iter_data;
149
150 /*
151 * Use the hardware MAC address as reference, the hardware uses it
152 * together with the BSSID mask when matching addresses.
153 */
154 iter_data.hw_macaddr = common->macaddr;
155 memset(&iter_data.mask, 0xff, ETH_ALEN);
156
157 if (vif)
158 ath9k_htc_bssid_iter(&iter_data, vif->addr, vif);
159
160 /* Get list of all active MAC addresses */
161 ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter,
162 &iter_data);
163
164 memcpy(common->bssidmask, iter_data.mask, ETH_ALEN);
165 ath_hw_setbssidmask(common);
166}
167
Sujith Manoharanffbe7c82011-02-21 07:49:31 +0530168static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv)
169{
170 if (priv->num_ibss_vif)
171 priv->ah->opmode = NL80211_IFTYPE_ADHOC;
172 else if (priv->num_ap_vif)
173 priv->ah->opmode = NL80211_IFTYPE_AP;
174 else
175 priv->ah->opmode = NL80211_IFTYPE_STATION;
176
177 ath9k_hw_setopmode(priv->ah);
178}
179
Sujith Manoharan73908672010-12-28 14:28:27 +0530180void ath9k_htc_reset(struct ath9k_htc_priv *priv)
181{
182 struct ath_hw *ah = priv->ah;
183 struct ath_common *common = ath9k_hw_common(ah);
184 struct ieee80211_channel *channel = priv->hw->conf.channel;
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530185 struct ath9k_hw_cal_data *caldata = NULL;
Sujith Manoharan73908672010-12-28 14:28:27 +0530186 enum htc_phymode mode;
187 __be16 htc_mode;
188 u8 cmd_rsp;
189 int ret;
190
191 mutex_lock(&priv->mutex);
192 ath9k_htc_ps_wakeup(priv);
193
Sujith Manoharana2362542011-02-21 07:49:38 +0530194 ath9k_htc_stop_ani(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530195 ieee80211_stop_queues(priv->hw);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530196
197 ath9k_htc_tx_drain(priv);
198
Sujith Manoharan73908672010-12-28 14:28:27 +0530199 WMI_CMD(WMI_DISABLE_INTR_CMDID);
200 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
201 WMI_CMD(WMI_STOP_RECV_CMDID);
202
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530203 ath9k_wmi_event_drain(priv);
204
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530205 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530206 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
207 if (ret) {
208 ath_err(common,
209 "Unable to reset device (%u Mhz) reset status %d\n",
210 channel->center_freq, ret);
211 }
212
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530213 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
214 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530215
216 WMI_CMD(WMI_START_RECV_CMDID);
217 ath9k_host_rx_init(priv);
218
219 mode = ath9k_htc_get_curmode(priv, ah->curchan);
220 htc_mode = cpu_to_be16(mode);
221 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
222
223 WMI_CMD(WMI_ENABLE_INTR_CMDID);
224 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530225 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530226 ieee80211_wake_queues(priv->hw);
227
228 ath9k_htc_ps_restore(priv);
229 mutex_unlock(&priv->mutex);
230}
231
Sujithfb9987d2010-03-17 14:25:25 +0530232static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
233 struct ieee80211_hw *hw,
234 struct ath9k_channel *hchan)
235{
236 struct ath_hw *ah = priv->ah;
237 struct ath_common *common = ath9k_hw_common(ah);
238 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530239 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530240 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530241 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530242 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530243 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530244 u8 cmd_rsp;
245 int ret;
246
247 if (priv->op_flags & OP_INVALID)
248 return -EIO;
249
Sujith Manoharan039a0722010-12-28 14:28:37 +0530250 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530251
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530252 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530253
254 ath9k_htc_tx_drain(priv);
255
Sujithfb9987d2010-03-17 14:25:25 +0530256 WMI_CMD(WMI_DISABLE_INTR_CMDID);
257 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
258 WMI_CMD(WMI_STOP_RECV_CMDID);
259
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530260 ath9k_wmi_event_drain(priv);
261
Joe Perches226afe62010-12-02 19:12:37 -0800262 ath_dbg(common, ATH_DBG_CONFIG,
263 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
264 priv->ah->curchan->channel,
265 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
266 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530267
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530268 if (!fastcc)
269 caldata = &priv->caldata;
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530270
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200271 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530272 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800273 ath_err(common,
274 "Unable to reset channel (%u Mhz) reset status %d\n",
275 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530276 goto err;
277 }
278
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530279 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
280 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530281
282 WMI_CMD(WMI_START_RECV_CMDID);
283 if (ret)
284 goto err;
285
286 ath9k_host_rx_init(priv);
287
288 mode = ath9k_htc_get_curmode(priv, hchan);
289 htc_mode = cpu_to_be16(mode);
290 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
291 if (ret)
292 goto err;
293
294 WMI_CMD(WMI_ENABLE_INTR_CMDID);
295 if (ret)
296 goto err;
297
298 htc_start(priv->htc);
Sujith Manoharana5fae372011-02-21 07:49:53 +0530299
300 if (!(priv->op_flags & OP_SCANNING) &&
301 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
302 ath9k_htc_vif_reconfig(priv);
303
Sujithfb9987d2010-03-17 14:25:25 +0530304err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530305 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530306 return ret;
307}
308
Sujith Manoharana97b4782011-02-21 07:48:00 +0530309/*
310 * Monitor mode handling is a tad complicated because the firmware requires
311 * an interface to be created exclusively, while mac80211 doesn't associate
312 * an interface with the mode.
313 *
314 * So, for now, only one monitor interface can be configured.
315 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530316static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530317{
318 struct ath_common *common = ath9k_hw_common(priv->ah);
319 struct ath9k_htc_target_vif hvif;
320 int ret = 0;
321 u8 cmd_rsp;
322
Sujith Manoharancc721282011-01-03 21:22:18 +0530323 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
324 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530325 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530326 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
327 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530328 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530329}
330
331static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
332{
333 struct ath_common *common = ath9k_hw_common(priv->ah);
334 struct ath9k_htc_target_vif hvif;
335 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530336 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530337 u8 cmd_rsp;
338
Sujith Manoharana97b4782011-02-21 07:48:00 +0530339 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
340 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
341 ret = -ENOBUFS;
342 goto err_vif;
343 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530344
Sujith Manoharana97b4782011-02-21 07:48:00 +0530345 sta_idx = ffz(priv->sta_slot);
346 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
347 ret = -ENOBUFS;
348 goto err_vif;
349 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530350
351 /*
352 * Add an interface.
353 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530354 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
355 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
356
Sujith Manoharane4c62502011-04-13 11:24:43 +0530357 hvif.opmode = HTC_M_MONITOR;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530358 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530359
360 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
361 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530362 goto err_vif;
363
364 /*
365 * Assign the monitor interface index as a special case here.
366 * This is needed when the interface is brought down.
367 */
368 priv->mon_vif_idx = hvif.index;
369 priv->vif_slot |= (1 << hvif.index);
370
371 /*
372 * Set the hardware mode to monitor only if there are no
373 * other interfaces.
374 */
375 if (!priv->nvifs)
376 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530377
378 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530379
380 /*
381 * Associate a station with the interface for packet injection.
382 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530383 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
384
385 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
386
387 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530388 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530389 tsta.vif_index = hvif.index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530390 tsta.maxampdu = cpu_to_be16(0xffff);
Sujith Manoharancc721282011-01-03 21:22:18 +0530391
392 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
393 if (ret) {
394 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530395 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530396 }
397
Sujith Manoharana97b4782011-02-21 07:48:00 +0530398 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530399 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530400 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530401 priv->ah->is_monitoring = true;
402
Sujith Manoharana97b4782011-02-21 07:48:00 +0530403 ath_dbg(common, ATH_DBG_CONFIG,
404 "Attached a monitor interface at idx: %d, sta idx: %d\n",
405 priv->mon_vif_idx, sta_idx);
406
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530407 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530408
Sujith Manoharana97b4782011-02-21 07:48:00 +0530409err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530410 /*
411 * Remove the interface from the target.
412 */
413 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530414err_vif:
415 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
416
Sujith Manoharancc721282011-01-03 21:22:18 +0530417 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530418}
419
420static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
421{
422 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530423 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530424 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530425
Sujith Manoharancc721282011-01-03 21:22:18 +0530426 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530427
Sujith Manoharana97b4782011-02-21 07:48:00 +0530428 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530429
430 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
431 if (ret) {
432 ath_err(common, "Unable to remove station entry for monitor mode\n");
433 return ret;
434 }
435
Sujith Manoharana97b4782011-02-21 07:48:00 +0530436 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530437 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530438 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530439
Sujith Manoharana97b4782011-02-21 07:48:00 +0530440 ath_dbg(common, ATH_DBG_CONFIG,
441 "Removed a monitor interface at idx: %d, sta idx: %d\n",
442 priv->mon_vif_idx, sta_idx);
443
Sujith Manoharancc721282011-01-03 21:22:18 +0530444 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530445}
446
Sujithfb9987d2010-03-17 14:25:25 +0530447static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
448 struct ieee80211_vif *vif,
449 struct ieee80211_sta *sta)
450{
451 struct ath_common *common = ath9k_hw_common(priv->ah);
452 struct ath9k_htc_target_sta tsta;
453 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
454 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530455 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530456 u8 cmd_rsp;
457
458 if (priv->nstations >= ATH9K_HTC_MAX_STA)
459 return -ENOBUFS;
460
Sujith Manoharana97b4782011-02-21 07:48:00 +0530461 sta_idx = ffz(priv->sta_slot);
462 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
463 return -ENOBUFS;
464
Sujithfb9987d2010-03-17 14:25:25 +0530465 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
466
467 if (sta) {
468 ista = (struct ath9k_htc_sta *) sta->drv_priv;
469 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
470 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
Sujithfb9987d2010-03-17 14:25:25 +0530471 tsta.is_vif_sta = 0;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530472 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530473 } else {
474 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
475 tsta.is_vif_sta = 1;
476 }
477
Sujith Manoharana97b4782011-02-21 07:48:00 +0530478 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530479 tsta.vif_index = avp->index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530480 tsta.maxampdu = cpu_to_be16(0xffff);
Sujithfb9987d2010-03-17 14:25:25 +0530481 if (sta && sta->ht_cap.ht_supported)
482 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
483
484 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
485 if (ret) {
486 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800487 ath_err(common,
488 "Unable to add station entry for: %pM\n",
489 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530490 return ret;
491 }
492
Sujith Manoharana97b4782011-02-21 07:48:00 +0530493 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800494 ath_dbg(common, ATH_DBG_CONFIG,
495 "Added a station entry for: %pM (idx: %d)\n",
496 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530497 } else {
498 ath_dbg(common, ATH_DBG_CONFIG,
499 "Added a station entry for VIF %d (idx: %d)\n",
500 avp->index, tsta.sta_index);
501 }
Sujithfb9987d2010-03-17 14:25:25 +0530502
Sujith Manoharana97b4782011-02-21 07:48:00 +0530503 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530504 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530505 if (!sta)
506 priv->vif_sta_pos[avp->index] = sta_idx;
507
Sujithfb9987d2010-03-17 14:25:25 +0530508 return 0;
509}
510
511static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
512 struct ieee80211_vif *vif,
513 struct ieee80211_sta *sta)
514{
515 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530516 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530517 struct ath9k_htc_sta *ista;
518 int ret;
519 u8 cmd_rsp, sta_idx;
520
521 if (sta) {
522 ista = (struct ath9k_htc_sta *) sta->drv_priv;
523 sta_idx = ista->index;
524 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530525 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530526 }
527
528 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
529 if (ret) {
530 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800531 ath_err(common,
532 "Unable to remove station entry for: %pM\n",
533 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530534 return ret;
535 }
536
Sujith Manoharana97b4782011-02-21 07:48:00 +0530537 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800538 ath_dbg(common, ATH_DBG_CONFIG,
539 "Removed a station entry for: %pM (idx: %d)\n",
540 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530541 } else {
542 ath_dbg(common, ATH_DBG_CONFIG,
543 "Removed a station entry for VIF %d (idx: %d)\n",
544 avp->index, sta_idx);
545 }
Sujithfb9987d2010-03-17 14:25:25 +0530546
Sujith Manoharana97b4782011-02-21 07:48:00 +0530547 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530548 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530549
Sujithfb9987d2010-03-17 14:25:25 +0530550 return 0;
551}
552
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530553int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530554{
555 struct ath9k_htc_cap_target tcap;
556 int ret;
557 u8 cmd_rsp;
558
559 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
560
561 /* FIXME: Values are hardcoded */
562 tcap.flags = 0x240c40;
563 tcap.flags_ext = 0x80601000;
564 tcap.ampdu_limit = 0xffff0000;
565 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530566 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530567 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530568 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530569
570 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
571
572 return ret;
573}
574
Sujith0d425a72010-05-17 12:01:16 +0530575static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
576 struct ieee80211_sta *sta,
577 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530578{
Sujithfb9987d2010-03-17 14:25:25 +0530579 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
580 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530581 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530582 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530583
Sujithea46e642010-06-02 15:53:50 +0530584 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530585
586 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
587 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530588 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530589 = (sband->bitrates[i].bitrate * 2) / 10;
590 j++;
591 }
592 }
Sujith0d425a72010-05-17 12:01:16 +0530593 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530594
595 if (sta->ht_cap.ht_supported) {
596 for (i = 0, j = 0; i < 77; i++) {
597 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530598 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530599 if (j == ATH_HTC_RATE_MAX)
600 break;
601 }
Sujith0d425a72010-05-17 12:01:16 +0530602 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530603
604 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200605 if (sta->ht_cap.mcs.rx_mask[1])
606 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530607 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
608 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530609 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530610 if (conf_is_ht40(&priv->hw->conf) &&
611 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530612 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530613 else if (conf_is_ht20(&priv->hw->conf) &&
614 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
615 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530616 }
617
Sujith0d425a72010-05-17 12:01:16 +0530618 trate->sta_index = ista->index;
619 trate->isnew = 1;
620 trate->capflags = cpu_to_be32(caps);
621}
Sujithfb9987d2010-03-17 14:25:25 +0530622
Sujith0d425a72010-05-17 12:01:16 +0530623static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
624 struct ath9k_htc_target_rate *trate)
625{
626 struct ath_common *common = ath9k_hw_common(priv->ah);
627 int ret;
628 u8 cmd_rsp;
629
630 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530631 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800632 ath_err(common,
633 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530634 }
635
Sujith0d425a72010-05-17 12:01:16 +0530636 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530637}
638
Sujith0d425a72010-05-17 12:01:16 +0530639static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
640 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530641{
Sujithfb9987d2010-03-17 14:25:25 +0530642 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530643 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530644 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530645
Sujith0d425a72010-05-17 12:01:16 +0530646 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
647 ath9k_htc_setup_rate(priv, sta, &trate);
648 ret = ath9k_htc_send_rate_cmd(priv, &trate);
649 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800650 ath_dbg(common, ATH_DBG_CONFIG,
651 "Updated target sta: %pM, rate caps: 0x%X\n",
652 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530653}
654
Sujith2c76ef82010-05-17 12:01:18 +0530655static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
656 struct ieee80211_vif *vif,
657 struct ieee80211_bss_conf *bss_conf)
658{
659 struct ath_common *common = ath9k_hw_common(priv->ah);
660 struct ath9k_htc_target_rate trate;
661 struct ieee80211_sta *sta;
662 int ret;
663
664 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
665
666 rcu_read_lock();
667 sta = ieee80211_find_sta(vif, bss_conf->bssid);
668 if (!sta) {
669 rcu_read_unlock();
670 return;
671 }
672 ath9k_htc_setup_rate(priv, sta, &trate);
673 rcu_read_unlock();
674
675 ret = ath9k_htc_send_rate_cmd(priv, &trate);
676 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800677 ath_dbg(common, ATH_DBG_CONFIG,
678 "Updated target sta: %pM, rate caps: 0x%X\n",
679 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530680}
681
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400682static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
683 struct ieee80211_vif *vif,
684 struct ieee80211_sta *sta,
685 enum ieee80211_ampdu_mlme_action action,
686 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530687{
688 struct ath_common *common = ath9k_hw_common(priv->ah);
689 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200690 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530691 int ret = 0;
692 u8 cmd_rsp;
693
Dan Carpenter0730d112010-05-08 18:24:02 +0200694 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530695 return -EINVAL;
696
Sujithfb9987d2010-03-17 14:25:25 +0530697 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530698 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530699
Sujithef98c3c2010-03-29 16:07:11 +0530700 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530701 aggr.tidno = tid & 0xf;
702 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530703
Sujithfb9987d2010-03-17 14:25:25 +0530704 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
705 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800706 ath_dbg(common, ATH_DBG_CONFIG,
707 "Unable to %s TX aggregation for (%pM, %d)\n",
708 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530709 else
Joe Perches226afe62010-12-02 19:12:37 -0800710 ath_dbg(common, ATH_DBG_CONFIG,
711 "%s TX aggregation for (%pM, %d)\n",
712 (aggr.aggr_enable) ? "Starting" : "Stopping",
713 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530714
Sujith Manoharan658ef042011-04-13 11:25:00 +0530715 spin_lock_bh(&priv->tx.tx_lock);
Sujithd7ca2132010-06-15 10:24:37 +0530716 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530717 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530718
719 return ret;
720}
721
Sujithfb9987d2010-03-17 14:25:25 +0530722/*******/
723/* ANI */
724/*******/
725
Sujith Manoharana2362542011-02-21 07:49:38 +0530726void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530727{
728 struct ath_common *common = ath9k_hw_common(priv->ah);
729 unsigned long timestamp = jiffies_to_msecs(jiffies);
730
731 common->ani.longcal_timer = timestamp;
732 common->ani.shortcal_timer = timestamp;
733 common->ani.checkani_timer = timestamp;
734
Sujith Manoharana2362542011-02-21 07:49:38 +0530735 priv->op_flags |= OP_ANI_RUNNING;
736
737 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530738 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
739}
740
Sujith Manoharana2362542011-02-21 07:49:38 +0530741void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
742{
743 cancel_delayed_work_sync(&priv->ani_work);
744 priv->op_flags &= ~OP_ANI_RUNNING;
745}
746
747void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530748{
749 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530750 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530751 struct ath_hw *ah = priv->ah;
752 struct ath_common *common = ath9k_hw_common(ah);
753 bool longcal = false;
754 bool shortcal = false;
755 bool aniflag = false;
756 unsigned int timestamp = jiffies_to_msecs(jiffies);
757 u32 cal_interval, short_cal_interval;
758
Sujith Manoharana2362542011-02-21 07:49:38 +0530759 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
760 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530761
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530762 /* Only calibrate if awake */
763 if (ah->power_mode != ATH9K_PM_AWAKE)
764 goto set_timer;
765
Sujithfb9987d2010-03-17 14:25:25 +0530766 /* Long calibration runs independently of short calibration. */
767 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
768 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800769 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530770 common->ani.longcal_timer = timestamp;
771 }
772
773 /* Short calibration applies only while caldone is false */
774 if (!common->ani.caldone) {
775 if ((timestamp - common->ani.shortcal_timer) >=
776 short_cal_interval) {
777 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800778 ath_dbg(common, ATH_DBG_ANI,
779 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530780 common->ani.shortcal_timer = timestamp;
781 common->ani.resetcal_timer = timestamp;
782 }
783 } else {
784 if ((timestamp - common->ani.resetcal_timer) >=
785 ATH_RESTART_CALINTERVAL) {
786 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
787 if (common->ani.caldone)
788 common->ani.resetcal_timer = timestamp;
789 }
790 }
791
792 /* Verify whether we must check ANI */
793 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
794 aniflag = true;
795 common->ani.checkani_timer = timestamp;
796 }
797
798 /* Skip all processing if there's nothing to do. */
799 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530800
801 ath9k_htc_ps_wakeup(priv);
802
Sujithfb9987d2010-03-17 14:25:25 +0530803 /* Call ANI routine if necessary */
804 if (aniflag)
805 ath9k_hw_ani_monitor(ah, ah->curchan);
806
807 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200808 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530809 common->ani.caldone =
810 ath9k_hw_calibrate(ah, ah->curchan,
811 common->rx_chainmask,
812 longcal);
813
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530814 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530815 }
816
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530817set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530818 /*
819 * Set timer interval based on previous results.
820 * The interval must be the shortest necessary to satisfy ANI,
821 * short calibration and long calibration.
822 */
823 cal_interval = ATH_LONG_CALINTERVAL;
824 if (priv->ah->config.enable_ani)
825 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
826 if (!common->ani.caldone)
827 cal_interval = min(cal_interval, (u32)short_cal_interval);
828
Sujith Manoharana2362542011-02-21 07:49:38 +0530829 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530830 msecs_to_jiffies(cal_interval));
831}
832
Sujithfb9987d2010-03-17 14:25:25 +0530833/**********************/
834/* mac80211 Callbacks */
835/**********************/
836
Johannes Berg7bb45682011-02-24 14:42:06 +0100837static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +0530838{
839 struct ieee80211_hdr *hdr;
840 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530841 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530842 int padpos, padsize, ret, slot;
Sujithfb9987d2010-03-17 14:25:25 +0530843
844 hdr = (struct ieee80211_hdr *) skb->data;
845
846 /* Add the padding after the header if this is not already done */
847 padpos = ath9k_cmn_padpos(hdr->frame_control);
848 padsize = padpos & 3;
849 if (padsize && skb->len > padpos) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530850 if (skb_headroom(skb) < padsize) {
851 ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n");
Johannes Berg7bb45682011-02-24 14:42:06 +0100852 goto fail_tx;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530853 }
Sujithfb9987d2010-03-17 14:25:25 +0530854 skb_push(skb, padsize);
855 memmove(skb->data, skb->data + padsize, padpos);
856 }
857
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530858 slot = ath9k_htc_tx_get_slot(priv);
859 if (slot < 0) {
860 ath_dbg(common, ATH_DBG_XMIT, "No free TX slot\n");
861 goto fail_tx;
862 }
863
864 ret = ath9k_htc_tx_start(priv, skb, slot, false);
Sujith7757dfe2010-03-29 16:07:17 +0530865 if (ret != 0) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530866 ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530867 goto clear_slot;
Sujithfb9987d2010-03-17 14:25:25 +0530868 }
869
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530870 ath9k_htc_check_stop_queues(priv);
871
Johannes Berg7bb45682011-02-24 14:42:06 +0100872 return;
Sujithfb9987d2010-03-17 14:25:25 +0530873
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530874clear_slot:
875 ath9k_htc_tx_clear_slot(priv, slot);
Sujithfb9987d2010-03-17 14:25:25 +0530876fail_tx:
877 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530878}
879
Sujith881ac6a2010-06-01 15:14:11 +0530880static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530881{
882 struct ath9k_htc_priv *priv = hw->priv;
883 struct ath_hw *ah = priv->ah;
884 struct ath_common *common = ath9k_hw_common(ah);
885 struct ieee80211_channel *curchan = hw->conf.channel;
886 struct ath9k_channel *init_channel;
887 int ret = 0;
888 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530889 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530890 u8 cmd_rsp;
891
Sujith881ac6a2010-06-01 15:14:11 +0530892 mutex_lock(&priv->mutex);
893
Joe Perches226afe62010-12-02 19:12:37 -0800894 ath_dbg(common, ATH_DBG_CONFIG,
895 "Starting driver with initial channel: %d MHz\n",
896 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530897
Sujith21d51302010-06-01 15:14:18 +0530898 /* Ensure that HW is awake before flushing RX */
899 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
900 WMI_CMD(WMI_FLUSH_RECV_CMDID);
901
Sujithfb9987d2010-03-17 14:25:25 +0530902 /* setup initial channel */
903 init_channel = ath9k_cmn_get_curchannel(hw, ah);
904
Sujithfb9987d2010-03-17 14:25:25 +0530905 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200906 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530907 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800908 ath_err(common,
909 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
910 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530911 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530912 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530913 }
914
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530915 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
916 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530917
918 mode = ath9k_htc_get_curmode(priv, init_channel);
919 htc_mode = cpu_to_be16(mode);
920 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530921 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530922 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530923
924 ath9k_host_rx_init(priv);
925
Sujith Manoharan1057b752011-02-21 07:48:09 +0530926 ret = ath9k_htc_update_cap_target(priv);
927 if (ret)
928 ath_dbg(common, ATH_DBG_CONFIG,
929 "Failed to update capability in target\n");
930
Sujithfb9987d2010-03-17 14:25:25 +0530931 priv->op_flags &= ~OP_INVALID;
932 htc_start(priv->htc);
933
Sujith Manoharan658ef042011-04-13 11:25:00 +0530934 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530935 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530936 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530937
938 ieee80211_wake_queues(hw);
939
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530940 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
941 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
942 AR_STOMP_LOW_WLAN_WGHT);
943 ath9k_hw_btcoex_enable(ah);
944 ath_htc_resume_btcoex_work(priv);
945 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530946 mutex_unlock(&priv->mutex);
947
948 return ret;
949}
950
Sujith881ac6a2010-06-01 15:14:11 +0530951static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530952{
953 struct ath9k_htc_priv *priv = hw->priv;
954 struct ath_hw *ah = priv->ah;
955 struct ath_common *common = ath9k_hw_common(ah);
956 int ret = 0;
957 u8 cmd_rsp;
958
Sujith881ac6a2010-06-01 15:14:11 +0530959 mutex_lock(&priv->mutex);
960
Sujithfb9987d2010-03-17 14:25:25 +0530961 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -0800962 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530963 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530964 return;
965 }
966
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530967 ath9k_htc_ps_wakeup(priv);
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530968
Sujithfb9987d2010-03-17 14:25:25 +0530969 WMI_CMD(WMI_DISABLE_INTR_CMDID);
970 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
971 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100972
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100973 tasklet_kill(&priv->rx_tasklet);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100974
Sujith Manoharanb587fc82011-04-13 11:25:59 +0530975 ath9k_htc_tx_drain(priv);
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530976 ath9k_wmi_event_drain(priv);
977
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100978 mutex_unlock(&priv->mutex);
979
980 /* Cancel all the running timers/work .. */
981 cancel_work_sync(&priv->fatal_work);
982 cancel_work_sync(&priv->ps_work);
983 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +0530984 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100985 ath9k_led_stop_brightness(priv);
986
987 mutex_lock(&priv->mutex);
988
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530989 if (ah->btcoex_hw.enabled) {
990 ath9k_hw_btcoex_disable(ah);
991 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
992 ath_htc_cancel_btcoex_work(priv);
993 }
994
Sujith Manoharana97b4782011-02-21 07:48:00 +0530995 /* Remove a monitor interface if it's present. */
996 if (priv->ah->is_monitoring)
997 ath9k_htc_remove_monitor_interface(priv);
998
Sujithe9201f02010-06-01 15:14:17 +0530999 ath9k_hw_phy_disable(ah);
1000 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +05301001 ath9k_htc_ps_restore(priv);
1002 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1003
Sujithfb9987d2010-03-17 14:25:25 +05301004 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301005
Joe Perches226afe62010-12-02 19:12:37 -08001006 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301007 mutex_unlock(&priv->mutex);
1008}
1009
Sujithfb9987d2010-03-17 14:25:25 +05301010static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1011 struct ieee80211_vif *vif)
1012{
1013 struct ath9k_htc_priv *priv = hw->priv;
1014 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1015 struct ath_common *common = ath9k_hw_common(priv->ah);
1016 struct ath9k_htc_target_vif hvif;
1017 int ret = 0;
1018 u8 cmd_rsp;
1019
1020 mutex_lock(&priv->mutex);
1021
Sujith Manoharana97b4782011-02-21 07:48:00 +05301022 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301023 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301024 return -ENOBUFS;
1025 }
1026
1027 if (priv->num_ibss_vif ||
1028 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1029 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1030 mutex_unlock(&priv->mutex);
1031 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301032 }
1033
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301034 if (((vif->type == NL80211_IFTYPE_AP) ||
1035 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1036 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1037 ath_err(common, "Max. number of beaconing interfaces reached\n");
1038 mutex_unlock(&priv->mutex);
1039 return -ENOBUFS;
1040 }
1041
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301042 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301043 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1044 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1045
1046 switch (vif->type) {
1047 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301048 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301049 break;
1050 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301051 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301052 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301053 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301054 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301055 break;
Sujithfb9987d2010-03-17 14:25:25 +05301056 default:
Joe Perches38002762010-12-02 19:12:36 -08001057 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301058 "Interface type %d not yet supported\n", vif->type);
1059 ret = -EOPNOTSUPP;
1060 goto out;
1061 }
1062
Sujithfb9987d2010-03-17 14:25:25 +05301063 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301064 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301065 hvif.rtsthreshold = cpu_to_be16(2304);
1066 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1067 if (ret)
1068 goto out;
1069
Sujithfb9987d2010-03-17 14:25:25 +05301070 /*
1071 * We need a node in target to tx mgmt frames
1072 * before association.
1073 */
1074 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301075 if (ret) {
1076 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301077 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301078 }
Sujithfb9987d2010-03-17 14:25:25 +05301079
Sujith Manoharan585895c2011-02-21 07:48:46 +05301080 ath9k_htc_set_bssid_mask(priv, vif);
1081
Sujith Manoharana97b4782011-02-21 07:48:00 +05301082 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301083 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301084
Sujith Manoharan0df83592011-02-21 07:49:15 +05301085 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301086
1087 if ((vif->type == NL80211_IFTYPE_AP) ||
1088 (vif->type == NL80211_IFTYPE_ADHOC))
1089 ath9k_htc_assign_bslot(priv, vif);
1090
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301091 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301092
Sujith Manoharana2362542011-02-21 07:49:38 +05301093 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301094 !(priv->op_flags & OP_ANI_RUNNING)) {
1095 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301096 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301097 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301098
Sujith Manoharana97b4782011-02-21 07:48:00 +05301099 ath_dbg(common, ATH_DBG_CONFIG,
1100 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1101
Sujithfb9987d2010-03-17 14:25:25 +05301102out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301103 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301104 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301105
Sujithfb9987d2010-03-17 14:25:25 +05301106 return ret;
1107}
1108
1109static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1110 struct ieee80211_vif *vif)
1111{
1112 struct ath9k_htc_priv *priv = hw->priv;
1113 struct ath_common *common = ath9k_hw_common(priv->ah);
1114 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1115 struct ath9k_htc_target_vif hvif;
1116 int ret = 0;
1117 u8 cmd_rsp;
1118
Sujithfb9987d2010-03-17 14:25:25 +05301119 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301120 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301121
1122 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1123 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1124 hvif.index = avp->index;
1125 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1126 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301127 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301128
1129 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301130
Sujith Manoharan0df83592011-02-21 07:49:15 +05301131 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301132
1133 if ((vif->type == NL80211_IFTYPE_AP) ||
1134 (vif->type == NL80211_IFTYPE_ADHOC))
1135 ath9k_htc_remove_bslot(priv, vif);
1136
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301137 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301138
Sujith Manoharana2362542011-02-21 07:49:38 +05301139 /*
1140 * Stop ANI only if there are no associated station interfaces.
1141 */
1142 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1143 priv->rearm_ani = false;
1144 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1145 ath9k_htc_vif_iter, priv);
1146 if (!priv->rearm_ani)
1147 ath9k_htc_stop_ani(priv);
1148 }
1149
Sujith Manoharana97b4782011-02-21 07:48:00 +05301150 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1151
Sujithcb551df2010-06-01 15:14:12 +05301152 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301153 mutex_unlock(&priv->mutex);
1154}
1155
1156static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1157{
1158 struct ath9k_htc_priv *priv = hw->priv;
1159 struct ath_common *common = ath9k_hw_common(priv->ah);
1160 struct ieee80211_conf *conf = &hw->conf;
1161
1162 mutex_lock(&priv->mutex);
1163
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301164 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1165 bool enable_radio = false;
1166 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1167
Sujith23367762010-06-01 15:14:16 +05301168 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301169 if (!idle && priv->ps_idle)
1170 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301171 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301172 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301173
1174 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001175 ath_dbg(common, ATH_DBG_CONFIG,
1176 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301177 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1178 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301179 }
1180 }
1181
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301182 /*
1183 * Monitor interface should be added before
1184 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1185 */
1186 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301187 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1188 !priv->ah->is_monitoring)
1189 ath9k_htc_add_monitor_interface(priv);
1190 else if (priv->ah->is_monitoring)
1191 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301192 }
1193
Sujithfb9987d2010-03-17 14:25:25 +05301194 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1195 struct ieee80211_channel *curchan = hw->conf.channel;
1196 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301197
Joe Perches226afe62010-12-02 19:12:37 -08001198 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1199 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301200
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001201 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1202 hw->conf.channel,
1203 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301204
1205 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001206 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301207 mutex_unlock(&priv->mutex);
1208 return -EINVAL;
1209 }
1210
1211 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301212
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301213 if (changed & IEEE80211_CONF_CHANGE_PS) {
1214 if (conf->flags & IEEE80211_CONF_PS) {
1215 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1216 priv->ps_enabled = true;
1217 } else {
1218 priv->ps_enabled = false;
1219 cancel_work_sync(&priv->ps_work);
1220 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1221 }
1222 }
Sujithfb9987d2010-03-17 14:25:25 +05301223
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301224 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1225 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301226 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1227 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301228 }
1229
Sujith23367762010-06-01 15:14:16 +05301230 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1231 mutex_lock(&priv->htc_pm_lock);
1232 if (!priv->ps_idle) {
1233 mutex_unlock(&priv->htc_pm_lock);
1234 goto out;
1235 }
1236 mutex_unlock(&priv->htc_pm_lock);
1237
Joe Perches226afe62010-12-02 19:12:37 -08001238 ath_dbg(common, ATH_DBG_CONFIG,
1239 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301240 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301241 }
1242
Sujith23367762010-06-01 15:14:16 +05301243out:
Sujithfb9987d2010-03-17 14:25:25 +05301244 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301245 return 0;
1246}
1247
1248#define SUPPORTED_FILTERS \
1249 (FIF_PROMISC_IN_BSS | \
1250 FIF_ALLMULTI | \
1251 FIF_CONTROL | \
1252 FIF_PSPOLL | \
1253 FIF_OTHER_BSS | \
1254 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301255 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301256 FIF_FCSFAIL)
1257
1258static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1259 unsigned int changed_flags,
1260 unsigned int *total_flags,
1261 u64 multicast)
1262{
1263 struct ath9k_htc_priv *priv = hw->priv;
1264 u32 rfilt;
1265
1266 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301267 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301268
Sujithfb9987d2010-03-17 14:25:25 +05301269 changed_flags &= SUPPORTED_FILTERS;
1270 *total_flags &= SUPPORTED_FILTERS;
1271
1272 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301273 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301274 ath9k_hw_setrxfilter(priv->ah, rfilt);
1275
Joe Perches226afe62010-12-02 19:12:37 -08001276 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1277 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301278
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301279 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301280 mutex_unlock(&priv->mutex);
1281}
1282
Sujithabd984e2010-05-18 15:26:04 +05301283static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1284 struct ieee80211_vif *vif,
1285 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301286{
1287 struct ath9k_htc_priv *priv = hw->priv;
1288 int ret;
1289
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301290 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301291 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301292 ret = ath9k_htc_add_station(priv, vif, sta);
1293 if (!ret)
1294 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301295 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301296 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301297
1298 return ret;
1299}
1300
1301static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1302 struct ieee80211_vif *vif,
1303 struct ieee80211_sta *sta)
1304{
1305 struct ath9k_htc_priv *priv = hw->priv;
1306 int ret;
1307
1308 mutex_lock(&priv->mutex);
1309 ath9k_htc_ps_wakeup(priv);
1310 ret = ath9k_htc_remove_station(priv, vif, sta);
1311 ath9k_htc_ps_restore(priv);
1312 mutex_unlock(&priv->mutex);
1313
1314 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301315}
1316
1317static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1318 const struct ieee80211_tx_queue_params *params)
1319{
1320 struct ath9k_htc_priv *priv = hw->priv;
1321 struct ath_common *common = ath9k_hw_common(priv->ah);
1322 struct ath9k_tx_queue_info qi;
1323 int ret = 0, qnum;
1324
1325 if (queue >= WME_NUM_AC)
1326 return 0;
1327
1328 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301329 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301330
1331 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1332
1333 qi.tqi_aifs = params->aifs;
1334 qi.tqi_cwmin = params->cw_min;
1335 qi.tqi_cwmax = params->cw_max;
1336 qi.tqi_burstTime = params->txop;
1337
1338 qnum = get_hw_qnum(queue, priv->hwq_map);
1339
Joe Perches226afe62010-12-02 19:12:37 -08001340 ath_dbg(common, ATH_DBG_CONFIG,
1341 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1342 queue, qnum, params->aifs, params->cw_min,
1343 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301344
Sujithe1572c52010-03-24 13:42:13 +05301345 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301346 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001347 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301348 goto out;
1349 }
Sujithfb9987d2010-03-17 14:25:25 +05301350
Sujith764580f2010-06-01 15:14:19 +05301351 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001352 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301353 ath9k_htc_beaconq_config(priv);
1354out:
Sujithcb551df2010-06-01 15:14:12 +05301355 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301356 mutex_unlock(&priv->mutex);
1357
1358 return ret;
1359}
1360
1361static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1362 enum set_key_cmd cmd,
1363 struct ieee80211_vif *vif,
1364 struct ieee80211_sta *sta,
1365 struct ieee80211_key_conf *key)
1366{
1367 struct ath9k_htc_priv *priv = hw->priv;
1368 struct ath_common *common = ath9k_hw_common(priv->ah);
1369 int ret = 0;
1370
Sujithe1572c52010-03-24 13:42:13 +05301371 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301372 return -ENOSPC;
1373
1374 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001375 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301376 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301377
1378 switch (cmd) {
1379 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001380 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301381 if (ret >= 0) {
1382 key->hw_key_idx = ret;
1383 /* push IV and Michael MIC generation to stack */
1384 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001385 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301386 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001387 if (priv->ah->sw_mgmt_crypto &&
1388 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301389 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1390 ret = 0;
1391 }
1392 break;
1393 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001394 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301395 break;
1396 default:
1397 ret = -EINVAL;
1398 }
1399
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301400 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301401 mutex_unlock(&priv->mutex);
1402
1403 return ret;
1404}
1405
1406static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1407 struct ieee80211_vif *vif,
1408 struct ieee80211_bss_conf *bss_conf,
1409 u32 changed)
1410{
1411 struct ath9k_htc_priv *priv = hw->priv;
1412 struct ath_hw *ah = priv->ah;
1413 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301414 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301415
1416 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301417 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301418
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301419 /*
1420 * Set the HW AID/BSSID only for the first station interface
1421 * or in IBSS mode.
1422 */
1423 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1424 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1425 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301426
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301427
1428 if (changed & BSS_CHANGED_ASSOC) {
1429 if (set_assoc) {
1430 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1431 bss_conf->assoc);
1432
1433 common->curaid = bss_conf->assoc ?
1434 bss_conf->aid : 0;
1435
1436 if (bss_conf->assoc)
1437 ath9k_htc_start_ani(priv);
1438 else
1439 ath9k_htc_stop_ani(priv);
1440 }
Sujithfb9987d2010-03-17 14:25:25 +05301441 }
1442
1443 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301444 if (set_assoc) {
1445 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1446 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301447
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301448 ath_dbg(common, ATH_DBG_CONFIG,
1449 "BSSID: %pM aid: 0x%x\n",
1450 common->curbssid, common->curaid);
1451 }
Sujithfb9987d2010-03-17 14:25:25 +05301452 }
1453
Sujith Manoharana5fae372011-02-21 07:49:53 +05301454 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1455 ath_dbg(common, ATH_DBG_CONFIG,
1456 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301457 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301458 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301459 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301460 }
1461
Sujith Manoharana5fae372011-02-21 07:49:53 +05301462 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1463 /*
1464 * Disable SWBA interrupt only if there are no
1465 * AP/IBSS interfaces.
1466 */
1467 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1468 ath_dbg(common, ATH_DBG_CONFIG,
1469 "Beacon disabled for BSS: %pM\n",
1470 bss_conf->bssid);
1471 priv->op_flags &= ~OP_ENABLE_BEACON;
1472 ath9k_htc_beacon_config(priv, vif);
1473 }
1474 }
1475
1476 if (changed & BSS_CHANGED_BEACON_INT) {
1477 /*
1478 * Reset the HW TSF for the first AP interface.
1479 */
1480 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1481 (priv->nvifs == 1) &&
1482 (priv->num_ap_vif == 1) &&
1483 (vif->type == NL80211_IFTYPE_AP)) {
1484 priv->op_flags |= OP_TSF_RESET;
1485 }
1486 ath_dbg(common, ATH_DBG_CONFIG,
1487 "Beacon interval changed for BSS: %pM\n",
1488 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301489 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301490 }
1491
Sujithfb9987d2010-03-17 14:25:25 +05301492 if (changed & BSS_CHANGED_ERP_SLOT) {
1493 if (bss_conf->use_short_slot)
1494 ah->slottime = 9;
1495 else
1496 ah->slottime = 20;
1497
1498 ath9k_hw_init_global_settings(ah);
1499 }
1500
Sujith2c76ef82010-05-17 12:01:18 +05301501 if (changed & BSS_CHANGED_HT)
1502 ath9k_htc_update_rate(priv, vif, bss_conf);
1503
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301504 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301505 mutex_unlock(&priv->mutex);
1506}
1507
1508static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1509{
1510 struct ath9k_htc_priv *priv = hw->priv;
1511 u64 tsf;
1512
1513 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301514 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301515 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301516 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301517 mutex_unlock(&priv->mutex);
1518
1519 return tsf;
1520}
1521
1522static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1523{
1524 struct ath9k_htc_priv *priv = hw->priv;
1525
1526 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301527 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301528 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301529 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301530 mutex_unlock(&priv->mutex);
1531}
1532
1533static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1534{
1535 struct ath9k_htc_priv *priv = hw->priv;
1536
1537 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301538 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301539 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301540 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301541 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301542}
1543
1544static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1545 struct ieee80211_vif *vif,
1546 enum ieee80211_ampdu_mlme_action action,
1547 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001548 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301549{
1550 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301551 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301552 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301553
Sujith Manoharan87df8952011-02-21 07:49:08 +05301554 mutex_lock(&priv->mutex);
1555
Sujithfb9987d2010-03-17 14:25:25 +05301556 switch (action) {
1557 case IEEE80211_AMPDU_RX_START:
1558 break;
1559 case IEEE80211_AMPDU_RX_STOP:
1560 break;
1561 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301562 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1563 if (!ret)
1564 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1565 break;
Sujithfb9987d2010-03-17 14:25:25 +05301566 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301567 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1568 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301569 break;
1570 case IEEE80211_AMPDU_TX_OPERATIONAL:
1571 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301572 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301573 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301574 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301575 break;
1576 default:
Joe Perches38002762010-12-02 19:12:36 -08001577 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301578 }
1579
Sujith Manoharan87df8952011-02-21 07:49:08 +05301580 mutex_unlock(&priv->mutex);
1581
Sujithd7ca2132010-06-15 10:24:37 +05301582 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301583}
1584
1585static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1586{
1587 struct ath9k_htc_priv *priv = hw->priv;
1588
1589 mutex_lock(&priv->mutex);
1590 spin_lock_bh(&priv->beacon_lock);
1591 priv->op_flags |= OP_SCANNING;
1592 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301593 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301594 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301595 mutex_unlock(&priv->mutex);
1596}
1597
1598static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1599{
1600 struct ath9k_htc_priv *priv = hw->priv;
1601
1602 mutex_lock(&priv->mutex);
1603 spin_lock_bh(&priv->beacon_lock);
1604 priv->op_flags &= ~OP_SCANNING;
1605 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301606 ath9k_htc_ps_wakeup(priv);
1607 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301608 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301609 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301610}
1611
1612static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1613{
1614 return 0;
1615}
1616
1617static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1618 u8 coverage_class)
1619{
1620 struct ath9k_htc_priv *priv = hw->priv;
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 priv->ah->coverage_class = coverage_class;
1625 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301626 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301627 mutex_unlock(&priv->mutex);
1628}
1629
1630struct ieee80211_ops ath9k_htc_ops = {
1631 .tx = ath9k_htc_tx,
1632 .start = ath9k_htc_start,
1633 .stop = ath9k_htc_stop,
1634 .add_interface = ath9k_htc_add_interface,
1635 .remove_interface = ath9k_htc_remove_interface,
1636 .config = ath9k_htc_config,
1637 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301638 .sta_add = ath9k_htc_sta_add,
1639 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301640 .conf_tx = ath9k_htc_conf_tx,
1641 .bss_info_changed = ath9k_htc_bss_info_changed,
1642 .set_key = ath9k_htc_set_key,
1643 .get_tsf = ath9k_htc_get_tsf,
1644 .set_tsf = ath9k_htc_set_tsf,
1645 .reset_tsf = ath9k_htc_reset_tsf,
1646 .ampdu_action = ath9k_htc_ampdu_action,
1647 .sw_scan_start = ath9k_htc_sw_scan_start,
1648 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1649 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1650 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1651 .set_coverage_class = ath9k_htc_set_coverage_class,
1652};