diff options
Diffstat (limited to 'drivers/net/wireless/ath5k/pcu.c')
-rw-r--r-- | drivers/net/wireless/ath5k/pcu.c | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath5k/pcu.c b/drivers/net/wireless/ath5k/pcu.c new file mode 100644 index 000000000000..5a896d1e2a2b --- /dev/null +++ b/drivers/net/wireless/ath5k/pcu.c | |||
@@ -0,0 +1,1002 @@ | |||
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 | pcu_reg = 0; | ||
50 | beacon_reg = 0; | ||
51 | |||
52 | ATH5K_TRACE(ah->ah_sc); | ||
53 | |||
54 | switch (ah->ah_op_mode) { | ||
55 | case IEEE80211_IF_TYPE_IBSS: | ||
56 | pcu_reg |= AR5K_STA_ID1_ADHOC | AR5K_STA_ID1_DESC_ANTENNA | | ||
57 | (ah->ah_version == AR5K_AR5210 ? | ||
58 | AR5K_STA_ID1_NO_PSPOLL : 0); | ||
59 | beacon_reg |= AR5K_BCR_ADHOC; | ||
60 | break; | ||
61 | |||
62 | case IEEE80211_IF_TYPE_AP: | ||
63 | case IEEE80211_IF_TYPE_MESH_POINT: | ||
64 | pcu_reg |= AR5K_STA_ID1_AP | AR5K_STA_ID1_RTS_DEF_ANTENNA | | ||
65 | (ah->ah_version == AR5K_AR5210 ? | ||
66 | AR5K_STA_ID1_NO_PSPOLL : 0); | ||
67 | beacon_reg |= AR5K_BCR_AP; | ||
68 | break; | ||
69 | |||
70 | case IEEE80211_IF_TYPE_STA: | ||
71 | pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | | ||
72 | (ah->ah_version == AR5K_AR5210 ? | ||
73 | AR5K_STA_ID1_PWR_SV : 0); | ||
74 | case IEEE80211_IF_TYPE_MNTR: | ||
75 | pcu_reg |= AR5K_STA_ID1_DEFAULT_ANTENNA | | ||
76 | (ah->ah_version == AR5K_AR5210 ? | ||
77 | AR5K_STA_ID1_NO_PSPOLL : 0); | ||
78 | break; | ||
79 | |||
80 | default: | ||
81 | return -EINVAL; | ||
82 | } | ||
83 | |||
84 | /* | ||
85 | * Set PCU registers | ||
86 | */ | ||
87 | low_id = AR5K_LOW_ID(ah->ah_sta_id); | ||
88 | high_id = AR5K_HIGH_ID(ah->ah_sta_id); | ||
89 | ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); | ||
90 | ath5k_hw_reg_write(ah, pcu_reg | high_id, AR5K_STA_ID1); | ||
91 | |||
92 | /* | ||
93 | * Set Beacon Control Register on 5210 | ||
94 | */ | ||
95 | if (ah->ah_version == AR5K_AR5210) | ||
96 | ath5k_hw_reg_write(ah, beacon_reg, AR5K_BCR); | ||
97 | |||
98 | return 0; | ||
99 | } | ||
100 | |||
101 | /** | ||
102 | * ath5k_hw_update - Update mib counters (mac layer statistics) | ||
103 | * | ||
104 | * @ah: The &struct ath5k_hw | ||
105 | * @stats: The &struct ieee80211_low_level_stats we use to track | ||
106 | * statistics on the driver | ||
107 | * | ||
108 | * Reads MIB counters from PCU and updates sw statistics. Must be | ||
109 | * called after a MIB interrupt. | ||
110 | */ | ||
111 | void ath5k_hw_update_mib_counters(struct ath5k_hw *ah, | ||
112 | struct ieee80211_low_level_stats *stats) | ||
113 | { | ||
114 | ATH5K_TRACE(ah->ah_sc); | ||
115 | |||
116 | /* Read-And-Clear */ | ||
117 | stats->dot11ACKFailureCount += ath5k_hw_reg_read(ah, AR5K_ACK_FAIL); | ||
118 | stats->dot11RTSFailureCount += ath5k_hw_reg_read(ah, AR5K_RTS_FAIL); | ||
119 | stats->dot11RTSSuccessCount += ath5k_hw_reg_read(ah, AR5K_RTS_OK); | ||
120 | stats->dot11FCSErrorCount += ath5k_hw_reg_read(ah, AR5K_FCS_FAIL); | ||
121 | |||
122 | /* XXX: Should we use this to track beacon count ? | ||
123 | * -we read it anyway to clear the register */ | ||
124 | ath5k_hw_reg_read(ah, AR5K_BEACON_CNT); | ||
125 | |||
126 | /* Reset profile count registers on 5212*/ | ||
127 | if (ah->ah_version == AR5K_AR5212) { | ||
128 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_TX); | ||
129 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RX); | ||
130 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_RXCLR); | ||
131 | ath5k_hw_reg_write(ah, 0, AR5K_PROFCNT_CYCLE); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | /** | ||
136 | * ath5k_hw_set_ack_bitrate - set bitrate for ACKs | ||
137 | * | ||
138 | * @ah: The &struct ath5k_hw | ||
139 | * @high: Flag to determine if we want to use high transmition rate | ||
140 | * for ACKs or not | ||
141 | * | ||
142 | * If high flag is set, we tell hw to use a set of control rates based on | ||
143 | * the current transmition rate (check out control_rates array inside reset.c). | ||
144 | * If not hw just uses the lowest rate available for the current modulation | ||
145 | * scheme being used (1Mbit for CCK and 6Mbits for OFDM). | ||
146 | */ | ||
147 | void ath5k_hw_set_ack_bitrate_high(struct ath5k_hw *ah, bool high) | ||
148 | { | ||
149 | if (ah->ah_version != AR5K_AR5212) | ||
150 | return; | ||
151 | else { | ||
152 | u32 val = AR5K_STA_ID1_BASE_RATE_11B | AR5K_STA_ID1_ACKCTS_6MB; | ||
153 | if (high) | ||
154 | AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, val); | ||
155 | else | ||
156 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, val); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | |||
161 | /******************\ | ||
162 | * ACK/CTS Timeouts * | ||
163 | \******************/ | ||
164 | |||
165 | /** | ||
166 | * ath5k_hw_het_ack_timeout - Get ACK timeout from PCU in usec | ||
167 | * | ||
168 | * @ah: The &struct ath5k_hw | ||
169 | */ | ||
170 | unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) | ||
171 | { | ||
172 | ATH5K_TRACE(ah->ah_sc); | ||
173 | |||
174 | return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, | ||
175 | AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo); | ||
176 | } | ||
177 | |||
178 | /** | ||
179 | * ath5k_hw_set_ack_timeout - Set ACK timeout on PCU | ||
180 | * | ||
181 | * @ah: The &struct ath5k_hw | ||
182 | * @timeout: Timeout in usec | ||
183 | */ | ||
184 | int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) | ||
185 | { | ||
186 | ATH5K_TRACE(ah->ah_sc); | ||
187 | if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), | ||
188 | ah->ah_turbo) <= timeout) | ||
189 | return -EINVAL; | ||
190 | |||
191 | AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, | ||
192 | ath5k_hw_htoclock(timeout, ah->ah_turbo)); | ||
193 | |||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * ath5k_hw_get_cts_timeout - Get CTS timeout from PCU in usec | ||
199 | * | ||
200 | * @ah: The &struct ath5k_hw | ||
201 | */ | ||
202 | unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) | ||
203 | { | ||
204 | ATH5K_TRACE(ah->ah_sc); | ||
205 | return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, | ||
206 | AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo); | ||
207 | } | ||
208 | |||
209 | /** | ||
210 | * ath5k_hw_set_cts_timeout - Set CTS timeout on PCU | ||
211 | * | ||
212 | * @ah: The &struct ath5k_hw | ||
213 | * @timeout: Timeout in usec | ||
214 | */ | ||
215 | int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) | ||
216 | { | ||
217 | ATH5K_TRACE(ah->ah_sc); | ||
218 | if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), | ||
219 | ah->ah_turbo) <= timeout) | ||
220 | return -EINVAL; | ||
221 | |||
222 | AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, | ||
223 | ath5k_hw_htoclock(timeout, ah->ah_turbo)); | ||
224 | |||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | |||
229 | /****************\ | ||
230 | * BSSID handling * | ||
231 | \****************/ | ||
232 | |||
233 | /** | ||
234 | * ath5k_hw_get_lladdr - Get station id | ||
235 | * | ||
236 | * @ah: The &struct ath5k_hw | ||
237 | * @mac: The card's mac address | ||
238 | * | ||
239 | * Initialize ah->ah_sta_id using the mac address provided | ||
240 | * (just a memcpy). | ||
241 | * | ||
242 | * TODO: Remove it once we merge ath5k_softc and ath5k_hw | ||
243 | */ | ||
244 | void ath5k_hw_get_lladdr(struct ath5k_hw *ah, u8 *mac) | ||
245 | { | ||
246 | ATH5K_TRACE(ah->ah_sc); | ||
247 | memcpy(mac, ah->ah_sta_id, ETH_ALEN); | ||
248 | } | ||
249 | |||
250 | /** | ||
251 | * ath5k_hw_set_lladdr - Set station id | ||
252 | * | ||
253 | * @ah: The &struct ath5k_hw | ||
254 | * @mac: The card's mac address | ||
255 | * | ||
256 | * Set station id on hw using the provided mac address | ||
257 | */ | ||
258 | int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac) | ||
259 | { | ||
260 | u32 low_id, high_id; | ||
261 | |||
262 | ATH5K_TRACE(ah->ah_sc); | ||
263 | /* Set new station ID */ | ||
264 | memcpy(ah->ah_sta_id, mac, ETH_ALEN); | ||
265 | |||
266 | low_id = AR5K_LOW_ID(mac); | ||
267 | high_id = AR5K_HIGH_ID(mac); | ||
268 | |||
269 | ath5k_hw_reg_write(ah, low_id, AR5K_STA_ID0); | ||
270 | ath5k_hw_reg_write(ah, high_id, AR5K_STA_ID1); | ||
271 | |||
272 | return 0; | ||
273 | } | ||
274 | |||
275 | /** | ||
276 | * ath5k_hw_set_associd - Set BSSID for association | ||
277 | * | ||
278 | * @ah: The &struct ath5k_hw | ||
279 | * @bssid: BSSID | ||
280 | * @assoc_id: Assoc id | ||
281 | * | ||
282 | * Sets the BSSID which trigers the "SME Join" operation | ||
283 | */ | ||
284 | void ath5k_hw_set_associd(struct ath5k_hw *ah, const u8 *bssid, u16 assoc_id) | ||
285 | { | ||
286 | u32 low_id, high_id; | ||
287 | u16 tim_offset = 0; | ||
288 | |||
289 | /* | ||
290 | * Set simple BSSID mask on 5212 | ||
291 | */ | ||
292 | if (ah->ah_version == AR5K_AR5212) { | ||
293 | ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM0); | ||
294 | ath5k_hw_reg_write(ah, 0xffffffff, AR5K_BSS_IDM1); | ||
295 | } | ||
296 | |||
297 | /* | ||
298 | * Set BSSID which triggers the "SME Join" operation | ||
299 | */ | ||
300 | low_id = AR5K_LOW_ID(bssid); | ||
301 | high_id = AR5K_HIGH_ID(bssid); | ||
302 | ath5k_hw_reg_write(ah, low_id, AR5K_BSS_ID0); | ||
303 | ath5k_hw_reg_write(ah, high_id | ((assoc_id & 0x3fff) << | ||
304 | AR5K_BSS_ID1_AID_S), AR5K_BSS_ID1); | ||
305 | |||
306 | if (assoc_id == 0) { | ||
307 | ath5k_hw_disable_pspoll(ah); | ||
308 | return; | ||
309 | } | ||
310 | |||
311 | AR5K_REG_WRITE_BITS(ah, AR5K_BEACON, AR5K_BEACON_TIM, | ||
312 | tim_offset ? tim_offset + 4 : 0); | ||
313 | |||
314 | ath5k_hw_enable_pspoll(ah, NULL, 0); | ||
315 | } | ||
316 | |||
317 | /** | ||
318 | * ath5k_hw_set_bssid_mask - filter out bssids we listen | ||
319 | * | ||
320 | * @ah: the &struct ath5k_hw | ||
321 | * @mask: the bssid_mask, a u8 array of size ETH_ALEN | ||
322 | * | ||
323 | * BSSID masking is a method used by AR5212 and newer hardware to inform PCU | ||
324 | * which bits of the interface's MAC address should be looked at when trying | ||
325 | * to decide which packets to ACK. In station mode and AP mode with a single | ||
326 | * BSS every bit matters since we lock to only one BSS. In AP mode with | ||
327 | * multiple BSSes (virtual interfaces) not every bit matters because hw must | ||
328 | * accept frames for all BSSes and so we tweak some bits of our mac address | ||
329 | * in order to have multiple BSSes. | ||
330 | * | ||
331 | * NOTE: This is a simple filter and does *not* filter out all | ||
332 | * relevant frames. Some frames that are not for us might get ACKed from us | ||
333 | * by PCU because they just match the mask. | ||
334 | * | ||
335 | * When handling multiple BSSes you can get the BSSID mask by computing the | ||
336 | * set of ~ ( MAC XOR BSSID ) for all bssids we handle. | ||
337 | * | ||
338 | * When you do this you are essentially computing the common bits of all your | ||
339 | * BSSes. Later it is assumed the harware will "and" (&) the BSSID mask with | ||
340 | * the MAC address to obtain the relevant bits and compare the result with | ||
341 | * (frame's BSSID & mask) to see if they match. | ||
342 | */ | ||
343 | /* | ||
344 | * Simple example: on your card you have have two BSSes you have created with | ||
345 | * BSSID-01 and BSSID-02. Lets assume BSSID-01 will not use the MAC address. | ||
346 | * There is another BSSID-03 but you are not part of it. For simplicity's sake, | ||
347 | * assuming only 4 bits for a mac address and for BSSIDs you can then have: | ||
348 | * | ||
349 | * \ | ||
350 | * MAC: 0001 | | ||
351 | * BSSID-01: 0100 | --> Belongs to us | ||
352 | * BSSID-02: 1001 | | ||
353 | * / | ||
354 | * ------------------- | ||
355 | * BSSID-03: 0110 | --> External | ||
356 | * ------------------- | ||
357 | * | ||
358 | * Our bssid_mask would then be: | ||
359 | * | ||
360 | * On loop iteration for BSSID-01: | ||
361 | * ~(0001 ^ 0100) -> ~(0101) | ||
362 | * -> 1010 | ||
363 | * bssid_mask = 1010 | ||
364 | * | ||
365 | * On loop iteration for BSSID-02: | ||
366 | * bssid_mask &= ~(0001 ^ 1001) | ||
367 | * bssid_mask = (1010) & ~(0001 ^ 1001) | ||
368 | * bssid_mask = (1010) & ~(1001) | ||
369 | * bssid_mask = (1010) & (0110) | ||
370 | * bssid_mask = 0010 | ||
371 | * | ||
372 | * A bssid_mask of 0010 means "only pay attention to the second least | ||
373 | * significant bit". This is because its the only bit common | ||
374 | * amongst the MAC and all BSSIDs we support. To findout what the real | ||
375 | * common bit is we can simply "&" the bssid_mask now with any BSSID we have | ||
376 | * or our MAC address (we assume the hardware uses the MAC address). | ||
377 | * | ||
378 | * Now, suppose there's an incoming frame for BSSID-03: | ||
379 | * | ||
380 | * IFRAME-01: 0110 | ||
381 | * | ||
382 | * An easy eye-inspeciton of this already should tell you that this frame | ||
383 | * will not pass our check. This is beacuse the bssid_mask tells the | ||
384 | * hardware to only look at the second least significant bit and the | ||
385 | * common bit amongst the MAC and BSSIDs is 0, this frame has the 2nd LSB | ||
386 | * as 1, which does not match 0. | ||
387 | * | ||
388 | * So with IFRAME-01 we *assume* the hardware will do: | ||
389 | * | ||
390 | * allow = (IFRAME-01 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; | ||
391 | * --> allow = (0110 & 0010) == (0010 & 0001) ? 1 : 0; | ||
392 | * --> allow = (0010) == 0000 ? 1 : 0; | ||
393 | * --> allow = 0 | ||
394 | * | ||
395 | * Lets now test a frame that should work: | ||
396 | * | ||
397 | * IFRAME-02: 0001 (we should allow) | ||
398 | * | ||
399 | * allow = (0001 & 1010) == 1010 | ||
400 | * | ||
401 | * allow = (IFRAME-02 & bssid_mask) == (bssid_mask & MAC) ? 1 : 0; | ||
402 | * --> allow = (0001 & 0010) == (0010 & 0001) ? 1 :0; | ||
403 | * --> allow = (0010) == (0010) | ||
404 | * --> allow = 1 | ||
405 | * | ||
406 | * Other examples: | ||
407 | * | ||
408 | * IFRAME-03: 0100 --> allowed | ||
409 | * IFRAME-04: 1001 --> allowed | ||
410 | * IFRAME-05: 1101 --> allowed but its not for us!!! | ||
411 | * | ||
412 | */ | ||
413 | int ath5k_hw_set_bssid_mask(struct ath5k_hw *ah, const u8 *mask) | ||
414 | { | ||
415 | u32 low_id, high_id; | ||
416 | ATH5K_TRACE(ah->ah_sc); | ||
417 | |||
418 | if (ah->ah_version == AR5K_AR5212) { | ||
419 | low_id = AR5K_LOW_ID(mask); | ||
420 | high_id = AR5K_HIGH_ID(mask); | ||
421 | |||
422 | ath5k_hw_reg_write(ah, low_id, AR5K_BSS_IDM0); | ||
423 | ath5k_hw_reg_write(ah, high_id, AR5K_BSS_IDM1); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | |||
428 | return -EIO; | ||
429 | } | ||
430 | |||
431 | |||
432 | /************\ | ||
433 | * RX Control * | ||
434 | \************/ | ||
435 | |||
436 | /** | ||
437 | * ath5k_hw_start_rx_pcu - Start RX engine | ||
438 | * | ||
439 | * @ah: The &struct ath5k_hw | ||
440 | * | ||
441 | * Starts RX engine on PCU so that hw can process RXed frames | ||
442 | * (ACK etc). | ||
443 | * | ||
444 | * NOTE: RX DMA should be already enabled using ath5k_hw_start_rx_dma | ||
445 | * TODO: Init ANI here | ||
446 | */ | ||
447 | void ath5k_hw_start_rx_pcu(struct ath5k_hw *ah) | ||
448 | { | ||
449 | ATH5K_TRACE(ah->ah_sc); | ||
450 | AR5K_REG_DISABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); | ||
451 | } | ||
452 | |||
453 | /** | ||
454 | * at5k_hw_stop_rx_pcu - Stop RX engine | ||
455 | * | ||
456 | * @ah: The &struct ath5k_hw | ||
457 | * | ||
458 | * Stops RX engine on PCU | ||
459 | * | ||
460 | * TODO: Detach ANI here | ||
461 | */ | ||
462 | void ath5k_hw_stop_rx_pcu(struct ath5k_hw *ah) | ||
463 | { | ||
464 | ATH5K_TRACE(ah->ah_sc); | ||
465 | AR5K_REG_ENABLE_BITS(ah, AR5K_DIAG_SW, AR5K_DIAG_SW_DIS_RX); | ||
466 | } | ||
467 | |||
468 | /* | ||
469 | * Set multicast filter | ||
470 | */ | ||
471 | void ath5k_hw_set_mcast_filter(struct ath5k_hw *ah, u32 filter0, u32 filter1) | ||
472 | { | ||
473 | ATH5K_TRACE(ah->ah_sc); | ||
474 | /* Set the multicat filter */ | ||
475 | ath5k_hw_reg_write(ah, filter0, AR5K_MCAST_FILTER0); | ||
476 | ath5k_hw_reg_write(ah, filter1, AR5K_MCAST_FILTER1); | ||
477 | } | ||
478 | |||
479 | /* | ||
480 | * Set multicast filter by index | ||
481 | */ | ||
482 | int ath5k_hw_set_mcast_filter_idx(struct ath5k_hw *ah, u32 index) | ||
483 | { | ||
484 | |||
485 | ATH5K_TRACE(ah->ah_sc); | ||
486 | if (index >= 64) | ||
487 | return -EINVAL; | ||
488 | else if (index >= 32) | ||
489 | AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER1, | ||
490 | (1 << (index - 32))); | ||
491 | else | ||
492 | AR5K_REG_ENABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * Clear Multicast filter by index | ||
499 | */ | ||
500 | int ath5k_hw_clear_mcast_filter_idx(struct ath5k_hw *ah, u32 index) | ||
501 | { | ||
502 | |||
503 | ATH5K_TRACE(ah->ah_sc); | ||
504 | if (index >= 64) | ||
505 | return -EINVAL; | ||
506 | else if (index >= 32) | ||
507 | AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER1, | ||
508 | (1 << (index - 32))); | ||
509 | else | ||
510 | AR5K_REG_DISABLE_BITS(ah, AR5K_MCAST_FILTER0, (1 << index)); | ||
511 | |||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | /** | ||
516 | * ath5k_hw_get_rx_filter - Get current rx filter | ||
517 | * | ||
518 | * @ah: The &struct ath5k_hw | ||
519 | * | ||
520 | * Returns the RX filter by reading rx filter and | ||
521 | * phy error filter registers. RX filter is used | ||
522 | * to set the allowed frame types that PCU will accept | ||
523 | * and pass to the driver. For a list of frame types | ||
524 | * check out reg.h. | ||
525 | */ | ||
526 | u32 ath5k_hw_get_rx_filter(struct ath5k_hw *ah) | ||
527 | { | ||
528 | u32 data, filter = 0; | ||
529 | |||
530 | ATH5K_TRACE(ah->ah_sc); | ||
531 | filter = ath5k_hw_reg_read(ah, AR5K_RX_FILTER); | ||
532 | |||
533 | /*Radar detection for 5212*/ | ||
534 | if (ah->ah_version == AR5K_AR5212) { | ||
535 | data = ath5k_hw_reg_read(ah, AR5K_PHY_ERR_FIL); | ||
536 | |||
537 | if (data & AR5K_PHY_ERR_FIL_RADAR) | ||
538 | filter |= AR5K_RX_FILTER_RADARERR; | ||
539 | if (data & (AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK)) | ||
540 | filter |= AR5K_RX_FILTER_PHYERR; | ||
541 | } | ||
542 | |||
543 | return filter; | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * ath5k_hw_set_rx_filter - Set rx filter | ||
548 | * | ||
549 | * @ah: The &struct ath5k_hw | ||
550 | * @filter: RX filter mask (see reg.h) | ||
551 | * | ||
552 | * Sets RX filter register and also handles PHY error filter | ||
553 | * register on 5212 and newer chips so that we have proper PHY | ||
554 | * error reporting. | ||
555 | */ | ||
556 | void ath5k_hw_set_rx_filter(struct ath5k_hw *ah, u32 filter) | ||
557 | { | ||
558 | u32 data = 0; | ||
559 | |||
560 | ATH5K_TRACE(ah->ah_sc); | ||
561 | |||
562 | /* Set PHY error filter register on 5212*/ | ||
563 | if (ah->ah_version == AR5K_AR5212) { | ||
564 | if (filter & AR5K_RX_FILTER_RADARERR) | ||
565 | data |= AR5K_PHY_ERR_FIL_RADAR; | ||
566 | if (filter & AR5K_RX_FILTER_PHYERR) | ||
567 | data |= AR5K_PHY_ERR_FIL_OFDM | AR5K_PHY_ERR_FIL_CCK; | ||
568 | } | ||
569 | |||
570 | /* | ||
571 | * The AR5210 uses promiscous mode to detect radar activity | ||
572 | */ | ||
573 | if (ah->ah_version == AR5K_AR5210 && | ||
574 | (filter & AR5K_RX_FILTER_RADARERR)) { | ||
575 | filter &= ~AR5K_RX_FILTER_RADARERR; | ||
576 | filter |= AR5K_RX_FILTER_PROM; | ||
577 | } | ||
578 | |||
579 | /*Zero length DMA*/ | ||
580 | if (data) | ||
581 | AR5K_REG_ENABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); | ||
582 | else | ||
583 | AR5K_REG_DISABLE_BITS(ah, AR5K_RXCFG, AR5K_RXCFG_ZLFDMA); | ||
584 | |||
585 | /*Write RX Filter register*/ | ||
586 | ath5k_hw_reg_write(ah, filter & 0xff, AR5K_RX_FILTER); | ||
587 | |||
588 | /*Write PHY error filter register on 5212*/ | ||
589 | if (ah->ah_version == AR5K_AR5212) | ||
590 | ath5k_hw_reg_write(ah, data, AR5K_PHY_ERR_FIL); | ||
591 | |||
592 | } | ||
593 | |||
594 | |||
595 | /****************\ | ||
596 | * Beacon control * | ||
597 | \****************/ | ||
598 | |||
599 | /** | ||
600 | * ath5k_hw_get_tsf32 - Get a 32bit TSF | ||
601 | * | ||
602 | * @ah: The &struct ath5k_hw | ||
603 | * | ||
604 | * Returns lower 32 bits of current TSF | ||
605 | */ | ||
606 | u32 ath5k_hw_get_tsf32(struct ath5k_hw *ah) | ||
607 | { | ||
608 | ATH5K_TRACE(ah->ah_sc); | ||
609 | return ath5k_hw_reg_read(ah, AR5K_TSF_L32); | ||
610 | } | ||
611 | |||
612 | /** | ||
613 | * ath5k_hw_get_tsf64 - Get the full 64bit TSF | ||
614 | * | ||
615 | * @ah: The &struct ath5k_hw | ||
616 | * | ||
617 | * Returns the current TSF | ||
618 | */ | ||
619 | u64 ath5k_hw_get_tsf64(struct ath5k_hw *ah) | ||
620 | { | ||
621 | u64 tsf = ath5k_hw_reg_read(ah, AR5K_TSF_U32); | ||
622 | ATH5K_TRACE(ah->ah_sc); | ||
623 | |||
624 | return ath5k_hw_reg_read(ah, AR5K_TSF_L32) | (tsf << 32); | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * ath5k_hw_reset_tsf - Force a TSF reset | ||
629 | * | ||
630 | * @ah: The &struct ath5k_hw | ||
631 | * | ||
632 | * Forces a TSF reset on PCU | ||
633 | */ | ||
634 | void ath5k_hw_reset_tsf(struct ath5k_hw *ah) | ||
635 | { | ||
636 | ATH5K_TRACE(ah->ah_sc); | ||
637 | AR5K_REG_ENABLE_BITS(ah, AR5K_BEACON, AR5K_BEACON_RESET_TSF); | ||
638 | } | ||
639 | |||
640 | /* | ||
641 | * Initialize beacon timers | ||
642 | */ | ||
643 | void ath5k_hw_init_beacon(struct ath5k_hw *ah, u32 next_beacon, u32 interval) | ||
644 | { | ||
645 | u32 timer1, timer2, timer3; | ||
646 | |||
647 | ATH5K_TRACE(ah->ah_sc); | ||
648 | /* | ||
649 | * Set the additional timers by mode | ||
650 | */ | ||
651 | switch (ah->ah_op_mode) { | ||
652 | case IEEE80211_IF_TYPE_STA: | ||
653 | if (ah->ah_version == AR5K_AR5210) { | ||
654 | timer1 = 0xffffffff; | ||
655 | timer2 = 0xffffffff; | ||
656 | } else { | ||
657 | timer1 = 0x0000ffff; | ||
658 | timer2 = 0x0007ffff; | ||
659 | } | ||
660 | break; | ||
661 | |||
662 | default: | ||
663 | timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) << 3; | ||
664 | timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) << 3; | ||
665 | } | ||
666 | |||
667 | timer3 = next_beacon + (ah->ah_atim_window ? ah->ah_atim_window : 1); | ||
668 | |||
669 | /* | ||
670 | * Set the beacon register and enable all timers. | ||
671 | * (next beacon, DMA beacon, software beacon, ATIM window time) | ||
672 | */ | ||
673 | ath5k_hw_reg_write(ah, next_beacon, AR5K_TIMER0); | ||
674 | ath5k_hw_reg_write(ah, timer1, AR5K_TIMER1); | ||
675 | ath5k_hw_reg_write(ah, timer2, AR5K_TIMER2); | ||
676 | ath5k_hw_reg_write(ah, timer3, AR5K_TIMER3); | ||
677 | |||
678 | ath5k_hw_reg_write(ah, interval & (AR5K_BEACON_PERIOD | | ||
679 | AR5K_BEACON_RESET_TSF | AR5K_BEACON_ENABLE), | ||
680 | AR5K_BEACON); | ||
681 | } | ||
682 | |||
683 | #if 0 | ||
684 | /* | ||
685 | * Set beacon timers | ||
686 | */ | ||
687 | int ath5k_hw_set_beacon_timers(struct ath5k_hw *ah, | ||
688 | const struct ath5k_beacon_state *state) | ||
689 | { | ||
690 | u32 cfp_period, next_cfp, dtim, interval, next_beacon; | ||
691 | |||
692 | /* | ||
693 | * TODO: should be changed through *state | ||
694 | * review struct ath5k_beacon_state struct | ||
695 | * | ||
696 | * XXX: These are used for cfp period bellow, are they | ||
697 | * ok ? Is it O.K. for tsf here to be 0 or should we use | ||
698 | * get_tsf ? | ||
699 | */ | ||
700 | u32 dtim_count = 0; /* XXX */ | ||
701 | u32 cfp_count = 0; /* XXX */ | ||
702 | u32 tsf = 0; /* XXX */ | ||
703 | |||
704 | ATH5K_TRACE(ah->ah_sc); | ||
705 | /* Return on an invalid beacon state */ | ||
706 | if (state->bs_interval < 1) | ||
707 | return -EINVAL; | ||
708 | |||
709 | interval = state->bs_interval; | ||
710 | dtim = state->bs_dtim_period; | ||
711 | |||
712 | /* | ||
713 | * PCF support? | ||
714 | */ | ||
715 | if (state->bs_cfp_period > 0) { | ||
716 | /* | ||
717 | * Enable PCF mode and set the CFP | ||
718 | * (Contention Free Period) and timer registers | ||
719 | */ | ||
720 | cfp_period = state->bs_cfp_period * state->bs_dtim_period * | ||
721 | state->bs_interval; | ||
722 | next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) * | ||
723 | state->bs_interval; | ||
724 | |||
725 | AR5K_REG_ENABLE_BITS(ah, AR5K_STA_ID1, | ||
726 | AR5K_STA_ID1_DEFAULT_ANTENNA | | ||
727 | AR5K_STA_ID1_PCF); | ||
728 | ath5k_hw_reg_write(ah, cfp_period, AR5K_CFP_PERIOD); | ||
729 | ath5k_hw_reg_write(ah, state->bs_cfp_max_duration, | ||
730 | AR5K_CFP_DUR); | ||
731 | ath5k_hw_reg_write(ah, (tsf + (next_cfp == 0 ? cfp_period : | ||
732 | next_cfp)) << 3, AR5K_TIMER2); | ||
733 | } else { | ||
734 | /* Disable PCF mode */ | ||
735 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, | ||
736 | AR5K_STA_ID1_DEFAULT_ANTENNA | | ||
737 | AR5K_STA_ID1_PCF); | ||
738 | } | ||
739 | |||
740 | /* | ||
741 | * Enable the beacon timer register | ||
742 | */ | ||
743 | ath5k_hw_reg_write(ah, state->bs_next_beacon, AR5K_TIMER0); | ||
744 | |||
745 | /* | ||
746 | * Start the beacon timers | ||
747 | */ | ||
748 | ath5k_hw_reg_write(ah, (ath5k_hw_reg_read(ah, AR5K_BEACON) & | ||
749 | ~(AR5K_BEACON_PERIOD | AR5K_BEACON_TIM)) | | ||
750 | AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0, | ||
751 | AR5K_BEACON_TIM) | AR5K_REG_SM(state->bs_interval, | ||
752 | AR5K_BEACON_PERIOD), AR5K_BEACON); | ||
753 | |||
754 | /* | ||
755 | * Write new beacon miss threshold, if it appears to be valid | ||
756 | * XXX: Figure out right values for min <= bs_bmiss_threshold <= max | ||
757 | * and return if its not in range. We can test this by reading value and | ||
758 | * setting value to a largest value and seeing which values register. | ||
759 | */ | ||
760 | |||
761 | AR5K_REG_WRITE_BITS(ah, AR5K_RSSI_THR, AR5K_RSSI_THR_BMISS, | ||
762 | state->bs_bmiss_threshold); | ||
763 | |||
764 | /* | ||
765 | * Set sleep control register | ||
766 | * XXX: Didn't find this in 5210 code but since this register | ||
767 | * exists also in ar5k's 5210 headers i leave it as common code. | ||
768 | */ | ||
769 | AR5K_REG_WRITE_BITS(ah, AR5K_SLEEP_CTL, AR5K_SLEEP_CTL_SLDUR, | ||
770 | (state->bs_sleep_duration - 3) << 3); | ||
771 | |||
772 | /* | ||
773 | * Set enhanced sleep registers on 5212 | ||
774 | */ | ||
775 | if (ah->ah_version == AR5K_AR5212) { | ||
776 | if (state->bs_sleep_duration > state->bs_interval && | ||
777 | roundup(state->bs_sleep_duration, interval) == | ||
778 | state->bs_sleep_duration) | ||
779 | interval = state->bs_sleep_duration; | ||
780 | |||
781 | if (state->bs_sleep_duration > dtim && (dtim == 0 || | ||
782 | roundup(state->bs_sleep_duration, dtim) == | ||
783 | state->bs_sleep_duration)) | ||
784 | dtim = state->bs_sleep_duration; | ||
785 | |||
786 | if (interval > dtim) | ||
787 | return -EINVAL; | ||
788 | |||
789 | next_beacon = interval == dtim ? state->bs_next_dtim : | ||
790 | state->bs_next_beacon; | ||
791 | |||
792 | ath5k_hw_reg_write(ah, | ||
793 | AR5K_REG_SM((state->bs_next_dtim - 3) << 3, | ||
794 | AR5K_SLEEP0_NEXT_DTIM) | | ||
795 | AR5K_REG_SM(10, AR5K_SLEEP0_CABTO) | | ||
796 | AR5K_SLEEP0_ENH_SLEEP_EN | | ||
797 | AR5K_SLEEP0_ASSUME_DTIM, AR5K_SLEEP0); | ||
798 | |||
799 | ath5k_hw_reg_write(ah, AR5K_REG_SM((next_beacon - 3) << 3, | ||
800 | AR5K_SLEEP1_NEXT_TIM) | | ||
801 | AR5K_REG_SM(10, AR5K_SLEEP1_BEACON_TO), AR5K_SLEEP1); | ||
802 | |||
803 | ath5k_hw_reg_write(ah, | ||
804 | AR5K_REG_SM(interval, AR5K_SLEEP2_TIM_PER) | | ||
805 | AR5K_REG_SM(dtim, AR5K_SLEEP2_DTIM_PER), AR5K_SLEEP2); | ||
806 | } | ||
807 | |||
808 | return 0; | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * Reset beacon timers | ||
813 | */ | ||
814 | void ath5k_hw_reset_beacon(struct ath5k_hw *ah) | ||
815 | { | ||
816 | ATH5K_TRACE(ah->ah_sc); | ||
817 | /* | ||
818 | * Disable beacon timer | ||
819 | */ | ||
820 | ath5k_hw_reg_write(ah, 0, AR5K_TIMER0); | ||
821 | |||
822 | /* | ||
823 | * Disable some beacon register values | ||
824 | */ | ||
825 | AR5K_REG_DISABLE_BITS(ah, AR5K_STA_ID1, | ||
826 | AR5K_STA_ID1_DEFAULT_ANTENNA | AR5K_STA_ID1_PCF); | ||
827 | ath5k_hw_reg_write(ah, AR5K_BEACON_PERIOD, AR5K_BEACON); | ||
828 | } | ||
829 | |||
830 | /* | ||
831 | * Wait for beacon queue to finish | ||
832 | */ | ||
833 | int ath5k_hw_beaconq_finish(struct ath5k_hw *ah, unsigned long phys_addr) | ||
834 | { | ||
835 | unsigned int i; | ||
836 | int ret; | ||
837 | |||
838 | ATH5K_TRACE(ah->ah_sc); | ||
839 | |||
840 | /* 5210 doesn't have QCU*/ | ||
841 | if (ah->ah_version == AR5K_AR5210) { | ||
842 | /* | ||
843 | * Wait for beaconn queue to finish by checking | ||
844 | * Control Register and Beacon Status Register. | ||
845 | */ | ||
846 | for (i = AR5K_TUNE_BEACON_INTERVAL / 2; i > 0; i--) { | ||
847 | if (!(ath5k_hw_reg_read(ah, AR5K_BSR) & AR5K_BSR_TXQ1F) | ||
848 | || | ||
849 | !(ath5k_hw_reg_read(ah, AR5K_CR) & AR5K_BSR_TXQ1F)) | ||
850 | break; | ||
851 | udelay(10); | ||
852 | } | ||
853 | |||
854 | /* Timeout... */ | ||
855 | if (i <= 0) { | ||
856 | /* | ||
857 | * Re-schedule the beacon queue | ||
858 | */ | ||
859 | ath5k_hw_reg_write(ah, phys_addr, AR5K_NOQCU_TXDP1); | ||
860 | ath5k_hw_reg_write(ah, AR5K_BCR_TQ1V | AR5K_BCR_BDMAE, | ||
861 | AR5K_BCR); | ||
862 | |||
863 | return -EIO; | ||
864 | } | ||
865 | ret = 0; | ||
866 | } else { | ||
867 | /*5211/5212*/ | ||
868 | ret = ath5k_hw_register_timeout(ah, | ||
869 | AR5K_QUEUE_STATUS(AR5K_TX_QUEUE_ID_BEACON), | ||
870 | AR5K_QCU_STS_FRMPENDCNT, 0, false); | ||
871 | |||
872 | if (AR5K_REG_READ_Q(ah, AR5K_QCU_TXE, AR5K_TX_QUEUE_ID_BEACON)) | ||
873 | return -EIO; | ||
874 | } | ||
875 | |||
876 | return ret; | ||
877 | } | ||
878 | #endif | ||
879 | |||
880 | |||
881 | /*********************\ | ||
882 | * Key table functions * | ||
883 | \*********************/ | ||
884 | |||
885 | /* | ||
886 | * Reset a key entry on the table | ||
887 | */ | ||
888 | int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry) | ||
889 | { | ||
890 | unsigned int i; | ||
891 | |||
892 | ATH5K_TRACE(ah->ah_sc); | ||
893 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | ||
894 | |||
895 | for (i = 0; i < AR5K_KEYCACHE_SIZE; i++) | ||
896 | ath5k_hw_reg_write(ah, 0, AR5K_KEYTABLE_OFF(entry, i)); | ||
897 | |||
898 | /* | ||
899 | * Set NULL encryption on AR5212+ | ||
900 | * | ||
901 | * Note: AR5K_KEYTABLE_TYPE -> AR5K_KEYTABLE_OFF(entry, 5) | ||
902 | * AR5K_KEYTABLE_TYPE_NULL -> 0x00000007 | ||
903 | * | ||
904 | * Note2: Windows driver (ndiswrapper) sets this to | ||
905 | * 0x00000714 instead of 0x00000007 | ||
906 | */ | ||
907 | if (ah->ah_version > AR5K_AR5211) | ||
908 | ath5k_hw_reg_write(ah, AR5K_KEYTABLE_TYPE_NULL, | ||
909 | AR5K_KEYTABLE_TYPE(entry)); | ||
910 | |||
911 | return 0; | ||
912 | } | ||
913 | |||
914 | /* | ||
915 | * Check if a table entry is valid | ||
916 | */ | ||
917 | int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry) | ||
918 | { | ||
919 | ATH5K_TRACE(ah->ah_sc); | ||
920 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | ||
921 | |||
922 | /* Check the validation flag at the end of the entry */ | ||
923 | return ath5k_hw_reg_read(ah, AR5K_KEYTABLE_MAC1(entry)) & | ||
924 | AR5K_KEYTABLE_VALID; | ||
925 | } | ||
926 | |||
927 | /* | ||
928 | * Set a key entry on the table | ||
929 | */ | ||
930 | int ath5k_hw_set_key(struct ath5k_hw *ah, u16 entry, | ||
931 | const struct ieee80211_key_conf *key, const u8 *mac) | ||
932 | { | ||
933 | unsigned int i; | ||
934 | __le32 key_v[5] = {}; | ||
935 | u32 keytype; | ||
936 | |||
937 | ATH5K_TRACE(ah->ah_sc); | ||
938 | |||
939 | /* key->keylen comes in from mac80211 in bytes */ | ||
940 | |||
941 | if (key->keylen > AR5K_KEYTABLE_SIZE / 8) | ||
942 | return -EOPNOTSUPP; | ||
943 | |||
944 | switch (key->keylen) { | ||
945 | /* WEP 40-bit = 40-bit entered key + 24 bit IV = 64-bit */ | ||
946 | case 40 / 8: | ||
947 | memcpy(&key_v[0], key->key, 5); | ||
948 | keytype = AR5K_KEYTABLE_TYPE_40; | ||
949 | break; | ||
950 | |||
951 | /* WEP 104-bit = 104-bit entered key + 24-bit IV = 128-bit */ | ||
952 | case 104 / 8: | ||
953 | memcpy(&key_v[0], &key->key[0], 6); | ||
954 | memcpy(&key_v[2], &key->key[6], 6); | ||
955 | memcpy(&key_v[4], &key->key[12], 1); | ||
956 | keytype = AR5K_KEYTABLE_TYPE_104; | ||
957 | break; | ||
958 | /* WEP 128-bit = 128-bit entered key + 24 bit IV = 152-bit */ | ||
959 | case 128 / 8: | ||
960 | memcpy(&key_v[0], &key->key[0], 6); | ||
961 | memcpy(&key_v[2], &key->key[6], 6); | ||
962 | memcpy(&key_v[4], &key->key[12], 4); | ||
963 | keytype = AR5K_KEYTABLE_TYPE_128; | ||
964 | break; | ||
965 | |||
966 | default: | ||
967 | return -EINVAL; /* shouldn't happen */ | ||
968 | } | ||
969 | |||
970 | for (i = 0; i < ARRAY_SIZE(key_v); i++) | ||
971 | ath5k_hw_reg_write(ah, le32_to_cpu(key_v[i]), | ||
972 | AR5K_KEYTABLE_OFF(entry, i)); | ||
973 | |||
974 | ath5k_hw_reg_write(ah, keytype, AR5K_KEYTABLE_TYPE(entry)); | ||
975 | |||
976 | return ath5k_hw_set_key_lladdr(ah, entry, mac); | ||
977 | } | ||
978 | |||
979 | int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) | ||
980 | { | ||
981 | u32 low_id, high_id; | ||
982 | |||
983 | ATH5K_TRACE(ah->ah_sc); | ||
984 | /* Invalid entry (key table overflow) */ | ||
985 | AR5K_ASSERT_ENTRY(entry, AR5K_KEYTABLE_SIZE); | ||
986 | |||
987 | /* MAC may be NULL if it's a broadcast key. In this case no need to | ||
988 | * to compute AR5K_LOW_ID and AR5K_HIGH_ID as we already know it. */ | ||
989 | if (unlikely(mac == NULL)) { | ||
990 | low_id = 0xffffffff; | ||
991 | high_id = 0xffff | AR5K_KEYTABLE_VALID; | ||
992 | } else { | ||
993 | low_id = AR5K_LOW_ID(mac); | ||
994 | high_id = AR5K_HIGH_ID(mac) | AR5K_KEYTABLE_VALID; | ||
995 | } | ||
996 | |||
997 | ath5k_hw_reg_write(ah, low_id, AR5K_KEYTABLE_MAC0(entry)); | ||
998 | ath5k_hw_reg_write(ah, high_id, AR5K_KEYTABLE_MAC1(entry)); | ||
999 | |||
1000 | return 0; | ||
1001 | } | ||
1002 | |||