1 | /* |
2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
3 | * Copyright (c) 2002-2008 Atheros Communications, Inc. |
4 | * |
5 | * Permission to use, copy, modify, and/or distribute this software for any |
6 | * purpose with or without fee is hereby granted, provided that the above |
7 | * copyright notice and this permission notice appear in all copies. |
8 | * |
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 | * |
17 | * $Id: ah.c,v 1.3 2011/03/07 11:25:42 cegger Exp $ |
18 | */ |
19 | #include "opt_ah.h" |
20 | |
21 | #include "ah.h" |
22 | #include "ah_internal.h" |
23 | #include "ah_devid.h" |
24 | |
25 | /* linker set of registered chips */ |
26 | OS_SET_DECLARE(ah_chips, struct ath_hal_chip); |
27 | |
28 | /* |
29 | * Check the set of registered chips to see if any recognize |
30 | * the device as one they can support. |
31 | */ |
32 | const char* |
33 | ath_hal_probe(uint16_t vendorid, uint16_t devid) |
34 | { |
35 | struct ath_hal_chip * const *pchip; |
36 | |
37 | OS_SET_FOREACH(pchip, ah_chips) { |
38 | const char *name = (*pchip)->probe(vendorid, devid); |
39 | if (name != AH_NULL) |
40 | return name; |
41 | } |
42 | return AH_NULL; |
43 | } |
44 | |
45 | /* |
46 | * Attach detects device chip revisions, initializes the hwLayer |
47 | * function list, reads EEPROM information, |
48 | * selects reset vectors, and performs a short self test. |
49 | * Any failures will return an error that should cause a hardware |
50 | * disable. |
51 | */ |
52 | struct ath_hal* |
53 | ath_hal_attach(uint16_t devid, HAL_SOFTC sc, |
54 | HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *error) |
55 | { |
56 | struct ath_hal_chip * const *pchip; |
57 | |
58 | OS_SET_FOREACH(pchip, ah_chips) { |
59 | struct ath_hal_chip *chip = *pchip; |
60 | struct ath_hal *ah; |
61 | |
62 | /* XXX don't have vendorid, assume atheros one works */ |
63 | if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) |
64 | continue; |
65 | ah = chip->attach(devid, sc, st, sh, error); |
66 | if (ah != AH_NULL) { |
67 | /* copy back private state to public area */ |
68 | ah->ah_devid = AH_PRIVATE(ah)->ah_devid; |
69 | ah->ah_subvendorid = AH_PRIVATE(ah)->ah_subvendorid; |
70 | ah->ah_macVersion = AH_PRIVATE(ah)->ah_macVersion; |
71 | ah->ah_macRev = AH_PRIVATE(ah)->ah_macRev; |
72 | ah->ah_phyRev = AH_PRIVATE(ah)->ah_phyRev; |
73 | ah->ah_analog5GhzRev = AH_PRIVATE(ah)->ah_analog5GhzRev; |
74 | ah->ah_analog2GhzRev = AH_PRIVATE(ah)->ah_analog2GhzRev; |
75 | return ah; |
76 | } |
77 | } |
78 | return AH_NULL; |
79 | } |
80 | |
81 | /* linker set of registered RF backends */ |
82 | OS_SET_DECLARE(ah_rfs, struct ath_hal_rf); |
83 | |
84 | /* |
85 | * Check the set of registered RF backends to see if |
86 | * any recognize the device as one they can support. |
87 | */ |
88 | struct ath_hal_rf * |
89 | ath_hal_rfprobe(struct ath_hal *ah, HAL_STATUS *ecode) |
90 | { |
91 | #ifdef AH_HAS_RF |
92 | struct ath_hal_rf * const *prf; |
93 | |
94 | OS_SET_FOREACH(prf, ah_rfs) { |
95 | struct ath_hal_rf *rf = *prf; |
96 | if (rf->probe(ah)) |
97 | return rf; |
98 | } |
99 | *ecode = HAL_ENOTSUPP; |
100 | #endif |
101 | return AH_NULL; |
102 | } |
103 | |
104 | /* |
105 | * Poll the register looking for a specific value. |
106 | */ |
107 | HAL_BOOL |
108 | ath_hal_wait(struct ath_hal *ah, u_int reg, uint32_t mask, uint32_t val) |
109 | { |
110 | #define AH_TIMEOUT 1000 |
111 | int i; |
112 | |
113 | for (i = 0; i < AH_TIMEOUT; i++) { |
114 | if ((OS_REG_READ(ah, reg) & mask) == val) |
115 | return AH_TRUE; |
116 | OS_DELAY(10); |
117 | } |
118 | HALDEBUG(ah, HAL_DEBUG_REGIO | HAL_DEBUG_PHYIO, |
119 | "%s: timeout on reg 0x%x: 0x%08x & 0x%08x != 0x%08x\n" , |
120 | __func__, reg, OS_REG_READ(ah, reg), mask, val); |
121 | return AH_FALSE; |
122 | #undef AH_TIMEOUT |
123 | } |
124 | |
125 | /* |
126 | * Reverse the bits starting at the low bit for a value of |
127 | * bit_count in size |
128 | */ |
129 | uint32_t |
130 | ath_hal_reverseBits(uint32_t val, uint32_t n) |
131 | { |
132 | uint32_t retval; |
133 | int i; |
134 | |
135 | for (i = 0, retval = 0; i < n; i++) { |
136 | retval = (retval << 1) | (val & 1); |
137 | val >>= 1; |
138 | } |
139 | return retval; |
140 | } |
141 | |
142 | /* |
143 | * Compute the time to transmit a frame of length frameLen bytes |
144 | * using the specified rate, phy, and short preamble setting. |
145 | */ |
146 | uint16_t |
147 | ath_hal_computetxtime(struct ath_hal *ah, |
148 | const HAL_RATE_TABLE *rates, uint32_t frameLen, uint16_t rateix, |
149 | HAL_BOOL shortPreamble) |
150 | { |
151 | uint32_t bitsPerSymbol, numBits, numSymbols, phyTime, txTime; |
152 | uint32_t kbps; |
153 | |
154 | kbps = rates->info[rateix].rateKbps; |
155 | /* |
156 | * index can be invalid duting dynamic Turbo transitions. |
157 | */ |
158 | if(kbps == 0) return 0; |
159 | switch (rates->info[rateix].phy) { |
160 | |
161 | case IEEE80211_T_CCK: |
162 | #define CCK_SIFS_TIME 10 |
163 | #define CCK_PREAMBLE_BITS 144 |
164 | #define CCK_PLCP_BITS 48 |
165 | phyTime = CCK_PREAMBLE_BITS + CCK_PLCP_BITS; |
166 | if (shortPreamble && rates->info[rateix].shortPreamble) |
167 | phyTime >>= 1; |
168 | numBits = frameLen << 3; |
169 | txTime = CCK_SIFS_TIME + phyTime |
170 | + ((numBits * 1000)/kbps); |
171 | break; |
172 | #undef CCK_SIFS_TIME |
173 | #undef CCK_PREAMBLE_BITS |
174 | #undef CCK_PLCP_BITS |
175 | |
176 | case IEEE80211_T_OFDM: |
177 | #define OFDM_SIFS_TIME 16 |
178 | #define OFDM_PREAMBLE_TIME 20 |
179 | #define OFDM_PLCP_BITS 22 |
180 | #define OFDM_SYMBOL_TIME 4 |
181 | |
182 | #define OFDM_SIFS_TIME_HALF 32 |
183 | #define OFDM_PREAMBLE_TIME_HALF 40 |
184 | #define OFDM_PLCP_BITS_HALF 22 |
185 | #define OFDM_SYMBOL_TIME_HALF 8 |
186 | |
187 | #define OFDM_SIFS_TIME_QUARTER 64 |
188 | #define OFDM_PREAMBLE_TIME_QUARTER 80 |
189 | #define OFDM_PLCP_BITS_QUARTER 22 |
190 | #define OFDM_SYMBOL_TIME_QUARTER 16 |
191 | |
192 | if (AH_PRIVATE(ah)->ah_curchan && |
193 | IS_CHAN_QUARTER_RATE(AH_PRIVATE(ah)->ah_curchan)) { |
194 | bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_QUARTER) / 1000; |
195 | HALASSERT(bitsPerSymbol != 0); |
196 | |
197 | numBits = OFDM_PLCP_BITS + (frameLen << 3); |
198 | numSymbols = howmany(numBits, bitsPerSymbol); |
199 | txTime = OFDM_SIFS_TIME_QUARTER |
200 | + OFDM_PREAMBLE_TIME_QUARTER |
201 | + (numSymbols * OFDM_SYMBOL_TIME_QUARTER); |
202 | } else if (AH_PRIVATE(ah)->ah_curchan && |
203 | IS_CHAN_HALF_RATE(AH_PRIVATE(ah)->ah_curchan)) { |
204 | bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME_HALF) / 1000; |
205 | HALASSERT(bitsPerSymbol != 0); |
206 | |
207 | numBits = OFDM_PLCP_BITS + (frameLen << 3); |
208 | numSymbols = howmany(numBits, bitsPerSymbol); |
209 | txTime = OFDM_SIFS_TIME_HALF + |
210 | OFDM_PREAMBLE_TIME_HALF |
211 | + (numSymbols * OFDM_SYMBOL_TIME_HALF); |
212 | } else { /* full rate channel */ |
213 | bitsPerSymbol = (kbps * OFDM_SYMBOL_TIME) / 1000; |
214 | HALASSERT(bitsPerSymbol != 0); |
215 | |
216 | numBits = OFDM_PLCP_BITS + (frameLen << 3); |
217 | numSymbols = howmany(numBits, bitsPerSymbol); |
218 | txTime = OFDM_SIFS_TIME + OFDM_PREAMBLE_TIME |
219 | + (numSymbols * OFDM_SYMBOL_TIME); |
220 | } |
221 | break; |
222 | |
223 | #undef OFDM_SIFS_TIME |
224 | #undef OFDM_PREAMBLE_TIME |
225 | #undef OFDM_PLCP_BITS |
226 | #undef OFDM_SYMBOL_TIME |
227 | |
228 | case IEEE80211_T_TURBO: |
229 | #define TURBO_SIFS_TIME 8 |
230 | #define TURBO_PREAMBLE_TIME 14 |
231 | #define TURBO_PLCP_BITS 22 |
232 | #define TURBO_SYMBOL_TIME 4 |
233 | /* we still save OFDM rates in kbps - so double them */ |
234 | bitsPerSymbol = ((kbps << 1) * TURBO_SYMBOL_TIME) / 1000; |
235 | HALASSERT(bitsPerSymbol != 0); |
236 | |
237 | numBits = TURBO_PLCP_BITS + (frameLen << 3); |
238 | numSymbols = howmany(numBits, bitsPerSymbol); |
239 | txTime = TURBO_SIFS_TIME + TURBO_PREAMBLE_TIME |
240 | + (numSymbols * TURBO_SYMBOL_TIME); |
241 | break; |
242 | #undef TURBO_SIFS_TIME |
243 | #undef TURBO_PREAMBLE_TIME |
244 | #undef TURBO_PLCP_BITS |
245 | #undef TURBO_SYMBOL_TIME |
246 | |
247 | default: |
248 | HALDEBUG(ah, HAL_DEBUG_PHYIO, |
249 | "%s: unknown phy %u (rate ix %u)\n" , |
250 | __func__, rates->info[rateix].phy, rateix); |
251 | txTime = 0; |
252 | break; |
253 | } |
254 | return txTime; |
255 | } |
256 | |
257 | static __inline int |
258 | mapgsm(u_int freq, u_int flags) |
259 | { |
260 | freq *= 10; |
261 | if (flags & CHANNEL_QUARTER) |
262 | freq += 5; |
263 | else if (flags & CHANNEL_HALF) |
264 | freq += 10; |
265 | else |
266 | freq += 20; |
267 | return (freq - 24220) / 5; |
268 | } |
269 | |
270 | static __inline int |
271 | mappsb(u_int freq, u_int flags) |
272 | { |
273 | return ((freq * 10) + (((freq % 5) == 2) ? 5 : 0) - 49400) / 5; |
274 | } |
275 | |
276 | /* |
277 | * Convert GHz frequency to IEEE channel number. |
278 | */ |
279 | int |
280 | ath_hal_mhz2ieee(struct ath_hal *ah, u_int freq, u_int flags) |
281 | { |
282 | if (flags & CHANNEL_2GHZ) { /* 2GHz band */ |
283 | if (freq == 2484) |
284 | return 14; |
285 | if (freq < 2484) { |
286 | if (ath_hal_isgsmsku(ah)) |
287 | return mapgsm(freq, flags); |
288 | return ((int)freq - 2407) / 5; |
289 | } else |
290 | return 15 + ((freq - 2512) / 20); |
291 | } else if (flags & CHANNEL_5GHZ) {/* 5Ghz band */ |
292 | if (ath_hal_ispublicsafetysku(ah) && |
293 | IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) { |
294 | return mappsb(freq, flags); |
295 | } else if ((flags & CHANNEL_A) && (freq <= 5000)) { |
296 | return (freq - 4000) / 5; |
297 | } else { |
298 | return (freq - 5000) / 5; |
299 | } |
300 | } else { /* either, guess */ |
301 | if (freq == 2484) |
302 | return 14; |
303 | if (freq < 2484) { |
304 | if (ath_hal_isgsmsku(ah)) |
305 | return mapgsm(freq, flags); |
306 | return ((int)freq - 2407) / 5; |
307 | } |
308 | if (freq < 5000) { |
309 | if (ath_hal_ispublicsafetysku(ah) && |
310 | IS_CHAN_IN_PUBLIC_SAFETY_BAND(freq)) { |
311 | return mappsb(freq, flags); |
312 | } else if (freq > 4900) { |
313 | return (freq - 4000) / 5; |
314 | } else { |
315 | return 15 + ((freq - 2512) / 20); |
316 | } |
317 | } |
318 | return (freq - 5000) / 5; |
319 | } |
320 | } |
321 | |
322 | typedef enum { |
323 | WIRELESS_MODE_11a = 0, |
324 | WIRELESS_MODE_TURBO = 1, |
325 | WIRELESS_MODE_11b = 2, |
326 | WIRELESS_MODE_11g = 3, |
327 | WIRELESS_MODE_108g = 4, |
328 | |
329 | WIRELESS_MODE_MAX |
330 | } WIRELESS_MODE; |
331 | |
332 | static WIRELESS_MODE |
333 | ath_hal_chan2wmode(struct ath_hal *ah, const HAL_CHANNEL *chan) |
334 | { |
335 | if (IS_CHAN_CCK(chan)) |
336 | return WIRELESS_MODE_11b; |
337 | if (IS_CHAN_G(chan)) |
338 | return WIRELESS_MODE_11g; |
339 | if (IS_CHAN_108G(chan)) |
340 | return WIRELESS_MODE_108g; |
341 | if (IS_CHAN_TURBO(chan)) |
342 | return WIRELESS_MODE_TURBO; |
343 | return WIRELESS_MODE_11a; |
344 | } |
345 | |
346 | /* |
347 | * Convert between microseconds and core system clocks. |
348 | */ |
349 | /* 11a Turbo 11b 11g 108g */ |
350 | static const uint8_t CLOCK_RATE[] = { 40, 80, 22, 44, 88 }; |
351 | |
352 | u_int |
353 | ath_hal_mac_clks(struct ath_hal *ah, u_int usecs) |
354 | { |
355 | const HAL_CHANNEL *c = (const HAL_CHANNEL *) AH_PRIVATE(ah)->ah_curchan; |
356 | u_int clks; |
357 | |
358 | /* NB: ah_curchan may be null when called attach time */ |
359 | if (c != AH_NULL) { |
360 | clks = usecs * CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; |
361 | if (IS_CHAN_HT40(c)) |
362 | clks <<= 1; |
363 | else if (IS_CHAN_HALF_RATE(c)) |
364 | clks >>= 1; |
365 | else if (IS_CHAN_QUARTER_RATE(c)) |
366 | clks >>= 2; |
367 | } else |
368 | clks = usecs * CLOCK_RATE[WIRELESS_MODE_11b]; |
369 | return clks; |
370 | } |
371 | |
372 | u_int |
373 | ath_hal_mac_usec(struct ath_hal *ah, u_int clks) |
374 | { |
375 | const HAL_CHANNEL *c = (const HAL_CHANNEL *) AH_PRIVATE(ah)->ah_curchan; |
376 | u_int usec; |
377 | |
378 | /* NB: ah_curchan may be null when called attach time */ |
379 | if (c != AH_NULL) { |
380 | usec = clks / CLOCK_RATE[ath_hal_chan2wmode(ah, c)]; |
381 | if (IS_CHAN_HT40(c)) |
382 | usec >>= 1; |
383 | else if (IS_CHAN_HALF_RATE(c)) |
384 | usec <<= 1; |
385 | else if (IS_CHAN_QUARTER_RATE(c)) |
386 | usec <<= 2; |
387 | } else |
388 | usec = clks / CLOCK_RATE[WIRELESS_MODE_11b]; |
389 | return usec; |
390 | } |
391 | |
392 | /* |
393 | * Setup a h/w rate table's reverse lookup table and |
394 | * fill in ack durations. This routine is called for |
395 | * each rate table returned through the ah_getRateTable |
396 | * method. The reverse lookup tables are assumed to be |
397 | * initialized to zero (or at least the first entry). |
398 | * We use this as a key that indicates whether or not |
399 | * we've previously setup the reverse lookup table. |
400 | * |
401 | * XXX not reentrant, but shouldn't matter |
402 | */ |
403 | void |
404 | ath_hal_setupratetable(struct ath_hal *ah, HAL_RATE_TABLE *rt) |
405 | { |
406 | #define N(a) (sizeof(a)/sizeof(a[0])) |
407 | int i; |
408 | |
409 | if (rt->rateCodeToIndex[0] != 0) /* already setup */ |
410 | return; |
411 | for (i = 0; i < N(rt->rateCodeToIndex); i++) |
412 | rt->rateCodeToIndex[i] = (uint8_t) -1; |
413 | for (i = 0; i < rt->rateCount; i++) { |
414 | uint8_t code = rt->info[i].rateCode; |
415 | uint8_t cix = rt->info[i].controlRate; |
416 | |
417 | HALASSERT(code < N(rt->rateCodeToIndex)); |
418 | rt->rateCodeToIndex[code] = i; |
419 | HALASSERT((code | rt->info[i].shortPreamble) < |
420 | N(rt->rateCodeToIndex)); |
421 | rt->rateCodeToIndex[code | rt->info[i].shortPreamble] = i; |
422 | /* |
423 | * XXX for 11g the control rate to use for 5.5 and 11 Mb/s |
424 | * depends on whether they are marked as basic rates; |
425 | * the static tables are setup with an 11b-compatible |
426 | * 2Mb/s rate which will work but is suboptimal |
427 | */ |
428 | rt->info[i].lpAckDuration = ath_hal_computetxtime(ah, rt, |
429 | WLAN_CTRL_FRAME_SIZE, cix, AH_FALSE); |
430 | rt->info[i].spAckDuration = ath_hal_computetxtime(ah, rt, |
431 | WLAN_CTRL_FRAME_SIZE, cix, AH_TRUE); |
432 | } |
433 | #undef N |
434 | } |
435 | |
436 | HAL_STATUS |
437 | ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, |
438 | uint32_t capability, uint32_t *result) |
439 | { |
440 | const HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; |
441 | |
442 | switch (type) { |
443 | case HAL_CAP_REG_DMN: /* regulatory domain */ |
444 | *result = AH_PRIVATE(ah)->ah_currentRD; |
445 | return HAL_OK; |
446 | case HAL_CAP_CIPHER: /* cipher handled in hardware */ |
447 | case HAL_CAP_TKIP_MIC: /* handle TKIP MIC in hardware */ |
448 | return HAL_ENOTSUPP; |
449 | case HAL_CAP_TKIP_SPLIT: /* hardware TKIP uses split keys */ |
450 | return HAL_ENOTSUPP; |
451 | case HAL_CAP_PHYCOUNTERS: /* hardware PHY error counters */ |
452 | return pCap->halHwPhyCounterSupport ? HAL_OK : HAL_ENXIO; |
453 | case HAL_CAP_WME_TKIPMIC: /* hardware can do TKIP MIC when WMM is turned on */ |
454 | return HAL_ENOTSUPP; |
455 | case HAL_CAP_DIVERSITY: /* hardware supports fast diversity */ |
456 | return HAL_ENOTSUPP; |
457 | case HAL_CAP_KEYCACHE_SIZE: /* hardware key cache size */ |
458 | *result = pCap->halKeyCacheSize; |
459 | return HAL_OK; |
460 | case HAL_CAP_NUM_TXQUEUES: /* number of hardware tx queues */ |
461 | *result = pCap->halTotalQueues; |
462 | return HAL_OK; |
463 | case HAL_CAP_VEOL: /* hardware supports virtual EOL */ |
464 | return pCap->halVEOLSupport ? HAL_OK : HAL_ENOTSUPP; |
465 | case HAL_CAP_PSPOLL: /* hardware PS-Poll support works */ |
466 | return pCap->halPSPollBroken ? HAL_ENOTSUPP : HAL_OK; |
467 | case HAL_CAP_COMPRESSION: |
468 | return pCap->halCompressSupport ? HAL_OK : HAL_ENOTSUPP; |
469 | case HAL_CAP_BURST: |
470 | return pCap->halBurstSupport ? HAL_OK : HAL_ENOTSUPP; |
471 | case HAL_CAP_FASTFRAME: |
472 | return pCap->halFastFramesSupport ? HAL_OK : HAL_ENOTSUPP; |
473 | case HAL_CAP_DIAG: /* hardware diagnostic support */ |
474 | *result = AH_PRIVATE(ah)->ah_diagreg; |
475 | return HAL_OK; |
476 | case HAL_CAP_TXPOW: /* global tx power limit */ |
477 | switch (capability) { |
478 | case 0: /* facility is supported */ |
479 | return HAL_OK; |
480 | case 1: /* current limit */ |
481 | *result = AH_PRIVATE(ah)->ah_powerLimit; |
482 | return HAL_OK; |
483 | case 2: /* current max tx power */ |
484 | *result = AH_PRIVATE(ah)->ah_maxPowerLevel; |
485 | return HAL_OK; |
486 | case 3: /* scale factor */ |
487 | *result = AH_PRIVATE(ah)->ah_tpScale; |
488 | return HAL_OK; |
489 | } |
490 | return HAL_ENOTSUPP; |
491 | case HAL_CAP_BSSIDMASK: /* hardware supports bssid mask */ |
492 | return pCap->halBssIdMaskSupport ? HAL_OK : HAL_ENOTSUPP; |
493 | case HAL_CAP_MCAST_KEYSRCH: /* multicast frame keycache search */ |
494 | return pCap->halMcastKeySrchSupport ? HAL_OK : HAL_ENOTSUPP; |
495 | case HAL_CAP_TSF_ADJUST: /* hardware has beacon tsf adjust */ |
496 | return HAL_ENOTSUPP; |
497 | case HAL_CAP_RFSILENT: /* rfsilent support */ |
498 | switch (capability) { |
499 | case 0: /* facility is supported */ |
500 | return pCap->halRfSilentSupport ? HAL_OK : HAL_ENOTSUPP; |
501 | case 1: /* current setting */ |
502 | return AH_PRIVATE(ah)->ah_rfkillEnabled ? |
503 | HAL_OK : HAL_ENOTSUPP; |
504 | case 2: /* rfsilent config */ |
505 | *result = AH_PRIVATE(ah)->ah_rfsilent; |
506 | return HAL_OK; |
507 | } |
508 | return HAL_ENOTSUPP; |
509 | case HAL_CAP_11D: |
510 | #ifdef AH_SUPPORT_11D |
511 | return HAL_OK; |
512 | #else |
513 | return HAL_ENOTSUPP; |
514 | #endif |
515 | case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ |
516 | return AH_PRIVATE(ah)->ah_rxornIsFatal ? HAL_OK : HAL_ENOTSUPP; |
517 | case HAL_CAP_HT: |
518 | return pCap->halHTSupport ? HAL_OK : HAL_ENOTSUPP; |
519 | case HAL_CAP_TX_CHAINMASK: /* mask of TX chains supported */ |
520 | *result = pCap->halTxChainMask; |
521 | return HAL_OK; |
522 | case HAL_CAP_RX_CHAINMASK: /* mask of RX chains supported */ |
523 | *result = pCap->halRxChainMask; |
524 | return HAL_OK; |
525 | case HAL_CAP_RXTSTAMP_PREC: /* rx desc tstamp precision (bits) */ |
526 | *result = pCap->halTstampPrecision; |
527 | return HAL_OK; |
528 | case HAL_CAP_INTRMASK: /* mask of supported interrupts */ |
529 | *result = pCap->halIntrMask; |
530 | return HAL_OK; |
531 | case HAL_CAP_BSSIDMATCH: /* hardware has disable bssid match */ |
532 | return pCap->halBssidMatchSupport ? HAL_OK : HAL_ENOTSUPP; |
533 | default: |
534 | return HAL_EINVAL; |
535 | } |
536 | } |
537 | |
538 | HAL_BOOL |
539 | ath_hal_setcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, |
540 | uint32_t capability, uint32_t setting, HAL_STATUS *status) |
541 | { |
542 | |
543 | switch (type) { |
544 | case HAL_CAP_TXPOW: |
545 | switch (capability) { |
546 | case 3: |
547 | if (setting <= HAL_TP_SCALE_MIN) { |
548 | AH_PRIVATE(ah)->ah_tpScale = setting; |
549 | return AH_TRUE; |
550 | } |
551 | break; |
552 | } |
553 | break; |
554 | case HAL_CAP_RFSILENT: /* rfsilent support */ |
555 | /* |
556 | * NB: allow even if halRfSilentSupport is false |
557 | * in case the EEPROM is misprogrammed. |
558 | */ |
559 | switch (capability) { |
560 | case 1: /* current setting */ |
561 | AH_PRIVATE(ah)->ah_rfkillEnabled = (setting != 0); |
562 | return AH_TRUE; |
563 | case 2: /* rfsilent config */ |
564 | /* XXX better done per-chip for validation? */ |
565 | AH_PRIVATE(ah)->ah_rfsilent = setting; |
566 | return AH_TRUE; |
567 | } |
568 | break; |
569 | case HAL_CAP_REG_DMN: /* regulatory domain */ |
570 | AH_PRIVATE(ah)->ah_currentRD = setting; |
571 | return AH_TRUE; |
572 | case HAL_CAP_RXORN_FATAL: /* HAL_INT_RXORN treated as fatal */ |
573 | AH_PRIVATE(ah)->ah_rxornIsFatal = setting; |
574 | return AH_TRUE; |
575 | default: |
576 | break; |
577 | } |
578 | if (status) |
579 | *status = HAL_EINVAL; |
580 | return AH_FALSE; |
581 | } |
582 | |
583 | /* |
584 | * Common support for getDiagState method. |
585 | */ |
586 | |
587 | static u_int |
588 | ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs, |
589 | void *dstbuf, int space) |
590 | { |
591 | uint32_t *dp = dstbuf; |
592 | int i; |
593 | |
594 | for (i = 0; space >= 2*sizeof(uint32_t); i++) { |
595 | u_int r = regs[i].start; |
596 | u_int e = regs[i].end; |
597 | *dp++ = (r<<16) | e; |
598 | space -= sizeof(uint32_t); |
599 | do { |
600 | *dp++ = OS_REG_READ(ah, r); |
601 | r += sizeof(uint32_t); |
602 | space -= sizeof(uint32_t); |
603 | } while (r <= e && space >= sizeof(uint32_t)); |
604 | } |
605 | return (char *) dp - (char *) dstbuf; |
606 | } |
607 | |
608 | HAL_BOOL |
609 | ath_hal_getdiagstate(struct ath_hal *ah, int request, |
610 | const void *args, uint32_t argsize, |
611 | void **result, uint32_t *resultsize) |
612 | { |
613 | switch (request) { |
614 | case HAL_DIAG_REVS: |
615 | *result = &AH_PRIVATE(ah)->ah_devid; |
616 | *resultsize = sizeof(HAL_REVS); |
617 | return AH_TRUE; |
618 | case HAL_DIAG_REGS: |
619 | *resultsize = ath_hal_getregdump(ah, args, *result,*resultsize); |
620 | return AH_TRUE; |
621 | case HAL_DIAG_FATALERR: |
622 | *result = &AH_PRIVATE(ah)->ah_fatalState[0]; |
623 | *resultsize = sizeof(AH_PRIVATE(ah)->ah_fatalState); |
624 | return AH_TRUE; |
625 | case HAL_DIAG_EEREAD: |
626 | if (argsize != sizeof(uint16_t)) |
627 | return AH_FALSE; |
628 | if (!ath_hal_eepromRead(ah, *(const uint16_t *)args, *result)) |
629 | return AH_FALSE; |
630 | *resultsize = sizeof(uint16_t); |
631 | return AH_TRUE; |
632 | #ifdef AH_PRIVATE_DIAG |
633 | case HAL_DIAG_SETKEY: { |
634 | const HAL_DIAG_KEYVAL *dk; |
635 | |
636 | if (argsize != sizeof(HAL_DIAG_KEYVAL)) |
637 | return AH_FALSE; |
638 | dk = (const HAL_DIAG_KEYVAL *)args; |
639 | return ah->ah_setKeyCacheEntry(ah, dk->dk_keyix, |
640 | &dk->dk_keyval, dk->dk_mac, dk->dk_xor); |
641 | } |
642 | case HAL_DIAG_RESETKEY: |
643 | if (argsize != sizeof(uint16_t)) |
644 | return AH_FALSE; |
645 | return ah->ah_resetKeyCacheEntry(ah, *(const uint16_t *)args); |
646 | #ifdef AH_SUPPORT_WRITE_EEPROM |
647 | case HAL_DIAG_EEWRITE: { |
648 | const HAL_DIAG_EEVAL *ee; |
649 | if (argsize != sizeof(HAL_DIAG_EEVAL)) |
650 | return AH_FALSE; |
651 | ee = (const HAL_DIAG_EEVAL *)args; |
652 | return ath_hal_eepromWrite(ah, ee->ee_off, ee->ee_data); |
653 | } |
654 | #endif /* AH_SUPPORT_WRITE_EEPROM */ |
655 | #endif /* AH_PRIVATE_DIAG */ |
656 | case HAL_DIAG_11NCOMPAT: |
657 | if (argsize == 0) { |
658 | *resultsize = sizeof(uint32_t); |
659 | *((uint32_t *)(*result)) = |
660 | AH_PRIVATE(ah)->ah_11nCompat; |
661 | } else if (argsize == sizeof(uint32_t)) { |
662 | AH_PRIVATE(ah)->ah_11nCompat = *(const uint32_t *)args; |
663 | } else |
664 | return AH_FALSE; |
665 | return AH_TRUE; |
666 | } |
667 | return AH_FALSE; |
668 | } |
669 | |
670 | /* |
671 | * Set the properties of the tx queue with the parameters |
672 | * from qInfo. |
673 | */ |
674 | HAL_BOOL |
675 | ath_hal_setTxQProps(struct ath_hal *ah, |
676 | HAL_TX_QUEUE_INFO *qi, const HAL_TXQ_INFO *qInfo) |
677 | { |
678 | uint32_t cw; |
679 | |
680 | if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { |
681 | HALDEBUG(ah, HAL_DEBUG_TXQUEUE, |
682 | "%s: inactive queue\n" , __func__); |
683 | return AH_FALSE; |
684 | } |
685 | /* XXX validate parameters */ |
686 | qi->tqi_ver = qInfo->tqi_ver; |
687 | qi->tqi_subtype = qInfo->tqi_subtype; |
688 | qi->tqi_qflags = qInfo->tqi_qflags; |
689 | qi->tqi_priority = qInfo->tqi_priority; |
690 | if (qInfo->tqi_aifs != HAL_TXQ_USEDEFAULT) |
691 | qi->tqi_aifs = AH_MIN(qInfo->tqi_aifs, 255); |
692 | else |
693 | qi->tqi_aifs = INIT_AIFS; |
694 | if (qInfo->tqi_cwmin != HAL_TXQ_USEDEFAULT) { |
695 | cw = AH_MIN(qInfo->tqi_cwmin, 1024); |
696 | /* make sure that the CWmin is of the form (2^n - 1) */ |
697 | qi->tqi_cwmin = 1; |
698 | while (qi->tqi_cwmin < cw) |
699 | qi->tqi_cwmin = (qi->tqi_cwmin << 1) | 1; |
700 | } else |
701 | qi->tqi_cwmin = qInfo->tqi_cwmin; |
702 | if (qInfo->tqi_cwmax != HAL_TXQ_USEDEFAULT) { |
703 | cw = AH_MIN(qInfo->tqi_cwmax, 1024); |
704 | /* make sure that the CWmax is of the form (2^n - 1) */ |
705 | qi->tqi_cwmax = 1; |
706 | while (qi->tqi_cwmax < cw) |
707 | qi->tqi_cwmax = (qi->tqi_cwmax << 1) | 1; |
708 | } else |
709 | qi->tqi_cwmax = INIT_CWMAX; |
710 | /* Set retry limit values */ |
711 | if (qInfo->tqi_shretry != 0) |
712 | qi->tqi_shretry = AH_MIN(qInfo->tqi_shretry, 15); |
713 | else |
714 | qi->tqi_shretry = INIT_SH_RETRY; |
715 | if (qInfo->tqi_lgretry != 0) |
716 | qi->tqi_lgretry = AH_MIN(qInfo->tqi_lgretry, 15); |
717 | else |
718 | qi->tqi_lgretry = INIT_LG_RETRY; |
719 | qi->tqi_cbrPeriod = qInfo->tqi_cbrPeriod; |
720 | qi->tqi_cbrOverflowLimit = qInfo->tqi_cbrOverflowLimit; |
721 | qi->tqi_burstTime = qInfo->tqi_burstTime; |
722 | qi->tqi_readyTime = qInfo->tqi_readyTime; |
723 | |
724 | switch (qInfo->tqi_subtype) { |
725 | case HAL_WME_UPSD: |
726 | if (qi->tqi_type == HAL_TX_QUEUE_DATA) |
727 | qi->tqi_intFlags = HAL_TXQ_USE_LOCKOUT_BKOFF_DIS; |
728 | break; |
729 | default: |
730 | break; /* NB: silence compiler */ |
731 | } |
732 | return AH_TRUE; |
733 | } |
734 | |
735 | HAL_BOOL |
736 | ath_hal_getTxQProps(struct ath_hal *ah, |
737 | HAL_TXQ_INFO *qInfo, const HAL_TX_QUEUE_INFO *qi) |
738 | { |
739 | if (qi->tqi_type == HAL_TX_QUEUE_INACTIVE) { |
740 | HALDEBUG(ah, HAL_DEBUG_TXQUEUE, |
741 | "%s: inactive queue\n" , __func__); |
742 | return AH_FALSE; |
743 | } |
744 | |
745 | qInfo->tqi_qflags = qi->tqi_qflags; |
746 | qInfo->tqi_ver = qi->tqi_ver; |
747 | qInfo->tqi_subtype = qi->tqi_subtype; |
748 | qInfo->tqi_qflags = qi->tqi_qflags; |
749 | qInfo->tqi_priority = qi->tqi_priority; |
750 | qInfo->tqi_aifs = qi->tqi_aifs; |
751 | qInfo->tqi_cwmin = qi->tqi_cwmin; |
752 | qInfo->tqi_cwmax = qi->tqi_cwmax; |
753 | qInfo->tqi_shretry = qi->tqi_shretry; |
754 | qInfo->tqi_lgretry = qi->tqi_lgretry; |
755 | qInfo->tqi_cbrPeriod = qi->tqi_cbrPeriod; |
756 | qInfo->tqi_cbrOverflowLimit = qi->tqi_cbrOverflowLimit; |
757 | qInfo->tqi_burstTime = qi->tqi_burstTime; |
758 | qInfo->tqi_readyTime = qi->tqi_readyTime; |
759 | return AH_TRUE; |
760 | } |
761 | |
762 | /* 11a Turbo 11b 11g 108g */ |
763 | static const int16_t NOISE_FLOOR[] = { -96, -93, -98, -96, -93 }; |
764 | |
765 | /* |
766 | * Read the current channel noise floor and return. |
767 | * If nf cal hasn't finished, channel noise floor should be 0 |
768 | * and we return a nominal value based on band and frequency. |
769 | * |
770 | * NB: This is a private routine used by per-chip code to |
771 | * implement the ah_getChanNoise method. |
772 | */ |
773 | int16_t |
774 | ath_hal_getChanNoise(struct ath_hal *ah, HAL_CHANNEL *chan) |
775 | { |
776 | HAL_CHANNEL_INTERNAL *ichan; |
777 | |
778 | ichan = ath_hal_checkchannel(ah, chan); |
779 | if (ichan == AH_NULL) { |
780 | HALDEBUG(ah, HAL_DEBUG_NFCAL, |
781 | "%s: invalid channel %u/0x%x; no mapping\n" , |
782 | __func__, chan->channel, chan->channelFlags); |
783 | return 0; |
784 | } |
785 | if (ichan->rawNoiseFloor == 0) { |
786 | WIRELESS_MODE mode = ath_hal_chan2wmode(ah, chan); |
787 | |
788 | HALASSERT(mode < WIRELESS_MODE_MAX); |
789 | return NOISE_FLOOR[mode] + ath_hal_getNfAdjust(ah, ichan); |
790 | } else |
791 | return ichan->rawNoiseFloor + ichan->noiseFloorAdjust; |
792 | } |
793 | |
794 | /* |
795 | * Process all valid raw noise floors into the dBm noise floor values. |
796 | * Though our device has no reference for a dBm noise floor, we perform |
797 | * a relative minimization of NF's based on the lowest NF found across a |
798 | * channel scan. |
799 | */ |
800 | void |
801 | ath_hal_process_noisefloor(struct ath_hal *ah) |
802 | { |
803 | HAL_CHANNEL_INTERNAL *c; |
804 | int16_t correct2, correct5; |
805 | int16_t lowest2, lowest5; |
806 | int i; |
807 | |
808 | /* |
809 | * Find the lowest 2GHz and 5GHz noise floor values after adjusting |
810 | * for statistically recorded NF/channel deviation. |
811 | */ |
812 | correct2 = lowest2 = 0; |
813 | correct5 = lowest5 = 0; |
814 | for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { |
815 | WIRELESS_MODE mode; |
816 | int16_t nf; |
817 | |
818 | c = &AH_PRIVATE(ah)->ah_channels[i]; |
819 | if (c->rawNoiseFloor >= 0) |
820 | continue; |
821 | mode = ath_hal_chan2wmode(ah, (HAL_CHANNEL *) c); |
822 | HALASSERT(mode < WIRELESS_MODE_MAX); |
823 | nf = c->rawNoiseFloor + NOISE_FLOOR[mode] + |
824 | ath_hal_getNfAdjust(ah, c); |
825 | if (IS_CHAN_5GHZ(c)) { |
826 | if (nf < lowest5) { |
827 | lowest5 = nf; |
828 | correct5 = NOISE_FLOOR[mode] - |
829 | (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); |
830 | } |
831 | } else { |
832 | if (nf < lowest2) { |
833 | lowest2 = nf; |
834 | correct2 = NOISE_FLOOR[mode] - |
835 | (c->rawNoiseFloor + ath_hal_getNfAdjust(ah, c)); |
836 | } |
837 | } |
838 | } |
839 | |
840 | /* Correct the channels to reach the expected NF value */ |
841 | for (i = 0; i < AH_PRIVATE(ah)->ah_nchan; i++) { |
842 | c = &AH_PRIVATE(ah)->ah_channels[i]; |
843 | if (c->rawNoiseFloor >= 0) |
844 | continue; |
845 | /* Apply correction factor */ |
846 | c->noiseFloorAdjust = ath_hal_getNfAdjust(ah, c) + |
847 | (IS_CHAN_5GHZ(c) ? correct5 : correct2); |
848 | HALDEBUG(ah, HAL_DEBUG_NFCAL, "%u/0x%x raw nf %d adjust %d\n" , |
849 | c->channel, c->channelFlags, c->rawNoiseFloor, |
850 | c->noiseFloorAdjust); |
851 | } |
852 | } |
853 | |
854 | /* |
855 | * INI support routines. |
856 | */ |
857 | |
858 | int |
859 | ath_hal_ini_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, |
860 | int col, int regWr) |
861 | { |
862 | int r; |
863 | |
864 | for (r = 0; r < ia->rows; r++) { |
865 | OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), |
866 | HAL_INI_VAL(ia, r, col)); |
867 | DMA_YIELD(regWr); |
868 | } |
869 | return regWr; |
870 | } |
871 | |
872 | void |
873 | ath_hal_ini_bank_setup(uint32_t data[], const HAL_INI_ARRAY *ia, int col) |
874 | { |
875 | int r; |
876 | |
877 | for (r = 0; r < ia->rows; r++) |
878 | data[r] = HAL_INI_VAL(ia, r, col); |
879 | } |
880 | |
881 | int |
882 | ath_hal_ini_bank_write(struct ath_hal *ah, const HAL_INI_ARRAY *ia, |
883 | const uint32_t data[], int regWr) |
884 | { |
885 | int r; |
886 | |
887 | for (r = 0; r < ia->rows; r++) { |
888 | OS_REG_WRITE(ah, HAL_INI_VAL(ia, r, 0), data[r]); |
889 | DMA_YIELD(regWr); |
890 | } |
891 | return regWr; |
892 | } |
893 | |