HAL Usage Notes
Disclaimer: these are notes started in an attempt to record tidbits of information about the HAL that are not immediately obvious from the header files. Many of these tidbits are "informed guesses" and you should treat everything you read here with a grain of salt. Take that back, you should treat everything you read here as incorrect until you verify it! If you do see something incorrect it would be great if you could correct it! THANKS!
HAL device member functions
These functions are members of the ath_hal device structure defined in hal/ah.h.
Reset functions
516 HAL_BOOL __ahdecl(*ah_reset)(struct ath_hal *, HAL_OPMODE, 517 HAL_CHANNEL *, HAL_BOOL bChannelChange, 518 HAL_STATUS *status);
ah_reset resets the chip and defines the new operation mode and channel. It needs to be called to change the channel, i.e. there is not other general way to change operation to a different channel. Rumour is that before calling ah_reset one has to call ath_hal_init_channels, or else the reset doesn't occur properly.
519 HAL_BOOL __ahdecl(*ah_phyDisable)(struct ath_hal *); 520 void __ahdecl(*ah_setPCUConfig)(struct ath_hal *); 521 HAL_BOOL __ahdecl(*ah_perCalibration)(struct ath_hal*, HAL_CHANNEL *);
ah_perCalibration performs a periodic calibration of the RF part. This must be called periodically by the driver.
522 HAL_BOOL __ahdecl(*ah_setTxPowerLimit)(struct ath_hal *, u_int32_t);
ah_setTxPowerLimit sets the overall (max?) power limit of the device. The integer power limit parameter is specified in 0.5dBm steps. Thus it appears that a value of 20 would represent 10dBm. Also note that ah_setupTxDesc has a txPower parameter, so transmit power can be set on a per-packet basis. It appears that ah_setTxPowerLimit is used to limit the overall device power and the ah_setupTxDesc is used to set the per-ath%d device power limit.
530 HAL_BOOL __ahdecl(*ah_radarWait)(struct ath_hal *, HAL_CHANNEL *);
For 802.11a channels in regulatory domains where DFS rules apply, this function is used to retrieve the status of DFS handling from the HAL. The function populates the HAL_CHANNEL structure passed in. The updated channelFlags and privFlags values should indicate whether the channel requires DFS (CHANNEL_DFS flag) and whether it has been through the channel availability check process (CHANNEL_DFS_CLEAR flag). The madwifi driver should wait to transmit until this has happened. With our current HAL, however, it seems that the CHANNEL_DFS flags are returned incorrectly for the US (countrycode 840) and that for ETSI countries where it does know that DFS is required, it seems to always return CHANNEL_DFS_CLEAR immediately. Note: I have yet to see the CHANNEL_DFS flag without CHANNEL_DFS_CLEAR already having been set. [mtaylor]
Transmit functions
533 HAL_BOOL __ahdecl(*ah_updateTxTrigLevel)(struct ath_hal*, 534 HAL_BOOL incTrigLevel); 535 int __ahdecl(*ah_setupTxQueue)(struct ath_hal *, HAL_TX_QUEUE, 536 const HAL_TXQ_INFO *qInfo); 537 HAL_BOOL __ahdecl(*ah_setTxQueueProps)(struct ath_hal *, int q, 538 const HAL_TXQ_INFO *qInfo); 539 HAL_BOOL __ahdecl(*ah_getTxQueueProps)(struct ath_hal *, int q, 540 HAL_TXQ_INFO *qInfo); 541 HAL_BOOL __ahdecl(*ah_releaseTxQueue)(struct ath_hal *ah, u_int q); 542 HAL_BOOL __ahdecl(*ah_resetTxQueue)(struct ath_hal *ah, u_int q); 543 u_int32_t __ahdecl(*ah_getTxDP)(struct ath_hal*, u_int); 544 HAL_BOOL __ahdecl(*ah_setTxDP)(struct ath_hal*, u_int, u_int32_t txdp); 545 u_int32_t __ahdecl(*ah_numTxPending)(struct ath_hal *, u_int q); 546 HAL_BOOL __ahdecl(*ah_startTxDma)(struct ath_hal*, u_int); 547 HAL_BOOL __ahdecl(*ah_stopTxDma)(struct ath_hal*, u_int); 548 HAL_BOOL __ahdecl(*ah_updateCTSForBursting)(struct ath_hal *, 549 struct ath_desc *, struct ath_desc *, 550 struct ath_desc *, struct ath_desc *, 551 u_int32_t, u_int32_t); 552 HAL_BOOL __ahdecl(*ah_setupTxDesc)(struct ath_hal *, struct ath_desc *, 553 u_int pktLen, u_int hdrLen, 554 HAL_PKT_TYPE type, u_int txPower, 555 u_int txRate0, u_int txTries0, 556 u_int keyIx, u_int antMode, u_int flags, 557 u_int rtsctsRate, u_int rtsctsDuration, 558 u_int compicvLen, u_int compivLen, 559 u_int comp);
Sets-up a transmit descriptor. txPower is apparently in 0.5dBm steps, see ah_setTxPowerLimit. txRate0 and txTries0 are the initial data rate and the number of retries at that data rate if no ack is received. The ack processing and the retries are performed in hardware. keyIx is presumably an index into a crypto keys table. antMode specifies which antenna to use for transmission (need to check the details).
560 HAL_BOOL __ahdecl(*ah_setupXTxDesc)(struct ath_hal *, struct ath_desc*, 561 u_int txRate1, u_int txTries1, 562 u_int txRate2, u_int txTries2, 563 u_int txRate3, u_int txTries3);
Augments a transmit descriptor with multi-rate retry. With the txRate0 above this allows the driver to specify 4 different bitrates at which the hardware tries to retransmit the packet until an ack is received. No idea whether there is an enforced limit on the txTries.
564 HAL_BOOL __ahdecl(*ah_fillTxDesc)(struct ath_hal *, struct ath_desc *, 565 u_int segLen, HAL_BOOL firstSeg, 566 HAL_BOOL lastSeg, const struct ath_desc *); 567 HAL_STATUS __ahdecl(*ah_procTxDesc)(struct ath_hal *, struct ath_desc*); 568 void __ahdecl(*ah_getTxIntrQueue)(struct ath_hal *, u_int32_t *); 569 void __ahdecl(*ah_reqTxIntrDesc)(struct ath_hal *, struct ath_desc*);
Receive Functions
572 u_int32_t __ahdecl(*ah_getRxDP)(struct ath_hal*); 573 void __ahdecl(*ah_setRxDP)(struct ath_hal*, u_int32_t rxdp); 574 void __ahdecl(*ah_enableReceive)(struct ath_hal*); 575 HAL_BOOL __ahdecl(*ah_stopDmaReceive)(struct ath_hal*); 576 void __ahdecl(*ah_startPcuReceive)(struct ath_hal*); 577 void __ahdecl(*ah_stopPcuReceive)(struct ath_hal*); 578 void __ahdecl(*ah_setMulticastFilter)(struct ath_hal*, 579 u_int32_t filter0, u_int32_t filter1); 580 HAL_BOOL __ahdecl(*ah_setMulticastFilterIndex)(struct ath_hal*, 581 u_int32_t index); 582 HAL_BOOL __ahdecl(*ah_clrMulticastFilterIndex)(struct ath_hal*, 583 u_int32_t index); 584 u_int32_t __ahdecl(*ah_getRxFilter)(struct ath_hal*); 585 void __ahdecl(*ah_setRxFilter)(struct ath_hal*, u_int32_t); 586 HAL_BOOL __ahdecl(*ah_setupRxDesc)(struct ath_hal *, struct ath_desc *, 587 u_int32_t size, u_int flags); 588 HAL_STATUS __ahdecl(*ah_procRxDesc)(struct ath_hal *, struct ath_desc *, 589 u_int32_t phyAddr, struct ath_desc *next, 590 u_int64_t tsf); 591 void __ahdecl(*ah_rxMonitor)(struct ath_hal *, 592 const HAL_NODE_STATS *, HAL_CHANNEL *);
ah_rxMonitor is called by the driver at the end of processing a received packet. It seems the driver is using it to pass rssi (receive signal strength) stats into the HAL.
593 void __ahdecl(*ah_procMibEvent)(struct ath_hal *, 594 const HAL_NODE_STATS *);
Misc Functions
597 HAL_STATUS __ahdecl(*ah_getCapability)(struct ath_hal *, 598 HAL_CAPABILITY_TYPE, u_int32_t capability, 599 u_int32_t *result); 600 HAL_BOOL __ahdecl(*ah_setCapability)(struct ath_hal *, 601 HAL_CAPABILITY_TYPE, u_int32_t capability, 602 u_int32_t setting, HAL_STATUS *); 603 HAL_BOOL __ahdecl(*ah_getDiagState)(struct ath_hal *, int request, 604 const void *args, u_int32_t argsize, 605 void **result, u_int32_t *resultsize); 606 void __ahdecl(*ah_getMacAddress)(struct ath_hal *, u_int8_t *); 607 HAL_BOOL __ahdecl(*ah_setMacAddress)(struct ath_hal *, const u_int8_t*); 608 void __ahdecl(*ah_getBssIdMask)(struct ath_hal *, u_int8_t *); 609 HAL_BOOL __ahdecl(*ah_setBssIdMask)(struct ath_hal *, const u_int8_t*); 610 HAL_BOOL __ahdecl(*ah_setRegulatoryDomain)(struct ath_hal*, 611 u_int16_t, HAL_STATUS *); 612 void __ahdecl(*ah_setLedState)(struct ath_hal*, HAL_LED_STATE); 613 void __ahdecl(*ah_writeAssocid)(struct ath_hal*, 614 const u_int8_t *bssid, u_int16_t assocId); 615 HAL_BOOL __ahdecl(*ah_gpioCfgOutput)(struct ath_hal *, u_int32_t gpio); 616 HAL_BOOL __ahdecl(*ah_gpioCfgInput)(struct ath_hal *, u_int32_t gpio); 617 u_int32_t __ahdecl(*ah_gpioGet)(struct ath_hal *, u_int32_t gpio); 618 HAL_BOOL __ahdecl(*ah_gpioSet)(struct ath_hal *, 619 u_int32_t gpio, u_int32_t val); 620 void __ahdecl(*ah_gpioSetIntr)(struct ath_hal*, u_int, u_int32_t); 621 u_int32_t __ahdecl(*ah_getTsf32)(struct ath_hal*); 622 u_int64_t __ahdecl(*ah_getTsf64)(struct ath_hal*); 623 void __ahdecl(*ah_resetTsf)(struct ath_hal*); 624 HAL_BOOL __ahdecl(*ah_detectCardPresent)(struct ath_hal*); 625 void __ahdecl(*ah_updateMibCounters)(struct ath_hal*, 626 HAL_MIB_STATS*); 627 HAL_RFGAIN __ahdecl(*ah_getRfGain)(struct ath_hal*); 628 u_int __ahdecl(*ah_getDefAntenna)(struct ath_hal*); 629 void __ahdecl(*ah_setDefAntenna)(struct ath_hal*, u_int);
Sets and gets the default antenna. Not entirely sure what this impacts as the tx antenna can be specified on a per-packet basic in ah_setupTxDesc.
630 HAL_BOOL __ahdecl(*ah_setSlotTime)(struct ath_hal*, u_int); 631 u_int __ahdecl(*ah_getSlotTime)(struct ath_hal*);
Sets and gets the slot time, one of the basic 802.11 timing parameters.
632 HAL_BOOL __ahdecl(*ah_setAckTimeout)(struct ath_hal*, u_int); 633 u_int __ahdecl(*ah_getAckTimeout)(struct ath_hal*);
The ack timeout refers to the timout for the ack that the receiving node sends back when receiving a correct data packet. It is specified here in microseconds. The various atheros chipset versions as well as the three operating modes (a/b/g) seem to all allow different maximums. It's a good idea to read the value back after writing it to verify that it didn't get clamped.
634 HAL_BOOL __ahdecl(*ah_setCTSTimeout)(struct ath_hal*, u_int); 635 u_int __ahdecl(*ah_getCTSTimeout)(struct ath_hal*); 636 HAL_BOOL __ahdecl(*ah_setDecompMask)(struct ath_hal*, u_int16_t, int); 637 void __ahdecl(*ah_setCoverageClass)(struct ath_hal*, u_int8_t, int);
Key Cache Functions
641 u_int32_t __ahdecl(*ah_getKeyCacheSize)(struct ath_hal*); 642 HAL_BOOL __ahdecl(*ah_resetKeyCacheEntry)(struct ath_hal*, u_int16_t); 643 HAL_BOOL __ahdecl(*ah_isKeyCacheEntryValid)(struct ath_hal *, 644 u_int16_t); 645 HAL_BOOL __ahdecl(*ah_setKeyCacheEntry)(struct ath_hal*, 646 u_int16_t, const HAL_KEYVAL *, 647 const u_int8_t *, int); 648 HAL_BOOL __ahdecl(*ah_setKeyCacheEntryMac)(struct ath_hal*, 649 u_int16_t, const u_int8_t *);
Power Management Functions
652 HAL_BOOL __ahdecl(*ah_setPowerMode)(struct ath_hal*, 653 HAL_POWER_MODE mode, int setChip); 654 HAL_POWER_MODE __ahdecl(*ah_getPowerMode)(struct ath_hal*);
Beacon Management Functions
657 void __ahdecl(*ah_beaconInit)(struct ath_hal *, 658 u_int32_t nexttbtt, u_int32_t intval); 659 void __ahdecl(*ah_setStationBeaconTimers)(struct ath_hal*, 660 const HAL_BEACON_STATE *); 661 void __ahdecl(*ah_resetStationBeaconTimers)(struct ath_hal*); 662 HAL_BOOL __ahdecl(*ah_waitForBeaconDone)(struct ath_hal *, 663 HAL_BUS_ADDR);
Interrupt functions
666 HAL_BOOL __ahdecl(*ah_isInterruptPending)(struct ath_hal*); 667 HAL_BOOL __ahdecl(*ah_getPendingInterrupts)(struct ath_hal*, HAL_INT*); 668 HAL_INT __ahdecl(*ah_getInterrupts)(struct ath_hal*); 669 HAL_INT __ahdecl(*ah_setInterrupts)(struct ath_hal*, HAL_INT);
HAL global functions
Note that these functions have extensive comments in the header file. Be sure to read those first and only note down information here that goes beyond those comments.
677 extern const char *__ahdecl ath_hal_probe(u_int16_t vendorid, u_int16_t devid); 692 extern struct ath_hal * __ahdecl ath_hal_attach(u_int16_t devid, HAL_SOFTC, 693 HAL_BUS_TAG, HAL_BUS_HANDLE, HAL_STATUS* status); 706 extern HAL_BOOL __ahdecl ath_hal_init_channels(struct ath_hal *, 707 HAL_CHANNEL *chans, u_int maxchans, u_int *nchans, 708 u_int8_t *regclassids, u_int maxregids, u_int *nregids, 709 HAL_CTRY_CODE cc, u_int16_t modeSelect, 710 HAL_BOOL enableOutdoor, HAL_BOOL enableExtendedChannels); 715 extern u_int __ahdecl ath_hal_getwirelessmodes(struct ath_hal*, HAL_CTRY_CODE); 720 extern const HAL_RATE_TABLE * __ahdecl ath_hal_getratetable(struct ath_hal *, 721 u_int mode); 726 extern u_int16_t __ahdecl ath_hal_computetxtime(struct ath_hal *, 727 const HAL_RATE_TABLE *rates, u_int32_t frameLen, 728 u_int16_t rateix, HAL_BOOL shortPreamble); 733 extern HAL_BOOL __ahdecl ath_hal_ispublicsafetysku(struct ath_hal *); 739 extern u_int __ahdecl ath_hal_mhz2ieee(struct ath_hal *, u_int mhz, u_int flags); 744 extern char ath_hal_version[]; 748 extern const char* ath_hal_buildopts[];
Transmit power
I'm not sure where these functions are defined, need to look it up... In any case question is:
Can you explain the difference between:
- ath_hal_gettxpowlimit(ah, &txpow);
- ath_hal_getmaxtxpow(sc->sc_ah, &txpow);
what needs to be done for proper txpower handling from your point of view?
Sam answers:
getmaxtxpow returns the txpow limit (in .5 dBm units) calculated from the card capabilities and the regulatory constraint
gettxpowlimit returns the current, potentially constrained, txpow limit (in .5 dBm units) as set for example when implementing 11h; it is clamped (internally) to the value returned by getmaxtxpow
There are 3 ways to control tx power:
- settxpowlimit to clamp the max txpower to a setting lower than the max allowed by the card++regdomain
- the per tx descriptor tpc which is an index into a table of size 64 which yields uneven tx power steps that you must calibrate per card but typically has finer grain control at the "low end"; you must explicitly enable this before use with HAL_CAP_TPC
- tx power scaling (HAL_CAP_TXPOW, 3) which is included solely for compatbility with Atheros' vxworks code and may go away at any time; this is a setting in the range [0..4] which causes the txpow tables to be down- scaled when they are calculated by 0, 3, 6, 9, and max dBm
The intended/preferred way to control txpower is with the per-packet TPC setting in the tx descriptor. However because it's been hard to get that right and because not all parts support it I added the tpScale knob.
I recently did some txpower tests on 5212 cards and believe things are working. There is one issue that we discovered; that the txpower for self-generated frames was not set correctly when using per-packet tpc (it is low)--this will be fixed in the next hal rev.
CPU Usage: A bit Gleaned From the Devel-Mailing List
Almost all the CPU time in the HAL is actually spent for PCI bus transfers between the HAL and the hardware. Even though memory mapped on modern systems, the PCI bus is a bottleneck, and frequent reading and writing of the atheros card registers uses up CPU time.
When receiving packets, almost 1/3 of the time spent in ath_hal goes to zz0b6c53a4 (as of 0.9.16.16), which reads three hardware registers containing some status information (at least thats what I assume from the code)
Another 20% go to reading the TSF from the card, where it might be possible to cache the value in certain situations to reduce load.
You can see these results yourself with "opannotate --assembly" applied to an oprofile dump of the driver.
The self-linked descriptor trick
The madwifi receive code uses something called a "self-linked descriptor" as part of the interface to the HAL. Here's how that code works:
- Madwifi initializes the HAL with a linked list of descriptors that are used for DMA.
- The HAL uses the linked list from front to back.
- As the hardware uses each descriptor, it generates an interrupt.
- As interrupts arrive, madwifi pulls descriptors off the front of the linked list, processes them, and then sticks them on to the back of the list so they can be reused.
So you have a race: If madwifi processes descriptors quickly, the HAL will never run out. If madwifi is slow to handle the interrupts, however, then the HAL can reach the end of the linked list. The code deals with this by forcing the last entry in the descriptor list to be self-linked. Normally, the HAL writes to a descriptor, then follows the link to the next descriptor in the chain. But if the HAL reaches the last descriptor, the next link in the chain will be back to that same descriptor. So the HAL starts overwriting the data in that descriptor, over and over again.
This means that received data is being lost. That's bad, but not fatal. The sending station will resend. (I'm unclear on whether the hardware does this, or whether the hardware has already acknowledged the received packet at this point and the software layers are responsible for forcing the resend, e.g. through a failure to send TCP ACKs.) Once madwifi catches up on its interrupt processing, that last descriptor stops being self-linked, things go back to normal, and no more data is lost.
The locking in this area is subtle, and at first glance it looked completely broken to me. I read through it more carefully, and could not find a problem. That's not the same thing as the locking being correct.
