blob: 690113673d25ec0cb4f0d355e4c4dc2f6230cd0d [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);
Sujith7757dfe2010-03-29 16:07:17 +0530837 int padpos, padsize, ret;
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 Manoharan7d547eb2011-04-13 11:23:34 +0530853 ret = ath9k_htc_tx_start(priv, skb, false);
Sujith7757dfe2010-03-29 16:07:17 +0530854 if (ret != 0) {
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530855 ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
Sujithfb9987d2010-03-17 14:25:25 +0530856 goto fail_tx;
857 }
858
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530859 ath9k_htc_check_stop_queues(priv);
860
Johannes Berg7bb45682011-02-24 14:42:06 +0100861 return;
Sujithfb9987d2010-03-17 14:25:25 +0530862
863fail_tx:
864 dev_kfree_skb_any(skb);
Sujithfb9987d2010-03-17 14:25:25 +0530865}
866
Sujith881ac6a2010-06-01 15:14:11 +0530867static int ath9k_htc_start(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530868{
869 struct ath9k_htc_priv *priv = hw->priv;
870 struct ath_hw *ah = priv->ah;
871 struct ath_common *common = ath9k_hw_common(ah);
872 struct ieee80211_channel *curchan = hw->conf.channel;
873 struct ath9k_channel *init_channel;
874 int ret = 0;
875 enum htc_phymode mode;
Sujith7f1f5a02010-04-16 11:54:03 +0530876 __be16 htc_mode;
Sujithfb9987d2010-03-17 14:25:25 +0530877 u8 cmd_rsp;
878
Sujith881ac6a2010-06-01 15:14:11 +0530879 mutex_lock(&priv->mutex);
880
Joe Perches226afe62010-12-02 19:12:37 -0800881 ath_dbg(common, ATH_DBG_CONFIG,
882 "Starting driver with initial channel: %d MHz\n",
883 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +0530884
Sujith21d51302010-06-01 15:14:18 +0530885 /* Ensure that HW is awake before flushing RX */
886 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
887 WMI_CMD(WMI_FLUSH_RECV_CMDID);
888
Sujithfb9987d2010-03-17 14:25:25 +0530889 /* setup initial channel */
890 init_channel = ath9k_cmn_get_curchannel(hw, ah);
891
Sujithfb9987d2010-03-17 14:25:25 +0530892 ath9k_hw_htc_resetinit(ah);
Felix Fietkau20bd2a02010-07-31 00:12:00 +0200893 ret = ath9k_hw_reset(ah, init_channel, ah->caldata, false);
Sujithfb9987d2010-03-17 14:25:25 +0530894 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -0800895 ath_err(common,
896 "Unable to reset hardware; reset status %d (freq %u MHz)\n",
897 ret, curchan->center_freq);
Sujith881ac6a2010-06-01 15:14:11 +0530898 mutex_unlock(&priv->mutex);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530899 return ret;
Sujithfb9987d2010-03-17 14:25:25 +0530900 }
901
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +0530902 ath9k_cmn_update_txpow(ah, priv->curtxpow, priv->txpowlimit,
903 &priv->curtxpow);
Sujithfb9987d2010-03-17 14:25:25 +0530904
905 mode = ath9k_htc_get_curmode(priv, init_channel);
906 htc_mode = cpu_to_be16(mode);
907 WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
Sujithfb9987d2010-03-17 14:25:25 +0530908 WMI_CMD(WMI_ATH_INIT_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530909 WMI_CMD(WMI_START_RECV_CMDID);
Sujithfb9987d2010-03-17 14:25:25 +0530910
911 ath9k_host_rx_init(priv);
912
Sujith Manoharan1057b752011-02-21 07:48:09 +0530913 ret = ath9k_htc_update_cap_target(priv);
914 if (ret)
915 ath_dbg(common, ATH_DBG_CONFIG,
916 "Failed to update capability in target\n");
917
Sujithfb9987d2010-03-17 14:25:25 +0530918 priv->op_flags &= ~OP_INVALID;
919 htc_start(priv->htc);
920
Sujith Manoharan658ef042011-04-13 11:25:00 +0530921 spin_lock_bh(&priv->tx.tx_lock);
Sujith Manoharan8e86a542011-04-13 11:25:29 +0530922 priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
Sujith Manoharan658ef042011-04-13 11:25:00 +0530923 spin_unlock_bh(&priv->tx.tx_lock);
Sujith7757dfe2010-03-29 16:07:17 +0530924
925 ieee80211_wake_queues(hw);
926
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530927 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
928 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
929 AR_STOMP_LOW_WLAN_WGHT);
930 ath9k_hw_btcoex_enable(ah);
931 ath_htc_resume_btcoex_work(priv);
932 }
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530933 mutex_unlock(&priv->mutex);
934
935 return ret;
936}
937
Sujith881ac6a2010-06-01 15:14:11 +0530938static void ath9k_htc_stop(struct ieee80211_hw *hw)
Sujithfb9987d2010-03-17 14:25:25 +0530939{
940 struct ath9k_htc_priv *priv = hw->priv;
941 struct ath_hw *ah = priv->ah;
942 struct ath_common *common = ath9k_hw_common(ah);
943 int ret = 0;
944 u8 cmd_rsp;
945
Sujith881ac6a2010-06-01 15:14:11 +0530946 mutex_lock(&priv->mutex);
947
Sujithfb9987d2010-03-17 14:25:25 +0530948 if (priv->op_flags & OP_INVALID) {
Joe Perches226afe62010-12-02 19:12:37 -0800949 ath_dbg(common, ATH_DBG_ANY, "Device not present\n");
Sujith881ac6a2010-06-01 15:14:11 +0530950 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +0530951 return;
952 }
953
Vivek Natarajanbde748a2010-04-05 14:48:05 +0530954 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +0530955 htc_stop(priv->htc);
956 WMI_CMD(WMI_DISABLE_INTR_CMDID);
957 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
958 WMI_CMD(WMI_STOP_RECV_CMDID);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100959
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100960 tasklet_kill(&priv->rx_tasklet);
961 tasklet_kill(&priv->tx_tasklet);
962
Sujith Manoharan658ef042011-04-13 11:25:00 +0530963 skb_queue_purge(&priv->tx.tx_queue);
Sujithfb9987d2010-03-17 14:25:25 +0530964
Sujith Manoharanf4c88992011-04-13 11:23:52 +0530965 ath9k_wmi_event_drain(priv);
966
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100967 mutex_unlock(&priv->mutex);
968
969 /* Cancel all the running timers/work .. */
970 cancel_work_sync(&priv->fatal_work);
971 cancel_work_sync(&priv->ps_work);
972 cancel_delayed_work_sync(&priv->ath9k_led_blink_work);
Sujith Manoharana2362542011-02-21 07:49:38 +0530973 ath9k_htc_stop_ani(priv);
Stanislaw Gruszkaea888352011-01-25 14:15:12 +0100974 ath9k_led_stop_brightness(priv);
975
976 mutex_lock(&priv->mutex);
977
Vivek Natarajan21cb9872010-08-18 19:57:49 +0530978 if (ah->btcoex_hw.enabled) {
979 ath9k_hw_btcoex_disable(ah);
980 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE)
981 ath_htc_cancel_btcoex_work(priv);
982 }
983
Sujith Manoharana97b4782011-02-21 07:48:00 +0530984 /* Remove a monitor interface if it's present. */
985 if (priv->ah->is_monitoring)
986 ath9k_htc_remove_monitor_interface(priv);
987
Sujithe9201f02010-06-01 15:14:17 +0530988 ath9k_hw_phy_disable(ah);
989 ath9k_hw_disable(ah);
Sujithe9201f02010-06-01 15:14:17 +0530990 ath9k_htc_ps_restore(priv);
991 ath9k_htc_setpower(priv, ATH9K_PM_FULL_SLEEP);
992
Sujithfb9987d2010-03-17 14:25:25 +0530993 priv->op_flags |= OP_INVALID;
Sujithfb9987d2010-03-17 14:25:25 +0530994
Joe Perches226afe62010-12-02 19:12:37 -0800995 ath_dbg(common, ATH_DBG_CONFIG, "Driver halt\n");
Vivek Natarajan8a8572a2010-04-27 13:05:37 +0530996 mutex_unlock(&priv->mutex);
997}
998
Sujithfb9987d2010-03-17 14:25:25 +0530999static int ath9k_htc_add_interface(struct ieee80211_hw *hw,
1000 struct ieee80211_vif *vif)
1001{
1002 struct ath9k_htc_priv *priv = hw->priv;
1003 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1004 struct ath_common *common = ath9k_hw_common(priv->ah);
1005 struct ath9k_htc_target_vif hvif;
1006 int ret = 0;
1007 u8 cmd_rsp;
1008
1009 mutex_lock(&priv->mutex);
1010
Sujith Manoharana97b4782011-02-21 07:48:00 +05301011 if (priv->nvifs >= ATH9K_HTC_MAX_VIF) {
Sujith Manoharanab77c702011-02-21 07:48:16 +05301012 mutex_unlock(&priv->mutex);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301013 return -ENOBUFS;
1014 }
1015
1016 if (priv->num_ibss_vif ||
1017 (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) {
1018 ath_err(common, "IBSS coexistence with other modes is not allowed\n");
1019 mutex_unlock(&priv->mutex);
1020 return -ENOBUFS;
Sujithfb9987d2010-03-17 14:25:25 +05301021 }
1022
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301023 if (((vif->type == NL80211_IFTYPE_AP) ||
1024 (vif->type == NL80211_IFTYPE_ADHOC)) &&
1025 ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) {
1026 ath_err(common, "Max. number of beaconing interfaces reached\n");
1027 mutex_unlock(&priv->mutex);
1028 return -ENOBUFS;
1029 }
1030
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301031 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301032 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1033 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1034
1035 switch (vif->type) {
1036 case NL80211_IFTYPE_STATION:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301037 hvif.opmode = HTC_M_STA;
Sujithfb9987d2010-03-17 14:25:25 +05301038 break;
1039 case NL80211_IFTYPE_ADHOC:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301040 hvif.opmode = HTC_M_IBSS;
Sujithfb9987d2010-03-17 14:25:25 +05301041 break;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301042 case NL80211_IFTYPE_AP:
Sujith Manoharane4c62502011-04-13 11:24:43 +05301043 hvif.opmode = HTC_M_HOSTAP;
Sujith Manoharanda8d9d92011-02-21 07:49:23 +05301044 break;
Sujithfb9987d2010-03-17 14:25:25 +05301045 default:
Joe Perches38002762010-12-02 19:12:36 -08001046 ath_err(common,
Sujithfb9987d2010-03-17 14:25:25 +05301047 "Interface type %d not yet supported\n", vif->type);
1048 ret = -EOPNOTSUPP;
1049 goto out;
1050 }
1051
Sujithfb9987d2010-03-17 14:25:25 +05301052 /* Index starts from zero on the target */
Sujith Manoharana97b4782011-02-21 07:48:00 +05301053 avp->index = hvif.index = ffz(priv->vif_slot);
Sujithfb9987d2010-03-17 14:25:25 +05301054 hvif.rtsthreshold = cpu_to_be16(2304);
1055 WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif);
1056 if (ret)
1057 goto out;
1058
Sujithfb9987d2010-03-17 14:25:25 +05301059 /*
1060 * We need a node in target to tx mgmt frames
1061 * before association.
1062 */
1063 ret = ath9k_htc_add_station(priv, vif, NULL);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301064 if (ret) {
1065 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
Sujithfb9987d2010-03-17 14:25:25 +05301066 goto out;
Sujith Manoharanab77c702011-02-21 07:48:16 +05301067 }
Sujithfb9987d2010-03-17 14:25:25 +05301068
Sujith Manoharan585895c2011-02-21 07:48:46 +05301069 ath9k_htc_set_bssid_mask(priv, vif);
1070
Sujith Manoharana97b4782011-02-21 07:48:00 +05301071 priv->vif_slot |= (1 << avp->index);
Sujith Manoharanab77c702011-02-21 07:48:16 +05301072 priv->nvifs++;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301073
Sujith Manoharan0df83592011-02-21 07:49:15 +05301074 INC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301075
1076 if ((vif->type == NL80211_IFTYPE_AP) ||
1077 (vif->type == NL80211_IFTYPE_ADHOC))
1078 ath9k_htc_assign_bslot(priv, vif);
1079
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301080 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301081
Sujith Manoharana2362542011-02-21 07:49:38 +05301082 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301083 !(priv->op_flags & OP_ANI_RUNNING)) {
1084 ath9k_hw_set_tsfadjust(priv->ah, 1);
Sujith Manoharana2362542011-02-21 07:49:38 +05301085 ath9k_htc_start_ani(priv);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301086 }
Sujith Manoharana2362542011-02-21 07:49:38 +05301087
Sujith Manoharana97b4782011-02-21 07:48:00 +05301088 ath_dbg(common, ATH_DBG_CONFIG,
1089 "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index);
1090
Sujithfb9987d2010-03-17 14:25:25 +05301091out:
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301092 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301093 mutex_unlock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301094
Sujithfb9987d2010-03-17 14:25:25 +05301095 return ret;
1096}
1097
1098static void ath9k_htc_remove_interface(struct ieee80211_hw *hw,
1099 struct ieee80211_vif *vif)
1100{
1101 struct ath9k_htc_priv *priv = hw->priv;
1102 struct ath_common *common = ath9k_hw_common(priv->ah);
1103 struct ath9k_htc_vif *avp = (void *)vif->drv_priv;
1104 struct ath9k_htc_target_vif hvif;
1105 int ret = 0;
1106 u8 cmd_rsp;
1107
Sujithfb9987d2010-03-17 14:25:25 +05301108 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301109 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301110
1111 memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif));
1112 memcpy(&hvif.myaddr, vif->addr, ETH_ALEN);
1113 hvif.index = avp->index;
1114 WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif);
1115 priv->nvifs--;
Sujith Manoharana97b4782011-02-21 07:48:00 +05301116 priv->vif_slot &= ~(1 << avp->index);
Sujithfb9987d2010-03-17 14:25:25 +05301117
1118 ath9k_htc_remove_station(priv, vif, NULL);
Sujithfb9987d2010-03-17 14:25:25 +05301119
Sujith Manoharan0df83592011-02-21 07:49:15 +05301120 DEC_VIF(priv, vif->type);
Sujith Manoharan832f6a12011-04-13 11:23:08 +05301121
1122 if ((vif->type == NL80211_IFTYPE_AP) ||
1123 (vif->type == NL80211_IFTYPE_ADHOC))
1124 ath9k_htc_remove_bslot(priv, vif);
1125
Sujith Manoharanffbe7c82011-02-21 07:49:31 +05301126 ath9k_htc_set_opmode(priv);
Sujith Manoharan0df83592011-02-21 07:49:15 +05301127
Sujith Manoharana2362542011-02-21 07:49:38 +05301128 /*
1129 * Stop ANI only if there are no associated station interfaces.
1130 */
1131 if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) {
1132 priv->rearm_ani = false;
1133 ieee80211_iterate_active_interfaces_atomic(priv->hw,
1134 ath9k_htc_vif_iter, priv);
1135 if (!priv->rearm_ani)
1136 ath9k_htc_stop_ani(priv);
1137 }
1138
Sujith Manoharana97b4782011-02-21 07:48:00 +05301139 ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index);
1140
Sujithcb551df2010-06-01 15:14:12 +05301141 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301142 mutex_unlock(&priv->mutex);
1143}
1144
1145static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed)
1146{
1147 struct ath9k_htc_priv *priv = hw->priv;
1148 struct ath_common *common = ath9k_hw_common(priv->ah);
1149 struct ieee80211_conf *conf = &hw->conf;
1150
1151 mutex_lock(&priv->mutex);
1152
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301153 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1154 bool enable_radio = false;
1155 bool idle = !!(conf->flags & IEEE80211_CONF_IDLE);
1156
Sujith23367762010-06-01 15:14:16 +05301157 mutex_lock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301158 if (!idle && priv->ps_idle)
1159 enable_radio = true;
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301160 priv->ps_idle = idle;
Sujith23367762010-06-01 15:14:16 +05301161 mutex_unlock(&priv->htc_pm_lock);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301162
1163 if (enable_radio) {
Joe Perches226afe62010-12-02 19:12:37 -08001164 ath_dbg(common, ATH_DBG_CONFIG,
1165 "not-idle: enabling radio\n");
Sujith23367762010-06-01 15:14:16 +05301166 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1167 ath9k_htc_radio_enable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301168 }
1169 }
1170
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301171 /*
1172 * Monitor interface should be added before
1173 * IEEE80211_CONF_CHANGE_CHANNEL is handled.
1174 */
1175 if (changed & IEEE80211_CONF_CHANGE_MONITOR) {
Sujith Manoharana97b4782011-02-21 07:48:00 +05301176 if ((conf->flags & IEEE80211_CONF_MONITOR) &&
1177 !priv->ah->is_monitoring)
1178 ath9k_htc_add_monitor_interface(priv);
1179 else if (priv->ah->is_monitoring)
1180 ath9k_htc_remove_monitor_interface(priv);
Sujith Manoharan55de80d2011-01-05 01:06:21 +05301181 }
1182
Sujithfb9987d2010-03-17 14:25:25 +05301183 if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
1184 struct ieee80211_channel *curchan = hw->conf.channel;
1185 int pos = curchan->hw_value;
Sujithfb9987d2010-03-17 14:25:25 +05301186
Joe Perches226afe62010-12-02 19:12:37 -08001187 ath_dbg(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n",
1188 curchan->center_freq);
Sujithfb9987d2010-03-17 14:25:25 +05301189
Felix Fietkaubabcbc22010-10-20 02:09:46 +02001190 ath9k_cmn_update_ichannel(&priv->ah->channels[pos],
1191 hw->conf.channel,
1192 hw->conf.channel_type);
Sujithfb9987d2010-03-17 14:25:25 +05301193
1194 if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) {
Joe Perches38002762010-12-02 19:12:36 -08001195 ath_err(common, "Unable to set channel\n");
Sujithfb9987d2010-03-17 14:25:25 +05301196 mutex_unlock(&priv->mutex);
1197 return -EINVAL;
1198 }
1199
1200 }
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301201
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301202 if (changed & IEEE80211_CONF_CHANGE_PS) {
1203 if (conf->flags & IEEE80211_CONF_PS) {
1204 ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP);
1205 priv->ps_enabled = true;
1206 } else {
1207 priv->ps_enabled = false;
1208 cancel_work_sync(&priv->ps_work);
1209 ath9k_htc_setpower(priv, ATH9K_PM_AWAKE);
1210 }
1211 }
Sujithfb9987d2010-03-17 14:25:25 +05301212
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301213 if (changed & IEEE80211_CONF_CHANGE_POWER) {
1214 priv->txpowlimit = 2 * conf->power_level;
Rajkumar Manoharanb2a5c3d2011-01-31 23:47:45 +05301215 ath9k_cmn_update_txpow(priv->ah, priv->curtxpow,
1216 priv->txpowlimit, &priv->curtxpow);
Sujith Manoharan692d6b12010-12-07 16:31:54 +05301217 }
1218
Sujith23367762010-06-01 15:14:16 +05301219 if (changed & IEEE80211_CONF_CHANGE_IDLE) {
1220 mutex_lock(&priv->htc_pm_lock);
1221 if (!priv->ps_idle) {
1222 mutex_unlock(&priv->htc_pm_lock);
1223 goto out;
1224 }
1225 mutex_unlock(&priv->htc_pm_lock);
1226
Joe Perches226afe62010-12-02 19:12:37 -08001227 ath_dbg(common, ATH_DBG_CONFIG,
1228 "idle: disabling radio\n");
Sujith881ac6a2010-06-01 15:14:11 +05301229 ath9k_htc_radio_disable(hw);
Vivek Natarajan8a8572a2010-04-27 13:05:37 +05301230 }
1231
Sujith23367762010-06-01 15:14:16 +05301232out:
Sujithfb9987d2010-03-17 14:25:25 +05301233 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301234 return 0;
1235}
1236
1237#define SUPPORTED_FILTERS \
1238 (FIF_PROMISC_IN_BSS | \
1239 FIF_ALLMULTI | \
1240 FIF_CONTROL | \
1241 FIF_PSPOLL | \
1242 FIF_OTHER_BSS | \
1243 FIF_BCN_PRBRESP_PROMISC | \
Rajkumar Manoharan94a40c02010-10-14 10:50:26 +05301244 FIF_PROBE_REQ | \
Sujithfb9987d2010-03-17 14:25:25 +05301245 FIF_FCSFAIL)
1246
1247static void ath9k_htc_configure_filter(struct ieee80211_hw *hw,
1248 unsigned int changed_flags,
1249 unsigned int *total_flags,
1250 u64 multicast)
1251{
1252 struct ath9k_htc_priv *priv = hw->priv;
1253 u32 rfilt;
1254
1255 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301256 ath9k_htc_ps_wakeup(priv);
Sujithcb551df2010-06-01 15:14:12 +05301257
Sujithfb9987d2010-03-17 14:25:25 +05301258 changed_flags &= SUPPORTED_FILTERS;
1259 *total_flags &= SUPPORTED_FILTERS;
1260
1261 priv->rxfilter = *total_flags;
Sujith0995d112010-03-29 16:07:09 +05301262 rfilt = ath9k_htc_calcrxfilter(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301263 ath9k_hw_setrxfilter(priv->ah, rfilt);
1264
Joe Perches226afe62010-12-02 19:12:37 -08001265 ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG,
1266 "Set HW RX filter: 0x%x\n", rfilt);
Sujithfb9987d2010-03-17 14:25:25 +05301267
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301268 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301269 mutex_unlock(&priv->mutex);
1270}
1271
Sujithabd984e2010-05-18 15:26:04 +05301272static int ath9k_htc_sta_add(struct ieee80211_hw *hw,
1273 struct ieee80211_vif *vif,
1274 struct ieee80211_sta *sta)
Sujithfb9987d2010-03-17 14:25:25 +05301275{
1276 struct ath9k_htc_priv *priv = hw->priv;
1277 int ret;
1278
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301279 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301280 ath9k_htc_ps_wakeup(priv);
Sujithabd984e2010-05-18 15:26:04 +05301281 ret = ath9k_htc_add_station(priv, vif, sta);
1282 if (!ret)
1283 ath9k_htc_init_rate(priv, sta);
Sujithcb551df2010-06-01 15:14:12 +05301284 ath9k_htc_ps_restore(priv);
Sujith.Manoharan@atheros.com05a30f92010-05-11 16:24:38 +05301285 mutex_unlock(&priv->mutex);
Sujithabd984e2010-05-18 15:26:04 +05301286
1287 return ret;
1288}
1289
1290static int ath9k_htc_sta_remove(struct ieee80211_hw *hw,
1291 struct ieee80211_vif *vif,
1292 struct ieee80211_sta *sta)
1293{
1294 struct ath9k_htc_priv *priv = hw->priv;
1295 int ret;
1296
1297 mutex_lock(&priv->mutex);
1298 ath9k_htc_ps_wakeup(priv);
1299 ret = ath9k_htc_remove_station(priv, vif, sta);
1300 ath9k_htc_ps_restore(priv);
1301 mutex_unlock(&priv->mutex);
1302
1303 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301304}
1305
1306static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue,
1307 const struct ieee80211_tx_queue_params *params)
1308{
1309 struct ath9k_htc_priv *priv = hw->priv;
1310 struct ath_common *common = ath9k_hw_common(priv->ah);
1311 struct ath9k_tx_queue_info qi;
1312 int ret = 0, qnum;
1313
1314 if (queue >= WME_NUM_AC)
1315 return 0;
1316
1317 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301318 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301319
1320 memset(&qi, 0, sizeof(struct ath9k_tx_queue_info));
1321
1322 qi.tqi_aifs = params->aifs;
1323 qi.tqi_cwmin = params->cw_min;
1324 qi.tqi_cwmax = params->cw_max;
1325 qi.tqi_burstTime = params->txop;
1326
1327 qnum = get_hw_qnum(queue, priv->hwq_map);
1328
Joe Perches226afe62010-12-02 19:12:37 -08001329 ath_dbg(common, ATH_DBG_CONFIG,
1330 "Configure tx [queue/hwq] [%d/%d], aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n",
1331 queue, qnum, params->aifs, params->cw_min,
1332 params->cw_max, params->txop);
Sujithfb9987d2010-03-17 14:25:25 +05301333
Sujithe1572c52010-03-24 13:42:13 +05301334 ret = ath_htc_txq_update(priv, qnum, &qi);
Sujith764580f2010-06-01 15:14:19 +05301335 if (ret) {
Joe Perches38002762010-12-02 19:12:36 -08001336 ath_err(common, "TXQ Update failed\n");
Sujith764580f2010-06-01 15:14:19 +05301337 goto out;
1338 }
Sujithfb9987d2010-03-17 14:25:25 +05301339
Sujith764580f2010-06-01 15:14:19 +05301340 if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) &&
Felix Fietkaue8c35a72010-06-12 00:33:50 -04001341 (qnum == priv->hwq_map[WME_AC_BE]))
Sujith764580f2010-06-01 15:14:19 +05301342 ath9k_htc_beaconq_config(priv);
1343out:
Sujithcb551df2010-06-01 15:14:12 +05301344 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301345 mutex_unlock(&priv->mutex);
1346
1347 return ret;
1348}
1349
1350static int ath9k_htc_set_key(struct ieee80211_hw *hw,
1351 enum set_key_cmd cmd,
1352 struct ieee80211_vif *vif,
1353 struct ieee80211_sta *sta,
1354 struct ieee80211_key_conf *key)
1355{
1356 struct ath9k_htc_priv *priv = hw->priv;
1357 struct ath_common *common = ath9k_hw_common(priv->ah);
1358 int ret = 0;
1359
Sujithe1572c52010-03-24 13:42:13 +05301360 if (htc_modparam_nohwcrypt)
Sujithfb9987d2010-03-17 14:25:25 +05301361 return -ENOSPC;
1362
1363 mutex_lock(&priv->mutex);
Joe Perches226afe62010-12-02 19:12:37 -08001364 ath_dbg(common, ATH_DBG_CONFIG, "Set HW Key\n");
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301365 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301366
1367 switch (cmd) {
1368 case SET_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001369 ret = ath_key_config(common, vif, sta, key);
Sujithfb9987d2010-03-17 14:25:25 +05301370 if (ret >= 0) {
1371 key->hw_key_idx = ret;
1372 /* push IV and Michael MIC generation to stack */
1373 key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
Johannes Berg97359d12010-08-10 09:46:38 +02001374 if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
Sujithfb9987d2010-03-17 14:25:25 +05301375 key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC;
Johannes Berg97359d12010-08-10 09:46:38 +02001376 if (priv->ah->sw_mgmt_crypto &&
1377 key->cipher == WLAN_CIPHER_SUITE_CCMP)
Sujithfb9987d2010-03-17 14:25:25 +05301378 key->flags |= IEEE80211_KEY_FLAG_SW_MGMT;
1379 ret = 0;
1380 }
1381 break;
1382 case DISABLE_KEY:
Bruno Randolf040e5392010-09-08 16:05:04 +09001383 ath_key_delete(common, key);
Sujithfb9987d2010-03-17 14:25:25 +05301384 break;
1385 default:
1386 ret = -EINVAL;
1387 }
1388
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301389 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301390 mutex_unlock(&priv->mutex);
1391
1392 return ret;
1393}
1394
1395static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw,
1396 struct ieee80211_vif *vif,
1397 struct ieee80211_bss_conf *bss_conf,
1398 u32 changed)
1399{
1400 struct ath9k_htc_priv *priv = hw->priv;
1401 struct ath_hw *ah = priv->ah;
1402 struct ath_common *common = ath9k_hw_common(ah);
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301403 bool set_assoc;
Sujithfb9987d2010-03-17 14:25:25 +05301404
1405 mutex_lock(&priv->mutex);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301406 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301407
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301408 /*
1409 * Set the HW AID/BSSID only for the first station interface
1410 * or in IBSS mode.
1411 */
1412 set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) ||
1413 ((priv->ah->opmode == NL80211_IFTYPE_STATION) &&
1414 (priv->num_sta_vif == 1)));
Sujithfb9987d2010-03-17 14:25:25 +05301415
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301416
1417 if (changed & BSS_CHANGED_ASSOC) {
1418 if (set_assoc) {
1419 ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n",
1420 bss_conf->assoc);
1421
1422 common->curaid = bss_conf->assoc ?
1423 bss_conf->aid : 0;
1424
1425 if (bss_conf->assoc)
1426 ath9k_htc_start_ani(priv);
1427 else
1428 ath9k_htc_stop_ani(priv);
1429 }
Sujithfb9987d2010-03-17 14:25:25 +05301430 }
1431
1432 if (changed & BSS_CHANGED_BSSID) {
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301433 if (set_assoc) {
1434 memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN);
1435 ath9k_hw_write_associd(ah);
Sujithfb9987d2010-03-17 14:25:25 +05301436
Sujith Manoharane7a2a4f2011-02-27 09:20:40 +05301437 ath_dbg(common, ATH_DBG_CONFIG,
1438 "BSSID: %pM aid: 0x%x\n",
1439 common->curbssid, common->curaid);
1440 }
Sujithfb9987d2010-03-17 14:25:25 +05301441 }
1442
Sujith Manoharana5fae372011-02-21 07:49:53 +05301443 if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) {
1444 ath_dbg(common, ATH_DBG_CONFIG,
1445 "Beacon enabled for BSS: %pM\n", bss_conf->bssid);
Sujith Manoharan9b674a02011-04-13 11:23:17 +05301446 ath9k_htc_set_tsfadjust(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301447 priv->op_flags |= OP_ENABLE_BEACON;
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301448 ath9k_htc_beacon_config(priv, vif);
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 /*
1453 * Disable SWBA interrupt only if there are no
1454 * AP/IBSS interfaces.
1455 */
1456 if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) {
1457 ath_dbg(common, ATH_DBG_CONFIG,
1458 "Beacon disabled for BSS: %pM\n",
1459 bss_conf->bssid);
1460 priv->op_flags &= ~OP_ENABLE_BEACON;
1461 ath9k_htc_beacon_config(priv, vif);
1462 }
1463 }
1464
1465 if (changed & BSS_CHANGED_BEACON_INT) {
1466 /*
1467 * Reset the HW TSF for the first AP interface.
1468 */
1469 if ((priv->ah->opmode == NL80211_IFTYPE_AP) &&
1470 (priv->nvifs == 1) &&
1471 (priv->num_ap_vif == 1) &&
1472 (vif->type == NL80211_IFTYPE_AP)) {
1473 priv->op_flags |= OP_TSF_RESET;
1474 }
1475 ath_dbg(common, ATH_DBG_CONFIG,
1476 "Beacon interval changed for BSS: %pM\n",
1477 bss_conf->bssid);
Vivek Natarajan1c3652a2010-04-05 14:48:06 +05301478 ath9k_htc_beacon_config(priv, vif);
Sujithfb9987d2010-03-17 14:25:25 +05301479 }
1480
Sujithfb9987d2010-03-17 14:25:25 +05301481 if (changed & BSS_CHANGED_ERP_SLOT) {
1482 if (bss_conf->use_short_slot)
1483 ah->slottime = 9;
1484 else
1485 ah->slottime = 20;
1486
1487 ath9k_hw_init_global_settings(ah);
1488 }
1489
Sujith2c76ef82010-05-17 12:01:18 +05301490 if (changed & BSS_CHANGED_HT)
1491 ath9k_htc_update_rate(priv, vif, bss_conf);
1492
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301493 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301494 mutex_unlock(&priv->mutex);
1495}
1496
1497static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw)
1498{
1499 struct ath9k_htc_priv *priv = hw->priv;
1500 u64 tsf;
1501
1502 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301503 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301504 tsf = ath9k_hw_gettsf64(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301505 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301506 mutex_unlock(&priv->mutex);
1507
1508 return tsf;
1509}
1510
1511static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf)
1512{
1513 struct ath9k_htc_priv *priv = hw->priv;
1514
1515 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301516 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301517 ath9k_hw_settsf64(priv->ah, tsf);
Sujithcb551df2010-06-01 15:14:12 +05301518 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301519 mutex_unlock(&priv->mutex);
1520}
1521
1522static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw)
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_reset_tsf(priv->ah);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301529 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301530 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301531}
1532
1533static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
1534 struct ieee80211_vif *vif,
1535 enum ieee80211_ampdu_mlme_action action,
1536 struct ieee80211_sta *sta,
Johannes Berg0b01f032011-01-18 13:51:05 +01001537 u16 tid, u16 *ssn, u8 buf_size)
Sujithfb9987d2010-03-17 14:25:25 +05301538{
1539 struct ath9k_htc_priv *priv = hw->priv;
Sujithfb9987d2010-03-17 14:25:25 +05301540 struct ath9k_htc_sta *ista;
Sujithd7ca2132010-06-15 10:24:37 +05301541 int ret = 0;
Sujithfb9987d2010-03-17 14:25:25 +05301542
Sujith Manoharan87df8952011-02-21 07:49:08 +05301543 mutex_lock(&priv->mutex);
1544
Sujithfb9987d2010-03-17 14:25:25 +05301545 switch (action) {
1546 case IEEE80211_AMPDU_RX_START:
1547 break;
1548 case IEEE80211_AMPDU_RX_STOP:
1549 break;
1550 case IEEE80211_AMPDU_TX_START:
Sujithd7ca2132010-06-15 10:24:37 +05301551 ret = ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1552 if (!ret)
1553 ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
1554 break;
Sujithfb9987d2010-03-17 14:25:25 +05301555 case IEEE80211_AMPDU_TX_STOP:
Sujithd7ca2132010-06-15 10:24:37 +05301556 ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
1557 ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
Sujithfb9987d2010-03-17 14:25:25 +05301558 break;
1559 case IEEE80211_AMPDU_TX_OPERATIONAL:
1560 ista = (struct ath9k_htc_sta *) sta->drv_priv;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301561 spin_lock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301562 ista->tid_state[tid] = AGGR_OPERATIONAL;
Sujith Manoharan658ef042011-04-13 11:25:00 +05301563 spin_unlock_bh(&priv->tx.tx_lock);
Sujithfb9987d2010-03-17 14:25:25 +05301564 break;
1565 default:
Joe Perches38002762010-12-02 19:12:36 -08001566 ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n");
Sujithfb9987d2010-03-17 14:25:25 +05301567 }
1568
Sujith Manoharan87df8952011-02-21 07:49:08 +05301569 mutex_unlock(&priv->mutex);
1570
Sujithd7ca2132010-06-15 10:24:37 +05301571 return ret;
Sujithfb9987d2010-03-17 14:25:25 +05301572}
1573
1574static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw)
1575{
1576 struct ath9k_htc_priv *priv = hw->priv;
1577
1578 mutex_lock(&priv->mutex);
1579 spin_lock_bh(&priv->beacon_lock);
1580 priv->op_flags |= OP_SCANNING;
1581 spin_unlock_bh(&priv->beacon_lock);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301582 cancel_work_sync(&priv->ps_work);
Sujith Manoharana2362542011-02-21 07:49:38 +05301583 ath9k_htc_stop_ani(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301584 mutex_unlock(&priv->mutex);
1585}
1586
1587static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw)
1588{
1589 struct ath9k_htc_priv *priv = hw->priv;
1590
1591 mutex_lock(&priv->mutex);
1592 spin_lock_bh(&priv->beacon_lock);
1593 priv->op_flags &= ~OP_SCANNING;
1594 spin_unlock_bh(&priv->beacon_lock);
Sujith Manoharan7c277342011-02-21 07:48:39 +05301595 ath9k_htc_ps_wakeup(priv);
1596 ath9k_htc_vif_reconfig(priv);
Vivek Natarajanbde748a2010-04-05 14:48:05 +05301597 ath9k_htc_ps_restore(priv);
Sujithcb551df2010-06-01 15:14:12 +05301598 mutex_unlock(&priv->mutex);
Sujithfb9987d2010-03-17 14:25:25 +05301599}
1600
1601static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value)
1602{
1603 return 0;
1604}
1605
1606static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw,
1607 u8 coverage_class)
1608{
1609 struct ath9k_htc_priv *priv = hw->priv;
1610
1611 mutex_lock(&priv->mutex);
Sujithcb551df2010-06-01 15:14:12 +05301612 ath9k_htc_ps_wakeup(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301613 priv->ah->coverage_class = coverage_class;
1614 ath9k_hw_init_global_settings(priv->ah);
Sujithcb551df2010-06-01 15:14:12 +05301615 ath9k_htc_ps_restore(priv);
Sujithfb9987d2010-03-17 14:25:25 +05301616 mutex_unlock(&priv->mutex);
1617}
1618
1619struct ieee80211_ops ath9k_htc_ops = {
1620 .tx = ath9k_htc_tx,
1621 .start = ath9k_htc_start,
1622 .stop = ath9k_htc_stop,
1623 .add_interface = ath9k_htc_add_interface,
1624 .remove_interface = ath9k_htc_remove_interface,
1625 .config = ath9k_htc_config,
1626 .configure_filter = ath9k_htc_configure_filter,
Sujithabd984e2010-05-18 15:26:04 +05301627 .sta_add = ath9k_htc_sta_add,
1628 .sta_remove = ath9k_htc_sta_remove,
Sujithfb9987d2010-03-17 14:25:25 +05301629 .conf_tx = ath9k_htc_conf_tx,
1630 .bss_info_changed = ath9k_htc_bss_info_changed,
1631 .set_key = ath9k_htc_set_key,
1632 .get_tsf = ath9k_htc_get_tsf,
1633 .set_tsf = ath9k_htc_set_tsf,
1634 .reset_tsf = ath9k_htc_reset_tsf,
1635 .ampdu_action = ath9k_htc_ampdu_action,
1636 .sw_scan_start = ath9k_htc_sw_scan_start,
1637 .sw_scan_complete = ath9k_htc_sw_scan_complete,
1638 .set_rts_threshold = ath9k_htc_set_rts_threshold,
1639 .rfkill_poll = ath9k_htc_rfkill_poll_state,
1640 .set_coverage_class = ath9k_htc_set_coverage_class,
1641};