diff --git a/sys/contrib/dev/athfw/ath-open-ar9271.bin b/sys/contrib/dev/athfw/ath-open-ar9271.bin new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ #include +#include +#include /* * WiSoC boards overload the bus tag with information about the @@ -271,9 +273,15 @@ void ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) { + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; + if (ahpriv->ah_isusb) { + ath_hal_usbWrite(ah, reg, val); + return; + } + #ifdef AH_DEBUG /* Debug - complain if we haven't fully waken things up */ if (! ath_hal_reg_whilst_asleep(ah, reg) && @@ -305,10 +313,14 @@ u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) { + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; u_int32_t val; + if (ahpriv->ah_isusb) + return ath_hal_usbRead(ah, reg); + #ifdef AH_DEBUG /* Debug - complain if we haven't fully waken things up */ if (! ath_hal_reg_whilst_asleep(ah, reg) && @@ -369,9 +381,15 @@ void ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) { + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; + if (ahpriv->ah_isusb) { + ath_hal_usbWrite(ah, reg, val); + return; + } + #ifdef AH_DEBUG /* Debug - complain if we haven't fully waken things up */ if (! ath_hal_reg_whilst_asleep(ah, reg) && @@ -392,10 +410,14 @@ u_int32_t ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) { + struct ath_hal_private *ahpriv = AH_PRIVATE(ah); bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; u_int32_t val; + if (ahpriv->ah_isusb) + return ath_hal_usbRead(ah, reg); + #ifdef AH_DEBUG /* Debug - complain if we haven't fully waken things up */ if (! ath_hal_reg_whilst_asleep(ah, reg) && diff --git a/sys/dev/ath/ah_osdep_ar5416.c b/sys/dev/ath/ah_osdep_ar5416.c --- a/sys/dev/ath/ah_osdep_ar5416.c +++ b/sys/dev/ath/ah_osdep_ar5416.c @@ -42,11 +42,13 @@ extern struct ath_hal_chip AR9280_chip; extern struct ath_hal_chip AR9285_chip; extern struct ath_hal_chip AR9287_chip; +extern struct ath_hal_chip AR9271_chip; extern struct ath_hal_rf RF2133_rf; extern struct ath_hal_rf RF9280_rf; extern struct ath_hal_rf RF9285_rf; extern struct ath_hal_rf RF9287_rf; +extern struct ath_hal_rf RF9271_rf; static int ath_hal_ar5416_modevent(module_t mod __unused, int type, void *data __unused) @@ -60,10 +62,12 @@ ath_hal_add_chip(&AR9280_chip); ath_hal_add_chip(&AR9285_chip); ath_hal_add_chip(&AR9287_chip); + ath_hal_add_chip(&AR9271_chip); ath_hal_add_rf(&RF2133_rf); ath_hal_add_rf(&RF9280_rf); ath_hal_add_rf(&RF9285_rf); ath_hal_add_rf(&RF9287_rf); + ath_hal_add_rf(&RF9271_rf); printf("[ar5416] loaded\n"); break; @@ -73,10 +77,12 @@ ath_hal_remove_chip(&AR9280_chip); ath_hal_remove_chip(&AR9285_chip); ath_hal_remove_chip(&AR9287_chip); + ath_hal_remove_chip(&AR9271_chip); ath_hal_remove_rf(&RF2133_rf); ath_hal_remove_rf(&RF9280_rf); ath_hal_remove_rf(&RF9285_rf); ath_hal_remove_rf(&RF9287_rf); + ath_hal_remove_rf(&RF9271_rf); printf("[ar5416] unloaded\n"); break; @@ -94,3 +100,4 @@ MODULE_VERSION(ath_hal_ar5416, 1); MODULE_DEPEND(ath_hal_ar5416, ath_hal, 1, 1, 1); MODULE_DEPEND(ath_hal_ar5416, ath_hal_ar5212, 1, 1, 1); +MODULE_DEPEND(ath_hal_ar5416, if_ath_usb, 1, 1, 1); diff --git a/sys/dev/ath/ath_hal/ah.h b/sys/dev/ath/ath_hal/ah.h --- a/sys/dev/ath/ath_hal/ah.h +++ b/sys/dev/ath/ath_hal/ah.h @@ -1568,8 +1568,8 @@ * null (AH_NULL) reference will be returned and a status code will * be returned if the status parameter is non-zero. */ -extern struct ath_hal * __ahdecl ath_hal_attach(uint16_t devid, HAL_SOFTC, - HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, +extern struct ath_hal * __ahdecl ath_hal_attach(uint16_t venid, uint16_t devid, + HAL_SOFTC, HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS* status); extern const char *ath_hal_mac_name(struct ath_hal *); diff --git a/sys/dev/ath/ath_hal/ah.c b/sys/dev/ath/ath_hal/ah.c --- a/sys/dev/ath/ath_hal/ah.c +++ b/sys/dev/ath/ath_hal/ah.c @@ -20,7 +20,6 @@ #include "ah.h" #include "ah_internal.h" -#include "ah_devid.h" #include "ah_eeprom.h" /* for 5ghz fast clock flag */ #include "ar5416/ar5416reg.h" /* NB: includes ar5212reg.h */ @@ -81,7 +80,7 @@ * disable. */ struct ath_hal* -ath_hal_attach(uint16_t devid, HAL_SOFTC sc, +ath_hal_attach(uint16_t venid, uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *error) @@ -94,7 +93,7 @@ struct ath_hal *ah; /* XXX don't have vendorid, assume atheros one works */ - if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) + if (chip->probe(venid, devid) == AH_NULL) continue; ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config, error); @@ -117,7 +116,7 @@ struct ath_hal *ah; /* XXX don't have vendorid, assume atheros one works */ - if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) + if (chip->probe(venid, devid) == AH_NULL) continue; ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config, error); @@ -179,6 +178,8 @@ if (AH_PRIVATE(ah)->ah_ispcie) return "AR9287"; return "AR9227"; + case AR_XSREV_VERSION_K2: + return "AR9271"; case AR_SREV_VERSION_AR9380: if (ah->ah_macRev >= AR_SREV_REVISION_AR9580_10) return "AR9580"; diff --git a/sys/dev/ath/ath_hal/ah_internal.h b/sys/dev/ath/ath_hal/ah_internal.h --- a/sys/dev/ath/ath_hal/ah_internal.h +++ b/sys/dev/ath/ath_hal/ah_internal.h @@ -29,6 +29,7 @@ #include #include /* XXX for reasons */ +#include "ah_osdep.h" #ifndef NBBY #define NBBY 8 /* number of bits/byte */ @@ -380,6 +381,8 @@ HAL_BOOL (*ah_eepromDiag)(struct ath_hal *, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize); + uint32_t (*ah_usb_read)(HAL_SOFTC *, uint32_t); + void (*ah_usb_write)(HAL_SOFTC *, uint32_t, uint32_t); /* * Device revision information. @@ -393,6 +396,7 @@ uint16_t ah_analog2GhzRev; /* 5GHz radio revision */ uint32_t ah_flags; /* misc flags */ uint8_t ah_ispcie; /* PCIE, special treatment */ + uint8_t ah_isusb; /* USB device */ uint8_t ah_devType; /* card type - CB, PCI, PCIe */ HAL_OPMODE ah_opmode; /* operating mode from reset */ @@ -491,7 +495,10 @@ AH_PRIVATE(_ah)->ah_getSpurChan(_ah, _ix, _is2G) #define ath_hal_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) \ AH_PRIVATE(_ah)->ah_eepromDiag(_ah, _request, _a, _asize, _r, _rsize) - +#define ath_hal_usbRead(_ah, _addr) \ + AH_PRIVATE(_ah)->ah_usb_read(_ah->ah_sc, _addr) +#define ath_hal_usbWrite(_ah, _addr, _val) \ + AH_PRIVATE(_ah)->ah_usb_write(_ah->ah_sc, _addr, _val) #ifndef _NET_IF_IEEE80211_H_ /* * Stuff that would naturally come from _ieee80211.h diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c b/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c --- a/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_gpio.c @@ -191,7 +191,9 @@ */ if (AR_SREV_KIWI_10_OR_LATER(ah)) bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9287_GPIO_IN_VAL); - if (AR_SREV_KITE_10_OR_LATER(ah)) + else if (AR_SREV_9271(ah)) + bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9271_GPIO_IN_VAL); + else if (AR_SREV_KITE_10_OR_LATER(ah)) bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR9285_GPIO_IN_VAL); else if (AR_SREV_MERLIN_10_OR_LATER(ah)) bits = MS(OS_REG_READ(ah, AR_GPIO_IN_OUT), AR928X_GPIO_IN_VAL); diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416reg.h b/sys/dev/ath/ath_hal/ar5416/ar5416reg.h --- a/sys/dev/ath/ath_hal/ar5416/ar5416reg.h +++ b/sys/dev/ath/ath_hal/ar5416/ar5416reg.h @@ -566,6 +566,8 @@ #define AR9285_GPIO_IN_VAL_S 12 #define AR9287_GPIO_IN_VAL 0x003FF800 #define AR9287_GPIO_IN_VAL_S 11 +#define AR9271_GPIO_IN_VAL 0x00FF0000 +#define AR9271_GPIO_IN_VAL_S 16 #define AR_GPIO_OE_OUT_DRV 0x3 /* 2 bit mask shifted by 2*bitpos */ #define AR_GPIO_OE_OUT_DRV_NO 0x0 /* tristate */ @@ -696,6 +698,7 @@ #define AR_XSREV_REVISION_KIWI_11 1 /* Kiwi 1.1 */ #define AR_XSREV_REVISION_KIWI_12 2 /* Kiwi 1.2 */ #define AR_XSREV_REVISION_KIWI_13 3 /* Kiwi 1.3 */ +#define AR_XSREV_VERSION_K2 0x140 /* K2 (AR9271) */ /* Owl (AR5416) */ #define AR_SREV_OWL(_ah) \ @@ -805,7 +808,9 @@ (AR_SREV_KIWI(_ah) && \ AH_PRIVATE((_ah))->ah_macRev >= AR_XSREV_REVISION_KIWI_13) -/* Not yet implemented chips */ -#define AR_SREV_9271(_ah) 0 +/* K2 (9271) */ + +#define AR_SREV_9271(_ah) \ + (AH_PRIVATE((_ah))->ah_macVersion == AR_XSREV_VERSION_K2) #endif /* _DEV_ATH_AR5416REG_H */ diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271.h b/sys/dev/ath/ath_hal/ar9002/ar9271.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271.h @@ -0,0 +1,58 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + * + * $FreeBSD$ + */ +#ifndef _ATH_AR9271_H_ +#define _ATH_AR9271_H_ + +#include "ar5416/ar5416.h" + +struct ath_hal_9271 { + struct ath_hal_5416 ah_5416; + + HAL_INI_ARRAY ah_ini_txgain; + HAL_INI_ARRAY ah_ini_rxgain; + + struct { + int32_t prev_offset; /* Previous value of PA offset value */ + int8_t max_skipcount; /* Max No. of times PACAL can be skipped */ + int8_t skipcount; /* No. of times the PACAL to be skipped */ + } pacal_info; +}; +#define AH9271(_ah) ((struct ath_hal_9271 *)(_ah)) + +#define AR9271_DEFAULT_RXCHAINMASK 1 +#define AR9271_DEFAULT_TXCHAINMASK 1 + +#define AR_PHY_CCA_NOM_VAL_9271_2GHZ -122 +#define AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ -127 +#define AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ -116 + +HAL_BOOL ar9271RfAttach(struct ath_hal *, HAL_STATUS *); + +extern HAL_BOOL ar9271SetTransmitPower(struct ath_hal *, + const struct ieee80211_channel *, uint16_t *); +extern HAL_BOOL ar9271SetBoardValues(struct ath_hal *, + const struct ieee80211_channel *); + +/* ar9285_btcoex.h */ +extern void ar9271BTCoexAntennaDiversity(struct ath_hal *ah); +extern void ar9271BTCoexSetParameter(struct ath_hal *ah, + u_int32_t value, u_int32_t type); + +#endif /* _ATH_AR9271_H_ */ diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271.c b/sys/dev/ath/ath_hal/ar9002/ar9271.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271.c @@ -0,0 +1,88 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" + +#include "ah_eeprom_v14.h" + +#include "ar9002/ar9280.h" +#include "ar9002/ar9271.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +/* + * The ordering of nfarray is thus: + * + * nfarray[0]: Chain 0 ctl + * nfarray[1]: Chain 1 ctl + * nfarray[2]: Chain 2 ctl + * nfarray[3]: Chain 0 ext + * nfarray[4]: Chain 1 ext + * nfarray[5]: Chain 2 ext + */ +static void +ar9271GetNoiseFloor(struct ath_hal *ah, int16_t nfarray[]) +{ + int16_t nf; + + nf = MS(OS_REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + HALDEBUG(ah, HAL_DEBUG_NFCAL, + "NF calibrated [ctl] [chain 0] is %d\n", nf); + nfarray[0] = nf; + + nf = MS(OS_REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + HALDEBUG(ah, HAL_DEBUG_NFCAL, + "NF calibrated [ext] [chain 0] is %d\n", nf); + nfarray[3] = nf; + + /* Chain 1 - invalid */ + nfarray[1] = 0; + nfarray[4] = 0; + + /* Chain 2 - invalid */ + nfarray[2] = 0; + nfarray[5] = 0; +} + +HAL_BOOL +ar9271RfAttach(struct ath_hal *ah, HAL_STATUS *status) +{ + if (ar9280RfAttach(ah, status) == AH_FALSE) + return AH_FALSE; + + AH_PRIVATE(ah)->ah_getNoiseFloor = ar9271GetNoiseFloor; + + return AH_TRUE; +} + +static HAL_BOOL +ar9271RfProbe(struct ath_hal *ah) +{ + return (AR_SREV_9271(ah)); +} + +AH_RF(RF9271, ar9271RfProbe, ar9271RfAttach); diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271.ini b/sys/dev/ath/ath_hal/ar9002/ar9271.ini new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271.ini @@ -0,0 +1,740 @@ +/* + * Copyright (c) 2010 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ + +static const u_int32_t ar9271Modes[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x00001030, 0x00000160, 0x000002c0, 0x000002c0, 0x00000160, 0x000002c0 }, + { 0x00001070, 0x0000018c, 0x00000318, 0x00000318, 0x0000018c, 0x00000318 }, + { 0x000010b0, 0x00003e38, 0x00007c70, 0x00007c70, 0x00003e38, 0x00007c70 }, + { 0x000010f0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00008014, 0x08400b00, 0x10801600, 0x10801600, 0x08400b00, 0x10801600 }, + { 0x0000801c, 0x12e0002b, 0x12e00057, 0x12e00057, 0x12e0002b, 0x12e00057 }, + { 0x00008318, 0x00003440, 0x00006880, 0x00006880, 0x00003440, 0x00006880 }, + { 0x00009804, 0x00000300, 0x000003c4, 0x000003c4, 0x00000300, 0x000003c4 }, + { 0x00009820, 0x02020200, 0x02020200, 0x02020200, 0x02020200, 0x02020200 }, + { 0x00009824, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e, 0x01000e0e }, + { 0x00009828, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001, 0x3a020001 }, + { 0x00009834, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e, 0x00000e0e }, + { 0x00009838, 0x00000007, 0x00000007, 0x00000007, 0x00000007, 0x00000007 }, + { 0x00009840, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e, 0x206a012e }, + { 0x00009844, 0x03721620, 0x03721620, 0x03721620, 0x03721620, 0x03721620 }, + { 0x00009848, 0x00001053, 0x00001053, 0x00001053, 0x00001053, 0x00001053 }, + { 0x0000a848, 0x00001053, 0x00001053, 0x00001053, 0x00001053, 0x00001053 }, + { 0x00009850, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2, 0x6d4000e2 }, + { 0x00009858, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e, 0x7ec84d2e }, + { 0x0000985c, 0x3137605e, 0x3137605e, 0x3137605e, 0x3137605e, 0x3137605e }, + { 0x00009860, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18 }, + { 0x00009864, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00, 0x0001ce00 }, + { 0x00009868, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0, 0x5ac640d0 }, + { 0x0000986c, 0x06903881, 0x06903881, 0x06903881, 0x06903881, 0x06903881 }, + { 0x00009910, 0x30002310, 0x30002310, 0x30002310, 0x30002310, 0x30002310 }, + { 0x00009914, 0x00000898, 0x00001130, 0x00001130, 0x00000898, 0x00001130 }, + { 0x00009918, 0x0000000b, 0x00000016, 0x00000016, 0x0000000b, 0x00000016 }, + { 0x00009924, 0xd00a800d, 0xd00a800d, 0xd00a800d, 0xd00a800d, 0xd00a800d }, + { 0x00009944, 0xffbc1020, 0xffbc1020, 0xffbc1020, 0xffbc1020, 0xffbc1020 }, + { 0x00009960, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009964, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099b8, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c, 0x0000421c }, + { 0x000099bc, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x000099c0, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4 }, + { 0x000099c4, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77, 0x06336f77 }, + { 0x000099c8, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f, 0x6af6532f }, + { 0x000099cc, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8, 0x08f186c8 }, + { 0x000099d0, 0x00046384, 0x00046384, 0x00046384, 0x00046384, 0x00046384 }, + { 0x000099d4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x000099d8, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x00009a00, 0x00058084, 0x00058084, 0x00058084, 0x00058084, 0x00058084 }, + { 0x00009a04, 0x00058088, 0x00058088, 0x00058088, 0x00058088, 0x00058088 }, + { 0x00009a08, 0x0005808c, 0x0005808c, 0x0005808c, 0x0005808c, 0x0005808c }, + { 0x00009a0c, 0x00058100, 0x00058100, 0x00058100, 0x00058100, 0x00058100 }, + { 0x00009a10, 0x00058104, 0x00058104, 0x00058104, 0x00058104, 0x00058104 }, + { 0x00009a14, 0x00058108, 0x00058108, 0x00058108, 0x00058108, 0x00058108 }, + { 0x00009a18, 0x0005810c, 0x0005810c, 0x0005810c, 0x0005810c, 0x0005810c }, + { 0x00009a1c, 0x00058110, 0x00058110, 0x00058110, 0x00058110, 0x00058110 }, + { 0x00009a20, 0x00058114, 0x00058114, 0x00058114, 0x00058114, 0x00058114 }, + { 0x00009a24, 0x00058180, 0x00058180, 0x00058180, 0x00058180, 0x00058180 }, + { 0x00009a28, 0x00058184, 0x00058184, 0x00058184, 0x00058184, 0x00058184 }, + { 0x00009a2c, 0x00058188, 0x00058188, 0x00058188, 0x00058188, 0x00058188 }, + { 0x00009a30, 0x0005818c, 0x0005818c, 0x0005818c, 0x0005818c, 0x0005818c }, + { 0x00009a34, 0x00058190, 0x00058190, 0x00058190, 0x00058190, 0x00058190 }, + { 0x00009a38, 0x00058194, 0x00058194, 0x00058194, 0x00058194, 0x00058194 }, + { 0x00009a3c, 0x000581a0, 0x000581a0, 0x000581a0, 0x000581a0, 0x000581a0 }, + { 0x00009a40, 0x0005820c, 0x0005820c, 0x0005820c, 0x0005820c, 0x0005820c }, + { 0x00009a44, 0x000581a8, 0x000581a8, 0x000581a8, 0x000581a8, 0x000581a8 }, + { 0x00009a48, 0x00058284, 0x00058284, 0x00058284, 0x00058284, 0x00058284 }, + { 0x00009a4c, 0x00058288, 0x00058288, 0x00058288, 0x00058288, 0x00058288 }, + { 0x00009a50, 0x00058224, 0x00058224, 0x00058224, 0x00058224, 0x00058224 }, + { 0x00009a54, 0x00058290, 0x00058290, 0x00058290, 0x00058290, 0x00058290 }, + { 0x00009a58, 0x00058300, 0x00058300, 0x00058300, 0x00058300, 0x00058300 }, + { 0x00009a5c, 0x00058304, 0x00058304, 0x00058304, 0x00058304, 0x00058304 }, + { 0x00009a60, 0x00058308, 0x00058308, 0x00058308, 0x00058308, 0x00058308 }, + { 0x00009a64, 0x0005830c, 0x0005830c, 0x0005830c, 0x0005830c, 0x0005830c }, + { 0x00009a68, 0x00058380, 0x00058380, 0x00058380, 0x00058380, 0x00058380 }, + { 0x00009a6c, 0x00058384, 0x00058384, 0x00058384, 0x00058384, 0x00058384 }, + { 0x00009a70, 0x00068700, 0x00068700, 0x00068700, 0x00068700, 0x00068700 }, + { 0x00009a74, 0x00068704, 0x00068704, 0x00068704, 0x00068704, 0x00068704 }, + { 0x00009a78, 0x00068708, 0x00068708, 0x00068708, 0x00068708, 0x00068708 }, + { 0x00009a7c, 0x0006870c, 0x0006870c, 0x0006870c, 0x0006870c, 0x0006870c }, + { 0x00009a80, 0x00068780, 0x00068780, 0x00068780, 0x00068780, 0x00068780 }, + { 0x00009a84, 0x00068784, 0x00068784, 0x00068784, 0x00068784, 0x00068784 }, + { 0x00009a88, 0x00078b00, 0x00078b00, 0x00078b00, 0x00078b00, 0x00078b00 }, + { 0x00009a8c, 0x00078b04, 0x00078b04, 0x00078b04, 0x00078b04, 0x00078b04 }, + { 0x00009a90, 0x00078b08, 0x00078b08, 0x00078b08, 0x00078b08, 0x00078b08 }, + { 0x00009a94, 0x00078b0c, 0x00078b0c, 0x00078b0c, 0x00078b0c, 0x00078b0c }, + { 0x00009a98, 0x00078b80, 0x00078b80, 0x00078b80, 0x00078b80, 0x00078b80 }, + { 0x00009a9c, 0x00078b84, 0x00078b84, 0x00078b84, 0x00078b84, 0x00078b84 }, + { 0x00009aa0, 0x00078b88, 0x00078b88, 0x00078b88, 0x00078b88, 0x00078b88 }, + { 0x00009aa4, 0x00078b8c, 0x00078b8c, 0x00078b8c, 0x00078b8c, 0x00078b8c }, + { 0x00009aa8, 0x00078b90, 0x00078b90, 0x00078b90, 0x00078b90, 0x00078b90 }, + { 0x00009aac, 0x000caf80, 0x000caf80, 0x000caf80, 0x000caf80, 0x000caf80 }, + { 0x00009ab0, 0x000caf84, 0x000caf84, 0x000caf84, 0x000caf84, 0x000caf84 }, + { 0x00009ab4, 0x000caf88, 0x000caf88, 0x000caf88, 0x000caf88, 0x000caf88 }, + { 0x00009ab8, 0x000caf8c, 0x000caf8c, 0x000caf8c, 0x000caf8c, 0x000caf8c }, + { 0x00009abc, 0x000caf90, 0x000caf90, 0x000caf90, 0x000caf90, 0x000caf90 }, + { 0x00009ac0, 0x000db30c, 0x000db30c, 0x000db30c, 0x000db30c, 0x000db30c }, + { 0x00009ac4, 0x000db310, 0x000db310, 0x000db310, 0x000db310, 0x000db310 }, + { 0x00009ac8, 0x000db384, 0x000db384, 0x000db384, 0x000db384, 0x000db384 }, + { 0x00009acc, 0x000db388, 0x000db388, 0x000db388, 0x000db388, 0x000db388 }, + { 0x00009ad0, 0x000db324, 0x000db324, 0x000db324, 0x000db324, 0x000db324 }, + { 0x00009ad4, 0x000eb704, 0x000eb704, 0x000eb704, 0x000eb704, 0x000eb704 }, + { 0x00009ad8, 0x000eb6a4, 0x000eb6a4, 0x000eb6a4, 0x000eb6a4, 0x000eb6a4 }, + { 0x00009adc, 0x000eb6a8, 0x000eb6a8, 0x000eb6a8, 0x000eb6a8, 0x000eb6a8 }, + { 0x00009ae0, 0x000eb710, 0x000eb710, 0x000eb710, 0x000eb710, 0x000eb710 }, + { 0x00009ae4, 0x000eb714, 0x000eb714, 0x000eb714, 0x000eb714, 0x000eb714 }, + { 0x00009ae8, 0x000eb720, 0x000eb720, 0x000eb720, 0x000eb720, 0x000eb720 }, + { 0x00009aec, 0x000eb724, 0x000eb724, 0x000eb724, 0x000eb724, 0x000eb724 }, + { 0x00009af0, 0x000eb728, 0x000eb728, 0x000eb728, 0x000eb728, 0x000eb728 }, + { 0x00009af4, 0x000eb72c, 0x000eb72c, 0x000eb72c, 0x000eb72c, 0x000eb72c }, + { 0x00009af8, 0x000eb7a0, 0x000eb7a0, 0x000eb7a0, 0x000eb7a0, 0x000eb7a0 }, + { 0x00009afc, 0x000eb7a4, 0x000eb7a4, 0x000eb7a4, 0x000eb7a4, 0x000eb7a4 }, + { 0x00009b00, 0x000eb7a8, 0x000eb7a8, 0x000eb7a8, 0x000eb7a8, 0x000eb7a8 }, + { 0x00009b04, 0x000eb7b0, 0x000eb7b0, 0x000eb7b0, 0x000eb7b0, 0x000eb7b0 }, + { 0x00009b08, 0x000eb7b4, 0x000eb7b4, 0x000eb7b4, 0x000eb7b4, 0x000eb7b4 }, + { 0x00009b0c, 0x000eb7b8, 0x000eb7b8, 0x000eb7b8, 0x000eb7b8, 0x000eb7b8 }, + { 0x00009b10, 0x000eb7a5, 0x000eb7a5, 0x000eb7a5, 0x000eb7a5, 0x000eb7a5 }, + { 0x00009b14, 0x000eb7a9, 0x000eb7a9, 0x000eb7a9, 0x000eb7a9, 0x000eb7a9 }, + { 0x00009b18, 0x000eb7ad, 0x000eb7ad, 0x000eb7ad, 0x000eb7ad, 0x000eb7ad }, + { 0x00009b1c, 0x000eb7b1, 0x000eb7b1, 0x000eb7b1, 0x000eb7b1, 0x000eb7b1 }, + { 0x00009b20, 0x000eb7b5, 0x000eb7b5, 0x000eb7b5, 0x000eb7b5, 0x000eb7b5 }, + { 0x00009b24, 0x000eb7b9, 0x000eb7b9, 0x000eb7b9, 0x000eb7b9, 0x000eb7b9 }, + { 0x00009b28, 0x000eb7c5, 0x000eb7c5, 0x000eb7c5, 0x000eb7c5, 0x000eb7c5 }, + { 0x00009b2c, 0x000eb7c9, 0x000eb7c9, 0x000eb7c9, 0x000eb7c9, 0x000eb7c9 }, + { 0x00009b30, 0x000eb7d1, 0x000eb7d1, 0x000eb7d1, 0x000eb7d1, 0x000eb7d1 }, + { 0x00009b34, 0x000eb7d5, 0x000eb7d5, 0x000eb7d5, 0x000eb7d5, 0x000eb7d5 }, + { 0x00009b38, 0x000eb7d9, 0x000eb7d9, 0x000eb7d9, 0x000eb7d9, 0x000eb7d9 }, + { 0x00009b3c, 0x000eb7c6, 0x000eb7c6, 0x000eb7c6, 0x000eb7c6, 0x000eb7c6 }, + { 0x00009b40, 0x000eb7ca, 0x000eb7ca, 0x000eb7ca, 0x000eb7ca, 0x000eb7ca }, + { 0x00009b44, 0x000eb7ce, 0x000eb7ce, 0x000eb7ce, 0x000eb7ce, 0x000eb7ce }, + { 0x00009b48, 0x000eb7d2, 0x000eb7d2, 0x000eb7d2, 0x000eb7d2, 0x000eb7d2 }, + { 0x00009b4c, 0x000eb7d6, 0x000eb7d6, 0x000eb7d6, 0x000eb7d6, 0x000eb7d6 }, + { 0x00009b50, 0x000eb7c3, 0x000eb7c3, 0x000eb7c3, 0x000eb7c3, 0x000eb7c3 }, + { 0x00009b54, 0x000eb7c7, 0x000eb7c7, 0x000eb7c7, 0x000eb7c7, 0x000eb7c7 }, + { 0x00009b58, 0x000eb7cb, 0x000eb7cb, 0x000eb7cb, 0x000eb7cb, 0x000eb7cb }, + { 0x00009b5c, 0x000eb7cf, 0x000eb7cf, 0x000eb7cf, 0x000eb7cf, 0x000eb7cf }, + { 0x00009b60, 0x000eb7d7, 0x000eb7d7, 0x000eb7d7, 0x000eb7d7, 0x000eb7d7 }, + { 0x00009b64, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b68, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b6c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b70, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b74, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b78, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b7c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b80, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b84, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b88, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b8c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b90, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b94, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b98, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009b9c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009ba0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009ba4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009ba8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bac, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bb0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bb4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bb8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bbc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bc0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bc4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bc8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bcc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bd0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bd4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bd8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bdc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009be0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009be4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009be8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bec, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bf0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bf4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bf8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x00009bfc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000aa00, 0x00058084, 0x00058084, 0x00058084, 0x00058084, 0x00058084 }, + { 0x0000aa04, 0x00058088, 0x00058088, 0x00058088, 0x00058088, 0x00058088 }, + { 0x0000aa08, 0x0005808c, 0x0005808c, 0x0005808c, 0x0005808c, 0x0005808c }, + { 0x0000aa0c, 0x00058100, 0x00058100, 0x00058100, 0x00058100, 0x00058100 }, + { 0x0000aa10, 0x00058104, 0x00058104, 0x00058104, 0x00058104, 0x00058104 }, + { 0x0000aa14, 0x00058108, 0x00058108, 0x00058108, 0x00058108, 0x00058108 }, + { 0x0000aa18, 0x0005810c, 0x0005810c, 0x0005810c, 0x0005810c, 0x0005810c }, + { 0x0000aa1c, 0x00058110, 0x00058110, 0x00058110, 0x00058110, 0x00058110 }, + { 0x0000aa20, 0x00058114, 0x00058114, 0x00058114, 0x00058114, 0x00058114 }, + { 0x0000aa24, 0x00058180, 0x00058180, 0x00058180, 0x00058180, 0x00058180 }, + { 0x0000aa28, 0x00058184, 0x00058184, 0x00058184, 0x00058184, 0x00058184 }, + { 0x0000aa2c, 0x00058188, 0x00058188, 0x00058188, 0x00058188, 0x00058188 }, + { 0x0000aa30, 0x0005818c, 0x0005818c, 0x0005818c, 0x0005818c, 0x0005818c }, + { 0x0000aa34, 0x00058190, 0x00058190, 0x00058190, 0x00058190, 0x00058190 }, + { 0x0000aa38, 0x00058194, 0x00058194, 0x00058194, 0x00058194, 0x00058194 }, + { 0x0000aa3c, 0x000581a0, 0x000581a0, 0x000581a0, 0x000581a0, 0x000581a0 }, + { 0x0000aa40, 0x0005820c, 0x0005820c, 0x0005820c, 0x0005820c, 0x0005820c }, + { 0x0000aa44, 0x000581a8, 0x000581a8, 0x000581a8, 0x000581a8, 0x000581a8 }, + { 0x0000aa48, 0x00058284, 0x00058284, 0x00058284, 0x00058284, 0x00058284 }, + { 0x0000aa4c, 0x00058288, 0x00058288, 0x00058288, 0x00058288, 0x00058288 }, + { 0x0000aa50, 0x00058224, 0x00058224, 0x00058224, 0x00058224, 0x00058224 }, + { 0x0000aa54, 0x00058290, 0x00058290, 0x00058290, 0x00058290, 0x00058290 }, + { 0x0000aa58, 0x00058300, 0x00058300, 0x00058300, 0x00058300, 0x00058300 }, + { 0x0000aa5c, 0x00058304, 0x00058304, 0x00058304, 0x00058304, 0x00058304 }, + { 0x0000aa60, 0x00058308, 0x00058308, 0x00058308, 0x00058308, 0x00058308 }, + { 0x0000aa64, 0x0005830c, 0x0005830c, 0x0005830c, 0x0005830c, 0x0005830c }, + { 0x0000aa68, 0x00058380, 0x00058380, 0x00058380, 0x00058380, 0x00058380 }, + { 0x0000aa6c, 0x00058384, 0x00058384, 0x00058384, 0x00058384, 0x00058384 }, + { 0x0000aa70, 0x00068700, 0x00068700, 0x00068700, 0x00068700, 0x00068700 }, + { 0x0000aa74, 0x00068704, 0x00068704, 0x00068704, 0x00068704, 0x00068704 }, + { 0x0000aa78, 0x00068708, 0x00068708, 0x00068708, 0x00068708, 0x00068708 }, + { 0x0000aa7c, 0x0006870c, 0x0006870c, 0x0006870c, 0x0006870c, 0x0006870c }, + { 0x0000aa80, 0x00068780, 0x00068780, 0x00068780, 0x00068780, 0x00068780 }, + { 0x0000aa84, 0x00068784, 0x00068784, 0x00068784, 0x00068784, 0x00068784 }, + { 0x0000aa88, 0x00078b00, 0x00078b00, 0x00078b00, 0x00078b00, 0x00078b00 }, + { 0x0000aa8c, 0x00078b04, 0x00078b04, 0x00078b04, 0x00078b04, 0x00078b04 }, + { 0x0000aa90, 0x00078b08, 0x00078b08, 0x00078b08, 0x00078b08, 0x00078b08 }, + { 0x0000aa94, 0x00078b0c, 0x00078b0c, 0x00078b0c, 0x00078b0c, 0x00078b0c }, + { 0x0000aa98, 0x00078b80, 0x00078b80, 0x00078b80, 0x00078b80, 0x00078b80 }, + { 0x0000aa9c, 0x00078b84, 0x00078b84, 0x00078b84, 0x00078b84, 0x00078b84 }, + { 0x0000aaa0, 0x00078b88, 0x00078b88, 0x00078b88, 0x00078b88, 0x00078b88 }, + { 0x0000aaa4, 0x00078b8c, 0x00078b8c, 0x00078b8c, 0x00078b8c, 0x00078b8c }, + { 0x0000aaa8, 0x00078b90, 0x00078b90, 0x00078b90, 0x00078b90, 0x00078b90 }, + { 0x0000aaac, 0x000caf80, 0x000caf80, 0x000caf80, 0x000caf80, 0x000caf80 }, + { 0x0000aab0, 0x000caf84, 0x000caf84, 0x000caf84, 0x000caf84, 0x000caf84 }, + { 0x0000aab4, 0x000caf88, 0x000caf88, 0x000caf88, 0x000caf88, 0x000caf88 }, + { 0x0000aab8, 0x000caf8c, 0x000caf8c, 0x000caf8c, 0x000caf8c, 0x000caf8c }, + { 0x0000aabc, 0x000caf90, 0x000caf90, 0x000caf90, 0x000caf90, 0x000caf90 }, + { 0x0000aac0, 0x000db30c, 0x000db30c, 0x000db30c, 0x000db30c, 0x000db30c }, + { 0x0000aac4, 0x000db310, 0x000db310, 0x000db310, 0x000db310, 0x000db310 }, + { 0x0000aac8, 0x000db384, 0x000db384, 0x000db384, 0x000db384, 0x000db384 }, + { 0x0000aacc, 0x000db388, 0x000db388, 0x000db388, 0x000db388, 0x000db388 }, + { 0x0000aad0, 0x000db324, 0x000db324, 0x000db324, 0x000db324, 0x000db324 }, + { 0x0000aad4, 0x000eb704, 0x000eb704, 0x000eb704, 0x000eb704, 0x000eb704 }, + { 0x0000aad8, 0x000eb6a4, 0x000eb6a4, 0x000eb6a4, 0x000eb6a4, 0x000eb6a4 }, + { 0x0000aadc, 0x000eb6a8, 0x000eb6a8, 0x000eb6a8, 0x000eb6a8, 0x000eb6a8 }, + { 0x0000aae0, 0x000eb710, 0x000eb710, 0x000eb710, 0x000eb710, 0x000eb710 }, + { 0x0000aae4, 0x000eb714, 0x000eb714, 0x000eb714, 0x000eb714, 0x000eb714 }, + { 0x0000aae8, 0x000eb720, 0x000eb720, 0x000eb720, 0x000eb720, 0x000eb720 }, + { 0x0000aaec, 0x000eb724, 0x000eb724, 0x000eb724, 0x000eb724, 0x000eb724 }, + { 0x0000aaf0, 0x000eb728, 0x000eb728, 0x000eb728, 0x000eb728, 0x000eb728 }, + { 0x0000aaf4, 0x000eb72c, 0x000eb72c, 0x000eb72c, 0x000eb72c, 0x000eb72c }, + { 0x0000aaf8, 0x000eb7a0, 0x000eb7a0, 0x000eb7a0, 0x000eb7a0, 0x000eb7a0 }, + { 0x0000aafc, 0x000eb7a4, 0x000eb7a4, 0x000eb7a4, 0x000eb7a4, 0x000eb7a4 }, + { 0x0000ab00, 0x000eb7a8, 0x000eb7a8, 0x000eb7a8, 0x000eb7a8, 0x000eb7a8 }, + { 0x0000ab04, 0x000eb7b0, 0x000eb7b0, 0x000eb7b0, 0x000eb7b0, 0x000eb7b0 }, + { 0x0000ab08, 0x000eb7b4, 0x000eb7b4, 0x000eb7b4, 0x000eb7b4, 0x000eb7b4 }, + { 0x0000ab0c, 0x000eb7b8, 0x000eb7b8, 0x000eb7b8, 0x000eb7b8, 0x000eb7b8 }, + { 0x0000ab10, 0x000eb7a5, 0x000eb7a5, 0x000eb7a5, 0x000eb7a5, 0x000eb7a5 }, + { 0x0000ab14, 0x000eb7a9, 0x000eb7a9, 0x000eb7a9, 0x000eb7a9, 0x000eb7a9 }, + { 0x0000ab18, 0x000eb7ad, 0x000eb7ad, 0x000eb7ad, 0x000eb7ad, 0x000eb7ad }, + { 0x0000ab1c, 0x000eb7b1, 0x000eb7b1, 0x000eb7b1, 0x000eb7b1, 0x000eb7b1 }, + { 0x0000ab20, 0x000eb7b5, 0x000eb7b5, 0x000eb7b5, 0x000eb7b5, 0x000eb7b5 }, + { 0x0000ab24, 0x000eb7b9, 0x000eb7b9, 0x000eb7b9, 0x000eb7b9, 0x000eb7b9 }, + { 0x0000ab28, 0x000eb7c5, 0x000eb7c5, 0x000eb7c5, 0x000eb7c5, 0x000eb7c5 }, + { 0x0000ab2c, 0x000eb7c9, 0x000eb7c9, 0x000eb7c9, 0x000eb7c9, 0x000eb7c9 }, + { 0x0000ab30, 0x000eb7d1, 0x000eb7d1, 0x000eb7d1, 0x000eb7d1, 0x000eb7d1 }, + { 0x0000ab34, 0x000eb7d5, 0x000eb7d5, 0x000eb7d5, 0x000eb7d5, 0x000eb7d5 }, + { 0x0000ab38, 0x000eb7d9, 0x000eb7d9, 0x000eb7d9, 0x000eb7d9, 0x000eb7d9 }, + { 0x0000ab3c, 0x000eb7c6, 0x000eb7c6, 0x000eb7c6, 0x000eb7c6, 0x000eb7c6 }, + { 0x0000ab40, 0x000eb7ca, 0x000eb7ca, 0x000eb7ca, 0x000eb7ca, 0x000eb7ca }, + { 0x0000ab44, 0x000eb7ce, 0x000eb7ce, 0x000eb7ce, 0x000eb7ce, 0x000eb7ce }, + { 0x0000ab48, 0x000eb7d2, 0x000eb7d2, 0x000eb7d2, 0x000eb7d2, 0x000eb7d2 }, + { 0x0000ab4c, 0x000eb7d6, 0x000eb7d6, 0x000eb7d6, 0x000eb7d6, 0x000eb7d6 }, + { 0x0000ab50, 0x000eb7c3, 0x000eb7c3, 0x000eb7c3, 0x000eb7c3, 0x000eb7c3 }, + { 0x0000ab54, 0x000eb7c7, 0x000eb7c7, 0x000eb7c7, 0x000eb7c7, 0x000eb7c7 }, + { 0x0000ab58, 0x000eb7cb, 0x000eb7cb, 0x000eb7cb, 0x000eb7cb, 0x000eb7cb }, + { 0x0000ab5c, 0x000eb7cf, 0x000eb7cf, 0x000eb7cf, 0x000eb7cf, 0x000eb7cf }, + { 0x0000ab60, 0x000eb7d7, 0x000eb7d7, 0x000eb7d7, 0x000eb7d7, 0x000eb7d7 }, + { 0x0000ab64, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab68, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab6c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab70, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab74, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab78, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab7c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab80, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab84, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab88, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab8c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab90, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab94, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab98, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000ab9c, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000aba0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000aba4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000aba8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abac, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abb0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abb4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abb8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abbc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abc0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abc4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abc8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abcc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abd0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abd4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abd8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abdc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abe0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abe4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abe8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abec, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abf0, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abf4, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abf8, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000abfc, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db, 0x000eb7db }, + { 0x0000a204, 0x00000004, 0x00000004, 0x00000004, 0x00000004, 0x00000004 }, + { 0x0000a20c, 0x0001f000, 0x0001f000, 0x0001f000, 0x0001f000, 0x0001f000 }, + { 0x0000b20c, 0x0001f000, 0x0001f000, 0x0001f000, 0x0001f000, 0x0001f000 }, + { 0x0000a21c, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a, 0x1883800a }, + { 0x0000a230, 0x00000108, 0x00000210, 0x00000210, 0x00000108, 0x00000210 }, + { 0x0000a250, 0x0004a000, 0x0004a000, 0x0004a000, 0x0004a000, 0x0004a000 }, + { 0x0000a358, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e, 0x7999aa0e } +}; + +static const u_int32_t ar9271Common[][2] = { + /* Addr allmodes */ + { 0x0000000c, 0x00000000 }, + { 0x00000030, 0x00020045 }, + { 0x00000034, 0x00000005 }, + { 0x00000040, 0x00000000 }, + { 0x00000044, 0x00000008 }, + { 0x00000048, 0x00000008 }, + { 0x0000004c, 0x00000010 }, + { 0x00000050, 0x00000000 }, + { 0x00000054, 0x0000001f }, + { 0x00000800, 0x00000000 }, + { 0x00000804, 0x00000000 }, + { 0x00000808, 0x00000000 }, + { 0x0000080c, 0x00000000 }, + { 0x00000810, 0x00000000 }, + { 0x00000814, 0x00000000 }, + { 0x00000818, 0x00000000 }, + { 0x0000081c, 0x00000000 }, + { 0x00000820, 0x00000000 }, + { 0x00000824, 0x00000000 }, + { 0x00001040, 0x002ffc0f }, + { 0x00001044, 0x002ffc0f }, + { 0x00001048, 0x002ffc0f }, + { 0x0000104c, 0x002ffc0f }, + { 0x00001050, 0x002ffc0f }, + { 0x00001054, 0x002ffc0f }, + { 0x00001058, 0x002ffc0f }, + { 0x0000105c, 0x002ffc0f }, + { 0x00001060, 0x002ffc0f }, + { 0x00001064, 0x002ffc0f }, + { 0x00001230, 0x00000000 }, + { 0x00001270, 0x00000000 }, + { 0x00001038, 0x00000000 }, + { 0x00001078, 0x00000000 }, + { 0x000010b8, 0x00000000 }, + { 0x000010f8, 0x00000000 }, + { 0x00001138, 0x00000000 }, + { 0x00001178, 0x00000000 }, + { 0x000011b8, 0x00000000 }, + { 0x000011f8, 0x00000000 }, + { 0x00001238, 0x00000000 }, + { 0x00001278, 0x00000000 }, + { 0x000012b8, 0x00000000 }, + { 0x000012f8, 0x00000000 }, + { 0x00001338, 0x00000000 }, + { 0x00001378, 0x00000000 }, + { 0x000013b8, 0x00000000 }, + { 0x000013f8, 0x00000000 }, + { 0x00001438, 0x00000000 }, + { 0x00001478, 0x00000000 }, + { 0x000014b8, 0x00000000 }, + { 0x000014f8, 0x00000000 }, + { 0x00001538, 0x00000000 }, + { 0x00001578, 0x00000000 }, + { 0x000015b8, 0x00000000 }, + { 0x000015f8, 0x00000000 }, + { 0x00001638, 0x00000000 }, + { 0x00001678, 0x00000000 }, + { 0x000016b8, 0x00000000 }, + { 0x000016f8, 0x00000000 }, + { 0x00001738, 0x00000000 }, + { 0x00001778, 0x00000000 }, + { 0x000017b8, 0x00000000 }, + { 0x000017f8, 0x00000000 }, + { 0x0000103c, 0x00000000 }, + { 0x0000107c, 0x00000000 }, + { 0x000010bc, 0x00000000 }, + { 0x000010fc, 0x00000000 }, + { 0x0000113c, 0x00000000 }, + { 0x0000117c, 0x00000000 }, + { 0x000011bc, 0x00000000 }, + { 0x000011fc, 0x00000000 }, + { 0x0000123c, 0x00000000 }, + { 0x0000127c, 0x00000000 }, + { 0x000012bc, 0x00000000 }, + { 0x000012fc, 0x00000000 }, + { 0x0000133c, 0x00000000 }, + { 0x0000137c, 0x00000000 }, + { 0x000013bc, 0x00000000 }, + { 0x000013fc, 0x00000000 }, + { 0x0000143c, 0x00000000 }, + { 0x0000147c, 0x00000000 }, + { 0x00004030, 0x00000002 }, + { 0x0000403c, 0x00000002 }, + { 0x00004024, 0x0000001f }, + { 0x00004060, 0x00000000 }, + { 0x00004064, 0x00000000 }, + { 0x00008004, 0x00000000 }, + { 0x00008008, 0x00000000 }, + { 0x0000800c, 0x00000000 }, + { 0x00008018, 0x00000700 }, + { 0x00008020, 0x00000000 }, + { 0x00008038, 0x00000000 }, + { 0x0000803c, 0x00000000 }, + { 0x00008048, 0x00000000 }, + { 0x00008054, 0x00000000 }, + { 0x00008058, 0x00000000 }, + { 0x0000805c, 0x000fc78f }, + { 0x00008060, 0x0000000f }, + { 0x00008064, 0x00000000 }, + { 0x00008070, 0x00000000 }, + { 0x000080b0, 0x00000000 }, + { 0x000080b4, 0x00000000 }, + { 0x000080b8, 0x00000000 }, + { 0x000080bc, 0x00000000 }, + { 0x000080c0, 0x2a80001a }, + { 0x000080c4, 0x05dc01e0 }, + { 0x000080c8, 0x1f402710 }, + { 0x000080cc, 0x01f40000 }, + { 0x000080d0, 0x00001e00 }, + { 0x000080d4, 0x00000000 }, + { 0x000080d8, 0x00400000 }, + { 0x000080e0, 0xffffffff }, + { 0x000080e4, 0x0000ffff }, + { 0x000080e8, 0x003f3f3f }, + { 0x000080ec, 0x00000000 }, + { 0x000080f0, 0x00000000 }, + { 0x000080f4, 0x00000000 }, + { 0x000080f8, 0x00000000 }, + { 0x000080fc, 0x00020000 }, + { 0x00008100, 0x00020000 }, + { 0x00008104, 0x00000001 }, + { 0x00008108, 0x00000052 }, + { 0x0000810c, 0x00000000 }, + { 0x00008110, 0x00000168 }, + { 0x00008118, 0x000100aa }, + { 0x0000811c, 0x00003210 }, + { 0x00008120, 0x08f04810 }, + { 0x00008124, 0x00000000 }, + { 0x00008128, 0x00000000 }, + { 0x0000812c, 0x00000000 }, + { 0x00008130, 0x00000000 }, + { 0x00008134, 0x00000000 }, + { 0x00008138, 0x00000000 }, + { 0x0000813c, 0x00000000 }, + { 0x00008144, 0xffffffff }, + { 0x00008168, 0x00000000 }, + { 0x0000816c, 0x00000000 }, + { 0x00008170, 0x32143320 }, + { 0x00008174, 0xfaa4fa50 }, + { 0x00008178, 0x00000100 }, + { 0x0000817c, 0x00000000 }, + { 0x000081c0, 0x00000000 }, + { 0x000081d0, 0x0000320a }, + { 0x000081ec, 0x00000000 }, + { 0x000081f0, 0x00000000 }, + { 0x000081f4, 0x00000000 }, + { 0x000081f8, 0x00000000 }, + { 0x000081fc, 0x00000000 }, + { 0x00008200, 0x00000000 }, + { 0x00008204, 0x00000000 }, + { 0x00008208, 0x00000000 }, + { 0x0000820c, 0x00000000 }, + { 0x00008210, 0x00000000 }, + { 0x00008214, 0x00000000 }, + { 0x00008218, 0x00000000 }, + { 0x0000821c, 0x00000000 }, + { 0x00008220, 0x00000000 }, + { 0x00008224, 0x00000000 }, + { 0x00008228, 0x00000000 }, + { 0x0000822c, 0x00000000 }, + { 0x00008230, 0x00000000 }, + { 0x00008234, 0x00000000 }, + { 0x00008238, 0x00000000 }, + { 0x0000823c, 0x00000000 }, + { 0x00008240, 0x00100000 }, + { 0x00008244, 0x0010f400 }, + { 0x00008248, 0x00000100 }, + { 0x0000824c, 0x0001e800 }, + { 0x00008250, 0x00000000 }, + { 0x00008254, 0x00000000 }, + { 0x00008258, 0x00000000 }, + { 0x0000825c, 0x400000ff }, + { 0x00008260, 0x00080922 }, + { 0x00008264, 0x88a00010 }, + { 0x00008270, 0x00000000 }, + { 0x00008274, 0x40000000 }, + { 0x00008278, 0x003e4180 }, + { 0x0000827c, 0x00000000 }, + { 0x00008284, 0x0000002c }, + { 0x00008288, 0x0000002c }, + { 0x0000828c, 0x00000000 }, + { 0x00008294, 0x00000000 }, + { 0x00008298, 0x00000000 }, + { 0x0000829c, 0x00000000 }, + { 0x00008300, 0x00000040 }, + { 0x00008314, 0x00000000 }, + { 0x00008328, 0x00000000 }, + { 0x0000832c, 0x00000001 }, + { 0x00008330, 0x00000302 }, + { 0x00008334, 0x00000e00 }, + { 0x00008338, 0x00ff0000 }, + { 0x0000833c, 0x00000000 }, + { 0x00008340, 0x00010380 }, + { 0x00008344, 0x00581043 }, + { 0x00007010, 0x00000030 }, + { 0x00007034, 0x00000002 }, + { 0x00007038, 0x000004c2 }, + { 0x00007800, 0x00140000 }, + { 0x00007804, 0x0e4548d8 }, + { 0x00007808, 0x54214514 }, + { 0x0000780c, 0x02025820 }, + { 0x00007810, 0x71c0d388 }, + { 0x00007814, 0x924934a8 }, + { 0x0000781c, 0x00000000 }, + { 0x00007828, 0x66964300 }, + { 0x0000782c, 0x8db6d961 }, + { 0x00007830, 0x8db6d96c }, + { 0x00007834, 0x6140008b }, + { 0x0000783c, 0x72ee0a72 }, + { 0x00007840, 0xbbfffffc }, + { 0x00007844, 0x000c0db6 }, + { 0x00007848, 0x6db6246f }, + { 0x0000784c, 0x6d9b66db }, + { 0x00007850, 0x6d8c6dba }, + { 0x00007854, 0x00040000 }, + { 0x00007858, 0xdb003012 }, + { 0x0000785c, 0x04924914 }, + { 0x00007860, 0x21084210 }, + { 0x00007864, 0xf7d7ffde }, + { 0x00007868, 0xc2034080 }, + { 0x00007870, 0x10142c00 }, + { 0x00009808, 0x00000000 }, + { 0x0000980c, 0xafe68e30 }, + { 0x00009810, 0xfd14e000 }, + { 0x00009814, 0x9c0a9f6b }, + { 0x0000981c, 0x00000000 }, + { 0x0000982c, 0x0000a000 }, + { 0x00009830, 0x00000000 }, + { 0x0000983c, 0x00200400 }, + { 0x0000984c, 0x0040233c }, + { 0x00009854, 0x00000044 }, + { 0x00009900, 0x00000000 }, + { 0x00009904, 0x00000000 }, + { 0x00009908, 0x00000000 }, + { 0x0000990c, 0x00000000 }, + { 0x0000991c, 0x10000fff }, + { 0x00009920, 0x04900000 }, + { 0x00009928, 0x00000001 }, + { 0x0000992c, 0x00000004 }, + { 0x00009934, 0x1e1f2022 }, + { 0x00009938, 0x0a0b0c0d }, + { 0x0000993c, 0x00000000 }, + { 0x00009940, 0x14750604 }, + { 0x00009948, 0x9280c00a }, + { 0x0000994c, 0x00020028 }, + { 0x00009954, 0x5f3ca3de }, + { 0x00009958, 0x0108ecff }, + { 0x00009968, 0x000003ce }, + { 0x00009970, 0x192bb514 }, + { 0x00009974, 0x00000000 }, + { 0x00009978, 0x00000001 }, + { 0x0000997c, 0x00000000 }, + { 0x00009980, 0x00000000 }, + { 0x00009984, 0x00000000 }, + { 0x00009988, 0x00000000 }, + { 0x0000998c, 0x00000000 }, + { 0x00009990, 0x00000000 }, + { 0x00009994, 0x00000000 }, + { 0x00009998, 0x00000000 }, + { 0x0000999c, 0x00000000 }, + { 0x000099a0, 0x00000000 }, + { 0x000099a4, 0x00000001 }, + { 0x000099a8, 0x201fff00 }, + { 0x000099ac, 0x2def0400 }, + { 0x000099b0, 0x03051000 }, + { 0x000099b4, 0x00000820 }, + { 0x000099dc, 0x00000000 }, + { 0x000099e0, 0x00000000 }, + { 0x000099e4, 0xaaaaaaaa }, + { 0x000099e8, 0x3c466478 }, + { 0x000099ec, 0x0cc80caa }, + { 0x000099f0, 0x00000000 }, + { 0x0000a208, 0x803e68c8 }, + { 0x0000a210, 0x4080a333 }, + { 0x0000a214, 0x00206c10 }, + { 0x0000a218, 0x009c4060 }, + { 0x0000a220, 0x01834061 }, + { 0x0000a224, 0x00000400 }, + { 0x0000a228, 0x000003b5 }, + { 0x0000a22c, 0x00000000 }, + { 0x0000a234, 0x20202020 }, + { 0x0000a238, 0x20202020 }, + { 0x0000a244, 0x00000000 }, + { 0x0000a248, 0xfffffffc }, + { 0x0000a24c, 0x00000000 }, + { 0x0000a254, 0x00000000 }, + { 0x0000a258, 0x0ccb5380 }, + { 0x0000a25c, 0x15151501 }, + { 0x0000a260, 0xdfa90f01 }, + { 0x0000a268, 0x00000000 }, + { 0x0000a26c, 0x0ebae9e6 }, + { 0x0000a388, 0x0c000000 }, + { 0x0000a38c, 0x20202020 }, + { 0x0000a390, 0x20202020 }, + { 0x0000a39c, 0x00000001 }, + { 0x0000a3a0, 0x00000000 }, + { 0x0000a3a4, 0x00000000 }, + { 0x0000a3a8, 0x00000000 }, + { 0x0000a3ac, 0x00000000 }, + { 0x0000a3b0, 0x00000000 }, + { 0x0000a3b4, 0x00000000 }, + { 0x0000a3b8, 0x00000000 }, + { 0x0000a3bc, 0x00000000 }, + { 0x0000a3c0, 0x00000000 }, + { 0x0000a3c4, 0x00000000 }, + { 0x0000a3cc, 0x20202020 }, + { 0x0000a3d0, 0x20202020 }, + { 0x0000a3d4, 0x20202020 }, + { 0x0000a3e4, 0x00000000 }, + { 0x0000a3e8, 0x18c43433 }, + { 0x0000a3ec, 0x00f70081 }, + { 0x0000a3f0, 0x01036a2f }, + { 0x0000a3f4, 0x00000000 }, + { 0x0000d270, 0x0d820820 }, + { 0x0000d35c, 0x07ffffef }, + { 0x0000d360, 0x0fffffe7 }, + { 0x0000d364, 0x17ffffe5 }, + { 0x0000d368, 0x1fffffe4 }, + { 0x0000d36c, 0x37ffffe3 }, + { 0x0000d370, 0x3fffffe3 }, + { 0x0000d374, 0x57ffffe3 }, + { 0x0000d378, 0x5fffffe2 }, + { 0x0000d37c, 0x7fffffe2 }, + { 0x0000d380, 0x7f3c7bba }, + { 0x0000d384, 0xf3307ff0 } +}; + +static const u_int32_t ar9271Modes_high_power_tx_gain[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x0000a300, 0x00010000, 0x00010000, 0x00010000, 0x00010000, 0x00010000 }, + { 0x0000a304, 0x00016200, 0x00016200, 0x00016200, 0x00016200, 0x00016200 }, + { 0x0000a308, 0x00018201, 0x00018201, 0x00018201, 0x00018201, 0x00018201 }, + { 0x0000a30c, 0x0001b240, 0x0001b240, 0x0001b240, 0x0001b240, 0x0001b240 }, + { 0x0000a310, 0x0001d241, 0x0001d241, 0x0001d241, 0x0001d241, 0x0001d241 }, + { 0x0000a314, 0x0001f600, 0x0001f600, 0x0001f600, 0x0001f600, 0x0001f600 }, + { 0x0000a318, 0x00022800, 0x00022800, 0x00022800, 0x00022800, 0x00022800 }, + { 0x0000a31c, 0x00026802, 0x00026802, 0x00026802, 0x00026802, 0x00026802 }, + { 0x0000a320, 0x0002b805, 0x0002b805, 0x0002b805, 0x0002b805, 0x0002b805 }, + { 0x0000a324, 0x0002ea41, 0x0002ea41, 0x0002ea41, 0x0002ea41, 0x0002ea41 }, + { 0x0000a328, 0x00038b00, 0x00038b00, 0x00038b00, 0x00038b00, 0x00038b00 }, + { 0x0000a32c, 0x0003ab40, 0x0003ab40, 0x0003ab40, 0x0003ab40, 0x0003ab40 }, + { 0x0000a330, 0x0003cd80, 0x0003cd80, 0x0003cd80, 0x0003cd80, 0x0003cd80 }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x000368de }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x00007838, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b, 0x0000002b }, + { 0x00007824, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff, 0x00d8a7ff }, + { 0x0000786c, 0x08609eba, 0x08609eba, 0x08609eba, 0x08609eba, 0x08609eba }, + { 0x00007820, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00, 0x00000c00 }, + { 0x0000a274, 0x0a214652, 0x0a214652, 0x0a214652, 0x0a214652, 0x0a214652 }, + { 0x0000a278, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7, 0x0e739ce7 }, + { 0x0000a27c, 0x05018063, 0x05018063, 0x05018063, 0x05018063, 0x05018063 }, + { 0x0000a394, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 }, + { 0x0000a398, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 }, + { 0x0000a3dc, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63, 0x06318c63 }, + { 0x0000a3e0, 0x00000063, 0x00000063, 0x00000063, 0x00000063, 0x00000063 } +}; + +static const u_int32_t ar9271Modes_original_tx_gain[][6] = { + /* Address 5G-HT20 5G-HT40 2G-HT40 2G-HT20 Turbo */ + { 0x0000a300, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, + { 0x0000a304, 0x00009200, 0x00009200, 0x00009200, 0x00009200, 0x00009200 }, + { 0x0000a308, 0x00010208, 0x00010208, 0x00010208, 0x00010208, 0x00010208 }, + { 0x0000a30c, 0x00019608, 0x00019608, 0x00019608, 0x00019608, 0x00019608 }, + { 0x0000a310, 0x0001e610, 0x0001e610, 0x0001e610, 0x0001e610, 0x0001e610 }, + { 0x0000a314, 0x0002d6d0, 0x0002d6d0, 0x0002d6d0, 0x0002d6d0, 0x0002d6d0 }, + { 0x0000a318, 0x00039758, 0x00039758, 0x00039758, 0x00039758, 0x00039758 }, + { 0x0000a31c, 0x0003b759, 0x0003b759, 0x0003b759, 0x0003b759, 0x0003b759 }, + { 0x0000a320, 0x0003d75a, 0x0003d75a, 0x0003d75a, 0x0003d75a, 0x0003d75a }, + { 0x0000a324, 0x0004175c, 0x0004175c, 0x0004175c, 0x0004175c, 0x0004175c }, + { 0x0000a328, 0x0004575e, 0x0004575e, 0x0004575e, 0x0004575e, 0x0004575e }, + { 0x0000a32c, 0x0004979f, 0x0004979f, 0x0004979f, 0x0004979f, 0x0004979f }, + { 0x0000a330, 0x0004d7df, 0x0004d7df, 0x0004d7df, 0x0004d7df, 0x0004d7df }, + { 0x0000a334, 0x000368de, 0x000368de, 0x000368de, 0x000368de, 0x000368de }, + { 0x0000a338, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e, 0x0003891e }, + { 0x0000a33c, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e, 0x0003a95e }, + { 0x0000a340, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a344, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a348, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a34c, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a350, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x0000a354, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df, 0x0003e9df }, + { 0x00007838, 0x00000029, 0x00000029, 0x00000029, 0x00000029, 0x00000029 }, + { 0x00007824, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff, 0x00d8abff }, + { 0x0000786c, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4, 0x48609eb4 }, + { 0x00007820, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04, 0x00000c04 }, + { 0x0000a274, 0x0a218652, 0x0a218652, 0x0a218652, 0x0a218652, 0x0a218652 }, + { 0x0000a278, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a27c, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd, 0x050e83bd }, + { 0x0000a394, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a398, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd }, + { 0x0000a3dc, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd, 0x3bdef7bd }, + { 0x0000a3e0, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd, 0x000003bd } +}; + +static const u_int32_t ar9271NonPciePhy_clkreq[][2] = { + /* Addr allmodes */ + {0x00004040, 0x9248fc00 }, + {0x00004040, 0x24924924 }, + {0x00004040, 0x28000029 }, + {0x00004040, 0x57160824 }, + {0x00004040, 0x25980579 }, + {0x00004040, 0x00000000 }, + {0x00004040, 0x1aaabe40 }, + {0x00004040, 0xbe105554 }, + {0x00004040, 0x000e1007 }, + {0x00004044, 0x00000000 }, +}; + diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_attach.c b/sys/dev/ath/ath_hal/ar9002/ar9271_attach.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_attach.c @@ -0,0 +1,516 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" + +#include "ah_eeprom_v4k.h" /* XXX for tx/rx gain */ + +#include "ar9002/ar9280.h" +#include "ar9002/ar9271.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +#include "ar9002/ar9285.ini" +#include "ar9002/ar9271.ini" +#include "ar9002/ar9280v2.ini" /* XXX ini for tx/rx gain */ + +#include "ar9002/ar9271_cal.h" +#include "ar9002/ar9271_phy.h" +#include "ar9002/ar9271_diversity.h" + +#include "if_ath_usb_devid.h" +#include "if_ath_usb.h" + +static const HAL_PERCAL_DATA ar9280_iq_cal = { /* single sample */ + .calName = "IQ", .calType = IQ_MISMATCH_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = PER_MAX_LOG_COUNT, + .calCollect = ar5416IQCalCollect, + .calPostProc = ar5416IQCalibration +}; +static const HAL_PERCAL_DATA ar9280_adc_gain_cal = { /* single sample */ + .calName = "ADC Gain", .calType = ADC_GAIN_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = PER_MIN_LOG_COUNT, + .calCollect = ar5416AdcGainCalCollect, + .calPostProc = ar5416AdcGainCalibration +}; +static const HAL_PERCAL_DATA ar9280_adc_dc_cal = { /* single sample */ + .calName = "ADC DC", .calType = ADC_DC_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = PER_MIN_LOG_COUNT, + .calCollect = ar5416AdcDcCalCollect, + .calPostProc = ar5416AdcDcCalibration +}; +static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { + .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = INIT_LOG_COUNT, + .calCollect = ar5416AdcDcCalCollect, + .calPostProc = ar5416AdcDcCalibration +}; + +static void ar9271ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, + HAL_BOOL power_off); +static void ar9271DisablePCIE(struct ath_hal *ah); +static HAL_BOOL ar9271FillCapabilityInfo(struct ath_hal *ah); +static void ar9271WriteIni(struct ath_hal *ah, + const struct ieee80211_channel *chan); + +static void +ar9271AniSetup(struct ath_hal *ah) +{ + /* + * These are the parameters from the AR5416 ANI code; + * they likely need quite a bit of adjustment for the + * AR9271. + */ + static const struct ar5212AniParams aniparams = { + .maxNoiseImmunityLevel = 4, /* levels 0..4 */ + .totalSizeDesired = { -55, -55, -55, -55, -62 }, + .coarseHigh = { -14, -14, -14, -14, -12 }, + .coarseLow = { -64, -64, -64, -64, -70 }, + .firpwr = { -78, -78, -78, -78, -80 }, + .maxSpurImmunityLevel = 7, + .cycPwrThr1 = { 2, 4, 6, 8, 10, 12, 14, 16 }, + .maxFirstepLevel = 2, /* levels 0..2 */ + .firstep = { 0, 4, 8 }, + .ofdmTrigHigh = 500, + .ofdmTrigLow = 200, + .cckTrigHigh = 200, + .cckTrigLow = 100, + .rssiThrHigh = 40, + .rssiThrLow = 7, + .period = 100, + }; + /* NB: disable ANI noise immmunity for reliable RIFS rx */ + AH5416(ah)->ah_ani_function &= ~(1 << HAL_ANI_NOISE_IMMUNITY_LEVEL); + + ar5416AniAttach(ah, &aniparams, &aniparams, AH_TRUE); +} + +static const char * ar9271_lna_conf[] = { + "LNA1-LNA2", + "LNA2", + "LNA1", + "LNA1+LNA2", +}; + +static void +ar9271_eeprom_print_diversity_settings(struct ath_hal *ah) +{ + const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader; + + ath_hal_printf(ah, "[ath] AR9271 Main LNA config: %s\n", + ar9271_lna_conf[(pModal->antdiv_ctl2 >> 2) & 0x3]); + ath_hal_printf(ah, "[ath] AR9271 Alt LNA config: %s\n", + ar9271_lna_conf[pModal->antdiv_ctl2 & 0x3]); + ath_hal_printf(ah, "[ath] LNA diversity %s, Diversity %s\n", + ((pModal->antdiv_ctl1 & 0x1) ? "enabled" : "disabled"), + ((pModal->antdiv_ctl1 & 0x8) ? "enabled" : "disabled")); +} + +/* + * Attach for an AR9271 part. + */ +static struct ath_hal * +ar9271Attach(uint16_t devid, HAL_SOFTC sc, + HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, + HAL_STATUS *status) +{ + struct ath_hal_9271 *ahp9271; + struct ath_hal_5212 *ahp; + struct ath_hal *ah; + uint32_t val; + HAL_STATUS ecode; + HAL_BOOL rfStatus; + + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + __func__, sc, (void*) st, (void*) sh); + + /* NB: memory is returned zero'd */ + ahp9271 = ath_hal_malloc(sizeof (struct ath_hal_9271)); + if (ahp9271 == AH_NULL) { + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, + "%s: cannot allocate memory for state block\n", __func__); + *status = HAL_ENOMEM; + return AH_NULL; + } + ahp = AH5212(ahp9271); + ah = &ahp->ah_priv.h; + + ar5416InitState(AH5416(ah), devid, sc, st, sh, status); + + /* + * Use the "local" EEPROM data given to us by the higher layers. + * This is a private copy out of system flash. The Linux ath9k + * commit for the initial AR9130 support mentions MMIO flash + * access is "unreliable." -adrian + */ + if (eepromdata != AH_NULL) { + AH_PRIVATE(ah)->ah_eepromRead = ath_hal_EepromDataRead; + AH_PRIVATE(ah)->ah_eepromWrite = NULL; + ah->ah_eepromdata = eepromdata; + } + + /* override with 9271 specific state */ + AH5416(ah)->ah_initPLL = ar9280InitPLL; + AH5416(ah)->ah_btCoexSetDiversity = ar9271BTCoexAntennaDiversity; + + ah->ah_setAntennaSwitch = ar9271SetAntennaSwitch; + ah->ah_configPCIE = ar9271ConfigPCIE; + ah->ah_disablePCIE = ar9271DisablePCIE; + ah->ah_setTxPower = ar9271SetTransmitPower; + ah->ah_setBoardValues = ar9271SetBoardValues; + ah->ah_btCoexSetParameter = ar9271BTCoexSetParameter; + ah->ah_divLnaConfGet = ar9271_antdiv_comb_conf_get; + ah->ah_divLnaConfSet = ar9271_antdiv_comb_conf_set; + + AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; + AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; + AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9280_adc_dc_cal; + AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9280_adc_init_dc_cal; + AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; + + AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; + AH5416(ah)->ah_writeIni = ar9271WriteIni; + AH5416(ah)->ah_rx_chainmask = AR9271_DEFAULT_RXCHAINMASK; + AH5416(ah)->ah_tx_chainmask = AR9271_DEFAULT_TXCHAINMASK; + + ahp->ah_maxTxTrigLev = MAX_TX_FIFO_THRESHOLD >> 1; + + AH_PRIVATE(ah)->ah_isusb = TRUE; + AH_PRIVATE(ah)->ah_usb_read = (void *)ath_usb_read; + AH_PRIVATE(ah)->ah_usb_write = (void *)ath_usb_write; + + if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { + /* reset chip */ + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", + __func__); + ecode = HAL_EIO; + goto bad; + } + + if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", + __func__); + ecode = HAL_EIO; + goto bad; + } + + /* Read Revisions from Chips before taking out of reset */ + val = OS_REG_READ(ah, AR_SREV); + HALDEBUG(ah, HAL_DEBUG_ATTACH, + "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", + __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), + MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); + /* NB: include chip type to differentiate from pre-Sowl versions */ + AH_PRIVATE(ah)->ah_macVersion = + (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; + AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); + AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; + + /* setup common ini data; rf backends handle remainder */ + HAL_INI_INIT(&ahp->ah_ini_modes, ar9271Modes, 6); + HAL_INI_INIT(&ahp->ah_ini_common, ar9271Common, 2); + HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, + ar9271NonPciePhy_clkreq, 2); + ar5416AttachPCIE(ah); + + AH5416(ah)->ah_cal_initcal = ar9271InitCalHardware; + AH5416(ah)->ah_cal_pacal = ar9271_hw_pa_cal; + + ecode = ath_hal_v4kEepromAttach(ah); + if (ecode != HAL_OK) + goto bad; + + if (!ar5416ChipReset(ah, AH_NULL, HAL_RESET_NORMAL)) { /* reset chip */ + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", + __func__); + ecode = HAL_EIO; + goto bad; + } + + AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); + + if (!ar5212ChipTest(ah)) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", + __func__); + ecode = HAL_ESELFTEST; + goto bad; + } + + /* + * Set correct Baseband to analog shift + * setting to access analog chips. + */ + OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); + + /* Read Radio Chip Rev Extract */ + AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); + switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { + case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ + case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ + break; + default: + if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { + AH_PRIVATE(ah)->ah_analog5GhzRev = + AR_RAD5133_SREV_MAJOR; + break; + } +#ifdef AH_DEBUG + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: 5G Radio Chip Rev 0x%02X is not supported by " + "this driver\n", __func__, + AH_PRIVATE(ah)->ah_analog5GhzRev); + ecode = HAL_ENOTSUPP; + goto bad; +#endif + } + rfStatus = ar9271RfAttach(ah, &ecode); + if (!rfStatus) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", + __func__, ecode); + goto bad; + } + + HAL_INI_INIT(&ahp9271->ah_ini_rxgain, ar9280Modes_original_rxgain_v2, + 6); + + /* setup txgain table */ + switch (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL)) { + case AR5416_EEP_TXGAIN_HIGH_POWER: + HAL_INI_INIT(&ahp9271->ah_ini_txgain, + ar9271Modes_high_power_tx_gain, 6); + break; + case AR5416_EEP_TXGAIN_ORIG: + HAL_INI_INIT(&ahp9271->ah_ini_txgain, + ar9271Modes_original_tx_gain, 6); + break; + default: + HALASSERT(AH_FALSE); + goto bad; /* XXX ? try to continue */ + } + + /* + * Got everything we need now to setup the capabilities. + */ + if (!ar9271FillCapabilityInfo(ah)) { + ecode = HAL_EEREAD; + goto bad; + } + + /* + * Print out the EEPROM antenna configuration mapping. + * Some devices have a hard-coded LNA configuration profile; + * others enable diversity. + */ + ar9271_eeprom_print_diversity_settings(ah); + + /* Print out whether the EEPROM settings enable AR9285 diversity */ + if (ar9271_check_div_comb(ah)) { + ath_hal_printf(ah, "[ath] Enabling diversity for Kite\n"); + } + + /* Disable 11n for the AR2427 */ + if (devid == AR2427_DEVID_PCIE) + AH_PRIVATE(ah)->ah_caps.halHTSupport = AH_FALSE; + + ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); + if (ecode != HAL_OK) { + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: error getting mac address from EEPROM\n", __func__); + goto bad; + } + /* XXX How about the serial number ? */ + /* Read Reg Domain */ + AH_PRIVATE(ah)->ah_currentRD = + ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); + /* + * For Kite and later chipsets, the following bits are not + * programmed in EEPROM and so are set as enabled always. + */ + AH_PRIVATE(ah)->ah_currentRDext = AR9285_RDEXT_DEFAULT; + + /* + * ah_miscMode is populated by ar5416FillCapabilityInfo() + * starting from griffin. Set here to make sure that + * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is + * placed into hardware. + */ + if (ahp->ah_miscMode != 0) + OS_REG_WRITE(ah, AR_MISC_MODE, OS_REG_READ(ah, AR_MISC_MODE) | ahp->ah_miscMode); + + ar9271AniSetup(ah); /* Anti Noise Immunity */ + + /* Setup noise floor min/max/nominal values */ + AH5416(ah)->nf_2g.max = AR_PHY_CCA_MAX_GOOD_VAL_9271_2GHZ; + AH5416(ah)->nf_2g.min = AR_PHY_CCA_MIN_GOOD_VAL_9271_2GHZ; + AH5416(ah)->nf_2g.nominal = AR_PHY_CCA_NOM_VAL_9271_2GHZ; + /* XXX no 5ghz values? */ + + ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); + + HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); + + return ah; +bad: + if (ah != AH_NULL) + ah->ah_detach(ah); + if (status) + *status = ecode; + return AH_NULL; +} + +static void +ar9271ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore, HAL_BOOL power_off) +{ +} + +static void +ar9271DisablePCIE(struct ath_hal *ah) +{ +} + +static void +ar9271WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + u_int modesIndex, freqIndex; + int regWrites = 0; + + /* Setup the indices for the next set of register array writes */ + /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ + freqIndex = 2; + if (IEEE80211_IS_CHAN_HT40(chan)) + modesIndex = 3; + else if (IEEE80211_IS_CHAN_108G(chan)) + modesIndex = 5; + else + modesIndex = 4; + + /* Set correct Baseband to analog shift setting to access analog chips. */ + OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); + OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); + regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, + modesIndex, regWrites); + if (AR_SREV_KITE_12_OR_LATER(ah)) { + regWrites = ath_hal_ini_write(ah, &AH9271(ah)->ah_ini_txgain, + modesIndex, regWrites); + } + regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, + 1, regWrites); +} + +/* + * Fill all software cached or static hardware state information. + * Return failure if capabilities are to come from EEPROM and + * cannot be read. + */ +static HAL_BOOL +ar9271FillCapabilityInfo(struct ath_hal *ah) +{ + HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; + + if (!ar5416FillCapabilityInfo(ah)) + return AH_FALSE; + pCap->halNumGpioPins = (AH_PRIVATE(ah)->ah_isusb) ? 16 : 12; + /* Wake-on-Wireless */ + pCap->halWowSupport = AH_FALSE; + pCap->halWowMatchPatternExact = AH_FALSE; + + /* AR9271 supports one transmit and one receive traffic stream */ + pCap->halTxStreams = 1; + pCap->halRxStreams = 1; + + if (ar9271_check_div_comb(ah)) + pCap->halAntDivCombSupport = AH_TRUE; + + pCap->halCSTSupport = AH_TRUE; + pCap->halRifsRxSupport = AH_TRUE; + pCap->halRifsTxSupport = AH_TRUE; + pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ + pCap->halExtChanDfsSupport = AH_TRUE; + pCap->halUseCombinedRadarRssi = AH_TRUE; +#if 1 + /* XXX bluetooth */ + pCap->halBtCoexSupport = AH_FALSE; +#endif + pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ + pCap->hal4kbSplitTransSupport = AH_FALSE; + /* Disable this so Block-ACK works correctly */ + pCap->halHasRxSelfLinkedTail = AH_FALSE; + pCap->halMbssidAggrSupport = AH_TRUE; + pCap->hal4AddrAggrSupport = AH_TRUE; + pCap->halSpectralScanSupport = AH_TRUE; + pCap->halRxUsingLnaMixing = AH_TRUE; + + /* TODO: Not sure. For AR9285 and AR9280 it's false for later revs */ + pCap->halPSPollBroken = AH_FALSE; + + /* Only RX STBC supported */ + pCap->halRxStbcSupport = 1; + pCap->halTxStbcSupport = 0; + + return AH_TRUE; +} + +static const char* +ar9271Probe(uint16_t vendorid, uint16_t devid) +{ + if (vendorid == USB_VENDOR_ATHEROS2) { + if ((devid == USB_PRODUCT_ATHEROS2_AR9271_1) || + (devid == USB_PRODUCT_ATHEROS2_AR9271_2) || + (devid == USB_PRODUCT_ATHEROS2_AR9271_3)) + return "Atheros 9271"; + } + if (vendorid == USB_VENDOR_AZUREWAVE) { + if ((devid == USB_PRODUCT_AZUREWAVE_AR9271_1) || + (devid == USB_PRODUCT_AZUREWAVE_AR9271_2) || + (devid == USB_PRODUCT_AZUREWAVE_AR9271_3) || + (devid == USB_PRODUCT_AZUREWAVE_AR9271_4) || + (devid == USB_PRODUCT_AZUREWAVE_AR9271_5) || + (devid == USB_PRODUCT_AZUREWAVE_AR9271_6)) + return "Atheros 9271 AZUREWAVE"; + } + if (vendorid == USB_VENDOR_DLINK2) { + if (devid == USB_PRODUCT_DLINK2_AR9271) + return "Atheros 9271 DLINK2"; + } + if (vendorid == USB_VENDOR_LITEON) { + if (devid == USB_PRODUCT_LITEON_AR9271) + return "Atheros 9271 LITEON"; + } + if (vendorid == USB_VENDOR_NETGEAR) { + if (devid == USB_PRODUCT_NETGEAR_WNA1100) + return "Atheros 9271 NETGEAR"; + } + if (vendorid == USB_VENDOR_VIA) { + if (devid == USB_PRODUCT_VIA_AR9271) + return "Atheros 9271 VIA"; + } + return AH_NULL; +} +AH_CHIP(AR9271, ar9271Probe, ar9271Attach); diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_btcoex.c b/sys/dev/ath/ath_hal/ar9002/ar9271_btcoex.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_btcoex.c @@ -0,0 +1,153 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2002-2005 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2005 Atheros Communications, Inc. + * Copyright (c) 2008-2010, Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + * + * $FreeBSD$ + */ + +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#ifdef AH_DEBUG +#include "ah_desc.h" /* NB: for HAL_PHYERR* */ +#endif + +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar5416/ar5416desc.h" /* AR5416_CONTTXMODE */ + +#include "ar9002/ar9285phy.h" +#include "ar9002/ar9285.h" +#include "ar9002/ar9271.h" + +/* + * This is specific to Kite. + * + * Kiwi and others don't have antenna diversity like this. + */ +void +ar9271BTCoexAntennaDiversity(struct ath_hal *ah) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + u_int32_t regVal; + u_int8_t ant_div_control1, ant_div_control2; + + HALDEBUG(ah, HAL_DEBUG_BT_COEX, + "%s: btCoexFlag: ALLOW=%d, ENABLE=%d\n", + __func__, + !! (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW), + !! (ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ENABLE)); + + if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ALLOW) || + (AH5212(ah)->ah_diversity != HAL_ANT_VARIABLE)) { + if ((ahp->ah_btCoexFlag & HAL_BT_COEX_FLAG_ANT_DIV_ENABLE) && + (AH5212(ah)->ah_antControl == HAL_ANT_VARIABLE)) { + /* Enable antenna diversity */ + ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_ENABLE; + ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_ENABLE; + + /* Don't disable BT ant to allow BB to control SWCOM */ + ahp->ah_btCoexMode2 &= (~(AR_BT_DISABLE_BT_ANT)); + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* Program the correct SWCOM table */ + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, + HAL_BT_COEX_ANT_DIV_SWITCH_COM); + OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000); + } else if (AH5212(ah)->ah_antControl == HAL_ANT_FIXED_B) { + /* Disable antenna diversity. Use antenna B(LNA2) only. */ + ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_B; + ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_B; + + /* Disable BT ant to allow concurrent BT and WLAN receive */ + ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT; + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* + * Program SWCOM table to make sure RF switch always parks + * at WLAN side + */ + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, + HAL_BT_COEX_ANT_DIV_SWITCH_COM); + OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0x60000000, 0xf0000000); + } else { + /* Disable antenna diversity. Use antenna A(LNA1) only */ + ant_div_control1 = HAL_BT_COEX_ANTDIV_CONTROL1_FIXED_A; + ant_div_control2 = HAL_BT_COEX_ANTDIV_CONTROL2_FIXED_A; + + /* Disable BT ant to allow concurrent BT and WLAN receive */ + ahp->ah_btCoexMode2 |= AR_BT_DISABLE_BT_ANT; + OS_REG_WRITE(ah, AR_BT_COEX_MODE2, ahp->ah_btCoexMode2); + + /* + * Program SWCOM table to make sure RF switch always + * parks at BT side + */ + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, 0); + OS_REG_RMW(ah, AR_PHY_SWITCH_CHAIN_0, 0, 0xf0000000); + } + + regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL)); + /* + * Clear ant_fast_div_bias [14:9] since for Janus the main LNA is + * always LNA1. + */ + regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS)); + + regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL); + regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); + + regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT); + regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + regVal |= SM((ant_div_control1 >> 3), + AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal); + } +} + +void +ar9271BTCoexSetParameter(struct ath_hal *ah, u_int32_t type, u_int32_t value) +{ + struct ath_hal_5416 *ahp = AH5416(ah); + + switch (type) { + case HAL_BT_COEX_ANTENNA_DIVERSITY: + if (AR_SREV_KITE(ah)) { + ahp->ah_btCoexFlag |= HAL_BT_COEX_FLAG_ANT_DIV_ALLOW; + if (value) + ahp->ah_btCoexFlag |= + HAL_BT_COEX_FLAG_ANT_DIV_ENABLE; + else + ahp->ah_btCoexFlag &= + ~HAL_BT_COEX_FLAG_ANT_DIV_ENABLE; + ar9271BTCoexAntennaDiversity(ah); + } + break; + default: + ar5416BTCoexSetParameter(ah, type, value); + break; + } +} diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_cal.h b/sys/dev/ath/ath_hal/ar9002/ar9271_cal.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_cal.h @@ -0,0 +1,36 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2010 Atheros Communications Inc. + * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef __AR9271_CAL_H__ +#define __AR9271_CAL_H__ + +extern void ar9271_hw_pa_cal(struct ath_hal *ah, HAL_BOOL is_reset); +extern HAL_BOOL ar9271InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan); + +#endif diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_cal.c b/sys/dev/ath/ath_hal/ar9002/ar9271_cal.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_cal.c @@ -0,0 +1,260 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2010 Atheros Communications Inc. + * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include "opt_ah.h" +#include "ah.h" +#include "ah_internal.h" + +#include "ah_eeprom_v4k.h" + +#include "ar9002/ar9271.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar9002/ar9002phy.h" +#include "ar9002/ar9285phy.h" +#include "ar9002/ar9271an.h" + +#include "ar9002/ar9271_cal.h" + +#define AR9271_CLCAL_REDO_THRESH 1 +#define MAX_PACAL_SKIPCOUNT 8 + +#define N(a) (sizeof (a) / sizeof (a[0])) + +static void +ar9271_hw_pa_calib(struct ath_hal *ah, HAL_BOOL is_reset) +{ + uint32_t regVal; + int i, offset, offs_6_1, offs_0; + uint32_t ccomp_org, reg_field; + uint32_t regList[][2] = { + { 0x786c, 0 }, + { 0x7854, 0 }, + { 0x7820, 0 }, + { 0x7824, 0 }, + { 0x7868, 0 }, + { 0x783c, 0 }, + { 0x7838, 0 }, + }; + + HALDEBUG(ah, HAL_DEBUG_PERCAL, "Running PA Calibration\n"); + + for (i = 0; i < N(regList); i++) + regList[i][1] = OS_REG_READ(ah, regList[i][0]); + + regVal = OS_REG_READ(ah, 0x7834); + regVal &= (~(0x1)); + OS_REG_WRITE(ah, 0x7834, regVal); + regVal = OS_REG_READ(ah, 0x9808); + regVal |= (0x1 << 27); + OS_REG_WRITE(ah, 0x9808, regVal); + + OS_REG_RMW_FIELD(ah, AR9285_AN_TOP3, AR9285_AN_TOP3_PWDDAC, 1); + OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDRXTXBB1, 1); + OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDV2I, 1); + OS_REG_RMW_FIELD(ah, AR9285_AN_RXTXBB1, AR9285_AN_RXTXBB1_PDDACIF, 1); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G2, AR9285_AN_RF2G2_OFFCAL, 0); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PWDDB, 0); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_ENPACAL, 0); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV1, 0); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPADRV2, 0); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G1, AR9285_AN_RF2G1_PDPAOUT, 0); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G8, AR9285_AN_RF2G8_PADRVGN2TAB0, 7); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G7, AR9285_AN_RF2G7_PADRVGN2TAB0, 0); + ccomp_org = OS_REG_READ(ah, AR9285_AN_RF2G3); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_CCOMP, 0xfff); + + OS_REG_WRITE(ah, AR9285_AN_TOP2, 0xca0358a0); + OS_DELAY(30); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9271_AN_RF2G6_OFFS, 0); + + for (i = 6; i > 0; i--) { + regVal = OS_REG_READ(ah, AR9285_AN_RF2G6); + regVal |= (1 << (AR9271_AN_RF2G6_OFFS_S + i)); + OS_REG_WRITE(ah, AR9285_AN_RF2G6, regVal); + OS_DELAY(1); + regVal = OS_REG_READ(ah, AR9285_AN_RF2G6); + regVal &= (~(0x1 << (AR9271_AN_RF2G6_OFFS_S + i))); + reg_field = MS(OS_REG_READ(ah, AR9285_AN_RF2G9), AR9285_AN_RXTXBB1_SPARE9); + regVal |= (reg_field << (AR9271_AN_RF2G6_OFFS_S + i)); + OS_REG_WRITE(ah, AR9285_AN_RF2G6, regVal); + } + + offs_6_1 = MS(OS_REG_READ(ah, AR9285_AN_RF2G6), AR9285_AN_RF2G6_OFFS); + offs_0 = MS(OS_REG_READ(ah, AR9285_AN_RF2G3), AR9285_AN_RF2G3_PDVCCOMP); + + offset = (offs_6_1<<1) | offs_0; + offset = offset - 0; + offs_6_1 = offset>>1; + offs_0 = offset & 1; + + if ((!is_reset) && (AH9271(ah)->pacal_info.prev_offset == offset)) { + if (AH9271(ah)->pacal_info.max_skipcount < MAX_PACAL_SKIPCOUNT) + AH9271(ah)->pacal_info.max_skipcount = + 2 * AH9271(ah)->pacal_info.max_skipcount; + AH9271(ah)->pacal_info.skipcount = AH9271(ah)->pacal_info.max_skipcount; + } else { + AH9271(ah)->pacal_info.max_skipcount = 1; + AH9271(ah)->pacal_info.skipcount = 0; + AH9271(ah)->pacal_info.prev_offset = offset; + } + + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G6, AR9285_AN_RF2G6_OFFS, offs_6_1); + OS_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9285_AN_RF2G3_PDVCCOMP, offs_0); + + regVal = OS_REG_READ(ah, AR9285_AN_RF2G6); + regVal |= 0x1; + OS_REG_WRITE(ah, AR9285_AN_RF2G6, regVal); + regVal = OS_REG_READ(ah, AR_PHY_TESTCTRL); + regVal &= (~(0x1 << 27)); + OS_REG_WRITE(ah, AR_PHY_TESTCTRL, regVal); + + for (i = 0; i < N(regList); i++) + OS_REG_WRITE(ah, regList[i][0], regList[i][1]); + + OS_REG_WRITE(ah, AR9285_AN_RF2G3, ccomp_org); +} + +void +ar9271_hw_pa_cal(struct ath_hal *ah, HAL_BOOL is_reset) +{ + if (AR_SREV_KITE_11_OR_LATER(ah)) { + if (is_reset || !AH9271(ah)->pacal_info.skipcount) + ar9271_hw_pa_calib(ah, is_reset); + else + AH9271(ah)->pacal_info.skipcount--; + } +} + +/* Carrier leakage Calibration fix */ +static HAL_BOOL +ar9271_hw_cl_cal(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + if (IEEE80211_IS_CHAN_HT20(chan)) { + OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); + OS_REG_SET_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); + OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_FLTR_CAL); + OS_REG_CLR_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); + if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, + AR_PHY_AGC_CONTROL_CAL, 0)) { + HALDEBUG(ah, HAL_DEBUG_PERCAL, + "offset calibration failed to complete in 1ms; noisy environment?\n"); + return AH_FALSE; + } + OS_REG_CLR_BIT(ah, AR_PHY_TURBO, AR_PHY_FC_DYN2040_EN); + OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_PARALLEL_CAL_ENABLE); + OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + } + OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + OS_REG_SET_BIT(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_PD_CAL_ENABLE); + OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL); + if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, + 0)) { + HALDEBUG(ah, HAL_DEBUG_PERCAL, + "offset calibration failed to complete in 1ms; noisy environment?\n"); + return AH_FALSE; + } + + OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC); + OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE); + OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_FLTR_CAL); + + return AH_TRUE; +} + +static HAL_BOOL +ar9271_hw_clc(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + int i; + uint32_t txgain_max; + uint32_t clc_gain, gain_mask = 0, clc_num = 0; + uint32_t reg_clc_I0, reg_clc_Q0; + uint32_t i0_num = 0; + uint32_t q0_num = 0; + uint32_t total_num = 0; + uint32_t reg_rf2g5_org; + HAL_BOOL retv = AH_TRUE; + + if (!(ar9271_hw_cl_cal(ah, chan))) + return AH_FALSE; + + txgain_max = MS(OS_REG_READ(ah, AR_PHY_TX_PWRCTRL7), + AR_PHY_TX_PWRCTRL_TX_GAIN_TAB_MAX); + + for (i = 0; i < (txgain_max+1); i++) { + clc_gain = (OS_REG_READ(ah, (AR_PHY_TX_GAIN_TBL1+(i<<2))) & + AR_PHY_TX_GAIN_CLC) >> AR_PHY_TX_GAIN_CLC_S; + if (!(gain_mask & (1 << clc_gain))) { + gain_mask |= (1 << clc_gain); + clc_num++; + } + } + + for (i = 0; i < clc_num; i++) { + reg_clc_I0 = (OS_REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) + & AR_PHY_CLC_I0) >> AR_PHY_CLC_I0_S; + reg_clc_Q0 = (OS_REG_READ(ah, (AR_PHY_CLC_TBL1 + (i << 2))) + & AR_PHY_CLC_Q0) >> AR_PHY_CLC_Q0_S; + if (reg_clc_I0 == 0) + i0_num++; + + if (reg_clc_Q0 == 0) + q0_num++; + } + total_num = i0_num + q0_num; + if (total_num > AR9271_CLCAL_REDO_THRESH) { + reg_rf2g5_org = OS_REG_READ(ah, AR9285_RF2G5); + if (AR_SREV_9285E_20(ah)) { + OS_REG_WRITE(ah, AR9285_RF2G5, + (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | + AR9285_RF2G5_IC50TX_XE_SET); + } else { + OS_REG_WRITE(ah, AR9285_RF2G5, + (reg_rf2g5_org & AR9285_RF2G5_IC50TX) | + AR9285_RF2G5_IC50TX_SET); + } + retv = ar9271_hw_cl_cal(ah, chan); + OS_REG_WRITE(ah, AR9285_RF2G5, reg_rf2g5_org); + } + return retv; +} + +HAL_BOOL +ar9271InitCalHardware(struct ath_hal *ah, + const struct ieee80211_channel *chan) +{ + if (AR_SREV_9271(ah) && (! ar9271_hw_clc(ah, chan))) + return AH_FALSE; + + return AH_TRUE; +} diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_diversity.h b/sys/dev/ath/ath_hal/ar9002/ar9271_diversity.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_diversity.h @@ -0,0 +1,35 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2010 Atheros Communications Inc. + * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef __AR9271_DIVERSITY_H__ +#define __AR9271_DIVERSITY_H__ + +extern HAL_BOOL ar9271SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); + +#endif diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_diversity.c b/sys/dev/ath/ath_hal/ar9002/ar9271_diversity.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_diversity.c @@ -0,0 +1,158 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2010 Atheros Communications Inc. + * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_desc.h" +#include "ah_internal.h" +#include "ah_eeprom_v4k.h" + +#include "ar9002/ar9280.h" +#include "ar9002/ar9271.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar9002/ar9285phy.h" +#include "ar9002/ar9271_phy.h" + +#include "ar9002/ar9271_diversity.h" + +/* + * Set the antenna switch to control RX antenna diversity. + * + * If a fixed configuration is used, the LNA and div bias + * settings are fixed and the antenna diversity scanning routine + * is disabled. + * + * If a variable configuration is used, a default is programmed + * in and sampling commences per RXed packet. + * + * Since this is called from ar9271SetBoardValues() to setup + * diversity, it means that after a reset or scan, any current + * software diversity combining settings will be lost and won't + * re-appear until after the first successful sample run. + * Please keep this in mind if you're seeing weird performance + * that happens to relate to scan/diversity timing. + */ +HAL_BOOL +ar9271SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) +{ + int regVal; + const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader; + uint8_t ant_div_control1, ant_div_control2; + + if (pModal->version < 3) { + HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: not supported\n", + __func__); + return AH_FALSE; /* Can't do diversity */ + } + + /* Store settings */ + AH5212(ah)->ah_antControl = settings; + AH5212(ah)->ah_diversity = (settings == HAL_ANT_VARIABLE); + + /* XXX don't fiddle if the PHY is in sleep mode or ! chan */ + + /* Begin setting the relevant registers */ + + ant_div_control1 = pModal->antdiv_ctl1; + ant_div_control2 = pModal->antdiv_ctl2; + + regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL)); + + /* enable antenna diversity only if diversityControl == HAL_ANT_VARIABLE */ + if (settings == HAL_ANT_VARIABLE) + regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL); + + if (settings == HAL_ANT_VARIABLE) { + HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_VARIABLE\n", + __func__); + regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + } else { + if (settings == HAL_ANT_FIXED_A) { + /* Diversity disabled, RX = LNA1 */ + HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_A\n", + __func__); + regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + } + else if (settings == HAL_ANT_FIXED_B) { + /* Diversity disabled, RX = LNA2 */ + HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_B\n", + __func__); + regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_ALT_GAINTB); + regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_MAIN_GAINTB); + } + } + + OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); + regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT); + regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + if (settings == HAL_ANT_VARIABLE) + regVal |= SM((ant_div_control1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV); + + OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal); + regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT); + + /* + * If Diversity combining is available and the diversity setting + * is to allow variable diversity, enable it by default. + * + * This will be eventually overridden by the software antenna + * diversity logic. + * + * Note that yes, this following section overrides the above + * settings for the LNA configuration and fast-bias. + */ + if (ar9271_check_div_comb(ah) && AH5212(ah)->ah_diversity == AH_TRUE) { + // If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning + HALDEBUG(ah, HAL_DEBUG_DIVERSITY, + "%s: Enable initial settings for combined diversity\n", + __func__); + regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF)); + regVal |= (HAL_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S); + regVal |= (HAL_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S); + regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS)); + regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S); + OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal); + } + + return AH_TRUE; +} diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_phy.h b/sys/dev/ath/ath_hal/ar9002/ar9271_phy.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_phy.h @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2010 Atheros Communications Inc. + * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef __AR9271_PHY_H__ +#define __AR9271_PHY_H__ + +/* + * Manipulate AR9271 antenna diversity configuration + */ + +extern void ar9271_antdiv_comb_conf_set(struct ath_hal *ah, + HAL_ANT_COMB_CONFIG *antconf); +extern void ar9271_antdiv_comb_conf_get(struct ath_hal *ah, + HAL_ANT_COMB_CONFIG *antconf); +extern HAL_BOOL ar9271_check_div_comb(struct ath_hal *ah); + +#endif diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_phy.c b/sys/dev/ath/ath_hal/ar9002/ar9271_phy.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_phy.c @@ -0,0 +1,107 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2008-2010 Atheros Communications Inc. + * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" +#include "ah_eeprom_v4k.h" + +#include "ar9002/ar9280.h" +#include "ar9002/ar9285.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar9002/ar9285phy.h" +#include "ar9002/ar9271_phy.h" + +void +ar9271_antdiv_comb_conf_get(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *antconf) +{ + uint32_t regval; + + regval = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + antconf->main_lna_conf = (regval & AR_PHY_9285_ANT_DIV_MAIN_LNACONF) >> + AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S; + antconf->alt_lna_conf = (regval & AR_PHY_9285_ANT_DIV_ALT_LNACONF) >> + AR_PHY_9285_ANT_DIV_ALT_LNACONF_S; + antconf->fast_div_bias = (regval & AR_PHY_9285_FAST_DIV_BIAS) >> + AR_PHY_9285_FAST_DIV_BIAS_S; + antconf->antdiv_configgroup = DEFAULT_ANTDIV_CONFIG_GROUP; +} + +void +ar9271_antdiv_comb_conf_set(struct ath_hal *ah, HAL_ANT_COMB_CONFIG *antconf) +{ + uint32_t regval; + + regval = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL); + regval &= ~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | + AR_PHY_9285_ANT_DIV_ALT_LNACONF | + AR_PHY_9285_FAST_DIV_BIAS); + regval |= ((antconf->main_lna_conf << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S) + & AR_PHY_9285_ANT_DIV_MAIN_LNACONF); + regval |= ((antconf->alt_lna_conf << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S) + & AR_PHY_9285_ANT_DIV_ALT_LNACONF); + regval |= ((antconf->fast_div_bias << AR_PHY_9285_FAST_DIV_BIAS_S) + & AR_PHY_9285_FAST_DIV_BIAS); + + OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval); +} + +/* + * Check whether combined + fast antenna diversity should be enabled. + * + * This enables software-driven RX antenna diversity based on RX + * RSSI + antenna config packet sampling. + */ +HAL_BOOL +ar9271_check_div_comb(struct ath_hal *ah) +{ + uint8_t ant_div_ctl1; + HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader; + +#if 0 + /* For now, simply disable this until it's better debugged. -adrian */ + return AH_FALSE; +#endif + + if (! AR_SREV_9271(ah)) + return AH_FALSE; + + if (pModal->version < 3) + return AH_FALSE; + + ant_div_ctl1 = pModal->antdiv_ctl1; + if ((ant_div_ctl1 & 0x1) && ((ant_div_ctl1 >> 3) & 0x1)) + return AH_TRUE; + + return AH_FALSE; +} diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271_reset.c b/sys/dev/ath/ath_hal/ar9002/ar9271_reset.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271_reset.c @@ -0,0 +1,789 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ + +/* + * This is almost the same as ar5416_reset.c but uses the v4k EEPROM and + * supports only 2Ghz operation. + */ + +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" + +#include "ah_eeprom_v14.h" +#include "ah_eeprom_v4k.h" + +#include "ar9002/ar9271.h" +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar9002/ar9002phy.h" +#include "ar9002/ar9271_phy.h" +#include "ar9002/ar9271an.h" +#include "ar9002/ar9271_diversity.h" + +/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ +#define EEP_MINOR(_ah) \ + (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) +#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) +#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) + +/* Additional Time delay to wait after activiting the Base band */ +#define BASE_ACTIVATE_DELAY 100 /* 100 usec */ +#define PLL_SETTLE_DELAY 300 /* 300 usec */ +#define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */ + +static HAL_BOOL ar9271SetPowerPerRateTable(struct ath_hal *ah, + struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, int16_t *ratesArray, + uint16_t cfgCtl, uint16_t AntennaReduction, + uint16_t twiceMaxRegulatoryPower, + uint16_t powerLimit); +static HAL_BOOL ar9271SetPowerCalTable(struct ath_hal *ah, + struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, + int16_t *pTxPowerIndexOffset); +static void ar9271GetGainBoundariesAndPdadcs(struct ath_hal *ah, + const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ_4K *pRawDataSet, + uint8_t * bChans, uint16_t availPiers, + uint16_t tPdGainOverlap, int16_t *pMinCalPower, + uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, + uint16_t numXpdGains); + +HAL_BOOL +ar9271SetTransmitPower(struct ath_hal *ah, + const struct ieee80211_channel *chan, uint16_t *rfXpdGain) +{ +#define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) +#define N(a) (sizeof (a) / sizeof (a[0])) + + MODAL_EEP4K_HEADER *pModal; + struct ath_hal_5212 *ahp = AH5212(ah); + int16_t txPowerIndexOffset = 0; + int i; + + uint16_t cfgCtl; + uint16_t powerLimit; + uint16_t twiceAntennaReduction; + uint16_t twiceMaxRegulatoryPower; + int16_t maxPower; + HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + struct ar5416eeprom_4k *pEepData = &ee->ee_base; + + HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); + + AH5416(ah)->ah_ht40PowerIncForPdadc = 2; + + /* Setup info for the actual eeprom */ + OS_MEMZERO(AH5416(ah)->ah_ratesArray, sizeof(AH5416(ah)->ah_ratesArray)); + cfgCtl = ath_hal_getctl(ah, chan); + powerLimit = chan->ic_maxregpower * 2; + twiceAntennaReduction = chan->ic_maxantgain; + twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); + pModal = &pEepData->modalHeader; + HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", + __func__,chan->ic_freq, cfgCtl ); + + if (IS_EEP_MINOR_V2(ah)) { + AH5416(ah)->ah_ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + } + + if (!ar9271SetPowerPerRateTable(ah, pEepData, chan, + &AH5416(ah)->ah_ratesArray[0],cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, powerLimit)) { + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: unable to set tx power per rate table\n", __func__); + return AH_FALSE; + } + + if (!ar9271SetPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", + __func__); + return AH_FALSE; + } + + maxPower = AH_MAX(AH5416(ah)->ah_ratesArray[rate6mb], + AH5416(ah)->ah_ratesArray[rateHt20_0]); + maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rate1l]); + + if (IEEE80211_IS_CHAN_HT40(chan)) { + maxPower = AH_MAX(maxPower, AH5416(ah)->ah_ratesArray[rateHt40_0]); + } + + ahp->ah_tx6PowerInHalfDbm = maxPower; + AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; + ahp->ah_txPowerIndexOffset = txPowerIndexOffset; + + /* + * txPowerIndexOffset is set by the SetPowerTable() call - + * adjust the rate table (0 offset if rates EEPROM not loaded) + */ + for (i = 0; i < N(AH5416(ah)->ah_ratesArray); i++) { + AH5416(ah)->ah_ratesArray[i] = (int16_t)(txPowerIndexOffset + AH5416(ah)->ah_ratesArray[i]); + /* -5 dBm offset for Merlin and later; this includes Kite */ + AH5416(ah)->ah_ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; + if (AH5416(ah)->ah_ratesArray[i] > AR5416_MAX_RATE_POWER) + AH5416(ah)->ah_ratesArray[i] = AR5416_MAX_RATE_POWER; + if (AH5416(ah)->ah_ratesArray[i] < 0) + AH5416(ah)->ah_ratesArray[i] = 0; + } + +#ifdef AH_EEPROM_DUMP + ar5416PrintPowerPerRate(ah, AH5416(ah)->ah_ratesArray); +#endif + + /* + * Adjust the HT40 power to meet the correct target TX power + * for 40MHz mode, based on TX power curves that are established + * for 20MHz mode. + * + * XXX handle overflow/too high power level? + */ + if (IEEE80211_IS_CHAN_HT40(chan)) { + AH5416(ah)->ah_ratesArray[rateHt40_0] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_1] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_2] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_3] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_4] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_5] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_6] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + AH5416(ah)->ah_ratesArray[rateHt40_7] += + AH5416(ah)->ah_ht40PowerIncForPdadc; + } + + /* Write the TX power rate registers */ + ar5416WriteTxPowerRateRegisters(ah, chan, AH5416(ah)->ah_ratesArray); + + return AH_TRUE; +#undef POW_SM +#undef N +} + +static void +ar9271SetBoardGain(struct ath_hal *ah, const MODAL_EEP4K_HEADER *pModal, + const struct ar5416eeprom_4k *eep, uint8_t txRxAttenLocal) +{ + OS_REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0, + pModal->antCtrlChain[0]); + + OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4_CHAIN(0), + (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4_CHAIN(0)) & + ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | + AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | + SM(pModal->iqCalICh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[0], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + txRxAttenLocal = pModal->txRxAttenCh[0]; + + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, pModal->bswMargin[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, pModal->xatten2Margin[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); + + /* Set the block 1 value to block 0 value */ + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN1_MARGIN, + pModal->bswMargin[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN1_DB, pModal->bswAtten[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN2_MARGIN, + pModal->xatten2Margin[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ + 0x1000, + AR_PHY_GAIN_2GHZ_XATTEN2_DB, pModal->xatten2Db[0]); + } + + OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN, + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); + OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN, + AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); + + OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, + AR9280_PHY_RXGAIN_TXRX_ATTEN, txRxAttenLocal); + OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN + 0x1000, + AR9280_PHY_RXGAIN_TXRX_MARGIN, pModal->rxTxMarginCh[0]); +} + +/* + * Read EEPROM header info and program the device for correct operation + * given the channel value. + */ +HAL_BOOL +ar9271SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + const struct ar5416eeprom_4k *eep = &ee->ee_base; + const MODAL_EEP4K_HEADER *pModal; + uint8_t txRxAttenLocal; + uint8_t ob[5], db1[5], db2[5]; + + pModal = &eep->modalHeader; + txRxAttenLocal = 23; + + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); + + /* Single chain for 4K EEPROM*/ + ar9271SetBoardGain(ah, pModal, eep, txRxAttenLocal); + + /* Initialize Ant Diversity settings if supported */ + (void) ar9271SetAntennaSwitch(ah, AH5212(ah)->ah_antControl); + + /* Configure TX power calibration */ + if (pModal->version >= 2) { + ob[0] = pModal->ob_0; + ob[1] = pModal->ob_1; + ob[2] = pModal->ob_2; + ob[3] = pModal->ob_3; + ob[4] = pModal->ob_4; + + db1[0] = pModal->db1_0; + db1[1] = pModal->db1_1; + db1[2] = pModal->db1_2; + db1[3] = pModal->db1_3; + db1[4] = pModal->db1_4; + + db2[0] = pModal->db2_0; + db2[1] = pModal->db2_1; + db2[2] = pModal->db2_2; + db2[3] = pModal->db2_3; + db2[4] = pModal->db2_4; + } else if (pModal->version == 1) { + ob[0] = pModal->ob_0; + ob[1] = ob[2] = ob[3] = ob[4] = pModal->ob_1; + db1[0] = pModal->db1_0; + db1[1] = db1[2] = db1[3] = db1[4] = pModal->db1_1; + db2[0] = pModal->db2_0; + db2[1] = db2[2] = db2[3] = db2[4] = pModal->db2_1; + } else { + int i; + + for (i = 0; i < 5; i++) { + ob[i] = pModal->ob_0; + db1[i] = pModal->db1_0; + db2[i] = pModal->db1_0; + } + } + + OS_A_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_OB_CCK, ob[0]); + OS_A_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_OB_PSK, ob[1]); + OS_A_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_OB_QAM, ob[2]); + OS_A_REG_RMW_FIELD(ah, AR9285_AN_RF2G3, AR9271_AN_RF2G3_DB1, db1[0]); + OS_A_REG_RMW_FIELD(ah, AR9285_AN_RF2G4, AR9271_AN_RF2G4_DB2, db2[0]); + + OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, + pModal->switchSettling); + OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, + pModal->adcDesiredSize); + + OS_REG_WRITE(ah, AR_PHY_RF_CTL4, + SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) | + SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) | + SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) | + SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); + + OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, + pModal->txEndToRxOn); + + OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, + pModal->thresh62); + OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, + pModal->thresh62); + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_2) { + OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_DATA_START, + pModal->txFrameToDataStart); + OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_PA_ON, + pModal->txFrameToPaOn); + } + + if ((eep->baseEepHeader.version & AR5416_EEP_VER_MINOR_MASK) >= + AR5416_EEP_MINOR_VER_3) { + if (IEEE80211_IS_CHAN_HT40(chan)) + OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, + AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); + } + + /* + * Program the CCK TX gain factor appropriately if needed. + * The AR9285/AR9271 has a non-constant PA tx gain behaviour + * for CCK versus OFDM rates; other chips deal with this + * differently. + * + * The mask/shift/multiply hackery is done so place the same + * value (bb_desired_scale) into multiple 5-bit fields. + * For example, AR_PHY_TX_PWRCTRL9 has bb_desired_scale written + * to three fields: (0..4), (5..9) and (10..14). + */ + if (AR_SREV_9271(ah) || AR_SREV_KITE(ah)) { + uint8_t bb_desired_scale = (pModal->bb_scale_smrt_antenna & EEP_4K_BB_DESIRED_SCALE_MASK); + if ((eep->baseEepHeader.txGainType == 0) && (bb_desired_scale != 0)) { + ath_hal_printf(ah, "[ath]: adjusting cck tx gain factor\n"); + uint32_t pwrctrl, mask, clr; + + mask = (1<<0) | (1<<5) | (1<<10) | (1<<15) | (1<<20) | (1<<25); + pwrctrl = mask * bb_desired_scale; + clr = mask * 0x1f; + OS_REG_RMW(ah, AR_PHY_TX_PWRCTRL8, pwrctrl, clr); + OS_REG_RMW(ah, AR_PHY_TX_PWRCTRL10, pwrctrl, clr); + OS_REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL12, pwrctrl, clr); + + mask = (1<<0) | (1<<5) | (1<<15); + pwrctrl = mask * bb_desired_scale; + clr = mask * 0x1f; + OS_REG_RMW(ah, AR_PHY_TX_PWRCTRL9, pwrctrl, clr); + + mask = (1<<0) | (1<<5); + pwrctrl = mask * bb_desired_scale; + clr = mask * 0x1f; + OS_REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL11, pwrctrl, clr); + OS_REG_RMW(ah, AR_PHY_CH0_TX_PWRCTRL13, pwrctrl, clr); + } + } + + return AH_TRUE; +} + +/* + * Helper functions common for AP/CB/XB + */ + +static HAL_BOOL +ar9271SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, + int16_t *ratesArray, uint16_t cfgCtl, + uint16_t AntennaReduction, + uint16_t twiceMaxRegulatoryPower, + uint16_t powerLimit) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) +/* Local defines to distinguish between extension and control CTL's */ +#define EXT_ADDITIVE (0x8000) +#define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) +#define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) + + uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + int i; + int16_t twiceLargestAntenna; + CAL_CTL_DATA_4K *rep; + CAL_TARGET_POWER_LEG targetPowerOfdm, targetPowerCck = {0, {0, 0, 0, 0}}; + CAL_TARGET_POWER_LEG targetPowerOfdmExt = {0, {0, 0, 0, 0}}, targetPowerCckExt = {0, {0, 0, 0, 0}}; + CAL_TARGET_POWER_HT targetPowerHt20, targetPowerHt40 = {0, {0, 0, 0, 0}}; + int16_t scaledPower, minCtlPower; + +#define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ + static const uint16_t ctlModesFor11g[] = { + CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 + }; + const uint16_t *pCtlMode; + uint16_t numCtlModes, ctlMode, freq; + CHAN_CENTERS centers; + + ar5416GetChannelCenters(ah, chan, ¢ers); + + /* Compute TxPower reduction due to Antenna Gain */ + + twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0]; + twiceLargestAntenna = (int16_t)AH_MIN((AntennaReduction) - twiceLargestAntenna, 0); + + /* XXX setup for 5212 use (really used?) */ + ath_hal_eepromSet(ah, AR_EEP_ANTGAINMAX_2, twiceLargestAntenna); + + /* + * scaledPower is the minimum of the user input power level and + * the regulatory allowed power level + */ + scaledPower = AH_MIN(powerLimit, twiceMaxRegulatoryPower + twiceLargestAntenna); + + /* Get target powers from EEPROM - our baseline for TX Power */ + /* Setup for CTL modes */ + numCtlModes = N(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; /* CTL_11B, CTL_11G, CTL_2GHT20 */ + pCtlMode = ctlModesFor11g; + + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, + AR5416_4K_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, AH_FALSE); + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, + AR5416_4K_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); + ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT20, + AR5416_4K_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); + + if (IEEE80211_IS_CHAN_HT40(chan)) { + numCtlModes = N(ctlModesFor11g); /* All 2G CTL's */ + + ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT40, + AR5416_4K_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); + /* Get target powers for extension channels */ + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, + AR5416_4K_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, AH_TRUE); + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, + AR5416_4K_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); + } + + /* + * For MIMO, need to apply regulatory caps individually across dynamically + * running modes: CCK, OFDM, HT20, HT40 + * + * The outer loop walks through each possible applicable runtime mode. + * The inner loop walks through each ctlIndex entry in EEPROM. + * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. + * + */ + for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { + HAL_BOOL isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || + (pCtlMode[ctlMode] == CTL_2GHT40); + if (isHt40CtlMode) { + freq = centers.ctl_center; + } else if (pCtlMode[ctlMode] & EXT_ADDITIVE) { + freq = centers.ext_center; + } else { + freq = centers.ctl_center; + } + + /* walk through each CTL index stored in EEPROM */ + for (i = 0; (i < AR5416_4K_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { + uint16_t twiceMinEdgePower; + + /* compare test group from regulatory channel list with test mode from pCtlMode list */ + if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || + (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == + ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { + rep = &(pEepData->ctlData[i]); + twiceMinEdgePower = ar5416GetMaxEdgePower(freq, + rep->ctlEdges[ + owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask) - 1], AH_TRUE); + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { + /* Find the minimum of all CTL edge powers that apply to this channel */ + twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); + } else { + /* specific */ + twiceMaxEdgePower = twiceMinEdgePower; + break; + } + } + } + minCtlPower = (uint8_t)AH_MIN(twiceMaxEdgePower, scaledPower); + /* Apply ctl mode to correct target power set */ + switch(pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; i < N(targetPowerCck.tPow2x); i++) { + targetPowerCck.tPow2x[i] = (uint8_t)AH_MIN(targetPowerCck.tPow2x[i], minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; i < N(targetPowerOfdm.tPow2x); i++) { + targetPowerOfdm.tPow2x[i] = (uint8_t)AH_MIN(targetPowerOfdm.tPow2x[i], minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { + targetPowerHt20.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt20.tPow2x[i], minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerCckExt.tPow2x[0], minCtlPower); + break; + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerOfdmExt.tPow2x[0], minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { + targetPowerHt40.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt40.tPow2x[i], minCtlPower); + } + break; + default: + return AH_FALSE; + break; + } + } /* end ctl mode checking */ + + /* Set rates Array from collected data */ + ar5416SetRatesArrayFromTargetPower(ah, chan, ratesArray, + &targetPowerCck, + &targetPowerCckExt, + &targetPowerOfdm, + &targetPowerOfdmExt, + &targetPowerHt20, + &targetPowerHt40); + + return AH_TRUE; +#undef EXT_ADDITIVE +#undef CTL_11G_EXT +#undef CTL_11B_EXT +#undef SUB_NUM_CTL_MODES_AT_2G_40 +#undef N +} + +static HAL_BOOL +ar9271SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) +{ + CAL_DATA_PER_FREQ_4K *pRawDataset; + uint8_t *pCalBChans = AH_NULL; + uint16_t pdGainOverlap_t2; + static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; + uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; + uint16_t numPiers, i; + int16_t tMinCalPower; + uint16_t numXpdGain, xpdMask; + uint16_t xpdGainValues[4]; /* v4k eeprom has 2; the other two stay 0 */ + uint32_t regChainOffset; + + OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); + + xpdMask = pEepData->modalHeader.xpdGain; + + if (IS_EEP_MINOR_V2(ah)) { + pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; + } else { + pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR5416_4K_NUM_2G_CAL_PIERS; + numXpdGain = 0; + + /* Calculate the value of xpdgains from the xpdGain Mask */ + for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR5416_4K_NUM_PD_GAINS) { + HALASSERT(0); + break; + } + xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); + numXpdGain++; + } + } + + /* Write the detector gain biases and their number */ + ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + regChainOffset = ar5416GetRegChainOffset(ah, i); + if (pEepData->baseEepHeader.txMask & (1 << i)) { + pRawDataset = pEepData->calPierData2G[i]; + + ar9271GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, + pCalBChans, numPiers, + pdGainOverlap_t2, + &tMinCalPower, gainBoundaries, + pdadcValues, numXpdGain); + + if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { + /* + * Note the pdadc table may not start at 0 dBm power, could be + * negative or greater than 0. Need to offset the power + * values by the amount of minPower for griffin + */ + ar5416SetGainBoundariesClosedLoop(ah, i, pdGainOverlap_t2, gainBoundaries); + } + + /* Write the power values into the baseband power table */ + ar5416WritePdadcValues(ah, i, pdadcValues); + } + } + *pTxPowerIndexOffset = 0; + + return AH_TRUE; +} + +static void +ar9271GetGainBoundariesAndPdadcs(struct ath_hal *ah, + const struct ieee80211_channel *chan, + CAL_DATA_PER_FREQ_4K *pRawDataSet, + uint8_t * bChans, uint16_t availPiers, + uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, + uint8_t * pPDADCValues, uint16_t numXpdGains) +{ + + int i, j, k; + int16_t ss; /* potentially -ve index for taking care of pdGainOverlap */ + uint16_t idxL, idxR, numPiers; /* Pier indexes */ + + /* filled out Vpd table for all pdGains (chanL) */ + static uint8_t vpdTableL[AR5416_4K_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + /* filled out Vpd table for all pdGains (chanR) */ + static uint8_t vpdTableR[AR5416_4K_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + /* filled out Vpd table for all pdGains (interpolated) */ + static uint8_t vpdTableI[AR5416_4K_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + uint8_t *pVpdL, *pVpdR, *pPwrL, *pPwrR; + uint8_t minPwrT4[AR5416_4K_NUM_PD_GAINS]; + uint8_t maxPwrT4[AR5416_4K_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + uint16_t sizeCurrVpdTable, maxIndex, tgtIndex; + HAL_BOOL match; + int16_t minDelta = 0; + CHAN_CENTERS centers; + + ar5416GetChannelCenters(ah, chan, ¢ers); + + /* Trim numPiers for the number of populated channel Piers */ + for (numPiers = 0; numPiers < availPiers; numPiers++) { + if (bChans[numPiers] == AR5416_BCHAN_UNUSED) { + break; + } + } + + /* Find pier indexes around the current channel */ + match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, + IEEE80211_IS_CHAN_2GHZ(chan)), bChans, numPiers, &idxL, &idxR); + + if (match) { + /* Directly fill both vpd tables from the matching index */ + for (i = 0; i < numXpdGains; i++) { + minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; + maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; + ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], + pRawDataSet[idxL].pwrPdg[i], + pRawDataSet[idxL].vpdPdg[i], + AR5416_PD_GAIN_ICEPTS, vpdTableI[i]); + } + } else { + for (i = 0; i < numXpdGains; i++) { + pVpdL = pRawDataSet[idxL].vpdPdg[i]; + pPwrL = pRawDataSet[idxL].pwrPdg[i]; + pVpdR = pRawDataSet[idxR].vpdPdg[i]; + pPwrR = pRawDataSet[idxR].pwrPdg[i]; + + /* Start Vpd interpolation from the max of the minimum powers */ + minPwrT4[i] = AH_MAX(pPwrL[0], pPwrR[0]); + + /* End Vpd interpolation from the min of the max powers */ + maxPwrT4[i] = AH_MIN(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); + HALASSERT(maxPwrT4[i] > minPwrT4[i]); + + /* Fill pier Vpds */ + ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrL, pVpdL, + AR5416_PD_GAIN_ICEPTS, vpdTableL[i]); + ath_ee_FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrR, pVpdR, + AR5416_PD_GAIN_ICEPTS, vpdTableR[i]); + + /* Interpolate the final vpd */ + for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { + vpdTableI[i][j] = (uint8_t)(ath_ee_interpolate((uint16_t)FREQ2FBIN(centers.synth_center, + IEEE80211_IS_CHAN_2GHZ(chan)), + bChans[idxL], bChans[idxR], vpdTableL[i][j], vpdTableR[i][j])); + } + } + } + *pMinCalPower = (int16_t)(minPwrT4[0] / 2); + + k = 0; /* index for the final table */ + for (i = 0; i < numXpdGains; i++) { + if (i == (numXpdGains - 1)) { + pPdGainBoundaries[i] = (uint16_t)(maxPwrT4[i] / 2); + } else { + pPdGainBoundaries[i] = (uint16_t)((maxPwrT4[i] + minPwrT4[i+1]) / 4); + } + + pPdGainBoundaries[i] = (uint16_t)AH_MIN(AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]); + + /* NB: only applies to owl 1.0 */ + if ((i == 0) && !AR_SREV_5416_V20_OR_LATER(ah) ) { + /* + * fix the gain delta, but get a delta that can be applied to min to + * keep the upper power values accurate, don't think max needs to + * be adjusted because should not be at that area of the table? + */ + minDelta = pPdGainBoundaries[0] - 23; + pPdGainBoundaries[0] = 23; + } + else { + minDelta = 0; + } + + /* Find starting index for this pdGain */ + if (i == 0) { + if (AR_SREV_MERLIN_20_OR_LATER(ah)) + ss = (int16_t)(0 - (minPwrT4[i] / 2)); + else + ss = 0; /* for the first pdGain, start from index 0 */ + } else { + /* need overlap entries extrapolated below. */ + ss = (int16_t)((pPdGainBoundaries[i-1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); + } + vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); + vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); + /* + *-ve ss indicates need to extrapolate data below for this pdGain + */ + while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); + pPDADCValues[k++] = (uint8_t)((tmpVal < 0) ? 0 : tmpVal); + ss++; + } + + sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 +1); + tgtIndex = (uint8_t)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); + maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; + + while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + pPDADCValues[k++] = vpdTableI[i][ss++]; + } + + vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - vpdTableI[i][sizeCurrVpdTable - 2]); + vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); + /* + * for last gain, pdGainBoundary == Pmax_t2, so will + * have to extrapolate + */ + if (tgtIndex >= maxIndex) { /* need to extrapolate above */ + while ((ss <= tgtIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + + (ss - maxIndex +1) * vpdStep)); + pPDADCValues[k++] = (uint8_t)((tmpVal > 255) ? 255 : tmpVal); + ss++; + } + } /* extrapolated above */ + } /* for all pdGainUsed */ + + /* Fill out pdGainBoundaries - only up to 2 allowed here, but hardware allows up to 4 */ + while (i < AR5416_PD_GAINS_IN_MASK) { + pPdGainBoundaries[i] = AR5416_4K_EEP_PD_GAIN_BOUNDARY_DEFAULT; + i++; + } + + while (k < AR5416_NUM_PDADC_VALUES) { + pPDADCValues[k] = pPDADCValues[k-1]; + k++; + } + return; +} diff --git a/sys/dev/ath/ath_hal/ar9002/ar9271an.h b/sys/dev/ath/ath_hal/ar9002/ar9271an.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar9002/ar9271an.h @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: ISC + * + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ + +#ifndef __AR9271_AN_H__ +#define __AR9271_AN_H__ + +#include "ar9002/ar9285an.h" + +/* AR9271 Analog register definitions */ + +/* Bits for AR9271_AN_RF2G3. */ +#define AR9271_AN_RF2G3_CCOMP 0x00000fff +#define AR9271_AN_RF2G3_CCOMP_S 0 +#define AR9271_AN_RF2G3_OB_QAM 0x00007000 +#define AR9271_AN_RF2G3_OB_QAM_S 12 +#define AR9271_AN_RF2G3_OB_PSK 0x00038000 +#define AR9271_AN_RF2G3_OB_PSK_S 15 +#define AR9271_AN_RF2G3_OB_CCK 0x001c0000 +#define AR9271_AN_RF2G3_OB_CCK_S 18 +#define AR9271_AN_RF2G3_DB1 0x00e00000 +#define AR9271_AN_RF2G3_DB1_S 21 + +/* Bits for AR9271_AN_RF2G4. */ +#define AR9271_AN_RF2G4_DB2 0xe0000000 +#define AR9271_AN_RF2G4_DB2_S 29 + +#endif /* __AR9271_AN_H__ */ diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -596,7 +596,7 @@ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ HAL_MODE_11NA_HT40PLUS | HAL_MODE_11NA_HT40MINUS) int -ath_attach(u_int16_t devid, struct ath_softc *sc) +ath_attach(u_int16_t venid, u_int16_t devid, struct ath_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ath_hal *ah = NULL; @@ -620,8 +620,8 @@ bzero(&ah_config, sizeof(ah_config)); ath_setup_hal_config(sc, &ah_config); - ah = ath_hal_attach(devid, sc, sc->sc_st, sc->sc_sh, - sc->sc_eepromdata, &ah_config, &status); + ah = ath_hal_attach(venid, devid, sc, sc->sc_st, sc->sc_sh, + sc->sc_eepromdata, &ah_config, &status); if (ah == NULL) { device_printf(sc->sc_dev, "unable to attach hardware; HAL status %u\n", status); @@ -1383,9 +1383,12 @@ /* * Put it to sleep for now. */ - ATH_LOCK(sc); + //TODO: ATH_LOCK disabled only for development purpose, need to investigate below issue: + // Sleeping on "athwmi" with the following non-sleepable locks held: + // exclusive sleep mutex if_ath_usb0 (if_ath_usb0) r = 0 (0xfffffe0062f011a0) locked @ /usr/src/sys/dev/ath/if_ath.c:1386 + //ATH_LOCK(sc); ath_power_setpower(sc, HAL_PM_FULL_SLEEP, 1); - ATH_UNLOCK(sc); + //ATH_UNLOCK(sc); return 0; bad2: @@ -2742,7 +2745,15 @@ __func__, sc->sc_imask); sc->sc_running = 1; - callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); + + // TODO Commented out due to RNDBOLT02-40 issue. + // It's a problem related to adding USB access to ath driver needed for AR9271. + // The watchdog calls ath_hal_gethangstate() -> ... -> ar5416GetDiagState() -> ath_usb_read() -> ath_usb_wmi_xcmd(). + // It causes sleeping in a function used in a callout which is forbidden in FreeBSD. + // We decided to disable watchdog temporarly. + // + //callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); + ath_hal_intrset(ah, sc->sc_imask); ath_power_restore_power_state(sc); diff --git a/sys/dev/ath/if_ath_drv.c b/sys/dev/ath/if_ath_drv.c --- a/sys/dev/ath/if_ath_drv.c +++ b/sys/dev/ath/if_ath_drv.c @@ -84,3 +84,4 @@ MODULE_DEPEND(if_ath, wlan, 1, 1, 1); MODULE_DEPEND(if_ath, ath_main, 1, 1, 1); MODULE_DEPEND(if_ath, if_ath_pci, 1, 1, 1); +MODULE_DEPEND(if_ath, if_ath_usb, 1, 1, 1); diff --git a/sys/dev/ath/if_ath_pci.c b/sys/dev/ath/if_ath_pci.c --- a/sys/dev/ath/if_ath_pci.c +++ b/sys/dev/ath/if_ath_pci.c @@ -63,6 +63,8 @@ #include #include +#include + /* For EEPROM firmware */ #ifdef ATH_EEPROM_FIRMWARE #include @@ -279,7 +281,7 @@ } #endif /* ATH_EEPROM_FIRMWARE */ - error = ath_attach(pci_get_device(dev), sc); + error = ath_attach(ATHEROS_VENDOR_ID, pci_get_device(dev), sc); if (error == 0) /* success */ return 0; diff --git a/sys/dev/ath/if_ath_usb.h b/sys/dev/ath/if_ath_usb.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb.h @@ -0,0 +1,11 @@ +#ifndef IF_ATH_USB_H +#define IF_ATH_USB_H + +struct ath_usb_softc; +struct ath_softc; + +void +ath_usb_write(struct ath_softc *sc, uint32_t addr, uint32_t val); +uint32_t +ath_usb_read(struct ath_softc *sc, uint32_t addr); +#endif /* IF_ATH_USB_H */ diff --git a/sys/dev/ath/if_ath_usb.c b/sys/dev/ath/if_ath_usb.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb.c @@ -0,0 +1,3549 @@ +/*- + * Copyright (c) 2011 Damien Bergamini + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ + +/* + * USB front-end for Atheros AR9271 and AR7010 chipsets. + */ + +// #include "bpfilter.h" +#define NBPFILTER 0 // TODO ? +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if NBPFILTER > 0 +#include +#endif +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "if_athvar.h" +#include "if_ath_usb_devlist.h" + +#include +#include +#include +#include + +#include "openbsd_adapt.h" +#include "if_ath_usb_def.h" +#include "if_ath_usb_fw.h" +#include "if_ath_debug.h" +// TODO: DPRINTF from ATHN has different parameters than DPRINTF from ATH +#ifdef DPRINTF + #undef DPRINTF +#endif +#ifdef ATHN_DEBUG + #define DPRINTF(x) do { if (athn_debug > 0) printf x; } while (0) + #define DPRINTFN(n, x) do { if (athn_debug >= (n)) printf x; } while (0) + extern int athn_debug; +#else + #define DPRINTF(x) + #define DPRINTFN(n, x) +#endif + + +unsigned int ifq_oactive = 0; + +#define ath_usb_lookup(uaa) \ + (usbd_lookup_id_by_uaa(ath_usb_devs, sizeof(ath_usb_devs), uaa)) + +// TODO: Move it or delete if not needed +/* Tx queue software indexes. */ +#define ATH_QID_AC_BE 0 +#define ATH_QID_PSPOLL 1 +#define ATH_QID_AC_BK 2 +#define ATH_QID_AC_VI 3 +#define ATH_QID_AC_VO 4 +#define ATH_QID_UAPSD 5 +#define ATH_QID_CAB 6 +#define ATH_QID_BEACON 7 +#define ATH_QID_COUNT 8 + +// TODO: Move it or delete if not needed +/* Bits for AR_RX_FILTER. */ +#define AR_RX_FILTER_UCAST 0x00000001 +#define AR_RX_FILTER_MCAST 0x00000002 +#define AR_RX_FILTER_BCAST 0x00000004 +#define AR_RX_FILTER_CONTROL 0x00000008 +#define AR_RX_FILTER_BEACON 0x00000010 +#define AR_RX_FILTER_PROM 0x00000020 +#define AR_RX_FILTER_PROBEREQ 0x00000080 +#define AR_RX_FILTER_MYBEACON 0x00000200 +#define AR_RX_FILTER_COMPR_BAR 0x00000400 +#define AR_RX_FILTER_PSPOLL 0x00004000 + +static device_probe_t ath_usb_match; +static device_attach_t ath_usb_attach; +static device_detach_t ath_usb_detach; + +// Temp +static void +ar9271_load_ani(struct ath_softc *sc) +{ +#if Nath_USB > 0 + /* Write ANI registers. */ + AR_WRITE(sc, AR_PHY_DESIRED_SZ, 0x6d4000e2); + AR_WRITE(sc, AR_PHY_AGC_CTL1, 0x3139605e); + AR_WRITE(sc, AR_PHY_FIND_SIG, 0x7ec84d2e); + AR_WRITE(sc, AR_PHY_SFCORR_LOW, 0x06903881); + AR_WRITE(sc, AR_PHY_SFCORR, 0x5ac640d0); + AR_WRITE(sc, AR_PHY_CCK_DETECT, 0x803e68c8); + AR_WRITE(sc, AR_PHY_TIMING5, 0xd00a8007); + AR_WRITE(sc, AR_PHY_SFCORR_EXT, 0x05eea6d4); + AR_WRITE_BARRIER(sc); +#endif /* Nath_USB */ +} + +void ath_usb_attachhook(device_t self); +int ath_usb_open_pipes(struct ath_usb_softc *); +void ath_usb_close_pipes(struct ath_usb_softc *); +static int ath_alloc_list(struct ath_usb_softc *sc, struct ath_usb_data data[], + int ndata, int maxsz); +static int ath_free_list(struct ath_usb_softc *sc, struct ath_usb_data data[], + int ndata); +int ath_usb_alloc_rx_list(struct ath_usb_softc *); +void ath_usb_free_rx_list(struct ath_usb_softc *); +int ath_usb_alloc_tx_list(struct ath_usb_softc *); +void ath_usb_free_tx_list(struct ath_usb_softc *); +int ath_usb_alloc_tx_cmd(struct ath_usb_softc *); +void ath_usb_free_tx_cmd(struct ath_usb_softc *); +void ath_usb_task(void *, int); +void ath_usb_do_async(struct ath_usb_softc *, + void (*)(struct ath_usb_softc *, void *), void *, int); +void ath_usb_wait_async(struct ath_usb_softc *); +int ath_usb_htc_msg(struct ath_usb_softc *, uint16_t, void *, + int); +int ath_usb_htc_setup(struct ath_usb_softc *); +int ath_usb_htc_connect_svc(struct ath_usb_softc *, uint16_t, + uint8_t, uint8_t, uint8_t *); +int ath_usb_wmi_xcmd(struct ath_usb_softc *, uint16_t, void *, + int, void *); +int ath_usb_read_rom(struct ath_softc *); +uint32_t ath_usb_read(struct ath_softc *, uint32_t); +void ath_usb_write(struct ath_softc *, uint32_t, uint32_t); +void ath_usb_write_barrier(struct ath_softc *); +int ath_usb_media_change(struct ifnet *); +void ath_usb_next_scan(void *, int); +int ath_usb_newstate(struct ieee80211vap *, enum ieee80211_state, + int); +//void ath_usb_newstate_cb(struct ath_usb_softc *, void *); +void ath_usb_newassoc(struct ieee80211com *, + struct ieee80211_node *, int); +void ath_usb_newassoc_cb(struct ath_usb_softc *, void *); +struct ieee80211_node *ath_usb_node_alloc(struct ieee80211com *); +void ath_usb_count_active_sta(void *, struct ieee80211_node *); +void ath_usb_newauth_cb(struct ath_usb_softc *, void *); +int ath_usb_newauth(struct ieee80211com *, + struct ieee80211_node *, int, uint16_t); +void ath_usb_node_free(struct ieee80211com *, + struct ieee80211_node *); +void ath_usb_node_free_cb(struct ath_usb_softc *, void *); +int ath_usb_ampdu_tx_start(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +void ath_usb_ampdu_tx_start_cb(struct ath_usb_softc *, void *); +void ath_usb_ampdu_tx_stop(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +void ath_usb_ampdu_tx_stop_cb(struct ath_usb_softc *, void *); +void ath_usb_clean_nodes(void *, struct ieee80211_node *); +int ath_usb_create_node(struct ath_usb_softc *, + struct ieee80211_node *); +int ath_usb_node_set_rates(struct ath_usb_softc *, + struct ieee80211_node *); +int ath_usb_remove_node(struct ath_usb_softc *, + struct ieee80211_node *); +void ath_usb_rx_enable(struct ath_softc *); +int ath_set_chan(struct ath_softc *, struct ieee80211_channel *, + struct ieee80211_channel *); +int ath_usb_switch_chan(struct ath_softc *, + struct ieee80211_channel *, struct ieee80211_channel *); +void ath_usb_updateedca(struct ieee80211com *); +void ath_usb_updateedca_cb(struct ath_usb_softc *, void *); +void ath_usb_updateslot(struct ieee80211com *); +void ath_usb_updateslot_cb(struct ath_usb_softc *, void *); +int ath_usb_set_key(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_key *); +void ath_usb_set_key_cb(struct ath_usb_softc *, void *); +void ath_usb_delete_key(struct ieee80211com *, + struct ieee80211_node *, struct ieee80211_key *); +void ath_usb_delete_key_cb(struct ath_usb_softc *, void *); +void ath_usb_bcneof(struct usb_xfer *xfer, void *priv, + usb_error_t status); +void ath_usb_swba(struct ath_usb_softc *); +void ath_usb_tx_status(void *, struct ieee80211_node *); +void ath_usb_rx_wmi_ctrl(struct ath_usb_softc *, uint8_t *, int); +void ath_usb_intr(struct usb_xfer *, void *, + usb_error_t); +void ath_usb_rx_radiotap(struct ath_softc *, struct mbuf *, + struct ar_rx_status *); +void ath_usb_rx_frame(struct ath_usb_softc *, struct mbuf */*, + struct mbuf_list **/); //MichalP: Uses mbuf_list which doesn't exist in FreeBSD +void ath_usb_rxeof(struct usb_xfer *, struct ath_usb_data*); +void ath_usb_txeof(struct usb_xfer *, struct ath_usb_data *); +int ath_usb_tx(struct ath_softc *, struct mbuf *, + struct ieee80211_node *); +void ath_usb_start(struct ifnet *); +void ath_usb_watchdog(struct ifnet *); +int ath_usb_ioctl(struct ifnet *, u_long, caddr_t); +int ath_usb_init(struct ifnet *); +void ath_usb_stop(struct ifnet *); +void ar9271_load_ani(struct ath_softc *); +int ar5008_ccmp_decap(struct ath_softc *, struct mbuf *, + struct ieee80211_node *); +int ar5008_ccmp_encap(struct mbuf *, u_int, struct ieee80211_key *); + +/* Shortcut. */ +#define ath_usb_wmi_cmd(sc, cmd_id) \ + ath_usb_wmi_xcmd(sc, cmd_id, NULL, 0, NULL) + +/* Extern functions. */ +#if ATHN_API +void ath_led_init(struct ath_softc *); +void ath_set_led(struct ath_softc *, int); +void ath_btcoex_init(struct ath_softc *); +void ath_set_rxfilter(struct ath_softc *, uint32_t); +int ath_reset(struct ath_softc *, int); + +void ath_init_pll(struct ath_softc *, + const struct ieee80211_channel *); +int ath_set_power_awake(struct ath_softc *); +void ath_set_power_sleep(struct ath_softc *); +void ath_reset_key(struct ath_softc *, int); +int ath_set_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void ath_delete_key(struct ieee80211com *, struct ieee80211_node *, + struct ieee80211_key *); +void ath_rx_start(struct ath_softc *); +void ath_set_sta_timers(struct ath_softc *); +void ath_set_hostap_timers(struct ath_softc *); +void ath_set_opmode(struct ath_softc *); +void ath_set_bss(struct ath_softc *, struct ieee80211_node *); +int ath_hw_reset(struct ath_softc *, struct ieee80211_channel *, + struct ieee80211_channel *, int); +void ath_updateedca(struct ieee80211com *); +void ath_updateslot(struct ieee80211com *); +#endif + +static void ath_if_intr_rx_callback(struct usb_xfer *xfer, usb_error_t error); +static void ath_if_intr_tx_callback(struct usb_xfer *xfer, usb_error_t error); +static void ath_if_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error); +static void ath_if_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error); + +static struct usb_config ath_if_config[ATH_N_XFER] = { + [ath_BULK_TX] = { // MichalP: standard I/O + .type = UE_BULK, + .endpoint = AR_PIPE_TX_DATA, + .direction = UE_DIR_OUT, + .bufsize = 512, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = ath_if_bulk_tx_callback, + .timeout = ath_USB_TX_TIMEOUT + }, // MichalP: standard I/O + [ath_BULK_RX] = { + .type = UE_BULK, + .endpoint = AR_PIPE_RX_DATA, + .direction = UE_DIR_IN, // .direction = UE_DIR_IN, + .bufsize = 512, + .flags = {.ext_buffer=1,.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = ath_if_bulk_rx_callback, + }, // MichalP: what the device sends to us (CMD replies) + [ath_BULK_IRQ] = { + .type = UE_INTERRUPT, + .endpoint = AR_PIPE_RX_INTR, + .direction = UE_DIR_IN, + .bufsize = 64, + .flags = {.pipe_bof = 1,.short_xfer_ok = 1,}, + .callback = ath_if_intr_rx_callback, + .interval = 1 + }, // MichalP: what we are sending to the device (CMDs) + [ath_BULK_CMD] = { + .type = UE_INTERRUPT, + .endpoint = AR_PIPE_TX_INTR, + .direction = UE_DIR_OUT, + .bufsize = 64, + .flags = {.pipe_bof = 1,.force_short_xfer = 1,}, + .callback = ath_if_intr_tx_callback, + .timeout = 5000, /* ms */ + .interval = 1 + } +}; + +#define INFSLP UINT64_MAX +#define NSEC_PER_SEC 1000000000 + +// TODO MichalP: we use msleep with mtx so this could be obsolete +#if OpenBSD_ONLY +int +tsleep_nsec(const void *ident, int priority, const char *wmesg, + uint64_t nsecs) +{ + uint64_t to_ticks; + int tick_nsec = (NSEC_PER_SEC + hz / 2) / hz; + + if (nsecs == INFSLP) + return tsleep(ident, priority, wmesg, 0); +#ifdef DIAGNOSTIC + if (nsecs == 0) { + log(LOG_WARNING, + "%s: %s[%d]: %s: trying to sleep zero nanoseconds\n", + __func__, curproc->p_p->ps_comm, curproc->p_p->ps_pid, + wmesg); + } +#endif + /* + * We want to sleep at least nsecs nanoseconds worth of ticks. + * + * - Clamp nsecs to prevent arithmetic overflow. + * + * - Round nsecs up to account for any nanoseconds that do not + * divide evenly into tick_nsec, otherwise we'll lose them to + * integer division in the next step. We add (tick_nsec - 1) + * to keep from introducing a spurious tick if there are no + * such nanoseconds, i.e. nsecs % tick_nsec == 0. + * + * - Divide the rounded value to a count of ticks. We divide + * by (tick_nsec + 1) to discard the extra tick introduced if, + * before rounding, nsecs % tick_nsec == 1. + * + * - Finally, add a tick to the result. We need to wait out + * the current tick before we can begin counting our interval, + * as we do not know how much time has elapsed since the + * current tick began. + */ + nsecs = MIN(nsecs, UINT64_MAX - tick_nsec); + to_ticks = (nsecs + tick_nsec - 1) / (tick_nsec + 1) + 1; + if (to_ticks > INT_MAX) + to_ticks = INT_MAX; + return tsleep(ident, priority, wmesg, (int)to_ticks); +} +#endif + +static device_method_t ath_usb_methods[] = { + DEVMETHOD(device_probe, ath_usb_match), + DEVMETHOD(device_attach, ath_usb_attach), + DEVMETHOD(device_detach, ath_usb_detach), + + DEVMETHOD_END +}; + +static driver_t ath_usb_driver = { + .name = "if_ath_usb", + .methods = ath_usb_methods, + .size = sizeof(struct ath_softc) +}; + +/* Temporary to nofiy that the module was loaded TODO MichalP: this can be removed at somepoint*/ +static int +ath_usb_load(struct module *m, int what, void *arg) +{ + int error = 0; + + switch (what) { + case MOD_LOAD: + uprintf("[ath_usb] loaded\n"); + break; + case MOD_UNLOAD: + uprintf("[ath_usb] unloaded\n"); + break; + default: + error = EOPNOTSUPP; + break; + } + return(error); +} + +DRIVER_MODULE(if_ath_usb, uhub, ath_usb_driver, ath_usb_load, NULL); +MODULE_DEPEND(if_ath_usb, usb, 1, 1, 1); +MODULE_DEPEND(if_ath_usb, wlan, 1, 1, 1); +MODULE_DEPEND(if_ath_usb, ath_main, 1, 1, 1); +MODULE_DEPEND(if_ath_usb, ath_hal, 1, 1, 1); +MODULE_VERSION(if_ath_usb, 1); +USB_PNP_HOST_INFO(ath_usb_devs); + +static int +ath_usb_match(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + int result = 0; + + if (uaa->usb_mode != USB_MODE_HOST) + return (ENXIO); + if (uaa->info.bConfigIndex != 0) + return (ENXIO); + if (uaa->info.bIfaceIndex != 0) + return (ENXIO); + + result = ath_usb_lookup(uaa); + + printf("ath_usb_lookup called with result %d \n", result); + + if (result == 0) { + printf("serial: %s \n", usb_get_serial(uaa->device)); + printf("product: %s-%s \n", usb_get_manufacturer(uaa->device), + usb_get_product(uaa->device)); + printf("ProductID: 0x%x \n", uaa->info.idProduct); + printf("VendorID: 0x%x \n", uaa->info.idVendor); + } + + return result; +} + +static int +ath_usb_attach(device_t self) +{ + struct usb_attach_arg *uaa = device_get_ivars(self); + struct ath_softc *sc = device_get_softc(self); + struct ieee80211com *ic = &sc->sc_ic; + struct ath_usb_softc *usc; + usb_error_t error; + struct usb_endpoint *ep, *ep_end; + + device_set_usb_desc(self); + usc = malloc(sizeof(struct ath_usb_softc), M_TEMP, + M_NOWAIT | M_ZERO); + sc->usc = usc; + usc->sc_sc = sc; + usc->sc_udev = uaa->device; + usc->no_buffer_write = TRUE; + sc->sc_dev = self; + ic->ic_name = device_get_nameunit(self); + + usc->flags = uaa->driver_info; +#ifdef notyet + /* Check if it is a combo WiFi+Bluetooth (WB193) device. */ + if (strncmp(product, "wb193", 5) == 0) + sc->flags |= ath_FLAG_BTCOEX3WIRE; +#endif + +#if ATHN_API + sc->ops.read = ath_usb_read; + sc->ops.write = ath_usb_write; + sc->ops.write_barrier = ath_usb_write_barrier; +#endif + //mtx_init(&sc->sc_usb_mtx, device_get_nameunit(self), MTX_NETWORK_LOCK, MTX_DEF); + + ATH_LOCK_INIT(sc); + ATH_USB_LOCK_INIT(sc); + ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TX_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); + +#if ATHN_API + // MichalP calib_to timeout missing don't know how to put it all together + TIMEOUT_TASK_INIT(taskqueue_thread, &sc->scan_to, 0, ath_usb_next_scan, sc); + TASK_INIT(&sc->sc_task, 0, ath_usb_task, sc); +#endif + error = usbd_transfer_setup(uaa->device, &uaa->info.bIfaceIndex, usc->sc_xfer, ath_if_config, + ATH_N_XFER, usc, &sc->sc_usb_mtx); + if (error) { + device_printf(sc->sc_dev, + "could not allocate USB transfers, err=%s\n", + usbd_errstr(error)); + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_USB_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + return error; + } + + // TODO MichalP just for debug purposes can be removed + ep = usc->sc_udev->endpoints; + ep_end = usc->sc_udev->endpoints + usc->sc_udev->endpoints_max; + for (; ep != ep_end; ep++) { + uint8_t eaddr; + + eaddr = ep->edesc->bEndpointAddress; + device_printf(sc->sc_dev, "%s: endpoint: addr %u, direction %s, endpoint: %p \n", __func__, + UE_GET_ADDR(eaddr), UE_GET_DIR(eaddr) == UE_DIR_OUT ? "output" : "input", ep); + } + + error = ath_usb_open_pipes(usc); + if (error != 0) + goto bad; + + /* + * Setup DMA descriptor area. + */ + if (bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), /* parent */ + 1, 0, /* alignment, bounds */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + 0x3ffff, /* maxsize XXX */ + ATH_MAX_SCATTER, /* nsegments */ + 0x3ffff, /* maxsegsize XXX */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &sc->sc_dmat)) { + device_printf(sc->sc_dev, "cannot allocate DMA tag\n"); + error = ENXIO; + goto bad1; + } + + /* Allocate xfer for firmware commands. */ + error = ath_usb_alloc_tx_cmd(usc); + if (error != 0) + goto bad2; + + ath_usb_attachhook(self); + usc->no_buffer_write = FALSE; + + return 0; +bad2: + bus_dma_tag_destroy(sc->sc_dmat); +bad1: + ath_usb_close_pipes(usc); +bad: + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_USB_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + return error; +} + +static int +ath_usb_detach(device_t self) +{ + struct ath_softc *sc = device_get_softc(self); + struct ath_usb_softc *usc = sc->usc; + + if (usc->sc_ath_attached) + ath_detach(sc); + + /* Wait for all async commands to complete. */ + ath_usb_wait_async(usc); + + usbd_transfer_unsetup(usc->sc_xfer, ATH_N_XFER); + + /* Abort and close Tx/Rx pipes. */ + ath_usb_close_pipes(usc); + + /* Free Tx/Rx buffers. */ + ath_usb_free_tx_cmd(usc); + ath_usb_free_tx_list(usc); + ath_usb_free_rx_list(usc); + + bus_dma_tag_destroy(sc->sc_dmat); +#if ATHN_API + ath_usb_unload_firmware(); +#endif + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_USB_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + free(usc, M_TEMP); + printf("ath_usb_detach called \n"); + return (0); +} + +static int +ath_usb_get_fw_ver(struct ath_softc *sc, struct ar_wmi_fw_version *version) +{ + struct ath_usb_softc *usc = sc->usc; + struct ar_wmi_fw_version cmd_rsp; + int error; + + device_printf(sc->sc_dev, "%s: called \n", __func__); + + ATH_USB_LOCK(usc->sc_sc); + error = ath_usb_wmi_xcmd(usc, AR_WMI_GET_FW_VERSION, NULL, 0, version); + ATH_USB_UNLOCK(usc->sc_sc); + + version->major = bswap16(version->major); + version->minor = bswap16(version->minor); + + if (error != 0) { + device_printf(sc->sc_dev, "%s: error = %d\n", __func__, error); + version->major = 0; + version->minor = 0; + } + + return error; +} + +/* Read fw version from module and compare it with passed image version */ +static int +ath_usb_verify_fw_ver(struct ath_softc *sc, struct ar_wmi_fw_version *img_ver) +{ + struct ath_usb_softc *usc = sc->usc; + struct ar_wmi_fw_version fw_ver; + int error; + + device_printf(sc->sc_dev, "%s: called \n", __func__); + + error = ath_usb_get_fw_ver(sc, &fw_ver); + + if ((img_ver->major != fw_ver.major) || + (img_ver->minor != fw_ver.minor)) { + device_printf(sc->sc_dev, "%s: Image fw version %d.%d differs from module fw version %d.%d\n", + __func__, img_ver->major, img_ver->minor, fw_ver.major, fw_ver.minor); + return -1; + } + return error; +} + +void +ath_usb_attachhook(device_t self) +{ + struct ath_softc *sc = device_get_softc(self); + struct ath_usb_softc *usc = sc->usc; + struct ar_wmi_fw_version img_ver; +#if ATHN_API + struct ath_ops *ops = &sc->ops; +#endif + struct usb_attach_arg *uaa = device_get_ivars(self); + + uint32_t val = 104; +#ifdef notyet + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; +#endif + int s, i, error; + /* Load firmware. */ + error = ath_usb_load_firmware(usc, &img_ver); + if (error != 0) { + printf("Could not load firmware\n"); + return; + } + // TODO MichalP: this can be used as a starting point for echo command or firmware command +// ATH_USB_LOCK(usc->sc_sc); +// device_printf(sc->sc_dev, " %s:val = %d\n", __func__, val); +// NOTE: command below is invalid because ath_usb_read has different arguments but let's keep that note about echo +// val = ath_usb_read(sc, AR_WMI_CMD_ECHO); +// device_printf(sc->sc_dev, "%s: returned val = %d\n", __func__, val); +// val = *(uint32_t*)usc->obuf; +// device_printf(sc->sc_dev, "%s: casted val = %d\n", __func__, val); +// ATH_USB_UNLOCK(usc->sc_sc); +// +// return; + + /* Setup the host transport communication interface. */ + error = ath_usb_htc_setup(usc); + if (error != 0) + return; + +// TODO: MikolajF:I couldn't read a FW version before the firmware upload because +// the module never responded to the WMI request, even if I moved the htc setup +// before loading the FW. I don't know why, but checking it at this point is not +// useful and adds to the initialization time. It's not critical and can be +// investigated or ignored in the future. +#if 0 + error = ath_usb_verify_fw_ver(sc, &img_ver); + if (error != 0) + return; + extern void ath_get_chipid(struct ath_softc *sc); + ath_get_chipid(sc); + + uint32_t vals[] = {0xffffffff, 0xDEADBEEF, 0x12345678, 0xDEADB11F, 0xDEADBEEE }; + extern int ath_reset_power_on(struct ath_softc *); + + ath_reset_power_on(sc); + for(int i=0; i<5; i++) + { + uint32_t val = vals[i]; + uint32_t r = AR_BSSMSKL; + + printf("Writing 0x%x to AR_BSSMSKL\n", val); + ATH_USB_LOCK(sc); + AR_WRITE(sc, r, val); + ATH_USB_UNLOCK(sc); + + ATH_USB_LOCK(sc); + uint32_t reg = AR_READ(sc, r); + ATH_USB_UNLOCK(sc); + printf("AR_BSSMSKL is: %x, expected: %x\n", reg, val); + } + +#endif + + /* We're now ready to attach the bus agnostic driver. */ + // TODO: MichalP needs proper FreeBSD adaptation because this uses code that is + // stubbed and/or commented + + error = ath_attach(uaa->info.idVendor, uaa->info.idProduct, sc); + if (error != 0) { + return; + } + + usc->sc_ath_attached = 1; +#if OpenBSD_IEEE80211_API + /* Override some operations for USB. */ + ifp->if_ioctl = ath_usb_ioctl; + ifp->if_start = ath_usb_start; + // TODO no member named 'if_watchdog' in 'struct ifnet' + // ifp->if_watchdog = ath_usb_watchdog; + ic->ic_node_alloc = ath_usb_node_alloc; + ic->ic_newauth = ath_usb_newauth; + ic->ic_newassoc = ath_usb_newassoc; +#ifndef IEEE80211_STA_ONLY + usc->sc_node_free = ic->ic_node_free; + ic->ic_node_free = ath_usb_node_free; +#endif + ic->ic_updateslot = ath_usb_updateslot; + ic->ic_updateedca = ath_usb_updateedca; + ic->ic_set_key = ath_usb_set_key; + ic->ic_delete_key = ath_usb_delete_key; + ic->ic_vap_create = ath_vap_create; // This function exist in if_ath.c + ic->ic_vap_delete = ath_vap_delete; + ic->ic_ampdu_tx_start = ath_usb_ampdu_tx_start; + ic->ic_ampdu_tx_stop = ath_usb_ampdu_tx_stop; + ic->ic_newstate = ath_usb_newstate; + + ic->ic_media.ifm_change_cb = ath_usb_media_change; + + ops->rx_enable = ath_usb_rx_enable; + + /* Reset HW key cache entries. */ + for (i = 0; i < sc->kc_entries; i++) + ath_reset_key(sc, i); + + ops->enable_antenna_diversity(sc); +#endif + +#ifdef ath_BT_COEXISTENCE + /* Configure bluetooth coexistence for combo chips. */ + if (sc->flags & ath_FLAG_BTCOEX) + ath_btcoex_init(sc); +#endif + /* Configure LED. */ +#if OpenBSD_ONLY + ath_led_init(sc); +#endif + +} + +int +ath_usb_open_pipes(struct ath_usb_softc *usc) +{ + struct ath_softc *sc = usc->sc_sc; + usb_error_t error; +#if OpenBSD_ONLY + /* Init host async commands ring. */ + usc->cmdq.cur = usc->cmdq.next = usc->cmdq.queued = 0; +#endif + /* Allocate Tx/Rx buffers. */ + if((error = ath_usb_alloc_rx_list(usc)) != 0) { + device_printf(sc->sc_dev, "%s: could not allocate Tx xfers\n", + __func__); + goto fail; + } + if ((error = ath_usb_alloc_tx_list(usc)) != 0) { + device_printf(sc->sc_dev, "%s: could not allocate Tx xfers\n", + __func__); + goto fail; + } +#if OpenBSD_ONLY + /* Steal one buffer for beacons. */ + usc->tx_bcn = TAILQ_FIRST(&usc->tx_free_list); + TAILQ_REMOVE(&usc->tx_free_list, usc->tx_bcn, next); +#endif + + ATH_USB_LOCK(sc); + usbd_transfer_start(usc->sc_xfer[ath_BULK_RX]); + usbd_transfer_start(usc->sc_xfer[ath_BULK_IRQ]); + ATH_USB_UNLOCK(sc); + return 0; + + fail: + ath_usb_close_pipes(usc); + return (error); +} + +void +ath_usb_close_pipes(struct ath_usb_softc *usc) +{ + struct ath_softc *sc = usc->sc_sc; + + ATH_USB_LOCK(sc); + ath_usb_free_rx_list(usc); + ath_usb_free_tx_list(usc); + ath_usb_free_tx_cmd(usc); + ATH_USB_UNLOCK(sc); +} + +static int +ath_alloc_list(struct ath_usb_softc *usc, struct ath_usb_data data[], + int ndata, int maxsz) +{ + struct ath_softc *sc = usc->sc_sc; + int i, error; + + for (i = 0; i < ndata; i++) { + struct ath_usb_data *dp = &data[i]; + dp->usc = usc; + // TODO MichalP might be needed not sure +// dp->m = NULL; + dp->buf = malloc(maxsz, M_USBDEV, M_NOWAIT | M_ZERO); + if (dp->buf == NULL) { + device_printf(sc->sc_dev, + "could not allocate buffer\n"); + error = ENOMEM; + goto fail; + } + + // TODO MichalP need IEEE80211 api implemented + //dp->ni = NULL; + } + + return (0); +fail: + ath_free_list(usc, data, ndata); + return (error); +} + +static int +ath_free_list(struct ath_usb_softc *usc, struct ath_usb_data data[], int ndata) +{ + int i; + + for (i = 0; i < ndata; i++) { + struct ath_usb_data *dp = &data[i]; + + if (dp->buf != NULL) { + free(dp->buf, M_USBDEV); + dp->buf = NULL; + } +// TODO MichalP need IEEE80211 api implemented +// if (dp->ni != NULL) { +// ieee80211_free_node(dp->ni); +// dp->ni = NULL; +// } + } + return 0; +} + +int +ath_usb_alloc_rx_list(struct ath_usb_softc *usc) +{ + int i, error; + + error = ath_alloc_list(usc, usc->rx_data, ath_USB_RX_LIST_COUNT, + ath_USB_RXBUFSZ); + + STAILQ_INIT(&usc->sc_rx_active); + STAILQ_INIT(&usc->sc_rx_inactive); + + for (i = 0; i < ath_USB_RX_LIST_COUNT; i++) + STAILQ_INSERT_HEAD(&usc->sc_rx_inactive, &usc->rx_data[i], next); + + return (error); +} + +void +ath_usb_free_rx_list(struct ath_usb_softc *usc) +{ + int i; + + STAILQ_INIT(&usc->sc_rx_inactive); + STAILQ_INIT(&usc->sc_rx_active); + + ath_free_list(usc, usc->rx_data, ath_USB_RX_LIST_COUNT); +} + +int +ath_usb_alloc_tx_list(struct ath_usb_softc *usc) +{ + int i, error; + + error = ath_alloc_list(usc, usc->tx_data, ath_USB_TX_LIST_COUNT, + ath_USB_TXBUFSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&usc->sc_tx_inactive); + + for (i = 0; i != ATH_N_XFER; i++) { + STAILQ_INIT(&usc->sc_tx_active[i]); + STAILQ_INIT(&usc->sc_tx_pending[i]); + } + + for (i = 0; i < ath_USB_TX_LIST_COUNT; i++) { + STAILQ_INSERT_HEAD(&usc->sc_tx_inactive, &usc->tx_data[i], next); + } + + return (0); +} + +void +ath_usb_free_tx_list(struct ath_usb_softc *usc) +{ + int i; + + STAILQ_INIT(&usc->sc_tx_inactive); + + for (i = 0; i != ATH_N_XFER; i++) { + STAILQ_INIT(&usc->sc_tx_active[i]); + STAILQ_INIT(&usc->sc_tx_pending[i]); + } + + ath_free_list(usc, usc->tx_data, ath_USB_TX_LIST_COUNT); +} + +int +ath_usb_alloc_tx_cmd(struct ath_usb_softc *usc) +{ + struct ath_usb_data *data; + int i, error = 0; + struct ath_softc *sc = usc->sc_sc; + + error = ath_alloc_list(usc, usc->tx_cmd, ATH_USB_HOST_CMD_RING_COUNT, + ATH_USB_TXCMDSZ); + if (error != 0) + return (error); + + STAILQ_INIT(&usc->sc_cmd_active); + STAILQ_INIT(&usc->sc_cmd_inactive); + STAILQ_INIT(&usc->sc_cmd_pending); + STAILQ_INIT(&usc->sc_cmd_waiting); + + for (i = 0; i < ATH_USB_HOST_CMD_RING_COUNT; i++) + STAILQ_INSERT_HEAD(&usc->sc_cmd_inactive, &usc->tx_cmd[i], next); + + return (0); +} + +void +ath_usb_free_tx_cmd(struct ath_usb_softc *usc) +{ + STAILQ_INIT(&usc->sc_cmd_active); + STAILQ_INIT(&usc->sc_cmd_inactive); + STAILQ_INIT(&usc->sc_cmd_pending); + STAILQ_INIT(&usc->sc_cmd_waiting); + + ath_free_list(usc, usc->tx_cmd, ATH_USB_HOST_CMD_RING_COUNT); +} + +char *state2Str(int state) { + switch (state) { + case USB_ST_SETUP: + return "USB_ST_SETUP"; + case USB_ST_TRANSFERRED: + return "USB_ST_TRANSFERRED"; + case USB_ST_ERROR: + return "USB_ST_ERROR"; + default: + return "state2Str UNKNOWN"; + } +} + + +char * epType_str(uint8_t bmAttributes) { + switch(bmAttributes) { + case UE_CONTROL: + return "UE_CONTROL"; + case UE_ISOCHRONOUS: + return "UE_ISOCHRONOUS"; + case UE_BULK: + return "UE_BULK"; + case UE_INTERRUPT: + return "UE_INTERRUPT"; + default: + return "UNKNOWN"; + } +} + +static boolean_t +ath_htc_rx_handle(struct ath_usb_softc *usc, uint8_t *buf, int actlen) +{ + struct ar_htc_msg_hdr *msg; + uint16_t msg_id; + + // Endpoint 0 carries HTC messages. + if (actlen < sizeof(*msg)) { + #ifdef DEVELOPMENT + // printf("TTTT: htc->flags & AR_HTC_FLAG_TRAILER\n"); + #endif + return TRUE; + } + + msg = (struct ar_htc_msg_hdr *)buf; + msg_id = be16toh(msg->msg_id); + #ifdef DEVELOPMENT + // printf("TTTT: Rx HTC msg_id %d, wait_msg_id %d \n", msg_id, usc->wait_msg_id); + #endif + switch (msg_id) { + case AR_HTC_MSG_READY: + if (usc->wait_msg_id != msg_id) + break; + usc->wait_msg_id = 0; + wakeup(&usc->wait_msg_id); + break; + case AR_HTC_MSG_CONN_SVC_RSP: + if (usc->wait_msg_id != msg_id) + break; + if (usc->msg_conn_svc_rsp != NULL) { + memcpy(usc->msg_conn_svc_rsp, &msg[1], + sizeof(struct ar_htc_msg_conn_svc_rsp)); + } + usc->wait_msg_id = 0; + wakeup(&usc->wait_msg_id); + break; + case AR_HTC_MSG_CONF_PIPE_RSP: + if (usc->wait_msg_id != msg_id) + break; + usc->wait_msg_id = 0; + wakeup(&usc->wait_msg_id); + break; + default: + printf("HTC message %d ignored\n", msg_id); + break; + } + return FALSE; +} + +static void +ath_if_intr_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ath_usb_softc *usc = usbd_xfer_softc(xfer); + struct ath_softc *sc = usc->sc_sc; + struct ar_htc_frame_hdr *htc; + uint8_t *buf; + + int actlen, sumlen; + int state = USB_GET_STATE(xfer); + char *state_str = state2Str(state); + + usbd_xfer_status(xfer, &actlen, &sumlen, NULL, NULL); + #ifdef DEVELOPMENT + // printf("TTTT: INTR RX Xfer callback: error = %s, state = %s, actlen = %d, " + // "sumlen = %d, endpoint = 0x%lx\n", + // usbd_errstr(error), state_str, actlen, sumlen, (long)xfer->endpoint); + #endif + switch(state) { + case USB_ST_TRANSFERRED: + { + #ifdef DEVELOPMENT + // printf("TTTT: INTR RX Xfer state USB_ST_TRANSFERRED\n"); + #endif + buf = usbd_xfer_get_frame_buffer(xfer, 0); + if (actlen >= 4 && *(uint32_t *)buf == htobe32(0x00c60000)) { + buf += 4; + actlen -= 4; + } + if ((actlen < sizeof(*htc))) + return; + + htc = (struct ar_htc_frame_hdr *)buf; + // Skip HTC header. + buf += sizeof(*htc); + actlen -= sizeof(*htc); + + if (htc->endpoint_id == 0) { + if (ath_htc_rx_handle(usc, buf, actlen)) + break; + } else { + #ifdef DEVELOPMENT + // printf("TTTT: htc->endpoint_id != 0\n"); + #endif + if (htc->endpoint_id != usc->ep_ctrl) { + #ifdef DEVELOPMENT + // printf("TTTT: htc->endpoint_id != usc->ep_ctrl\n"); + #endif + return; + } + + /// Remove trailer if present. + if (htc->flags & AR_HTC_FLAG_TRAILER) { + #ifdef DEVELOPMENT + // printf("TTTT: htc->flags & AR_HTC_FLAG_TRAILER\n"); + #endif + if (actlen < htc->control[0]) { + #ifdef DEVELOPMENT + // printf("TTTT: actlen < htc->control[0]\n"); + #endif + return; + } + + actlen -= htc->control[0]; + } + ath_usb_rx_wmi_ctrl(usc, buf, actlen); + } + } + /* FALLTHROUGH */ + case USB_ST_SETUP: + printf("USB_ST_SETUP called\n"); + usbd_xfer_set_frame_len(xfer, 0, usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + default: /* Error */ + #ifdef DEVELOPMENT + // printf("TTTT: INTR RX Xfer: error\n"); + #endif + break; + } + return; +} + +static void +ath_if_intr_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ath_usb_data *cmd; + struct ath_usb_softc *usc = usbd_xfer_softc(xfer); + struct ath_softc *sc = usc->sc_sc; + + int actlen; + int state = USB_GET_STATE(xfer); + char *state_str = state2Str(state); + + ATH_USB_LOCK_ASSERT(sc); + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + #ifdef DEVELOPMENT + // MichalP: Debuginfo + // printf("TTTT: INTR TX Xfer callback: error = %s, state = %s, actlen = %d, endpoint = 0x%lx\n", + // usbd_errstr(error), state_str, actlen, (long)xfer->endpoint); + #endif + + switch(state) { + case USB_ST_TRANSFERRED: + cmd = STAILQ_FIRST(&usc->sc_cmd_active); + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: continue with USB_ST_TRANSFERRED cmd: %p\n", __func__, cmd); + #endif + if (cmd == NULL) + goto tr_setup; + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: transfer done cmd: %p\n", __func__, cmd); + #endif + STAILQ_REMOVE_HEAD(&usc->sc_cmd_active, next); + // MichalP TODO: this still needs some thinking maybe separate function + // different object that the thread sleeps on (cmd?) + if ((usc->wait_msg_id == AR_HTC_MSG_CONN_SVC_RSP) || + (usc->wait_msg_id == AR_WMI_CMD_MSG)) { + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: we are waiting for a response cmd: %p\n", __func__, cmd); + #endif + STAILQ_INSERT_TAIL(&usc->sc_cmd_waiting, cmd, next); + } else { + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: we DONT wait for response cmd: %p\n", __func__, cmd); + #endif + wakeup(&usc->wait_msg_id); + STAILQ_INSERT_TAIL(&usc->sc_cmd_inactive, cmd, next); + } + if (usc->wait_msg_id == AR_WMI_CMD_MSG) + STAILQ_INSERT_TAIL(&usc->sc_cmd_inactive, cmd, next); + + /* FALLTHROUGH */ + case USB_ST_SETUP: +tr_setup: + cmd = STAILQ_FIRST(&usc->sc_cmd_pending); + if (cmd == NULL) { + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: empty pending queue cmd: %p\n", __func__, cmd); + #endif + return; + } + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: continue with USB_ST_SETUP cmd: %p\n", __func__, cmd); + #endif + STAILQ_REMOVE_HEAD(&usc->sc_cmd_pending, next); + STAILQ_INSERT_TAIL(&usc->sc_cmd_active, cmd, next); + usbd_xfer_set_frame_data(xfer, 0, cmd->buf, cmd->buflen); + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: submitting transfer %p; buf=%p, buflen=%d\n", + // __func__, cmd, cmd->buf, cmd->buflen); + #endif + usbd_transfer_submit(xfer); + break; + default: + cmd = STAILQ_FIRST(&usc->sc_cmd_active); + #ifdef DEVELOPMENT + // device_printf(sc->sc_dev, "%s: continue with default %p\n", __func__, usc); + #endif + if (cmd != NULL) { + device_printf(sc->sc_dev, "%s: cmd not NULL %p\n", __func__, usc); + STAILQ_REMOVE_HEAD(&usc->sc_cmd_active, next); +// if (cmd->odata) { +// STAILQ_INSERT_TAIL(&usc->sc_cmd_waiting, cmd, next_cmd); +// } else { +// wakeup(cmd); +// otus_free_txcmd(sc, cmd); +// } + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +} + +static void +ath_if_bulk_rx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + struct ath_usb_softc *usc = usbd_xfer_softc(xfer); + struct ath_softc *sc = usc->sc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct ath_usb_data *data; + + int actlen; + int state = USB_GET_STATE(xfer); + char *state_str = state2Str(state); + + ATH_USB_LOCK_ASSERT(sc); + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); +#ifdef DEVELOPMENT + // MichalP: Debuginfo + // printf("TTTT: BULK RX Xfer callback: error = %s, state = %s, actlen = %d, endpoint = 0x%lx\n", + // usbd_errstr(error), state_str, actlen, (long)xfer->endpoint); +#endif + switch(state) { + case USB_ST_TRANSFERRED: + device_printf(sc->sc_dev, "%s: USB_ST_TRANSFERRED called \n", __func__); + data = STAILQ_FIRST(&usc->sc_rx_active); + if (data == NULL) + goto tr_setup; + STAILQ_REMOVE_HEAD(&usc->sc_rx_active, next); + ath_usb_rxeof(xfer, data); + STAILQ_INSERT_TAIL(&usc->sc_rx_inactive, data, next); + /* FALLTHROUGH */ + case USB_ST_SETUP: + device_printf(sc->sc_dev, "%s: USB_ST_SETUP called \n", __func__); + tr_setup: + device_printf(sc->sc_dev, "%s: USB_ST_SETUP after goto called \n", __func__); + data = STAILQ_FIRST(&usc->sc_rx_inactive); + if (data == NULL) { + //KASSERT(m == NULL, ("mbuf isn't NULL")); + return; + } + STAILQ_REMOVE_HEAD(&usc->sc_rx_inactive, next); + STAILQ_INSERT_TAIL(&usc->sc_rx_active, data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, + usbd_xfer_max_len(xfer)); + usbd_transfer_submit(xfer); + break; + default: + device_printf(sc->sc_dev, "%s: default called \n", __func__); + /* needs it to the inactive queue due to a error. */ + data = STAILQ_FIRST(&usc->sc_rx_active); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&usc->sc_rx_active, next); + STAILQ_INSERT_TAIL(&usc->sc_rx_inactive, data, next); + } + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + counter_u64_add(ic->ic_ierrors, 1); + goto tr_setup; + } + break; + } +} + +static void +ath_if_bulk_tx_callback(struct usb_xfer *xfer, usb_error_t error) +{ + uint8_t which = ath_BULK_TX; + struct ath_usb_softc *usc = usbd_xfer_softc(xfer); + struct ath_softc *sc = usc->sc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_usb_data *data; + + ATH_USB_LOCK_ASSERT(sc); + + int actlen; + int state = USB_GET_STATE(xfer); + char *state_str = state2Str(state); + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); +#ifdef DEVELOPMENT + // printf("TTTT: BULK TX Xfer callback: error = %s, state = %s, actlen = %d, endpoint = 0x%lx\n", + // usbd_errstr(error), state_str, actlen, (long)xfer->endpoint); +#endif + switch(state) { + case USB_ST_TRANSFERRED: + data = STAILQ_FIRST(&usc->sc_tx_active[which]); + if (data == NULL) + goto tr_setup; + device_printf(sc->sc_dev, "%s: transfer done %p\n", __func__, data); + STAILQ_REMOVE_HEAD(&usc->sc_tx_active[which], next); + ath_usb_txeof(xfer, data); + STAILQ_INSERT_TAIL(&usc->sc_tx_inactive, data, next); + case USB_ST_SETUP: + tr_setup: + data = STAILQ_FIRST(&usc->sc_tx_pending[which]); + if (data == NULL) { + device_printf(sc->sc_dev, "%s: empty pending queue sc %p\n", __func__, sc); + usc->sc_tx_n_active = 0; + } + STAILQ_REMOVE_HEAD(&usc->sc_tx_pending[which], next); + STAILQ_INSERT_TAIL(&usc->sc_tx_active[which], data, next); + usbd_xfer_set_frame_data(xfer, 0, data->buf, data->buflen); + device_printf(sc->sc_dev, "%s: submitting transfer %p\n", __func__, data); + usbd_transfer_submit(xfer); + usc->sc_tx_n_active++; + break; + default: + data = STAILQ_FIRST(&usc->sc_tx_active[which]); + if (data != NULL) { + STAILQ_REMOVE_HEAD(&usc->sc_tx_active[which], next); + ath_usb_txeof(xfer, data); + STAILQ_INSERT_TAIL(&usc->sc_tx_inactive, data, next); + } + counter_u64_add(ic->ic_oerrors, 1); + + if (error != USB_ERR_CANCELLED) { + usbd_xfer_set_stall(xfer); + goto tr_setup; + } + break; + } +#if ATHN_API + taskqueue_enqueue(taskqueue_thread, &sc->sc_task); +#endif +} + +void +ath_usb_task(void *arg, int pending) +{ +#ifdef otus + struct ath_usb_softc *usc = arg; + struct ath_usb_host_cmd_ring *ring = &usc->cmdq; + struct ath_usb_host_cmd *cmd; + int s; + + // lock + + /* Process host commands. */ + while (ring->next != ring->cur) { + cmd = &ring->cmd[ring->next]; + /* Invoke callback. */ + cmd->cb(usc, cmd->data); + ring->queued--; + ring->next = (ring->next + 1) % ATH_USB_HOST_CMD_RING_COUNT; + } +#endif +} + +void +ath_usb_do_async(struct ath_usb_softc *usc, + void (*cb)(struct ath_usb_softc *, void *), void *arg, int len) +{ +#if OpenBSD_ONLY + struct ath_usb_host_cmd_ring *ring = &usc->cmdq; + struct ath_usb_host_cmd *cmd; + int s; + + if (ring->queued == ATH_USB_HOST_CMD_RING_COUNT) { + printf("%s: host cmd queue overrun\n", device_get_name(usc->usb_dev)); + return; /* XXX */ + } + + s = splusb(); + cmd = &ring->cmd[ring->cur]; + cmd->cb = cb; + KASSERT(len <= sizeof(cmd->data), "ath_usb_do_async"); + memcpy(cmd->data, arg, len); + ring->cur = (ring->cur + 1) % ATH_USB_HOST_CMD_RING_COUNT; + + /* If there is no pending command already, schedule a task. */ + if (++ring->queued == 1) + usb_add_task(usc->sc_udev, &usc->sc_task); + splx(s); +#endif +} + +void +ath_usb_wait_async(struct ath_usb_softc *usc) +{ + /* Wait for all queued asynchronous commands to complete. */ +#if OpenBSD_ONLY + usb_wait_task(usc->sc_udev, &usc->sc_task); +#endif +} + +void +ath_usb_verify_fw(struct ath_usb_softc *usc) +{ + struct ath_softc *sc = usc->sc_sc; + struct ath_usb_data *data; + + ATH_USB_LOCK(sc); + + data = STAILQ_FIRST(&usc->sc_cmd_inactive); + if (data == NULL) { + device_printf(sc->sc_dev, "%s: no tx cmd buffers\n", + __func__); + return; + } + STAILQ_REMOVE_HEAD(&usc->sc_cmd_inactive, next); +} + +int +ath_usb_htc_msg(struct ath_usb_softc *usc, uint16_t msg_id, void *buf, + int len) +{ + struct ath_softc *sc = usc->sc_sc; + struct ath_usb_data *cmd; + struct ar_htc_frame_hdr *htc; + struct ar_htc_msg_hdr *msg; + int error = 0; + + device_printf(sc->sc_dev, "%s: message id: %d\n", __func__, msg_id); + + ATH_USB_LOCK_ASSERT(sc); + + cmd = STAILQ_FIRST(&usc->sc_cmd_inactive); + if (cmd == NULL) { + device_printf(sc->sc_dev, "%s: no tx cmd buffers\n", + __func__); + return -1; + } + STAILQ_REMOVE_HEAD(&usc->sc_cmd_inactive, next); + + htc = (struct ar_htc_frame_hdr *)cmd->buf; + memset(htc, 0, sizeof(*htc)); + htc->endpoint_id = 0; + htc->payload_len = htobe16(sizeof(*msg) + len); + + msg = (struct ar_htc_msg_hdr *)&htc[1]; + msg->msg_id = htobe16(msg_id); + + memcpy(&msg[1], buf, len); + + cmd->buflen = sizeof(*htc) + sizeof(*msg) + len; + device_printf(sc->sc_dev, "%s: prepare transfer %p; buf=%p, buflen=%d\n", + __func__, cmd, cmd->buf, cmd->buflen); + + STAILQ_INSERT_TAIL(&usc->sc_cmd_pending, cmd, next); + usbd_transfer_start(usc->sc_xfer[ath_BULK_CMD]); + + return error; +} + +int +ath_usb_htc_setup(struct ath_usb_softc *usc) +{ + struct ar_htc_msg_config_pipe cfg; + int s, error; + + /* + * Connect WMI services to USB pipes. + */ + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_CONTROL, + AR_PIPE_TX_INTR, AR_PIPE_RX_INTR, &usc->ep_ctrl); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_BEACON, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_bcn); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_CAB, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_cab); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_UAPSD, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_uapsd); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_MGMT, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_mgmt); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_BE, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[ATH_QID_AC_BE]); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_BK, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[ATH_QID_AC_BK]); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_VI, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[ATH_QID_AC_VI]); + if (error != 0) + return (error); + error = ath_usb_htc_connect_svc(usc, AR_SVC_WMI_DATA_VO, + AR_PIPE_TX_DATA, AR_PIPE_RX_DATA, &usc->ep_data[ATH_QID_AC_VO]); + if (error != 0) + return (error); + + /* Set credits for WLAN Tx pipe. */ + memset(&cfg, 0, sizeof(cfg)); + cfg.pipe_id = UE_GET_ADDR(AR_PIPE_TX_DATA); + cfg.credits = (usc->flags & ATH_USB_FLAG_AR7010) ? 45 : 33; + + ATH_USB_LOCK(usc->sc_sc); + + usc->wait_msg_id = AR_HTC_MSG_CONF_PIPE_RSP; + error = ath_usb_htc_msg(usc, AR_HTC_MSG_CONF_PIPE, &cfg, sizeof(cfg)); + if (error == 0 && usc->wait_msg_id != 0) + error = msleep(&usc->wait_msg_id, &usc->sc_sc->sc_usb_mtx, PCATCH, "athhtc", + hz); + usc->wait_msg_id = 0; + + ATH_USB_UNLOCK(usc->sc_sc); + + if (error != 0) { + printf("%s: could not configure pipe\n", + device_get_name(usc->usb_dev)); + return (error); + } + + ATH_USB_LOCK(usc->sc_sc); + + error = ath_usb_htc_msg(usc, AR_HTC_MSG_SETUP_COMPLETE, NULL, 0); + if (error != 0) { + ATH_USB_UNLOCK(usc->sc_sc); + printf("%s: could not complete setup\n", + device_get_name(usc->usb_dev)); + return (error); + } + + ATH_USB_UNLOCK(usc->sc_sc); + + return (0); +} + +int +ath_usb_htc_connect_svc(struct ath_usb_softc *usc, uint16_t svc_id, + uint8_t ul_pipe, uint8_t dl_pipe, uint8_t *endpoint_id) +{ + struct ar_htc_msg_conn_svc msg; + struct ar_htc_msg_conn_svc_rsp rsp; + int s, error; + + struct ath_softc *sc = usc->sc_sc; + device_printf(sc->sc_dev, "%s: configuring svc_id %d \n", __func__, svc_id); + + memset(&msg, 0, sizeof(msg)); + msg.svc_id = htobe16(svc_id); + msg.dl_pipeid = UE_GET_ADDR(dl_pipe); + msg.ul_pipeid = UE_GET_ADDR(ul_pipe); + usc->msg_conn_svc_rsp = &rsp; + + ATH_USB_LOCK(usc->sc_sc); + + usc->wait_msg_id = AR_HTC_MSG_CONN_SVC_RSP; + error = ath_usb_htc_msg(usc, AR_HTC_MSG_CONN_SVC, &msg, sizeof(msg)); + /* Wait at most 1 second for response. */ + if (error == 0 && usc->wait_msg_id != 0) + error = msleep(&usc->wait_msg_id, &usc->sc_sc->sc_usb_mtx, PCATCH, "athhtc", hz * 2); + usc->wait_msg_id = 0; + + ATH_USB_UNLOCK(usc->sc_sc); + + if (error != 0) { + device_printf(sc->sc_dev, "%s: error waiting for service %d connection " + "error: %d \n", __func__, htobe16(svc_id), error); + return (error); + } + if (rsp.status != AR_HTC_SVC_SUCCESS) { + device_printf(sc->sc_dev, "%s: service %d connection failed, " + "error: %d\n", __func__, htobe16(svc_id), rsp.status); + return (EIO); + } + + device_printf(sc->sc_dev, "%s: service %d successfully connected to endpoint %d\n", + __func__, svc_id, rsp.endpoint_id); + + /* Return endpoint id. */ + *endpoint_id = rsp.endpoint_id; + return (0); +} + +int +ath_usb_wmi_xcmd(struct ath_usb_softc *usc, uint16_t cmd_id, void *ibuf, + int ilen, void *obuf) +{ + struct ath_softc *sc = usc->sc_sc; + struct ath_usb_data *data; + struct ar_htc_frame_hdr *htc; + struct ar_wmi_cmd_hdr *wmi; + int xferlen, error; + + //ATH_USB_LOCK_ASSERT(sc); + + data = STAILQ_FIRST(&usc->sc_cmd_inactive); + if (data == NULL) { + device_printf(sc->sc_dev, "%s: no tx cmd buffers\n", + __func__); + return -1; + } + STAILQ_REMOVE_HEAD(&usc->sc_cmd_inactive, next); + ATH_USB_LOCK(sc); + while (usc->wait_cmd_id) { + /* + * The previous USB transfer is not done yet. We can't use + * data->xfer until it is done or we'll cause major confusion + * in the USB stack. + */ + msleep(&usc->wait_msg_id, &usc->sc_sc->sc_usb_mtx, PCATCH, "athwmx", hz); + } + xferlen = sizeof(*htc) + sizeof(*wmi) + ilen; + data->buflen = xferlen; + + htc = (struct ar_htc_frame_hdr *)data->buf; + memset(htc, 0, sizeof(*htc)); + htc->endpoint_id = usc->ep_ctrl; + htc->payload_len = htobe16(sizeof(*wmi) + ilen); + + wmi = (struct ar_wmi_cmd_hdr *)&htc[1]; + wmi->cmd_id = htobe16(cmd_id); + usc->wmi_seq_no++; + wmi->seq_no = usc->wmi_seq_no; + usc->wait_msg_id = AR_WMI_CMD_MSG; + + memcpy(&wmi[1], ibuf, ilen); + + usc->wait_cmd_id = cmd_id; + usc->obuf = obuf; + STAILQ_INSERT_TAIL(&usc->sc_cmd_pending, data, next); + usbd_transfer_start(usc->sc_xfer[ath_BULK_CMD]); + + /* + * Wait for WMI command complete interrupt. In case it does not fire + * wait until the USB transfer times out to avoid racing the transfer. + */ + error = msleep(&usc->wait_cmd_id, &usc->sc_sc->sc_usb_mtx, PCATCH, "athwmi", 2*hz); + if (error == EWOULDBLOCK) { + printf("%s: firmware command 0x%x timed out\n", + device_get_name(usc->usb_dev), cmd_id); + error = ETIMEDOUT; + } + + device_printf(sc->sc_dev, "%s: aftersleep\n", __func__); + + /* + * Both the WMI command and transfer are done or have timed out. + * Allow other threads to enter this function and use data->xfer. + */ + usc->obuf = NULL; + usc->wait_msg_id = 0; + usc->wait_cmd_id = 0; + wakeup(&usc->wait_cmd_id); + ATH_USB_UNLOCK(sc); + return (error); +} + +int +ath_usb_read_rom(struct ath_softc *sc) +{ + struct ath_usb_softc *usc = sc->usc; + uint32_t addrs[8], vals[8], addr; + uint16_t *eep; + int i, j, error; +// no member named 'eep' in 'struct ath_softc' +#if ATHN_API + /* Read EEPROM by blocks of 16 bytes. */ + eep = sc->eep; + addr = AR_EEPROM_OFFSET(sc->eep_base); + for (i = 0; i < sc->eep_size / 16; i++) { + for (j = 0; j < 8; j++, addr += 4) + addrs[j] = htobe32(addr); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_READ, + addrs, sizeof(addrs), vals); + if (error != 0) + break; + for (j = 0; j < 8; j++) + *eep++ = be32toh(vals[j]); + } +#else + return 0; +#endif + return (error); +} + +uint32_t +ath_usb_read(struct ath_softc *sc, uint32_t addr) +{ + struct ath_usb_softc *usc = sc->usc; + uint32_t val; + int error; + + device_printf(sc->sc_dev, "%s: called \n", __func__); + + /* Flush pending writes for strict consistency. */ + ath_usb_write_barrier(sc); + + addr = htobe32(addr); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_READ, + &addr, sizeof(addr), &val); + if (error != 0) + device_printf(sc->sc_dev, + "%s: error \n", + __func__); + return be32toh(val); +} + +void +ath_usb_write(struct ath_softc *sc, uint32_t addr, uint32_t val) +{ + struct ath_usb_softc *usc = sc->usc; + + usc->wbuf[usc->wcount].addr = htobe32(addr); + usc->wbuf[usc->wcount].val = htobe32(val); + + if ((++usc->wcount == AR_MAX_WRITE_COUNT) || (usc->no_buffer_write)) + ath_usb_write_barrier(sc); +} + +void +ath_usb_write_barrier(struct ath_softc *sc) +{ + struct ath_usb_softc *usc = sc->usc; + + if (usc->wcount == 0) + return; /* Nothing to write. */ + + (void)ath_usb_wmi_xcmd(usc, AR_WMI_CMD_REG_WRITE, + usc->wbuf, usc->wcount * sizeof(usc->wbuf[0]), NULL); + usc->wcount = 0; /* Always flush buffer. */ +} + +int +ath_usb_media_change(struct ifnet *ifp) +{ + int error; + + error = ieee80211_media_change(ifp); + if (error != ENETRESET) + return (error); + + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + ath_usb_stop(ifp); + error = ath_usb_init(ifp); + } + + return (error); +} + +void +ath_usb_next_scan(void *arg, int pending) +{ + struct ath_usb_softc *usc = arg; + struct ath_softc *sc = usc->sc_sc; + struct ieee80211com *ic = &sc->sc_ic; + int s; + +#if OpenBSD_IEEE80211_API + if (ic->ic_state == IEEE80211_S_SCAN) + ieee80211_next_scan(&ic->ic_if); +#endif +} + +// uncomment after investigation if it is needed to be async +// int +// ath_usb_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, +// int arg) +// { +// struct ieee80211com *ic = vap->iv_ic; +// struct ath_usb_softc *usc = ic->ic_softc; +// struct ath_vap *uvp = ATH_VAP(vap); +// struct ath_usb_cmd_newstate cmd; + +// /* Do it in a process context. */ +// cmd.state = nstate; +// cmd.arg = arg; +// ath_usb_do_async(usc, ath_usb_newstate_cb, &cmd, sizeof(cmd)); +// return (0); +// } +#if ATHN_API +int +ath_usb_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, + int arg) +{ + struct ath_vap *uvp = ATH_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + struct ieee80211_node *ni = ieee80211_ref_node(vap->iv_bss); + enum ieee80211_state ostate; + uint32_t reg, imask; + int s, error; +#if OpenBSD_ONLY + timeout_del(&sc->calib_to); +#endif + IEEE80211_UNLOCK(ic); + ATH_USB_LOCK(sc); + ostate = vap->iv_state; + + if (ostate == IEEE80211_S_RUN && ic->ic_opmode == IEEE80211_M_STA) { + ath_usb_remove_node(usc, ni); + reg = AR_READ(sc, AR_RX_FILTER); + reg = (reg & ~AR_RX_FILTER_MYBEACON) | + AR_RX_FILTER_BEACON; + AR_WRITE(sc, AR_RX_FILTER, reg); + AR_WRITE_BARRIER(sc); + } + + switch (nstate) { + case IEEE80211_S_INIT: + ath_set_led(sc, 0); + break; + case IEEE80211_S_SCAN: + /* Make the LED blink while scanning. */ + ath_set_led(sc, !sc->led_state); + error = ath_usb_switch_chan(sc, ni->ni_chan, NULL); + if (error) + printf("%s: could not switch to channel %d\n", + device_get_name(usc->usb_dev), 0); + ieee80211_chan2ieee(ic, ni->ni_chan); + // uncomment after investigation if it is needed to be async + // if (!usbd_is_dying(usc->sc_udev)) + // timeout_add_msec(&sc->scan_to, 200); + break; + case IEEE80211_S_AUTH: + ath_set_led(sc, 0); + error = ath_usb_switch_chan(sc, ni->ni_chan, NULL); + if (error) + printf("%s: could not switch to channel %d\n", + device_get_name(usc->usb_dev), 0); + ieee80211_chan2ieee(ic, ni->ni_chan); + break; + case IEEE80211_S_ASSOC: + break; + case IEEE80211_S_RUN: + ath_set_led(sc, 1); + + if (ic->ic_opmode == IEEE80211_M_MONITOR) + break; + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* Create node entry for our BSS */ + error = ath_usb_create_node(usc, ni); + if (error) + printf("%s: could not update firmware station " + "table\n", device_get_name(usc->usb_dev)); + } + ath_set_bss(sc, ni); + ath_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR); +#ifndef IEEE80211_STA_ONLY + if (ic->ic_opmode == IEEE80211_M_HOSTAP) { + ath_usb_switch_chan(sc, ni->ni_chan, NULL); + ath_set_hostap_timers(sc); + /* Enable software beacon alert interrupts. */ + imask = htobe32(HAL_INT_SWBA); + } else +#endif + { + ath_set_sta_timers(sc); + /* Enable beacon miss interrupts. */ + imask = htobe32(HAL_INT_BMISS); + + /* Stop receiving beacons from other BSS. */ + reg = AR_READ(sc, AR_RX_FILTER); + reg = (reg & ~AR_RX_FILTER_BEACON) | + AR_RX_FILTER_MYBEACON; + AR_WRITE(sc, AR_RX_FILTER, reg); + AR_WRITE_BARRIER(sc); + } + ath_usb_wmi_xcmd(usc, AR_WMI_CMD_ENABLE_INTR, + &imask, sizeof(imask), NULL); + break; + default: + break; + } +#if OpenBSD_IEEE80211_API + return sc->sc_newstate(ic, state, arg); + splx(s); + #endif + ATH_USB_UNLOCK(sc); + IEEE80211_LOCK(ic); + return (uvp->newstate(vap, nstate, arg)); +} +#endif + +void +ath_usb_newassoc(struct ieee80211com *ic, struct ieee80211_node *ni, + int isnew) +{ +#ifndef IEEE80211_STA_ONLY + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; +#if OpenBSD_IEEE80211_API + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_state != IEEE80211_S_RUN) + return; +#endif + /* Update the node's supported rates in a process context. */ + ieee80211_ref_node(ni); + ath_usb_do_async(usc, ath_usb_newassoc_cb, &ni, sizeof(ni)); +#endif +} + +#ifndef IEEE80211_STA_ONLY +void +ath_usb_newassoc_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ieee80211com *ic = &usc->sc_sc->sc_ic; + struct ieee80211_node *ni = *(void **)arg; + struct ath_node *an = (struct ath_node *)ni; + int s; +#if OpenBSD_IEEE80211_API + if (ic->ic_state != IEEE80211_S_RUN) + return; + + s = splnet(); + /* NB: Node may have left before we got scheduled. */ + if (an->sta_index != 0) + (void)ath_usb_node_set_rates(usc, ni); + ieee80211_release_node(ic, ni); + splx(s); +#endif +} +#endif + +struct ieee80211_node * +ath_usb_node_alloc(struct ieee80211com *ic) +{ + struct ath_node *an; + + an = malloc(sizeof(struct ath_node), M_USBDEV, M_NOWAIT | M_ZERO); + return (struct ieee80211_node *)an; +} + + +#ifndef IEEE80211_STA_ONLY +void +ath_usb_count_active_sta(void *arg, struct ieee80211_node *ni) +{ + int *nsta = arg; + struct ath_node *an = (struct ath_node *)ni; +#if ATHN_API + if (an->sta_index == 0) + return; +#endif +#if OpenBSD_IEEE80211_API + if ((ni->ni_state == IEEE80211_STA_AUTH || + ni->ni_state == IEEE80211_STA_ASSOC) && + ni->ni_inact < IEEE80211_INACT_MAX) + (*nsta)++; +#endif +} + +struct ath_usb_newauth_cb_arg { + struct ieee80211_node *ni; + uint16_t seq; +}; + +void +ath_usb_newauth_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ieee80211com *ic = &usc->sc_sc->sc_ic; + struct ath_usb_newauth_cb_arg *a = arg; + struct ieee80211_node *ni = a->ni; + uint16_t seq = a->seq; + struct ath_node *an = (struct ath_node *)ni; + int s, error = 0; + +#if OpenBSD_IEEE80211_API + if (ic->ic_state != IEEE80211_S_RUN) + return; +#endif + + s = splnet(); +#if ATHN_API + if (an->sta_index == 0) { + error = ath_usb_create_node(usc, ni); + if (error) + printf("%s: could not add station %s to firmware " + "table\n", device_get_name(usc->usb_dev), + ether_sprintf(ni->ni_macaddr)); + } +#endif +#if OpenBSD_IEEE80211_API + if (error == 0) + ieee80211_auth_open_confirm(ic, ni, seq); + ieee80211_unref_node(&ni); +#endif + splx(s); +} +#endif + +int +ath_usb_newauth(struct ieee80211com *ic, struct ieee80211_node *ni, + int isnew, uint16_t seq) +{ +#ifndef IEEE80211_STA_ONLY + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; +#if OpenBSD_IEEE80211_API + struct ifnet *ifp = &ic->ic_if; +#endif + struct ath_node *an = (struct ath_node *)ni; + int nsta; + struct ath_usb_newauth_cb_arg arg; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP) + return 0; +#if ATHN_API + if (!isnew && an->sta_index != 0) /* already in firmware table */ + return 0; +#endif + /* Check if we have room in the firmware table. */ + nsta = 1; /* Account for default node. */ +#if OpenBSD_IEEE80211_API + ieee80211_iterate_nodes(ic, ath_usb_count_active_sta, &nsta); + if (nsta >= AR_USB_MAX_STA) { + if (ifp->if_flags & IFF_DEBUG) + printf("%s: cannot authenticate station %s: firmware " + "table is full\n", device_get_name(usc->usb_dev), + ether_sprintf(ni->ni_macaddr)); + return ENOSPC; + } +#endif + + /* + * In a process context, try to add this node to the + * firmware table and confirm the AUTH request. + */ + arg.ni = ieee80211_ref_node(ni); + arg.seq = seq; + ath_usb_do_async(usc, ath_usb_newauth_cb, &arg, sizeof(arg)); + return EBUSY; +#else + return 0; +#endif /* IEEE80211_STA_ONLY */ +} + +#ifndef IEEE80211_STA_ONLY +void +ath_usb_node_free(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + struct ath_node *an = (struct ath_node *)ni; + + /* + * Remove the node from the firmware table in a process context. + * Pass an index rather than the pointer which we will free. + */ +#if ATHN_API + if (an->sta_index != 0) + ath_usb_do_async(usc, ath_usb_node_free_cb, + &an->sta_index, sizeof(an->sta_index)); +#endif + usc->sc_node_free(ic, ni); +} + +void +ath_usb_node_free_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ieee80211com *ic = &usc->sc_sc->sc_ic; +#if OpenBSD_IEEE80211_API + struct ifnet *ifp = &ic->ic_if; +#endif + uint8_t sta_index = *(uint8_t *)arg; + int error; + + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, + &sta_index, sizeof(sta_index), NULL); + if (error) { + printf("%s: could not remove station %u from firmware table\n", + device_get_name(usc->usb_dev), sta_index); + return; + } + usc->free_node_slots |= (1 << sta_index); +#if OpenBSD_IEEE80211_API + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %u removed from firmware table\n", + device_get_name(usc->usb_dev), sta_index); +#endif +} +#endif /* IEEE80211_STA_ONLY */ + +int +ath_usb_ampdu_tx_start(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + struct ath_node *an = (struct ath_node *)ni; + struct ath_usb_aggr_cmd cmd; +#if ATHN_API + /* Do it in a process context. */ + cmd.sta_index = an->sta_index; +#endif + cmd.tid = tid; + ath_usb_do_async(usc, ath_usb_ampdu_tx_start_cb, &cmd, sizeof(cmd)); + return (0); +} + +void +ath_usb_ampdu_tx_start_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ath_usb_aggr_cmd *cmd = arg; + struct ar_htc_target_aggr aggr; + + memset(&aggr, 0, sizeof(aggr)); + aggr.sta_index = cmd->sta_index; + aggr.tidno = cmd->tid; + aggr.aggr_enable = 1; + (void)ath_usb_wmi_xcmd(usc, AR_WMI_CMD_TX_AGGR_ENABLE, + &aggr, sizeof(aggr), NULL); +} + +void +ath_usb_ampdu_tx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + struct ath_node *an = (struct ath_node *)ni; + struct ath_usb_aggr_cmd cmd; + + /* Do it in a process context. */ +#if ATHN_API + cmd.sta_index = an->sta_index; +#endif + cmd.tid = tid; + ath_usb_do_async(usc, ath_usb_ampdu_tx_stop_cb, &cmd, sizeof(cmd)); +} + +void +ath_usb_ampdu_tx_stop_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ath_usb_aggr_cmd *cmd = arg; + struct ar_htc_target_aggr aggr; + + memset(&aggr, 0, sizeof(aggr)); + aggr.sta_index = cmd->sta_index; + aggr.tidno = cmd->tid; + aggr.aggr_enable = 0; + (void)ath_usb_wmi_xcmd(usc, AR_WMI_CMD_TX_AGGR_ENABLE, + &aggr, sizeof(aggr), NULL); +} + +#ifndef IEEE80211_STA_ONLY +/* Try to find a node we can evict to make room in the firmware table. */ +void +ath_usb_clean_nodes(void *arg, struct ieee80211_node *ni) +{ + struct ath_usb_softc *usc = arg; + struct ieee80211com *ic = &usc->sc_sc->sc_ic; + struct ath_node *an = (struct ath_node *)ni; + + /* + * Don't remove the default node (used for management frames). + * Nodes which are not in the firmware table also have index zero. + */ +#if ATHN_API + if (an->sta_index == 0) + return; +#endif +#if OpenBSD_IEEE80211_API + /* Remove non-associated nodes. */ + if (ni->ni_state != IEEE80211_STA_AUTH && + ni->ni_state != IEEE80211_STA_ASSOC) { + ath_usb_remove_node(usc, ni); + return; + } + + /* + * Kick off inactive associated nodes. This won't help + * immediately but will help if the new STA retries later. + */ + if (ni->ni_inact >= IEEE80211_INACT_MAX) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_EXPIRE); + ieee80211_node_leave(ic, ni); + } +#endif +} +#endif + +int +ath_usb_create_node(struct ath_usb_softc *usc, struct ieee80211_node *ni) +{ + struct ath_node *an = (struct ath_node *)ni; + struct ar_htc_target_sta sta; + int error, sta_index; +#ifndef IEEE80211_STA_ONLY + struct ieee80211com *ic = &usc->sc_sc->sc_ic; +#if OpenBSD_IEEE80211_API + struct ifnet *ifp = &ic->ic_if; +#endif + + /* Firmware cannot handle more than 8 STAs. Try to make room first. */ +#if OpenBSD_IEEE80211_API + if (ic->ic_opmode == IEEE80211_M_HOSTAP) + ieee80211_iterate_nodes(ic, ath_usb_clean_nodes, usc); +#endif +#endif + if (usc->free_node_slots == 0x00) + return ENOBUFS; + + sta_index = ffs(usc->free_node_slots) - 1; + if (sta_index < 0 || sta_index >= AR_USB_MAX_STA) + return ENOSPC; + + /* Create node entry on target. */ + memset(&sta, 0, sizeof(sta)); + IEEE80211_ADDR_COPY(sta.macaddr, ni->ni_macaddr); + IEEE80211_ADDR_COPY(sta.bssid, ni->ni_bssid); + sta.sta_index = sta_index; + sta.maxampdu = 0xffff; + if (ni->ni_flags & IEEE80211_NODE_HT) + sta.flags |= htobe16(AR_HTC_STA_HT); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_CREATE, + &sta, sizeof(sta), NULL); + if (error != 0) + return (error); +#if ATHN_API + an->sta_index = sta_index; + usc->free_node_slots &= ~(1 << an->sta_index); +#endif + +#if OpenBSD_IEEE80211_API +#ifndef IEEE80211_STA_ONLY + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %u (%s) added to firmware table\n", + device_get_name(usc->usb_dev), sta_index, + ether_sprintf(ni->ni_macaddr)); +#endif +#endif + return ath_usb_node_set_rates(usc, ni); +} + +int +ath_usb_node_set_rates(struct ath_usb_softc *usc, struct ieee80211_node *ni) +{ + struct ath_node *an = (struct ath_node *)ni; + struct ar_htc_target_rate rate; + int i, j; + + /* Setup supported rates. */ + memset(&rate, 0, sizeof(rate)); +#if ATHN_API + rate.sta_index = an->sta_index; +#endif + rate.isnew = 1; + rate.lg_rates.rs_nrates = ni->ni_rates.rs_nrates; + memcpy(rate.lg_rates.rs_rates, ni->ni_rates.rs_rates, + ni->ni_rates.rs_nrates); + if (ni->ni_flags & IEEE80211_NODE_HT) { + rate.capflags |= htobe32(AR_RC_HT_FLAG); + /* Setup HT rates. */ + #if OpenBSD_IEEE80211_API + for (i = 0, j = 0; i < IEEE80211_HT_NUM_MCS; i++) { + if (!isset(ni->ni_rxmcs, i)) + continue; + if (j >= AR_HTC_RATE_MAX) + break; + rate.ht_rates.rs_rates[j++] = i; + } + #endif + rate.ht_rates.rs_nrates = j; + +#if OpenBSD_IEEE80211_API + if (ni->ni_rxmcs[1]) /* dual-stream MIMO rates */ + rate.capflags |= htobe32(AR_RC_DS_FLAG); +#endif +#ifdef notyet + if (ni->ni_htcaps & IEEE80211_HTCAP_CBW20_40) + rate.capflags |= htobe32(AR_RC_40_FLAG); + if (ni->ni_htcaps & IEEE80211_HTCAP_SGI40) + rate.capflags |= htobe32(AR_RC_SGI_FLAG); + if (ni->ni_htcaps & IEEE80211_HTCAP_SGI20) + rate.capflags |= htobe32(AR_RC_SGI_FLAG); +#endif + } + + return ath_usb_wmi_xcmd(usc, AR_WMI_CMD_RC_RATE_UPDATE, + &rate, sizeof(rate), NULL); +} + +int +ath_usb_remove_node(struct ath_usb_softc *usc, struct ieee80211_node *ni) +{ + struct ath_node *an = (struct ath_node *)ni; + int error; +#ifndef IEEE80211_STA_ONLY + struct ieee80211com *ic = &usc->sc_sc->sc_ic; +#if OpenBSD_IEEE80211_API + struct ifnet *ifp = &ic->ic_if; +#endif +#endif +#if ATHN_API + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, + &an->sta_index, sizeof(an->sta_index), NULL); + if (error) { + printf("%s: could not remove station %u (%s) from " + "firmware table\n", device_get_name(usc->usb_dev), an->sta_index, + ether_sprintf(ni->ni_macaddr)); + return error; + } +#endif +#if OpenBSD_IEEE80211_API +#ifndef IEEE80211_STA_ONLY + if (ifp->if_flags & IFF_DEBUG) + printf("%s: station %u (%s) removed from firmware table\n", + device_get_name(usc->usb_dev), an->sta_index, + ether_sprintf(ni->ni_macaddr)); +#endif +#endif +#if ATHN_API + usc->free_node_slots |= (1 << an->sta_index); + an->sta_index = 0; +#endif + return 0; +} + +#if ATHN_API +void +ath_usb_rx_enable(struct ath_softc *sc) +{ + AR_WRITE(sc, AR_CR, AR_CR_RXE); + AR_WRITE_BARRIER(sc); +} + +int +ath_usb_switch_chan(struct ath_softc *sc, struct ieee80211_channel *c, + struct ieee80211_channel *extc) +{ + struct ath_usb_softc *usc = sc->usc; + uint16_t mode; + int error; + + /* Disable interrupts. */ + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR); + if (error != 0) + goto reset; + /* Stop all Tx queues. */ + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL); + if (error != 0) + goto reset; + /* Stop Rx. */ + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_STOP_RECV); + if (error != 0) + goto reset; + + /* If band or bandwidth changes, we need to do a full reset. */ + if (c->ic_flags != sc->curchan->ic_flags || + ((extc != NULL) ^ (sc->curchanext != NULL))) { + DPRINTFN(2, ("channel band switch\n")); + goto reset; + } + + error = ath_set_chan(sc, c, extc); + if (AR_SREV_9271(sc) && error == 0) + ar9271_load_ani(sc); + if (error != 0) { + reset: /* Error found, try a full reset. */ + DPRINTFN(3, ("needs a full reset\n")); + error = ath_hw_reset(sc, c, extc, 0); + if (error != 0) /* Hopeless case. */ + return (error); + + error = ath_set_chan(sc, c, extc); + if (AR_SREV_9271(sc) && error == 0) + ar9271_load_ani(sc); + if (error != 0) + return (error); + } + + sc->ops.set_txpower(sc, c, extc); + + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV); + if (error != 0) + return (error); + ath_rx_start(sc); + + mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ? + AR_HTC_MODE_11NG : AR_HTC_MODE_11NA); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE, + &mode, sizeof(mode), NULL); + if (error != 0) + return (error); + + /* Re-enable interrupts. */ + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_ENABLE_INTR); + return (error); +} +#endif + +void +ath_usb_updateedca(struct ieee80211com *ic) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + + /* Do it in a process context. */ + ath_usb_do_async(usc, ath_usb_updateedca_cb, NULL, 0); +} + +void +ath_usb_updateedca_cb(struct ath_usb_softc *usc, void *arg) +{ + int s; + + s = splnet(); +#if ATHN_API + ath_updateedca(&usc->sc_sc->sc_ic); +#endif + splx(s); +} + +void +ath_usb_updateslot(struct ieee80211com *ic) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + + return; /* XXX */ + /* Do it in a process context. */ + ath_usb_do_async(usc, ath_usb_updateslot_cb, NULL, 0); +} + +void +ath_usb_updateslot_cb(struct ath_usb_softc *usc, void *arg) +{ + int s; + + s = splnet(); +#if ATHN_API + ath_updateslot(&usc->sc_sc->sc_ic); +#endif + splx(s); +} + +int +ath_usb_set_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + struct ath_usb_cmd_key cmd; + + /* Defer setting of WEP keys until interface is brought up. */ +#if OpenBSD_IEEE80211_API + if ((ic->ic_if.if_flags & (IFF_UP | IFF_RUNNING)) != + (IFF_UP | IFF_RUNNING)) + return (0); +#endif + + /* Do it in a process context. */ + cmd.ni = (ni != NULL) ? ieee80211_ref_node(ni) : NULL; + cmd.key = k; + ath_usb_do_async(usc, ath_usb_set_key_cb, &cmd, sizeof(cmd)); + usc->sc_key_tasks++; + return EBUSY; +} + +void +ath_usb_set_key_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ieee80211com *ic = &usc->sc_sc->sc_ic; + struct ath_usb_cmd_key *cmd = arg; + int s; + + usc->sc_key_tasks--; + + s = splnet(); + ath_usb_write_barrier(usc->sc_sc); +#if ATHN_API + ath_set_key(ic, cmd->ni, cmd->key); +#endif +#if OpenBSD_IEEE80211_API + if (usc->sc_key_tasks == 0) { + DPRINTF(("marking port %s valid\n", + ether_sprintf(cmd->ni->ni_macaddr))); + cmd->ni->ni_port_valid = 1; + ieee80211_set_link_state(ic, LINK_STATE_UP); + } + if (cmd->ni != NULL) + ieee80211_release_node(ic, cmd->ni); + splx(s); +#endif +} + +void +ath_usb_delete_key(struct ieee80211com *ic, struct ieee80211_node *ni, + struct ieee80211_key *k) +{ + struct ath_softc *sc = ic->ic_softc; + struct ath_usb_softc *usc = sc->usc; + struct ath_usb_cmd_key cmd; +#if OpenBSD_IEEE80211_API + if (!(ic->ic_if.if_flags & IFF_RUNNING) || + ic->ic_state != IEEE80211_S_RUN) + return; /* Nothing to do. */ +#endif + /* Do it in a process context. */ + cmd.ni = (ni != NULL) ? ieee80211_ref_node(ni) : NULL; + cmd.key = k; + ath_usb_do_async(usc, ath_usb_delete_key_cb, &cmd, sizeof(cmd)); +} + +void +ath_usb_delete_key_cb(struct ath_usb_softc *usc, void *arg) +{ + struct ieee80211com *ic = &usc->sc_sc->sc_ic; + struct ath_usb_cmd_key *cmd = arg; + int s; + + s = splnet(); +#if ATHN_API + ath_delete_key(ic, cmd->ni, cmd->key); +#endif +#if OpenBSD_IEEE80211_API + if (cmd->ni != NULL) + ieee80211_release_node(ic, cmd->ni); + splx(s); +#endif +} + +#ifndef IEEE80211_STA_ONLY +void +ath_usb_bcneof(struct usb_xfer *xfer, void *priv, + usb_error_t status) +{ + struct ath_usb_data *data = priv; +#if OpenBSD_ONLY + struct ath_usb_softc *usc = data->sc->usc; + + if (__predict_false(status == USB_ERR_STALLED)) + usbd_clear_endpoint_stall_async(usc->tx_data_pipe); + usc->tx_bcn = data; +#endif +} + +/* + * Process Software Beacon Alert interrupts. + */ +void +ath_usb_swba(struct ath_usb_softc *usc) +{ + struct ath_softc *sc = usc->sc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct ath_usb_data *data; + struct ieee80211_frame *wh; + struct ar_stream_hdr *hdr; + struct ar_htc_frame_hdr *htc; + struct ar_tx_bcn *bcn; + struct mbuf *m; + int error; +#if OpenBSD_IEEE80211_API + if (ic->ic_dtim_count == 0) + ic->ic_dtim_count = ic->ic_dtim_period - 1; + else + ic->ic_dtim_count--; +#endif +#if OpenBSD_ONLY + /* Make sure previous beacon has been sent. */ + if (usc->tx_bcn == NULL) + return; + data = usc->tx_bcn; +#endif +#if OpenBSD_IEEE80211_API + /* Get new beacon. */ + m = ieee80211_beacon_alloc(ic, ic->ic_bss); + if (__predict_false(m == NULL)) + return; + /* Assign sequence number. */ + wh = mtod(m, struct ieee80211_frame *); + *(uint16_t *)&wh->i_seq[0] = + htole16(ic->ic_bss->ni_txseq << IEEE80211_SEQ_SEQ_SHIFT); + ic->ic_bss->ni_txseq++; + + hdr = (struct ar_stream_hdr *)data->buf; + hdr->tag = htole16(AR_USB_TX_STREAM_TAG); + hdr->len = htole16(sizeof(*htc) + sizeof(*bcn) + m->m_pkthdr.len); + + htc = (struct ar_htc_frame_hdr *)&hdr[1]; + memset(htc, 0, sizeof(*htc)); + htc->endpoint_id = usc->ep_bcn; + htc->payload_len = htobe16(sizeof(*bcn) + m->m_pkthdr.len); + + bcn = (struct ar_tx_bcn *)&htc[1]; + memset(bcn, 0, sizeof(*bcn)); + bcn->vif_idx = 0; + + m_copydata(m, 0, m->m_pkthdr.len, &bcn[1]); +#endif +#if OpenBSD_ONLY + usbd_setup_xfer(data->xfer, usc->tx_data_pipe, data, data->buf, + sizeof(*hdr) + sizeof(*htc) + sizeof(*bcn) + m->m_pkthdr.len, + USBD_SHORT_XFER_OK | USBD_NO_COPY, ath_USB_TX_TIMEOUT, + ath_usb_bcneof); + + m_freem(m); + usc->tx_bcn = NULL; + error = usbd_transfer_start(data->xfer); + if (__predict_false(error != USBD_IN_PROGRESS && error != 0)) + usc->tx_bcn = data; +#endif +} +#endif + +/* Update current transmit rate for a node based on firmware Tx status. */ +void +ath_usb_tx_status(void *arg, struct ieee80211_node *ni) +{ + struct ar_wmi_evt_txstatus *ts = arg; + struct ath_node *an = (struct ath_node *)ni; + uint8_t rate_index = (ts->rate & AR_HTC_TXSTAT_RATE); +#if ATHN_API + if (an->sta_index != ts->cookie) /* Tx report for a different node */ + return; +#endif +#if OpenBSD_IEEE80211_API + if (ts->flags & AR_HTC_TXSTAT_MCS) { + if (isset(ni->ni_rxmcs, rate_index)) + ni->ni_txmcs = rate_index; + } else if (rate_index < ni->ni_rates.rs_nrates) + ni->ni_txrate = rate_index; +#endif +} + +void +ath_usb_rx_wmi_ctrl(struct ath_usb_softc *usc, uint8_t *buf, int len) +{ + struct ar_wmi_cmd_hdr *wmi; + uint16_t cmd_id; + + if (__predict_false(len < sizeof(*wmi))) + return; + wmi = (struct ar_wmi_cmd_hdr *)buf; + cmd_id = be16toh(wmi->cmd_id); + + if (!(cmd_id & AR_WMI_EVT_FLAG)) { + if (usc->wait_cmd_id != cmd_id) + return; /* Unexpected reply. */ + if (usc->obuf != NULL) { + /* Copy answer into caller supplied buffer. */ + memcpy(usc->obuf, &wmi[1], len - sizeof(*wmi)); + } + /* Notify caller of completion. */ + wakeup(&usc->wait_cmd_id); + return; + } + switch (cmd_id & 0xfff) { +#ifndef IEEE80211_STA_ONLY + case AR_WMI_EVT_SWBA: + ath_usb_swba(usc); + break; +#endif + case AR_WMI_EVT_TXSTATUS: { + struct ar_wmi_evt_txstatus_list *tsl; + int i; + + tsl = (struct ar_wmi_evt_txstatus_list *)&wmi[1]; + for (i = 0; i < tsl->count && i < nitems(tsl->ts); i++) { + struct ieee80211com *ic = &usc->sc_sc->sc_ic; +#if OpenBSD_IEEE80211_API + struct ath_node *an = (struct ath_node *)ic->ic_bss; +#endif + struct ar_wmi_evt_txstatus *ts = &tsl->ts[i]; + uint8_t qid; + + /* Skip the node we use to send management frames. */ + if (ts->cookie == 0) + continue; + + /* Skip Tx reports for non-data frame endpoints. */ + qid = (ts->rate & AR_HTC_TXSTAT_EPID) >> + AR_HTC_TXSTAT_EPID_SHIFT; + if (qid != usc->ep_data[ATH_QID_AC_BE] && + qid != usc->ep_data[ATH_QID_AC_BK-1] && + qid != usc->ep_data[ATH_QID_AC_VI-1] && + qid != usc->ep_data[ATH_QID_AC_VO-1]) + continue; +#if OpenBSD_IEEE80211_API + if (ts->cookie == an->sta_index) + ath_usb_tx_status(ts, ic->ic_bss); + else + ieee80211_iterate_nodes(ic, ath_usb_tx_status, + ts); +#endif + } + break; + } + case AR_WMI_EVT_FATAL: + printf("%s: fatal firmware error\n", device_get_name(usc->usb_dev)); + break; + default: + DPRINTF(("WMI event %d ignored\n", cmd_id)); + break; + } +} + +#if NBPFILTER > 0 +void +ath_usb_rx_radiotap(struct ath_softc *sc, struct mbuf *m, + struct ar_rx_status *rs) +{ +#define IEEE80211_RADIOTAP_F_SHORTGI 0x80 /* XXX from FBSD */ + + struct ath_rx_radiotap_header *tap = &sc->sc_rxtap; + struct ieee80211com *ic = &sc->sc_ic; + struct mbuf mb; + uint8_t rate; + + tap->wr_flags = IEEE80211_RADIOTAP_F_FCS; + tap->wr_tsft = htole64(be64toh(rs->rs_tstamp)); + tap->wr_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); + tap->wr_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); + tap->wr_dbm_antsignal = rs->rs_rssi; + /* XXX noise. */ + tap->wr_antenna = rs->rs_antenna; + tap->wr_rate = 0; /* In case it can't be found below. */ + rate = rs->rs_rate; + if (rate & 0x80) { /* HT. */ + /* Bit 7 set means HT MCS instead of rate. */ + tap->wr_rate = rate; + if (!(rs->rs_flags & AR_RXS_FLAG_GI)) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI; + + } else if (rate & 0x10) { /* CCK. */ + if (rate & 0x04) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + switch (rate & ~0x14) { + case 0xb: tap->wr_rate = 2; break; + case 0xa: tap->wr_rate = 4; break; + case 0x9: tap->wr_rate = 11; break; + case 0x8: tap->wr_rate = 22; break; + } + } else { /* OFDM. */ + switch (rate) { + case 0xb: tap->wr_rate = 12; break; + case 0xf: tap->wr_rate = 18; break; + case 0xa: tap->wr_rate = 24; break; + case 0xe: tap->wr_rate = 36; break; + case 0x9: tap->wr_rate = 48; break; + case 0xd: tap->wr_rate = 72; break; + case 0x8: tap->wr_rate = 96; break; + case 0xc: tap->wr_rate = 108; break; + } + } + mb.m_data = (caddr_t)tap; + mb.m_len = sc->sc_rxtap_len; + mb.m_next = m; + mb.m_nextpkt = NULL; + mb.m_type = 0; + mb.m_flags = 0; + bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_IN); +} +#endif + + +void +ath_usb_rx_frame(struct ath_usb_softc *usc, struct mbuf *m/*, + struct mbuf_list *ml*/) +{ +#if OpenBSD_ONLY + struct ath_softc *sc = usc->sc_sc; + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = &ic->ic_if; + struct ieee80211_frame *wh; + struct ieee80211_node *ni; + struct ieee80211_rxinfo rxi; + + struct ar_htc_frame_hdr *htc; + struct ar_rx_status *rs; + uint16_t datalen; + int s; + + if (__predict_false(m->m_len < sizeof(*htc))) + goto skip; + htc = mtod(m, struct ar_htc_frame_hdr *); + if (__predict_false(htc->endpoint_id == 0)) { + DPRINTF(("bad endpoint %d\n", htc->endpoint_id)); + goto skip; + } + if (htc->flags & AR_HTC_FLAG_TRAILER) { + if (m->m_len < htc->control[0]) + goto skip; + m_adj(m, -(int)htc->control[0]); + } + m_adj(m, sizeof(*htc)); /* Strip HTC header. */ + + if (__predict_false(m->m_len < sizeof(*rs))) + goto skip; + rs = mtod(m, struct ar_rx_status *); + + /* Make sure that payload fits. */ + datalen = be16toh(rs->rs_datalen); + if (__predict_false(m->m_len < sizeof(*rs) + datalen)) + goto skip; + + if (__predict_false(datalen < sizeof(*wh) + IEEE80211_CRC_LEN)) + goto skip; + + if (rs->rs_status != 0) { + if (rs->rs_status & AR_RXS_RXERR_DECRYPT) + ic->ic_stats.is_ccmp_dec_errs++; + goto skip; + } + + m_adj(m, sizeof(*rs)); /* Strip Rx status. */ + + s = splnet(); + + /* Grab a reference to the source node. */ + + wh = mtod(m, struct ieee80211_frame *); + ni = ieee80211_find_rxnode(ic, wh); + + /* Remove any HW padding after the 802.11 header. */ + if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL)) { + + u_int hdrlen = ieee80211_get_hdrlen(wh); + if (hdrlen & 3) { + memmove((caddr_t)wh + 2, wh, hdrlen); + m_adj(m, 2); + } + wh = mtod(m, struct ieee80211_frame *); + } + +#if NBPFILTER > 0 + if (__predict_false(sc->sc_drvbpf != NULL)) + ath_usb_rx_radiotap(sc, m, rs); +#endif + /* Trim 802.11 FCS after radiotap. */ + m_adj(m, -IEEE80211_CRC_LEN); + + /* Send the frame to the 802.11 layer. */ + + memset(&rxi, 0, sizeof(rxi)); + rxi.rxi_rssi = rs->rs_rssi + AR_USB_DEFAULT_NF; + rxi.rxi_tstamp = be64toh(rs->rs_tstamp); + + if (!(wh->i_fc[0] & IEEE80211_FC0_TYPE_CTL) && + (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_flags & IEEE80211_NODE_RXPROT) && + (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP || + (IEEE80211_IS_MULTICAST(wh->i_addr1) && + ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP))) { + if (ar5008_ccmp_decap(sc, m, ni) != 0) { + ieee80211_release_node(ic, ni); + splx(s); + goto skip; + } + rxi.rxi_flags |= IEEE80211_RXI_HWDEC; + } + ieee80211_inputm(ifp, m, ni, &rxi, ml); + + + /* Node is no longer needed. */ + ieee80211_release_node(ic, ni); + + splx(s); + return; + skip: + m_freem(m); +#endif +} + +void +ath_usb_rxeof(struct usb_xfer *xfer, struct ath_usb_data *data) +{ +#if OpenBSD_ONLY + struct mbuf_list ml = MBUF_LIST_INITIALIZER(); +#endif + struct ath_usb_softc *usc = usbd_xfer_softc(xfer); + struct ath_softc *sc = usc->sc_sc; +#if OpenBSD_IEEE80211_API + struct ifnet *ifp = &sc->sc_ic.ic_if; +#endif + struct ath_usb_rx_stream *stream = &usc->rx_stream; + char *buf = data->buf; + struct ar_stream_hdr *hdr; + struct mbuf *m; + uint16_t pktlen; + int off; + int32_t len; + +#if OpenBSD_ONLY + if (__predict_false(status != USBD_NORMAL_COMPLETION)) { + DPRINTF(("RX status=%d\n", status)); + if (status == USBD_STALLED) + usbd_clear_endpoint_stall_async(usc->rx_data_pipe); + if (status != USBD_CANCELLED) + goto resubmit; + return; + } +#endif + usbd_xfer_status(xfer, NULL, NULL, &len, NULL); + + if (stream->left > 0) { + if (len >= stream->left) { + /* We have all our pktlen bytes now. */ + if (__predict_true(stream->m != NULL)) { + memcpy(mtod(stream->m, uint8_t *) + + stream->moff, buf, stream->left); + ath_usb_rx_frame(usc, stream->m/*, &ml*/); // MichalP: mbuf_list doesn't exist in FreeBSD + stream->m = NULL; + } + /* Next header is 32-bit aligned. */ + off = (stream->left + 3) & ~3; + buf += off; + len -= off; + stream->left = 0; + } else { + /* Still need more bytes, save what we have. */ + if (__predict_true(stream->m != NULL)) { + memcpy(mtod(stream->m, uint8_t *) + + stream->moff, buf, len); + stream->moff += len; + } + stream->left -= len; + goto resubmit; + } + } + KASSERT(stream->left == 0, "ath_usb_rxeof"); + while (len >= sizeof(*hdr)) { + hdr = (struct ar_stream_hdr *)buf; + if (hdr->tag != htole16(AR_USB_RX_STREAM_TAG)) { + DPRINTF(("invalid tag 0x%x\n", hdr->tag)); + break; + } + pktlen = le16toh(hdr->len); + buf += sizeof(*hdr); + len -= sizeof(*hdr); +#if OpenBSD_ONLY // MichalP: mbuf_list operations needs proper refactoring + if (__predict_true(pktlen <= MCLBYTES)) { + /* Allocate an mbuf to store the next pktlen bytes. */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (__predict_true(m != NULL)) { + m->m_pkthdr.len = m->m_len = pktlen; + if (pktlen > MHLEN) { + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_free(m); + m = NULL; + } + } + } + } else /* Drop frames larger than MCLBYTES. */ + m = NULL; +#endif + /* + * NB: m can be NULL, in which case the next pktlen bytes + * will be discarded from the Rx stream. + */ + if (pktlen > len) { + /* Need more bytes, save what we have. */ + stream->m = m; /* NB: m can be NULL. */ + if (__predict_true(stream->m != NULL)) { + memcpy(mtod(stream->m, uint8_t *), buf, len); + stream->moff = len; + } + stream->left = pktlen - len; + goto resubmit; + } + if (__predict_true(m != NULL)) { + /* We have all the pktlen bytes in this xfer. */ + memcpy(mtod(m, uint8_t *), buf, pktlen); + ath_usb_rx_frame(usc, m/*, &ml*/); // MichalP: Another mbuf_list + } + + /* Next header is 32-bit aligned. */ + off = (pktlen + 3) & ~3; + buf += off; + len -= off; + } +#if OpenBSD_IEEE80211_API + // TODO Refactor functions which use mbuf_list. + // mbuf_list is OpenBSD specific and if_input uses the mbuf directly. + // In the next steps, we should remove mbuf_list support from this driver. + if_input(ifp, m); +#endif + + resubmit: + printf("Resubmit called"); // MichalP: Temp printf call to maintain resubmit goto + /* Setup a new transfer. */ +#if OpenBSD_ONLY + usbd_setup_xfer(xfer, usc->rx_data_pipe, data, data->buf, + ath_USB_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, ath_usb_rxeof); + (void)usbd_transfer_start(xfer); +#endif +} + +void +ath_usb_txeof(struct usb_xfer *xfer, struct ath_usb_data* data) +{ + struct ath_usb_softc *usc = usbd_xfer_softc(xfer); + struct ath_softc *sc = usc->sc_sc; +#if OpenBSD_IEEE80211_API + struct ifnet *ifp = &sc->sc_ic.ic_if; +#endif + + device_printf(sc->sc_dev, "%s: called; data=%p\n", __func__, data); + + ATH_USB_LOCK_ASSERT(sc); + + if (usc->sc_tx_n_active == 0) { + device_printf(sc->sc_dev, + "%s: completed but tx_active=0\n", + __func__); + } else { + usc->sc_tx_n_active--; + } + +// if (data->m) { + /* XXX status? */ + /* XXX we get TX status via the RX path.. */ +// ieee80211_tx_complete(data->ni, data->m, 0); +// data->m = NULL; +// data->ni = NULL; +// } +} + +int +ath_usb_tx(struct ath_softc *sc, struct mbuf *m, struct ieee80211_node *ni) +{ + struct ath_usb_softc *usc = sc->usc; + struct ath_node *an = (struct ath_node *)ni; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_frame *wh; + struct ieee80211_key *k = NULL; + struct ath_usb_data *data; + struct ar_stream_hdr *hdr; + struct ar_htc_frame_hdr *htc; + struct ar_tx_frame *txf; + struct ar_tx_mgmt *txm; + caddr_t frm; + uint16_t qos; + uint8_t qid, tid = 0; + int hasqos, xferlen, error; + + wh = mtod(m, struct ieee80211_frame *); +#if OpenBSD_IEEE80211_API + if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { + k = ieee80211_get_txkey(ic, wh, ni); + if (k->k_cipher == IEEE80211_CIPHER_CCMP) { + u_int hdrlen = ieee80211_get_hdrlen(wh); + if (ar5008_ccmp_encap(m, hdrlen, k) != 0) + return (ENOBUFS); + } else { + if ((m = ieee80211_encrypt(ic, m, k)) == NULL) + return (ENOBUFS); + k = NULL; /* skip hardware crypto further below */ + } + wh = mtod(m, struct ieee80211_frame *); + } + if ((hasqos = ieee80211_has_qos(wh))) { + qos = ieee80211_get_qos(wh); + tid = qos & IEEE80211_QOS_TID; + qid = ieee80211_up_to_ac(ic, tid); + } else + qid = ATH_QID_AC_BE; +#endif + /* Grab a Tx buffer from our free list. */ +#if OpenBSD_ONLY + data = TAILQ_FIRST(&usc->tx_free_list); + TAILQ_REMOVE(&usc->tx_free_list, data, next); +#endif + +#if NBPFILTER > 0 + /* XXX Change radiotap Tx header for USB (no txrate). */ + if (__predict_false(sc->sc_drvbpf != NULL)) { + struct ath_tx_radiotap_header *tap = &sc->sc_txtap; + struct mbuf mb; + + tap->wt_flags = 0; + tap->wt_chan_freq = htole16(ic->ic_bss->ni_chan->ic_freq); + tap->wt_chan_flags = htole16(ic->ic_bss->ni_chan->ic_flags); + mb.m_data = (caddr_t)tap; + mb.m_len = sc->sc_txtap_len; + mb.m_next = m; + mb.m_nextpkt = NULL; + mb.m_type = 0; + mb.m_flags = 0; + bpf_mtap(sc->sc_drvbpf, &mb, BPF_DIRECTION_OUT); + } +#endif + + /* NB: We don't take advantage of USB Tx stream mode for now. */ + hdr = (struct ar_stream_hdr *)data->buf; + hdr->tag = htole16(AR_USB_TX_STREAM_TAG); + + htc = (struct ar_htc_frame_hdr *)&hdr[1]; + memset(htc, 0, sizeof(*htc)); + if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == + IEEE80211_FC0_TYPE_DATA) { + htc->endpoint_id = usc->ep_data[qid]; + + txf = (struct ar_tx_frame *)&htc[1]; + memset(txf, 0, sizeof(*txf)); + txf->data_type = AR_HTC_NORMAL; +#if ATHN_API + txf->node_idx = an->sta_index; +#endif + txf->vif_idx = 0; + txf->tid = tid; +#if OpenBSD_IEEE80211_API + if (m->m_pkthdr.len + IEEE80211_CRC_LEN > ic->ic_rtsthreshold) + txf->flags |= htobe32(AR_HTC_TX_RTSCTS); + else if (ic->ic_flags & IEEE80211_F_USEPROT) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + txf->flags |= htobe32(AR_HTC_TX_CTSONLY); + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + txf->flags |= htobe32(AR_HTC_TX_RTSCTS); + } + + if (k != NULL) { + /* Map 802.11 cipher to hardware encryption type. */ + if (k->k_cipher == IEEE80211_CIPHER_CCMP) { + txf->key_type = AR_ENCR_TYPE_AES; + } else + panic("unsupported cipher"); + /* + * NB: The key cache entry index is stored in the key + * private field when the key is installed. + */ + txf->key_idx = (uintptr_t)k->k_priv; + } else + txf->key_idx = 0xff; +#endif +#if ATHN_API + txf->cookie = an->sta_index; +#endif + frm = (caddr_t)&txf[1]; + } else { + htc->endpoint_id = usc->ep_mgmt; + + txm = (struct ar_tx_mgmt *)&htc[1]; + memset(txm, 0, sizeof(*txm)); +#if ATHN_API + txm->node_idx = an->sta_index; +#endif + txm->vif_idx = 0; + txm->key_idx = 0xff; +#if ATHN_API + txm->cookie = an->sta_index; +#endif + frm = (caddr_t)&txm[1]; + } + /* Copy payload. */ + m_copydata(m, 0, m->m_pkthdr.len, frm); + frm += m->m_pkthdr.len; + m_freem(m); + + /* Finalize headers. */ + htc->payload_len = htobe16(frm - (caddr_t)&htc[1]); + hdr->len = htole16(frm - (caddr_t)&hdr[1]); + xferlen = frm - data->buf; + + data->buflen = xferlen; + + STAILQ_INSERT_TAIL(&usc->sc_tx_pending[ath_BULK_TX], data, next); + usbd_transfer_start(usc->sc_xfer[ath_BULK_TX]); + +#if OpenBSD_IEEE80211_API + ieee80211_release_node(ic, ni); +#endif + return (0); +} + +void +ath_usb_start(struct ifnet *ifp) +{ + struct ath_softc *sc = ifp->if_softc; + struct ath_usb_softc *usc = sc->usc; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni; + struct mbuf *m; + + if (!(ifp->if_flags & IFF_RUNNING) || ifq_is_oactive()) + return; + + for (;;) { +#if OpenBSD_ONLY + if (TAILQ_EMPTY(&usc->tx_free_list)) { + ifq_set_oactive(); + break; + } +#endif + /* Send pending management frames first. */ +#if OpenBSD_IEEE80211_API + m = mq_dequeue(&ic->ic_mgtq); + if (m != NULL) { + ni = m->m_pkthdr.ph_cookie; + goto sendit; + } + if (ic->ic_state != IEEE80211_S_RUN) + break; +#endif + + /* Encapsulate and send data frames. */ + ALTQ_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; +#if NBPFILTER > 0 + f (ifp->if_bpf != NULL) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT); +#endif +#if OpenBSD_IEEE80211_API + if ((m = ieee80211_encap(ifp, m, &ni)) == NULL) + continue; +#endif + sendit: +#if NBPFILTER > 0 + if (ic->ic_rawbpf != NULL) + bpf_mtap(ic->ic_rawbpf, m, BPF_DIRECTION_OUT); +#endif + if (ath_usb_tx(sc, m, ni) != 0) { +#if OpenBSD_IEEE80211_API + ieee80211_release_node(ic, ni); +#endif + continue; + } +#if ATHN_API + sc->sc_tx_timer = 5; +#endif + } +} + +// Comented because there is no sc_tx_timer in the ath_softc structure. +#if ATHN_API +void +ath_usb_watchdog(struct ifnet *ifp) +{ + struct ath_softc *sc = ifp->if_softc; + + if (sc->sc_tx_timer > 0) { + if (--sc->sc_tx_timer == 0) { + printf("%s: device timeout\n", device_get_name(sc->sc_dev)); + ath_usb_init(ifp); + return; + } + } +#if OpenBSD_IEEE80211_API + ieee80211_watchdog(ifp); +#endif +} +#endif + +int +ath_usb_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct ath_softc *sc = ifp->if_softc; + struct ath_usb_softc *usc = sc->usc; + struct ieee80211com *ic = &sc->sc_ic; + int s, error = 0; + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + /* FALLTHROUGH */ + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (!(ifp->if_flags & IFF_RUNNING)) + error = ath_usb_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + ath_usb_stop(ifp); + } + break; +#if OpenBSD_IEEE80211_API + case SIOCS80211CHANNEL: + error = ieee80211_ioctl(ifp, cmd, data); + if (error == ENETRESET && + ic->ic_opmode == IEEE80211_M_MONITOR) { + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + ath_usb_switch_chan(sc, ic->ic_ibss_chan, + NULL); + } + error = 0; + } + break; +#endif + default: + error = ieee80211_ioctl(ifp, cmd, data); + } + + if (error == ENETRESET) { + error = 0; + if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == + (IFF_UP | IFF_RUNNING)) { + ath_usb_stop(ifp); + error = ath_usb_init(ifp); + } + } + return (error); +} + + +int +ath_usb_init(struct ifnet *ifp) +{ +#if ATHN_API + struct ath_softc *sc = ifp->if_softc; + struct ath_usb_softc *usc = sc->usc; + struct ath_ops *ops = &sc->ops; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c, *extc; + struct ath_usb_data *data; + struct ar_htc_target_vif hvif; + struct ar_htc_target_sta sta; + struct ar_htc_cap_target hic; + uint16_t mode; + int i, error; + +#if OpenBSD_IEEE80211_API + c = ic->ic_bss->ni_chan = ic->ic_ibss_chan; + extc = NULL; + + /* In case a new MAC address has been configured. */ + IEEE80211_ADDR_COPY(ic->ic_myaddr, IF_LLADDR(ifp)); +#endif + error = ath_set_power_awake(sc); + if (error != 0) + goto fail; + + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_FLUSH_RECV); + if (error != 0) + goto fail; + + error = ath_hw_reset(sc, c, extc, 1); + if (error != 0) + goto fail; + + ops->set_txpower(sc, c, extc); + + mode = htobe16(IEEE80211_IS_CHAN_2GHZ(c) ? + AR_HTC_MODE_11NG : AR_HTC_MODE_11NA); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_SET_MODE, + &mode, sizeof(mode), NULL); + if (error != 0) + goto fail; + + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_ATH_INIT); + if (error != 0) + goto fail; + + error = ath_usb_wmi_cmd(usc, AR_WMI_CMD_START_RECV); + if (error != 0) + goto fail; + + ath_rx_start(sc); + + /* Create main interface on target. */ + memset(&hvif, 0, sizeof(hvif)); + hvif.index = 0; +#if OpenBSD_IEEE80211_API + IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr); +#endif + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + hvif.opmode = htobe32(AR_HTC_M_STA); + break; + case IEEE80211_M_MONITOR: + hvif.opmode = htobe32(AR_HTC_M_MONITOR); + break; +#ifndef IEEE80211_STA_ONLY + case IEEE80211_M_IBSS: + hvif.opmode = htobe32(AR_HTC_M_IBSS); + break; + case IEEE80211_M_AHDEMO: + hvif.opmode = htobe32(AR_HTC_M_AHDEMO); + break; + case IEEE80211_M_HOSTAP: + hvif.opmode = htobe32(AR_HTC_M_HOSTAP); + break; + case IEEE80211_M_MBSS: + break; + case IEEE80211_M_WDS: + break; +#endif + } +#if OpenBSD_IEEE80211_API + hvif.rtsthreshold = htobe16(ic->ic_rtsthreshold); +#endif + DPRINTF(("creating VAP\n")); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_CREATE, + &hvif, sizeof(hvif), NULL); + if (error != 0) + goto fail; + + /* Create a fake node to send management frames before assoc. */ + memset(&sta, 0, sizeof(sta)); +#if OpenBSD_IEEE80211_API + IEEE80211_ADDR_COPY(sta.macaddr, ic->ic_myaddr); +#endif + sta.sta_index = 0; + sta.is_vif_sta = 1; + sta.vif_index = hvif.index; + sta.maxampdu = 0xffff; + DPRINTF(("creating default node\n")); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_CREATE, + &sta, sizeof(sta), NULL); + if (error != 0) + goto fail; + usc->free_node_slots = ~(1 << sta.sta_index); + + /* Update target capabilities. */ + memset(&hic, 0, sizeof(hic)); + hic.ampdu_limit = htobe32(0x0000ffff); + hic.ampdu_subframes = 20; + hic.txchainmask = sc->txchainmask; + DPRINTF(("updating target configuration\n")); + error = ath_usb_wmi_xcmd(usc, AR_WMI_CMD_TARGET_IC_UPDATE, + &hic, sizeof(hic), NULL); + if (error != 0) + goto fail; + +#if OpenBSD_ONLY + /* Queue Rx xfers. */ + for (i = 0; i < ath_USB_RX_LIST_COUNT; i++) { + data = &usc->rx_data[i]; + usbd_setup_xfer(data->xfer, usc->rx_data_pipe, data, data->buf, + ath_USB_RXBUFSZ, USBD_SHORT_XFER_OK | USBD_NO_COPY, + USBD_NO_TIMEOUT, ath_usb_rxeof); + error = usbd_transfer_start(data->xfer); + if (error != 0 && error != USBD_IN_PROGRESS) + goto fail; + } +#endif + /* We're ready to go. */ + ifp->if_flags |= IFF_RUNNING; + ifq_clr_oactive(); + +#ifdef notyet + if (ic->ic_flags & IEEE80211_F_WEPON) { + /* Install WEP keys. */ + for (i = 0; i < IEEE80211_WEP_NKID; i++) + ath_usb_set_key(ic, NULL, &ic->ic_nw_keys[i]); + } +#endif +#if OpenBSD_IEEE80211_API + if (ic->ic_opmode == IEEE80211_M_MONITOR) + ieee80211_new_state(ic, IEEE80211_S_RUN, -1); + else + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); +#endif + ath_usb_wait_async(usc); + return (0); + fail: + ath_usb_stop(ifp); + return (error); +#else + return 0; +#endif +} + + +void +ath_usb_stop(struct ifnet *ifp) +{ + struct ath_softc *sc = ifp->if_softc; + struct ath_usb_softc *usc = sc->usc; + struct ieee80211com *ic = &sc->sc_ic; + struct ar_htc_target_vif hvif; + uint8_t sta_index; + int s; +#if ATHN_API + sc->sc_tx_timer = 0; +#endif + ifp->if_flags &= ~IFF_RUNNING; + ifq_clr_oactive(); + + s = splusb(); +#if OpenBSD_IEEE80211_API + ieee80211_new_state(ic, IEEE80211_S_INIT, -1); +#endif + + /* Wait for all async commands to complete. */ + ath_usb_wait_async(usc); + +#if OpenBSD_ONLY + timeout_del(&sc->scan_to); + timeout_del(&sc->calib_to); +#endif + + /* Remove all non-default nodes. */ + for (sta_index = 1; sta_index < AR_USB_MAX_STA; sta_index++) { + if (usc->free_node_slots & (1 << sta_index)) + continue; + (void)ath_usb_wmi_xcmd(usc, AR_WMI_CMD_NODE_REMOVE, + &sta_index, sizeof(sta_index), NULL); + } + + /* Remove main interface. This also invalidates our default node. */ + memset(&hvif, 0, sizeof(hvif)); + hvif.index = 0; +#if OpenBSD_IEEE80211_API + IEEE80211_ADDR_COPY(hvif.myaddr, ic->ic_myaddr); +#endif + (void)ath_usb_wmi_xcmd(usc, AR_WMI_CMD_VAP_REMOVE, + &hvif, sizeof(hvif), NULL); + + usc->free_node_slots = 0xff; + + (void)ath_usb_wmi_cmd(usc, AR_WMI_CMD_DISABLE_INTR); + (void)ath_usb_wmi_cmd(usc, AR_WMI_CMD_DRAIN_TXQ_ALL); + (void)ath_usb_wmi_cmd(usc, AR_WMI_CMD_STOP_RECV); + +#if ATHN_API + ath_reset(sc, 0); + ath_init_pll(sc, NULL); + ath_set_power_awake(sc); + ath_reset(sc, 1); + ath_init_pll(sc, NULL); + ath_set_power_sleep(sc); +#endif +#if OpenBSD_ONLY + /* Abort Tx/Rx. */ + usbd_abort_pipe(usc->tx_data_pipe); + usbd_abort_pipe(usc->rx_data_pipe); +#endif + + /* Free Tx/Rx buffers. */ + ath_usb_free_tx_list(usc); + ath_usb_free_rx_list(usc); + splx(s); + + /* Flush Rx stream. */ + m_freem(usc->rx_stream.m); + usc->rx_stream.m = NULL; + usc->rx_stream.left = 0; +} diff --git a/sys/dev/ath/if_ath_usb_def.h b/sys/dev/ath/if_ath_usb_def.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb_def.h @@ -0,0 +1,531 @@ +/* $OpenBSD: if_ath_usb_def.h,v 1.13 2022/01/09 05:43:00 jsg Exp $ */ + +/*- + * Copyright (c) 2011 Damien Bergamini + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ +#ifndef IF_ATH_USB_DEF_H +#define IF_ATH_USB_DEF_H + +/* Maximum number of STAs firmware can handle. */ +#define AR_USB_MAX_STA 8 + +#define AR_USB_DEFAULT_NF (-95) + +/* USB requests. */ +#define AR_FW_DOWNLOAD 0x30 +#define AR_FW_DOWNLOAD_COMP 0x31 + +/* USB endpoints addresses. */ +#define AR_PIPE_TX_DATA (UE_DIR_OUT | 1) +#define AR_PIPE_RX_DATA (UE_DIR_IN | 2) +#define AR_PIPE_RX_INTR (UE_DIR_IN | 3) +#define AR_PIPE_TX_INTR (UE_DIR_OUT | 4) + +/* Wireless module interface commands. */ +#define AR_WMI_CMD_ECHO 0x001 +#define AR_WMI_CMD_ACCESS_MEMORY 0x002 +#define AR_WMI_GET_FW_VERSION 0x003 +#define AR_WMI_CMD_DISABLE_INTR 0x004 +#define AR_WMI_CMD_ENABLE_INTR 0x005 +#define AR_WMI_CMD_ATH_INIT 0x006 +#define AR_WMI_CMD_ABORT_TXQ 0x007 +#define AR_WMI_CMD_STOP_TX_DMA 0x008 +#define AR_WMI_CMD_ABORT_TX_DMA 0x009 +#define AR_WMI_CMD_DRAIN_TXQ 0x00a +#define AR_WMI_CMD_DRAIN_TXQ_ALL 0x00b +#define AR_WMI_CMD_START_RECV 0x00c +#define AR_WMI_CMD_STOP_RECV 0x00d +#define AR_WMI_CMD_FLUSH_RECV 0x00e +#define AR_WMI_CMD_SET_MODE 0x00f +#define AR_WMI_CMD_NODE_CREATE 0x010 +#define AR_WMI_CMD_NODE_REMOVE 0x011 +#define AR_WMI_CMD_VAP_REMOVE 0x012 +#define AR_WMI_CMD_VAP_CREATE 0x013 +#define AR_WMI_CMD_REG_READ 0x014 +#define AR_WMI_CMD_REG_WRITE 0x015 +#define AR_WMI_CMD_RC_STATE_CHANGE 0x016 +#define AR_WMI_CMD_RC_RATE_UPDATE 0x017 +#define AR_WMI_CMD_TARGET_IC_UPDATE 0x018 +#define AR_WMI_CMD_TX_AGGR_ENABLE 0x019 +#define AR_WMI_CMD_TGT_DETACH 0x020 +#define AR_WMI_CMD_NODE_UPDATE 0x021 +#define AR_WMI_CMD_INT_STATS 0x022 +#define AR_WMI_CMD_TX_STATS 0x023 +#define AR_WMI_CMD_RX_STATS 0x024 +#define AR_WMI_CMD_BITRATE_MASK 0x025 +#define AR_WMI_CMD_REG_RMW 0x026 + +/* Wireless module interface events. */ +#define AR_WMI_EVT_TGT_RDY 0x001 +#define AR_WMI_EVT_SWBA 0x002 +#define AR_WMI_EVT_FATAL 0x003 +#define AR_WMI_EVT_TXTO 0x004 +#define AR_WMI_EVT_BMISS 0x005 +#define AR_WMI_EVT_DELBA 0x006 +#define AR_WMI_EVT_TXSTATUS 0x007 + +/* Structure for service AR_SVC_WMI_CONTROL. */ +struct ar_wmi_cmd_hdr { + uint16_t cmd_id; +#define AR_WMI_EVT_FLAG 0x1000 + + uint16_t seq_no; +} __packed; + +/* Values for AR_WMI_CMD_SET_MODE. */ +#define AR_HTC_MODE_11NA 0 +#define AR_HTC_MODE_11NG 1 + +#define AR_MAX_WRITE_COUNT 32 +/* Structure for command AR_WMI_CMD_REG_WRITE. */ +struct ar_wmi_cmd_reg_write { + uint32_t addr; + uint32_t val; +} __packed; + +/* Structure for command AR_WMI_CMD_NODE_{CREATE,REMOVE}. */ +struct ar_htc_target_sta { + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t sta_index; + uint8_t vif_index; + uint8_t is_vif_sta; + uint16_t flags; +#define AR_HTC_STA_AUTH 0x0001 +#define AR_HTC_STA_QOS 0x0002 +#define AR_HTC_STA_ERP 0x0004 +#define AR_HTC_STA_HT 0x0008 + + uint16_t htcap; + uint16_t maxampdu; + uint8_t pad; + + /* Internal state. */ + uint16_t txseqmgmt; + uint16_t iv16; + uint32_t iv32; + void *ni_vap; +} __packed; + +/* Structures for command AR_WMI_CMD_RC_RATE_UPDATE. */ +#define AR_HTC_RATE_MAX 30 +struct ar_htc_rateset { + uint8_t rs_nrates; + uint8_t rs_rates[AR_HTC_RATE_MAX]; +} __packed; + +struct ar_htc_target_rate { + uint8_t sta_index; + uint8_t isnew; + uint8_t pad[2]; + uint32_t capflags; +#define AR_RC_DS_FLAG 0x00000001 +#define AR_RC_40_FLAG 0x00000002 +#define AR_RC_SGI_FLAG 0x00000004 +#define AR_RC_HT_FLAG 0x00000008 +#define AR_RC_STBC_FLAG 0x00000030 /* 2 bits */ +#define AR_RC_WEP_TKIP_FLAG 0x00000100 + + struct ar_htc_rateset lg_rates; + struct ar_htc_rateset ht_rates; +} __packed; + +/* Structure for command AR_WMI_CMD_TX_AGGR_ENABLE. */ +struct ar_htc_target_aggr { + uint8_t sta_index; + uint8_t tidno; + uint8_t aggr_enable; + uint8_t padding; +} __packed; + +/* Structure for command AR_WMI_CMD_VAP_CREATE. */ +struct ar_htc_target_vif { + uint8_t index; + uint32_t opmode; +#define AR_HTC_M_IBSS 0 +#define AR_HTC_M_STA 1 +#define AR_HTC_M_WDS 2 +#define AR_HTC_M_AHDEMO 3 +#define AR_HTC_M_HOSTAP 6 +#define AR_HTC_M_MONITOR 8 + uint8_t myaddr[IEEE80211_ADDR_LEN]; + uint8_t ath_cap; + uint16_t rtsthreshold; + uint8_t pad; + + /* Internal state. */ + int8_t nodeindex; + void *iv_bss; +} __packed; + +/* Structure for command AM_WMI_CMD_TARGET_IC_UPDATE. */ +struct ar_htc_cap_target { + uint32_t ampdu_limit; + uint8_t ampdu_subframes; + uint8_t enable_coex; + uint8_t txchainmask; + uint8_t pad; +} __packed; + +struct ar_wmi_evt_txstatus { + uint8_t cookie; + + /* + * Legacy rates are indicated as rate array indices. + * HT rates are indicated as MCS indices. + */ + uint8_t rate; +#define AR_HTC_TXSTAT_RATE 0x0f +#define AR_HTC_TXSTAT_EPID 0xf0 +#define AR_HTC_TXSTAT_EPID_SHIFT 4 + + uint8_t flags; +#define AR_HTC_TXSTAT_ACK 0x01 +#define AR_HTC_TXSTAT_FILT 0x02 +#define AR_HTC_TXSTAT_RTC_CTS 0x04 +#define AR_HTC_TXSTAT_MCS 0x08 +#define AR_HTC_TXSTAT_CW40 0x10 +#define AR_HTC_TXSTAT_SGI 0x20 +} __packed; + +/* Structure for event AR_WMI_EVT_TXSTATUS. */ +#define AR_HTC_MAX_TX_STATUS 12 +struct ar_wmi_evt_txstatus_list { + uint8_t count; + struct ar_wmi_evt_txstatus ts[AR_HTC_MAX_TX_STATUS]; +} __packed; + +/* HTC header. */ +struct ar_htc_frame_hdr { + uint8_t endpoint_id; + uint8_t flags; +#define AR_HTC_FLAG_NEED_CREDIT_UPDATE 0x01 +#define AR_HTC_FLAG_TRAILER 0x02 +#define AR_HTC_FLAG_CREDIT_REDISTRIBUTION 0x03 + + uint16_t payload_len; + uint8_t control[4]; +} __packed; + +/* Structure for HTC endpoint id 0. */ +struct ar_htc_msg_hdr { + uint16_t msg_id; +#define AR_HTC_MSG_READY 0x0001 +#define AR_HTC_MSG_CONN_SVC 0x0002 +#define AR_HTC_MSG_CONN_SVC_RSP 0x0003 +#define AR_HTC_MSG_SETUP_COMPLETE 0x0004 +#define AR_HTC_MSG_CONF_PIPE 0x0005 +#define AR_HTC_MSG_CONF_PIPE_RSP 0x0006 +/* TODO: Mikolaj F.: Created to wait for result of AR_WMI_GET_FW_VERSION command. + * Consider if every WMI command shall have own id or a 1 common is fine. + */ +#define AR_WMI_CMD_MSG 0x0007 +} __packed; + +/* Structure for services AR_SVC_WMI_DATA_{VO,VI,BE,BK}. */ +struct ar_tx_frame { + uint8_t data_type; +#define AR_HTC_AMPDU 1 +#define AR_HTC_NORMAL 2 + + uint8_t node_idx; + uint8_t vif_idx; + uint8_t tid; + uint32_t flags; +#define AR_HTC_TX_CTSONLY 0x00000001 +#define AR_HTC_TX_RTSCTS 0x00000002 +#define AR_HTC_TX_USE_MIN_RATE 0x00000100 + + uint8_t key_type; + uint8_t key_idx; + uint8_t cookie; + uint8_t pad; +} __packed; + +/* Structure for service AR_SVC_WMI_MGMT. */ +struct ar_tx_mgmt { + uint8_t node_idx; + uint8_t vif_idx; + uint8_t tid; + uint8_t flags; + uint8_t key_type; + uint8_t key_idx; + uint8_t cookie; + uint8_t pad; +} __packed; + +/* Structure for service AR_SVC_WMI_BEACON. */ +struct ar_tx_bcn { + uint8_t len_changed; + uint8_t vif_idx; + uint16_t rev; +} __packed; + +/* Structure for message AR_HTC_MSG_READY. */ +struct ar_htc_msg_ready { + uint16_t credits; + uint16_t credits_size; + uint8_t max_endpoints; + uint8_t reserved; +} __packed; + +/* Structure for message AR_HTC_MSG_CONF_PIPE. */ +struct ar_htc_msg_config_pipe { + uint8_t pipe_id; + uint8_t credits; +} __packed; + +/* Structure for message AR_HTC_MSG_CONN_SVC. */ +struct ar_htc_msg_conn_svc { + uint16_t svc_id; + uint16_t conn_flags; + uint8_t dl_pipeid; + uint8_t ul_pipeid; + uint8_t svc_meta_len; + uint8_t reserved; +} __packed; + +/* Structure for message AR_HTC_MSG_CONN_SVC_RSP. */ +struct ar_htc_msg_conn_svc_rsp { + uint16_t svc_id; + uint8_t status; +#define AR_HTC_SVC_SUCCESS 0 +#define AR_HTC_SVC_NOT_FOUND 1 +#define AR_HTC_SVC_FAILED 2 +#define AR_HTC_SVC_NO_RESOURCES 3 +#define AR_HTC_SVC_NO_MORE_EP 4 + + uint8_t endpoint_id; + uint16_t max_msg_len; + uint8_t svc_meta_len; + uint8_t reserved; +} __packed; + +#define AR_SVC(grp, idx) ((grp) << 8 | (idx)) +#define AR_SVC_IDX(svc) ((svc) & 0xff) +/* Service groups. */ +#define AR_SVC_GRP_RSVD 0 +#define AR_SVC_GRP_WMI 1 +/* Service identifiers for WMI group. */ +#define AR_SVC_WMI_CONTROL AR_SVC(AR_SVC_GRP_WMI, 0) +#define AR_SVC_WMI_BEACON AR_SVC(AR_SVC_GRP_WMI, 1) +#define AR_SVC_WMI_CAB AR_SVC(AR_SVC_GRP_WMI, 2) +#define AR_SVC_WMI_UAPSD AR_SVC(AR_SVC_GRP_WMI, 3) +#define AR_SVC_WMI_MGMT AR_SVC(AR_SVC_GRP_WMI, 4) +#define AR_SVC_WMI_DATA_VO AR_SVC(AR_SVC_GRP_WMI, 5) +#define AR_SVC_WMI_DATA_VI AR_SVC(AR_SVC_GRP_WMI, 6) +#define AR_SVC_WMI_DATA_BE AR_SVC(AR_SVC_GRP_WMI, 7) +#define AR_SVC_WMI_DATA_BK AR_SVC(AR_SVC_GRP_WMI, 8) + +struct ar_stream_hdr { + uint16_t len; + uint16_t tag; +#define AR_USB_RX_STREAM_TAG 0x4e00 +#define AR_USB_TX_STREAM_TAG 0x697e +} __packed __attribute__((aligned(4))); + +#define AR_MAX_CHAINS 3 + +/* Rx descriptor. */ +struct ar_rx_status { + uint64_t rs_tstamp; + uint16_t rs_datalen; + uint8_t rs_status; +#define AR_RXS_RXERR_CRC 0x01 +#define AR_RXS_RXERR_PHY 0x02 +#define AR_RXS_RXERR_FIFO 0x04 +#define AR_RXS_RXERR_DECRYPT 0x08 +#define AR_RXS_RXERR_MIC 0x10 + uint8_t rs_phyerr; + int8_t rs_rssi; + int8_t rs_rssi_ctl[AR_MAX_CHAINS]; + int8_t rs_rssi_ext[AR_MAX_CHAINS]; + uint8_t rs_keyix; + uint8_t rs_rate; + uint8_t rs_antenna; + uint8_t rs_more; + uint8_t rs_isaggr; + uint8_t rs_moreaggr; + uint8_t rs_num_delims; + uint8_t rs_flags; +#define AR_RXS_FLAG_GI 0x04 +#define AR_RXS_FLAG_2040 0x08 + + uint8_t rs_dummy; + uint32_t rs_evm[AR_MAX_CHAINS]; +} __packed __attribute__((aligned(4))); + +/* Structure to read major and minor version via WMI */ +struct ar_wmi_fw_version { + uint16_t major; + uint16_t minor; +} __packed; + +/* + * Driver definitions. + */ +enum { + ath_BULK_TX, + ath_BULK_RX, + ath_BULK_IRQ, + ath_BULK_CMD, + ATH_N_XFER +}; + +#define ath_USB_RX_LIST_COUNT 1 +#define ath_USB_TX_LIST_COUNT (8 + 1) /* NB: +1 for beacons. */ + +#define ATH_USB_HOST_CMD_RING_COUNT 32 + +#define ath_USB_RXBUFSZ (8 * 1024) /* XXX Linux 16K */ +#define ath_USB_TXBUFSZ \ + ((sizeof(struct ar_stream_hdr) + \ + sizeof(struct ar_htc_frame_hdr) + \ + sizeof(struct ar_tx_frame) + \ + IEEE80211_MAX_LEN + 3) & ~3) +#define ATH_USB_TXCMDSZ 512 + +#define ath_USB_TX_TIMEOUT 5000 /* ms */ +#define ath_USB_CMD_TIMEOUT 1000 /* ms */ + +struct ath_usb_softc; + +struct ath_usb_rx_stream { + struct mbuf *m; + int moff; + int left; +}; + +struct ath_usb_rx_data { + struct ath_usb_softc *sc; + struct usb_xfer *xfer; + char *buf; +}; + +#if OpenBSD_ONLY +struct ath_usb_tx_data { + struct ath_usb_softc *sc; + struct usb_xfer *xfer; + char *buf; + TAILQ_ENTRY(ath_usb_tx_data) next; +}; +#endif +struct ath_usb_data { + struct ath_usb_softc *usc; + struct usb_xfer *xfer; + char *buf; + usb_frlength_t buflen; + STAILQ_ENTRY(ath_usb_data) next; +}; + +struct ath_usb_host_cmd { + void (*cb)(struct ath_usb_softc *, void *); + char data[256]; +}; + +struct ath_usb_cmd_newstate { + enum ieee80211_state state; + int arg; +}; + +struct ath_usb_cmd_key { + struct ieee80211_node *ni; + struct ieee80211_key *key; +}; + +struct ath_usb_aggr_cmd { + int8_t sta_index; + int8_t tid; +}; + +struct ath_usb_host_cmd_ring { + struct ath_usb_host_cmd cmd[ATH_USB_HOST_CMD_RING_COUNT]; + int cur; + int next; + int queued; +}; + +struct ath_usb_softc { + struct ath_softc *sc_sc; +#define usb_dev sc_sc->sc_dev + int sc_ath_attached; + + /* USB specific goo. */ + struct usb_device *sc_udev; + struct usb_interface *sc_iface; + + u_int flags; +#define ATH_USB_FLAG_AR7010 0x01 + + struct ath_usb_rx_stream rx_stream; + + uint8_t *ibuf; + size_t ibuflen; + + struct ar_wmi_cmd_reg_write wbuf[AR_MAX_WRITE_COUNT]; + int wcount; + + uint16_t wmi_seq_no; + uint16_t wait_cmd_id; + uint16_t wait_msg_id; + void *obuf; + struct ar_htc_msg_conn_svc_rsp *msg_conn_svc_rsp; + + struct usb_xfer *sc_xfer[ATH_N_XFER]; + +// struct ath_usb_host_cmd_ring cmdq; + struct ath_usb_data rx_data[ath_USB_RX_LIST_COUNT]; + struct ath_usb_data tx_data[ath_USB_TX_LIST_COUNT]; + struct ath_usb_data tx_cmd[ATH_USB_HOST_CMD_RING_COUNT]; +// TAILQ_HEAD(, ath_usb_tx_data) tx_free_list; +// struct ath_usb_host_cmd tx_cmd; +// struct ath_usb_host_cmd *tx_bcn; + + int sc_tx_n_active; + + STAILQ_HEAD(, ath_usb_data) sc_rx_active; + STAILQ_HEAD(, ath_usb_data) sc_rx_inactive; + STAILQ_HEAD(, ath_usb_data) sc_tx_active[ATH_N_XFER]; + STAILQ_HEAD(, ath_usb_data) sc_tx_inactive; + STAILQ_HEAD(, ath_usb_data) sc_tx_pending[ATH_N_XFER]; + + STAILQ_HEAD(, ath_usb_data) sc_cmd_active; + STAILQ_HEAD(, ath_usb_data) sc_cmd_inactive; + STAILQ_HEAD(, ath_usb_data) sc_cmd_pending; + STAILQ_HEAD(, ath_usb_data) sc_cmd_waiting; + + uint8_t ep_ctrl; + uint8_t ep_bcn; + uint8_t ep_cab; + uint8_t ep_uapsd; + uint8_t ep_mgmt; + uint8_t ep_data[ATH_N_XFER]; + + /* + * Firmware cannot handle more than 8 STAs. + * We use a bitmask to keep track of available slots in the firmware's + * node array. A 1 bit at index N, as determined by ffs(3), means the + * slot at this index is available. + */ + uint8_t free_node_slots; + + void (*sc_node_free)(struct ieee80211com *, + struct ieee80211_node *); + int sc_key_tasks; + bool no_buffer_write; +}; + +#endif diff --git a/sys/dev/ath/if_ath_usb_devid.h b/sys/dev/ath/if_ath_usb_devid.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb_devid.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 2011 Damien Bergamini + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ + +#ifndef __IF_ATH_USB_DEVID_H__ +#define __IF_ATH_USB_DEVID_H__ + +/* $OpenBSD: usbdevs.h,v 1.768 2023/05/10 18:26:43 miod Exp $ */ +#define USB_VENDOR_ATHEROS2 0x0cf3 /* Atheros Communications */ +#define USB_VENDOR_AZUREWAVE 0x13d3 /* AsureWave */ +#define USB_VENDOR_DLINK2 0x07d1 /* D-Link */ +#define USB_VENDOR_LITEON 0x04ca /* Lite-On Technology */ +#define USB_VENDOR_NETGEAR 0x0846 /* BayNETGEAR */ +#define USB_VENDOR_VIA 0x040d /* VIA */ +#define USB_VENDOR_ACCTON 0x083a /* Accton Technology */ +#define USB_VENDOR_ACTIONTEC 0x1668 /* Actiontec Electronics */ +#define USB_VENDOR_PANASONIC 0x04da /* Panasonic (Matsushita) */ +#define USB_VENDOR_MELCO 0x0411 /* Melco */ + +#define USB_PRODUCT_ATHEROS2_AR9271_1 0x1006 /* AR9271 */ +#define USB_PRODUCT_ATHEROS2_AR9271_2 0x9271 /* AR9271 */ +#define USB_PRODUCT_ATHEROS2_AR9271_3 0xb003 /* AR9271 */ +#define USB_PRODUCT_AZUREWAVE_AR9271_1 0x3327 /* AR9271 */ +#define USB_PRODUCT_AZUREWAVE_AR9271_2 0x3328 /* AR9271 */ +#define USB_PRODUCT_AZUREWAVE_AR9271_3 0x3346 /* AR9271 */ +#define USB_PRODUCT_AZUREWAVE_AR9271_4 0x3348 /* AR9271 */ +#define USB_PRODUCT_AZUREWAVE_AR9271_5 0x3349 /* AR9271 */ +#define USB_PRODUCT_AZUREWAVE_AR9271_6 0x3350 /* AR9271 */ +#define USB_PRODUCT_DLINK2_AR9271 0x3a10 /* AR9271 */ +#define USB_PRODUCT_LITEON_AR9271 0x4605 /* AR9271 */ +#define USB_PRODUCT_NETGEAR_WNA1100 0x9030 /* WNA1100 */ +#define USB_PRODUCT_VIA_AR9271 0x3801 /* AR9271 */ +#define USB_PRODUCT_ACCTON_AR9280 0xa704 /* AR9280+AR7010 */ +#define USB_PRODUCT_ACTIONTEC_AR9287 0x1200 /* AR9287+AR7010 */ +#define USB_PRODUCT_NETGEAR_WNDA3200 0x9018 /* WNDA3200 */ +#define USB_PRODUCT_PANASONIC_N5HBZ0000055 0x3904 /* UB94 */ +#define USB_PRODUCT_MELCO_UWABR100 0x017f /* SONY UWA-BR100 */ +#define USB_PRODUCT_ATHEROS2_AR9280 0x7010 /* AR9280+AR7010 */ +#define USB_PRODUCT_ATHEROS2_AR9287 0x7015 /* AR9287+AR7010 */ + +#define ATHN_USB_FLAG_AR7010 0x01 + +#endif diff --git a/sys/dev/ath/if_ath_usb_devlist.h b/sys/dev/ath/if_ath_usb_devlist.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb_devlist.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2011 Damien Bergamini + * Copyright (c) 2018 Stefan Sperling + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, 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. + */ + +/* + * USB front-end for Atheros AR9271 and AR7010 chipsets. + */ + +#ifndef __IF_ATH_USB_DEVLIST_H__ +#define __IF_ATH_USB_DEVLIST_H__ + +#include +#include +#include "if_ath_usb_devid.h" + +/* $OpenBSD: copy from if_athn_usb.c,v 1.65 2022/07/10 21:13:41 bluhm Exp $ */ +static const STRUCT_USB_HOST_ID ath_usb_devs[] = { + { USB_VP(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_1) }, + { USB_VP(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_2) }, + { USB_VP(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9271_3)}, + { USB_VP(USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_1) }, + { USB_VP(USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_2) }, + { USB_VP(USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_3) }, + { USB_VP(USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_4) }, + { USB_VP(USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_5) }, + { USB_VP(USB_VENDOR_AZUREWAVE, USB_PRODUCT_AZUREWAVE_AR9271_6) }, + { USB_VP(USB_VENDOR_DLINK2, USB_PRODUCT_DLINK2_AR9271) }, + { USB_VP(USB_VENDOR_LITEON, USB_PRODUCT_LITEON_AR9271) }, + { USB_VP(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNA1100) }, + { USB_VP(USB_VENDOR_VIA, USB_PRODUCT_VIA_AR9271) }, + { USB_VPI(USB_VENDOR_ACCTON, USB_PRODUCT_ACCTON_AR9280, ATHN_USB_FLAG_AR7010) }, + { USB_VPI(USB_VENDOR_ACTIONTEC, USB_PRODUCT_ACTIONTEC_AR9287, ATHN_USB_FLAG_AR7010) }, + { USB_VPI(USB_VENDOR_NETGEAR, USB_PRODUCT_NETGEAR_WNDA3200, ATHN_USB_FLAG_AR7010) }, + { USB_VPI(USB_VENDOR_PANASONIC, USB_PRODUCT_PANASONIC_N5HBZ0000055, ATHN_USB_FLAG_AR7010) }, + { USB_VPI(USB_VENDOR_MELCO, USB_PRODUCT_MELCO_UWABR100, ATHN_USB_FLAG_AR7010) }, + { USB_VPI(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9280, ATHN_USB_FLAG_AR7010) }, + { USB_VPI(USB_VENDOR_ATHEROS2, USB_PRODUCT_ATHEROS2_AR9287, ATHN_USB_FLAG_AR7010) } +}; + +#endif diff --git a/sys/dev/ath/if_ath_usb_fw.h b/sys/dev/ath/if_ath_usb_fw.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb_fw.h @@ -0,0 +1,9 @@ +#ifndef IF_ATH_USB_FW_H +#define IF_ATH_USB_FW_H + +struct ath_usb_softc; + +int ath_usb_load_firmware(struct ath_usb_softc *, struct ar_wmi_fw_version *); +const struct firmware* ath_usb_unload_firmware(); + +#endif /* IF_ATH_USB_FW_H */ diff --git a/sys/dev/ath/if_ath_usb_fw.c b/sys/dev/ath/if_ath_usb_fw.c new file mode 100644 --- /dev/null +++ b/sys/dev/ath/if_ath_usb_fw.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ar5212/ar5212reg.h" +#include "dev/ath/if_athvar.h" + + +// FreeBSD version +#include +#include + +#include "if_ath_usb_def.h" + +#define AR9271_CLOCK_CONTROL 0x050040 +#define AR9271_RESET_POWER_DOWN_CONTROL 0x050044 +#define AR9271_FIRMWARE 0x501000 +#define AR9271_FIRMWARE_TEXT 0x903000 +#define AR7010_FIRMWARE_TEXT 0x906000 +static const struct firmware *fware = NULL; + +extern int tsleep_nsec(const void *ident, int priority, const char *wmesg, uint64_t nsecs); + +void +ath_usb_unload_firmware() +{ + if (fware == NULL) { + printf("Fail: null firmware handler\n"); + return; + } + + firmware_put(fware, FIRMWARE_UNLOAD); + printf("Successully called firmware_put\n"); + return; +} + +int +ath_usb_get_firmware(struct ath_usb_softc *usc, struct ar_wmi_fw_version *version) +{ + usb_device_descriptor_t *dd; + const char *name; + int error = ENXIO; + + /* Determine which firmware image to load. */ + if (usc->flags & ATH_USB_FLAG_AR7010) { + dd = usbd_get_device_descriptor(usc->sc_udev); + name = "ath-open-ar7010.bin"; + } else + name = "ath-open-ar9271.bin"; + /* Read firmware image from the filesystem. */ + fware = firmware_get(name); + if (fware == NULL) { + printf("Failed firmware_get of file %s\n", name); + return (error); + } else { + printf("Success firmware_get of file %s\n", name); + printf("Firmware name: %s:\n", fware->name); + printf("Firmware version: %u:\n", fware->version); + printf("Firmware size: %zu:\n", fware->datasize); + version->major = fware->version / 100; + version->minor = (fware->version / 10) % 10; + return 0; + } +} + +int +ath_usb_transfer_firmware(struct ath_usb_softc *usc) +{ + usb_device_request_t req; + const char *name; + void *ptr; + size_t fwsize, size; + uint32_t addr; + int s, mlen; + int error = 0; + + ATH_USB_LOCK(usc->sc_sc); + + /* Load firmware image. */ + ptr = (void *)fware->data; + addr = AR9271_FIRMWARE >> 8; + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AR_FW_DOWNLOAD; + USETW(req.wIndex, 0); + size = fware->datasize; + + while (size > 0) { + printf("Uploading %zu bytes\n", size); + mlen = MIN(size, 4096); + + USETW(req.wValue, addr); + USETW(req.wLength, mlen); + + error = usbd_do_request(usc->sc_udev, &usc->sc_sc->sc_usb_mtx, &req, ptr); + if (error != 0) { + ATH_USB_UNLOCK(usc->sc_sc); + ath_usb_unload_firmware(); + return (error); + } + addr += mlen >> 8; + ptr += mlen; + size -= mlen; + } + + /* Start firmware. */ + if (usc->flags & ATH_USB_FLAG_AR7010) + addr = AR7010_FIRMWARE_TEXT >> 8; + else { + addr = AR9271_FIRMWARE_TEXT >> 8; + device_printf(usc->sc_sc->sc_dev, "%s: selected device: AR9271\n", __func__); + } + req.bmRequestType = UT_WRITE_VENDOR_DEVICE; + req.bRequest = AR_FW_DOWNLOAD_COMP; + USETW(req.wIndex, 0); + USETW(req.wValue, addr); + USETW(req.wLength, 0); + + usc->wait_msg_id = AR_HTC_MSG_READY; + error = usbd_do_request(usc->sc_udev, &usc->sc_sc->sc_usb_mtx, &req, NULL); + + /* Wait at most 1 second for firmware to boot. */ + device_printf(usc->sc_sc->sc_dev, "%s: Waiting for firmware to boot\n", __func__); + if (error == 0 && usc->wait_msg_id != 0){ + error = msleep(&usc->wait_msg_id, &usc->sc_sc->sc_usb_mtx, PCATCH, "athnfw", hz); + } + + if (usc->wait_msg_id == 0) { + device_printf(usc->sc_sc->sc_dev, "%s: Firmware booted successfully!\n", __func__); + } + + ATH_USB_UNLOCK(usc->sc_sc); + + ath_usb_unload_firmware(); + + return (error); +} + +int +ath_usb_load_firmware(struct ath_usb_softc *usc, struct ar_wmi_fw_version *img_version) +{ + int error = 0; + + error = ath_usb_get_firmware(usc, img_version); + if (error) { + printf("Failed to get firmware\n"); + return error; + } + + error = ath_usb_transfer_firmware(usc); + if (error) { + printf("Failed to transfer firmware to the device\n"); + } + + return error; +} diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -562,6 +562,7 @@ struct ath_stats sc_stats; /* device statistics */ struct ath_tx_aggr_stats sc_aggr_stats; struct ath_intr_stats sc_intr_stats; + struct ath_usb_softc *usc; uint64_t sc_debug; uint64_t sc_ktrdebug; int sc_nvaps; /* # vaps */ @@ -607,6 +608,8 @@ char sc_tx_mtx_name[32]; struct mtx sc_tx_ic_mtx; /* TX queue mutex */ char sc_tx_ic_mtx_name[32]; + struct mtx sc_usb_mtx; /* USB access mutex */ + char sc_usb_mtx_name[32]; struct taskqueue *sc_tq; /* private task queue */ struct ath_hal *sc_ah; /* Atheros HAL */ struct ath_ratectrl *sc_rc; /* tx rate control support */ @@ -1043,8 +1046,19 @@ #define ATH_TXSTATUS_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_txcomplock) #define ATH_TXSTATUS_LOCK_ASSERT(_sc) \ mtx_assert(&(_sc)->sc_txcomplock, MA_OWNED) - -int ath_attach(u_int16_t, struct ath_softc *); +#define ATH_USB_LOCK_INIT(_sc) do { \ + snprintf((_sc)->sc_usb_mtx_name, \ + sizeof((_sc)->sc_usb_mtx_name), \ + "%s USB lock", \ + device_get_nameunit((_sc)->sc_dev)); \ + mtx_init(&sc->sc_usb_mtx, (_sc)->sc_usb_mtx_name, MTX_NETWORK_LOCK, \ + MTX_DEF); \ +} while (0) +#define ATH_USB_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_usb_mtx) +#define ATH_USB_LOCK(_sc) mtx_lock(&(_sc)->sc_usb_mtx) +#define ATH_USB_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_usb_mtx) +#define ATH_USB_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_usb_mtx, MA_OWNED) +int ath_attach(u_int16_t, u_int16_t, struct ath_softc *); int ath_detach(struct ath_softc *); void ath_resume(struct ath_softc *); void ath_suspend(struct ath_softc *); diff --git a/sys/dev/ath/openbsd_adapt.h b/sys/dev/ath/openbsd_adapt.h new file mode 100644 --- /dev/null +++ b/sys/dev/ath/openbsd_adapt.h @@ -0,0 +1,155 @@ +/* $OpenBSD: time.h,v 1.63 2022/12/13 17:30:36 cheloha Exp $ */ +/* $NetBSD: time.h,v 1.18 1996/04/23 10:29:33 mycroft Exp $ */ + +/* + * Copyright (c) 1982, 1986, 1993 + * The Regents of the University of California. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)time.h 8.2 (Berkeley) 7/10/94 + */ + +#ifndef _OPENBSD_ADAPT_H_ +#define _OPENBSD_ADAPT_H_ + +/* + * Set to 1 to 'uncomment' (enable) + * parts of the code that depend on the USB OpenBSD API + */ +#define OpenBSD_IEEE80211_API 0 + +/* + * Code marked by this macro + * needs a OpenBSD equivalent function or complete rework + * Some might not be needed. + * Set to 1 to 'uncomment' (enable) + */ +#define OpenBSD_ONLY 0 + +// map OpenBSD flag name to FreeBSD +#define IFF_RUNNING IFF_DRV_RUNNING + +// map 3-argument OpenBSD free function to 2-argument FreeBSD one +// if the number of arguments is 2 - do nothing +#define free(addr,type,...) free(addr,type) + +#define IEEE80211_HT_NUM_MCS 77 +#define IEEE80211_DUR_DS_SHSLOT 9 +#define MBUF_LIST_INITIALIZER() { NULL, NULL, 0 } + + +struct mbuf_list { + struct mbuf *ml_head; + struct mbuf *ml_tail; + u_int ml_len; +}; + +enum ieee80211_edca_ac { + EDCA_AC_BK = 1, /* Background */ + EDCA_AC_BE = 0, /* Best Effort */ + EDCA_AC_VI = 2, /* Video */ + EDCA_AC_VO = 3 /* Voice */ +}; + + + +// TODO: try remove these defines, use proper include instead + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + + +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + + +static inline uint64_t +SEC_TO_NSEC(uint64_t seconds) +{ + if (seconds > UINT64_MAX / 1000000000ULL) + return UINT64_MAX; + return seconds * 1000000000ULL; +} + +static inline uint64_t +MSEC_TO_NSEC(uint64_t milliseconds) +{ + if (milliseconds > UINT64_MAX / 1000000ULL) + return UINT64_MAX; + return milliseconds * 1000000ULL; +} + +// ifq_oactive is not available in FreeBSD. +// Need to use additional variable to provide this functionality. +extern unsigned int ifq_oactive; + +static inline void +ifq_clr_oactive() +{ + ifq_oactive = 0; +} + +static inline unsigned int +ifq_is_oactive() +{ + return ifq_oactive; +} + +static inline void +ifq_set_oactive() +{ + ifq_oactive = 1; +} + +static __inline intrmask_t splusb(void) { return 0; } + +#endif /* _OPENBSD_ADAPT_H_ */ diff --git a/sys/modules/ath_hal_ar5416/Makefile b/sys/modules/ath_hal_ar5416/Makefile --- a/sys/modules/ath_hal_ar5416/Makefile +++ b/sys/modules/ath_hal_ar5416/Makefile @@ -69,6 +69,10 @@ SRCS+= ar9285.c ar9285_reset.c ar9285_attach.c ar9285_cal.c ar9285_phy.c SRCS+= ar9285_diversity.c ar9285_btcoex.c +# + AR9271 - K2 +SRCS+= ar9271.c ar9271_reset.c ar9271_attach.c ar9271_cal.c ar9271_phy.c +SRCS+= ar9271_diversity.c ar9271_btcoex.c + # + AR9287 - Kiwi .PATH: ${SRCTOP}/sys/dev/ath/ath_hal SRCS+= ah_eeprom_9287.c diff --git a/sys/modules/usb/ath/Makefile b/sys/modules/usb/ath/Makefile new file mode 100644 --- /dev/null +++ b/sys/modules/usb/ath/Makefile @@ -0,0 +1,38 @@ +# $FreeBSD$ + +INCDIR=${SRCTOP}/sys/compat/linuxkpi/common/include + +.PATH: ${SRCTOP}/sys/dev/usb/wlan ${SRCTOP}/sys/dev/ath ${SRCTOP}/sys/dev/ath/ath_hal + +.include + +KMOD = if_ath_usb + +EXPORT_SYMS= YES + +SRCS= if_ath_usb.c if_ath_usb_fw.c +SRCS+= if_ath_usb_devlist.h +# SRCS+= opt_inet.h opt_inet6.h opt_route.h opt_netlink.h + +SRCS+= ${LINUXKPI_GENSRCS} + +CFLAGS+= -ferror-limit=0 +CFLAGS+= -I${INCDIR} + +CFLAGS+= -I. -I${SRCTOP}/sys/dev/ath -I${SRCTOP}/sys/dev/ath/ath_hal +CFLAGS+= -I. -I${SRCTOP}/sys/contrib/dev/ath/ath_hal/ + +# CFLAGS+= -I${SRCTOP}/sys/dev/athn/headers + +DEBUG_FLAGS+= -O0 -g + +# CFLAGS+= -I${SRCTOP}/sys/dev/athn/include/amd64 +# CFLAGS+= -I${SRCTOP}/sys/dev/athn/include/sys +# CFLAGS+= -I${SRCTOP}/sys/dev/athn/include/net80211 + + +CWARNFLAGS+= ${NO_WUNUSED_BUT_SET_VARIABLE} +CWARNFLAGS+= ${NO_WUNUSED_PARAMETER} +CWARNFLAGS+= ${NO_WUNUSED_FUNCTION} + +.include diff --git a/sys/modules/usb/athfw/Makefile b/sys/modules/usb/athfw/Makefile new file mode 100644 --- /dev/null +++ b/sys/modules/usb/athfw/Makefile @@ -0,0 +1,10 @@ +IMG= ath-open-ar9271.bin + +KMOD= ${IMG} +CLEANFILES+= ${IMG} +FIRMWS= ${IMG}:${IMG}:141 + +${IMG}: ${SRCTOP}/sys/contrib/dev/athfw/${IMG} + cp ${.ALLSRC} ${.TARGET} + +.include \ No newline at end of file