Please note: This project is no longer active. The website is kept online for historic purposes only.
If you´re looking for a Linux driver for your Atheros WLAN device, you should continue here .

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:

  1. settxpowlimit to clamp the max txpower to a setting lower than the max allowed by the card++regdomain
  2. 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
  3. 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.