diff options
author | Luis R. Rodriguez <lrodriguez@atheros.com> | 2009-03-30 22:30:33 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-04-22 16:54:38 -0400 |
commit | 203c4805e91786f9a010bc7945a0fde70c9da28e (patch) | |
tree | 00415276b2fe65713f066ffe07b11ad2d8b6bea8 /drivers/net/wireless/ath/ath5k/pcu.c | |
parent | 1878f77e13b9d720b78c4f818b94bfd4a7f596e5 (diff) |
atheros: put atheros wireless drivers into ath/
Signed-off-by: Luis R. Rodriguez <lrodriguez@atheros.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/ath/ath5k/pcu.c')
-rw-r--r-- | drivers/net/wireless/ath/ath5k/pcu.c | 1174 |
1 files changed, 1174 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c new file mode 100644 index 000000000000..55122f1e1986 --- /dev/null +++ b/drivers/net/wireless/ath/ath5k/pcu.c | |||
@@ -0,0 +1,1174 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004-2008 Reyk Floeter <reyk@openbsd.org> | ||
3 | * Copyright (c) 2006-2008 Nick Kossifidis <mickflemm@gmail.com> | ||
4 | * Copyright (c) 2007-2008 Matthew W. S. Bell <mentor@madwifi.org> | ||
5 | * Copyright (c) 2007-2008 Luis Rodriguez <mcgrof@winlab.rutgers.edu> | ||
6 | * Copyright (c) 2007-2008 Pavel Roskin <proski@gnu.org> | ||
7 | * Copyright (c) 2007-2008 Jiri Slaby <jirislaby@gmail.com> | ||
8 | * | ||
9 | * Permission to use, copy, modify, and distribute this software for any | ||
10 | * purpose with or without fee is hereby granted, provided that the above | ||
11 | * copyright notice and this permission notice appear in all copies. | ||
12 | * | ||
13 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
14 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
15 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
16 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
17 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
18 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
19 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
20 | * | ||
21 | */ | ||
22 | |||
23 | /*********************************\ | ||
24 | * Protocol Control Unit Functions * | ||
25 | \*********************************/ | ||
26 | |||
27 | #include "ath5k.h" | ||
28 | #include "reg.h" | ||
29 | #include "debug.h" | ||
30 | #include "base.h" | ||
31 | |||
32 | /*******************\ | ||
33 | * Generic functions * | ||
34 | \*******************/ | ||
35 | |||
36 | /** | ||
37 | * ath5k_hw_set_opmode - Set PCU operating mode | ||
38 | * | ||
39 | * @ah: The &struct ath5k_hw | ||
40 | * | ||
41 | * Initialize PCU for the various operating modes (AP/STA etc) | ||
42 | * | ||
43 | * NOTE: ah->ah_op_mode must be set before calling this. | ||
44 | */ | ||
45 | int ath5k_hw_set_opmode(struct ath5k_hw *ah) | ||
46 | { | ||
47 | u32 pcu_reg, beacon_reg, low_id, high_id; | ||
48 | |||
49 | |||
50 | /* Preserve rest settings */ | ||
51 | pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; | ||
52 | pcu_reg &= ~(AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_AP | ||
53 | | AR5K_STA_ID1_KEYSRCH_MODE | ||
54 | | (ah->ah_version == AR5K_AR5210 ? | ||
55 | (AR5K_STA_ID1_PWR_SV | AR5K_STA_ID1_NO_PSPOLL) : 0)); | ||
56 | |||
57 | beacon_reg = 0; | ||
58 | |||
59 | ATH5K_TRACE(ah->ah_sc); | ||
60 | |||
61 | switch (ah->ah_op_mode) { | ||
62 | case NL80211_IFTYPE_ADHOC: | ||
63 | pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_KEYSRCH_MODE; | ||
64 | beacon_reg |= AR5K_BCR_ADHOC; | ||
65 | if (ah->ah_version == AR5K_AR5210) | ||
66 | pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; | ||
67 | else | ||
68 | AR5K_REG_ENABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); | ||
69 | break; | ||
70 | |||
71 | case NL80211_IFTYPE_AP: | ||
72 | case NL80211_IFTYPE_MESH_POINT: | ||
73 | pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_KEYSRCH_MODE; | ||
74 | beacon_reg |= AR5K_BCR_AP; | ||
75 | if (ah->ah_version == AR5K_AR5210) | ||
76 | pcu_reg |= AR5K_STA_ID1_NO_PSPOLL; | ||
77 | else | ||
78 | AR5K_REG_DISABLE_BITS(ah, AR5K_CFG, AR5K_CFG_IBSS); | ||
79 | break; | ||
80 | |||
81 | case NL80211_IFTYPE_STATION: | ||
82 | pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | ||
83 | | (ah->ah_version == AR5K_AR5210 ? | ||
84 | AR5K_STA_ID1_PWR_SV : 0); | ||
85 | case NL80211_IFTYPE_MONITOR: | ||
86 | pcu_reg |= AR5K_STA_ID1_KEYSRCH_MODE | ||
87 | | (ah->ah_version == AR5K_AR5210 ? | ||
88 | AR5K_STA_ID1_NO_PSPOLL : 0); | ||
89 | break; | ||
90 | |||
91 | default: | ||
92 | return -EINVAL; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Set PCU registers | ||
97 | */ | ||
98 | low_id = AR5K_LOW_ID(ah->ah_sta_id); | ||
99 | high_id = AR5K_HIGH_ID(ah->ah_sta_id); | ||
100 | ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); | ||
101 | ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); | ||
102 | |||
103 | /* | ||
104 | * Set Beacon Control Register on 5210 | ||
105 | */ | ||
106 | if (ah->ah_version == AR5K_AR5210) | ||
107 | ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | /** | ||
113 | * ath5k_hw_update - Update mib counters (mac layer statistics) | ||
114 | * | ||
115 | * @ah: The &struct ath5k_hw | ||
116 | * @stats: The &struct ieee80211_low_level_stats we use to track | ||
117 | * statistics on the driver | ||
118 | * | ||
119 | * Reads MIB counters from PCU and updates sw statistics. Must be | ||
120 | * called after a MIB interrupt. | ||
121 | */ | ||
122 | void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, | ||
123 | struct ieee80211_low_level_stats *stats) | ||
124 | { | ||
125 | ATH5K_TRACE(ah->ah_sc); | ||
126 | |||
127 | /* Read-And-Clear */ | ||
128 | stats->dot11ACKFailureCount += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL); | ||
129 | stats->dot11RTSFailureCount += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL); | ||
130 | stats->dot11RTSSuccessCount += ath5k_hw_reg_read(ah, AR5K_RTS_OK); | ||
131 | stats->dot11FCSErrorCount += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL); | ||
132 | |||
133 | /* XXX: Should we use this to track beacon count ? | ||
134 | * -we read it anyway to clear the register */ | ||
135 | ath5k_hw_reg_read(ah, AR5K_BEACON_CNT); | ||
136 | |||
137 | /* Reset profile count registers on 5212*/ | ||
138 | if (ah->ah_version == AR5K_AR5212) { | ||
139 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX); | ||
140 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX); | ||
141 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); | ||
142 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); | ||
143 | } | ||
144 | |||
145 | /* TODO: Handle ANI stats */ | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * ath5k_hw_set_ack_bitrate - set bitrate for ACKs | ||
150 | * | ||
151 | * @ah: The &struct ath5k_hw | ||
152 | * @high: Flag to determine if we want to use high transmition rate | ||
153 | * for ACKs or not | ||
154 | * | ||
155 | * If high flag is set, we tell hw to use a set of control rates based on | ||
156 | * the current transmition rate (check out control_rates array inside reset.c). | ||
157 | * If not hw just uses the lowest rate available for the current modulation | ||
158 | * scheme being used (1Mbit for CCK and 6Mbits for OFDM). | ||
159 | */ | ||
160 | void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high) | ||
161 | { | ||
162 | if (ah->ah_version != AR5K_AR5212) | ||
163 | return; | ||
164 | else { | ||
165 | u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB; | ||
166 | if (high) | ||
167 | AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val); | ||
168 | else | ||
169 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | |||
174 | /******************\ | ||
175 | * ACK/CTS Timeouts * | ||
176 | \******************/ | ||
177 | |||
178 | /** | ||
179 | * ath5k_hw_het_ack_timeout - Get ACK timeout from PCU in usec | ||
180 | * | ||
181 | * @ah: The &struct ath5k_hw | ||
182 | */ | ||
183 | unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) | ||
184 | { | ||
185 | ATH5K_TRACE(ah->ah_sc); | ||
186 | |||
187 | return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, | ||
188 | AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo); | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * ath5k_hw_set_ack_timeout - Set ACK timeout on PCU | ||
193 | * | ||
194 | * @ah: The &struct ath5k_hw | ||
195 | * @timeout: Timeout in usec | ||
196 | */ | ||
197 | int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) | ||
198 | { | ||
199 | ATH5K_TRACE(ah->ah_sc); | ||
200 | if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), | ||
201 | ah->ah_turbo) <= timeout) | ||
202 | return -EINVAL; | ||
203 | |||
204 | AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, | ||
205 | ath5k_hw_htoclock(timeout, ah->ah_turbo)); | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /** | ||
211 | * ath5k_hw_get_cts_timeout - Get CTS timeout from PCU in usec | ||
212 | * | ||
213 | * @ah: The &struct ath5k_hw | ||
214 | */ | ||
215 | unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) | ||
216 | { | ||
217 | ATH5K_TRACE(ah->ah_sc); | ||
218 | return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, | ||
219 | AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo); | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * ath5k_hw_set_cts_timeout - Set CTS timeout on PCU | ||
224 | * | ||
225 | * @ah: The &struct ath5k_hw | ||
226 | * @timeout: Timeout in usec | ||
227 | */ | ||
228 | int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) | ||
229 | { | ||
230 | ATH5K_TRACE(ah->ah_sc); | ||
231 | if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), | ||
232 | ah->ah_turbo) <= timeout) | ||
233 | return -EINVAL; | ||
234 | |||
235 | AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, | ||
236 | ath5k_hw_htoclock(timeout, ah->ah_turbo)); | ||
237 | |||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | |||
242 | /****************\ | ||
243 | * BSSID handling * | ||
244 | \****************/ | ||
245 | |||
246 | /** | ||
247 | * ath5k_hw_get_lladdr - Get station id | ||
248 | * | ||
249 | * @ah: The &struct ath5k_hw | ||
250 | * @mac: The card's mac address | ||
251 | * | ||
252 | * Initialize ah->ah_sta_id using the mac address provided | ||
253 | * (just a memcpy). | ||
254 | * | ||
255 | * TODO: Remove it once we merge ath5k_softc and ath5k_hw | ||
256 | */ | ||
257 | void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) | ||
258 | { | ||
259 | ATH5K_TRACE(ah->ah_sc); | ||
260 | memcpy(mac, ah->ah_sta_id, ETH_ALEN); | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * ath5k_hw_set_lladdr - Set station id | ||
265 | * | ||
266 | * @ah: The &struct ath5k_hw | ||
267 | * @mac: The card's mac address | ||
268 | * | ||
269 | * Set station id on hw using the provided mac address | ||
270 | */ | ||
271 | int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) | ||
272 | { | ||
273 | u32 low_id, high_id; | ||
274 | u32 pcu_reg; | ||
275 | |||
276 | ATH5K_TRACE(ah->ah_sc); | ||
277 | /* Set new station ID */ | ||
278 | memcpy(ah->ah_sta_id, mac, ETH_ALEN); | ||
279 | |||
280 | pcu_reg = ath5k_hw_reg_read(ah, AR5K_STA_ID1) & 0xffff0000; | ||
281 | |||
282 | low_id = AR5K_LOW_ID(mac); | ||
283 | high_id = AR5K_HIGH_ID(mac); | ||
284 | |||
285 | ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); | ||
286 | ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); | ||
287 | |||
288 | return 0; | ||
289 | } | ||
290 | |||
291 | /** | ||
292 | * ath5k_hw_set_associd - Set BSSID for association | ||
293 | * | ||
294 | * @ah: The &struct ath5k_hw | ||
295 | * @bssid: BSSID | ||
296 | * @assoc_id: Assoc id | ||
297 | * | ||
298 | * Sets the BSSID which trigers the "SME Join" operation | ||
299 | */ | ||
300 | void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) | ||
301 | { | ||
302 | u32 low_id, high_id; | ||
303 | u16 tim_offset = 0; | ||
304 | |||
305 | /* | ||
306 | * Set simple BSSID mask on 5212 | ||
307 | */ | ||
308 | if (ah->ah_version == AR5K_AR5212) { | ||
309 | ath5k_hw_reg_write(ah, AR5K_LOW_ID(ah->ah_bssid_mask), | ||
310 | AR5K_BSS_IDM0); | ||
311 | ath5k_hw_reg_write(ah, AR5K_HIGH_ID(ah->ah_bssid_mask), | ||
312 | AR5K_BSS_IDM1); | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Set BSSID which triggers the "SME Join" operation | ||
317 | */ | ||
318 | low_id = AR5K_LOW_ID(bssid); | ||
319 | high_id = AR5K_HIGH_ID(bssid); | ||
320 | ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); | ||
321 | ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << | ||
322 | AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); | ||
323 | |||
324 | if (assoc_id == 0) { | ||
325 | ath5k_hw_disable_pspoll(ah); | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, | ||
330 | tim_offset ? tim_offset + 4 : 0); | ||
331 | |||
332 | ath5k_hw_enable_pspoll(ah, NULL, 0); | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * ath5k_hw_set_bssid_mask - filter out bssids we listen | ||
337 | * | ||
338 | * @ah: the &struct ath5k_hw | ||
339 | * @mask: the bssid_mask, a u8 array of size ETH_ALEN | ||
340 | * | ||
341 | * BSSID masking is a method used by AR5212 and newer hardware to inform PCU | ||
342 | * which bits of the interface's MAC address should be looked at when trying | ||
343 | * to decide which packets to ACK. In station mode and AP mode with a single | ||
344 | * BSS every bit matters since we lock to only one BSS. In AP mode with | ||
345 | * multiple BSSes (virtual interfaces) not every bit matters because hw must | ||
346 | * accept frames for all BSSes and so we tweak some bits of our mac address | ||
347 | * in order to have multiple BSSes. | ||
348 | * | ||
349 | * NOTE: This is a simple filter and does *not* filter out all | ||
350 | * relevant frames. Some frames that are not for us might get ACKed from us | ||
351 | * by PCU because they just match the mask. | ||
352 | * | ||
353 | * When handling multiple BSSes you can get the BSSID mask by computing the | ||
354 | * set of ~ ( MAC XOR BSSID ) for all bssids we handle. | ||
355 | * | ||
356 | * When you do this you are essentially computing the common bits of all your | ||
357 | * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with | ||
358 | * the MAC address to obtain the relevant bits and compare the result with | ||
359 | * (frame's BSSID & mask) to see if they match. | ||
360 | */ | ||
361 | /* | ||
362 | * Simple example: on your card you have have two BSSes you have created with | ||
363 | * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. | ||
364 | * There is another BSSID-03 but you are not part of it. For simplicity's sake, | ||
365 | * assuming only 4 bits for a mac address and for BSSIDs you can then have: | ||
366 | * | ||
367 | * \ | ||
368 | * MAC: 0001 | | ||
369 | * BSSID-01: 0100 | --> Belongs to us | ||
370 | * BSSID-02: 1001 | | ||
371 | * / | ||
372 | * ------------------- | ||
373 | * BSSID-03: 0110 | --> External | ||
374 | * ------------------- | ||
375 | * | ||
376 | * Our bssid_mask would then be: | ||
377 | * | ||
378 | * On loop iteration for BSSID-01: | ||
379 | * ~(0001 ^ 0100) -> ~(0101) | ||
380 | * -> 1010 | ||
381 | * bssid_mask = 1010 | ||
382 | * | ||
383 | * On loop iteration for BSSID-02: | ||
384 | * bssid_mask &= ~(0001 ^ 1001) | ||
385 | * bssid_mask = (1010) & ~(0001 ^ 1001) | ||
386 | * bssid_mask = (1010) & ~(1001) | ||
387 | * bssid_mask = (1010) & (0110) | ||
388 | * bssid_mask = 0010 | ||
389 | * | ||
390 | * A bssid_mask of 0010 means "only pay attention to the second least | ||
391 | * significant bit". This is because its the only bit common | ||
392 | * amongst the MAC and all BSSIDs we support. To findout what the real | ||
393 | * common bit is we can simply "&" the bssid_mask now with any BSSID we have | ||
394 | * or our MAC address (we assume the hardware uses the MAC address). | ||
395 | * | ||
396 | * Now, suppose there's an incoming frame for BSSID-03: | ||
397 | * | ||
398 | * IFRAME-01: 0110 | ||
399 | * | ||
400 | * An easy eye-inspeciton of this already should tell you that this frame | ||
401 | * will not pass our check. This is beacuse the bssid_mask tells the | ||
402 | * hardware to only look at the second least significant bit and the | ||
403 | * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB | ||
404 | * as 1, which does not match 0. | ||
405 | * | ||
406 | * So with IFRAME-01 we *assume* the hardware will do: | ||
407 | * | ||
408 | * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; | ||
409 | * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; | ||
410 | * --> allow = (0010) == 0000 ? 1 : 0; | ||
411 | * --> allow = 0 | ||
412 | * | ||
413 | * Lets now test a frame that should work: | ||
414 | * | ||
415 | * IFRAME-02: 0001 (we should allow) | ||
416 | * | ||
417 | * allow = (0001 & 1010) == 1010 | ||
418 | * | ||
419 | * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; | ||
420 | * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; | ||
421 | * --> allow = (0010) == (0010) | ||
422 | * --> allow = 1 | ||
423 | * | ||
424 | * Other examples: | ||
425 | * | ||
426 | * IFRAME-03: 0100 --> allowed | ||
427 | * IFRAME-04: 1001 --> allowed | ||
428 | * IFRAME-05: 1101 --> allowed but its not for us!!! | ||
429 | * | ||
430 | */ | ||
431 | int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) | ||
432 | { | ||
433 | u32 low_id, high_id; | ||
434 | ATH5K_TRACE(ah->ah_sc); | ||
435 | |||
436 | /* Cache bssid mask so that we can restore it | ||
437 | * on reset */ | ||
438 | memcpy(ah->ah_bssid_mask, mask, ETH_ALEN); | ||
439 | if (ah->ah_version == AR5K_AR5212) { | ||
440 | low_id = AR5K_LOW_ID(mask); | ||
441 | high_id = AR5K_HIGH_ID(mask); | ||
442 | |||
443 | ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); | ||
444 | ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); | ||
445 | |||
446 | return 0; | ||
447 | } | ||
448 | |||
449 | return -EIO; | ||
450 | } | ||
451 | |||
452 | |||
453 | /************\ | ||
454 | * RX Control * | ||
455 | \************/ | ||
456 | |||
457 | /** | ||
458 | * ath5k_hw_start_rx_pcu - Start RX engine | ||
459 | * | ||
460 | * @ah: The &struct ath5k_hw | ||
461 | * | ||
462 | * Starts RX engine on PCU so that hw can process RXed frames | ||
463 | * (ACK etc). | ||
464 | * | ||
465 | * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma | ||
466 | * TODO: Init ANI here | ||
467 | */ | ||
468 | void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) | ||
469 | { | ||
470 | ATH5K_TRACE(ah->ah_sc); | ||
471 | AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); | ||
472 | } | ||
473 | |||
474 | /** | ||
475 | * at5k_hw_stop_rx_pcu - Stop RX engine | ||
476 | * | ||
477 | * @ah: The &struct ath5k_hw | ||
478 | * | ||
479 | * Stops RX engine on PCU | ||
480 | * | ||
481 | * TODO: Detach ANI here | ||
482 | */ | ||
483 | void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah) | ||
484 | { | ||
485 | ATH5K_TRACE(ah->ah_sc); | ||
486 | AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * Set multicast filter | ||
491 | */ | ||
492 | void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) | ||
493 | { | ||
494 | ATH5K_TRACE(ah->ah_sc); | ||
495 | /* Set the multicat filter */ | ||
496 | ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0); | ||
497 | ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1); | ||
498 | } | ||
499 | |||
500 | /* | ||
501 | * Set multicast filter by index | ||
502 | */ | ||
503 | int ath5k_hw_set_mcast_filter_idx(struct ath5k_hw *ah, u32 index) | ||
504 | { | ||
505 | |||
506 | ATH5K_TRACE(ah->ah_sc); | ||
507 | if (index >= 64) | ||
508 | return -EINVAL; | ||
509 | else if (index >= 32) | ||
510 | AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER1, | ||
511 | (1 << (index - 32))); | ||
512 | else | ||
513 | AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); | ||
514 | |||
515 | return 0; | ||
516 | } | ||
517 | |||
518 | /* | ||
519 | * Clear Multicast filter by index | ||
520 | */ | ||
521 | int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index) | ||
522 | { | ||
523 | |||
524 | ATH5K_TRACE(ah->ah_sc); | ||
525 | if (index >= 64) | ||
526 | return -EINVAL; | ||
527 | else if (index >= 32) | ||
528 | AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER1, | ||
529 | (1 << (index - 32))); | ||
530 | else | ||
531 | AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); | ||
532 | |||
533 | return 0; | ||
534 | } | ||
535 | |||
536 | /** | ||
537 | * ath5k_hw_get_rx_filter - Get current rx filter | ||
538 | * | ||
539 | * @ah: The &struct ath5k_hw | ||
540 | * | ||
541 | * Returns the RX filter by reading rx filter and | ||
542 | * phy error filter registers. RX filter is used | ||
543 | * to set the allowed frame types that PCU will accept | ||
544 | * and pass to the driver. For a list of frame types | ||
545 | * check out reg.h. | ||
546 | */ | ||
547 | u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah) | ||
548 | { | ||
549 | u32 data, filter = 0; | ||
550 | |||
551 | ATH5K_TRACE(ah->ah_sc); | ||
552 | filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER); | ||
553 | |||
554 | /*Radar detection for 5212*/ | ||
555 | if (ah->ah_version == AR5K_AR5212) { | ||
556 | data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL); | ||
557 | |||
558 | if (data & AR5K_PHY_ERR_FIL_RADAR) | ||
559 | filter |= AR5K_RX_FILTER_RADARERR; | ||
560 | if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK)) | ||
561 | filter |= AR5K_RX_FILTER_PHYERR; | ||
562 | } | ||
563 | |||
564 | return filter; | ||
565 | } | ||
566 | |||
567 | /** | ||
568 | * ath5k_hw_set_rx_filter - Set rx filter | ||
569 | * | ||
570 | * @ah: The &struct ath5k_hw | ||
571 | * @filter: RX filter mask (see reg.h) | ||
572 | * | ||
573 | * Sets RX filter register and also handles PHY error filter | ||
574 | * register on 5212 and newer chips so that we have proper PHY | ||
575 | * error reporting. | ||
576 | */ | ||
577 | void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) | ||
578 | { | ||
579 | u32 data = 0; | ||
580 | |||
581 | ATH5K_TRACE(ah->ah_sc); | ||
582 | |||
583 | /* Set PHY error filter register on 5212*/ | ||
584 | if (ah->ah_version == AR5K_AR5212) { | ||
585 | if (filter & AR5K_RX_FILTER_RADARERR) | ||
586 | data |= AR5K_PHY_ERR_FIL_RADAR; | ||
587 | if (filter & AR5K_RX_FILTER_PHYERR) | ||
588 | data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK; | ||
589 | } | ||
590 | |||
591 | /* | ||
592 | * The AR5210 uses promiscous mode to detect radar activity | ||
593 | */ | ||
594 | if (ah->ah_version == AR5K_AR5210 && | ||
595 | (filter & AR5K_RX_FILTER_RADARERR)) { | ||
596 | filter &= ~AR5K_RX_FILTER_RADARERR; | ||
597 | filter |= AR5K_RX_FILTER_PROM; | ||
598 | } | ||
599 | |||
600 | /*Zero length DMA (phy error reporting) */ | ||
601 | if (data) | ||
602 | AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); | ||
603 | else | ||
604 | AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); | ||
605 | |||
606 | /*Write RX Filter register*/ | ||
607 | ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER); | ||
608 | |||
609 | /*Write PHY error filter register on 5212*/ | ||
610 | if (ah->ah_version == AR5K_AR5212) | ||
611 | ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL); | ||
612 | |||
613 | } | ||
614 | |||
615 | |||
616 | /****************\ | ||
617 | * Beacon control * | ||
618 | \****************/ | ||
619 | |||
620 | /** | ||
621 | * ath5k_hw_get_tsf32 - Get a 32bit TSF | ||
622 | * | ||
623 | * @ah: The &struct ath5k_hw | ||
624 | * | ||
625 | * Returns lower 32 bits of current TSF | ||
626 | */ | ||
627 | u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah) | ||
628 | { | ||
629 | ATH5K_TRACE(ah->ah_sc); | ||
630 | return ath5k_hw_reg_read(ah, AR5K_TSF_L32); | ||
631 | } | ||
632 | |||
633 | /** | ||
634 | * ath5k_hw_get_tsf64 - Get the full 64bit TSF | ||
635 | * | ||
636 | * @ah: The &struct ath5k_hw | ||
637 | * | ||
638 | * Returns the current TSF | ||
639 | */ | ||
640 | u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) | ||
641 | { | ||
642 | u64 tsf = ath5k_hw_reg_read(ah, AR5K_TSF_U32); | ||
643 | ATH5K_TRACE(ah->ah_sc); | ||
644 | |||
645 | return ath5k_hw_reg_read(ah, AR5K_TSF_L32) | (tsf << 32); | ||
646 | } | ||
647 | |||
648 | /** | ||
649 | * ath5k_hw_set_tsf64 - Set a new 64bit TSF | ||
650 | * | ||
651 | * @ah: The &struct ath5k_hw | ||
652 | * @tsf64: The new 64bit TSF | ||
653 | * | ||
654 | * Sets the new TSF | ||
655 | */ | ||
656 | void ath5k_hw_set_tsf64(struct ath5k_hw *ah, u64 tsf64) | ||
657 | { | ||
658 | ATH5K_TRACE(ah->ah_sc); | ||
659 | |||
660 | ath5k_hw_reg_write(ah, tsf64 & 0xffffffff, AR5K_TSF_L32); | ||
661 | ath5k_hw_reg_write(ah, (tsf64 >> 32) & 0xffffffff, AR5K_TSF_U32); | ||
662 | } | ||
663 | |||
664 | /** | ||
665 | * ath5k_hw_reset_tsf - Force a TSF reset | ||
666 | * | ||
667 | * @ah: The &struct ath5k_hw | ||
668 | * | ||
669 | * Forces a TSF reset on PCU | ||
670 | */ | ||
671 | void ath5k_hw_reset_tsf(struct ath5k_hw *ah) | ||
672 | { | ||
673 | u32 val; | ||
674 | |||
675 | ATH5K_TRACE(ah->ah_sc); | ||
676 | |||
677 | val = ath5k_hw_reg_read(ah, AR5K_BEACON) | AR5K_BEACON_RESET_TSF; | ||
678 | |||
679 | /* | ||
680 | * Each write to the RESET_TSF bit toggles a hardware internal | ||
681 | * signal to reset TSF, but if left high it will cause a TSF reset | ||
682 | * on the next chip reset as well. Thus we always write the value | ||
683 | * twice to clear the signal. | ||
684 | */ | ||
685 | ath5k_hw_reg_write(ah, val, AR5K_BEACON); | ||
686 | ath5k_hw_reg_write(ah, val, AR5K_BEACON); | ||
687 | } | ||
688 | |||
689 | /* | ||
690 | * Initialize beacon timers | ||
691 | */ | ||
692 | void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | ||
693 | { | ||
694 | u32 timer1, timer2, timer3; | ||
695 | |||
696 | ATH5K_TRACE(ah->ah_sc); | ||
697 | /* | ||
698 | * Set the additional timers by mode | ||
699 | */ | ||
700 | switch (ah->ah_op_mode) { | ||
701 | case NL80211_IFTYPE_MONITOR: | ||
702 | case NL80211_IFTYPE_STATION: | ||
703 | /* In STA mode timer1 is used as next wakeup | ||
704 | * timer and timer2 as next CFP duration start | ||
705 | * timer. Both in 1/8TUs. */ | ||
706 | /* TODO: PCF handling */ | ||
707 | if (ah->ah_version == AR5K_AR5210) { | ||
708 | timer1 = 0xffffffff; | ||
709 | timer2 = 0xffffffff; | ||
710 | } else { | ||
711 | timer1 = 0x0000ffff; | ||
712 | timer2 = 0x0007ffff; | ||
713 | } | ||
714 | /* Mark associated AP as PCF incapable for now */ | ||
715 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PCF); | ||
716 | break; | ||
717 | case NL80211_IFTYPE_ADHOC: | ||
718 | AR5K_REG_ENABLE_BITS(ah, AR5K_TXCFG, AR5K_TXCFG_ADHOC_BCN_ATIM); | ||
719 | default: | ||
720 | /* On non-STA modes timer1 is used as next DMA | ||
721 | * beacon alert (DBA) timer and timer2 as next | ||
722 | * software beacon alert. Both in 1/8TUs. */ | ||
723 | timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; | ||
724 | timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; | ||
725 | break; | ||
726 | } | ||
727 | |||
728 | /* Timer3 marks the end of our ATIM window | ||
729 | * a zero length window is not allowed because | ||
730 | * we 'll get no beacons */ | ||
731 | timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); | ||
732 | |||
733 | /* | ||
734 | * Set the beacon register and enable all timers. | ||
735 | */ | ||
736 | /* When in AP mode zero timer0 to start TSF */ | ||
737 | if (ah->ah_op_mode == NL80211_IFTYPE_AP) | ||
738 | ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); | ||
739 | else | ||
740 | ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); | ||
741 | ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); | ||
742 | ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); | ||
743 | ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); | ||
744 | |||
745 | /* Force a TSF reset if requested and enable beacons */ | ||
746 | if (interval & AR5K_BEACON_RESET_TSF) | ||
747 | ath5k_hw_reset_tsf(ah); | ||
748 | |||
749 | ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | | ||
750 | AR5K_BEACON_ENABLE), | ||
751 | AR5K_BEACON); | ||
752 | |||
753 | /* Flush any pending BMISS interrupts on ISR by | ||
754 | * performing a clear-on-write operation on PISR | ||
755 | * register for the BMISS bit (writing a bit on | ||
756 | * ISR togles a reset for that bit and leaves | ||
757 | * the rest bits intact) */ | ||
758 | if (ah->ah_version == AR5K_AR5210) | ||
759 | ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_ISR); | ||
760 | else | ||
761 | ath5k_hw_reg_write(ah, AR5K_ISR_BMISS, AR5K_PISR); | ||
762 | |||
763 | /* TODO: Set enchanced sleep registers on AR5212 | ||
764 | * based on vif->bss_conf params, until then | ||
765 | * disable power save reporting.*/ | ||
766 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, AR5K_STA_ID1_PWR_SV); | ||
767 | |||
768 | } | ||
769 | |||
770 | #if 0 | ||
771 | /* | ||
772 | * Set beacon timers | ||
773 | */ | ||
774 | int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah, | ||
775 | const struct ath5k_beacon_state *state) | ||
776 | { | ||
777 | u32 cfp_period, next_cfp, dtim, interval, next_beacon; | ||
778 | |||
779 | /* | ||
780 | * TODO: should be changed through *state | ||
781 | * review struct ath5k_beacon_state struct | ||
782 | * | ||
783 | * XXX: These are used for cfp period bellow, are they | ||
784 | * ok ? Is it O.K. for tsf here to be 0 or should we use | ||
785 | * get_tsf ? | ||
786 | */ | ||
787 | u32 dtim_count = 0; /* XXX */ | ||
788 | u32 cfp_count = 0; /* XXX */ | ||
789 | u32 tsf = 0; /* XXX */ | ||
790 | |||
791 | ATH5K_TRACE(ah->ah_sc); | ||
792 | /* Return on an invalid beacon state */ | ||
793 | if (state->bs_interval < 1) | ||
794 | return -EINVAL; | ||
795 | |||
796 | interval = state->bs_interval; | ||
797 | dtim = state->bs_dtim_period; | ||
798 | |||
799 | /* | ||
800 | * PCF support? | ||
801 | */ | ||
802 | if (state->bs_cfp_period > 0) { | ||
803 | /* | ||
804 | * Enable PCF mode and set the CFP | ||
805 | * (Contention Free Period) and timer registers | ||
806 | */ | ||
807 | cfp_period = state->bs_cfp_period * state->bs_dtim_period * | ||
808 | state->bs_interval; | ||
809 | next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) * | ||
810 | state->bs_interval; | ||
811 | |||
812 | AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, | ||
813 | AR5K_STA_ID1_DEFAULT_ANTENNA | | ||
814 | AR5K_STA_ID1_PCF); | ||
815 | ath5k_hw_reg_write(ah, cfp_period, AR5K_CFP_PERIOD); | ||
816 | ath5k_hw_reg_write(ah, state->bs_cfp_max_duration, | ||
817 | AR5K_CFP_DUR); | ||
818 | ath5k_hw_reg_write(ah, (tsf + (next_cfp == 0 ? cfp_period : | ||
819 | next_cfp)) << 3, AR5K_TIMER2); | ||
820 | } else { | ||
821 | /* Disable PCF mode */ | ||
822 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, | ||
823 | AR5K_STA_ID1_DEFAULT_ANTENNA | | ||
824 | AR5K_STA_ID1_PCF); | ||
825 | } | ||
826 | |||
827 | /* | ||
828 | * Enable the beacon timer register | ||
829 | */ | ||
830 | ath5k_hw_reg_write(ah, state->bs_next_beacon, AR5K_TIMER0); | ||
831 | |||
832 | /* | ||
833 | * Start the beacon timers | ||
834 | */ | ||
835 | ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_BEACON) & | ||
836 | ~(AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) | | ||
837 | AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0, | ||
838 | AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval, | ||
839 | AR5K_BEACON_PERIOD), AR5K_BEACON); | ||
840 | |||
841 | /* | ||
842 | * Write new beacon miss threshold, if it appears to be valid | ||
843 | * XXX: Figure out right values for min <= bs_bmiss_threshold <= max | ||
844 | * and return if its not in range. We can test this by reading value and | ||
845 | * setting value to a largest value and seeing which values register. | ||
846 | */ | ||
847 | |||
848 | AR5K_REG_WRITE_BITS(ah, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS, | ||
849 | state->bs_bmiss_threshold); | ||
850 | |||
851 | /* | ||
852 | * Set sleep control register | ||
853 | * XXX: Didn't find this in 5210 code but since this register | ||
854 | * exists also in ar5k's 5210 headers i leave it as common code. | ||
855 | */ | ||
856 | AR5K_REG_WRITE_BITS(ah, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR, | ||
857 | (state->bs_sleep_duration - 3) << 3); | ||
858 | |||
859 | /* | ||
860 | * Set enhanced sleep registers on 5212 | ||
861 | */ | ||
862 | if (ah->ah_version == AR5K_AR5212) { | ||
863 | if (state->bs_sleep_duration > state->bs_interval && | ||
864 | roundup(state->bs_sleep_duration, interval) == | ||
865 | state->bs_sleep_duration) | ||
866 | interval = state->bs_sleep_duration; | ||
867 | |||
868 | if (state->bs_sleep_duration > dtim && (dtim == 0 || | ||
869 | roundup(state->bs_sleep_duration, dtim) == | ||
870 | state->bs_sleep_duration)) | ||
871 | dtim = state->bs_sleep_duration; | ||
872 | |||
873 | if (interval > dtim) | ||
874 | return -EINVAL; | ||
875 | |||
876 | next_beacon = interval == dtim ? state->bs_next_dtim : | ||
877 | state->bs_next_beacon; | ||
878 | |||
879 | ath5k_hw_reg_write(ah, | ||
880 | AR5K_REG_SM((state->bs_next_dtim - 3) << 3, | ||
881 | AR5K_SLEEP0_NEXT_DTIM) | | ||
882 | AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) | | ||
883 | AR5K_SLEEP0_ENH_SLEEP_EN | | ||
884 | AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0); | ||
885 | |||
886 | ath5k_hw_reg_write(ah, AR5K_REG_SM((next_beacon - 3) << 3, | ||
887 | AR5K_SLEEP1_NEXT_TIM) | | ||
888 | AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1); | ||
889 | |||
890 | ath5k_hw_reg_write(ah, | ||
891 | AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) | | ||
892 | AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2); | ||
893 | } | ||
894 | |||
895 | return 0; | ||
896 | } | ||
897 | |||
898 | /* | ||
899 | * Reset beacon timers | ||
900 | */ | ||
901 | void ath5k_hw_reset_beacon(struct ath5k_hw *ah) | ||
902 | { | ||
903 | ATH5K_TRACE(ah->ah_sc); | ||
904 | /* | ||
905 | * Disable beacon timer | ||
906 | */ | ||
907 | ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); | ||
908 | |||
909 | /* | ||
910 | * Disable some beacon register values | ||
911 | */ | ||
912 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, | ||
913 | AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF); | ||
914 | ath5k_hw_reg_write(ah, AR5K_BEACON_PERIOD, AR5K_BEACON); | ||
915 | } | ||
916 | |||
917 | /* | ||
918 | * Wait for beacon queue to finish | ||
919 | */ | ||
920 | int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) | ||
921 | { | ||
922 | unsigned int i; | ||
923 | int ret; | ||
924 | |||
925 | ATH5K_TRACE(ah->ah_sc); | ||
926 | |||
927 | /* 5210 doesn't have QCU*/ | ||
928 | if (ah->ah_version == AR5K_AR5210) { | ||
929 | /* | ||
930 | * Wait for beaconn queue to finish by checking | ||
931 | * Control Register and Beacon Status Register. | ||
932 | */ | ||
933 | for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) { | ||
934 | if (!(ath5k_hw_reg_read(ah, AR5K_BSR) & AR5K_BSR_TXQ1F) | ||
935 | || | ||
936 | !(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_BSR_TXQ1F)) | ||
937 | break; | ||
938 | udelay(10); | ||
939 | } | ||
940 | |||
941 | /* Timeout... */ | ||
942 | if (i <= 0) { | ||
943 | /* | ||
944 | * Re-schedule the beacon queue | ||
945 | */ | ||
946 | ath5k_hw_reg_write(ah, phys_addr, AR5K_NOQCU_TXDP1); | ||
947 | ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, | ||
948 | AR5K_BCR); | ||
949 | |||
950 | return -EIO; | ||
951 | } | ||
952 | ret = 0; | ||
953 | } else { | ||
954 | /*5211/5212*/ | ||
955 | ret = ath5k_hw_register_timeout(ah, | ||
956 | AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON), | ||
957 | AR5K_QCU_STS_FRMPENDCNT, 0, false); | ||
958 | |||
959 | if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON)) | ||
960 | return -EIO; | ||
961 | } | ||
962 | |||
963 | return ret; | ||
964 | } | ||
965 | #endif | ||
966 | |||
967 | |||
968 | /*********************\ | ||
969 | * Key table functions * | ||
970 | \*********************/ | ||
971 | |||
972 | /* | ||
973 | * Reset a key entry on the table | ||
974 | */ | ||
975 | int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) | ||
976 | { | ||
977 | unsigned int i, type; | ||
978 | u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; | ||
979 | |||
980 | ATH5K_TRACE(ah->ah_sc); | ||
981 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | ||
982 | |||
983 | type = ath5k_hw_reg_read(ah, AR5K_KEYTABLE_TYPE(entry)); | ||
984 | |||
985 | for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) | ||
986 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); | ||
987 | |||
988 | /* Reset associated MIC entry if TKIP | ||
989 | * is enabled located at offset (entry + 64) */ | ||
990 | if (type == AR5K_KEYTABLE_TYPE_TKIP) { | ||
991 | AR5K_ASSERT_ENTRY(micentry, AR5K_KEYTABLE_SIZE); | ||
992 | for (i = 0; i < AR5K_KEYCACHE_SIZE / 2 ; i++) | ||
993 | ath5k_hw_reg_write(ah, 0, | ||
994 | AR5K_KEYTABLE_OFF(micentry, i)); | ||
995 | } | ||
996 | |||
997 | /* | ||
998 | * Set NULL encryption on AR5212+ | ||
999 | * | ||
1000 | * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5) | ||
1001 | * AR5K_KEYTABLE_TYPE_NULL -> 0x00000007 | ||
1002 | * | ||
1003 | * Note2: Windows driver (ndiswrapper) sets this to | ||
1004 | * 0x00000714 instead of 0x00000007 | ||
1005 | */ | ||
1006 | if (ah->ah_version > AR5K_AR5211) { | ||
1007 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||
1008 | AR5K_KEYTABLE_TYPE(entry)); | ||
1009 | |||
1010 | if (type == AR5K_KEYTABLE_TYPE_TKIP) { | ||
1011 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||
1012 | AR5K_KEYTABLE_TYPE(micentry)); | ||
1013 | } | ||
1014 | } | ||
1015 | |||
1016 | return 0; | ||
1017 | } | ||
1018 | |||
1019 | /* | ||
1020 | * Check if a table entry is valid | ||
1021 | */ | ||
1022 | int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry) | ||
1023 | { | ||
1024 | ATH5K_TRACE(ah->ah_sc); | ||
1025 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | ||
1026 | |||
1027 | /* Check the validation flag at the end of the entry */ | ||
1028 | return ath5k_hw_reg_read(ah, AR5K_KEYTABLE_MAC1(entry)) & | ||
1029 | AR5K_KEYTABLE_VALID; | ||
1030 | } | ||
1031 | |||
1032 | static | ||
1033 | int ath5k_keycache_type(const struct ieee80211_key_conf *key) | ||
1034 | { | ||
1035 | switch (key->alg) { | ||
1036 | case ALG_TKIP: | ||
1037 | return AR5K_KEYTABLE_TYPE_TKIP; | ||
1038 | case ALG_CCMP: | ||
1039 | return AR5K_KEYTABLE_TYPE_CCM; | ||
1040 | case ALG_WEP: | ||
1041 | if (key->keylen == LEN_WEP40) | ||
1042 | return AR5K_KEYTABLE_TYPE_40; | ||
1043 | else if (key->keylen == LEN_WEP104) | ||
1044 | return AR5K_KEYTABLE_TYPE_104; | ||
1045 | return -EINVAL; | ||
1046 | default: | ||
1047 | return -EINVAL; | ||
1048 | } | ||
1049 | return -EINVAL; | ||
1050 | } | ||
1051 | |||
1052 | /* | ||
1053 | * Set a key entry on the table | ||
1054 | */ | ||
1055 | int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, | ||
1056 | const struct ieee80211_key_conf *key, const u8 *mac) | ||
1057 | { | ||
1058 | unsigned int i; | ||
1059 | int keylen; | ||
1060 | __le32 key_v[5] = {}; | ||
1061 | __le32 key0 = 0, key1 = 0; | ||
1062 | __le32 *rxmic, *txmic; | ||
1063 | int keytype; | ||
1064 | u16 micentry = entry + AR5K_KEYTABLE_MIC_OFFSET; | ||
1065 | bool is_tkip; | ||
1066 | const u8 *key_ptr; | ||
1067 | |||
1068 | ATH5K_TRACE(ah->ah_sc); | ||
1069 | |||
1070 | is_tkip = (key->alg == ALG_TKIP); | ||
1071 | |||
1072 | /* | ||
1073 | * key->keylen comes in from mac80211 in bytes. | ||
1074 | * TKIP is 128 bit + 128 bit mic | ||
1075 | */ | ||
1076 | keylen = (is_tkip) ? (128 / 8) : key->keylen; | ||
1077 | |||
1078 | if (entry > AR5K_KEYTABLE_SIZE || | ||
1079 | (is_tkip && micentry > AR5K_KEYTABLE_SIZE)) | ||
1080 | return -EOPNOTSUPP; | ||
1081 | |||
1082 | if (unlikely(keylen > 16)) | ||
1083 | return -EOPNOTSUPP; | ||
1084 | |||
1085 | keytype = ath5k_keycache_type(key); | ||
1086 | if (keytype < 0) | ||
1087 | return keytype; | ||
1088 | |||
1089 | /* | ||
1090 | * each key block is 6 bytes wide, written as pairs of | ||
1091 | * alternating 32 and 16 bit le values. | ||
1092 | */ | ||
1093 | key_ptr = key->key; | ||
1094 | for (i = 0; keylen >= 6; keylen -= 6) { | ||
1095 | memcpy(&key_v[i], key_ptr, 6); | ||
1096 | i += 2; | ||
1097 | key_ptr += 6; | ||
1098 | } | ||
1099 | if (keylen) | ||
1100 | memcpy(&key_v[i], key_ptr, keylen); | ||
1101 | |||
1102 | /* intentionally corrupt key until mic is installed */ | ||
1103 | if (is_tkip) { | ||
1104 | key0 = key_v[0] = ~key_v[0]; | ||
1105 | key1 = key_v[1] = ~key_v[1]; | ||
1106 | } | ||
1107 | |||
1108 | for (i = 0; i < ARRAY_SIZE(key_v); i++) | ||
1109 | ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), | ||
1110 | AR5K_KEYTABLE_OFF(entry, i)); | ||
1111 | |||
1112 | ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); | ||
1113 | |||
1114 | if (is_tkip) { | ||
1115 | /* Install rx/tx MIC */ | ||
1116 | rxmic = (__le32 *) &key->key[16]; | ||
1117 | txmic = (__le32 *) &key->key[24]; | ||
1118 | |||
1119 | if (ah->ah_combined_mic) { | ||
1120 | key_v[0] = rxmic[0]; | ||
1121 | key_v[1] = cpu_to_le32(le32_to_cpu(txmic[0]) >> 16); | ||
1122 | key_v[2] = rxmic[1]; | ||
1123 | key_v[3] = cpu_to_le32(le32_to_cpu(txmic[0]) & 0xffff); | ||
1124 | key_v[4] = txmic[1]; | ||
1125 | } else { | ||
1126 | key_v[0] = rxmic[0]; | ||
1127 | key_v[1] = 0; | ||
1128 | key_v[2] = rxmic[1]; | ||
1129 | key_v[3] = 0; | ||
1130 | key_v[4] = 0; | ||
1131 | } | ||
1132 | for (i = 0; i < ARRAY_SIZE(key_v); i++) | ||
1133 | ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), | ||
1134 | AR5K_KEYTABLE_OFF(micentry, i)); | ||
1135 | |||
1136 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||
1137 | AR5K_KEYTABLE_TYPE(micentry)); | ||
1138 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC0(micentry)); | ||
1139 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_MAC1(micentry)); | ||
1140 | |||
1141 | /* restore first 2 words of key */ | ||
1142 | ath5k_hw_reg_write(ah, le32_to_cpu(~key0), | ||
1143 | AR5K_KEYTABLE_OFF(entry, 0)); | ||
1144 | ath5k_hw_reg_write(ah, le32_to_cpu(~key1), | ||
1145 | AR5K_KEYTABLE_OFF(entry, 1)); | ||
1146 | } | ||
1147 | |||
1148 | return ath5k_hw_set_key_lladdr(ah, entry, mac); | ||
1149 | } | ||
1150 | |||
1151 | int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) | ||
1152 | { | ||
1153 | u32 low_id, high_id; | ||
1154 | |||
1155 | ATH5K_TRACE(ah->ah_sc); | ||
1156 | /* Invalid entry (key table overflow) */ | ||
1157 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | ||
1158 | |||
1159 | /* MAC may be NULL if it's a broadcast key. In this case no need to | ||
1160 | * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ | ||
1161 | if (!mac) { | ||
1162 | low_id = 0xffffffff; | ||
1163 | high_id = 0xffff | AR5K_KEYTABLE_VALID; | ||
1164 | } else { | ||
1165 | low_id = AR5K_LOW_ID(mac); | ||
1166 | high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; | ||
1167 | } | ||
1168 | |||
1169 | ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); | ||
1170 | ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry)); | ||
1171 | |||
1172 | return 0; | ||
1173 | } | ||
1174 | |||