blob: c7e056b40e1d34812333506b0119fe95738b5931 [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);
196 htc_stop(priv->htc);
197 WMI_CMD(WMI_DISABLE_INTR_CMDID);
198 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
199 WMI_CMD(WMI_STOP_RECV_CMDID);
200
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530201 ath9k_wmi_event_drain(priv);
202
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530203 caldata = &priv->caldata;
Sujith Manoharan73908672010-12-28 14:28:27 +0530204 ret = ath9k_hw_reset(ah, ah->curchan, caldata, false);
205 if (ret) {
206 ath_err(common,
207 "Unable to reset device (%u Mhz) reset status %d\n",
208 channel->center_freq, ret);
209 }
210
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530211 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
212 &priv->curtxpow);
Sujith Manoharan73908672010-12-28 14:28:27 +0530213
214 WMI_CMD(WMI_START_RECV_CMDID);
215 ath9k_host_rx_init(priv);
216
217 mode = ath9k_htc_get_curmode(priv, ah->curchan);
218 htc_mode = cpu_to_be16(mode);
219 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
220
221 WMI_CMD(WMI_ENABLE_INTR_CMDID);
222 htc_start(priv->htc);
Sujith Manoharan7c277342011-02-21 07:48:39 +0530223 ath9k_htc_vif_reconfig(priv);
Sujith Manoharan73908672010-12-28 14:28:27 +0530224 ieee80211_wake_queues(priv->hw);
225
226 ath9k_htc_ps_restore(priv);
227 mutex_unlock(&priv->mutex);
228}
229
Sujithfb9987d2010-03-17 14:25:25 +0530230static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
231 struct ieee80211_hw *hw,
232 struct ath9k_channel *hchan)
233{
234 struct ath_hw *ah = priv->ah;
235 struct ath_common *common = ath9k_hw_common(ah);
236 struct ieee80211_conf *conf = &common->hw->conf;
Sujith Manoharan039a0722010-12-28 14:28:37 +0530237 bool fastcc;
Sujithfb9987d2010-03-17 14:25:25 +0530238 struct ieee80211_channel *channel = hw->conf.channel;
Vivek Natarajan8354dd32011-02-18 16:09:51 +0530239 struct ath9k_hw_cal_data *caldata = NULL;
Sujithfb9987d2010-03-17 14:25:25 +0530240 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530241 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530242 u8 cmd_rsp;
243 int ret;
244
245 if (priv->op_flags & OP_INVALID)
246 return -EIO;
247
Sujith Manoharan039a0722010-12-28 14:28:37 +0530248 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
Sujithfb9987d2010-03-17 14:25:25 +0530249
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530250 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530251 htc_stop(priv->htc);
252 WMI_CMD(WMI_DISABLE_INTR_CMDID);
253 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
254 WMI_CMD(WMI_STOP_RECV_CMDID);
255
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530256 ath9k_wmi_event_drain(priv);
257
Joe Perches226afe62010-12-02 19:12:37 -0800258 ath_dbg(common, ATH_DBG_CONFIG,
259 "(%u MHz) -> (%u MHz), HT: %d, HT40: %d fastcc: %d\n",
260 priv->ah->curchan->channel,
261 channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf),
262 fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530263
Rajkumar Manoharan4e3ae382011-01-15 01:33:28 +0530264 if (!fastcc)
265 caldata = &priv->caldata;
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200266 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
Sujithfb9987d2010-03-17 14:25:25 +0530267 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800268 ath_err(common,
269 "Unable to reset channel (%u Mhz) reset status %d\n",
270 channel->center_freq, ret);
Sujithfb9987d2010-03-17 14:25:25 +0530271 goto err;
272 }
273
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530274 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
275 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530276
277 WMI_CMD(WMI_START_RECV_CMDID);
278 if (ret)
279 goto err;
280
281 ath9k_host_rx_init(priv);
282
283 mode = ath9k_htc_get_curmode(priv, hchan);
284 htc_mode = cpu_to_be16(mode);
285 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
286 if (ret)
287 goto err;
288
289 WMI_CMD(WMI_ENABLE_INTR_CMDID);
290 if (ret)
291 goto err;
292
293 htc_start(priv->htc);
Sujith Manoharana5fae372011-02-21 07:49:53 +0530294
295 if (!(priv->op_flags & OP_SCANNING) &&
296 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
297 ath9k_htc_vif_reconfig(priv);
298
Sujithfb9987d2010-03-17 14:25:25 +0530299err:
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530300 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530301 return ret;
302}
303
Sujith Manoharana97b4782011-02-21 07:48:00 +0530304/*
305 * Monitor mode handling is a tad complicated because the firmware requires
306 * an interface to be created exclusively, while mac80211 doesn't associate
307 * an interface with the mode.
308 *
309 * So, for now, only one monitor interface can be configured.
310 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530311static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530312{
313 struct ath_common *common = ath9k_hw_common(priv->ah);
314 struct ath9k_htc_target_vif hvif;
315 int ret = 0;
316 u8 cmd_rsp;
317
Sujith Manoharancc721282011-01-03 21:22:18 +0530318 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
319 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530320 hvif.index = priv->mon_vif_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530321 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
322 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530323 priv->vif_slot &= ~(1 << priv->mon_vif_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530324}
325
326static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv)
327{
328 struct ath_common *common = ath9k_hw_common(priv->ah);
329 struct ath9k_htc_target_vif hvif;
330 struct ath9k_htc_target_sta tsta;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530331 int ret = 0, sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530332 u8 cmd_rsp;
333
Sujith Manoharana97b4782011-02-21 07:48:00 +0530334 if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) ||
335 (priv->nstations >= ATH9K_HTC_MAX_STA)) {
336 ret = -ENOBUFS;
337 goto err_vif;
338 }
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530339
Sujith Manoharana97b4782011-02-21 07:48:00 +0530340 sta_idx = ffz(priv->sta_slot);
341 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) {
342 ret = -ENOBUFS;
343 goto err_vif;
344 }
Sujith Manoharancc721282011-01-03 21:22:18 +0530345
346 /*
347 * Add an interface.
348 */
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530349 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
350 memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN);
351
Sujith Manoharane4c62502011-04-13 11:24:43 +0530352 hvif.opmode = HTC_M_MONITOR;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530353 hvif.index = ffz(priv->vif_slot);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530354
355 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
356 if (ret)
Sujith Manoharana97b4782011-02-21 07:48:00 +0530357 goto err_vif;
358
359 /*
360 * Assign the monitor interface index as a special case here.
361 * This is needed when the interface is brought down.
362 */
363 priv->mon_vif_idx = hvif.index;
364 priv->vif_slot |= (1 << hvif.index);
365
366 /*
367 * Set the hardware mode to monitor only if there are no
368 * other interfaces.
369 */
370 if (!priv->nvifs)
371 priv->ah->opmode = NL80211_IFTYPE_MONITOR;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530372
373 priv->nvifs++;
Sujith Manoharancc721282011-01-03 21:22:18 +0530374
375 /*
376 * Associate a station with the interface for packet injection.
377 */
Sujith Manoharancc721282011-01-03 21:22:18 +0530378 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
379
380 memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN);
381
382 tsta.is_vif_sta = 1;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530383 tsta.sta_index = sta_idx;
Sujith Manoharancc721282011-01-03 21:22:18 +0530384 tsta.vif_index = hvif.index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530385 tsta.maxampdu = cpu_to_be16(0xffff);
Sujith Manoharancc721282011-01-03 21:22:18 +0530386
387 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
388 if (ret) {
389 ath_err(common, "Unable to add station entry for monitor mode\n");
Sujith Manoharana97b4782011-02-21 07:48:00 +0530390 goto err_sta;
Sujith Manoharancc721282011-01-03 21:22:18 +0530391 }
392
Sujith Manoharana97b4782011-02-21 07:48:00 +0530393 priv->sta_slot |= (1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530394 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530395 priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530396 priv->ah->is_monitoring = true;
397
Sujith Manoharana97b4782011-02-21 07:48:00 +0530398 ath_dbg(common, ATH_DBG_CONFIG,
399 "Attached a monitor interface at idx: %d, sta idx: %d\n",
400 priv->mon_vif_idx, sta_idx);
401
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530402 return 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530403
Sujith Manoharana97b4782011-02-21 07:48:00 +0530404err_sta:
Sujith Manoharancc721282011-01-03 21:22:18 +0530405 /*
406 * Remove the interface from the target.
407 */
408 __ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530409err_vif:
410 ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n");
411
Sujith Manoharancc721282011-01-03 21:22:18 +0530412 return ret;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530413}
414
415static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv)
416{
417 struct ath_common *common = ath9k_hw_common(priv->ah);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530418 int ret = 0;
Sujith Manoharancc721282011-01-03 21:22:18 +0530419 u8 cmd_rsp, sta_idx;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530420
Sujith Manoharancc721282011-01-03 21:22:18 +0530421 __ath9k_htc_remove_monitor_interface(priv);
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530422
Sujith Manoharana97b4782011-02-21 07:48:00 +0530423 sta_idx = priv->vif_sta_pos[priv->mon_vif_idx];
Sujith Manoharancc721282011-01-03 21:22:18 +0530424
425 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
426 if (ret) {
427 ath_err(common, "Unable to remove station entry for monitor mode\n");
428 return ret;
429 }
430
Sujith Manoharana97b4782011-02-21 07:48:00 +0530431 priv->sta_slot &= ~(1 << sta_idx);
Sujith Manoharancc721282011-01-03 21:22:18 +0530432 priv->nstations--;
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530433 priv->ah->is_monitoring = false;
Sujith Manoharancc721282011-01-03 21:22:18 +0530434
Sujith Manoharana97b4782011-02-21 07:48:00 +0530435 ath_dbg(common, ATH_DBG_CONFIG,
436 "Removed a monitor interface at idx: %d, sta idx: %d\n",
437 priv->mon_vif_idx, sta_idx);
438
Sujith Manoharancc721282011-01-03 21:22:18 +0530439 return 0;
Rajkumar Manoharan81fc2a32010-11-26 23:24:33 +0530440}
441
Sujithfb9987d2010-03-17 14:25:25 +0530442static int ath9k_htc_add_station(struct ath9k_htc_priv *priv,
443 struct ieee80211_vif *vif,
444 struct ieee80211_sta *sta)
445{
446 struct ath_common *common = ath9k_hw_common(priv->ah);
447 struct ath9k_htc_target_sta tsta;
448 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
449 struct ath9k_htc_sta *ista;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530450 int ret, sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530451 u8 cmd_rsp;
452
453 if (priv->nstations >= ATH9K_HTC_MAX_STA)
454 return -ENOBUFS;
455
Sujith Manoharana97b4782011-02-21 07:48:00 +0530456 sta_idx = ffz(priv->sta_slot);
457 if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA))
458 return -ENOBUFS;
459
Sujithfb9987d2010-03-17 14:25:25 +0530460 memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta));
461
462 if (sta) {
463 ista = (struct ath9k_htc_sta *) sta->drv_priv;
464 memcpy(&tsta.macaddr, sta->addr, ETH_ALEN);
465 memcpy(&tsta.bssid, common->curbssid, ETH_ALEN);
Sujithfb9987d2010-03-17 14:25:25 +0530466 tsta.is_vif_sta = 0;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530467 ista->index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530468 } else {
469 memcpy(&tsta.macaddr, vif->addr, ETH_ALEN);
470 tsta.is_vif_sta = 1;
471 }
472
Sujith Manoharana97b4782011-02-21 07:48:00 +0530473 tsta.sta_index = sta_idx;
Sujithfb9987d2010-03-17 14:25:25 +0530474 tsta.vif_index = avp->index;
Sujith Manoharanb97c57f2011-04-13 11:24:37 +0530475 tsta.maxampdu = cpu_to_be16(0xffff);
Sujithfb9987d2010-03-17 14:25:25 +0530476 if (sta && sta->ht_cap.ht_supported)
477 tsta.flags = cpu_to_be16(ATH_HTC_STA_HT);
478
479 WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta);
480 if (ret) {
481 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800482 ath_err(common,
483 "Unable to add station entry for: %pM\n",
484 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530485 return ret;
486 }
487
Sujith Manoharana97b4782011-02-21 07:48:00 +0530488 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800489 ath_dbg(common, ATH_DBG_CONFIG,
490 "Added a station entry for: %pM (idx: %d)\n",
491 sta->addr, tsta.sta_index);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530492 } else {
493 ath_dbg(common, ATH_DBG_CONFIG,
494 "Added a station entry for VIF %d (idx: %d)\n",
495 avp->index, tsta.sta_index);
496 }
Sujithfb9987d2010-03-17 14:25:25 +0530497
Sujith Manoharana97b4782011-02-21 07:48:00 +0530498 priv->sta_slot |= (1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530499 priv->nstations++;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530500 if (!sta)
501 priv->vif_sta_pos[avp->index] = sta_idx;
502
Sujithfb9987d2010-03-17 14:25:25 +0530503 return 0;
504}
505
506static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv,
507 struct ieee80211_vif *vif,
508 struct ieee80211_sta *sta)
509{
510 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530511 struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530512 struct ath9k_htc_sta *ista;
513 int ret;
514 u8 cmd_rsp, sta_idx;
515
516 if (sta) {
517 ista = (struct ath9k_htc_sta *) sta->drv_priv;
518 sta_idx = ista->index;
519 } else {
Sujith Manoharana97b4782011-02-21 07:48:00 +0530520 sta_idx = priv->vif_sta_pos[avp->index];
Sujithfb9987d2010-03-17 14:25:25 +0530521 }
522
523 WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx);
524 if (ret) {
525 if (sta)
Joe Perches38002762010-12-02 19:12:36 -0800526 ath_err(common,
527 "Unable to remove station entry for: %pM\n",
528 sta->addr);
Sujithfb9987d2010-03-17 14:25:25 +0530529 return ret;
530 }
531
Sujith Manoharana97b4782011-02-21 07:48:00 +0530532 if (sta) {
Joe Perches226afe62010-12-02 19:12:37 -0800533 ath_dbg(common, ATH_DBG_CONFIG,
534 "Removed a station entry for: %pM (idx: %d)\n",
535 sta->addr, sta_idx);
Sujith Manoharana97b4782011-02-21 07:48:00 +0530536 } else {
537 ath_dbg(common, ATH_DBG_CONFIG,
538 "Removed a station entry for VIF %d (idx: %d)\n",
539 avp->index, sta_idx);
540 }
Sujithfb9987d2010-03-17 14:25:25 +0530541
Sujith Manoharana97b4782011-02-21 07:48:00 +0530542 priv->sta_slot &= ~(1 << sta_idx);
Sujithfb9987d2010-03-17 14:25:25 +0530543 priv->nstations--;
Sujith Manoharana97b4782011-02-21 07:48:00 +0530544
Sujithfb9987d2010-03-17 14:25:25 +0530545 return 0;
546}
547
Sujith Manoharan55de80d2011-01-05 01:06:21 +0530548int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530549{
550 struct ath9k_htc_cap_target tcap;
551 int ret;
552 u8 cmd_rsp;
553
554 memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target));
555
556 /* FIXME: Values are hardcoded */
557 tcap.flags = 0x240c40;
558 tcap.flags_ext = 0x80601000;
559 tcap.ampdu_limit = 0xffff0000;
560 tcap.ampdu_subframes = 20;
Sujith29d90752010-06-02 15:53:43 +0530561 tcap.tx_chainmask_legacy = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530562 tcap.protmode = 1;
Sujith29d90752010-06-02 15:53:43 +0530563 tcap.tx_chainmask = priv->ah->caps.tx_chainmask;
Sujithfb9987d2010-03-17 14:25:25 +0530564
565 WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap);
566
567 return ret;
568}
569
Sujith0d425a72010-05-17 12:01:16 +0530570static void ath9k_htc_setup_rate(struct ath9k_htc_priv *priv,
571 struct ieee80211_sta *sta,
572 struct ath9k_htc_target_rate *trate)
Sujithfb9987d2010-03-17 14:25:25 +0530573{
Sujithfb9987d2010-03-17 14:25:25 +0530574 struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv;
575 struct ieee80211_supported_band *sband;
Sujithfb9987d2010-03-17 14:25:25 +0530576 u32 caps = 0;
Sujith0d425a72010-05-17 12:01:16 +0530577 int i, j;
Sujithfb9987d2010-03-17 14:25:25 +0530578
Sujithea46e642010-06-02 15:53:50 +0530579 sband = priv->hw->wiphy->bands[priv->hw->conf.channel->band];
Sujithfb9987d2010-03-17 14:25:25 +0530580
581 for (i = 0, j = 0; i < sband->n_bitrates; i++) {
582 if (sta->supp_rates[sband->band] & BIT(i)) {
Sujith0d425a72010-05-17 12:01:16 +0530583 trate->rates.legacy_rates.rs_rates[j]
Sujithfb9987d2010-03-17 14:25:25 +0530584 = (sband->bitrates[i].bitrate * 2) / 10;
585 j++;
586 }
587 }
Sujith0d425a72010-05-17 12:01:16 +0530588 trate->rates.legacy_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530589
590 if (sta->ht_cap.ht_supported) {
591 for (i = 0, j = 0; i < 77; i++) {
592 if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8)))
Sujith0d425a72010-05-17 12:01:16 +0530593 trate->rates.ht_rates.rs_rates[j++] = i;
Sujithfb9987d2010-03-17 14:25:25 +0530594 if (j == ATH_HTC_RATE_MAX)
595 break;
596 }
Sujith0d425a72010-05-17 12:01:16 +0530597 trate->rates.ht_rates.rs_nrates = j;
Sujithfb9987d2010-03-17 14:25:25 +0530598
599 caps = WLAN_RC_HT_FLAG;
Felix Fietkau35537272010-06-12 17:22:33 +0200600 if (sta->ht_cap.mcs.rx_mask[1])
601 caps |= WLAN_RC_DS_FLAG;
Vivek Natarajan71ba1862010-08-12 14:23:28 +0530602 if ((sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) &&
603 (conf_is_ht40(&priv->hw->conf)))
Sujithfb9987d2010-03-17 14:25:25 +0530604 caps |= WLAN_RC_40_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530605 if (conf_is_ht40(&priv->hw->conf) &&
606 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40))
Sujithfb9987d2010-03-17 14:25:25 +0530607 caps |= WLAN_RC_SGI_FLAG;
Sujithb4dec5e2010-05-17 12:01:19 +0530608 else if (conf_is_ht20(&priv->hw->conf) &&
609 (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20))
610 caps |= WLAN_RC_SGI_FLAG;
Sujithfb9987d2010-03-17 14:25:25 +0530611 }
612
Sujith0d425a72010-05-17 12:01:16 +0530613 trate->sta_index = ista->index;
614 trate->isnew = 1;
615 trate->capflags = cpu_to_be32(caps);
616}
Sujithfb9987d2010-03-17 14:25:25 +0530617
Sujith0d425a72010-05-17 12:01:16 +0530618static int ath9k_htc_send_rate_cmd(struct ath9k_htc_priv *priv,
619 struct ath9k_htc_target_rate *trate)
620{
621 struct ath_common *common = ath9k_hw_common(priv->ah);
622 int ret;
623 u8 cmd_rsp;
624
625 WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, trate);
Sujithfb9987d2010-03-17 14:25:25 +0530626 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800627 ath_err(common,
628 "Unable to initialize Rate information on target\n");
Sujithfb9987d2010-03-17 14:25:25 +0530629 }
630
Sujith0d425a72010-05-17 12:01:16 +0530631 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530632}
633
Sujith0d425a72010-05-17 12:01:16 +0530634static void ath9k_htc_init_rate(struct ath9k_htc_priv *priv,
635 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +0530636{
Sujithfb9987d2010-03-17 14:25:25 +0530637 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith0d425a72010-05-17 12:01:16 +0530638 struct ath9k_htc_target_rate trate;
Sujithfb9987d2010-03-17 14:25:25 +0530639 int ret;
Sujithfb9987d2010-03-17 14:25:25 +0530640
Sujith0d425a72010-05-17 12:01:16 +0530641 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
642 ath9k_htc_setup_rate(priv, sta, &trate);
643 ret = ath9k_htc_send_rate_cmd(priv, &trate);
644 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800645 ath_dbg(common, ATH_DBG_CONFIG,
646 "Updated target sta: %pM, rate caps: 0x%X\n",
647 sta->addr, be32_to_cpu(trate.capflags));
Sujithfb9987d2010-03-17 14:25:25 +0530648}
649
Sujith2c76ef82010-05-17 12:01:18 +0530650static void ath9k_htc_update_rate(struct ath9k_htc_priv *priv,
651 struct ieee80211_vif *vif,
652 struct ieee80211_bss_conf *bss_conf)
653{
654 struct ath_common *common = ath9k_hw_common(priv->ah);
655 struct ath9k_htc_target_rate trate;
656 struct ieee80211_sta *sta;
657 int ret;
658
659 memset(&trate, 0, sizeof(struct ath9k_htc_target_rate));
660
661 rcu_read_lock();
662 sta = ieee80211_find_sta(vif, bss_conf->bssid);
663 if (!sta) {
664 rcu_read_unlock();
665 return;
666 }
667 ath9k_htc_setup_rate(priv, sta, &trate);
668 rcu_read_unlock();
669
670 ret = ath9k_htc_send_rate_cmd(priv, &trate);
671 if (!ret)
Joe Perches226afe62010-12-02 19:12:37 -0800672 ath_dbg(common, ATH_DBG_CONFIG,
673 "Updated target sta: %pM, rate caps: 0x%X\n",
674 bss_conf->bssid, be32_to_cpu(trate.capflags));
Sujith2c76ef82010-05-17 12:01:18 +0530675}
676
Luis R. Rodriguez9edd9522010-07-13 21:27:25 -0400677static int ath9k_htc_tx_aggr_oper(struct ath9k_htc_priv *priv,
678 struct ieee80211_vif *vif,
679 struct ieee80211_sta *sta,
680 enum ieee80211_ampdu_mlme_action action,
681 u16 tid)
Sujithfb9987d2010-03-17 14:25:25 +0530682{
683 struct ath_common *common = ath9k_hw_common(priv->ah);
684 struct ath9k_htc_target_aggr aggr;
Dan Carpenter277a64d2010-05-08 18:23:20 +0200685 struct ath9k_htc_sta *ista;
Sujithfb9987d2010-03-17 14:25:25 +0530686 int ret = 0;
687 u8 cmd_rsp;
688
Dan Carpenter0730d112010-05-08 18:24:02 +0200689 if (tid >= ATH9K_HTC_MAX_TID)
Sujithfb9987d2010-03-17 14:25:25 +0530690 return -EINVAL;
691
Sujithfb9987d2010-03-17 14:25:25 +0530692 memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr));
Sujithef98c3c2010-03-29 16:07:11 +0530693 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujithfb9987d2010-03-17 14:25:25 +0530694
Sujithef98c3c2010-03-29 16:07:11 +0530695 aggr.sta_index = ista->index;
Sujithd7ca2132010-06-15 10:24:37 +0530696 aggr.tidno = tid & 0xf;
697 aggr.aggr_enable = (action == IEEE80211_AMPDU_TX_START) ? true : false;
Sujithef98c3c2010-03-29 16:07:11 +0530698
Sujithfb9987d2010-03-17 14:25:25 +0530699 WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr);
700 if (ret)
Joe Perches226afe62010-12-02 19:12:37 -0800701 ath_dbg(common, ATH_DBG_CONFIG,
702 "Unable to %s TX aggregation for (%pM, %d)\n",
703 (aggr.aggr_enable) ? "start" : "stop", sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +0530704 else
Joe Perches226afe62010-12-02 19:12:37 -0800705 ath_dbg(common, ATH_DBG_CONFIG,
706 "%s TX aggregation for (%pM, %d)\n",
707 (aggr.aggr_enable) ? "Starting" : "Stopping",
708 sta->addr, tid);
Sujithd7ca2132010-06-15 10:24:37 +0530709
Sujith Manoharan658ef042011-04-13 11:25:00 +0530710 spin_lock_bh(&priv->tx.tx_lock);
Sujithd7ca2132010-06-15 10:24:37 +0530711 ista->tid_state[tid] = (aggr.aggr_enable && !ret) ? AGGR_START : AGGR_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530712 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +0530713
714 return ret;
715}
716
Sujithfb9987d2010-03-17 14:25:25 +0530717/*******/
718/* ANI */
719/*******/
720
Sujith Manoharana2362542011-02-21 07:49:38 +0530721void ath9k_htc_start_ani(struct ath9k_htc_priv *priv)
Sujithfb9987d2010-03-17 14:25:25 +0530722{
723 struct ath_common *common = ath9k_hw_common(priv->ah);
724 unsigned long timestamp = jiffies_to_msecs(jiffies);
725
726 common->ani.longcal_timer = timestamp;
727 common->ani.shortcal_timer = timestamp;
728 common->ani.checkani_timer = timestamp;
729
Sujith Manoharana2362542011-02-21 07:49:38 +0530730 priv->op_flags |= OP_ANI_RUNNING;
731
732 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530733 msecs_to_jiffies(ATH_ANI_POLLINTERVAL));
734}
735
Sujith Manoharana2362542011-02-21 07:49:38 +0530736void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv)
737{
738 cancel_delayed_work_sync(&priv->ani_work);
739 priv->op_flags &= ~OP_ANI_RUNNING;
740}
741
742void ath9k_htc_ani_work(struct work_struct *work)
Sujithfb9987d2010-03-17 14:25:25 +0530743{
744 struct ath9k_htc_priv *priv =
Sujith Manoharana2362542011-02-21 07:49:38 +0530745 container_of(work, struct ath9k_htc_priv, ani_work.work);
Sujithfb9987d2010-03-17 14:25:25 +0530746 struct ath_hw *ah = priv->ah;
747 struct ath_common *common = ath9k_hw_common(ah);
748 bool longcal = false;
749 bool shortcal = false;
750 bool aniflag = false;
751 unsigned int timestamp = jiffies_to_msecs(jiffies);
752 u32 cal_interval, short_cal_interval;
753
Sujith Manoharana2362542011-02-21 07:49:38 +0530754 short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ?
755 ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL;
Sujithfb9987d2010-03-17 14:25:25 +0530756
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530757 /* Only calibrate if awake */
758 if (ah->power_mode != ATH9K_PM_AWAKE)
759 goto set_timer;
760
Sujithfb9987d2010-03-17 14:25:25 +0530761 /* Long calibration runs independently of short calibration. */
762 if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) {
763 longcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800764 ath_dbg(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530765 common->ani.longcal_timer = timestamp;
766 }
767
768 /* Short calibration applies only while caldone is false */
769 if (!common->ani.caldone) {
770 if ((timestamp - common->ani.shortcal_timer) >=
771 short_cal_interval) {
772 shortcal = true;
Joe Perches226afe62010-12-02 19:12:37 -0800773 ath_dbg(common, ATH_DBG_ANI,
774 "shortcal @%lu\n", jiffies);
Sujithfb9987d2010-03-17 14:25:25 +0530775 common->ani.shortcal_timer = timestamp;
776 common->ani.resetcal_timer = timestamp;
777 }
778 } else {
779 if ((timestamp - common->ani.resetcal_timer) >=
780 ATH_RESTART_CALINTERVAL) {
781 common->ani.caldone = ath9k_hw_reset_calvalid(ah);
782 if (common->ani.caldone)
783 common->ani.resetcal_timer = timestamp;
784 }
785 }
786
787 /* Verify whether we must check ANI */
788 if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) {
789 aniflag = true;
790 common->ani.checkani_timer = timestamp;
791 }
792
793 /* Skip all processing if there's nothing to do. */
794 if (longcal || shortcal || aniflag) {
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530795
796 ath9k_htc_ps_wakeup(priv);
797
Sujithfb9987d2010-03-17 14:25:25 +0530798 /* Call ANI routine if necessary */
799 if (aniflag)
800 ath9k_hw_ani_monitor(ah, ah->curchan);
801
802 /* Perform calibration if necessary */
Felix Fietkau35ecfe02010-09-29 17:15:26 +0200803 if (longcal || shortcal)
Sujithfb9987d2010-03-17 14:25:25 +0530804 common->ani.caldone =
805 ath9k_hw_calibrate(ah, ah->curchan,
806 common->rx_chainmask,
807 longcal);
808
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530809 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530810 }
811
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530812set_timer:
Sujithfb9987d2010-03-17 14:25:25 +0530813 /*
814 * Set timer interval based on previous results.
815 * The interval must be the shortest necessary to satisfy ANI,
816 * short calibration and long calibration.
817 */
818 cal_interval = ATH_LONG_CALINTERVAL;
819 if (priv->ah->config.enable_ani)
820 cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL);
821 if (!common->ani.caldone)
822 cal_interval = min(cal_interval, (u32)short_cal_interval);
823
Sujith Manoharana2362542011-02-21 07:49:38 +0530824 ieee80211_queue_delayed_work(common->hw, &priv->ani_work,
Sujithfb9987d2010-03-17 14:25:25 +0530825 msecs_to_jiffies(cal_interval));
826}
827
Sujithfb9987d2010-03-17 14:25:25 +0530828/**********************/
829/* mac80211 Callbacks */
830/**********************/
831
Johannes Berg7bb45682011-02-24 14:42:06 +0100832static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
Sujithfb9987d2010-03-17 14:25:25 +0530833{
834 struct ieee80211_hdr *hdr;
835 struct ath9k_htc_priv *priv = hw->priv;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530836 struct ath_common *common = ath9k_hw_common(priv->ah);
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530837 int padpos, padsize, ret, slot;
Sujithfb9987d2010-03-17 14:25:25 +0530838
839 hdr = (struct ieee80211_hdr *) skb->data;
840
841 /* Add the padding after the header if this is not already done */
842 padpos = ath9k_cmn_padpos(hdr->frame_control);
843 padsize = padpos & 3;
844 if (padsize && skb->len > padpos) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530845 if (skb_headroom(skb) < padsize) {
846 ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n");
Johannes Berg7bb45682011-02-24 14:42:06 +0100847 goto fail_tx;
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530848 }
Sujithfb9987d2010-03-17 14:25:25 +0530849 skb_push(skb, padsize);
850 memmove(skb->data, skb->data + padsize, padpos);
851 }
852
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530853 slot = ath9k_htc_tx_get_slot(priv);
854 if (slot < 0) {
855 ath_dbg(common, ATH_DBG_XMIT, "No free TX slot\n");
856 goto fail_tx;
857 }
858
859 ret = ath9k_htc_tx_start(priv, skb, slot, false);
Sujith7757dfe2010-03-29 16:07:17 +0530860 if (ret != 0) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530861 ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530862 goto clear_slot;
Sujithfb9987d2010-03-17 14:25:25 +0530863 }
864
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530865 ath9k_htc_check_stop_queues(priv);
866
Johannes Berg7bb45682011-02-24 14:42:06 +0100867 return;
Sujithfb9987d2010-03-17 14:25:25 +0530868
Sujith Manoharan2c5d57f2011-04-13 11:25:47 +0530869clear_slot:
870 ath9k_htc_tx_clear_slot(priv, slot);
Sujithfb9987d2010-03-17 14:25:25 +0530871fail_tx:
872 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530873}
874
Sujith881ac6a2010-06-01 15:14:11 +0530875static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530876{
877 struct ath9k_htc_priv *priv = hw->priv;
878 struct ath_hw *ah = priv->ah;
879 struct ath_common *common = ath9k_hw_common(ah);
880 struct ieee80211_channel *curchan = hw->conf.channel;
881 struct ath9k_channel *init_channel;
882 int ret = 0;
883 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530884 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530885 u8 cmd_rsp;
886
Sujith881ac6a2010-06-01 15:14:11 +0530887 mutex_lock(&priv->mutex);
888
Joe Perches226afe62010-12-02 19:12:37 -0800889 ath_dbg(common, ATH_DBG_CONFIG,
890 "Starting driver with initial channel: %d MHz\n",
891 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530892
Sujith21d51302010-06-01 15:14:18 +0530893 /* Ensure that HW is awake before flushing RX */
894 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
895 WMI_CMD(WMI_FLUSH_RECV_CMDID);
896
Sujithfb9987d2010-03-17 14:25:25 +0530897 /* setup initial channel */
898 init_channel = ath9k_cmn_get_curchannel(hw, ah);
899
Sujithfb9987d2010-03-17 14:25:25 +0530900 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200901 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530902 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800903 ath_err(common,
904 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
905 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530906 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530907 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530908 }
909
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530910 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
911 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530912
913 mode = ath9k_htc_get_curmode(priv, init_channel);
914 htc_mode = cpu_to_be16(mode);
915 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530916 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530917 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530918
919 ath9k_host_rx_init(priv);
920
Sujith Manoharan1057b752011-02-21 07:48:09 +0530921 ret = ath9k_htc_update_cap_target(priv);
922 if (ret)
923 ath_dbg(common, ATH_DBG_CONFIG,
924 "Failed to update capability in target\n");
925
Sujithfb9987d2010-03-17 14:25:25 +0530926 priv->op_flags &= ~OP_INVALID;
927 htc_start(priv->htc);
928
Sujith Manoharan658ef042011-04-13 11:25:00 +0530929 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530930 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530931 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530932
933 ieee80211_wake_queues(hw);
934
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530935 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
936 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
937 AR_STOMP_LOW_WLAN_WGHT);
938 ath9k_hw_btcoex_enable(ah);
939 ath_htc_resume_btcoex_work(priv);
940 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530941 mutex_unlock(&priv->mutex);
942
943 return ret;
944}
945
Sujith881ac6a2010-06-01 15:14:11 +0530946static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530947{
948 struct ath9k_htc_priv *priv = hw->priv;
949 struct ath_hw *ah = priv->ah;
950 struct ath_common *common = ath9k_hw_common(ah);
951 int ret = 0;
952 u8 cmd_rsp;
953
Sujith881ac6a2010-06-01 15:14:11 +0530954 mutex_lock(&priv->mutex);
955
Sujithfb9987d2010-03-17 14:25:25 +0530956 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -0800957 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530958 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530959 return;
960 }
961
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530962 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530963 htc_stop(priv->htc);
964 WMI_CMD(WMI_DISABLE_INTR_CMDID);
965 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
966 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100967
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100968 tasklet_kill(&priv->rx_tasklet);
969 tasklet_kill(&priv->tx_tasklet);
970
Sujith Manoharan658ef042011-04-13 11:25:00 +0530971 skb_queue_purge(&priv->tx.tx_queue);
Sujithfb9987d2010-03-17 14:25:25 +0530972
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530973 ath9k_wmi_event_drain(priv);
974
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100975 mutex_unlock(&priv->mutex);
976
977 /* Cancel all the running timers/work .. */
978 cancel_work_sync(&priv->fatal_work);
979 cancel_work_sync(&priv->ps_work);
980 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +0530981 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100982 ath9k_led_stop_brightness(priv);
983
984 mutex_lock(&priv->mutex);
985
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530986 if (ah->btcoex_hw.enabled) {
987 ath9k_hw_btcoex_disable(ah);
988 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
989 ath_htc_cancel_btcoex_work(priv);
990 }
991
Sujith Manoharana97b4782011-02-21 07:48:00 +0530992 /* Remove a monitor interface if it's present. */
993 if (priv->ah->is_monitoring)
994 ath9k_htc_remove_monitor_interface(priv);
995
Sujithe9201f02010-06-01 15:14:17 +0530996 ath9k_hw_phy_disable(ah);
997 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +0530998 ath9k_htc_ps_restore(priv);
999 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
1000
Sujithfb9987d2010-03-17 14:25:25 +05301001 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +05301002
Joe Perches226afe62010-12-02 19:12:37 -08001003 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301004 mutex_unlock(&priv->mutex);
1005}
1006
Sujithfb9987d2010-03-17 14:25:25 +05301007static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1008 struct ieee80211_vif *vif)
1009{
1010 struct ath9k_htc_priv *priv = hw->priv;
1011 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1012 struct ath_common *common = ath9k_hw_common(priv->ah);
1013 struct ath9k_htc_target_vif hvif;
1014 int ret = 0;
1015 u8 cmd_rsp;
1016
1017 mutex_lock(&priv->mutex);
1018
Sujith Manoharana97b4782011-02-21 07:48:00 +05301019 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301020 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301021 return -ENOBUFS;
1022 }
1023
1024 if (priv->num_ibss_vif ||
1025 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1026 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1027 mutex_unlock(&priv->mutex);
1028 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301029 }
1030
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301031 if (((vif->type == NL80211_IFTYPE_AP) ||
1032 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1033 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1034 ath_err(common, "Max. number of beaconing interfaces reached\n");
1035 mutex_unlock(&priv->mutex);
1036 return -ENOBUFS;
1037 }
1038
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301039 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301040 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1041 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1042
1043 switch (vif->type) {
1044 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301045 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301046 break;
1047 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301048 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301049 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301050 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301051 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301052 break;
Sujithfb9987d2010-03-17 14:25:25 +05301053 default:
Joe Perches38002762010-12-02 19:12:36 -08001054 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301055 "Interface type %d not yet supported\n", vif->type);
1056 ret = -EOPNOTSUPP;
1057 goto out;
1058 }
1059
Sujithfb9987d2010-03-17 14:25:25 +05301060 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301061 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301062 hvif.rtsthreshold = cpu_to_be16(2304);
1063 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1064 if (ret)
1065 goto out;
1066
Sujithfb9987d2010-03-17 14:25:25 +05301067 /*
1068 * We need a node in target to tx mgmt frames
1069 * before association.
1070 */
1071 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301072 if (ret) {
1073 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301074 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301075 }
Sujithfb9987d2010-03-17 14:25:25 +05301076
Sujith Manoharan585895c2011-02-21 07:48:46 +05301077 ath9k_htc_set_bssid_mask(priv, vif);
1078
Sujith Manoharana97b4782011-02-21 07:48:00 +05301079 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301080 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301081
Sujith Manoharan0df83592011-02-21 07:49:15 +05301082 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301083
1084 if ((vif->type == NL80211_IFTYPE_AP) ||
1085 (vif->type == NL80211_IFTYPE_ADHOC))
1086 ath9k_htc_assign_bslot(priv, vif);
1087
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301088 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301089
Sujith Manoharana2362542011-02-21 07:49:38 +05301090 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301091 !(priv->op_flags & OP_ANI_RUNNING)) {
1092 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301093 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301094 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301095
Sujith Manoharana97b4782011-02-21 07:48:00 +05301096 ath_dbg(common, ATH_DBG_CONFIG,
1097 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1098
Sujithfb9987d2010-03-17 14:25:25 +05301099out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301100 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301101 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301102
Sujithfb9987d2010-03-17 14:25:25 +05301103 return ret;
1104}
1105
1106static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1107 struct ieee80211_vif *vif)
1108{
1109 struct ath9k_htc_priv *priv = hw->priv;
1110 struct ath_common *common = ath9k_hw_common(priv->ah);
1111 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1112 struct ath9k_htc_target_vif hvif;
1113 int ret = 0;
1114 u8 cmd_rsp;
1115
Sujithfb9987d2010-03-17 14:25:25 +05301116 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301117 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301118
1119 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1120 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1121 hvif.index = avp->index;
1122 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1123 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301124 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301125
1126 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301127
Sujith Manoharan0df83592011-02-21 07:49:15 +05301128 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301129
1130 if ((vif->type == NL80211_IFTYPE_AP) ||
1131 (vif->type == NL80211_IFTYPE_ADHOC))
1132 ath9k_htc_remove_bslot(priv, vif);
1133
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301134 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301135
Sujith Manoharana2362542011-02-21 07:49:38 +05301136 /*
1137 * Stop ANI only if there are no associated station interfaces.
1138 */
1139 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1140 priv->rearm_ani = false;
1141 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1142 ath9k_htc_vif_iter, priv);
1143 if (!priv->rearm_ani)
1144 ath9k_htc_stop_ani(priv);
1145 }
1146
Sujith Manoharana97b4782011-02-21 07:48:00 +05301147 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1148
Sujithcb551df2010-06-01 15:14:12 +05301149 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301150 mutex_unlock(&priv->mutex);
1151}
1152
1153static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1154{
1155 struct ath9k_htc_priv *priv = hw->priv;
1156 struct ath_common *common = ath9k_hw_common(priv->ah);
1157 struct ieee80211_conf *conf = &hw->conf;
1158
1159 mutex_lock(&priv->mutex);
1160
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301161 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1162 bool enable_radio = false;
1163 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1164
Sujith23367762010-06-01 15:14:16 +05301165 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301166 if (!idle && priv->ps_idle)
1167 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301168 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301169 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301170
1171 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001172 ath_dbg(common, ATH_DBG_CONFIG,
1173 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301174 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1175 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301176 }
1177 }
1178
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301179 /*
1180 * Monitor interface should be added before
1181 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1182 */
1183 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301184 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1185 !priv->ah->is_monitoring)
1186 ath9k_htc_add_monitor_interface(priv);
1187 else if (priv->ah->is_monitoring)
1188 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301189 }
1190
Sujithfb9987d2010-03-17 14:25:25 +05301191 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1192 struct ieee80211_channel *curchan = hw->conf.channel;
1193 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301194
Joe Perches226afe62010-12-02 19:12:37 -08001195 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1196 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301197
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001198 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1199 hw->conf.channel,
1200 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301201
1202 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001203 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301204 mutex_unlock(&priv->mutex);
1205 return -EINVAL;
1206 }
1207
1208 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301209
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301210 if (changed & IEEE80211_CONF_CHANGE_PS) {
1211 if (conf->flags & IEEE80211_CONF_PS) {
1212 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1213 priv->ps_enabled = true;
1214 } else {
1215 priv->ps_enabled = false;
1216 cancel_work_sync(&priv->ps_work);
1217 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1218 }
1219 }
Sujithfb9987d2010-03-17 14:25:25 +05301220
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301221 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1222 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301223 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1224 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301225 }
1226
Sujith23367762010-06-01 15:14:16 +05301227 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1228 mutex_lock(&priv->htc_pm_lock);
1229 if (!priv->ps_idle) {
1230 mutex_unlock(&priv->htc_pm_lock);
1231 goto out;
1232 }
1233 mutex_unlock(&priv->htc_pm_lock);
1234
Joe Perches226afe62010-12-02 19:12:37 -08001235 ath_dbg(common, ATH_DBG_CONFIG,
1236 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301237 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301238 }
1239
Sujith23367762010-06-01 15:14:16 +05301240out:
Sujithfb9987d2010-03-17 14:25:25 +05301241 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301242 return 0;
1243}
1244
1245#define SUPPORTED_FILTERS \
1246 (FIF_PROMISC_IN_BSS | \
1247 FIF_ALLMULTI | \
1248 FIF_CONTROL | \
1249 FIF_PSPOLL | \
1250 FIF_OTHER_BSS | \
1251 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301252 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301253 FIF_FCSFAIL)
1254
1255static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1256 unsigned int changed_flags,
1257 unsigned int *total_flags,
1258 u64 multicast)
1259{
1260 struct ath9k_htc_priv *priv = hw->priv;
1261 u32 rfilt;
1262
1263 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301264 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301265
Sujithfb9987d2010-03-17 14:25:25 +05301266 changed_flags &= SUPPORTED_FILTERS;
1267 *total_flags &= SUPPORTED_FILTERS;
1268
1269 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301270 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301271 ath9k_hw_setrxfilter(priv->ah, rfilt);
1272
Joe Perches226afe62010-12-02 19:12:37 -08001273 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1274 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301275
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301276 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301277 mutex_unlock(&priv->mutex);
1278}
1279
Sujithabd984e2010-05-18 15:26:04 +05301280static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1281 struct ieee80211_vif *vif,
1282 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301283{
1284 struct ath9k_htc_priv *priv = hw->priv;
1285 int ret;
1286
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301287 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301288 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301289 ret = ath9k_htc_add_station(priv, vif, sta);
1290 if (!ret)
1291 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301292 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301293 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301294
1295 return ret;
1296}
1297
1298static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1299 struct ieee80211_vif *vif,
1300 struct ieee80211_sta *sta)
1301{
1302 struct ath9k_htc_priv *priv = hw->priv;
1303 int ret;
1304
1305 mutex_lock(&priv->mutex);
1306 ath9k_htc_ps_wakeup(priv);
1307 ret = ath9k_htc_remove_station(priv, vif, sta);
1308 ath9k_htc_ps_restore(priv);
1309 mutex_unlock(&priv->mutex);
1310
1311 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301312}
1313
1314static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1315 const struct ieee80211_tx_queue_params *params)
1316{
1317 struct ath9k_htc_priv *priv = hw->priv;
1318 struct ath_common *common = ath9k_hw_common(priv->ah);
1319 struct ath9k_tx_queue_info qi;
1320 int ret = 0, qnum;
1321
1322 if (queue >= WME_NUM_AC)
1323 return 0;
1324
1325 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301326 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301327
1328 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1329
1330 qi.tqi_aifs = params->aifs;
1331 qi.tqi_cwmin = params->cw_min;
1332 qi.tqi_cwmax = params->cw_max;
1333 qi.tqi_burstTime = params->txop;
1334
1335 qnum = get_hw_qnum(queue, priv->hwq_map);
1336
Joe Perches226afe62010-12-02 19:12:37 -08001337 ath_dbg(common, ATH_DBG_CONFIG,
1338 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1339 queue, qnum, params->aifs, params->cw_min,
1340 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301341
Sujithe1572c52010-03-24 13:42:13 +05301342 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301343 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001344 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301345 goto out;
1346 }
Sujithfb9987d2010-03-17 14:25:25 +05301347
Sujith764580f2010-06-01 15:14:19 +05301348 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001349 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301350 ath9k_htc_beaconq_config(priv);
1351out:
Sujithcb551df2010-06-01 15:14:12 +05301352 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301353 mutex_unlock(&priv->mutex);
1354
1355 return ret;
1356}
1357
1358static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1359 enum set_key_cmd cmd,
1360 struct ieee80211_vif *vif,
1361 struct ieee80211_sta *sta,
1362 struct ieee80211_key_conf *key)
1363{
1364 struct ath9k_htc_priv *priv = hw->priv;
1365 struct ath_common *common = ath9k_hw_common(priv->ah);
1366 int ret = 0;
1367
Sujithe1572c52010-03-24 13:42:13 +05301368 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301369 return -ENOSPC;
1370
1371 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001372 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301373 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301374
1375 switch (cmd) {
1376 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001377 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301378 if (ret >= 0) {
1379 key->hw_key_idx = ret;
1380 /* push IV and Michael MIC generation to stack */
1381 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001382 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301383 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001384 if (priv->ah->sw_mgmt_crypto &&
1385 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301386 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1387 ret = 0;
1388 }
1389 break;
1390 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001391 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301392 break;
1393 default:
1394 ret = -EINVAL;
1395 }
1396
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301397 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301398 mutex_unlock(&priv->mutex);
1399
1400 return ret;
1401}
1402
1403static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1404 struct ieee80211_vif *vif,
1405 struct ieee80211_bss_conf *bss_conf,
1406 u32 changed)
1407{
1408 struct ath9k_htc_priv *priv = hw->priv;
1409 struct ath_hw *ah = priv->ah;
1410 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301411 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301412
1413 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301414 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301415
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301416 /*
1417 * Set the HW AID/BSSID only for the first station interface
1418 * or in IBSS mode.
1419 */
1420 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1421 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1422 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301423
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301424
1425 if (changed & BSS_CHANGED_ASSOC) {
1426 if (set_assoc) {
1427 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1428 bss_conf->assoc);
1429
1430 common->curaid = bss_conf->assoc ?
1431 bss_conf->aid : 0;
1432
1433 if (bss_conf->assoc)
1434 ath9k_htc_start_ani(priv);
1435 else
1436 ath9k_htc_stop_ani(priv);
1437 }
Sujithfb9987d2010-03-17 14:25:25 +05301438 }
1439
1440 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301441 if (set_assoc) {
1442 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1443 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301444
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301445 ath_dbg(common, ATH_DBG_CONFIG,
1446 "BSSID: %pM aid: 0x%x\n",
1447 common->curbssid, common->curaid);
1448 }
Sujithfb9987d2010-03-17 14:25:25 +05301449 }
1450
Sujith Manoharana5fae372011-02-21 07:49:53 +05301451 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1452 ath_dbg(common, ATH_DBG_CONFIG,
1453 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301454 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301455 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301456 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301457 }
1458
Sujith Manoharana5fae372011-02-21 07:49:53 +05301459 if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) {
1460 /*
1461 * Disable SWBA interrupt only if there are no
1462 * AP/IBSS interfaces.
1463 */
1464 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1465 ath_dbg(common, ATH_DBG_CONFIG,
1466 "Beacon disabled for BSS: %pM\n",
1467 bss_conf->bssid);
1468 priv->op_flags &= ~OP_ENABLE_BEACON;
1469 ath9k_htc_beacon_config(priv, vif);
1470 }
1471 }
1472
1473 if (changed & BSS_CHANGED_BEACON_INT) {
1474 /*
1475 * Reset the HW TSF for the first AP interface.
1476 */
1477 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1478 (priv->nvifs == 1) &&
1479 (priv->num_ap_vif == 1) &&
1480 (vif->type == NL80211_IFTYPE_AP)) {
1481 priv->op_flags |= OP_TSF_RESET;
1482 }
1483 ath_dbg(common, ATH_DBG_CONFIG,
1484 "Beacon interval changed for BSS: %pM\n",
1485 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301486 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301487 }
1488
Sujithfb9987d2010-03-17 14:25:25 +05301489 if (changed & BSS_CHANGED_ERP_SLOT) {
1490 if (bss_conf->use_short_slot)
1491 ah->slottime = 9;
1492 else
1493 ah->slottime = 20;
1494
1495 ath9k_hw_init_global_settings(ah);
1496 }
1497
Sujith2c76ef82010-05-17 12:01:18 +05301498 if (changed & BSS_CHANGED_HT)
1499 ath9k_htc_update_rate(priv, vif, bss_conf);
1500
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301501 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301502 mutex_unlock(&priv->mutex);
1503}
1504
1505static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1506{
1507 struct ath9k_htc_priv *priv = hw->priv;
1508 u64 tsf;
1509
1510 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301511 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301512 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301513 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301514 mutex_unlock(&priv->mutex);
1515
1516 return tsf;
1517}
1518
1519static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1520{
1521 struct ath9k_htc_priv *priv = hw->priv;
1522
1523 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301524 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301525 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301526 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301527 mutex_unlock(&priv->mutex);
1528}
1529
1530static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
1531{
1532 struct ath9k_htc_priv *priv = hw->priv;
1533
1534 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301535 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301536 ath9k_hw_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301537 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301538 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301539}
1540
1541static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1542 struct ieee80211_vif *vif,
1543 enum ieee80211_ampdu_mlme_action action,
1544 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001545 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301546{
1547 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301548 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301549 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301550
Sujith Manoharan87df8952011-02-21 07:49:08 +05301551 mutex_lock(&priv->mutex);
1552
Sujithfb9987d2010-03-17 14:25:25 +05301553 switch (action) {
1554 case IEEE80211_AMPDU_RX_START:
1555 break;
1556 case IEEE80211_AMPDU_RX_STOP:
1557 break;
1558 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301559 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1560 if (!ret)
1561 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1562 break;
Sujithfb9987d2010-03-17 14:25:25 +05301563 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301564 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1565 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301566 break;
1567 case IEEE80211_AMPDU_TX_OPERATIONAL:
1568 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301569 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301570 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301571 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301572 break;
1573 default:
Joe Perches38002762010-12-02 19:12:36 -08001574 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301575 }
1576
Sujith Manoharan87df8952011-02-21 07:49:08 +05301577 mutex_unlock(&priv->mutex);
1578
Sujithd7ca2132010-06-15 10:24:37 +05301579 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301580}
1581
1582static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1583{
1584 struct ath9k_htc_priv *priv = hw->priv;
1585
1586 mutex_lock(&priv->mutex);
1587 spin_lock_bh(&priv->beacon_lock);
1588 priv->op_flags |= OP_SCANNING;
1589 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301590 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301591 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301592 mutex_unlock(&priv->mutex);
1593}
1594
1595static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1596{
1597 struct ath9k_htc_priv *priv = hw->priv;
1598
1599 mutex_lock(&priv->mutex);
1600 spin_lock_bh(&priv->beacon_lock);
1601 priv->op_flags &= ~OP_SCANNING;
1602 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301603 ath9k_htc_ps_wakeup(priv);
1604 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301605 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301606 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301607}
1608
1609static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1610{
1611 return 0;
1612}
1613
1614static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1615 u8 coverage_class)
1616{
1617 struct ath9k_htc_priv *priv = hw->priv;
1618
1619 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301620 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301621 priv->ah->coverage_class = coverage_class;
1622 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301623 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301624 mutex_unlock(&priv->mutex);
1625}
1626
1627struct ieee80211_ops ath9k_htc_ops = {
1628 .tx = ath9k_htc_tx,
1629 .start = ath9k_htc_start,
1630 .stop = ath9k_htc_stop,
1631 .add_interface = ath9k_htc_add_interface,
1632 .remove_interface = ath9k_htc_remove_interface,
1633 .config = ath9k_htc_config,
1634 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301635 .sta_add = ath9k_htc_sta_add,
1636 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301637 .conf_tx = ath9k_htc_conf_tx,
1638 .bss_info_changed = ath9k_htc_bss_info_changed,
1639 .set_key = ath9k_htc_set_key,
1640 .get_tsf = ath9k_htc_get_tsf,
1641 .set_tsf = ath9k_htc_set_tsf,
1642 .reset_tsf = ath9k_htc_reset_tsf,
1643 .ampdu_action = ath9k_htc_ampdu_action,
1644 .sw_scan_start = ath9k_htc_sw_scan_start,
1645 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1646 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1647 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1648 .set_coverage_class = ath9k_htc_set_coverage_class,
1649};