summaryrefslogtreecommitdiff
path: root/sys/dev/ic/ar5210.c
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2004-11-02 03:01:17 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2004-11-02 03:01:17 +0000
commit6f310ee239544802e42e2a984bac432479b1a8ba (patch)
tree358dd508a47f8cb4c1cef09b798a9236c1cb6c3c /sys/dev/ic/ar5210.c
parent811904472e873a7a84e99ef1b66849f83cbc0f3b (diff)
import of a free hal part for the ath driver as a replacement for the
binary-only hal module found in FreeBSD and NetBSD. OpenBSD's approach is based on reverse engineering because it is _not_ possible to include a non-free and binary-only piece of software in a 100% free operating system. it still lacks some features found in the "official" hal module but this will be done very soon with a help by a lot of contributors - because it's free. ok deraadt@
Diffstat (limited to 'sys/dev/ic/ar5210.c')
-rw-r--r--sys/dev/ic/ar5210.c2514
1 files changed, 2514 insertions, 0 deletions
diff --git a/sys/dev/ic/ar5210.c b/sys/dev/ic/ar5210.c
new file mode 100644
index 00000000000..220eb9d44d4
--- /dev/null
+++ b/sys/dev/ic/ar5210.c
@@ -0,0 +1,2514 @@
+/* $OpenBSD: ar5210.c,v 1.1 2004/11/02 03:01:16 reyk Exp $ */
+
+/*
+ * Copyright (c) 2004 Reyk Floeter <reyk@vantronix.net>.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
+ * SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * HAL interface for the Atheros AR5000 Wireless LAN chipset
+ * (AR5210 + AR5110).
+ */
+
+#include <dev/ic/ar5xxx.h>
+
+#ifdef AR5K_SUPPORT_AR5210
+
+#include <dev/ic/ar5210reg.h>
+#include <dev/ic/ar5210var.h>
+
+HAL_BOOL ar5k_ar5210_nic_reset(struct ath_hal *, u_int32_t);
+HAL_BOOL ar5k_ar5210_nic_wakeup(struct ath_hal *, HAL_BOOL, HAL_BOOL);
+u_int32_t ar5k_ar5210_chan2athchan(HAL_CHANNEL *);
+HAL_BOOL ar5k_ar5210_set_channel(struct ath_hal *, HAL_CHANNEL *);
+void ar5k_ar5210_init_tx_queue(struct ath_hal *, u_int, HAL_BOOL);
+const void ar5k_ar5210_fill(struct ath_hal *);
+
+AR5K_HAL_FUNCTIONS(extern, ar5k_ar5210,);
+
+const void
+ar5k_ar5210_fill(hal)
+ struct ath_hal *hal;
+{
+ hal->ah_magic = AR5K_AR5210_MAGIC;
+
+ /*
+ * Init/Exit functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, getRateTable);
+ AR5K_HAL_FUNCTION(hal, ar5210, detach);
+
+ /*
+ * Reset functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, reset);
+ AR5K_HAL_FUNCTION(hal, ar5210, setPCUConfig);
+ AR5K_HAL_FUNCTION(hal, ar5210, perCalibration);
+
+ /*
+ * TX functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, updateTxTrigLevel);
+ AR5K_HAL_FUNCTION(hal, ar5210, setupTxQueue);
+ AR5K_HAL_FUNCTION(hal, ar5210, setTxQueueProps);
+ AR5K_HAL_FUNCTION(hal, ar5210, releaseTxQueue);
+ AR5K_HAL_FUNCTION(hal, ar5210, resetTxQueue);
+ AR5K_HAL_FUNCTION(hal, ar5210, getTxDP);
+ AR5K_HAL_FUNCTION(hal, ar5210, setTxDP);
+ AR5K_HAL_FUNCTION(hal, ar5210, startTxDma);
+ AR5K_HAL_FUNCTION(hal, ar5210, stopTxDma);
+ AR5K_HAL_FUNCTION(hal, ar5210, setupTxDesc);
+ AR5K_HAL_FUNCTION(hal, ar5210, setupXTxDesc);
+ AR5K_HAL_FUNCTION(hal, ar5210, fillTxDesc);
+ AR5K_HAL_FUNCTION(hal, ar5210, procTxDesc);
+ AR5K_HAL_FUNCTION(hal, ar5210, hasVEOL);
+
+ /*
+ * RX functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, getRxDP);
+ AR5K_HAL_FUNCTION(hal, ar5210, setRxDP);
+ AR5K_HAL_FUNCTION(hal, ar5210, enableReceive);
+ AR5K_HAL_FUNCTION(hal, ar5210, stopDmaReceive);
+ AR5K_HAL_FUNCTION(hal, ar5210, startPcuReceive);
+ AR5K_HAL_FUNCTION(hal, ar5210, stopPcuReceive);
+ AR5K_HAL_FUNCTION(hal, ar5210, setMulticastFilter);
+ AR5K_HAL_FUNCTION(hal, ar5210, setMulticastFilterIndex);
+ AR5K_HAL_FUNCTION(hal, ar5210, clrMulticastFilterIndex);
+ AR5K_HAL_FUNCTION(hal, ar5210, getRxFilter);
+ AR5K_HAL_FUNCTION(hal, ar5210, setRxFilter);
+ AR5K_HAL_FUNCTION(hal, ar5210, setupRxDesc);
+ AR5K_HAL_FUNCTION(hal, ar5210, procRxDesc);
+ AR5K_HAL_FUNCTION(hal, ar5210, rxMonitor);
+
+ /*
+ * Misc functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, dumpState);
+ AR5K_HAL_FUNCTION(hal, ar5210, getDiagState);
+ AR5K_HAL_FUNCTION(hal, ar5210, getMacAddress);
+ AR5K_HAL_FUNCTION(hal, ar5210, setMacAddress);
+ AR5K_HAL_FUNCTION(hal, ar5210, setRegulatoryDomain);
+ AR5K_HAL_FUNCTION(hal, ar5210, setLedState);
+ AR5K_HAL_FUNCTION(hal, ar5210, writeAssocid);
+ AR5K_HAL_FUNCTION(hal, ar5210, gpioCfgInput);
+ AR5K_HAL_FUNCTION(hal, ar5210, gpioCfgOutput);
+ AR5K_HAL_FUNCTION(hal, ar5210, gpioGet);
+ AR5K_HAL_FUNCTION(hal, ar5210, gpioSet);
+ AR5K_HAL_FUNCTION(hal, ar5210, gpioSetIntr);
+ AR5K_HAL_FUNCTION(hal, ar5210, getTsf32);
+ AR5K_HAL_FUNCTION(hal, ar5210, getTsf64);
+ AR5K_HAL_FUNCTION(hal, ar5210, resetTsf);
+ AR5K_HAL_FUNCTION(hal, ar5210, getRegDomain);
+ AR5K_HAL_FUNCTION(hal, ar5210, detectCardPresent);
+ AR5K_HAL_FUNCTION(hal, ar5210, updateMibCounters);
+ AR5K_HAL_FUNCTION(hal, ar5210, getRfGain);
+ AR5K_HAL_FUNCTION(hal, ar5210, setSlotTime);
+ AR5K_HAL_FUNCTION(hal, ar5210, getSlotTime);
+ AR5K_HAL_FUNCTION(hal, ar5210, setAckTimeout);
+ AR5K_HAL_FUNCTION(hal, ar5210, getAckTimeout);
+ AR5K_HAL_FUNCTION(hal, ar5210, setCTSTimeout);
+ AR5K_HAL_FUNCTION(hal, ar5210, getCTSTimeout);
+
+ /*
+ * Key table (WEP) functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, isHwCipherSupported);
+ AR5K_HAL_FUNCTION(hal, ar5210, getKeyCacheSize);
+ AR5K_HAL_FUNCTION(hal, ar5210, resetKeyCacheEntry);
+ AR5K_HAL_FUNCTION(hal, ar5210, isKeyCacheEntryValid);
+ AR5K_HAL_FUNCTION(hal, ar5210, setKeyCacheEntry);
+ AR5K_HAL_FUNCTION(hal, ar5210, setKeyCacheEntryMac);
+
+ /*
+ * Power management functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, setPowerMode);
+ AR5K_HAL_FUNCTION(hal, ar5210, getPowerMode);
+ AR5K_HAL_FUNCTION(hal, ar5210, queryPSPollSupport);
+ AR5K_HAL_FUNCTION(hal, ar5210, initPSPoll);
+ AR5K_HAL_FUNCTION(hal, ar5210, enablePSPoll);
+ AR5K_HAL_FUNCTION(hal, ar5210, disablePSPoll);
+
+ /*
+ * Beacon functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, beaconInit);
+ AR5K_HAL_FUNCTION(hal, ar5210, setStationBeaconTimers);
+ AR5K_HAL_FUNCTION(hal, ar5210, resetStationBeaconTimers);
+ AR5K_HAL_FUNCTION(hal, ar5210, waitForBeaconDone);
+
+ /*
+ * Interrupt functions
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, isInterruptPending);
+ AR5K_HAL_FUNCTION(hal, ar5210, getPendingInterrupts);
+ AR5K_HAL_FUNCTION(hal, ar5210, getInterrupts);
+ AR5K_HAL_FUNCTION(hal, ar5210, setInterrupts);
+
+ /*
+ * Chipset functions (ar5k-specific, non-HAL)
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, get_capabilities);
+ AR5K_HAL_FUNCTION(hal, ar5210, radar_alert);
+ AR5K_HAL_FUNCTION(hal, ar5210, regulation_domain);
+
+ /*
+ * EEPROM access
+ */
+ AR5K_HAL_FUNCTION(hal, ar5210, eeprom_init);
+ AR5K_HAL_FUNCTION(hal, ar5210, eeprom_is_busy);
+ AR5K_HAL_FUNCTION(hal, ar5210, eeprom_read);
+ AR5K_HAL_FUNCTION(hal, ar5210, eeprom_write);
+}
+
+struct ath_hal *
+ar5k_ar5210_attach(device, sc, st, sh, status)
+ u_int16_t device;
+ void *sc;
+ bus_space_tag_t st;
+ bus_space_handle_t sh;
+ int *status;
+{
+ int i;
+ struct ath_hal *hal = (struct ath_hal*) sc;
+ u_int8_t mac[IEEE80211_ADDR_LEN];
+
+ ar5k_ar5210_fill(hal);
+
+ /* Bring device out of sleep and reset it's units */
+ if(ar5k_ar5210_nic_wakeup(hal, AH_FALSE, AH_TRUE) != AH_TRUE)
+ return(NULL);
+
+ /* Get MAC, PHY and RADIO revisions */
+ hal->ah_mac_version = 1;
+ hal->ah_mac_revision = (AR5K_REG_READ(AR5K_AR5210_SREV) &
+ AR5K_AR5210_SREV_ID_M);
+ hal->ah_phy_revision = AR5K_REG_READ(AR5K_AR5210_PHY_CHIP_ID) &
+ 0x00ffffffff;
+
+ /* ...wait until PHY is ready and read RADIO revision */
+ AR5K_REG_WRITE(AR5K_AR5210_PHY(0x34), 0x00001c16);
+ for(i = 0; i < 4; i++)
+ AR5K_REG_WRITE(AR5K_AR5210_PHY(0x20), 0x00010000);
+ hal->ah_radio_5ghz_revision = (u_int16_t)
+ (ar5k_bitswap((AR5K_REG_READ(AR5K_AR5210_PHY(256) >> 28) &
+ 0xf), 4) + 1);
+ hal->ah_radio_2ghz_revision = 0;
+
+ memset(&mac, 0xff, sizeof(mac));
+ ar5k_ar5210_writeAssocid(hal, mac, 0, 0);
+ ar5k_ar5210_getMacAddress(hal, mac);
+ ar5k_ar5210_setPCUConfig(hal);
+
+ return(hal);
+}
+
+HAL_BOOL
+ar5k_ar5210_nic_reset(hal, val)
+ struct ath_hal *hal;
+ u_int32_t val;
+{
+ HAL_BOOL ret = AH_FALSE;
+ u_int32_t mask = val ? val : ~0;
+
+ /*
+ * Reset the device and wait until success
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_RC, val);
+
+ /* Wait at least 128 PCI clocks */
+ AR5K_DELAY(15);
+
+ val &=
+ AR5K_AR5210_RC_PCU | AR5K_AR5210_RC_MAC |
+ AR5K_AR5210_RC_PHY | AR5K_AR5210_RC_DMA;
+
+ mask &=
+ AR5K_AR5210_RC_PCU | AR5K_AR5210_RC_MAC |
+ AR5K_AR5210_RC_PHY | AR5K_AR5210_RC_DMA;
+
+ ret = ar5k_register_timeout(hal, AR5K_AR5210_RC, mask, val, AH_FALSE);
+
+ /*
+ * Reset configuration register
+ */
+ if((val & AR5K_AR5210_RC_MAC) == 0)
+ AR5K_REG_WRITE(AR5K_AR5210_CFG, AR5K_AR5210_INIT_CFG);
+
+ return(ret);
+}
+
+HAL_BOOL
+ar5k_ar5210_nic_wakeup(hal, turbo, initial)
+ struct ath_hal *hal;
+ HAL_BOOL turbo;
+ HAL_BOOL initial;
+{
+ /*
+ * Reset and wakeup the device
+ */
+
+ if(initial == AH_TRUE) {
+ /* ...reset hardware */
+ if(ar5k_ar5210_nic_reset(hal, AR5K_AR5210_RC_PCI) == AH_FALSE) {
+ AR5K_PRINTF("failed to reset the PCI chipset\n");
+ return(AH_FALSE);
+ }
+
+ AR5K_DELAY(1000);
+ }
+
+ /* ...wakeup the device */
+ if(ar5k_ar5210_setPowerMode(hal, HAL_PM_AWAKE, AH_TRUE, 0) == AH_FALSE) {
+ AR5K_PRINTF("failed to resume the AR5210 chipset\n");
+ return(AH_FALSE);
+ }
+
+ /* ...enable Atheros turbo mode if requested */
+ AR5K_REG_WRITE(AR5K_AR5210_PHY_FC,
+ turbo == AH_TRUE ? AR5K_AR5210_PHY_FC_TURBO_MODE : 0);
+
+ /* ...reset chipset */
+ if(ar5k_ar5210_nic_reset(hal, AR5K_AR5210_RC_CHIP) == AH_FALSE) {
+ AR5K_PRINTF("failed to reset the AR5210 chipset\n");
+ return(AH_FALSE);
+ }
+
+ AR5K_DELAY(1000);
+
+ /* ...reset chipset and PCI device */
+ if(ar5k_ar5210_nic_reset(hal, AR5K_AR5210_RC_CHIP |
+ AR5K_AR5210_RC_PCI) == AH_FALSE) {
+ AR5K_PRINTF("failed to reset the AR5210 + PCI chipset\n");
+ return(AH_FALSE);
+ }
+
+ AR5K_DELAY(2300);
+
+ /* ...wakeup (again) */
+ if(ar5k_ar5210_setPowerMode(hal, HAL_PM_AWAKE, AH_TRUE, 0) == AH_FALSE) {
+ AR5K_PRINTF("failed to resume the AR5210 (again)\n");
+ return(AH_FALSE);
+ }
+
+ /* ...final warm reset */
+ if(ar5k_ar5210_nic_reset(hal, 0) == AH_FALSE) {
+ AR5K_PRINTF("failed to warm reset the AR5210\n");
+ return(AH_FALSE);
+ }
+
+ return(AH_TRUE);
+}
+
+u_int32_t
+ar5k_ar5210_chan2athchan(channel)
+ HAL_CHANNEL *channel;
+{
+ u_int32_t athchan;
+
+ /*
+ * Convert IEEE channel/MHz to an internal channel value used
+ * by the AR5210 chipset. This has not been verified with
+ * newer chipsets like the AR5212A who have a completely
+ * different RF/PHY part.
+ */
+ athchan = (ar5k_bitswap((ieee80211_mhz2ieee(channel->channel,
+ channel->channelFlags) - 24)
+ / 2, 5) << 1) | (1 << 6) | 0x1;
+
+ return(athchan);
+}
+
+HAL_BOOL
+ar5k_ar5210_set_channel(hal, channel)
+ struct ath_hal *hal;
+ HAL_CHANNEL *channel;
+{
+ u_int32_t data;
+
+ /* Disable phy and wait */
+ AR5K_REG_WRITE(AR5K_AR5210_PHY_ACTIVE, AR5K_AR5210_PHY_DISABLE);
+ AR5K_DELAY(1000);
+
+ /*
+ * Check bounds supported by the PHY
+ * (don't care about regulation restrictions at this point)
+ */
+ if(channel->channel < hal->ah_capabilities.cap_range.range_5ghz_min ||
+ channel->channel > hal->ah_capabilities.cap_range.range_5ghz_max) {
+ AR5K_PRINTF("channel out of supported range (%u MHz)\n",
+ channel->channel);
+ return(AH_FALSE);
+ }
+
+ /*
+ * Set the channel and wait
+ */
+ data = ar5k_ar5210_chan2athchan(channel);
+ AR5K_REG_WRITE(AR5K_AR5210_PHY(0x27), data);
+ AR5K_REG_WRITE(AR5K_AR5210_PHY(0x30), 0);
+ AR5K_DELAY(1000);
+
+ /*
+ * Activate phy and wait
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_PHY_ACTIVE, AR5K_AR5210_PHY_ENABLE);
+ AR5K_DELAY(1000);
+
+ hal->ah_current_channel.channel = channel->channel;
+ hal->ah_current_channel.channelFlags = channel->channelFlags;
+ hal->ah_turbo = channel->channelFlags == CHANNEL_T ? AH_TRUE : AH_FALSE;
+
+ return(AH_TRUE);
+}
+
+const HAL_RATE_TABLE *
+ar5k_ar5210_getRateTable(hal, mode)
+ struct ath_hal *hal;
+ u_int mode;
+{
+ switch(mode) {
+ case HAL_MODE_11A:
+ return(&hal->ah_rt_11a);
+ case HAL_MODE_TURBO:
+ return(&hal->ah_rt_turbo);
+ case HAL_MODE_11B:
+ case HAL_MODE_11G:
+ default:
+ return(NULL);
+ }
+
+ return(NULL);
+}
+
+void
+ar5k_ar5210_detach(hal)
+ struct ath_hal *hal;
+{
+ /*
+ * Free HAL structure, assume interrupts are down
+ */
+ free(hal, M_DEVBUF);
+}
+
+HAL_BOOL
+ar5k_ar5210_reset(hal, op_mode, channel, change_channel, status)
+ struct ath_hal *hal;
+ HAL_OPMODE op_mode;
+ HAL_CHANNEL *channel;
+ HAL_BOOL change_channel;
+ HAL_STATUS *status;
+{
+ int i;
+ struct ar5k_ini initial[] = AR5K_AR5210_INI;
+
+ if(ar5k_ar5210_nic_wakeup(hal,
+ channel->channelFlags & IEEE80211_CHAN_T ?
+ AH_TRUE : AH_FALSE, AH_FALSE) == AH_FALSE)
+ return(AH_FALSE);
+
+ /*
+ * Initialize operating mode
+ */
+ hal->ah_op_mode = op_mode;
+ ar5k_ar5210_setPCUConfig(hal);
+
+ /*
+ * Write initial mode register settings
+ */
+ for(i = 0; i < AR5K_ELEMENTS(initial); i++) {
+ if(change_channel == AH_TRUE &&
+ initial[i].ini_register >= AR5K_AR5210_PCU_MIN &&
+ initial[i].ini_register <= AR5K_AR5210_PCU_MAX)
+ continue;
+
+ switch(initial[i].ini_mode) {
+ case INI_READ:
+ /* Cleared on read */
+ AR5K_REG_READ(initial[i].ini_register);
+ break;
+
+ case INI_WRITE:
+ default:
+ AR5K_REG_WRITE(initial[i].ini_register,
+ initial[i].ini_value);
+ }
+ }
+
+ AR5K_DELAY(1000);
+
+ /*
+ * Set channel and calibrate the PHY
+ */
+ if(ar5k_ar5210_perCalibration(hal, channel) == AH_FALSE)
+ return(AH_FALSE);
+
+ /*
+ * Set RF kill flags if supported by the device (read from the EEPROM)
+ */
+ if(hal->ah_capabilities.cap_eeprom.ee_rfkill != 0) {
+ if ((hal->ah_gpio[0] = ar5k_ar5210_gpioGet(hal, 0)) == 0)
+ ar5k_ar5210_gpioSetIntr(hal, 0, 1);
+ else
+ ar5k_ar5210_gpioSetIntr(hal, 0, 0);
+ }
+
+ /*
+ * Reset queues and start beacon timers at the end of the reset routine
+ */
+ for(i = 0; i < hal->ah_capabilities.cap_queues.q_tx_num; i++) {
+ if(ar5k_ar5210_resetTxQueue(hal, i) == AH_FALSE) {
+ AR5K_PRINTF("failed to reset TX queue #%d\n", i);
+ return(AH_FALSE);
+ }
+ }
+
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_BEACON,
+ AR5K_AR5210_BEACON_EN | AR5K_AR5210_BEACON_RESET_TSF);
+
+ return(AH_TRUE);
+}
+
+void
+ar5k_ar5210_setPCUConfig(hal)
+ struct ath_hal *hal;
+{
+ u_int32_t pcu_reg, beacon_reg, low_id, high_id;
+
+ beacon_reg = 0;
+ pcu_reg = 0;
+
+ switch(hal->ah_op_mode) {
+ case IEEE80211_M_STA:
+ pcu_reg |= AR5K_AR5210_STA_ID1_NO_PSPOLL |
+ AR5K_AR5210_STA_ID1_DESC_ANTENNA |
+ AR5K_AR5210_STA_ID1_PWR_SV;
+ break;
+
+ case IEEE80211_M_IBSS:
+ pcu_reg |= AR5K_AR5210_STA_ID1_ADHOC |
+ AR5K_AR5210_STA_ID1_NO_PSPOLL |
+ AR5K_AR5210_STA_ID1_DESC_ANTENNA;
+ beacon_reg |= AR5K_AR5210_BCR_ADHOC;
+ break;
+
+ case IEEE80211_M_HOSTAP:
+ pcu_reg |= AR5K_AR5210_STA_ID1_AP |
+ AR5K_AR5210_STA_ID1_NO_PSPOLL |
+ AR5K_AR5210_STA_ID1_DESC_ANTENNA;
+ beacon_reg |= AR5K_AR5210_BCR_AP;
+ break;
+
+ case IEEE80211_M_MONITOR:
+ pcu_reg |= AR5K_AR5210_STA_ID1_NO_PSPOLL;
+ break;
+
+ default:
+ return;
+ }
+
+ /*
+ * Set PCU and BCR registers
+ */
+ memcpy(&low_id, &(hal->ah_sta_id[0]), 4);
+ memcpy(&high_id, &(hal->ah_sta_id[4]), 2);
+ AR5K_REG_WRITE(AR5K_AR5210_STA_ID0, low_id);
+ AR5K_REG_WRITE(AR5K_AR5210_STA_ID1, pcu_reg | high_id);
+ AR5K_REG_WRITE(AR5K_AR5210_BCR, beacon_reg);
+
+ return;
+}
+
+HAL_BOOL
+ar5k_ar5210_perCalibration(hal, channel)
+ struct ath_hal *hal;
+ HAL_CHANNEL *channel;
+{
+ /*
+ * Disable beacons and RX/TX queues, wait
+ */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_DIAG_SW,
+ AR5K_AR5210_DIAG_SW_DIS_TX | AR5K_AR5210_DIAG_SW_DIS_RX);
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_BEACON, AR5K_AR5210_BEACON_EN);
+
+ AR5K_DELAY(2300);
+
+ /*
+ * Set the channel (with AGC turned off)
+ */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_PHY_AGC, AR5K_AR5210_PHY_AGC_DISABLE);
+
+ if(ar5k_ar5210_set_channel(hal, channel) != AH_TRUE)
+ return(AH_FALSE);
+
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_PHY_AGC, AR5K_AR5210_PHY_AGC_DISABLE);
+
+ /*
+ * Enable noise floor calibration and wait until completion
+ */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_PHY_AGCCTL,
+ AR5K_AR5210_PHY_AGC_CAL);
+
+ if(ar5k_register_timeout(hal, AR5K_AR5210_PHY_AGCCTL,
+ AR5K_AR5210_PHY_AGC_CAL, 0, AH_FALSE) == AH_FALSE) {
+ AR5K_PRINTF("calibration timeout\n");
+ return(AH_FALSE);
+ }
+
+ /*
+ * XXX Check the current noise floor?
+ */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_PHY_AGCCTL,
+ AR5K_AR5210_PHY_AGC_NF);
+
+ /*
+ * Re-enable RX/TX and beacons
+ */
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_DIAG_SW,
+ AR5K_AR5210_DIAG_SW_DIS_TX | AR5K_AR5210_DIAG_SW_DIS_RX);
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_BEACON, AR5K_AR5210_BEACON_EN);
+
+ return(AH_TRUE);
+}
+
+/*
+ * Transmit functions
+ */
+
+HAL_BOOL
+ar5k_ar5210_updateTxTrigLevel(hal, increase)
+ struct ath_hal *hal;
+ HAL_BOOL increase;
+{
+ u_int32_t trigger_level;
+ HAL_BOOL status = AH_FALSE;
+
+ /*
+ * Disable interrupts by setting the mask
+ */
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_IMR, HAL_INT_GLOBAL);
+
+ trigger_level = AR5K_REG_READ(AR5K_AR5210_TRIG_LVL);
+
+ if(increase == AH_FALSE) {
+ if(--trigger_level < AR5K_TUNE_MIN_TX_FIFO_THRES)
+ goto done;
+ } else
+ trigger_level +=
+ ((AR5K_TUNE_MAX_TX_FIFO_THRES - trigger_level) / 2);
+
+ /*
+ * Update trigger level on success
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_TRIG_LVL, trigger_level);
+ status = AH_TRUE;
+
+ done:
+ /*
+ * Restore interrupt mask
+ */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_IMR, HAL_INT_GLOBAL);
+
+ return(status);
+}
+
+int
+ar5k_ar5210_setupTxQueue(hal, queue_type, queue_info)
+ struct ath_hal *hal;
+ HAL_TX_QUEUE queue_type;
+ const HAL_TXQ_INFO *queue_info;
+{
+ u_int queue;
+
+ /*
+ * Get queue by type
+ */
+ switch(queue_type) {
+ case HAL_TX_QUEUE_DATA:
+ queue = 0;
+ break;
+ case HAL_TX_QUEUE_BEACON:
+ case HAL_TX_QUEUE_CAB:
+ queue = 1;
+ break;
+ default:
+ return(-EINVAL);
+ }
+
+ /*
+ * Setup internal queue structure
+ */
+ bzero(&hal->ah_txq[queue], sizeof(HAL_TXQ_INFO));
+ hal->ah_txq[queue].tqi_type = queue_type;
+
+ if(queue_info != NULL) {
+ if(ar5k_ar5210_setTxQueueProps(hal, queue, queue_info) != AH_TRUE)
+ return(AH_FALSE);
+ }
+
+ return(0);
+}
+
+HAL_BOOL
+ar5k_ar5210_setTxQueueProps(hal, queue, queue_info)
+ struct ath_hal *hal;
+ int queue;
+ const HAL_TXQ_INFO *queue_info;
+{
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ if(hal->ah_txq[queue].tqi_type == HAL_TX_QUEUE_INACTIVE)
+ return(AH_FALSE);
+
+ hal->ah_txq[queue].tqi_aifs = queue_info->tqi_aifs;
+ hal->ah_txq[queue].tqi_cw_max = queue_info->tqi_cw_max;
+ hal->ah_txq[queue].tqi_cw_min = queue_info->tqi_cw_min;
+ hal->ah_txq[queue].tqi_flags = queue_info->tqi_flags;
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_releaseTxQueue(hal, queue)
+ struct ath_hal *hal;
+ u_int queue;
+{
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ /* This queue will be skipped in further operations */
+ hal->ah_txq[queue].tqi_type = HAL_TX_QUEUE_INACTIVE;
+
+ return(AH_FALSE);
+}
+
+void
+ar5k_ar5210_init_tx_queue(hal, aifs, turbo)
+ struct ath_hal *hal;
+ u_int aifs;
+ HAL_BOOL turbo;
+{
+ int i;
+ struct {
+ u_int16_t mode_register;
+ u_int32_t mode_base, mode_turbo;
+ } initial[] = AR5K_AR5210_INI_MODE(aifs);
+
+ /*
+ * Write initial mode register settings
+ */
+ for(i = 0; i < AR5K_ELEMENTS(initial); i++)
+ AR5K_REG_WRITE(initial[i].mode_register, turbo == AH_TRUE ?
+ initial[i].mode_turbo : initial[i].mode_base);
+}
+
+HAL_BOOL
+ar5k_ar5210_resetTxQueue(hal, queue)
+ struct ath_hal *hal;
+ u_int queue;
+{
+ u_int32_t cw_min, retry_lg, retry_sh;
+ HAL_TXQ_INFO *tq;
+
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ tq = &hal->ah_txq[queue];
+
+ /* Only handle data queues, others will be ignored */
+ if(tq->tqi_type != HAL_TX_QUEUE_DATA)
+ return(AH_TRUE);
+
+ /* Set turbo/base mode parameters */
+ ar5k_ar5210_init_tx_queue(hal, hal->ah_aifs + tq->tqi_aifs,
+ hal->ah_turbo == AH_TRUE ? AH_TRUE : AH_FALSE);
+
+ /*
+ * Set retry limits
+ */
+ if(hal->ah_software_retry == AH_TRUE) {
+ /* XXX Need to test this */
+ retry_lg = hal->ah_limit_tx_retries;
+ retry_sh = retry_lg = retry_lg > AR5K_AR5210_RETRY_LMT_SH_RETRY ?
+ AR5K_AR5210_RETRY_LMT_SH_RETRY : retry_lg;
+ } else {
+ retry_lg = AR5K_INIT_LG_RETRY;
+ retry_sh = AR5K_INIT_SH_RETRY;
+ }
+
+ /*
+ * Set initial content window (cw_min/cw_max)
+ */
+ cw_min = 1;
+ while(cw_min < hal->ah_cw_min)
+ cw_min = (cw_min << 1) | 1;
+
+ cw_min = tq->tqi_cw_min < 0 ?
+ (cw_min >> (-tq->tqi_cw_min)) :
+ ((cw_min << tq->tqi_cw_min) + (1 << tq->tqi_cw_min) - 1);
+
+ /* Commit values */
+ AR5K_REG_WRITE(AR5K_AR5210_RETRY_LMT,
+ (cw_min << AR5K_AR5210_RETRY_LMT_CW_MIN_S)
+ | AR5K_REG_SM(AR5K_INIT_SLG_RETRY, AR5K_AR5210_RETRY_LMT_SLG_RETRY)
+ | AR5K_REG_SM(AR5K_INIT_SSH_RETRY, AR5K_AR5210_RETRY_LMT_SSH_RETRY)
+ | AR5K_REG_SM(retry_lg, AR5K_AR5210_RETRY_LMT_LG_RETRY)
+ | AR5K_REG_SM(retry_sh, AR5K_AR5210_RETRY_LMT_SH_RETRY));
+
+ return(AH_TRUE);
+}
+
+u_int32_t
+ar5k_ar5210_getTxDP(hal, queue)
+ struct ath_hal *hal;
+ u_int queue;
+{
+ u_int16_t tx_reg;
+
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ /*
+ * Get the transmit queue descriptor pointer register by type
+ */
+ switch(hal->ah_txq[queue].tqi_type) {
+ case HAL_TX_QUEUE_DATA:
+ tx_reg = AR5K_AR5210_TXDP0;
+ break;
+ case HAL_TX_QUEUE_BEACON:
+ case HAL_TX_QUEUE_CAB:
+ tx_reg = AR5K_AR5210_TXDP1;
+ break;
+ default:
+ return(0xffffffff);
+ }
+
+ return(AR5K_REG_READ(tx_reg));
+}
+
+HAL_BOOL
+ar5k_ar5210_setTxDP(hal, queue, phys_addr)
+ struct ath_hal *hal;
+ u_int queue;
+ u_int32_t phys_addr;
+{
+ u_int16_t tx_reg;
+
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ /*
+ * Get the transmit queue descriptor pointer register by type
+ */
+ switch(hal->ah_txq[queue].tqi_type) {
+ case HAL_TX_QUEUE_DATA:
+ tx_reg = AR5K_AR5210_TXDP0;
+ break;
+ case HAL_TX_QUEUE_BEACON:
+ case HAL_TX_QUEUE_CAB:
+ tx_reg = AR5K_AR5210_TXDP1;
+ break;
+ default:
+ return(AH_FALSE);
+ }
+
+ /* Set descriptor pointer */
+ AR5K_REG_WRITE(tx_reg, phys_addr);
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_startTxDma(hal, queue)
+ struct ath_hal *hal;
+ u_int queue;
+{
+ u_int32_t tx_queue;
+
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ tx_queue = AR5K_REG_READ(AR5K_AR5210_CR);
+
+ /*
+ * Set the queue type
+ */
+ switch(hal->ah_txq[queue].tqi_type) {
+ case HAL_TX_QUEUE_DATA:
+ tx_queue |= AR5K_AR5210_CR_TXE0 & ~AR5K_AR5210_CR_TXD0;
+ break;
+
+ case HAL_TX_QUEUE_BEACON:
+ tx_queue |= AR5K_AR5210_CR_TXE1 & ~AR5K_AR5210_CR_TXD1;
+ AR5K_REG_WRITE(AR5K_AR5210_BSR,
+ AR5K_AR5210_BCR_TQ1V | AR5K_AR5210_BCR_BDMAE);
+ break;
+
+ case HAL_TX_QUEUE_CAB:
+ tx_queue |= AR5K_AR5210_CR_TXE1 & ~AR5K_AR5210_CR_TXD1;
+ AR5K_REG_WRITE(AR5K_AR5210_BSR,
+ AR5K_AR5210_BCR_TQ1FV | AR5K_AR5210_BCR_TQ1V | AR5K_AR5210_BCR_BDMAE);
+ break;
+
+ default:
+ return(AH_FALSE);
+ }
+
+ /* Start queue */
+ AR5K_REG_WRITE(AR5K_AR5210_CR, tx_queue);
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_stopTxDma(hal, queue)
+ struct ath_hal *hal;
+ u_int queue;
+{
+ u_int32_t tx_queue;
+
+ AR5K_ASSERT_ENTRY(queue, hal->ah_capabilities.cap_queues.q_tx_num);
+
+ tx_queue = AR5K_REG_READ(AR5K_AR5210_CR);
+
+ /*
+ * Set by queue type
+ */
+ switch(hal->ah_txq[queue].tqi_type) {
+ case HAL_TX_QUEUE_DATA:
+ tx_queue |= AR5K_AR5210_CR_TXD0 & ~AR5K_AR5210_CR_TXE0;
+ break;
+
+ case HAL_TX_QUEUE_BEACON:
+ case HAL_TX_QUEUE_CAB:
+ /* XXX Fix me... */
+ tx_queue |= AR5K_AR5210_CR_TXD1 & ~AR5K_AR5210_CR_TXD1;
+ AR5K_REG_WRITE(AR5K_AR5210_BSR, 0);
+ break;
+
+ default:
+ return(AH_FALSE);
+ }
+
+ /* Stop queue */
+ AR5K_REG_WRITE(AR5K_AR5210_CR, tx_queue);
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_setupTxDesc(hal, desc, packet_length, header_length, type, tx_power,
+ tx_rate0, tx_tries0, key_index, antenna_mode, flags, rtscts_rate,
+ rtscts_duration)
+ struct ath_hal *hal;
+ struct ath_desc *desc;
+ u_int packet_length;
+ u_int header_length;
+ HAL_PKT_TYPE type;
+ u_int tx_power;
+ u_int tx_rate0;
+ u_int tx_tries0;
+ u_int key_index;
+ u_int antenna_mode;
+ u_int flags;
+ u_int rtscts_rate;
+ u_int rtscts_duration;
+{
+ struct ar5k_ar5210_tx_desc *tx_desc;
+
+ tx_desc = (struct ar5k_ar5210_tx_desc*)&desc->ds_ctl0;
+
+ /* Clear descriptor */
+ bzero(tx_desc, sizeof(struct ar5k_ar5210_tx_desc));
+
+ /*
+ * Validate input
+ */
+ if(tx_tries0 == 0)
+ return(AH_FALSE);
+
+ switch(type) {
+ case HAL_PKT_TYPE_NORMAL:
+ tx_desc->frame_type = AR5K_AR5210_DESC_TX_FRAME_TYPE_NORMAL;
+ break;
+
+ case HAL_PKT_TYPE_ATIM:
+ tx_desc->frame_type = AR5K_AR5210_DESC_TX_FRAME_TYPE_ATIM;
+ break;
+
+ case HAL_PKT_TYPE_PSPOLL:
+ tx_desc->frame_type = AR5K_AR5210_DESC_TX_FRAME_TYPE_PSPOLL;
+ break;
+
+ case HAL_PKT_TYPE_BEACON:
+ case HAL_PKT_TYPE_PROBE_RESP:
+ tx_desc->frame_type = AR5K_AR5210_DESC_TX_FRAME_TYPE_NO_DELAY;
+ break;
+
+ case HAL_PKT_TYPE_PIFS:
+ tx_desc->frame_type = AR5K_AR5210_DESC_TX_FRAME_TYPE_PIFS;
+ break;
+
+ default:
+ /* Invalid packet type (possibly not supported) */
+ return(AH_FALSE);
+ }
+
+ if((tx_desc->frame_len = packet_length) != packet_length)
+ return(AH_FALSE);
+
+ if((tx_desc->header_len = header_length) != header_length)
+ return(AH_FALSE);
+
+ tx_desc->xmit_rate = tx_rate0;
+ tx_desc->ant_mode_xmit = antenna_mode ? 1 : 0;
+ tx_desc->clear_dest_mask = flags & HAL_TXDESC_CLRDMASK ? 1 : 0;
+
+ /*
+ * WEP crap
+ */
+ if(key_index != HAL_TXKEYIX_INVALID) {
+ tx_desc->encrypt_key_valid = 1;
+ tx_desc->encrypt_key_index = key_index;
+ }
+
+ /*
+ * RTS/CTS
+ */
+ if(flags & (HAL_TXDESC_RTSENA|HAL_TXDESC_CTSENA)) {
+ tx_desc->rts_cts_enable = 1;
+ tx_desc->rts_duration = rtscts_duration;
+ }
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_fillTxDesc(hal, desc, segment_length, first_segment, last_segment)
+ struct ath_hal *hal;
+ struct ath_desc *desc;
+ u_int segment_length;
+ HAL_BOOL first_segment;
+ HAL_BOOL last_segment;
+{
+ struct ar5k_ar5210_tx_desc *tx_desc;
+
+ tx_desc = (struct ar5k_ar5210_tx_desc*)&desc->ds_ctl0;
+
+ /* Clear status descriptor */
+ desc->ds_hw[0] = desc->ds_hw[1] = 0;
+
+ /* Validate segment length and initialize the descriptor */
+ if((tx_desc->buf_len = segment_length) != segment_length)
+ return(AH_FALSE);
+
+ if(first_segment != AH_TRUE)
+ tx_desc->frame_len = 0;
+
+ tx_desc->more = last_segment == AH_TRUE ? 0 : 1;
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_setupXTxDesc(hal, desc, tx_rate1, tx_tries1, tx_rate2, tx_tries2,
+ tx_rate3, tx_tries3)
+ struct ath_hal *hal;
+ struct ath_desc *desc;
+ u_int tx_rate1;
+ u_int tx_tries1;
+ u_int tx_rate2;
+ u_int tx_tries2;
+ u_int tx_rate3;
+ u_int tx_tries3;
+{
+ /*
+ * Does this function is for setting up XR? Not sure...
+ * Nevertheless, I didn't find any information about XR support
+ * by the AR5210. This seems to be a slightly new feature.
+ */
+ return(AH_FALSE);
+}
+
+HAL_STATUS
+ar5k_ar5210_procTxDesc(hal, desc)
+ struct ath_hal *hal;
+ struct ath_desc *desc;
+{
+ struct ar5k_ar5210_tx_status *tx_status;
+ struct ar5k_ar5210_tx_desc *tx_desc;
+
+ tx_desc = (struct ar5k_ar5210_tx_desc*)&desc->ds_ctl0;
+ tx_status = (struct ar5k_ar5210_tx_status*)&desc->ds_hw[0];
+
+ /* No frame has been send or error */
+ if(tx_status->done == 0)
+ return(HAL_EINPROGRESS);
+
+ /*
+ * Get descriptor status
+ */
+ desc->ds_us.tx.ts_seqnum = tx_status->seq_num;
+ desc->ds_us.tx.ts_tstamp = tx_status->send_timestamp;
+ desc->ds_us.tx.ts_shortretry = tx_status->short_retry_count;
+ desc->ds_us.tx.ts_longretry = tx_status->long_retry_count;
+ desc->ds_us.tx.ts_rssi = tx_status->ack_sig_strength;
+ desc->ds_us.tx.ts_rate = tx_desc->xmit_rate;
+ desc->ds_us.tx.ts_antenna = 0;
+ desc->ds_us.tx.ts_status = 0;
+
+ if(tx_status->frame_xmit_ok == 0) {
+ if(tx_status->excessive_retries)
+ desc->ds_us.tx.ts_status |= HAL_TXERR_XRETRY;
+
+ if(tx_status->fifo_underrun)
+ desc->ds_us.tx.ts_status |= HAL_TXERR_FIFO;
+
+ if(tx_status->filtered)
+ desc->ds_us.tx.ts_status |= HAL_TXERR_FILT;
+ }
+
+#if 0
+ /*
+ * Reset descriptor
+ */
+ bzero(tx_desc, sizeof(struct ar5k_ar5210_tx_desc));
+ bzero(tx_status, sizeof(struct ar5k_ar5210_tx_status));
+#endif
+
+ return(HAL_OK);
+}
+
+HAL_BOOL
+ar5k_ar5210_hasVEOL(hal)
+ struct ath_hal *hal;
+{
+ return(AH_FALSE);
+}
+
+/*
+ * Receive functions
+ */
+
+u_int32_t
+ar5k_ar5210_getRxDP(hal)
+ struct ath_hal *hal;
+{
+ return(AR5K_REG_READ(AR5K_AR5210_RXDP));
+}
+
+void
+ar5k_ar5210_setRxDP(hal, phys_addr)
+ struct ath_hal *hal;
+ u_int32_t phys_addr;
+{
+ AR5K_REG_WRITE(AR5K_AR5210_RXDP, phys_addr);
+}
+
+void
+ar5k_ar5210_enableReceive(hal)
+ struct ath_hal *hal;
+{
+ AR5K_REG_WRITE(AR5K_AR5210_CR, AR5K_AR5210_CR_RXE);
+}
+
+HAL_BOOL
+ar5k_ar5210_stopDmaReceive(hal)
+ struct ath_hal *hal;
+{
+ int i;
+
+ AR5K_REG_WRITE(AR5K_AR5210_CR, AR5K_AR5210_CR_RXD);
+
+ /*
+ * It may take some time to disable the DMA receive unit
+ */
+ for(i = 2000;
+ i > 0 && (AR5K_REG_READ(AR5K_AR5210_CR) & AR5K_AR5210_CR_RXE) != 0;
+ i--)
+ AR5K_DELAY(10);
+
+ return(i > 0 ? AH_TRUE : AH_FALSE);
+}
+
+void
+ar5k_ar5210_startPcuReceive(hal)
+ struct ath_hal *hal;
+{
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_DIAG_SW, AR5K_AR5210_DIAG_SW_DIS_RX);
+}
+
+void
+ar5k_ar5210_stopPcuReceive(hal)
+ struct ath_hal *hal;
+{
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_DIAG_SW, AR5K_AR5210_DIAG_SW_DIS_RX);
+}
+
+void
+ar5k_ar5210_setMulticastFilter(hal, filter0, filter1)
+ struct ath_hal *hal;
+ u_int32_t filter0;
+ u_int32_t filter1;
+{
+ /* Set the multicat filter */
+ AR5K_REG_WRITE(AR5K_AR5210_MCAST_FIL0, filter0);
+ AR5K_REG_WRITE(AR5K_AR5210_MCAST_FIL1, filter1);
+}
+
+HAL_BOOL
+ar5k_ar5210_setMulticastFilterIndex(hal, index)
+ struct ath_hal *hal;
+ u_int32_t index;
+{
+ if(index >= 64)
+ return(AH_FALSE);
+ else if(index >= 32)
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_MCAST_FIL1,
+ (1 << (index - 32)));
+ else
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_MCAST_FIL0,
+ (1 << index));
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_clrMulticastFilterIndex(hal, index)
+ struct ath_hal *hal;
+ u_int32_t index;
+{
+ if(index >= 64)
+ return(AH_FALSE);
+ else if(index >= 32)
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_MCAST_FIL1,
+ (1 << (index - 32)));
+ else
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_MCAST_FIL0,
+ (1 << index));
+
+ return(AH_TRUE);
+}
+
+u_int32_t
+ar5k_ar5210_getRxFilter(hal)
+ struct ath_hal *hal;
+{
+ return(AR5K_REG_READ(AR5K_AR5210_RX_FILTER));
+}
+
+void
+ar5k_ar5210_setRxFilter(hal, filter)
+ struct ath_hal *hal;
+ u_int32_t filter;
+{
+ /*
+ * The AR5210 uses promiscous mode to detect radar activity
+ */
+ if(filter & HAL_RX_FILTER_PHYRADAR) {
+ filter &= ~HAL_RX_FILTER_PHYRADAR;
+ filter |= AR5K_AR5210_RX_FILTER_PROMISC;
+ }
+
+ AR5K_REG_WRITE(AR5K_AR5210_RX_FILTER, filter);
+}
+
+HAL_BOOL
+ar5k_ar5210_setupRxDesc(hal, desc, size, flags)
+ struct ath_hal *hal;
+ struct ath_desc *desc;
+ u_int32_t size;
+ u_int flags;
+{
+ struct ar5k_ar5210_rx_desc *rx_desc;
+
+ /* Reset descriptor */
+ desc->ds_ctl0 = 0;
+ desc->ds_ctl1 = 0;
+ bzero(&desc->ds_hw[0], sizeof(struct ar5k_ar5210_rx_status));
+
+ rx_desc = (struct ar5k_ar5210_rx_desc*)&desc->ds_ctl0;
+
+ if((rx_desc->buf_len = size) != size)
+ return(AH_FALSE);
+
+ if(flags & HAL_RXDESC_INTREQ)
+ rx_desc->inter_req = 1;
+
+ return(AH_TRUE);
+}
+
+HAL_STATUS
+ar5k_ar5210_procRxDesc(hal, desc, phys_addr, next)
+ struct ath_hal *hal;
+ struct ath_desc *desc;
+ u_int32_t phys_addr;
+ struct ath_desc *next;
+{
+ u_int32_t now, tstamp;
+ struct ar5k_ar5210_rx_status *rx_status;
+
+ rx_status = (struct ar5k_ar5210_rx_status*)&desc->ds_hw[0];
+
+ /* No frame received / not ready */
+ if(!rx_status->done)
+ return(HAL_EINPROGRESS);
+
+ /*
+ * Frame receive status
+ */
+ now = (AR5K_REG_READ(AR5K_AR5210_TSF_L32) >> 10) & 0xffff;
+ tstamp = ((now & 0x1fff) < rx_status->receive_timestamp) ?
+ (((now - 0x2000) & 0xffff) | (u_int32_t) rx_status->receive_timestamp) :
+ (now | (u_int32_t) rx_status->receive_timestamp);
+ desc->ds_us.rx.rs_tstamp = rx_status->receive_timestamp & 0x7fff;
+ desc->ds_us.rx.rs_datalen = rx_status->data_len;
+ desc->ds_us.rx.rs_rssi = rx_status->receive_sig_strength;
+ desc->ds_us.rx.rs_rate = rx_status->receive_rate;
+ desc->ds_us.rx.rs_antenna = rx_status->receive_antenna ? 1 : 0;
+ desc->ds_us.rx.rs_more = rx_status->more ? 1 : 0;
+ desc->ds_us.rx.rs_status = 0;
+
+ /*
+ * Key table status
+ */
+ if(!rx_status->key_index_valid)
+ desc->ds_us.rx.rs_keyix = HAL_RXKEYIX_INVALID;
+ else
+ desc->ds_us.rx.rs_keyix = rx_status->key_index;
+
+ /*
+ * Receive/descriptor errors
+ */
+ if(!rx_status->frame_receive_ok) {
+ if(rx_status->crc_error)
+ desc->ds_us.rx.rs_status |= HAL_RXERR_CRC;
+
+ if(rx_status->phy_error) {
+ desc->ds_us.rx.rs_status |= HAL_RXERR_PHY;
+ desc->ds_us.rx.rs_phyerr = rx_status->phy_error;
+ }
+
+ if(rx_status->fifo_overrun)
+ desc->ds_us.rx.rs_status |= HAL_RXERR_FIFO;
+
+ if(rx_status->decrypt_crc_error)
+ desc->ds_us.rx.rs_status |= HAL_RXERR_DECRYPT;
+ }
+
+ return(HAL_OK);
+}
+
+void
+ar5k_ar5210_rxMonitor(hal)
+ struct ath_hal *hal;
+{
+ /*
+ * XXX Not sure, if this works correctly.
+ */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_RX_FILTER,
+ AR5K_AR5210_RX_FILTER_PROMISC);
+}
+
+/*
+ * Misc functions
+ */
+
+void
+ar5k_ar5210_dumpState(hal)
+ struct ath_hal *hal;
+{
+#define AR5K_PRINT_REGISTER(_x) \
+ printf("(%s: %08x) ", #_x, AR5K_REG_READ(AR5K_AR5210_##_x));
+
+ printf("DMA registers:\n");
+ AR5K_PRINT_REGISTER(TXDP0);
+ AR5K_PRINT_REGISTER(TXDP1);
+ AR5K_PRINT_REGISTER(CR);
+ AR5K_PRINT_REGISTER(RXDP);
+ AR5K_PRINT_REGISTER(CFG);
+ AR5K_PRINT_REGISTER(ISR);
+ AR5K_PRINT_REGISTER(IMR);
+ AR5K_PRINT_REGISTER(IER);
+ AR5K_PRINT_REGISTER(BCR);
+ AR5K_PRINT_REGISTER(BSR);
+ AR5K_PRINT_REGISTER(TXCFG);
+ AR5K_PRINT_REGISTER(RXCFG);
+ AR5K_PRINT_REGISTER(MIBC);
+ AR5K_PRINT_REGISTER(TOPS);
+ AR5K_PRINT_REGISTER(RXNOFRM);
+ AR5K_PRINT_REGISTER(TXNOFRM);
+ AR5K_PRINT_REGISTER(RPGTO);
+ AR5K_PRINT_REGISTER(RFCNT);
+ AR5K_PRINT_REGISTER(MISC);
+ AR5K_PRINT_REGISTER(RC);
+ AR5K_PRINT_REGISTER(SCR);
+ AR5K_PRINT_REGISTER(INTPEND);
+ AR5K_PRINT_REGISTER(SFR);
+ AR5K_PRINT_REGISTER(PCICFG);
+ AR5K_PRINT_REGISTER(GPIOCR);
+ AR5K_PRINT_REGISTER(GPIODO);
+ AR5K_PRINT_REGISTER(GPIODI);
+ AR5K_PRINT_REGISTER(SREV);
+ printf("\n");
+
+ printf("PCU registers:\n");
+ AR5K_PRINT_REGISTER(STA_ID0);
+ AR5K_PRINT_REGISTER(STA_ID1);
+ AR5K_PRINT_REGISTER(BSS_ID0);
+ AR5K_PRINT_REGISTER(BSS_ID1);
+ AR5K_PRINT_REGISTER(SLOT_TIME);
+ AR5K_PRINT_REGISTER(TIME_OUT);
+ AR5K_PRINT_REGISTER(RSSI_THR);
+ AR5K_PRINT_REGISTER(RETRY_LMT);
+ AR5K_PRINT_REGISTER(USEC);
+ AR5K_PRINT_REGISTER(BEACON);
+ AR5K_PRINT_REGISTER(CFP_PERIOD);
+ AR5K_PRINT_REGISTER(TIMER0);
+ AR5K_PRINT_REGISTER(TIMER1);
+ AR5K_PRINT_REGISTER(TIMER2);
+ AR5K_PRINT_REGISTER(TIMER3);
+ AR5K_PRINT_REGISTER(IFS0);
+ AR5K_PRINT_REGISTER(IFS1);
+ AR5K_PRINT_REGISTER(CFP_DUR);
+ AR5K_PRINT_REGISTER(RX_FILTER);
+ AR5K_PRINT_REGISTER(MCAST_FIL0);
+ AR5K_PRINT_REGISTER(MCAST_FIL1);
+ AR5K_PRINT_REGISTER(TX_MASK0);
+ AR5K_PRINT_REGISTER(TX_MASK1);
+ AR5K_PRINT_REGISTER(CLR_TMASK);
+ AR5K_PRINT_REGISTER(TRIG_LVL);
+ AR5K_PRINT_REGISTER(DIAG_SW);
+ AR5K_PRINT_REGISTER(TSF_L32);
+ AR5K_PRINT_REGISTER(TSF_U32);
+ AR5K_PRINT_REGISTER(LAST_TSTP);
+ AR5K_PRINT_REGISTER(RETRY_CNT);
+ AR5K_PRINT_REGISTER(BACKOFF);
+ AR5K_PRINT_REGISTER(NAV);
+ AR5K_PRINT_REGISTER(RTS_OK);
+ AR5K_PRINT_REGISTER(RTS_FAIL);
+ AR5K_PRINT_REGISTER(ACK_FAIL);
+ AR5K_PRINT_REGISTER(FCS_FAIL);
+ AR5K_PRINT_REGISTER(BEACON_CNT);
+ AR5K_PRINT_REGISTER(KEYTABLE_0);
+ printf("\n");
+
+ printf("PHY registers:\n");
+ AR5K_PRINT_REGISTER(PHY(0));
+ AR5K_PRINT_REGISTER(PHY_FC);
+ AR5K_PRINT_REGISTER(PHY_AGC);
+ AR5K_PRINT_REGISTER(PHY_CHIP_ID);
+ AR5K_PRINT_REGISTER(PHY_ACTIVE);
+ AR5K_PRINT_REGISTER(PHY_AGCCTL);
+ printf("\n");
+}
+
+HAL_BOOL
+ar5k_ar5210_getDiagState(hal, id, device, size)
+ struct ath_hal *hal;
+ int id;
+ void **device;
+ u_int *size;
+
+{
+ /*
+ * We'll ignore this right now. This seems to be some kind of an obscure
+ * debugging interface for the binary-only HAL.
+ */
+ return(AH_FALSE);
+}
+
+void
+ar5k_ar5210_getMacAddress(hal, mac)
+ struct ath_hal *hal;
+ u_int8_t *mac;
+{
+ memcpy(mac, hal->ah_sta_id, IEEE80211_ADDR_LEN);
+}
+
+HAL_BOOL
+ar5k_ar5210_setMacAddress(hal, mac)
+ struct ath_hal *hal;
+ const u_int8_t *mac;
+{
+ u_int32_t low_id, high_id;
+
+ /* Set new station ID */
+ memcpy(hal->ah_sta_id, mac, IEEE80211_ADDR_LEN);
+
+ memcpy(&low_id, mac, 4);
+ memcpy(&high_id, mac + 4, 2);
+ high_id = 0x0000ffff & htole32(high_id);
+
+ AR5K_REG_WRITE(AR5K_AR5210_STA_ID0, htole32(low_id));
+ AR5K_REG_WRITE(AR5K_AR5210_STA_ID1, high_id);
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_setRegulatoryDomain(hal, regdomain, status)
+ struct ath_hal *hal;
+ u_int16_t regdomain;
+ HAL_STATUS *status;
+
+{
+ if(ar5k_ar5210_regulation_domain(hal, AH_TRUE,
+ ar5k_regdomain_to_ieee((u_int8_t)regdomain)) == AH_TRUE) {
+ *status = HAL_OK;
+ return(AH_TRUE);
+ }
+
+ *status = -EIO;
+
+ return(AH_FALSE);
+}
+
+void
+ar5k_ar5210_setLedState(hal, state)
+ struct ath_hal *hal;
+ HAL_LED_STATE state;
+{
+ u_int32_t led;
+
+ led = AR5K_REG_READ(AR5K_AR5210_PCICFG);
+
+ /*
+ * Some blinking values, define at your wish
+ */
+ switch(state) {
+ case IEEE80211_S_SCAN:
+ case IEEE80211_S_INIT:
+ led |=
+ AR5K_AR5210_PCICFG_LED_PEND |
+ AR5K_AR5210_PCICFG_LED_BCTL;
+ break;
+ case IEEE80211_S_RUN:
+ led |=
+ AR5K_AR5210_PCICFG_LED_ACT;
+ break;
+ default:
+ led |=
+ AR5K_AR5210_PCICFG_LED_ACT |
+ AR5K_AR5210_PCICFG_LED_BCTL;
+ break;
+ }
+
+ AR5K_REG_WRITE(AR5K_AR5210_PCICFG, led);
+}
+
+void
+ar5k_ar5210_writeAssocid(hal, bssid, assoc_id, tim_offset)
+ struct ath_hal *hal;
+ const u_int8_t *bssid;
+ u_int16_t assoc_id;
+ u_int16_t tim_offset;
+{
+ u_int32_t low_id, high_id;
+
+ /*
+ * Set BSSID which triggers the "SME Join" operation
+ */
+ memcpy(&low_id, bssid, 4);
+ memcpy(&high_id, bssid + 4, 2);
+ AR5K_REG_WRITE(AR5K_AR5210_BSS_ID0, htole32(low_id));
+ AR5K_REG_WRITE(AR5K_AR5210_BSS_ID1, htole32(high_id) |
+ ((assoc_id & 0x3fff) << AR5K_AR5210_BSS_ID1_AID_S));
+ memcpy(&hal->ah_bssid, bssid, IEEE80211_ADDR_LEN);
+
+ if(assoc_id == 0) {
+ ar5k_ar5210_disablePSPoll(hal);
+ return;
+ }
+
+ AR5K_REG_WRITE(AR5K_AR5210_BEACON,
+ (AR5K_REG_READ(AR5K_AR5210_BEACON) &
+ ~AR5K_AR5210_BEACON_TIM) |
+ (((tim_offset ? tim_offset + 4 : 0) <<
+ AR5K_AR5210_BEACON_TIM_S) &
+ AR5K_AR5210_BEACON_TIM));
+
+ ar5k_ar5210_enablePSPoll(hal, NULL, 0);
+}
+
+HAL_BOOL
+ar5k_ar5210_gpioCfgOutput(hal, gpio)
+ struct ath_hal *hal;
+ u_int32_t gpio;
+{
+ if(gpio > AR5K_AR5210_NUM_GPIO)
+ return(AH_FALSE);
+
+ AR5K_REG_WRITE(AR5K_AR5210_GPIOCR,
+ (AR5K_REG_READ(AR5K_AR5210_GPIOCR) &~ AR5K_AR5210_GPIOCR_ALL(gpio))
+ | AR5K_AR5210_GPIOCR_OUT1(gpio));
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_gpioCfgInput(hal, gpio)
+ struct ath_hal *hal;
+ u_int32_t gpio;
+{
+ if(gpio > AR5K_AR5210_NUM_GPIO)
+ return(AH_FALSE);
+
+ AR5K_REG_WRITE(AR5K_AR5210_GPIOCR,
+ (AR5K_REG_READ(AR5K_AR5210_GPIOCR) &~ AR5K_AR5210_GPIOCR_ALL(gpio))
+ | AR5K_AR5210_GPIOCR_IN(gpio));
+
+ return(AH_TRUE);
+}
+
+u_int32_t
+ar5k_ar5210_gpioGet(hal, gpio)
+ struct ath_hal *hal;
+ u_int32_t gpio;
+{
+ if(gpio > AR5K_AR5210_NUM_GPIO)
+ return(0xffffffff);
+
+ /* GPIO input magic */
+ return(((AR5K_REG_READ(AR5K_AR5210_GPIODI) &
+ AR5K_AR5210_GPIOD_MASK) >> gpio) & 0x1);
+}
+
+HAL_BOOL
+ar5k_ar5210_gpioSet(hal, gpio, val)
+ struct ath_hal *hal;
+ u_int32_t gpio;
+ u_int32_t val;
+{
+ u_int32_t data;
+
+ if(gpio > AR5K_AR5210_NUM_GPIO)
+ return(0xffffffff);
+
+ /* GPIO output magic */
+ data = AR5K_REG_READ(AR5K_AR5210_GPIODO);
+
+ data &= ~(1 << gpio);
+ data |= (val&1) << gpio;
+
+ AR5K_REG_WRITE(AR5K_AR5210_GPIODO, data);
+
+ return(AH_TRUE);
+}
+
+void
+ar5k_ar5210_gpioSetIntr(hal, gpio, interrupt_level)
+ struct ath_hal *hal;
+ u_int gpio;
+ u_int32_t interrupt_level;
+{
+ u_int32_t data;
+
+ if(gpio > AR5K_AR5210_NUM_GPIO)
+ return;
+
+ /*
+ * Set the GPIO interrupt
+ */
+ data = (AR5K_REG_READ(AR5K_AR5210_GPIOCR) &
+ ~(AR5K_AR5210_GPIOCR_INT_SEL(gpio) | AR5K_AR5210_GPIOCR_INT_SELH |
+ AR5K_AR5210_GPIOCR_INT_ENA | AR5K_AR5210_GPIOCR_ALL(gpio))) |
+ (AR5K_AR5210_GPIOCR_INT_SEL(gpio) | AR5K_AR5210_GPIOCR_INT_ENA);
+
+ AR5K_REG_WRITE(AR5K_AR5210_GPIOCR,
+ interrupt_level ? data : (data | AR5K_AR5210_GPIOCR_INT_SELH));
+
+ hal->ah_imr |= AR5K_AR5210_IMR_GPIO;
+
+ /* Enable GPIO interrupts */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_IMR, AR5K_AR5210_IMR_GPIO);
+}
+
+u_int32_t
+ar5k_ar5210_getTsf32(hal)
+ struct ath_hal *hal;
+{
+ return(AR5K_REG_READ(AR5K_AR5210_TSF_L32));
+}
+
+u_int64_t
+ar5k_ar5210_getTsf64(hal)
+ struct ath_hal *hal;
+{
+ u_int64_t tsf = AR5K_REG_READ(AR5K_AR5210_TSF_U32);
+ return(AR5K_REG_READ(AR5K_AR5210_TSF_L32) | (tsf << 32));
+}
+
+void
+ar5k_ar5210_resetTsf(hal)
+ struct ath_hal *hal;
+{
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_BEACON,
+ AR5K_AR5210_BEACON_RESET_TSF);
+}
+
+u_int16_t
+ar5k_ar5210_getRegDomain(hal)
+ struct ath_hal *hal;
+{
+ u_int16_t regdomain;
+ ieee80211_regdomain_t ieee_regdomain;
+
+ if(ar5k_ar5210_regulation_domain(hal, AH_FALSE, &ieee_regdomain) == AH_TRUE) {
+ regdomain = ar5k_regdomain_from_ieee(&ieee_regdomain);
+ return(regdomain > 0 ? regdomain : hal->ah_regdomain);
+ }
+
+ return(0);
+}
+
+HAL_BOOL
+ar5k_ar5210_detectCardPresent(hal)
+ struct ath_hal *hal;
+{
+ u_int16_t magic;
+
+ /*
+ * Checking the EEPROM's magic value could be an indication
+ * if the card is still present. I didn't find another suitable
+ * way to do this.
+ */
+ if(ar5k_ar5210_eeprom_read(hal, AR5K_AR5210_EEPROM_MAGIC, &magic) != 0)
+ return(AH_FALSE);
+
+ return(magic == AR5K_AR5210_EEPROM_MAGIC_VALUE ? AH_TRUE : AH_FALSE);
+}
+
+void
+ar5k_ar5210_updateMibCounters(hal, statistics)
+ struct ath_hal *hal;
+ HAL_MIB_STATS *statistics;
+{
+ statistics->ackrcv_bad += AR5K_REG_READ(AR5K_AR5210_ACK_FAIL);
+ statistics->rts_bad += AR5K_REG_READ(AR5K_AR5210_RTS_FAIL);
+ statistics->rts_good += AR5K_REG_READ(AR5K_AR5210_RTS_OK);
+ statistics->fcs_bad += AR5K_REG_READ(AR5K_AR5210_FCS_FAIL);
+ statistics->beacons += AR5K_REG_READ(AR5K_AR5210_BEACON_CNT);
+}
+
+HAL_RFGAIN
+ar5k_ar5210_getRfGain(hal)
+ struct ath_hal *hal;
+{
+ return(HAL_RFGAIN_INACTIVE);
+}
+
+HAL_BOOL
+ar5k_ar5210_setSlotTime(hal, slot_time)
+ struct ath_hal *hal;
+ u_int slot_time;
+
+{
+ if(slot_time < HAL_SLOT_TIME_9 || slot_time > HAL_SLOT_TIME_MAX)
+ return(AH_FALSE);
+
+ AR5K_REG_WRITE(AR5K_AR5210_SLOT_TIME,
+ ar5k_htoclock(slot_time, hal->ah_turbo));
+
+ return(AH_TRUE);
+}
+
+u_int
+ar5k_ar5210_getSlotTime(hal)
+ struct ath_hal *hal;
+{
+ return(ar5k_clocktoh(AR5K_REG_READ(AR5K_AR5210_SLOT_TIME) & 0xffff,
+ hal->ah_turbo));
+}
+
+HAL_BOOL
+ar5k_ar5210_setAckTimeout(hal, timeout)
+ struct ath_hal *hal;
+ u_int timeout;
+{
+ if(ar5k_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_AR5210_TIME_OUT_ACK),
+ hal->ah_turbo) <= timeout)
+ return(AH_FALSE);
+
+ AR5K_REG_WRITE_BITS(AR5K_AR5210_TIME_OUT, AR5K_AR5210_TIME_OUT_ACK,
+ ar5k_htoclock(timeout, hal->ah_turbo));
+
+ return(AH_TRUE);
+}
+
+u_int
+ar5k_ar5210_getAckTimeout(hal)
+ struct ath_hal *hal;
+{
+ return(ar5k_clocktoh(AR5K_REG_MS(AR5K_REG_READ(AR5K_AR5210_TIME_OUT),
+ AR5K_AR5210_TIME_OUT_ACK), hal->ah_turbo));
+}
+
+HAL_BOOL
+ar5k_ar5210_setCTSTimeout(hal, timeout)
+ struct ath_hal *hal;
+ u_int timeout;
+{
+ if(ar5k_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_AR5210_TIME_OUT_CTS),
+ hal->ah_turbo) <= timeout)
+ return(AH_FALSE);
+
+ AR5K_REG_WRITE_BITS(AR5K_AR5210_TIME_OUT, AR5K_AR5210_TIME_OUT_CTS,
+ ar5k_htoclock(timeout, hal->ah_turbo));
+
+ return(AH_TRUE);
+}
+
+u_int
+ar5k_ar5210_getCTSTimeout(hal)
+ struct ath_hal *hal;
+{
+ return(ar5k_clocktoh(AR5K_REG_MS(AR5K_REG_READ(AR5K_AR5210_TIME_OUT),
+ AR5K_AR5210_TIME_OUT_CTS), hal->ah_turbo));
+}
+
+/*
+ * Key table (WEP) functions
+ */
+
+HAL_BOOL
+ar5k_ar5210_isHwCipherSupported(hal, cipher)
+ struct ath_hal *hal;
+ HAL_CIPHER cipher;
+{
+ /*
+ * The AR5210 only supports WEP
+ */
+ if(cipher == HAL_CIPHER_WEP)
+ return(AH_TRUE);
+
+ return(AH_FALSE);
+}
+
+u_int32_t
+ar5k_ar5210_getKeyCacheSize(hal)
+ struct ath_hal *hal;
+{
+ return(AR5K_AR5210_KEYTABLE_SIZE);
+}
+
+HAL_BOOL
+ar5k_ar5210_resetKeyCacheEntry(hal, entry)
+ struct ath_hal *hal;
+ u_int16_t entry;
+{
+ int i;
+
+ AR5K_ASSERT_ENTRY(entry, AR5K_AR5210_KEYTABLE_SIZE);
+
+ for(i = 0; i < AR5K_AR5210_KEYCACHE_SIZE; i++)
+ AR5K_REG_WRITE(AR5K_AR5210_KEYTABLE(entry) + (i * 4), 0);
+
+ return(AH_FALSE);
+}
+
+HAL_BOOL
+ar5k_ar5210_isKeyCacheEntryValid(hal, entry)
+ struct ath_hal *hal;
+ u_int16_t entry;
+{
+ int offset;
+
+ AR5K_ASSERT_ENTRY(entry, AR5K_AR5210_KEYTABLE_SIZE);
+
+ /*
+ * Check the validation flag at the end of the entry
+ */
+ offset = (AR5K_AR5210_KEYCACHE_SIZE - 1) * 4;
+ if(AR5K_REG_READ(AR5K_AR5210_KEYTABLE(entry) + offset) &
+ AR5K_AR5210_KEYTABLE_VALID)
+ return AH_TRUE;
+
+ return(AH_FALSE);
+}
+
+HAL_BOOL
+ar5k_ar5210_setKeyCacheEntry(hal, entry, keyval, mac, xor_notused)
+ struct ath_hal *hal;
+ u_int16_t entry;
+ const HAL_KEYVAL *keyval;
+ const u_int8_t *mac;
+ int xor_notused;
+{
+ int elements = AR5K_AR5210_KEYCACHE_SIZE - 2;
+ u_int32_t key_v[elements];
+ int i, offset = 0;
+
+ AR5K_ASSERT_ENTRY(entry, AR5K_AR5210_KEYTABLE_SIZE);
+
+ /*
+ * Store the key type in the last field
+ */
+ switch(keyval->wk_len) {
+ case 5:
+ key_v[elements - 1] = AR5K_AR5210_KEYTABLE_TYPE_40;
+ break;
+
+ case 13:
+ key_v[elements - 1] = AR5K_AR5210_KEYTABLE_TYPE_104;
+ break;
+
+ case 16:
+ key_v[elements - 1] = AR5K_AR5210_KEYTABLE_TYPE_128;
+ break;
+
+ default:
+ /* Unsupported key length (not WEP40/104/128) */
+ return(AH_FALSE);
+ }
+
+ /*
+ * Write key cache entry
+ */
+ for(i = 0; i < elements; i++) {
+ if(elements < 5) {
+ if(i % 2) {
+ key_v[i] = AR5K_LE_READ_2(keyval->wk_key +
+ offset) & 0xffff;
+ offset += 2;
+ } else {
+ key_v[i] = AR5K_LE_READ_4(keyval->wk_key +
+ offset);
+ offset += 4;
+ }
+
+ if(i == 4 && keyval->wk_len <= 13)
+ key_v[i] &= 0xff;
+ }
+
+ /* Write value */
+ AR5K_REG_WRITE(AR5K_AR5210_KEYTABLE(entry) + (i * 4), key_v[i]);
+ }
+
+ return(ar5k_ar5210_setKeyCacheEntryMac(hal, entry, mac));
+}
+
+HAL_BOOL
+ar5k_ar5210_setKeyCacheEntryMac(hal, entry, mac)
+ struct ath_hal *hal;
+ u_int16_t entry;
+ const u_int8_t *mac;
+{
+ u_int32_t low_id, high_id;
+ int offset;
+
+ /*
+ * Invalid entry (key table overflow)
+ */
+ AR5K_TRACE;
+
+ AR5K_ASSERT_ENTRY(entry, AR5K_AR5210_KEYTABLE_SIZE);
+
+ AR5K_TRACE;
+
+ offset = AR5K_AR5210_KEYCACHE_SIZE - 2;
+
+ AR5K_TRACE;
+
+ /* XXX big endian problems? */
+ bcopy(mac, &low_id, 4);
+
+ AR5K_TRACE;
+
+ bcopy(mac + 4, &high_id, 2);
+
+ AR5K_TRACE;
+
+ high_id = 0x0000ffff & htole32(high_id);
+
+ AR5K_TRACE;
+
+ AR5K_REG_WRITE(AR5K_AR5210_KEYTABLE(entry) + (offset++ * 4), htole32(low_id));
+
+ AR5K_TRACE;
+
+ AR5K_REG_WRITE(AR5K_AR5210_KEYTABLE(entry) + (offset * 4), high_id);
+
+ AR5K_TRACE;
+
+ return(AH_TRUE);
+}
+
+/*
+ * Power management functions
+ */
+
+HAL_BOOL
+ar5k_ar5210_setPowerMode(hal, mode, set_chip, sleep_duration)
+ struct ath_hal *hal;
+ HAL_POWER_MODE mode;
+ int set_chip;
+ u_int16_t sleep_duration;
+{
+ int i;
+
+ switch(mode) {
+ case HAL_PM_AUTO:
+ if(set_chip)
+ AR5K_REG_WRITE(AR5K_AR5210_SCR,
+ AR5K_AR5210_SCR_SLE | sleep_duration);
+ break;
+
+ case HAL_PM_FULL_SLEEP:
+ if(set_chip)
+ AR5K_REG_WRITE(AR5K_AR5210_SCR, AR5K_AR5210_SCR_SLE_SLP);
+ break;
+
+ case HAL_PM_AWAKE:
+ if(!set_chip)
+ goto commit;
+
+ AR5K_REG_WRITE(AR5K_AR5210_SCR, AR5K_AR5210_SCR_SLE_WAKE);
+
+ for(i = 5000; i > 0; i--) {
+ /* Check if the AR5210 did wake up */
+ if((AR5K_REG_READ(AR5K_AR5210_PCICFG) &
+ AR5K_AR5210_PCICFG_SPWR_DN) == 0)
+ break;
+
+ /* Wait a bit and retry */
+ AR5K_DELAY(200);
+ AR5K_REG_WRITE(AR5K_AR5210_SCR,
+ AR5K_AR5210_SCR_SLE_WAKE);
+ }
+
+ /* Fail if the AR5210 didn't wake up */
+ if(i <= 0)
+ return(AH_FALSE);
+ break;
+
+ case HAL_PM_NETWORK_SLEEP:
+ case HAL_PM_UNDEFINED:
+ default:
+ return(AH_FALSE);
+ }
+
+ commit:
+ hal->ah_power_mode = mode;
+
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_DEFAULT_ANTENNA);
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_PWR_SV);
+
+ return(AH_TRUE);
+}
+
+HAL_POWER_MODE
+ar5k_ar5210_getPowerMode(hal)
+ struct ath_hal *hal;
+{
+ return(hal->ah_power_mode);
+}
+
+HAL_BOOL
+ar5k_ar5210_queryPSPollSupport(hal)
+ struct ath_hal *hal;
+{
+ /* I think so, why not? */
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_initPSPoll(hal)
+ struct ath_hal *hal;
+{
+ /*
+ * Not used on the AR5210
+ */
+ return(AH_FALSE);
+}
+
+HAL_BOOL
+ar5k_ar5210_enablePSPoll(hal, bssid, assoc_id)
+ struct ath_hal *hal;
+ u_int8_t *bssid;
+ u_int16_t assoc_id;
+{
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_NO_PSPOLL |
+ AR5K_AR5210_STA_ID1_DEFAULT_ANTENNA);
+
+ return(AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_disablePSPoll(hal)
+ struct ath_hal *hal;
+{
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_NO_PSPOLL |
+ AR5K_AR5210_STA_ID1_DEFAULT_ANTENNA);
+
+ return(AH_TRUE);
+}
+
+/*
+ * Beacon functions
+ */
+
+void
+ar5k_ar5210_beaconInit(hal, next_beacon, interval)
+ struct ath_hal *hal;
+ u_int32_t next_beacon;
+ u_int32_t interval;
+{
+ u_int32_t timer1, timer2, timer3;
+
+ /*
+ * Set the additional timers by mode
+ */
+ switch(hal->ah_op_mode) {
+ case HAL_M_STA:
+ timer1 = 0xffffffff;
+ timer2 = 0xffffffff;
+ timer3 = 1;
+ break;
+
+ default:
+ timer1 = (next_beacon - AR5K_TUNE_DMA_BEACON_RESP) <<
+ 0x00000003;
+ timer2 = (next_beacon - AR5K_TUNE_SW_BEACON_RESP) <<
+ 0x00000003;
+ timer3 = next_beacon + hal->ah_atim_window;
+ }
+
+ /*
+ * Enable all timers and set the beacon register
+ * (next beacon, DMA beacon, software beacon, ATIM window time)
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER0, next_beacon);
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER1, timer1);
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER2, timer2);
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER3, timer3);
+
+ AR5K_REG_WRITE(AR5K_AR5210_BEACON, interval &
+ (AR5K_AR5210_BEACON_PERIOD | AR5K_AR5210_BEACON_RESET_TSF |
+ AR5K_AR5210_BEACON_EN));
+}
+
+void
+ar5k_ar5210_setStationBeaconTimers(hal, state, tsf, dtim_count, cfp_count)
+ struct ath_hal *hal;
+ const HAL_BEACON_STATE *state;
+ u_int32_t tsf;
+ u_int32_t dtim_count;
+ u_int32_t cfp_count;
+
+{
+ u_int32_t cfp_period, next_cfp;
+
+ /* Return on an invalid beacon state */
+ if(state->bs_interval > 0)
+ return;
+
+ /*
+ * PCF support?
+ */
+ if(state->bs_cfp_period > 0) {
+ /* Enable CFP mode and set the CFP and timer registers */
+ cfp_period = state->bs_cfp_period * state->bs_dtim_period *
+ state->bs_interval;
+ next_cfp = (cfp_count * state->bs_dtim_period + dtim_count) *
+ state->bs_interval;
+
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_DEFAULT_ANTENNA |
+ AR5K_AR5210_STA_ID1_PCF);
+ AR5K_REG_WRITE(AR5K_AR5210_CFP_PERIOD, cfp_period);
+ AR5K_REG_WRITE(AR5K_AR5210_CFP_DUR, state->bs_cfp_max_duration);
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER2,
+ (tsf + (next_cfp == 0 ? cfp_period : next_cfp)) << 3);
+ } else {
+ /* Disable PCF mode */
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_DEFAULT_ANTENNA |
+ AR5K_AR5210_STA_ID1_PCF);
+ }
+
+ /*
+ * Enable the beacon timer register
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER0, state->bs_next_beacon);
+
+ /*
+ * Start the beacon timers
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_BEACON, (AR5K_REG_READ(AR5K_AR5210_BEACON) &~
+ (AR5K_AR5210_BEACON_PERIOD | AR5K_AR5210_BEACON_TIM)) |
+ AR5K_REG_SM(state->bs_tim_offset ? state->bs_tim_offset + 4 : 0,
+ AR5K_AR5210_BEACON_TIM) | AR5K_REG_SM(state->bs_interval,
+ AR5K_AR5210_BEACON_PERIOD));
+
+ /*
+ * Write new beacon miss threshold, if it appears to be valid
+ */
+ if((state->bs_bmiss_threshold >
+ (AR5K_AR5210_RSSI_THR_BM_THR >> AR5K_AR5210_RSSI_THR_BM_THR_S)) &&
+ (state->bs_bmiss_threshold & 0x00007) != 0)
+ AR5K_REG_WRITE_BITS(AR5K_AR5210_RSSI_THR,
+ AR5K_AR5210_RSSI_THR_BM_THR, state->bs_bmiss_threshold);
+}
+
+void
+ar5k_ar5210_resetStationBeaconTimers(hal)
+ struct ath_hal *hal;
+{
+ /*
+ * Disable beacon timer
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_TIMER0, 0);
+
+ /*
+ * Disable some beacon register values
+ */
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_STA_ID1,
+ AR5K_AR5210_STA_ID1_DEFAULT_ANTENNA | AR5K_AR5210_STA_ID1_PCF);
+ AR5K_REG_WRITE(AR5K_AR5210_BEACON, AR5K_AR5210_BEACON_PERIOD);
+}
+
+HAL_BOOL
+ar5k_ar5210_waitForBeaconDone(hal, phys_addr)
+ struct ath_hal *hal;
+ bus_addr_t phys_addr;
+{
+ int i;
+
+ /*
+ * Wait for beaconn queue to be done
+ */
+ for(i = (AR5K_TUNE_BEACON_INTERVAL / 2); i > 0 &&
+ (AR5K_REG_READ(AR5K_AR5210_BSR) &
+ AR5K_AR5210_BSR_TXQ1F) != 0 &&
+ (AR5K_REG_READ(AR5K_AR5210_CR) &
+ AR5K_AR5210_CR_TXE1) != 0; i--);
+
+ /* Timeout... */
+ if(i <= 0) {
+ /*
+ * Re-schedule the beacon queue
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_TXDP1, (u_int32_t)phys_addr);
+ AR5K_REG_WRITE(AR5K_AR5210_BCR,
+ AR5K_AR5210_BCR_TQ1V | AR5K_AR5210_BCR_BDMAE);
+
+ return(AH_FALSE);
+ }
+
+ return(AH_TRUE);
+}
+
+/*
+ * Interrupt handling
+ */
+
+HAL_BOOL
+ar5k_ar5210_isInterruptPending(hal)
+ struct ath_hal *hal;
+{
+ return(AR5K_REG_READ(AR5K_AR5210_INTPEND) == 0 ? AH_FALSE : AH_TRUE);
+}
+
+HAL_BOOL
+ar5k_ar5210_getPendingInterrupts(hal, interrupt_mask)
+ struct ath_hal *hal;
+ u_int32_t *interrupt_mask;
+{
+ u_int32_t data;
+
+ if((data = AR5K_REG_READ(AR5K_AR5210_ISR)) == HAL_INT_NOCARD) {
+ *interrupt_mask = data;
+ return(AH_FALSE);
+ }
+
+ /*
+ * Get abstract interrupt mask (HAL-compatible)
+ */
+ *interrupt_mask = (data & HAL_INT_COMMON) & hal->ah_imr;
+
+ if(data & (AR5K_AR5210_ISR_RXOK | AR5K_AR5210_ISR_RXERR))
+ *interrupt_mask |= HAL_INT_RX;
+ if(data & (AR5K_AR5210_ISR_TXOK | AR5K_AR5210_ISR_TXERR))
+ *interrupt_mask |= HAL_INT_TX;
+ if(data & AR5K_AR5210_ISR_FATAL)
+ *interrupt_mask |= HAL_INT_FATAL;
+
+ /*
+ * Special interrupt handling (not catched by the driver)
+ */
+ if(((*interrupt_mask) & AR5K_AR5210_ISR_RXPHY) &&
+ hal->ah_radar.r_enabled == AH_TRUE)
+ ar5k_radar_alert(hal);
+
+ return(AH_TRUE);
+}
+
+u_int32_t
+ar5k_ar5210_getInterrupts(hal)
+ struct ath_hal *hal;
+{
+ /* Return the interrupt mask stored previously */
+ return(hal->ah_imr);
+}
+
+HAL_INT
+ar5k_ar5210_setInterrupts(hal, new_mask)
+ struct ath_hal *hal;
+ HAL_INT new_mask;
+{
+ HAL_INT old_mask, int_mask;
+
+ /*
+ * Disable card interrupts to prevent any race conditions
+ * (they will be re-enabled afterwards).
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_IER, AR5K_AR5210_IER_DISABLE);
+
+ old_mask = hal->ah_imr;
+
+ /*
+ * Add additional, chipset-dependent interrupt mask flags
+ * and write them to the IMR (interrupt mask register).
+ */
+ int_mask = new_mask & HAL_INT_COMMON;
+
+ if(new_mask & HAL_INT_RX)
+ int_mask |=
+ AR5K_AR5210_IMR_RXOK |
+ AR5K_AR5210_IMR_RXERR |
+ AR5K_AR5210_IMR_RXORN;
+
+ if(new_mask & HAL_INT_TX)
+ int_mask |=
+ AR5K_AR5210_IMR_TXOK |
+ AR5K_AR5210_IMR_TXERR |
+ AR5K_AR5210_IMR_TXURN;
+
+ AR5K_REG_WRITE(AR5K_AR5210_IMR, int_mask);
+
+ /* Store new interrupt mask */
+ hal->ah_imr = new_mask;
+
+ /* ..re-enable interrupts */
+ AR5K_REG_WRITE(AR5K_AR5210_IER, AR5K_AR5210_IER_ENABLE);
+
+ return(old_mask);
+}
+
+/*
+ * Misc internal functions
+ */
+
+HAL_BOOL
+ar5k_ar5210_get_capabilities(hal)
+ struct ath_hal *hal;
+{
+ /*
+ * Get the value stored in the EEPROM
+ */
+ if(ar5k_ar5210_eeprom_init(hal) != 0)
+ return(AH_FALSE);
+
+ /* Set number of supported TX queues */
+ hal->ah_capabilities.cap_queues.q_tx_num = AR5K_AR5210_TX_NUM_QUEUES;
+
+ /*
+ * Set radio capabilities
+ * (The AR5210 only supports the middle 5GHz band)
+ */
+ hal->ah_capabilities.cap_range.range_5ghz_min = 5120;
+ hal->ah_capabilities.cap_range.range_5ghz_max = 5430;
+ hal->ah_capabilities.cap_range.range_2ghz_min = 0;
+ hal->ah_capabilities.cap_range.range_2ghz_max = 0;
+
+ /* Set supported modes */
+ hal->ah_capabilities.cap_mode = HAL_MODE_11A | HAL_MODE_TURBO;
+
+ return(AH_TRUE);
+}
+
+void
+ar5k_ar5210_radar_alert(hal, enable)
+ struct ath_hal *hal;
+ HAL_BOOL enable;
+{
+ /*
+ * Set the RXPHY interrupt to be able to detect
+ * possible radar activity.
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_IER, AR5K_AR5210_IER_DISABLE);
+
+ if(enable == AH_TRUE)
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_IMR,
+ AR5K_AR5210_IMR_RXPHY);
+ else
+ AR5K_REG_DISABLE_BITS(AR5K_AR5210_IMR,
+ AR5K_AR5210_IMR_RXPHY);
+
+ AR5K_REG_WRITE(AR5K_AR5210_IER, AR5K_AR5210_IER_ENABLE);
+}
+
+HAL_BOOL
+ar5k_ar5210_regulation_domain(hal, write, regdomain)
+ struct ath_hal *hal;
+ HAL_BOOL write;
+ ieee80211_regdomain_t *regdomain;
+{
+ /* Read current value */
+ if(write != AH_TRUE) {
+ memcpy(regdomain, &hal->ah_capabilities.cap_regdomain.reg_current,
+ sizeof(ieee80211_regdomain_t));
+ return(AH_TRUE);
+ }
+
+ /* Try to write a new value */
+ memcpy(&hal->ah_capabilities.cap_regdomain.reg_current, regdomain,
+ sizeof(ieee80211_regdomain_t));
+
+ if(hal->ah_capabilities.cap_eeprom.ee_protect &
+ AR5K_AR5210_EEPROM_PROTECT_128_191)
+ return(AH_FALSE);
+
+ hal->ah_capabilities.cap_eeprom.ee_regdomain =
+ ar5k_regdomain_from_ieee(regdomain);
+
+ AR5K_PRINTF("writing new regulation domain to EEPROM: 0x%04x\n",
+ hal->ah_capabilities.cap_eeprom.ee_regdomain);
+
+ if(ar5k_ar5210_eeprom_write(hal, AR5K_AR5210_EEPROM_REG_DOMAIN,
+ hal->ah_capabilities.cap_eeprom.ee_regdomain) != 0)
+ return(AH_FALSE);
+
+ return(AH_TRUE);
+}
+
+/*
+ * EEPROM access functions
+ */
+
+int
+ar5k_ar5210_eeprom_init(hal)
+ struct ath_hal *hal;
+{
+ int ret;
+
+ /* Check if EEPROM is busy */
+ if(ar5k_ar5210_eeprom_is_busy(hal) == AH_TRUE)
+ return(-EBUSY);
+
+ /*
+ * Read values from EEPROM and store them in the capability structure
+ */
+ if((ret = ar5k_ar5210_eeprom_read(hal, AR5K_AR5210_EEPROM_MAGIC,
+ &hal->ah_capabilities.cap_eeprom.ee_magic)) != 0)
+ return(ret);
+
+ if(hal->ah_capabilities.cap_eeprom.ee_magic !=
+ AR5K_AR5210_EEPROM_MAGIC_VALUE)
+ return(-EFTYPE);
+
+ if((ret = ar5k_ar5210_eeprom_read(hal, AR5K_AR5210_EEPROM_INFO_VERSION,
+ &hal->ah_capabilities.cap_eeprom.ee_version)) != 0)
+ return(ret);
+
+ if((ret = ar5k_ar5210_eeprom_read(hal, AR5K_AR5210_EEPROM_PROTECT,
+ &hal->ah_capabilities.cap_eeprom.ee_protect)) != 0)
+ return(ret);
+
+ if((ret = ar5k_ar5210_eeprom_read(hal, AR5K_AR5210_EEPROM_REG_DOMAIN,
+ &hal->ah_capabilities.cap_eeprom.ee_regdomain)) != 0)
+ return(ret);
+
+ return(0);
+}
+
+HAL_BOOL
+ar5k_ar5210_eeprom_is_busy(hal)
+ struct ath_hal *hal;
+{
+ return(AR5K_REG_READ(AR5K_AR5210_CFG) & AR5K_AR5210_CFG_EEBS ?
+ AH_TRUE : AH_FALSE);
+}
+
+int
+ar5k_ar5210_eeprom_read(hal, offset, data)
+ struct ath_hal *hal;
+ u_int32_t offset;
+ u_int16_t *data;
+{
+ u_int32_t status, timeout;
+
+ /* Enable eeprom access */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_PCICFG, AR5K_AR5210_PCICFG_EEAE);
+
+ /*
+ * Prime read pump
+ */
+ (void)AR5K_REG_READ(AR5K_AR5210_EEPROM_BASE + (4 * offset));
+
+ for(timeout = 10000; timeout > 0; timeout--) {
+ AR5K_DELAY(1);
+ status = AR5K_REG_READ(AR5K_AR5210_EEPROM_STATUS);
+ if(status & AR5K_AR5210_EEPROM_STAT_RDDONE) {
+ if(status & AR5K_AR5210_EEPROM_STAT_RDERR)
+ return(-EIO);
+ *data = (u_int16_t)
+ (AR5K_REG_READ(AR5K_AR5210_EEPROM_RDATA) & 0xffff);
+ return(0);
+ }
+ }
+
+ return(-ETIMEDOUT);
+}
+
+int
+ar5k_ar5210_eeprom_write(hal, offset, data)
+ struct ath_hal *hal;
+ u_int32_t offset;
+ u_int16_t data;
+{
+ u_int32_t status, timeout;
+
+ /* Enable eeprom access */
+ AR5K_REG_ENABLE_BITS(AR5K_AR5210_PCICFG, AR5K_AR5210_PCICFG_EEAE);
+
+ /*
+ * Prime write pump
+ */
+ AR5K_REG_WRITE(AR5K_AR5210_EEPROM_BASE + (4 * offset), data);
+
+ for(timeout = 10000; timeout > 0; timeout--) {
+ AR5K_DELAY(1);
+ status = AR5K_REG_READ(AR5K_AR5210_EEPROM_STATUS);
+ if(status & AR5K_AR5210_EEPROM_STAT_WRDONE) {
+ if(status & AR5K_AR5210_EEPROM_STAT_WRERR)
+ return(-EIO);
+ return(0);
+ }
+ }
+
+ return(-ETIMEDOUT);
+}
+
+#endif /* AR5K_SUPPORT_AR5210 */