Commit | Line | Data |
---|---|---|
fb9987d0 S |
1 | /* |
2 | * Copyright (c) 2010 Atheros Communications Inc. | |
3 | * | |
4 | * Permission to use, copy, modify, and/or distribute this software for any | |
5 | * purpose with or without fee is hereby granted, provided that the above | |
6 | * copyright notice and this permission notice appear in all copies. | |
7 | * | |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
15 | */ | |
16 | ||
17 | #include "htc.h" | |
18 | ||
19 | #ifdef CONFIG_ATH9K_HTC_DEBUGFS | |
20 | static struct dentry *ath9k_debugfs_root; | |
21 | #endif | |
22 | ||
23 | /*************/ | |
24 | /* Utilities */ | |
25 | /*************/ | |
26 | ||
27 | static void ath_update_txpow(struct ath9k_htc_priv *priv) | |
28 | { | |
29 | struct ath_hw *ah = priv->ah; | |
30 | u32 txpow; | |
31 | ||
32 | if (priv->curtxpow != priv->txpowlimit) { | |
33 | ath9k_hw_set_txpowerlimit(ah, priv->txpowlimit); | |
34 | /* read back in case value is clamped */ | |
35 | ath9k_hw_getcapability(ah, ATH9K_CAP_TXPOW, 1, &txpow); | |
36 | priv->curtxpow = txpow; | |
37 | } | |
38 | } | |
39 | ||
40 | /* HACK Alert: Use 11NG for 2.4, use 11NA for 5 */ | |
41 | static enum htc_phymode ath9k_htc_get_curmode(struct ath9k_htc_priv *priv, | |
42 | struct ath9k_channel *ichan) | |
43 | { | |
44 | enum htc_phymode mode; | |
45 | ||
46 | mode = HTC_MODE_AUTO; | |
47 | ||
48 | switch (ichan->chanmode) { | |
49 | case CHANNEL_G: | |
50 | case CHANNEL_G_HT20: | |
51 | case CHANNEL_G_HT40PLUS: | |
52 | case CHANNEL_G_HT40MINUS: | |
53 | mode = HTC_MODE_11NG; | |
54 | break; | |
55 | case CHANNEL_A: | |
56 | case CHANNEL_A_HT20: | |
57 | case CHANNEL_A_HT40PLUS: | |
58 | case CHANNEL_A_HT40MINUS: | |
59 | mode = HTC_MODE_11NA; | |
60 | break; | |
61 | default: | |
62 | break; | |
63 | } | |
64 | ||
65 | return mode; | |
66 | } | |
67 | ||
68 | static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, | |
69 | struct ieee80211_hw *hw, | |
70 | struct ath9k_channel *hchan) | |
71 | { | |
72 | struct ath_hw *ah = priv->ah; | |
73 | struct ath_common *common = ath9k_hw_common(ah); | |
74 | struct ieee80211_conf *conf = &common->hw->conf; | |
75 | bool fastcc = true; | |
76 | struct ieee80211_channel *channel = hw->conf.channel; | |
77 | enum htc_phymode mode; | |
78 | u16 htc_mode; | |
79 | u8 cmd_rsp; | |
80 | int ret; | |
81 | ||
82 | if (priv->op_flags & OP_INVALID) | |
83 | return -EIO; | |
84 | ||
85 | if (priv->op_flags & OP_FULL_RESET) | |
86 | fastcc = false; | |
87 | ||
88 | /* Fiddle around with fastcc later on, for now just use full reset */ | |
89 | fastcc = false; | |
90 | ||
91 | htc_stop(priv->htc); | |
92 | WMI_CMD(WMI_DISABLE_INTR_CMDID); | |
93 | WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); | |
94 | WMI_CMD(WMI_STOP_RECV_CMDID); | |
95 | ||
96 | ath_print(common, ATH_DBG_CONFIG, | |
97 | "(%u MHz) -> (%u MHz), HT: %d, HT40: %d\n", | |
98 | priv->ah->curchan->channel, | |
99 | channel->center_freq, conf_is_ht(conf), conf_is_ht40(conf)); | |
100 | ||
101 | ret = ath9k_hw_reset(ah, hchan, fastcc); | |
102 | if (ret) { | |
103 | ath_print(common, ATH_DBG_FATAL, | |
104 | "Unable to reset channel (%u Mhz) " | |
105 | "reset status %d\n", channel->center_freq, ret); | |
106 | goto err; | |
107 | } | |
108 | ||
109 | ath_update_txpow(priv); | |
110 | ||
111 | WMI_CMD(WMI_START_RECV_CMDID); | |
112 | if (ret) | |
113 | goto err; | |
114 | ||
115 | ath9k_host_rx_init(priv); | |
116 | ||
117 | mode = ath9k_htc_get_curmode(priv, hchan); | |
118 | htc_mode = cpu_to_be16(mode); | |
119 | WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); | |
120 | if (ret) | |
121 | goto err; | |
122 | ||
123 | WMI_CMD(WMI_ENABLE_INTR_CMDID); | |
124 | if (ret) | |
125 | goto err; | |
126 | ||
127 | htc_start(priv->htc); | |
128 | ||
129 | priv->op_flags &= ~OP_FULL_RESET; | |
130 | err: | |
131 | return ret; | |
132 | } | |
133 | ||
134 | static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) | |
135 | { | |
136 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
137 | struct ath9k_htc_target_vif hvif; | |
138 | int ret = 0; | |
139 | u8 cmd_rsp; | |
140 | ||
141 | if (priv->nvifs > 0) | |
142 | return -ENOBUFS; | |
143 | ||
144 | memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); | |
145 | memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); | |
146 | ||
147 | hvif.opmode = cpu_to_be32(HTC_M_MONITOR); | |
148 | priv->ah->opmode = NL80211_IFTYPE_MONITOR; | |
149 | hvif.index = priv->nvifs; | |
150 | ||
151 | WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); | |
152 | if (ret) | |
153 | return ret; | |
154 | ||
155 | priv->nvifs++; | |
156 | return 0; | |
157 | } | |
158 | ||
159 | static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) | |
160 | { | |
161 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
162 | struct ath9k_htc_target_vif hvif; | |
163 | int ret = 0; | |
164 | u8 cmd_rsp; | |
165 | ||
166 | memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); | |
167 | memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); | |
168 | hvif.index = 0; /* Should do for now */ | |
169 | WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); | |
170 | priv->nvifs--; | |
171 | ||
172 | return ret; | |
173 | } | |
174 | ||
175 | static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, | |
176 | struct ieee80211_vif *vif, | |
177 | struct ieee80211_sta *sta) | |
178 | { | |
179 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
180 | struct ath9k_htc_target_sta tsta; | |
181 | struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; | |
182 | struct ath9k_htc_sta *ista; | |
183 | int ret; | |
184 | u8 cmd_rsp; | |
185 | ||
186 | if (priv->nstations >= ATH9K_HTC_MAX_STA) | |
187 | return -ENOBUFS; | |
188 | ||
189 | memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); | |
190 | ||
191 | if (sta) { | |
192 | ista = (struct ath9k_htc_sta *) sta->drv_priv; | |
193 | memcpy(&tsta.macaddr, sta->addr, ETH_ALEN); | |
194 | memcpy(&tsta.bssid, common->curbssid, ETH_ALEN); | |
195 | tsta.associd = common->curaid; | |
196 | tsta.is_vif_sta = 0; | |
197 | tsta.valid = true; | |
198 | ista->index = priv->nstations; | |
199 | } else { | |
200 | memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); | |
201 | tsta.is_vif_sta = 1; | |
202 | } | |
203 | ||
204 | tsta.sta_index = priv->nstations; | |
205 | tsta.vif_index = avp->index; | |
206 | tsta.maxampdu = 0xffff; | |
207 | if (sta && sta->ht_cap.ht_supported) | |
208 | tsta.flags = cpu_to_be16(ATH_HTC_STA_HT); | |
209 | ||
210 | WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); | |
211 | if (ret) { | |
212 | if (sta) | |
213 | ath_print(common, ATH_DBG_FATAL, | |
214 | "Unable to add station entry for: %pM\n", sta->addr); | |
215 | return ret; | |
216 | } | |
217 | ||
218 | if (sta) | |
219 | ath_print(common, ATH_DBG_CONFIG, | |
220 | "Added a station entry for: %pM (idx: %d)\n", | |
221 | sta->addr, tsta.sta_index); | |
222 | ||
223 | priv->nstations++; | |
224 | return 0; | |
225 | } | |
226 | ||
227 | static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, | |
228 | struct ieee80211_vif *vif, | |
229 | struct ieee80211_sta *sta) | |
230 | { | |
231 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
232 | struct ath9k_htc_sta *ista; | |
233 | int ret; | |
234 | u8 cmd_rsp, sta_idx; | |
235 | ||
236 | if (sta) { | |
237 | ista = (struct ath9k_htc_sta *) sta->drv_priv; | |
238 | sta_idx = ista->index; | |
239 | } else { | |
240 | sta_idx = 0; | |
241 | } | |
242 | ||
243 | WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); | |
244 | if (ret) { | |
245 | if (sta) | |
246 | ath_print(common, ATH_DBG_FATAL, | |
247 | "Unable to remove station entry for: %pM\n", | |
248 | sta->addr); | |
249 | return ret; | |
250 | } | |
251 | ||
252 | if (sta) | |
253 | ath_print(common, ATH_DBG_CONFIG, | |
254 | "Removed a station entry for: %pM (idx: %d)\n", | |
255 | sta->addr, sta_idx); | |
256 | ||
257 | priv->nstations--; | |
258 | return 0; | |
259 | } | |
260 | ||
261 | static int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv) | |
262 | { | |
263 | struct ath9k_htc_cap_target tcap; | |
264 | int ret; | |
265 | u8 cmd_rsp; | |
266 | ||
267 | memset(&tcap, 0, sizeof(struct ath9k_htc_cap_target)); | |
268 | ||
269 | /* FIXME: Values are hardcoded */ | |
270 | tcap.flags = 0x240c40; | |
271 | tcap.flags_ext = 0x80601000; | |
272 | tcap.ampdu_limit = 0xffff0000; | |
273 | tcap.ampdu_subframes = 20; | |
274 | tcap.tx_chainmask_legacy = 1; | |
275 | tcap.protmode = 1; | |
276 | tcap.tx_chainmask = 1; | |
277 | ||
278 | WMI_CMD_BUF(WMI_TARGET_IC_UPDATE_CMDID, &tcap); | |
279 | ||
280 | return ret; | |
281 | } | |
282 | ||
283 | static int ath9k_htc_init_rate(struct ath9k_htc_priv *priv, | |
284 | struct ieee80211_vif *vif, | |
285 | struct ieee80211_sta *sta) | |
286 | { | |
287 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
288 | struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; | |
289 | struct ieee80211_supported_band *sband; | |
290 | struct ath9k_htc_target_rate trate; | |
291 | u32 caps = 0; | |
292 | u8 cmd_rsp; | |
293 | int i, j, ret; | |
294 | ||
295 | memset(&trate, 0, sizeof(trate)); | |
296 | ||
297 | /* Only 2GHz is supported */ | |
298 | sband = priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; | |
299 | ||
300 | for (i = 0, j = 0; i < sband->n_bitrates; i++) { | |
301 | if (sta->supp_rates[sband->band] & BIT(i)) { | |
302 | priv->tgt_rate.rates.legacy_rates.rs_rates[j] | |
303 | = (sband->bitrates[i].bitrate * 2) / 10; | |
304 | j++; | |
305 | } | |
306 | } | |
307 | priv->tgt_rate.rates.legacy_rates.rs_nrates = j; | |
308 | ||
309 | if (sta->ht_cap.ht_supported) { | |
310 | for (i = 0, j = 0; i < 77; i++) { | |
311 | if (sta->ht_cap.mcs.rx_mask[i/8] & (1<<(i%8))) | |
312 | priv->tgt_rate.rates.ht_rates.rs_rates[j++] = i; | |
313 | if (j == ATH_HTC_RATE_MAX) | |
314 | break; | |
315 | } | |
316 | priv->tgt_rate.rates.ht_rates.rs_nrates = j; | |
317 | ||
318 | caps = WLAN_RC_HT_FLAG; | |
319 | if (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) | |
320 | caps |= WLAN_RC_40_FLAG; | |
321 | if (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) | |
322 | caps |= WLAN_RC_SGI_FLAG; | |
323 | ||
324 | } | |
325 | ||
326 | priv->tgt_rate.sta_index = ista->index; | |
327 | priv->tgt_rate.isnew = 1; | |
328 | trate = priv->tgt_rate; | |
329 | priv->tgt_rate.capflags = caps; | |
330 | trate.capflags = cpu_to_be32(caps); | |
331 | ||
332 | WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate); | |
333 | if (ret) { | |
334 | ath_print(common, ATH_DBG_FATAL, | |
335 | "Unable to initialize Rate information on target\n"); | |
336 | return ret; | |
337 | } | |
338 | ||
339 | ath_print(common, ATH_DBG_CONFIG, | |
340 | "Updated target STA: %pM (caps: 0x%x)\n", sta->addr, caps); | |
341 | return 0; | |
342 | } | |
343 | ||
344 | static bool check_rc_update(struct ieee80211_hw *hw, bool *cw40) | |
345 | { | |
346 | struct ath9k_htc_priv *priv = hw->priv; | |
347 | struct ieee80211_conf *conf = &hw->conf; | |
348 | ||
349 | if (!conf_is_ht(conf)) | |
350 | return false; | |
351 | ||
352 | if (!(priv->op_flags & OP_ASSOCIATED) || | |
353 | (priv->op_flags & OP_SCANNING)) | |
354 | return false; | |
355 | ||
356 | if (conf_is_ht40(conf)) { | |
357 | if (priv->ah->curchan->chanmode & | |
358 | (CHANNEL_HT40PLUS | CHANNEL_HT40MINUS)) { | |
359 | return false; | |
360 | } else { | |
361 | *cw40 = true; | |
362 | return true; | |
363 | } | |
364 | } else { /* ht20 */ | |
365 | if (priv->ah->curchan->chanmode & CHANNEL_HT20) | |
366 | return false; | |
367 | else | |
368 | return true; | |
369 | } | |
370 | } | |
371 | ||
372 | static void ath9k_htc_rc_update(struct ath9k_htc_priv *priv, bool is_cw40) | |
373 | { | |
374 | struct ath9k_htc_target_rate trate; | |
375 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
376 | int ret; | |
377 | u8 cmd_rsp; | |
378 | ||
379 | memset(&trate, 0, sizeof(trate)); | |
380 | ||
381 | trate = priv->tgt_rate; | |
382 | ||
383 | if (is_cw40) | |
384 | priv->tgt_rate.capflags |= WLAN_RC_40_FLAG; | |
385 | else | |
386 | priv->tgt_rate.capflags &= ~WLAN_RC_40_FLAG; | |
387 | ||
388 | trate.capflags = cpu_to_be32(priv->tgt_rate.capflags); | |
389 | ||
390 | WMI_CMD_BUF(WMI_RC_RATE_UPDATE_CMDID, &trate); | |
391 | if (ret) { | |
392 | ath_print(common, ATH_DBG_FATAL, | |
393 | "Unable to update Rate information on target\n"); | |
394 | return; | |
395 | } | |
396 | ||
397 | ath_print(common, ATH_DBG_CONFIG, "Rate control updated with " | |
398 | "caps:0x%x on target\n", priv->tgt_rate.capflags); | |
399 | } | |
400 | ||
401 | static int ath9k_htc_aggr_oper(struct ath9k_htc_priv *priv, | |
402 | struct ieee80211_vif *vif, | |
403 | u8 *sta_addr, u8 tid, bool oper) | |
404 | { | |
405 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
406 | struct ath9k_htc_target_aggr aggr; | |
407 | struct ieee80211_sta *sta = NULL; | |
408 | struct ath9k_htc_sta *ista = (struct ath9k_htc_sta *) sta->drv_priv; | |
409 | int ret = 0; | |
410 | u8 cmd_rsp; | |
411 | ||
412 | if (tid > ATH9K_HTC_MAX_TID) | |
413 | return -EINVAL; | |
414 | ||
415 | rcu_read_lock(); | |
416 | sta = ieee80211_find_sta(vif, sta_addr); | |
417 | if (sta) { | |
418 | ista = (struct ath9k_htc_sta *) sta->drv_priv; | |
419 | } else { | |
420 | rcu_read_unlock(); | |
421 | return -EINVAL; | |
422 | } | |
423 | ||
424 | if (!ista) { | |
425 | rcu_read_unlock(); | |
426 | return -EINVAL; | |
427 | } | |
428 | ||
429 | memset(&aggr, 0, sizeof(struct ath9k_htc_target_aggr)); | |
430 | ||
431 | aggr.sta_index = ista->index; | |
432 | rcu_read_unlock(); | |
433 | aggr.tidno = tid; | |
434 | aggr.aggr_enable = oper; | |
435 | ||
436 | if (oper) | |
437 | ista->tid_state[tid] = AGGR_START; | |
438 | else | |
439 | ista->tid_state[tid] = AGGR_STOP; | |
440 | ||
441 | WMI_CMD_BUF(WMI_TX_AGGR_ENABLE_CMDID, &aggr); | |
442 | if (ret) | |
443 | ath_print(common, ATH_DBG_CONFIG, | |
444 | "Unable to %s TX aggregation for (%pM, %d)\n", | |
445 | (oper) ? "start" : "stop", sta->addr, tid); | |
446 | else | |
447 | ath_print(common, ATH_DBG_CONFIG, | |
448 | "%s aggregation for (%pM, %d)\n", | |
449 | (oper) ? "Starting" : "Stopping", sta->addr, tid); | |
450 | ||
451 | return ret; | |
452 | } | |
453 | ||
454 | void ath9k_htc_aggr_work(struct work_struct *work) | |
455 | { | |
456 | int ret = 0; | |
457 | struct ath9k_htc_priv *priv = | |
458 | container_of(work, struct ath9k_htc_priv, | |
459 | ath9k_aggr_work.work); | |
460 | struct ath9k_htc_aggr_work *wk = &priv->aggr_work; | |
461 | ||
462 | mutex_lock(&wk->mutex); | |
463 | ||
464 | switch (wk->action) { | |
465 | case IEEE80211_AMPDU_TX_START: | |
466 | ret = ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr, | |
467 | wk->tid, true); | |
468 | if (!ret) | |
469 | ieee80211_start_tx_ba_cb(wk->vif, wk->sta_addr, | |
470 | wk->tid); | |
471 | break; | |
472 | case IEEE80211_AMPDU_TX_STOP: | |
473 | ath9k_htc_aggr_oper(priv, wk->vif, wk->sta_addr, | |
474 | wk->tid, false); | |
475 | ieee80211_stop_tx_ba_cb(wk->vif, wk->sta_addr, wk->tid); | |
476 | break; | |
477 | default: | |
478 | ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, | |
479 | "Unknown AMPDU action\n"); | |
480 | } | |
481 | ||
482 | mutex_unlock(&wk->mutex); | |
483 | } | |
484 | ||
485 | /*********/ | |
486 | /* DEBUG */ | |
487 | /*********/ | |
488 | ||
489 | #ifdef CONFIG_ATH9K_HTC_DEBUGFS | |
490 | ||
491 | static int ath9k_debugfs_open(struct inode *inode, struct file *file) | |
492 | { | |
493 | file->private_data = inode->i_private; | |
494 | return 0; | |
495 | } | |
496 | ||
497 | static ssize_t read_file_tgt_stats(struct file *file, char __user *user_buf, | |
498 | size_t count, loff_t *ppos) | |
499 | { | |
500 | struct ath9k_htc_priv *priv = | |
501 | (struct ath9k_htc_priv *) file->private_data; | |
502 | struct ath9k_htc_target_stats cmd_rsp; | |
503 | char buf[512]; | |
504 | unsigned int len = 0; | |
505 | int ret = 0; | |
506 | ||
507 | memset(&cmd_rsp, 0, sizeof(cmd_rsp)); | |
508 | ||
509 | WMI_CMD(WMI_TGT_STATS_CMDID); | |
510 | if (ret) | |
511 | return -EINVAL; | |
512 | ||
513 | ||
514 | len += snprintf(buf + len, sizeof(buf) - len, | |
515 | "%19s : %10u\n", "TX Short Retries", | |
516 | be32_to_cpu(cmd_rsp.tx_shortretry)); | |
517 | len += snprintf(buf + len, sizeof(buf) - len, | |
518 | "%19s : %10u\n", "TX Long Retries", | |
519 | be32_to_cpu(cmd_rsp.tx_longretry)); | |
520 | len += snprintf(buf + len, sizeof(buf) - len, | |
521 | "%19s : %10u\n", "TX Xretries", | |
522 | be32_to_cpu(cmd_rsp.tx_xretries)); | |
523 | len += snprintf(buf + len, sizeof(buf) - len, | |
524 | "%19s : %10u\n", "TX Unaggr. Xretries", | |
525 | be32_to_cpu(cmd_rsp.ht_txunaggr_xretry)); | |
526 | len += snprintf(buf + len, sizeof(buf) - len, | |
527 | "%19s : %10u\n", "TX Xretries (HT)", | |
528 | be32_to_cpu(cmd_rsp.ht_tx_xretries)); | |
529 | len += snprintf(buf + len, sizeof(buf) - len, | |
530 | "%19s : %10u\n", "TX Rate", priv->debug.txrate); | |
531 | ||
532 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | |
533 | } | |
534 | ||
535 | static const struct file_operations fops_tgt_stats = { | |
536 | .read = read_file_tgt_stats, | |
537 | .open = ath9k_debugfs_open, | |
538 | .owner = THIS_MODULE | |
539 | }; | |
540 | ||
541 | static ssize_t read_file_xmit(struct file *file, char __user *user_buf, | |
542 | size_t count, loff_t *ppos) | |
543 | { | |
544 | struct ath9k_htc_priv *priv = | |
545 | (struct ath9k_htc_priv *) file->private_data; | |
546 | char buf[512]; | |
547 | unsigned int len = 0; | |
548 | ||
549 | len += snprintf(buf + len, sizeof(buf) - len, | |
550 | "%20s : %10u\n", "Buffers queued", | |
551 | priv->debug.tx_stats.buf_queued); | |
552 | len += snprintf(buf + len, sizeof(buf) - len, | |
553 | "%20s : %10u\n", "Buffers completed", | |
554 | priv->debug.tx_stats.buf_completed); | |
555 | len += snprintf(buf + len, sizeof(buf) - len, | |
556 | "%20s : %10u\n", "SKBs queued", | |
557 | priv->debug.tx_stats.skb_queued); | |
558 | len += snprintf(buf + len, sizeof(buf) - len, | |
559 | "%20s : %10u\n", "SKBs completed", | |
560 | priv->debug.tx_stats.skb_completed); | |
561 | ||
562 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | |
563 | } | |
564 | ||
565 | static const struct file_operations fops_xmit = { | |
566 | .read = read_file_xmit, | |
567 | .open = ath9k_debugfs_open, | |
568 | .owner = THIS_MODULE | |
569 | }; | |
570 | ||
571 | static ssize_t read_file_recv(struct file *file, char __user *user_buf, | |
572 | size_t count, loff_t *ppos) | |
573 | { | |
574 | struct ath9k_htc_priv *priv = | |
575 | (struct ath9k_htc_priv *) file->private_data; | |
576 | char buf[512]; | |
577 | unsigned int len = 0; | |
578 | ||
579 | len += snprintf(buf + len, sizeof(buf) - len, | |
580 | "%20s : %10u\n", "SKBs allocated", | |
581 | priv->debug.rx_stats.skb_allocated); | |
582 | len += snprintf(buf + len, sizeof(buf) - len, | |
583 | "%20s : %10u\n", "SKBs completed", | |
584 | priv->debug.rx_stats.skb_completed); | |
585 | len += snprintf(buf + len, sizeof(buf) - len, | |
586 | "%20s : %10u\n", "SKBs Dropped", | |
587 | priv->debug.rx_stats.skb_dropped); | |
588 | ||
589 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | |
590 | } | |
591 | ||
592 | static const struct file_operations fops_recv = { | |
593 | .read = read_file_recv, | |
594 | .open = ath9k_debugfs_open, | |
595 | .owner = THIS_MODULE | |
596 | }; | |
597 | ||
e1572c5e | 598 | int ath9k_htc_init_debug(struct ath_hw *ah) |
fb9987d0 S |
599 | { |
600 | struct ath_common *common = ath9k_hw_common(ah); | |
601 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
602 | ||
603 | if (!ath9k_debugfs_root) | |
604 | return -ENOENT; | |
605 | ||
606 | priv->debug.debugfs_phy = debugfs_create_dir(wiphy_name(priv->hw->wiphy), | |
607 | ath9k_debugfs_root); | |
608 | if (!priv->debug.debugfs_phy) | |
609 | goto err; | |
610 | ||
611 | priv->debug.debugfs_tgt_stats = debugfs_create_file("tgt_stats", S_IRUSR, | |
612 | priv->debug.debugfs_phy, | |
613 | priv, &fops_tgt_stats); | |
614 | if (!priv->debug.debugfs_tgt_stats) | |
615 | goto err; | |
616 | ||
617 | ||
618 | priv->debug.debugfs_xmit = debugfs_create_file("xmit", S_IRUSR, | |
619 | priv->debug.debugfs_phy, | |
620 | priv, &fops_xmit); | |
621 | if (!priv->debug.debugfs_xmit) | |
622 | goto err; | |
623 | ||
624 | priv->debug.debugfs_recv = debugfs_create_file("recv", S_IRUSR, | |
625 | priv->debug.debugfs_phy, | |
626 | priv, &fops_recv); | |
627 | if (!priv->debug.debugfs_recv) | |
628 | goto err; | |
629 | ||
630 | return 0; | |
631 | ||
632 | err: | |
e1572c5e | 633 | ath9k_htc_exit_debug(ah); |
fb9987d0 S |
634 | return -ENOMEM; |
635 | } | |
636 | ||
e1572c5e | 637 | void ath9k_htc_exit_debug(struct ath_hw *ah) |
fb9987d0 S |
638 | { |
639 | struct ath_common *common = ath9k_hw_common(ah); | |
640 | struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) common->priv; | |
641 | ||
642 | debugfs_remove(priv->debug.debugfs_recv); | |
643 | debugfs_remove(priv->debug.debugfs_xmit); | |
644 | debugfs_remove(priv->debug.debugfs_tgt_stats); | |
645 | debugfs_remove(priv->debug.debugfs_phy); | |
646 | } | |
647 | ||
e1572c5e | 648 | int ath9k_htc_debug_create_root(void) |
fb9987d0 S |
649 | { |
650 | ath9k_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); | |
651 | if (!ath9k_debugfs_root) | |
652 | return -ENOENT; | |
653 | ||
654 | return 0; | |
655 | } | |
656 | ||
e1572c5e | 657 | void ath9k_htc_debug_remove_root(void) |
fb9987d0 S |
658 | { |
659 | debugfs_remove(ath9k_debugfs_root); | |
660 | ath9k_debugfs_root = NULL; | |
661 | } | |
662 | ||
663 | #endif /* CONFIG_ATH9K_HTC_DEBUGFS */ | |
664 | ||
665 | /*******/ | |
666 | /* ANI */ | |
667 | /*******/ | |
668 | ||
669 | static void ath_start_ani(struct ath9k_htc_priv *priv) | |
670 | { | |
671 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
672 | unsigned long timestamp = jiffies_to_msecs(jiffies); | |
673 | ||
674 | common->ani.longcal_timer = timestamp; | |
675 | common->ani.shortcal_timer = timestamp; | |
676 | common->ani.checkani_timer = timestamp; | |
677 | ||
678 | ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, | |
679 | msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); | |
680 | } | |
681 | ||
682 | void ath9k_ani_work(struct work_struct *work) | |
683 | { | |
684 | struct ath9k_htc_priv *priv = | |
685 | container_of(work, struct ath9k_htc_priv, | |
686 | ath9k_ani_work.work); | |
687 | struct ath_hw *ah = priv->ah; | |
688 | struct ath_common *common = ath9k_hw_common(ah); | |
689 | bool longcal = false; | |
690 | bool shortcal = false; | |
691 | bool aniflag = false; | |
692 | unsigned int timestamp = jiffies_to_msecs(jiffies); | |
693 | u32 cal_interval, short_cal_interval; | |
694 | ||
695 | short_cal_interval = ATH_STA_SHORT_CALINTERVAL; | |
696 | ||
697 | /* Long calibration runs independently of short calibration. */ | |
698 | if ((timestamp - common->ani.longcal_timer) >= ATH_LONG_CALINTERVAL) { | |
699 | longcal = true; | |
700 | ath_print(common, ATH_DBG_ANI, "longcal @%lu\n", jiffies); | |
701 | common->ani.longcal_timer = timestamp; | |
702 | } | |
703 | ||
704 | /* Short calibration applies only while caldone is false */ | |
705 | if (!common->ani.caldone) { | |
706 | if ((timestamp - common->ani.shortcal_timer) >= | |
707 | short_cal_interval) { | |
708 | shortcal = true; | |
709 | ath_print(common, ATH_DBG_ANI, | |
710 | "shortcal @%lu\n", jiffies); | |
711 | common->ani.shortcal_timer = timestamp; | |
712 | common->ani.resetcal_timer = timestamp; | |
713 | } | |
714 | } else { | |
715 | if ((timestamp - common->ani.resetcal_timer) >= | |
716 | ATH_RESTART_CALINTERVAL) { | |
717 | common->ani.caldone = ath9k_hw_reset_calvalid(ah); | |
718 | if (common->ani.caldone) | |
719 | common->ani.resetcal_timer = timestamp; | |
720 | } | |
721 | } | |
722 | ||
723 | /* Verify whether we must check ANI */ | |
724 | if ((timestamp - common->ani.checkani_timer) >= ATH_ANI_POLLINTERVAL) { | |
725 | aniflag = true; | |
726 | common->ani.checkani_timer = timestamp; | |
727 | } | |
728 | ||
729 | /* Skip all processing if there's nothing to do. */ | |
730 | if (longcal || shortcal || aniflag) { | |
731 | /* Call ANI routine if necessary */ | |
732 | if (aniflag) | |
733 | ath9k_hw_ani_monitor(ah, ah->curchan); | |
734 | ||
735 | /* Perform calibration if necessary */ | |
736 | if (longcal || shortcal) { | |
737 | common->ani.caldone = | |
738 | ath9k_hw_calibrate(ah, ah->curchan, | |
739 | common->rx_chainmask, | |
740 | longcal); | |
741 | ||
742 | if (longcal) | |
743 | common->ani.noise_floor = | |
744 | ath9k_hw_getchan_noise(ah, ah->curchan); | |
745 | ||
746 | ath_print(common, ATH_DBG_ANI, | |
747 | " calibrate chan %u/%x nf: %d\n", | |
748 | ah->curchan->channel, | |
749 | ah->curchan->channelFlags, | |
750 | common->ani.noise_floor); | |
751 | } | |
752 | } | |
753 | ||
754 | /* | |
755 | * Set timer interval based on previous results. | |
756 | * The interval must be the shortest necessary to satisfy ANI, | |
757 | * short calibration and long calibration. | |
758 | */ | |
759 | cal_interval = ATH_LONG_CALINTERVAL; | |
760 | if (priv->ah->config.enable_ani) | |
761 | cal_interval = min(cal_interval, (u32)ATH_ANI_POLLINTERVAL); | |
762 | if (!common->ani.caldone) | |
763 | cal_interval = min(cal_interval, (u32)short_cal_interval); | |
764 | ||
765 | ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, | |
766 | msecs_to_jiffies(cal_interval)); | |
767 | } | |
768 | ||
769 | /*******/ | |
770 | /* LED */ | |
771 | /*******/ | |
772 | ||
773 | static void ath9k_led_blink_work(struct work_struct *work) | |
774 | { | |
775 | struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv, | |
776 | ath9k_led_blink_work.work); | |
777 | ||
778 | if (!(priv->op_flags & OP_LED_ASSOCIATED)) | |
779 | return; | |
780 | ||
781 | if ((priv->led_on_duration == ATH_LED_ON_DURATION_IDLE) || | |
782 | (priv->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) | |
783 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); | |
784 | else | |
785 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, | |
786 | (priv->op_flags & OP_LED_ON) ? 1 : 0); | |
787 | ||
788 | ieee80211_queue_delayed_work(priv->hw, | |
789 | &priv->ath9k_led_blink_work, | |
790 | (priv->op_flags & OP_LED_ON) ? | |
791 | msecs_to_jiffies(priv->led_off_duration) : | |
792 | msecs_to_jiffies(priv->led_on_duration)); | |
793 | ||
794 | priv->led_on_duration = priv->led_on_cnt ? | |
795 | max((ATH_LED_ON_DURATION_IDLE - priv->led_on_cnt), 25) : | |
796 | ATH_LED_ON_DURATION_IDLE; | |
797 | priv->led_off_duration = priv->led_off_cnt ? | |
798 | max((ATH_LED_OFF_DURATION_IDLE - priv->led_off_cnt), 10) : | |
799 | ATH_LED_OFF_DURATION_IDLE; | |
800 | priv->led_on_cnt = priv->led_off_cnt = 0; | |
801 | ||
802 | if (priv->op_flags & OP_LED_ON) | |
803 | priv->op_flags &= ~OP_LED_ON; | |
804 | else | |
805 | priv->op_flags |= OP_LED_ON; | |
806 | } | |
807 | ||
808 | static void ath9k_led_brightness_work(struct work_struct *work) | |
809 | { | |
810 | struct ath_led *led = container_of(work, struct ath_led, | |
811 | brightness_work.work); | |
812 | struct ath9k_htc_priv *priv = led->priv; | |
813 | ||
814 | switch (led->brightness) { | |
815 | case LED_OFF: | |
816 | if (led->led_type == ATH_LED_ASSOC || | |
817 | led->led_type == ATH_LED_RADIO) { | |
818 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, | |
819 | (led->led_type == ATH_LED_RADIO)); | |
820 | priv->op_flags &= ~OP_LED_ASSOCIATED; | |
821 | if (led->led_type == ATH_LED_RADIO) | |
822 | priv->op_flags &= ~OP_LED_ON; | |
823 | } else { | |
824 | priv->led_off_cnt++; | |
825 | } | |
826 | break; | |
827 | case LED_FULL: | |
828 | if (led->led_type == ATH_LED_ASSOC) { | |
829 | priv->op_flags |= OP_LED_ASSOCIATED; | |
830 | ieee80211_queue_delayed_work(priv->hw, | |
831 | &priv->ath9k_led_blink_work, 0); | |
832 | } else if (led->led_type == ATH_LED_RADIO) { | |
833 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 0); | |
834 | priv->op_flags |= OP_LED_ON; | |
835 | } else { | |
836 | priv->led_on_cnt++; | |
837 | } | |
838 | break; | |
839 | default: | |
840 | break; | |
841 | } | |
842 | } | |
843 | ||
844 | static void ath9k_led_brightness(struct led_classdev *led_cdev, | |
845 | enum led_brightness brightness) | |
846 | { | |
847 | struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); | |
848 | struct ath9k_htc_priv *priv = led->priv; | |
849 | ||
850 | led->brightness = brightness; | |
851 | if (!(priv->op_flags & OP_LED_DEINIT)) | |
852 | ieee80211_queue_delayed_work(priv->hw, | |
853 | &led->brightness_work, 0); | |
854 | } | |
855 | ||
856 | static void ath9k_led_stop_brightness(struct ath9k_htc_priv *priv) | |
857 | { | |
858 | cancel_delayed_work_sync(&priv->radio_led.brightness_work); | |
859 | cancel_delayed_work_sync(&priv->assoc_led.brightness_work); | |
860 | cancel_delayed_work_sync(&priv->tx_led.brightness_work); | |
861 | cancel_delayed_work_sync(&priv->rx_led.brightness_work); | |
862 | } | |
863 | ||
864 | static int ath9k_register_led(struct ath9k_htc_priv *priv, struct ath_led *led, | |
865 | char *trigger) | |
866 | { | |
867 | int ret; | |
868 | ||
869 | led->priv = priv; | |
870 | led->led_cdev.name = led->name; | |
871 | led->led_cdev.default_trigger = trigger; | |
872 | led->led_cdev.brightness_set = ath9k_led_brightness; | |
873 | ||
874 | ret = led_classdev_register(wiphy_dev(priv->hw->wiphy), &led->led_cdev); | |
875 | if (ret) | |
876 | ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, | |
877 | "Failed to register led:%s", led->name); | |
878 | else | |
879 | led->registered = 1; | |
880 | ||
881 | INIT_DELAYED_WORK(&led->brightness_work, ath9k_led_brightness_work); | |
882 | ||
883 | return ret; | |
884 | } | |
885 | ||
886 | static void ath9k_unregister_led(struct ath_led *led) | |
887 | { | |
888 | if (led->registered) { | |
889 | led_classdev_unregister(&led->led_cdev); | |
890 | led->registered = 0; | |
891 | } | |
892 | } | |
893 | ||
894 | void ath9k_deinit_leds(struct ath9k_htc_priv *priv) | |
895 | { | |
896 | priv->op_flags |= OP_LED_DEINIT; | |
897 | ath9k_unregister_led(&priv->assoc_led); | |
898 | priv->op_flags &= ~OP_LED_ASSOCIATED; | |
899 | ath9k_unregister_led(&priv->tx_led); | |
900 | ath9k_unregister_led(&priv->rx_led); | |
901 | ath9k_unregister_led(&priv->radio_led); | |
902 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); | |
903 | } | |
904 | ||
905 | void ath9k_init_leds(struct ath9k_htc_priv *priv) | |
906 | { | |
907 | char *trigger; | |
908 | int ret; | |
909 | ||
910 | if (AR_SREV_9287(priv->ah)) | |
911 | priv->ah->led_pin = ATH_LED_PIN_9287; | |
912 | else if (AR_SREV_9271(priv->ah)) | |
913 | priv->ah->led_pin = ATH_LED_PIN_9271; | |
914 | else | |
915 | priv->ah->led_pin = ATH_LED_PIN_DEF; | |
916 | ||
917 | /* Configure gpio 1 for output */ | |
918 | ath9k_hw_cfg_output(priv->ah, priv->ah->led_pin, | |
919 | AR_GPIO_OUTPUT_MUX_AS_OUTPUT); | |
920 | /* LED off, active low */ | |
921 | ath9k_hw_set_gpio(priv->ah, priv->ah->led_pin, 1); | |
922 | ||
923 | INIT_DELAYED_WORK(&priv->ath9k_led_blink_work, ath9k_led_blink_work); | |
924 | ||
925 | trigger = ieee80211_get_radio_led_name(priv->hw); | |
926 | snprintf(priv->radio_led.name, sizeof(priv->radio_led.name), | |
927 | "ath9k-%s::radio", wiphy_name(priv->hw->wiphy)); | |
928 | ret = ath9k_register_led(priv, &priv->radio_led, trigger); | |
929 | priv->radio_led.led_type = ATH_LED_RADIO; | |
930 | if (ret) | |
931 | goto fail; | |
932 | ||
933 | trigger = ieee80211_get_assoc_led_name(priv->hw); | |
934 | snprintf(priv->assoc_led.name, sizeof(priv->assoc_led.name), | |
935 | "ath9k-%s::assoc", wiphy_name(priv->hw->wiphy)); | |
936 | ret = ath9k_register_led(priv, &priv->assoc_led, trigger); | |
937 | priv->assoc_led.led_type = ATH_LED_ASSOC; | |
938 | if (ret) | |
939 | goto fail; | |
940 | ||
941 | trigger = ieee80211_get_tx_led_name(priv->hw); | |
942 | snprintf(priv->tx_led.name, sizeof(priv->tx_led.name), | |
943 | "ath9k-%s::tx", wiphy_name(priv->hw->wiphy)); | |
944 | ret = ath9k_register_led(priv, &priv->tx_led, trigger); | |
945 | priv->tx_led.led_type = ATH_LED_TX; | |
946 | if (ret) | |
947 | goto fail; | |
948 | ||
949 | trigger = ieee80211_get_rx_led_name(priv->hw); | |
950 | snprintf(priv->rx_led.name, sizeof(priv->rx_led.name), | |
951 | "ath9k-%s::rx", wiphy_name(priv->hw->wiphy)); | |
952 | ret = ath9k_register_led(priv, &priv->rx_led, trigger); | |
953 | priv->rx_led.led_type = ATH_LED_RX; | |
954 | if (ret) | |
955 | goto fail; | |
956 | ||
957 | priv->op_flags &= ~OP_LED_DEINIT; | |
958 | ||
959 | return; | |
960 | ||
961 | fail: | |
962 | cancel_delayed_work_sync(&priv->ath9k_led_blink_work); | |
963 | ath9k_deinit_leds(priv); | |
964 | } | |
965 | ||
966 | /*******************/ | |
967 | /* Rfkill */ | |
968 | /*******************/ | |
969 | ||
970 | static bool ath_is_rfkill_set(struct ath9k_htc_priv *priv) | |
971 | { | |
972 | return ath9k_hw_gpio_get(priv->ah, priv->ah->rfkill_gpio) == | |
973 | priv->ah->rfkill_polarity; | |
974 | } | |
975 | ||
976 | static void ath9k_htc_rfkill_poll_state(struct ieee80211_hw *hw) | |
977 | { | |
978 | struct ath9k_htc_priv *priv = hw->priv; | |
979 | bool blocked = !!ath_is_rfkill_set(priv); | |
980 | ||
981 | wiphy_rfkill_set_hw_state(hw->wiphy, blocked); | |
982 | } | |
983 | ||
984 | void ath9k_start_rfkill_poll(struct ath9k_htc_priv *priv) | |
985 | { | |
986 | if (priv->ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) | |
987 | wiphy_rfkill_start_polling(priv->hw->wiphy); | |
988 | } | |
989 | ||
990 | /**********************/ | |
991 | /* mac80211 Callbacks */ | |
992 | /**********************/ | |
993 | ||
994 | static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) | |
995 | { | |
996 | struct ieee80211_hdr *hdr; | |
997 | struct ath9k_htc_priv *priv = hw->priv; | |
998 | int padpos, padsize; | |
999 | ||
1000 | hdr = (struct ieee80211_hdr *) skb->data; | |
1001 | ||
1002 | /* Add the padding after the header if this is not already done */ | |
1003 | padpos = ath9k_cmn_padpos(hdr->frame_control); | |
1004 | padsize = padpos & 3; | |
1005 | if (padsize && skb->len > padpos) { | |
1006 | if (skb_headroom(skb) < padsize) | |
1007 | return -1; | |
1008 | skb_push(skb, padsize); | |
1009 | memmove(skb->data, skb->data + padsize, padpos); | |
1010 | } | |
1011 | ||
1012 | if (ath9k_htc_tx_start(priv, skb) != 0) { | |
1013 | ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, "Tx failed"); | |
1014 | goto fail_tx; | |
1015 | } | |
1016 | ||
1017 | return 0; | |
1018 | ||
1019 | fail_tx: | |
1020 | dev_kfree_skb_any(skb); | |
1021 | return 0; | |
1022 | } | |
1023 | ||
1024 | static int ath9k_htc_start(struct ieee80211_hw *hw) | |
1025 | { | |
1026 | struct ath9k_htc_priv *priv = hw->priv; | |
1027 | struct ath_hw *ah = priv->ah; | |
1028 | struct ath_common *common = ath9k_hw_common(ah); | |
1029 | struct ieee80211_channel *curchan = hw->conf.channel; | |
1030 | struct ath9k_channel *init_channel; | |
1031 | int ret = 0; | |
1032 | enum htc_phymode mode; | |
1033 | u16 htc_mode; | |
1034 | u8 cmd_rsp; | |
1035 | ||
1036 | ath_print(common, ATH_DBG_CONFIG, | |
1037 | "Starting driver with initial channel: %d MHz\n", | |
1038 | curchan->center_freq); | |
1039 | ||
1040 | mutex_lock(&priv->mutex); | |
1041 | ||
1042 | /* setup initial channel */ | |
1043 | init_channel = ath9k_cmn_get_curchannel(hw, ah); | |
1044 | ||
1045 | /* Reset SERDES registers */ | |
1046 | ath9k_hw_configpcipowersave(ah, 0, 0); | |
1047 | ||
1048 | ath9k_hw_htc_resetinit(ah); | |
1049 | ret = ath9k_hw_reset(ah, init_channel, false); | |
1050 | if (ret) { | |
1051 | ath_print(common, ATH_DBG_FATAL, | |
1052 | "Unable to reset hardware; reset status %d " | |
1053 | "(freq %u MHz)\n", ret, curchan->center_freq); | |
1054 | goto mutex_unlock; | |
1055 | } | |
1056 | ||
1057 | ath_update_txpow(priv); | |
1058 | ||
1059 | mode = ath9k_htc_get_curmode(priv, init_channel); | |
1060 | htc_mode = cpu_to_be16(mode); | |
1061 | WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode); | |
1062 | if (ret) | |
1063 | goto mutex_unlock; | |
1064 | ||
1065 | WMI_CMD(WMI_ATH_INIT_CMDID); | |
1066 | if (ret) | |
1067 | goto mutex_unlock; | |
1068 | ||
1069 | WMI_CMD(WMI_START_RECV_CMDID); | |
1070 | if (ret) | |
1071 | goto mutex_unlock; | |
1072 | ||
1073 | ath9k_host_rx_init(priv); | |
1074 | ||
1075 | priv->op_flags &= ~OP_INVALID; | |
1076 | htc_start(priv->htc); | |
1077 | ||
1078 | mutex_unlock: | |
1079 | mutex_unlock(&priv->mutex); | |
1080 | return ret; | |
1081 | } | |
1082 | ||
1083 | static void ath9k_htc_stop(struct ieee80211_hw *hw) | |
1084 | { | |
1085 | struct ath9k_htc_priv *priv = hw->priv; | |
1086 | struct ath_hw *ah = priv->ah; | |
1087 | struct ath_common *common = ath9k_hw_common(ah); | |
1088 | int ret = 0; | |
1089 | u8 cmd_rsp; | |
1090 | ||
1091 | mutex_lock(&priv->mutex); | |
1092 | ||
1093 | if (priv->op_flags & OP_INVALID) { | |
1094 | ath_print(common, ATH_DBG_ANY, "Device not present\n"); | |
1095 | mutex_unlock(&priv->mutex); | |
1096 | return; | |
1097 | } | |
1098 | ||
1099 | htc_stop(priv->htc); | |
1100 | WMI_CMD(WMI_DISABLE_INTR_CMDID); | |
1101 | WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); | |
1102 | WMI_CMD(WMI_STOP_RECV_CMDID); | |
1103 | ath9k_hw_phy_disable(ah); | |
1104 | ath9k_hw_disable(ah); | |
1105 | ath9k_hw_configpcipowersave(ah, 1, 1); | |
1106 | ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); | |
1107 | ||
1108 | cancel_delayed_work_sync(&priv->ath9k_ani_work); | |
1109 | cancel_delayed_work_sync(&priv->ath9k_aggr_work); | |
1110 | cancel_delayed_work_sync(&priv->ath9k_led_blink_work); | |
1111 | ath9k_led_stop_brightness(priv); | |
1112 | skb_queue_purge(&priv->tx_queue); | |
1113 | ||
1114 | /* Remove monitor interface here */ | |
1115 | if (ah->opmode == NL80211_IFTYPE_MONITOR) { | |
1116 | if (ath9k_htc_remove_monitor_interface(priv)) | |
1117 | ath_print(common, ATH_DBG_FATAL, | |
1118 | "Unable to remove monitor interface\n"); | |
1119 | else | |
1120 | ath_print(common, ATH_DBG_CONFIG, | |
1121 | "Monitor interface removed\n"); | |
1122 | } | |
1123 | ||
1124 | priv->op_flags |= OP_INVALID; | |
1125 | mutex_unlock(&priv->mutex); | |
1126 | ||
1127 | ath_print(common, ATH_DBG_CONFIG, "Driver halt\n"); | |
1128 | } | |
1129 | ||
1130 | static int ath9k_htc_add_interface(struct ieee80211_hw *hw, | |
1131 | struct ieee80211_vif *vif) | |
1132 | { | |
1133 | struct ath9k_htc_priv *priv = hw->priv; | |
1134 | struct ath9k_htc_vif *avp = (void *)vif->drv_priv; | |
1135 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
1136 | struct ath9k_htc_target_vif hvif; | |
1137 | int ret = 0; | |
1138 | u8 cmd_rsp; | |
1139 | ||
1140 | mutex_lock(&priv->mutex); | |
1141 | ||
1142 | /* Only one interface for now */ | |
1143 | if (priv->nvifs > 0) { | |
1144 | ret = -ENOBUFS; | |
1145 | goto out; | |
1146 | } | |
1147 | ||
1148 | memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); | |
1149 | memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); | |
1150 | ||
1151 | switch (vif->type) { | |
1152 | case NL80211_IFTYPE_STATION: | |
1153 | hvif.opmode = cpu_to_be32(HTC_M_STA); | |
1154 | break; | |
1155 | case NL80211_IFTYPE_ADHOC: | |
1156 | hvif.opmode = cpu_to_be32(HTC_M_IBSS); | |
1157 | break; | |
1158 | default: | |
1159 | ath_print(common, ATH_DBG_FATAL, | |
1160 | "Interface type %d not yet supported\n", vif->type); | |
1161 | ret = -EOPNOTSUPP; | |
1162 | goto out; | |
1163 | } | |
1164 | ||
1165 | ath_print(common, ATH_DBG_CONFIG, | |
1166 | "Attach a VIF of type: %d\n", vif->type); | |
1167 | ||
1168 | priv->ah->opmode = vif->type; | |
1169 | ||
1170 | /* Index starts from zero on the target */ | |
1171 | avp->index = hvif.index = priv->nvifs; | |
1172 | hvif.rtsthreshold = cpu_to_be16(2304); | |
1173 | WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); | |
1174 | if (ret) | |
1175 | goto out; | |
1176 | ||
1177 | priv->nvifs++; | |
1178 | ||
1179 | /* | |
1180 | * We need a node in target to tx mgmt frames | |
1181 | * before association. | |
1182 | */ | |
1183 | ret = ath9k_htc_add_station(priv, vif, NULL); | |
1184 | if (ret) | |
1185 | goto out; | |
1186 | ||
1187 | ret = ath9k_htc_update_cap_target(priv); | |
1188 | if (ret) | |
1189 | ath_print(common, ATH_DBG_CONFIG, "Failed to update" | |
1190 | " capability in target \n"); | |
1191 | ||
1192 | priv->vif = vif; | |
1193 | out: | |
1194 | mutex_unlock(&priv->mutex); | |
1195 | return ret; | |
1196 | } | |
1197 | ||
1198 | static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, | |
1199 | struct ieee80211_vif *vif) | |
1200 | { | |
1201 | struct ath9k_htc_priv *priv = hw->priv; | |
1202 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
1203 | struct ath9k_htc_vif *avp = (void *)vif->drv_priv; | |
1204 | struct ath9k_htc_target_vif hvif; | |
1205 | int ret = 0; | |
1206 | u8 cmd_rsp; | |
1207 | ||
1208 | ath_print(common, ATH_DBG_CONFIG, "Detach Interface\n"); | |
1209 | ||
1210 | mutex_lock(&priv->mutex); | |
1211 | ||
1212 | memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); | |
1213 | memcpy(&hvif.myaddr, vif->addr, ETH_ALEN); | |
1214 | hvif.index = avp->index; | |
1215 | WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); | |
1216 | priv->nvifs--; | |
1217 | ||
1218 | ath9k_htc_remove_station(priv, vif, NULL); | |
1219 | ||
1220 | if (vif->type == NL80211_IFTYPE_ADHOC) { | |
1221 | spin_lock_bh(&priv->beacon_lock); | |
1222 | if (priv->beacon) | |
1223 | dev_kfree_skb_any(priv->beacon); | |
1224 | priv->beacon = NULL; | |
1225 | spin_unlock_bh(&priv->beacon_lock); | |
1226 | } | |
1227 | ||
1228 | priv->vif = NULL; | |
1229 | ||
1230 | mutex_unlock(&priv->mutex); | |
1231 | } | |
1232 | ||
1233 | static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) | |
1234 | { | |
1235 | struct ath9k_htc_priv *priv = hw->priv; | |
1236 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
1237 | struct ieee80211_conf *conf = &hw->conf; | |
1238 | ||
1239 | mutex_lock(&priv->mutex); | |
1240 | ||
1241 | if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { | |
1242 | struct ieee80211_channel *curchan = hw->conf.channel; | |
1243 | int pos = curchan->hw_value; | |
1244 | bool is_cw40 = false; | |
1245 | ||
1246 | ath_print(common, ATH_DBG_CONFIG, "Set channel: %d MHz\n", | |
1247 | curchan->center_freq); | |
1248 | ||
1249 | if (check_rc_update(hw, &is_cw40)) | |
1250 | ath9k_htc_rc_update(priv, is_cw40); | |
1251 | ||
1252 | ath9k_cmn_update_ichannel(hw, &priv->ah->channels[pos]); | |
1253 | ||
1254 | if (ath9k_htc_set_channel(priv, hw, &priv->ah->channels[pos]) < 0) { | |
1255 | ath_print(common, ATH_DBG_FATAL, | |
1256 | "Unable to set channel\n"); | |
1257 | mutex_unlock(&priv->mutex); | |
1258 | return -EINVAL; | |
1259 | } | |
1260 | ||
1261 | } | |
1262 | ||
1263 | if (changed & IEEE80211_CONF_CHANGE_MONITOR) { | |
1264 | if (conf->flags & IEEE80211_CONF_MONITOR) { | |
1265 | if (ath9k_htc_add_monitor_interface(priv)) | |
1266 | ath_print(common, ATH_DBG_FATAL, | |
1267 | "Failed to set monitor mode\n"); | |
1268 | else | |
1269 | ath_print(common, ATH_DBG_CONFIG, | |
1270 | "HW opmode set to Monitor mode\n"); | |
1271 | } | |
1272 | } | |
1273 | ||
1274 | mutex_unlock(&priv->mutex); | |
1275 | ||
1276 | return 0; | |
1277 | } | |
1278 | ||
1279 | #define SUPPORTED_FILTERS \ | |
1280 | (FIF_PROMISC_IN_BSS | \ | |
1281 | FIF_ALLMULTI | \ | |
1282 | FIF_CONTROL | \ | |
1283 | FIF_PSPOLL | \ | |
1284 | FIF_OTHER_BSS | \ | |
1285 | FIF_BCN_PRBRESP_PROMISC | \ | |
1286 | FIF_FCSFAIL) | |
1287 | ||
1288 | static void ath9k_htc_configure_filter(struct ieee80211_hw *hw, | |
1289 | unsigned int changed_flags, | |
1290 | unsigned int *total_flags, | |
1291 | u64 multicast) | |
1292 | { | |
1293 | struct ath9k_htc_priv *priv = hw->priv; | |
1294 | u32 rfilt; | |
1295 | ||
1296 | mutex_lock(&priv->mutex); | |
1297 | ||
1298 | changed_flags &= SUPPORTED_FILTERS; | |
1299 | *total_flags &= SUPPORTED_FILTERS; | |
1300 | ||
1301 | priv->rxfilter = *total_flags; | |
0995d110 | 1302 | rfilt = ath9k_htc_calcrxfilter(priv); |
fb9987d0 S |
1303 | ath9k_hw_setrxfilter(priv->ah, rfilt); |
1304 | ||
1305 | ath_print(ath9k_hw_common(priv->ah), ATH_DBG_CONFIG, | |
1306 | "Set HW RX filter: 0x%x\n", rfilt); | |
1307 | ||
1308 | mutex_unlock(&priv->mutex); | |
1309 | } | |
1310 | ||
1311 | static void ath9k_htc_sta_notify(struct ieee80211_hw *hw, | |
1312 | struct ieee80211_vif *vif, | |
1313 | enum sta_notify_cmd cmd, | |
1314 | struct ieee80211_sta *sta) | |
1315 | { | |
1316 | struct ath9k_htc_priv *priv = hw->priv; | |
1317 | int ret; | |
1318 | ||
1319 | switch (cmd) { | |
1320 | case STA_NOTIFY_ADD: | |
1321 | ret = ath9k_htc_add_station(priv, vif, sta); | |
1322 | if (!ret) | |
1323 | ath9k_htc_init_rate(priv, vif, sta); | |
1324 | break; | |
1325 | case STA_NOTIFY_REMOVE: | |
1326 | ath9k_htc_remove_station(priv, vif, sta); | |
1327 | break; | |
1328 | default: | |
1329 | break; | |
1330 | } | |
1331 | } | |
1332 | ||
1333 | static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, u16 queue, | |
1334 | const struct ieee80211_tx_queue_params *params) | |
1335 | { | |
1336 | struct ath9k_htc_priv *priv = hw->priv; | |
1337 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
1338 | struct ath9k_tx_queue_info qi; | |
1339 | int ret = 0, qnum; | |
1340 | ||
1341 | if (queue >= WME_NUM_AC) | |
1342 | return 0; | |
1343 | ||
1344 | mutex_lock(&priv->mutex); | |
1345 | ||
1346 | memset(&qi, 0, sizeof(struct ath9k_tx_queue_info)); | |
1347 | ||
1348 | qi.tqi_aifs = params->aifs; | |
1349 | qi.tqi_cwmin = params->cw_min; | |
1350 | qi.tqi_cwmax = params->cw_max; | |
1351 | qi.tqi_burstTime = params->txop; | |
1352 | ||
1353 | qnum = get_hw_qnum(queue, priv->hwq_map); | |
1354 | ||
1355 | ath_print(common, ATH_DBG_CONFIG, | |
1356 | "Configure tx [queue/hwq] [%d/%d], " | |
1357 | "aifs: %d, cw_min: %d, cw_max: %d, txop: %d\n", | |
1358 | queue, qnum, params->aifs, params->cw_min, | |
1359 | params->cw_max, params->txop); | |
1360 | ||
e1572c5e | 1361 | ret = ath_htc_txq_update(priv, qnum, &qi); |
fb9987d0 S |
1362 | if (ret) |
1363 | ath_print(common, ATH_DBG_FATAL, "TXQ Update failed\n"); | |
1364 | ||
1365 | mutex_unlock(&priv->mutex); | |
1366 | ||
1367 | return ret; | |
1368 | } | |
1369 | ||
1370 | static int ath9k_htc_set_key(struct ieee80211_hw *hw, | |
1371 | enum set_key_cmd cmd, | |
1372 | struct ieee80211_vif *vif, | |
1373 | struct ieee80211_sta *sta, | |
1374 | struct ieee80211_key_conf *key) | |
1375 | { | |
1376 | struct ath9k_htc_priv *priv = hw->priv; | |
1377 | struct ath_common *common = ath9k_hw_common(priv->ah); | |
1378 | int ret = 0; | |
1379 | ||
e1572c5e | 1380 | if (htc_modparam_nohwcrypt) |
fb9987d0 S |
1381 | return -ENOSPC; |
1382 | ||
1383 | mutex_lock(&priv->mutex); | |
1384 | ath_print(common, ATH_DBG_CONFIG, "Set HW Key\n"); | |
1385 | ||
1386 | switch (cmd) { | |
1387 | case SET_KEY: | |
1388 | ret = ath9k_cmn_key_config(common, vif, sta, key); | |
1389 | if (ret >= 0) { | |
1390 | key->hw_key_idx = ret; | |
1391 | /* push IV and Michael MIC generation to stack */ | |
1392 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; | |
1393 | if (key->alg == ALG_TKIP) | |
1394 | key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; | |
1395 | if (priv->ah->sw_mgmt_crypto && key->alg == ALG_CCMP) | |
1396 | key->flags |= IEEE80211_KEY_FLAG_SW_MGMT; | |
1397 | ret = 0; | |
1398 | } | |
1399 | break; | |
1400 | case DISABLE_KEY: | |
1401 | ath9k_cmn_key_delete(common, key); | |
1402 | break; | |
1403 | default: | |
1404 | ret = -EINVAL; | |
1405 | } | |
1406 | ||
1407 | mutex_unlock(&priv->mutex); | |
1408 | ||
1409 | return ret; | |
1410 | } | |
1411 | ||
1412 | static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, | |
1413 | struct ieee80211_vif *vif, | |
1414 | struct ieee80211_bss_conf *bss_conf, | |
1415 | u32 changed) | |
1416 | { | |
1417 | struct ath9k_htc_priv *priv = hw->priv; | |
1418 | struct ath_hw *ah = priv->ah; | |
1419 | struct ath_common *common = ath9k_hw_common(ah); | |
1420 | ||
1421 | mutex_lock(&priv->mutex); | |
1422 | ||
1423 | if (changed & BSS_CHANGED_ASSOC) { | |
1424 | common->curaid = bss_conf->assoc ? | |
1425 | bss_conf->aid : 0; | |
1426 | ath_print(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", | |
1427 | bss_conf->assoc); | |
1428 | ||
1429 | if (bss_conf->assoc) { | |
1430 | priv->op_flags |= OP_ASSOCIATED; | |
1431 | ath_start_ani(priv); | |
1432 | } else { | |
1433 | priv->op_flags &= ~OP_ASSOCIATED; | |
1434 | cancel_delayed_work_sync(&priv->ath9k_ani_work); | |
1435 | } | |
1436 | } | |
1437 | ||
1438 | if (changed & BSS_CHANGED_BSSID) { | |
1439 | /* Set BSSID */ | |
1440 | memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); | |
1441 | ath9k_hw_write_associd(ah); | |
1442 | ||
1443 | ath_print(common, ATH_DBG_CONFIG, | |
1444 | "BSSID: %pM aid: 0x%x\n", | |
1445 | common->curbssid, common->curaid); | |
1446 | } | |
1447 | ||
1448 | if ((changed & BSS_CHANGED_BEACON_INT) || | |
1449 | (changed & BSS_CHANGED_BEACON) || | |
1450 | ((changed & BSS_CHANGED_BEACON_ENABLED) && | |
1451 | bss_conf->enable_beacon)) { | |
1452 | priv->op_flags |= OP_ENABLE_BEACON; | |
1453 | ath9k_htc_beacon_config(priv, vif, bss_conf); | |
1454 | } | |
1455 | ||
1456 | if (changed & BSS_CHANGED_BEACON) | |
1457 | ath9k_htc_beacon_update(priv, vif); | |
1458 | ||
1459 | if ((changed & BSS_CHANGED_BEACON_ENABLED) && | |
1460 | !bss_conf->enable_beacon) { | |
1461 | priv->op_flags &= ~OP_ENABLE_BEACON; | |
1462 | ath9k_htc_beacon_config(priv, vif, bss_conf); | |
1463 | } | |
1464 | ||
1465 | if (changed & BSS_CHANGED_ERP_PREAMBLE) { | |
1466 | ath_print(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", | |
1467 | bss_conf->use_short_preamble); | |
1468 | if (bss_conf->use_short_preamble) | |
1469 | priv->op_flags |= OP_PREAMBLE_SHORT; | |
1470 | else | |
1471 | priv->op_flags &= ~OP_PREAMBLE_SHORT; | |
1472 | } | |
1473 | ||
1474 | if (changed & BSS_CHANGED_ERP_CTS_PROT) { | |
1475 | ath_print(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", | |
1476 | bss_conf->use_cts_prot); | |
1477 | if (bss_conf->use_cts_prot && | |
1478 | hw->conf.channel->band != IEEE80211_BAND_5GHZ) | |
1479 | priv->op_flags |= OP_PROTECT_ENABLE; | |
1480 | else | |
1481 | priv->op_flags &= ~OP_PROTECT_ENABLE; | |
1482 | } | |
1483 | ||
1484 | if (changed & BSS_CHANGED_ERP_SLOT) { | |
1485 | if (bss_conf->use_short_slot) | |
1486 | ah->slottime = 9; | |
1487 | else | |
1488 | ah->slottime = 20; | |
1489 | ||
1490 | ath9k_hw_init_global_settings(ah); | |
1491 | } | |
1492 | ||
1493 | mutex_unlock(&priv->mutex); | |
1494 | } | |
1495 | ||
1496 | static u64 ath9k_htc_get_tsf(struct ieee80211_hw *hw) | |
1497 | { | |
1498 | struct ath9k_htc_priv *priv = hw->priv; | |
1499 | u64 tsf; | |
1500 | ||
1501 | mutex_lock(&priv->mutex); | |
1502 | tsf = ath9k_hw_gettsf64(priv->ah); | |
1503 | mutex_unlock(&priv->mutex); | |
1504 | ||
1505 | return tsf; | |
1506 | } | |
1507 | ||
1508 | static void ath9k_htc_set_tsf(struct ieee80211_hw *hw, u64 tsf) | |
1509 | { | |
1510 | struct ath9k_htc_priv *priv = hw->priv; | |
1511 | ||
1512 | mutex_lock(&priv->mutex); | |
1513 | ath9k_hw_settsf64(priv->ah, tsf); | |
1514 | mutex_unlock(&priv->mutex); | |
1515 | } | |
1516 | ||
1517 | static void ath9k_htc_reset_tsf(struct ieee80211_hw *hw) | |
1518 | { | |
1519 | struct ath9k_htc_priv *priv = hw->priv; | |
1520 | ||
1521 | mutex_lock(&priv->mutex); | |
1522 | ath9k_hw_reset_tsf(priv->ah); | |
1523 | mutex_unlock(&priv->mutex); | |
1524 | } | |
1525 | ||
1526 | static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, | |
1527 | struct ieee80211_vif *vif, | |
1528 | enum ieee80211_ampdu_mlme_action action, | |
1529 | struct ieee80211_sta *sta, | |
1530 | u16 tid, u16 *ssn) | |
1531 | { | |
1532 | struct ath9k_htc_priv *priv = hw->priv; | |
1533 | struct ath9k_htc_aggr_work *work = &priv->aggr_work; | |
1534 | struct ath9k_htc_sta *ista; | |
1535 | ||
1536 | switch (action) { | |
1537 | case IEEE80211_AMPDU_RX_START: | |
1538 | break; | |
1539 | case IEEE80211_AMPDU_RX_STOP: | |
1540 | break; | |
1541 | case IEEE80211_AMPDU_TX_START: | |
1542 | case IEEE80211_AMPDU_TX_STOP: | |
1543 | if (!(priv->op_flags & OP_TXAGGR)) | |
1544 | return -ENOTSUPP; | |
1545 | memcpy(work->sta_addr, sta->addr, ETH_ALEN); | |
1546 | work->hw = hw; | |
1547 | work->vif = vif; | |
1548 | work->action = action; | |
1549 | work->tid = tid; | |
1550 | ieee80211_queue_delayed_work(hw, &priv->ath9k_aggr_work, 0); | |
1551 | break; | |
1552 | case IEEE80211_AMPDU_TX_OPERATIONAL: | |
1553 | ista = (struct ath9k_htc_sta *) sta->drv_priv; | |
1554 | ista->tid_state[tid] = AGGR_OPERATIONAL; | |
1555 | break; | |
1556 | default: | |
1557 | ath_print(ath9k_hw_common(priv->ah), ATH_DBG_FATAL, | |
1558 | "Unknown AMPDU action\n"); | |
1559 | } | |
1560 | ||
1561 | return 0; | |
1562 | } | |
1563 | ||
1564 | static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) | |
1565 | { | |
1566 | struct ath9k_htc_priv *priv = hw->priv; | |
1567 | ||
1568 | mutex_lock(&priv->mutex); | |
1569 | spin_lock_bh(&priv->beacon_lock); | |
1570 | priv->op_flags |= OP_SCANNING; | |
1571 | spin_unlock_bh(&priv->beacon_lock); | |
1572 | cancel_delayed_work_sync(&priv->ath9k_ani_work); | |
1573 | mutex_unlock(&priv->mutex); | |
1574 | } | |
1575 | ||
1576 | static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) | |
1577 | { | |
1578 | struct ath9k_htc_priv *priv = hw->priv; | |
1579 | ||
1580 | mutex_lock(&priv->mutex); | |
1581 | spin_lock_bh(&priv->beacon_lock); | |
1582 | priv->op_flags &= ~OP_SCANNING; | |
1583 | spin_unlock_bh(&priv->beacon_lock); | |
1584 | priv->op_flags |= OP_FULL_RESET; | |
1585 | ath_start_ani(priv); | |
1586 | mutex_unlock(&priv->mutex); | |
1587 | } | |
1588 | ||
1589 | static int ath9k_htc_set_rts_threshold(struct ieee80211_hw *hw, u32 value) | |
1590 | { | |
1591 | return 0; | |
1592 | } | |
1593 | ||
1594 | static void ath9k_htc_set_coverage_class(struct ieee80211_hw *hw, | |
1595 | u8 coverage_class) | |
1596 | { | |
1597 | struct ath9k_htc_priv *priv = hw->priv; | |
1598 | ||
1599 | mutex_lock(&priv->mutex); | |
1600 | priv->ah->coverage_class = coverage_class; | |
1601 | ath9k_hw_init_global_settings(priv->ah); | |
1602 | mutex_unlock(&priv->mutex); | |
1603 | } | |
1604 | ||
1605 | struct ieee80211_ops ath9k_htc_ops = { | |
1606 | .tx = ath9k_htc_tx, | |
1607 | .start = ath9k_htc_start, | |
1608 | .stop = ath9k_htc_stop, | |
1609 | .add_interface = ath9k_htc_add_interface, | |
1610 | .remove_interface = ath9k_htc_remove_interface, | |
1611 | .config = ath9k_htc_config, | |
1612 | .configure_filter = ath9k_htc_configure_filter, | |
1613 | .sta_notify = ath9k_htc_sta_notify, | |
1614 | .conf_tx = ath9k_htc_conf_tx, | |
1615 | .bss_info_changed = ath9k_htc_bss_info_changed, | |
1616 | .set_key = ath9k_htc_set_key, | |
1617 | .get_tsf = ath9k_htc_get_tsf, | |
1618 | .set_tsf = ath9k_htc_set_tsf, | |
1619 | .reset_tsf = ath9k_htc_reset_tsf, | |
1620 | .ampdu_action = ath9k_htc_ampdu_action, | |
1621 | .sw_scan_start = ath9k_htc_sw_scan_start, | |
1622 | .sw_scan_complete = ath9k_htc_sw_scan_complete, | |
1623 | .set_rts_threshold = ath9k_htc_set_rts_threshold, | |
1624 | .rfkill_poll = ath9k_htc_rfkill_poll_state, | |
1625 | .set_coverage_class = ath9k_htc_set_coverage_class, | |
1626 | }; |