diff options
Diffstat (limited to 'sys/dev')
93 files changed, 11176 insertions, 3923 deletions
diff --git a/sys/dev/ath/ah_osdep.c b/sys/dev/ath/ah_osdep.c index 043ebed..fe4a657 100644 --- a/sys/dev/ath/ah_osdep.c +++ b/sys/dev/ath/ah_osdep.c @@ -96,9 +96,8 @@ static SYSCTL_NODE(_hw_ath, OID_AUTO, hal, CTLFLAG_RD, 0, #ifdef AH_DEBUG int ath_hal_debug = 0; -SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RW, &ath_hal_debug, +SYSCTL_INT(_hw_ath_hal, OID_AUTO, debug, CTLFLAG_RWTUN, &ath_hal_debug, 0, "Atheros HAL debugging printfs"); -TUNABLE_INT("hw.ath.hal.debug", &ath_hal_debug); #endif /* AH_DEBUG */ static MALLOC_DEFINE(M_ATH_HAL, "ath_hal", "ath hal data"); @@ -138,6 +137,24 @@ ath_hal_ether_sprintf(const u_int8_t *mac) #ifdef AH_DEBUG +/* + * XXX This is highly relevant only for the AR5416 and later + * PCI/PCIe NICs. It'll need adjustment for other hardware + * variations. + */ +static int +ath_hal_reg_whilst_asleep(struct ath_hal *ah, uint32_t reg) +{ + + if (reg >= 0x4000 && reg < 0x5000) + return (1); + if (reg >= 0x6000 && reg < 0x7000) + return (1); + if (reg >= 0x7000 && reg < 0x8000) + return (1); + return (0); +} + void DO_HALDEBUG(struct ath_hal *ah, u_int mask, const char* fmt, ...) { @@ -253,6 +270,13 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", + __func__, reg, val, ah->ah_powerMode); + } + if (ath_hal_alq) { struct ale *ale = ath_hal_alq_get(ah); if (ale) { @@ -278,6 +302,13 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) bus_space_handle_t h = ah->ah_sh; u_int32_t val; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", + __func__, reg, ah->ah_powerMode); + } + if (ah->ah_config.ah_serialise_reg_war) mtx_lock_spin(&ah_regser_mtx); val = bus_space_read_4(tag, h, reg); @@ -330,6 +361,13 @@ ath_hal_reg_write(struct ath_hal *ah, u_int32_t reg, u_int32_t val) bus_space_tag_t tag = BUSTAG(ah); bus_space_handle_t h = ah->ah_sh; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, val=0x%08x, pm=%d\n", + __func__, reg, val, ah->ah_powerMode); + } + if (ah->ah_config.ah_serialise_reg_war) mtx_lock_spin(&ah_regser_mtx); bus_space_write_4(tag, h, reg, val); @@ -344,6 +382,13 @@ ath_hal_reg_read(struct ath_hal *ah, u_int32_t reg) bus_space_handle_t h = ah->ah_sh; u_int32_t val; + /* Debug - complain if we haven't fully waken things up */ + if (! ath_hal_reg_whilst_asleep(ah, reg) && + ah->ah_powerMode != HAL_PM_AWAKE) { + ath_hal_printf(ah, "%s: reg=0x%08x, pm=%d\n", + __func__, reg, ah->ah_powerMode); + } + if (ah->ah_config.ah_serialise_reg_war) mtx_lock_spin(&ah_regser_mtx); val = bus_space_read_4(tag, h, reg); diff --git a/sys/dev/ath/ath_dfs/null/dfs_null.c b/sys/dev/ath/ath_dfs/null/dfs_null.c index 36b2042..c980a79 100644 --- a/sys/dev/ath/ath_dfs/null/dfs_null.c +++ b/sys/dev/ath/ath_dfs/null/dfs_null.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/kernel.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/errno.h> @@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_arp.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ diff --git a/sys/dev/ath/ath_hal/ah.c b/sys/dev/ath/ath_hal/ah.c index 7187d57..4eb7fb6 100644 --- a/sys/dev/ath/ath_hal/ah.c +++ b/sys/dev/ath/ath_hal/ah.c @@ -55,7 +55,9 @@ ath_hal_probe(uint16_t vendorid, uint16_t devid) */ struct ath_hal* ath_hal_attach(uint16_t devid, HAL_SOFTC sc, - HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_STATUS *error) + HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, + HAL_STATUS *error) { struct ath_hal_chip * const *pchip; @@ -66,7 +68,8 @@ ath_hal_attach(uint16_t devid, HAL_SOFTC sc, /* XXX don't have vendorid, assume atheros one works */ if (chip->probe(ATHEROS_VENDOR_ID, devid) == AH_NULL) continue; - ah = chip->attach(devid, sc, st, sh, eepromdata, error); + ah = chip->attach(devid, sc, st, sh, eepromdata, ah_config, + error); if (ah != AH_NULL) { /* copy back private state to public area */ ah->ah_devid = AH_PRIVATE(ah)->ah_devid; @@ -786,6 +789,8 @@ ath_hal_getcapability(struct ath_hal *ah, HAL_CAPABILITY_TYPE type, return HAL_OK; case HAL_CAP_RX_LNA_MIXING: /* Hardware uses an RX LNA mixer to map 2 antennas to a 1 stream receiver */ return pCap->halRxUsingLnaMixing ? HAL_OK : HAL_ENOTSUPP; + case HAL_CAP_DO_MYBEACON: /* Hardware supports filtering my-beacons */ + return pCap->halRxDoMyBeacon ? HAL_OK : HAL_ENOTSUPP; default: return HAL_EINVAL; } @@ -848,10 +853,11 @@ ath_hal_getregdump(struct ath_hal *ah, const HAL_REGRANGE *regs, int i; for (i = 0; space >= 2*sizeof(uint32_t); i++) { - u_int r = regs[i].start; - u_int e = regs[i].end; - *dp++ = (r<<16) | e; - space -= sizeof(uint32_t); + uint32_t r = regs[i].start; + uint32_t e = regs[i].end; + *dp++ = r; + *dp++ = e; + space -= 2*sizeof(uint32_t); do { *dp++ = OS_REG_READ(ah, r); r += sizeof(uint32_t); @@ -875,6 +881,7 @@ ath_hal_getdiagstate(struct ath_hal *ah, int request, const void *args, uint32_t argsize, void **result, uint32_t *resultsize) { + switch (request) { case HAL_DIAG_REVS: *result = &AH_PRIVATE(ah)->ah_devid; @@ -932,6 +939,10 @@ ath_hal_getdiagstate(struct ath_hal *ah, int request, } else return AH_FALSE; return AH_TRUE; + case HAL_DIAG_CHANSURVEY: + *result = &AH_PRIVATE(ah)->ah_chansurvey; + *resultsize = sizeof(HAL_CHANNEL_SURVEY); + return AH_TRUE; } return AH_FALSE; } @@ -1427,3 +1438,32 @@ ath_hal_mhz2ieee_2ghz(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan) else return 15 + ((ichan->channel - 2512) / 20); } + +/* + * Clear the current survey data. + * + * This should be done during a channel change. + */ +void +ath_hal_survey_clear(struct ath_hal *ah) +{ + + OS_MEMZERO(&AH_PRIVATE(ah)->ah_chansurvey, + sizeof(AH_PRIVATE(ah)->ah_chansurvey)); +} + +/* + * Add a sample to the channel survey. + */ +void +ath_hal_survey_add_sample(struct ath_hal *ah, HAL_SURVEY_SAMPLE *hs) +{ + HAL_CHANNEL_SURVEY *cs; + + cs = &AH_PRIVATE(ah)->ah_chansurvey; + + OS_MEMCPY(&cs->samples[cs->cur_sample], hs, sizeof(*hs)); + cs->samples[cs->cur_sample].seq_num = cs->cur_seq; + cs->cur_sample = (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; + cs->cur_seq++; +} diff --git a/sys/dev/ath/ath_hal/ah.h b/sys/dev/ath/ath_hal/ah.h index 2480803..facfceb1 100644 --- a/sys/dev/ath/ath_hal/ah.h +++ b/sys/dev/ath/ath_hal/ah.h @@ -199,6 +199,7 @@ typedef enum { HAL_CAP_SERIALISE_WAR = 245, /* serialise register access on PCI */ HAL_CAP_ENFORCE_TXOP = 246, /* Enforce TXOP if supported */ HAL_CAP_RX_LNA_MIXING = 247, /* RX hardware uses LNA mixing */ + HAL_CAP_DO_MYBEACON = 248, /* Supports HAL_RX_FILTER_MYBEACON */ } HAL_CAPABILITY_TYPE; /* @@ -404,6 +405,7 @@ typedef enum { HAL_RX_FILTER_PROM = 0x00000020, /* Promiscuous mode */ HAL_RX_FILTER_PROBEREQ = 0x00000080, /* Allow probe request frames */ HAL_RX_FILTER_PHYERR = 0x00000100, /* Allow phy errors */ + HAL_RX_FILTER_MYBEACON = 0x00000200, /* Filter beacons other than mine */ HAL_RX_FILTER_COMPBAR = 0x00000400, /* Allow compressed BAR */ HAL_RX_FILTER_COMP_BA = 0x00000800, /* Allow compressed blockack */ HAL_RX_FILTER_PHYRADAR = 0x00002000, /* Allow phy radar errors */ @@ -538,6 +540,7 @@ typedef enum { typedef struct { u_int32_t cyclecnt_diff; /* delta cycle count */ u_int32_t rxclr_cnt; /* rx clear count */ + u_int32_t extrxclr_cnt; /* ext chan rx clear count */ u_int32_t txframecnt_diff; /* delta tx frame count */ u_int32_t rxframecnt_diff; /* delta rx frame count */ u_int32_t listen_time; /* listen time in msec - time for which ch is free */ @@ -847,6 +850,48 @@ typedef struct { #define HAL_RSSI_EP_MULTIPLIER (1<<7) /* pow2 to optimize out * and / */ +/* + * This is the ANI state and MIB stats. + * + * It's used by the HAL modules to keep state /and/ by the debug ioctl + * to fetch ANI information. + */ +typedef struct { + uint32_t ast_ani_niup; /* ANI increased noise immunity */ + uint32_t ast_ani_nidown; /* ANI decreased noise immunity */ + uint32_t ast_ani_spurup; /* ANI increased spur immunity */ + uint32_t ast_ani_spurdown;/* ANI descreased spur immunity */ + uint32_t ast_ani_ofdmon; /* ANI OFDM weak signal detect on */ + uint32_t ast_ani_ofdmoff;/* ANI OFDM weak signal detect off */ + uint32_t ast_ani_cckhigh;/* ANI CCK weak signal threshold high */ + uint32_t ast_ani_ccklow; /* ANI CCK weak signal threshold low */ + uint32_t ast_ani_stepup; /* ANI increased first step level */ + uint32_t ast_ani_stepdown;/* ANI decreased first step level */ + uint32_t ast_ani_ofdmerrs;/* ANI cumulative ofdm phy err count */ + uint32_t ast_ani_cckerrs;/* ANI cumulative cck phy err count */ + uint32_t ast_ani_reset; /* ANI parameters zero'd for non-STA */ + uint32_t ast_ani_lzero; /* ANI listen time forced to zero */ + uint32_t ast_ani_lneg; /* ANI listen time calculated < 0 */ + HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ + HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ +} HAL_ANI_STATS; + +typedef struct { + uint8_t noiseImmunityLevel; + uint8_t spurImmunityLevel; + uint8_t firstepLevel; + uint8_t ofdmWeakSigDetectOff; + uint8_t cckWeakSigThreshold; + uint32_t listenTime; + + /* NB: intentionally ordered so data exported to user space is first */ + uint32_t txFrameCount; /* Last txFrameCount */ + uint32_t rxFrameCount; /* Last rx Frame count */ + uint32_t cycleCount; /* Last cycleCount + (to detect wrap-around) */ + uint32_t ofdmPhyErrCount;/* OFDM err count since last reset */ + uint32_t cckPhyErrCount; /* CCK err count since last reset */ +} HAL_ANI_STATE; struct ath_desc; struct ath_tx_status; @@ -1262,6 +1307,7 @@ typedef struct int ath_hal_show_bb_panic; int ath_hal_ant_ctrl_comm2g_switch_enable; int ath_hal_ext_atten_margin_cfg; + int ath_hal_min_gainidx; int ath_hal_war70c; uint32_t ath_hal_mci_config; } HAL_OPS_CONFIG; @@ -1297,6 +1343,9 @@ struct ath_hal { uint32_t ah_intrstate[8]; /* last int state */ uint32_t ah_syncstate; /* last sync intr state */ + /* Current powerstate from HAL calls */ + HAL_POWER_MODE ah_powerMode; + HAL_OPS_CONFIG ah_config; const HAL_RATE_TABLE *__ahdecl(*ah_getRateTable)(struct ath_hal *, u_int mode); @@ -1583,6 +1632,18 @@ struct ath_hal { void __ahdecl(*ah_btCoexDisable)(struct ath_hal *); int __ahdecl(*ah_btCoexEnable)(struct ath_hal *); + /* Bluetooth MCI methods */ + void __ahdecl(*ah_btMciSetup)(struct ath_hal *, + uint32_t, void *, uint16_t, uint32_t); + HAL_BOOL __ahdecl(*ah_btMciSendMessage)(struct ath_hal *, + uint8_t, uint32_t, uint32_t *, uint8_t, + HAL_BOOL, HAL_BOOL); + uint32_t __ahdecl(*ah_btMciGetInterrupt)(struct ath_hal *, + uint32_t *, uint32_t *); + uint32_t __ahdecl(*ah_btMciGetState)(struct ath_hal *, + uint32_t, uint32_t *); + void __ahdecl(*ah_btMciDetach)(struct ath_hal *); + /* LNA diversity configuration */ void __ahdecl(*ah_divLnaConfGet)(struct ath_hal *, HAL_ANT_COMB_CONFIG *); @@ -1611,7 +1672,8 @@ extern const char *__ahdecl ath_hal_probe(uint16_t vendorid, uint16_t devid); * 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, HAL_STATUS* status); + 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 *); extern const char *ath_hal_rf_name(struct ath_hal *); diff --git a/sys/dev/ath/ath_hal/ah_decode.h b/sys/dev/ath/ath_hal/ah_decode.h index 07c4ac7..3aca975 100644 --- a/sys/dev/ath/ath_hal/ah_decode.h +++ b/sys/dev/ath/ath_hal/ah_decode.h @@ -53,6 +53,8 @@ enum { AH_MARK_ANI_POLL, /* ar*AniReset, listen time */ AH_MARK_ANI_CONTROL, /* ar*AniReset, cmd */ AH_MARK_RX_CTL, /* RX DMA control */ + AH_MARK_CHIP_POWER, /* chip power control, mode */ + AH_MARK_CHIP_POWER_DONE, /* chip power control done, status */ }; enum { @@ -61,6 +63,7 @@ enum { AH_MARK_RX_CTL_DMA_START, AH_MARK_RX_CTL_DMA_STOP, AH_MARK_RX_CTL_DMA_STOP_ERR, + AH_MARK_RX_CTL_DMA_STOP_OK, }; #endif /* _ATH_AH_DECODE_H_ */ diff --git a/sys/dev/ath/ath_hal/ah_devid.h b/sys/dev/ath/ath_hal/ah_devid.h index 43d994d..1e4d473 100644 --- a/sys/dev/ath/ath_hal/ah_devid.h +++ b/sys/dev/ath/ath_hal/ah_devid.h @@ -92,6 +92,7 @@ #define AR9300_DEVID_AR946X_PCIE 0x0034 #define AR9300_DEVID_AR9330 0x0035 #define AR9300_DEVID_QCA9565 0x0036 +#define AR9300_DEVID_AR1111_PCIE 0x0037 #define AR9300_DEVID_QCA955X 0x0039 #define AR_SUBVENDOR_ID_NOG 0x0e11 /* No 11G subvendor ID */ diff --git a/sys/dev/ath/ath_hal/ah_internal.h b/sys/dev/ath/ath_hal/ah_internal.h index 908f33e..0c855d4 100644 --- a/sys/dev/ath/ath_hal/ah_internal.h +++ b/sys/dev/ath/ath_hal/ah_internal.h @@ -50,8 +50,8 @@ #endif typedef struct { - uint16_t start; /* first register */ - uint16_t end; /* ending register or zero */ + uint32_t start; /* first register */ + uint32_t end; /* ending register or zero */ } HAL_REGRANGE; typedef struct { @@ -91,6 +91,7 @@ struct ath_hal_chip { const char *(*probe)(uint16_t vendorid, uint16_t devid); struct ath_hal *(*attach)(uint16_t devid, HAL_SOFTC, HAL_BUS_TAG, HAL_BUS_HANDLE, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah, HAL_STATUS *error); }; #ifndef AH_CHIP @@ -280,7 +281,9 @@ typedef struct { halAntDivCombSupportOrg : 1, halRadioRetentionSupport : 1, halSpectralScanSupport : 1, - halRxUsingLnaMixing : 1; + halRxUsingLnaMixing : 1, + halRxDoMyBeacon : 1, + halHwUapsdTrig : 1; uint32_t halWirelessModes; uint16_t halTotalQueues; @@ -419,9 +422,13 @@ struct ath_hal_private { uint32_t ah_fatalState[6]; /* AR_ISR+shadow regs */ int ah_rxornIsFatal; /* how to treat HAL_INT_RXORN */ -#ifndef ATH_NF_PER_CHAN + /* Only used if ATH_NF_PER_CHAN is defined */ HAL_NFCAL_HIST_FULL nf_cal_hist; -#endif /* ! ATH_NF_PER_CHAN */ + + /* + * Channel survey history - current channel only. + */ + HAL_CHANNEL_SURVEY ah_chansurvey; /* channel survey */ }; #define AH_PRIVATE(_ah) ((struct ath_hal_private *)(_ah)) @@ -1026,4 +1033,15 @@ ath_hal_getantennaallowed(struct ath_hal *ah, */ extern int ath_hal_mhz2ieee_2ghz(struct ath_hal *, HAL_CHANNEL_INTERNAL *); +/* + * Clear the channel survey data. + */ +extern void ath_hal_survey_clear(struct ath_hal *ah); + +/* + * Add a sample to the channel survey data. + */ +extern void ath_hal_survey_add_sample(struct ath_hal *ah, + HAL_SURVEY_SAMPLE *hs); + #endif /* _ATH_AH_INTERAL_H_ */ diff --git a/sys/dev/ath/ath_hal/ah_regdomain.c b/sys/dev/ath/ath_hal/ah_regdomain.c index 807db44..fb0c841 100644 --- a/sys/dev/ath/ath_hal/ah_regdomain.c +++ b/sys/dev/ath/ath_hal/ah_regdomain.c @@ -169,6 +169,11 @@ isEepromValid(struct ath_hal *ah) if (regDomainPairs[i].regDmnEnum == rd) return AH_TRUE; } + + if (rd == FCC_UBNT) { + return AH_TRUE; + } + HALDEBUG(ah, HAL_DEBUG_REGDOMAIN, "%s: invalid regulatory domain/country code 0x%x\n", __func__, rd); return AH_FALSE; diff --git a/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h b/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h index bc569cb..d8d211e 100644 --- a/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h +++ b/sys/dev/ath/ath_hal/ah_regdomain/ah_rd_regenum.h @@ -51,6 +51,7 @@ enum { FCC2_FCCA = 0x20, /* Canada */ FCC2_WORLD = 0x21, /* Australia & HK */ FCC2_ETSIC = 0x22, + FCC_UBNT = 0x2A, /* Ubiquity PicoStation M2HP */ FRANCE_RES = 0x31, /* Legacy France for OEM */ FCC3_FCCA = 0x3A, /* USA & Canada w/5470 band, 11h, DFS enabled */ FCC3_WORLD = 0x3B, /* USA & Canada w/5470 band, 11h, DFS enabled */ diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210.h b/sys/dev/ath/ath_hal/ar5210/ar5210.h index 48ccc20..3e372f7 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210.h +++ b/sys/dev/ath/ath_hal/ar5210/ar5210.h @@ -108,7 +108,6 @@ struct ath_hal_5210 { uint32_t ah_txDescInterruptMask; uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; - HAL_POWER_MODE ah_powerMode; uint8_t ah_bssid[IEEE80211_ADDR_LEN]; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; /* beacon+cab+data */ /* @@ -121,6 +120,8 @@ struct ath_hal_5210 { u_int ah_slottime; /* user-specified slot time */ u_int ah_acktimeout; /* user-specified ack timeout */ u_int ah_ctstimeout; /* user-specified cts timeout */ + + uint16_t ah_associd; /* association id */ }; #define AH5210(ah) ((struct ath_hal_5210 *)(ah)) diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c b/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c index 3c0ed71..ceafa99 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c +++ b/sys/dev/ath/ath_hal/ar5210/ar5210_attach.c @@ -183,7 +183,7 @@ static HAL_BOOL ar5210FillCapabilityInfo(struct ath_hal *ah); */ static struct ath_hal * ar5210Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, - uint16_t *eepromdata, HAL_STATUS *status) + uint16_t *eepromdata, HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) struct ath_hal_5210 *ahp; @@ -219,7 +219,7 @@ ar5210Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, AH_PRIVATE(ah)->ah_powerLimit = AR5210_MAX_RATE_POWER; AH_PRIVATE(ah)->ah_tpScale = HAL_TP_SCALE_MAX; /* no scaling */ - ahp->ah_powerMode = HAL_PM_UNDEFINED; + ah->ah_powerMode = HAL_PM_UNDEFINED; ahp->ah_staId1Defaults = 0; ahp->ah_rssiThr = INIT_RSSI_THR; ahp->ah_sifstime = (u_int) -1; diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c b/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c index 8170880..5eaa18e 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c +++ b/sys/dev/ath/ath_hal/ar5210/ar5210_misc.c @@ -315,6 +315,7 @@ ar5210WriteAssocid(struct ath_hal *ah, const uint8_t *bssid, uint16_t assocId) /* XXX save bssid for possible re-use on reset */ OS_MEMCPY(ahp->ah_bssid, bssid, IEEE80211_ADDR_LEN); + ahp->ah_associd = assocId; OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid+4) | ((assocId & 0x3fff)<<AR_BSS_ID1_AID_S)); diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210_power.c b/sys/dev/ath/ath_hal/ar5210/ar5210_power.c index 1c6909b..ec5e75d 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210_power.c +++ b/sys/dev/ath/ath_hal/ar5210/ar5210_power.c @@ -93,7 +93,6 @@ ar5210SetPowerModeSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5210SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5210 *ahp = AH5210(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -105,25 +104,30 @@ ar5210SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5210SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5210SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5210SetPowerModeAuto(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; - return status; + return status; } HAL_POWER_MODE diff --git a/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c b/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c index 1d50f99..1dba729 100644 --- a/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c +++ b/sys/dev/ath/ath_hal/ar5210/ar5210_reset.c @@ -152,8 +152,12 @@ ar5210Reset(struct ath_hal *ah, HAL_OPMODE opmode, /* Restore previous led state */ OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | ledstate); +#if 0 OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid)); OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4)); +#endif + /* BSSID, association id, ps-poll */ + ar5210WriteAssocid(ah, ahp->ah_bssid, ahp->ah_associd); OS_REG_WRITE(ah, AR_TXDP0, 0); OS_REG_WRITE(ah, AR_TXDP1, 0); diff --git a/sys/dev/ath/ath_hal/ar5211/ar5211.h b/sys/dev/ath/ath_hal/ar5211/ar5211.h index c50531e..6f04624 100644 --- a/sys/dev/ath/ath_hal/ar5211/ar5211.h +++ b/sys/dev/ath/ath_hal/ar5211/ar5211.h @@ -119,7 +119,6 @@ struct ath_hal_5211 { uint32_t ah_txEolInterruptMask; uint32_t ah_txUrnInterruptMask; HAL_TX_QUEUE_INFO ah_txq[HAL_NUM_TX_QUEUES]; - HAL_POWER_MODE ah_powerMode; HAL_ANT_SETTING ah_diversityControl; /* antenna setting */ uint32_t ah_calibrationTime; HAL_BOOL ah_bIQCalibration; diff --git a/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c b/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c index 4549295..3416dc0 100644 --- a/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c +++ b/sys/dev/ath/ath_hal/ar5211/ar5211_attach.c @@ -203,7 +203,7 @@ ar5211GetRadioRev(struct ath_hal *ah) static struct ath_hal * ar5211Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define N(a) (sizeof(a)/sizeof(a[0])) struct ath_hal_5211 *ahp; diff --git a/sys/dev/ath/ath_hal/ar5211/ar5211_power.c b/sys/dev/ath/ath_hal/ar5211/ar5211_power.c index 776cfb3..e646d90 100644 --- a/sys/dev/ath/ath_hal/ar5211/ar5211_power.c +++ b/sys/dev/ath/ath_hal/ar5211/ar5211_power.c @@ -95,7 +95,6 @@ ar5211SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5211SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5211 *ahp = AH5211(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -107,25 +106,30 @@ ar5211SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5211SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5211SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5211SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; - return status; + return status; } HAL_POWER_MODE diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212.h b/sys/dev/ath/ath_hal/ar5212/ar5212.h index 6d38d65..938a68c 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212.h +++ b/sys/dev/ath/ath_hal/ar5212/ar5212.h @@ -200,6 +200,7 @@ struct ar5212AniState { #define HAL_ANI_ENA 0x00000001 /* ANI operation enabled */ #define HAL_RSSI_ANI_ENA 0x00000002 /* rssi-based processing ena'd*/ +#if 0 struct ar5212Stats { uint32_t ast_ani_niup; /* ANI increased noise immunity */ uint32_t ast_ani_nidown; /* ANI decreased noise immunity */ @@ -219,6 +220,7 @@ struct ar5212Stats { HAL_MIB_STATS ast_mibstats; /* MIB counter stats */ HAL_NODE_STATS ast_nodestats; /* Latest rssi stats from driver */ }; +#endif /* * NF Cal history buffer @@ -258,7 +260,7 @@ struct ath_hal_5212 { * Runtime state. */ uint32_t ah_maskReg; /* copy of AR_IMR */ - struct ar5212Stats ah_stats; /* various statistics */ + HAL_ANI_STATS ah_stats; /* various statistics */ RF_HAL_FUNCS *ah_rfHal; uint32_t ah_txDescMask; /* mask for TXDESC */ uint32_t ah_txOkInterruptMask; @@ -270,7 +272,6 @@ struct ath_hal_5212 { uint32_t ah_intrTxqs; /* tx q interrupt state */ /* decomp mask array */ uint8_t ah_decompMask[HAL_DECOMP_MASK_SIZE]; - HAL_POWER_MODE ah_powerMode; HAL_ANT_SETTING ah_antControl; /* antenna setting */ HAL_BOOL ah_diversity; /* fast diversity setting */ enum { @@ -320,7 +321,6 @@ struct ath_hal_5212 { struct ar5212AniParams ah_aniParams5; /* 5GHz parameters */ struct ar5212AniState *ah_curani; /* cached last reference */ struct ar5212AniState ah_ani[AH_MAXCHAN]; /* per-channel state */ - HAL_CHANNEL_SURVEY ah_chansurvey; /* channel survey */ /* AR5416 uses some of the AR5212 ANI code; these are the ANI methods */ HAL_BOOL (*ah_aniControl) (struct ath_hal *, HAL_ANI_CMD cmd, int param); @@ -345,6 +345,9 @@ struct ath_hal_5212 { uint32_t ah_txBusy; uint32_t ah_rx_chainmask; uint32_t ah_tx_chainmask; + + /* Used to return ANI statistics to the diagnostic API */ + HAL_ANI_STATS ext_ani_stats; }; #define AH5212(_ah) ((struct ath_hal_5212 *)(_ah)) @@ -627,7 +630,7 @@ extern void ar5212AniAttach(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *, HAL_BOOL ena); extern void ar5212AniDetach(struct ath_hal *); extern struct ar5212AniState *ar5212AniGetCurrentState(struct ath_hal *); -extern struct ar5212Stats *ar5212AniGetCurrentStats(struct ath_hal *); +extern HAL_ANI_STATS *ar5212AniGetCurrentStats(struct ath_hal *); extern HAL_BOOL ar5212AniControl(struct ath_hal *, HAL_ANI_CMD cmd, int param); extern HAL_BOOL ar5212AniSetParams(struct ath_hal *, const struct ar5212AniParams *, const struct ar5212AniParams *); diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c b/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c index 8e87a2f..c7b5932 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c @@ -110,7 +110,7 @@ ar5212AniGetCurrentState(struct ath_hal *ah) /* * Return the current statistics. */ -struct ar5212Stats * +HAL_ANI_STATS * ar5212AniGetCurrentStats(struct ath_hal *ah) { struct ath_hal_5212 *ahp = AH5212(ah); @@ -869,7 +869,6 @@ ar5212AniGetListenTime(struct ath_hal *ah) int32_t listenTime = 0; int good; HAL_SURVEY_SAMPLE hs; - HAL_CHANNEL_SURVEY *cs = AH_NULL; /* * We shouldn't see ah_curchan be NULL, but just in case.. @@ -879,21 +878,13 @@ ar5212AniGetListenTime(struct ath_hal *ah) return (0); } - cs = &ahp->ah_chansurvey; - /* * Fetch the current statistics, squirrel away the current * sample, bump the sequence/sample counter. */ OS_MEMZERO(&hs, sizeof(hs)); good = ar5212GetMibCycleCounts(ah, &hs); - if (cs != AH_NULL) { - OS_MEMCPY(&cs->samples[cs->cur_sample], &hs, sizeof(hs)); - cs->samples[cs->cur_sample].seq_num = cs->cur_seq; - cs->cur_sample = - (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; - cs->cur_seq++; - } + ath_hal_survey_add_sample(ah, &hs); if (ANI_ENA(ah)) aniState = ahp->ah_curani; diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c b/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c index e0af27c..a95f244 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_attach.c @@ -317,7 +317,7 @@ ar5212IsMacSupported(uint8_t macVersion, uint8_t macRev) static struct ath_hal * ar5212Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { #define AH_EEPROM_PROTECT(ah) \ (AH_PRIVATE(ah)->ah_ispcie)? AR_EEPROM_PROTECT_PCIE : AR_EEPROM_PROTECT) diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c b/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c index 71ee845..e2f1eb7 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_misc.c @@ -1052,6 +1052,7 @@ ar5212GetDiagState(struct ath_hal *ah, int request, void **result, uint32_t *resultsize) { struct ath_hal_5212 *ahp = AH5212(ah); + HAL_ANI_STATS *astats; (void) ahp; if (ath_hal_getdiagstate(ah, request, args, argsize, result, resultsize)) @@ -1083,9 +1084,16 @@ ar5212GetDiagState(struct ath_hal *ah, int request, 0 : sizeof(struct ar5212AniState); return AH_TRUE; case HAL_DIAG_ANI_STATS: - *result = ar5212AniGetCurrentStats(ah); - *resultsize = (*result == AH_NULL) ? - 0 : sizeof(struct ar5212Stats); + OS_MEMZERO(&ahp->ext_ani_stats, sizeof(ahp->ext_ani_stats)); + astats = ar5212AniGetCurrentStats(ah); + if (astats == NULL) { + *result = NULL; + *resultsize = 0; + } else { + OS_MEMCPY(&ahp->ext_ani_stats, astats, sizeof(HAL_ANI_STATS)); + *result = &ahp->ext_ani_stats; + *resultsize = sizeof(ahp->ext_ani_stats); + } return AH_TRUE; case HAL_DIAG_ANI_CMD: if (argsize != 2*sizeof(uint32_t)) @@ -1113,10 +1121,6 @@ ar5212GetDiagState(struct ath_hal *ah, int request, return ar5212AniSetParams(ah, args, args); } break; - case HAL_DIAG_CHANSURVEY: - *result = &ahp->ah_chansurvey; - *resultsize = sizeof(HAL_CHANNEL_SURVEY); - return AH_TRUE; } return AH_FALSE; } diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_power.c b/sys/dev/ath/ath_hal/ar5212/ar5212_power.c index 3f755bd..3068510 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_power.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_power.c @@ -119,7 +119,6 @@ ar5212SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -131,24 +130,29 @@ ar5212SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5212SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5212SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5212SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode %u\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; return status; } diff --git a/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c b/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c index ccdccb6..fd3b473 100644 --- a/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c +++ b/sys/dev/ath/ath_hal/ar5212/ar5212_reset.c @@ -197,7 +197,8 @@ ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode, saveFrameSeqCount = 0; /* NB: silence compiler */ /* Blank the channel survey statistics */ - OS_MEMZERO(&ahp->ah_chansurvey, sizeof(ahp->ah_chansurvey)); + ath_hal_survey_clear(ah); + #if 0 /* * XXX disable for now; this appears to sometimes cause OFDM diff --git a/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c b/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c index 4ca1a4d..5c84eb8 100644 --- a/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c +++ b/sys/dev/ath/ath_hal/ar5312/ar5312_attach.c @@ -62,7 +62,7 @@ ar5312AniSetup(struct ath_hal *ah) static struct ath_hal * ar5312Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5212 *ahp = AH_NULL; struct ath_hal *ah; diff --git a/sys/dev/ath/ath_hal/ar5312/ar5312_power.c b/sys/dev/ath/ath_hal/ar5312/ar5312_power.c index 94a0f1c..7db6d80 100644 --- a/sys/dev/ath/ath_hal/ar5312/ar5312_power.c +++ b/sys/dev/ath/ath_hal/ar5312/ar5312_power.c @@ -71,7 +71,6 @@ ar5312SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -83,7 +82,7 @@ ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) int status = AH_TRUE; HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: @@ -100,7 +99,7 @@ ar5312SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; + ah->ah_powerMode = mode; return status; } diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c b/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c index 3f9bc69..fadd5ad 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_ani.c @@ -818,7 +818,6 @@ ar5416AniGetListenTime(struct ath_hal *ah) int32_t listenTime = 0; int good; HAL_SURVEY_SAMPLE hs; - HAL_CHANNEL_SURVEY *cs = AH_NULL; /* * We shouldn't see ah_curchan be NULL, but just in case.. @@ -828,21 +827,13 @@ ar5416AniGetListenTime(struct ath_hal *ah) return (0); } - cs = &ahp->ah_chansurvey; - /* * Fetch the current statistics, squirrel away the current - * sample, bump the sequence/sample counter. + * sample. */ OS_MEMZERO(&hs, sizeof(hs)); good = ar5416GetMibCycleCounts(ah, &hs); - if (cs != AH_NULL) { - OS_MEMCPY(&cs->samples[cs->cur_sample], &hs, sizeof(hs)); - cs->samples[cs->cur_sample].seq_num = cs->cur_seq; - cs->cur_sample = - (cs->cur_sample + 1) % CHANNEL_SURVEY_SAMPLE_COUNT; - cs->cur_seq++; - } + ath_hal_survey_add_sample(ah, &hs); if (ANI_ENA(ah)) aniState = ahp->ah_curani; diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c index 99bab06..a20499a 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c @@ -297,7 +297,7 @@ ar5416GetRadioRev(struct ath_hal *ah) static struct ath_hal * ar5416Attach(uint16_t devid, HAL_SOFTC sc, HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, - HAL_STATUS *status) + HAL_OPS_CONFIG *ah_config, HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; @@ -1059,6 +1059,11 @@ ar5416FillCapabilityInfo(struct ath_hal *ah) if (! AH_PRIVATE(ah)->ah_ispcie) pCap->halSerialiseRegWar = 1; + /* + * AR5416 and later NICs support MYBEACON filtering. + */ + pCap->halRxDoMyBeacon = AH_TRUE; + return AH_TRUE; } diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c b/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c index e2bf6c7..6691c11 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_beacon.c @@ -197,6 +197,25 @@ ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) * beacon jitter; cab timeout is max time to wait for cab * after seeing the last DTIM or MORE CAB bit */ + +/* + * I've bumped these to 30TU for now. + * + * Some APs (AR933x/AR934x?) in 2GHz especially seem to not always + * transmit beacon frames at exactly the right times and with it set + * to 10TU, the NIC starts not waking up at the right times to hear + * these slightly-larger-jitering beacons. It also never recovers + * from that (it doesn't resync? I'm not sure.) + * + * So for now bump this to 30TU. Ideally we'd cap this based on + * the beacon interval so the sum of CAB+BEACON timeouts never + * exceeded the beacon interval. + * + * Now, since we're doing all the math in the ath(4) driver in TU + * rather than TSF, we may be seeing the result of dumb rounding + * errors causing the jitter to actually be a much bigger problem. + * I'll have to investigate that with a fine tooth comb. + */ #define CAB_TIMEOUT_VAL 10 /* in TU */ #define BEACON_TIMEOUT_VAL 10 /* in TU */ #define SLEEP_SLOP 3 /* in TU */ @@ -248,6 +267,13 @@ ar5416SetStaBeaconTimers(struct ath_hal *ah, const HAL_BEACON_STATE *bs) OS_REG_SET_BIT(ah, AR_TIMER_MODE, AR_TIMER_MODE_TBTT | AR_TIMER_MODE_TIM | AR_TIMER_MODE_DTIM); + +#define HAL_TSFOOR_THRESHOLD 0x00004240 /* TSF OOR threshold (16k us) */ + + /* TSF out of range threshold */ +// OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, bs->bs_tsfoor_threshold); + OS_REG_WRITE(ah, AR_TSFOOR_THRESHOLD, HAL_TSFOOR_THRESHOLD); + HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next DTIM %d\n", __func__, bs->bs_nextdtim); HALDEBUG(ah, HAL_DEBUG_BEACON, "%s: next beacon %d\n", diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c b/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c index d51417f4..35bb8d4 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c @@ -663,7 +663,7 @@ ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan) * by the median we just loaded. This will be initial (and max) value * of next noise floor calibration the baseband does. */ - for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) + for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) { /* Don't write to EXT radio CCA registers unless in HT/40 mode */ /* XXX this check should really be cleaner! */ @@ -676,6 +676,7 @@ ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan) val |= (((uint32_t)(-50) << 1) & 0x1ff); OS_REG_WRITE(ah, ar5416_cca_regs[i], val); } + } } /* diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c b/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c index 631ca2f..32ce2ed 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_interrupts.c @@ -337,6 +337,9 @@ ar5416SetInterrupts(struct ath_hal *ah, HAL_INT ints) /* Write the new IMR and store off our SW copy. */ HALDEBUG(ah, HAL_DEBUG_INTERRUPT, "%s: new IMR 0x%x\n", __func__, mask); OS_REG_WRITE(ah, AR_IMR, mask); + /* Flush write */ + (void) OS_REG_READ(ah, AR_IMR); + mask = OS_REG_READ(ah, AR_IMR_S2) & ~(AR_IMR_S2_TIM | AR_IMR_S2_DTIM | AR_IMR_S2_DTIMSYNC | diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_power.c b/sys/dev/ath/ath_hal/ar5416/ar5416_power.c index 43a9241..dff9a85 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_power.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_power.c @@ -124,7 +124,6 @@ ar5416SetPowerModeNetworkSleep(struct ath_hal *ah, int setChip) HAL_BOOL ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) { - struct ath_hal_5212 *ahp = AH5212(ah); #ifdef AH_DEBUG static const char* modes[] = { "AWAKE", @@ -134,27 +133,35 @@ ar5416SetPowerMode(struct ath_hal *ah, HAL_POWER_MODE mode, int setChip) }; #endif int status = AH_TRUE; + +#if 0 if (!setChip) return AH_TRUE; +#endif HALDEBUG(ah, HAL_DEBUG_POWER, "%s: %s -> %s (%s)\n", __func__, - modes[ahp->ah_powerMode], modes[mode], setChip ? "set chip " : ""); + modes[ah->ah_powerMode], modes[mode], setChip ? "set chip " : ""); switch (mode) { case HAL_PM_AWAKE: + if (setChip) + ah->ah_powerMode = mode; status = ar5416SetPowerModeAwake(ah, setChip); break; case HAL_PM_FULL_SLEEP: ar5416SetPowerModeSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; case HAL_PM_NETWORK_SLEEP: ar5416SetPowerModeNetworkSleep(ah, setChip); + if (setChip) + ah->ah_powerMode = mode; break; default: HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown power mode 0x%x\n", __func__, mode); return AH_FALSE; } - ahp->ah_powerMode = mode; return status; } diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c index eb31f08..baf0ccc 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c @@ -120,9 +120,10 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); /* Blank the channel survey statistics */ - OS_MEMZERO(&ahp->ah_chansurvey, sizeof(ahp->ah_chansurvey)); + ath_hal_survey_clear(ah); /* XXX Turn on fast channel change for 5416 */ + /* * Preserve the bmiss rssi threshold and count threshold * across resets diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416reg.h b/sys/dev/ath/ath_hal/ar5416/ar5416reg.h index 435599c..0ee1e70 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416reg.h +++ b/sys/dev/ath/ath_hal/ar5416/ar5416reg.h @@ -476,10 +476,10 @@ /* Sleep control */ #define AR5416_SLEEP1_ASSUME_DTIM 0x00080000 #define AR5416_SLEEP1_CAB_TIMEOUT 0xFFE00000 /* Cab timeout (TU) */ -#define AR5416_SLEEP1_CAB_TIMEOUT_S 22 +#define AR5416_SLEEP1_CAB_TIMEOUT_S 21 #define AR5416_SLEEP2_BEACON_TIMEOUT 0xFFE00000 /* Beacon timeout (TU)*/ -#define AR5416_SLEEP2_BEACON_TIMEOUT_S 22 +#define AR5416_SLEEP2_BEACON_TIMEOUT_S 21 /* Sleep Registers */ #define AR_SLP32_HALFCLK_LATENCY 0x000FFFFF /* rising <-> falling edge */ diff --git a/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c b/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c index 4f478c0..c270bab 100644 --- a/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c +++ b/sys/dev/ath/ath_hal/ar9001/ar9130_attach.c @@ -69,7 +69,9 @@ static HAL_BOOL ar9130FillCapabilityInfo(struct ath_hal *ah); */ static struct ath_hal * ar9130Attach(uint16_t devid, HAL_SOFTC sc, - HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, HAL_STATUS *status) + HAL_BUS_TAG st, HAL_BUS_HANDLE sh, uint16_t *eepromdata, + HAL_OPS_CONFIG *ah_config, + HAL_STATUS *status) { struct ath_hal_5416 *ahp5416; struct ath_hal_5212 *ahp; diff --git a/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c b/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c index 979ba1a..5bda519 100644 --- a/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c +++ b/sys/dev/ath/ath_hal/ar9001/ar9160_attach.c @@ -114,6 +114,7 @@ ar9160InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) static struct ath_hal * ar9160Attach(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_5416 *ahp5416; diff --git a/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c b/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c index 2a67fe5..3be3e35 100644 --- a/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c +++ b/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c @@ -148,6 +148,7 @@ ar9280InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) static struct ath_hal * ar9280Attach(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_9280 *ahp9280; diff --git a/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c b/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c index edb6f26..eb3490a 100644 --- a/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c +++ b/sys/dev/ath/ath_hal/ar9002/ar9285_attach.c @@ -133,6 +133,7 @@ ar9285_eeprom_print_diversity_settings(struct ath_hal *ah) static struct ath_hal * ar9285Attach(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_9285 *ahp9285; diff --git a/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c b/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c index 010e2c3..0ea565c 100644 --- a/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c +++ b/sys/dev/ath/ath_hal/ar9002/ar9287_attach.c @@ -111,6 +111,7 @@ ar9287AniSetup(struct ath_hal *ah) static struct ath_hal * ar9287Attach(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_9287 *ahp9287; diff --git a/sys/dev/ath/ath_rate/sample/sample.c b/sys/dev/ath/ath_rate/sample/sample.c index b3f82fa..36cd679 100644 --- a/sys/dev/ath/ath_rate/sample/sample.c +++ b/sys/dev/ath/ath_rate/sample/sample.c @@ -51,6 +51,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/kernel.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/errno.h> @@ -61,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_arp.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ @@ -889,8 +891,8 @@ ath_rate_tx_complete(struct ath_softc *sc, struct ath_node *an, if (!mrr || ts->ts_finaltsi == 0) { if (!IS_RATE_DEFINED(sn, final_rix)) { - device_printf(sc->sc_dev, "%s: ts_rate=%d ts_finaltsi=%d\n", - __func__, ts->ts_rate, ts->ts_finaltsi); + device_printf(sc->sc_dev, "%s: ts_rate=%d ts_finaltsi=%d, final_rix=%d\n", + __func__, ts->ts_rate, ts->ts_finaltsi, final_rix); badrate(ifp, 0, ts->ts_rate, long_tries, status); return; } diff --git a/sys/dev/ath/ath_rate/sample/sample.h b/sys/dev/ath/ath_rate/sample/sample.h index 7438a3d..1c57dee 100644 --- a/sys/dev/ath/ath_rate/sample/sample.h +++ b/sys/dev/ath/ath_rate/sample/sample.h @@ -113,7 +113,7 @@ struct sample_node { #ifdef _KERNEL #define ATH_NODE_SAMPLE(an) ((struct sample_node *)&(an)[1]) -#define IS_RATE_DEFINED(sn, rix) (((sn)->ratemask & (1<<(rix))) != 0) +#define IS_RATE_DEFINED(sn, rix) (((uint64_t) (sn)->ratemask & (1ULL<<((uint64_t) rix))) != 0) #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 6e58896..3787500 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -164,6 +165,7 @@ static void ath_bmiss_vap(struct ieee80211vap *); static void ath_bmiss_proc(void *, int); static void ath_key_update_begin(struct ieee80211vap *); static void ath_key_update_end(struct ieee80211vap *); +static void ath_update_mcast_hw(struct ath_softc *); static void ath_update_mcast(struct ifnet *); static void ath_update_promisc(struct ifnet *); static void ath_updateslot(struct ifnet *); @@ -238,17 +240,14 @@ SYSCTL_INT(_hw_ath, OID_AUTO, anical, CTLFLAG_RW, &ath_anicalinterval, 0, "ANI calibration (msecs)"); int ath_rxbuf = ATH_RXBUF; /* # rx buffers to allocate */ -SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RW, &ath_rxbuf, +SYSCTL_INT(_hw_ath, OID_AUTO, rxbuf, CTLFLAG_RWTUN, &ath_rxbuf, 0, "rx buffers allocated"); -TUNABLE_INT("hw.ath.rxbuf", &ath_rxbuf); int ath_txbuf = ATH_TXBUF; /* # tx buffers to allocate */ -SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RW, &ath_txbuf, +SYSCTL_INT(_hw_ath, OID_AUTO, txbuf, CTLFLAG_RWTUN, &ath_txbuf, 0, "tx buffers allocated"); -TUNABLE_INT("hw.ath.txbuf", &ath_txbuf); int ath_txbuf_mgmt = ATH_MGMT_TXBUF; /* # mgmt tx buffers to allocate */ -SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RW, &ath_txbuf_mgmt, +SYSCTL_INT(_hw_ath, OID_AUTO, txbuf_mgmt, CTLFLAG_RWTUN, &ath_txbuf_mgmt, 0, "tx (mgmt) buffers allocated"); -TUNABLE_INT("hw.ath.txbuf_mgmt", &ath_txbuf_mgmt); int ath_bstuck_threshold = 4; /* max missed beacons */ SYSCTL_INT(_hw_ath, OID_AUTO, bstuck, CTLFLAG_RW, &ath_bstuck_threshold, @@ -278,6 +277,293 @@ ath_legacy_attach_comp_func(struct ath_softc *sc) } } +/* + * Set the target power mode. + * + * If this is called during a point in time where + * the hardware is being programmed elsewhere, it will + * simply store it away and update it when all current + * uses of the hardware are completed. + */ +void +_ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line) +{ + ATH_LOCK_ASSERT(sc); + + sc->sc_target_powerstate = power_state; + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_powersave_refcnt); + + if (sc->sc_powersave_refcnt == 0 && + power_state != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = power_state; + ath_hal_setpower(sc->sc_ah, power_state); + + /* + * If the NIC is force-awake, then set the + * self-gen frame state appropriately. + * + * If the nic is in network sleep or full-sleep, + * we let the above call leave the self-gen + * state as "sleep". + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE && + sc->sc_target_selfgen_state != HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, + sc->sc_target_selfgen_state); + } + } +} + +/* + * Set the current self-generated frames state. + * + * This is separate from the target power mode. The chip may be + * awake but the desired state is "sleep", so frames sent to the + * destination has PWRMGT=1 in the 802.11 header. The NIC also + * needs to know to set PWRMGT=1 in self-generated frames. + */ +void +_ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line) +{ + + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_target_selfgen_state); + + sc->sc_target_selfgen_state = power_state; + + /* + * If the NIC is force-awake, then set the power state. + * Network-state and full-sleep will already transition it to + * mark self-gen frames as sleeping - and we can't + * guarantee the NIC is awake to program the self-gen frame + * setting anyway. + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, power_state); + } +} + +/* + * Set the hardware power mode and take a reference. + * + * This doesn't update the target power mode in the driver; + * it just updates the hardware power state. + * + * XXX it should only ever force the hardware awake; it should + * never be called to set it asleep. + */ +void +_ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line) +{ + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) state=%d, refcnt=%d\n", + __func__, + file, + line, + power_state, + sc->sc_powersave_refcnt); + + sc->sc_powersave_refcnt++; + + if (power_state != sc->sc_cur_powerstate) { + ath_hal_setpower(sc->sc_ah, power_state); + sc->sc_cur_powerstate = power_state; + + /* + * Adjust the self-gen powerstate if appropriate. + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE && + sc->sc_target_selfgen_state != HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, + sc->sc_target_selfgen_state); + } + + } +} + +/* + * Restore the power save mode to what it once was. + * + * This will decrement the reference counter and once it hits + * zero, it'll restore the powersave state. + */ +void +_ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line) +{ + + ATH_LOCK_ASSERT(sc); + + DPRINTF(sc, ATH_DEBUG_PWRSAVE, "%s: (%s:%d) refcnt=%d, target state=%d\n", + __func__, + file, + line, + sc->sc_powersave_refcnt, + sc->sc_target_powerstate); + + if (sc->sc_powersave_refcnt == 0) + device_printf(sc->sc_dev, "%s: refcnt=0?\n", __func__); + else + sc->sc_powersave_refcnt--; + + if (sc->sc_powersave_refcnt == 0 && + sc->sc_target_powerstate != sc->sc_cur_powerstate) { + sc->sc_cur_powerstate = sc->sc_target_powerstate; + ath_hal_setpower(sc->sc_ah, sc->sc_target_powerstate); + } + + /* + * Adjust the self-gen powerstate if appropriate. + */ + if (sc->sc_cur_powerstate == HAL_PM_AWAKE && + sc->sc_target_selfgen_state != HAL_PM_AWAKE) { + ath_hal_setselfgenpower(sc->sc_ah, + sc->sc_target_selfgen_state); + } + +} + +/* + * Configure the initial HAL configuration values based on bus + * specific parameters. + * + * Some PCI IDs and other information may need tweaking. + * + * XXX TODO: ath9k and the Atheros HAL only program comm2g_switch_enable + * if BT antenna diversity isn't enabled. + * + * So, let's also figure out how to enable BT diversity for AR9485. + */ +static void +ath_setup_hal_config(struct ath_softc *sc, HAL_OPS_CONFIG *ah_config) +{ + /* XXX TODO: only for PCI devices? */ + + if (sc->sc_pci_devinfo & (ATH_PCI_CUS198 | ATH_PCI_CUS230)) { + ah_config->ath_hal_ext_lna_ctl_gpio = 0x200; /* bit 9 */ + ah_config->ath_hal_ext_atten_margin_cfg = AH_TRUE; + ah_config->ath_hal_min_gainidx = AH_TRUE; + ah_config->ath_hal_ant_ctrl_comm2g_switch_enable = 0x000bbb88; + /* XXX low_rssi_thresh */ + /* XXX fast_div_bias */ + device_printf(sc->sc_dev, "configuring for %s\n", + (sc->sc_pci_devinfo & ATH_PCI_CUS198) ? + "CUS198" : "CUS230"); + } + + if (sc->sc_pci_devinfo & ATH_PCI_CUS217) + device_printf(sc->sc_dev, "CUS217 card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_CUS252) + device_printf(sc->sc_dev, "CUS252 card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_AR9565_1ANT) + device_printf(sc->sc_dev, "WB335 1-ANT card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_AR9565_2ANT) + device_printf(sc->sc_dev, "WB335 2-ANT card detected\n"); + + if (sc->sc_pci_devinfo & ATH_PCI_KILLER) + device_printf(sc->sc_dev, "Killer Wireless card detected\n"); + +#if 0 + /* + * Some WB335 cards do not support antenna diversity. Since + * we use a hardcoded value for AR9565 instead of using the + * EEPROM/OTP data, remove the combining feature from + * the HW capabilities bitmap. + */ + if (sc->sc_pci_devinfo & (ATH9K_PCI_AR9565_1ANT | ATH9K_PCI_AR9565_2ANT)) { + if (!(sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV)) + pCap->hw_caps &= ~ATH9K_HW_CAP_ANT_DIV_COMB; + } + + if (sc->sc_pci_devinfo & ATH9K_PCI_BT_ANT_DIV) { + pCap->hw_caps |= ATH9K_HW_CAP_BT_ANT_DIV; + device_printf(sc->sc_dev, "Set BT/WLAN RX diversity capability\n"); + } +#endif + + if (sc->sc_pci_devinfo & ATH_PCI_D3_L1_WAR) { + ah_config->ath_hal_pcie_waen = 0x0040473b; + device_printf(sc->sc_dev, "Enable WAR for ASPM D3/L1\n"); + } + +#if 0 + if (sc->sc_pci_devinfo & ATH9K_PCI_NO_PLL_PWRSAVE) { + ah->config.no_pll_pwrsave = true; + device_printf(sc->sc_dev, "Disable PLL PowerSave\n"); + } +#endif + +} + +/* + * Attempt to fetch the MAC address from the kernel environment. + * + * Returns 0, macaddr in macaddr if successful; -1 otherwise. + */ +static int +ath_fetch_mac_kenv(struct ath_softc *sc, uint8_t *macaddr) +{ + char devid_str[32]; + int local_mac = 0; + char *local_macstr; + + /* + * Fetch from the kenv rather than using hints. + * + * Hints would be nice but the transition to dynamic + * hints/kenv doesn't happen early enough for this + * to work reliably (eg on anything embedded.) + */ + snprintf(devid_str, 32, "hint.%s.%d.macaddr", + device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev)); + + if ((local_macstr = getenv(devid_str)) != NULL) { + uint32_t tmpmac[ETHER_ADDR_LEN]; + int count; + int i; + + /* Have a MAC address; should use it */ + device_printf(sc->sc_dev, + "Overriding MAC address from environment: '%s'\n", + local_macstr); + + /* Extract out the MAC address */ + count = sscanf(local_macstr, "%x%*c%x%*c%x%*c%x%*c%x%*c%x", + &tmpmac[0], &tmpmac[1], + &tmpmac[2], &tmpmac[3], + &tmpmac[4], &tmpmac[5]); + if (count == 6) { + /* Valid! */ + local_mac = 1; + for (i = 0; i < ETHER_ADDR_LEN; i++) + macaddr[i] = tmpmac[i]; + } + /* Done! */ + freeenv(local_macstr); + local_macstr = NULL; + } + + if (local_mac) + return (0); + return (-1); +} + #define HAL_MODE_HT20 (HAL_MODE_11NG_HT20 | HAL_MODE_11NA_HT20) #define HAL_MODE_HT40 \ (HAL_MODE_11NG_HT40PLUS | HAL_MODE_11NG_HT40MINUS | \ @@ -293,6 +579,7 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) u_int wmodes; uint8_t macaddr[IEEE80211_ADDR_LEN]; int rx_chainmask, tx_chainmask; + HAL_OPS_CONFIG ah_config; DPRINTF(sc, ATH_DEBUG_ANY, "%s: devid 0x%x\n", __func__, devid); @@ -311,8 +598,17 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) device_get_unit(sc->sc_dev)); CURVNET_RESTORE(); + /* + * Configure the initial configuration data. + * + * This is stuff that may be needed early during attach + * rather than done via configuration calls later. + */ + 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, &status); + sc->sc_eepromdata, &ah_config, &status); if (ah == NULL) { if_printf(ifp, "unable to attach hardware; HAL status %u\n", status); @@ -340,6 +636,10 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) ath_xmit_setup_legacy(sc); } + if (ath_hal_hasmybeacon(sc->sc_ah)) { + sc->sc_do_mybeacon = 1; + } + /* * Check if the MAC has multi-rate retry support. * We do this by trying to setup a fake extended @@ -604,6 +904,8 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) #ifdef ATH_ENABLE_DFS | IEEE80211_C_DFS /* Enable radar detection */ #endif + | IEEE80211_C_PMGT /* Station side power mgmt */ + | IEEE80211_C_SWSLEEP ; /* * Query the hal to figure out h/w crypto support. @@ -901,8 +1203,14 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) */ sc->sc_hasveol = ath_hal_hasveol(ah); - /* get mac address from hardware */ - ath_hal_getmac(ah, macaddr); + /* get mac address from kenv first, then hardware */ + if (ath_fetch_mac_kenv(sc, macaddr) == 0) { + /* Tell the HAL now about the new MAC */ + ath_hal_setmac(ah, macaddr); + } else { + ath_hal_getmac(ah, macaddr); + } + if (sc->sc_hasbmask) ath_hal_getbssidmask(ah, sc->sc_hwbssidmask); @@ -993,6 +1301,14 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) if (bootverbose) ieee80211_announce(ic); ath_announce(sc); + + /* + * Put it to sleep for now. + */ + ATH_LOCK(sc); + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + ATH_UNLOCK(sc); + return 0; bad2: ath_tx_cleanup(sc); @@ -1038,7 +1354,22 @@ ath_detach(struct ath_softc *sc) * it last * Other than that, it's straightforward... */ + + /* + * XXX Wake the hardware up first. ath_stop() will still + * wake it up first, but I'd rather do it here just to + * ensure it's awake. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + /* + * Stop things cleanly. + */ ath_stop(ifp); + ieee80211_ifdetach(ifp->if_l2com); taskqueue_free(sc->sc_tq); #ifdef ATH_TX99_DIAG @@ -1401,6 +1732,10 @@ ath_vap_delete(struct ieee80211vap *vap) struct ath_hal *ah = sc->sc_ah; struct ath_vap *avp = ATH_VAP(vap); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_RESET, "%s: called\n", __func__); if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* @@ -1409,11 +1744,13 @@ ath_vap_delete(struct ieee80211vap *vap) * the vap state by any frames pending on the tx queues. */ ath_hal_intrset(ah, 0); /* disable interrupts */ - ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */ /* XXX Do all frames from all vaps/nodes need draining here? */ ath_stoprecv(sc, 1); /* stop recv side */ + ath_draintxq(sc, ATH_RESET_DEFAULT); /* stop hw xmit side */ } + /* .. leave the hardware awake for now. */ + ieee80211_vap_detach(vap); /* @@ -1501,6 +1838,9 @@ ath_vap_delete(struct ieee80211vap *vap) } ath_hal_intrset(ah, sc->sc_imask); } + + /* Ok, let the hardware asleep. */ + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } @@ -1520,12 +1860,25 @@ ath_suspend(struct ath_softc *sc) * NB: don't worry about putting the chip in low power * mode; pci will power off our socket on suspend and * CardBus detaches the device. + * + * XXX TODO: well, that's great, except for non-cardbus + * devices! */ /* - * XXX ensure none of the taskqueues are running + * XXX This doesn't wait until all pending taskqueue + * items and parallel transmit/receive/other threads + * are running! + */ + ath_hal_intrset(sc->sc_ah, 0); + taskqueue_block(sc->sc_tq); + + ATH_LOCK(sc); + callout_stop(&sc->sc_cal_ch); + ATH_UNLOCK(sc); + + /* * XXX ensure sc_invalid is 1 - * XXX ensure the calibration callout is disabled */ /* Disable the PCIe PHY, complete with workarounds */ @@ -1546,8 +1899,12 @@ ath_reset_keycache(struct ath_softc *sc) struct ath_hal *ah = sc->sc_ah; int i; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); for (i = 0; i < sc->sc_keymax; i++) ath_hal_keyreset(ah, i); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); ieee80211_crypto_reload_keys(ic); } @@ -1599,11 +1956,24 @@ ath_resume(struct ath_softc *sc) sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); + + /* Ensure we set the current power state to on */ + ATH_LOCK(sc); + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_reset(ah, sc->sc_opmode, sc->sc_curchan != NULL ? sc->sc_curchan : ic->ic_curchan, AH_FALSE, &status); ath_reset_keycache(sc); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); @@ -1631,6 +2001,10 @@ ath_resume(struct ath_softc *sc) if (sc->sc_resume_up) ieee80211_resume_all(ic); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + /* XXX beacons ? */ } @@ -1688,6 +2062,10 @@ ath_intr(void *arg) return; } + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if ((ifp->if_flags & IFF_UP) == 0 || (ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { HAL_INT status; @@ -1697,6 +2075,10 @@ ath_intr(void *arg) ath_hal_getisr(ah, &status); /* clear ISR */ ath_hal_intrset(ah, 0); /* disable further intr's */ ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return; } @@ -1736,6 +2118,11 @@ ath_intr(void *arg) /* Short-circuit un-handled interrupts */ if (status == 0x0) { ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return; } @@ -1791,44 +2178,46 @@ ath_intr(void *arg) if (status & HAL_INT_RXEOL) { int imask; ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXEOL"); - ATH_PCU_LOCK(sc); - /* - * NB: the hardware should re-read the link when - * RXE bit is written, but it doesn't work at - * least on older hardware revs. - */ - sc->sc_stats.ast_rxeol++; - /* - * Disable RXEOL/RXORN - prevent an interrupt - * storm until the PCU logic can be reset. - * In case the interface is reset some other - * way before "sc_kickpcu" is called, don't - * modify sc_imask - that way if it is reset - * by a call to ath_reset() somehow, the - * interrupt mask will be correctly reprogrammed. - */ - imask = sc->sc_imask; - imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); - ath_hal_intrset(ah, imask); - /* - * Only blank sc_rxlink if we've not yet kicked - * the PCU. - * - * This isn't entirely correct - the correct solution - * would be to have a PCU lock and engage that for - * the duration of the PCU fiddling; which would include - * running the RX process. Otherwise we could end up - * messing up the RX descriptor chain and making the - * RX desc list much shorter. - */ - if (! sc->sc_kickpcu) - sc->sc_rxlink = NULL; - sc->sc_kickpcu = 1; - ATH_PCU_UNLOCK(sc); + if (! sc->sc_isedma) { + ATH_PCU_LOCK(sc); + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ + sc->sc_stats.ast_rxeol++; + /* + * Disable RXEOL/RXORN - prevent an interrupt + * storm until the PCU logic can be reset. + * In case the interface is reset some other + * way before "sc_kickpcu" is called, don't + * modify sc_imask - that way if it is reset + * by a call to ath_reset() somehow, the + * interrupt mask will be correctly reprogrammed. + */ + imask = sc->sc_imask; + imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); + ath_hal_intrset(ah, imask); + /* + * Only blank sc_rxlink if we've not yet kicked + * the PCU. + * + * This isn't entirely correct - the correct solution + * would be to have a PCU lock and engage that for + * the duration of the PCU fiddling; which would include + * running the RX process. Otherwise we could end up + * messing up the RX descriptor chain and making the + * RX desc list much shorter. + */ + if (! sc->sc_kickpcu) + sc->sc_rxlink = NULL; + sc->sc_kickpcu = 1; + ATH_PCU_UNLOCK(sc); + } /* - * Enqueue an RX proc, to handled whatever + * Enqueue an RX proc to handle whatever * is in the RX queue. - * This will then kick the PCU. + * This will then kick the PCU if required. */ sc->sc_rx.recv_sched(sc, 1); } @@ -1902,10 +2291,18 @@ ath_intr(void *arg) ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXORN"); sc->sc_stats.ast_rxorn++; } + if (status & HAL_INT_TSFOOR) { + device_printf(sc->sc_dev, "%s: TSFOOR\n", __func__); + sc->sc_syncbeacon = 1; + } } ATH_PCU_LOCK(sc); sc->sc_intr_cnt--; ATH_PCU_UNLOCK(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } static void @@ -1936,6 +2333,8 @@ ath_fatal_proc(void *arg, int pending) static void ath_bmiss_vap(struct ieee80211vap *vap) { + struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + /* * Workaround phantom bmiss interrupts by sanity-checking * the time of our last rx'd frame. If it is within the @@ -1944,6 +2343,16 @@ ath_bmiss_vap(struct ieee80211vap *vap) * be dispatched up for processing. Note this applies only * for h/w beacon miss events. */ + + /* + * XXX TODO: Just read the TSF during the interrupt path; + * that way we don't have to wake up again just to read it + * again. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if ((vap->iv_flags_ext & IEEE80211_FEXT_SWBMISS) == 0) { struct ifnet *ifp = vap->iv_ic->ic_ifp; struct ath_softc *sc = ifp->if_softc; @@ -1961,12 +2370,32 @@ ath_bmiss_vap(struct ieee80211vap *vap) if (tsf - lastrx <= bmisstimeout) { sc->sc_stats.ast_bmiss_phantom++; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return; } } + + /* + * There's no need to keep the hardware awake during the call + * to av_bmiss(). + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* + * Attempt to force a beacon resync. + */ + sc->sc_syncbeacon = 1; + ATH_VAP(vap)->av_bmiss(vap); } +/* XXX this needs a force wakeup! */ int ath_hal_gethangstate(struct ath_hal *ah, uint32_t mask, uint32_t *hangs) { @@ -1989,6 +2418,12 @@ ath_bmiss_proc(void *arg, int pending) DPRINTF(sc, ATH_DEBUG_ANY, "%s: pending %u\n", __func__, pending); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_beacon_miss(sc); + /* * Do a reset upon any becaon miss event. * @@ -2002,6 +2437,13 @@ ath_bmiss_proc(void *arg, int pending) ath_reset(ifp, ATH_RESET_NOLOSS); ieee80211_beacon_miss(ifp->if_l2com); } + + /* Force a beacon resync, in case they've drifted */ + sc->sc_syncbeacon = 1; + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } /* @@ -2041,6 +2483,13 @@ ath_init(void *arg) ATH_LOCK(sc); /* + * Force the sleep state awake. + */ + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + + /* * Stop anything previously setup. This is safe * whether this is the first time through or not. */ @@ -2057,12 +2506,19 @@ ath_init(void *arg) ath_update_chainmasks(sc, ic->ic_curchan); ath_hal_setchainmasks(sc->sc_ah, sc->sc_cur_txchainmask, sc->sc_cur_rxchainmask); + if (!ath_hal_reset(ah, sc->sc_opmode, ic->ic_curchan, AH_FALSE, &status)) { if_printf(ifp, "unable to reset hardware; hal status %u\n", status); ATH_UNLOCK(sc); return; } + + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + ath_chan_change(sc, ic->ic_curchan); /* Let DFS at it in case it's a DFS channel */ @@ -2090,11 +2546,11 @@ ath_init(void *arg) * state cached in the driver. */ sc->sc_diversity = ath_hal_getdiversity(ah); - sc->sc_lastlongcal = 0; + sc->sc_lastlongcal = ticks; sc->sc_resetcal = 1; sc->sc_lastcalreset = 0; - sc->sc_lastani = 0; - sc->sc_lastshortcal = 0; + sc->sc_lastani = ticks; + sc->sc_lastshortcal = ticks; sc->sc_doresetcal = AH_FALSE; /* * Beacon timers were cleared here; give ath_newstate() @@ -2112,6 +2568,7 @@ ath_init(void *arg) */ if (ath_startrecv(sc) != 0) { if_printf(ifp, "unable to start recv logic\n"); + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); return; } @@ -2120,8 +2577,7 @@ ath_init(void *arg) * Enable interrupts. */ sc->sc_imask = HAL_INT_RX | HAL_INT_TX - | HAL_INT_RXEOL | HAL_INT_RXORN - | HAL_INT_TXURN + | HAL_INT_RXORN | HAL_INT_TXURN | HAL_INT_FATAL | HAL_INT_GLOBAL; /* @@ -2132,12 +2588,29 @@ ath_init(void *arg) sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP); /* + * If we're an EDMA NIC, we don't care about RXEOL. + * Writing a new descriptor in will simply restart + * RX DMA. + */ + if (! sc->sc_isedma) + sc->sc_imask |= HAL_INT_RXEOL; + + /* * Enable MIB interrupts when there are hardware phy counters. * Note we only do this (at the moment) for station mode. */ if (sc->sc_needmib && ic->ic_opmode == IEEE80211_M_STA) sc->sc_imask |= HAL_INT_MIB; + /* + * XXX add capability for this. + * + * If we're in STA mode (and maybe IBSS?) then register for + * TSFOOR interrupts. + */ + if (ic->ic_opmode == IEEE80211_M_STA) + sc->sc_imask |= HAL_INT_TSFOOR; + /* Enable global TX timeout and carrier sense timeout if available */ if (ath_hal_gtxto_supported(ah)) sc->sc_imask |= HAL_INT_GTT; @@ -2149,6 +2622,7 @@ ath_init(void *arg) callout_reset(&sc->sc_wd_ch, hz, ath_watchdog, sc); ath_hal_intrset(ah, sc->sc_imask); + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); #ifdef ATH_TX99_DIAG @@ -2169,6 +2643,12 @@ ath_stop_locked(struct ifnet *ifp) __func__, sc->sc_invalid, ifp->if_flags); ATH_LOCK_ASSERT(sc); + + /* + * Wake the hardware up before fiddling with it. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ifp->if_drv_flags & IFF_DRV_RUNNING) { /* * Shutdown the hardware and driver: @@ -2201,17 +2681,29 @@ ath_stop_locked(struct ifnet *ifp) } ath_hal_intrset(ah, 0); } - ath_draintxq(sc, ATH_RESET_DEFAULT); + /* XXX we should stop RX regardless of whether it's valid */ if (!sc->sc_invalid) { ath_stoprecv(sc, 1); ath_hal_phydisable(ah); } else sc->sc_rxlink = NULL; + ath_draintxq(sc, ATH_RESET_DEFAULT); ath_beacon_free(sc); /* XXX not needed */ } + + /* And now, restore the current power state */ + ath_power_restore_power_state(sc); } -#define MAX_TXRX_ITERATIONS 1000 +/* + * Wait until all pending TX/RX has completed. + * + * This waits until all existing transmit, receive and interrupts + * have completed. It's assumed that the caller has first + * grabbed the reset lock so it doesn't try to do overlapping + * chip resets. + */ +#define MAX_TXRX_ITERATIONS 100 static void ath_txrx_stop_locked(struct ath_softc *sc) { @@ -2230,7 +2722,8 @@ ath_txrx_stop_locked(struct ath_softc *sc) sc->sc_txstart_cnt || sc->sc_intr_cnt) { if (i <= 0) break; - msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", 1); + msleep(sc, &sc->sc_pcu_mtx, 0, "ath_txrx_stop", + msecs_to_ticks(10)); i--; } @@ -2277,7 +2770,7 @@ ath_txrx_start(struct ath_softc *sc) * Another, cleaner way should be found to serialise all of * these operations. */ -#define MAX_RESET_ITERATIONS 10 +#define MAX_RESET_ITERATIONS 25 static int ath_reset_grablock(struct ath_softc *sc, int dowait) { @@ -2295,7 +2788,11 @@ ath_reset_grablock(struct ath_softc *sc, int dowait) break; } ATH_PCU_UNLOCK(sc); - pause("ath_reset_grablock", 1); + /* + * 1 tick is likely not enough time for long calibrations + * to complete. So we should wait quite a while. + */ + pause("ath_reset_grablock", msecs_to_ticks(100)); i--; ATH_PCU_LOCK(sc); } while (i > 0); @@ -2360,6 +2857,13 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) /* Try to (stop any further TX/RX from occuring */ taskqueue_block(sc->sc_tq); + /* + * Wake the hardware up. + */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); /* @@ -2385,13 +2889,6 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) ATH_PCU_UNLOCK(sc); /* - * Should now wait for pending TX/RX to complete - * and block future ones from occuring. This needs to be - * done before the TX queue is drained. - */ - ath_draintxq(sc, reset_type); /* stop xmit side */ - - /* * Regardless of whether we're doing a no-loss flush or * not, stop the PCU and handle what's in the RX queue. * That way frames aren't dropped which shouldn't be. @@ -2399,6 +2896,13 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) ath_stoprecv(sc, (reset_type != ATH_RESET_NOLOSS)); ath_rx_flush(sc); + /* + * Should now wait for pending TX/RX to complete + * and block future ones from occuring. This needs to be + * done before the TX queue is drained. + */ + ath_draintxq(sc, reset_type); /* stop xmit side */ + ath_settkipmic(sc); /* configure TKIP MIC handling */ /* NB: indicate channel change so we do a full reset */ ath_update_chainmasks(sc, ic->ic_curchan); @@ -2409,6 +2913,11 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) __func__, status); sc->sc_diversity = ath_hal_getdiversity(ah); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); @@ -2454,9 +2963,13 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) * reset counter - this way ath_intr() doesn't end up * disabling interrupts without a corresponding enable * in the rest or channel change path. + * + * Grab the TX reference in case we need to transmit. + * That way a parallel transmit doesn't. */ ATH_PCU_LOCK(sc); sc->sc_inreset_cnt--; + sc->sc_txstart_cnt++; /* XXX only do this if sc_inreset_cnt == 0? */ ath_hal_intrset(ah, sc->sc_imask); ATH_PCU_UNLOCK(sc); @@ -2473,6 +2986,8 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) /* Restart TX/RX as needed */ ath_txrx_start(sc); + /* XXX TODO: we need to hold the tx refcount here! */ + /* Restart TX completion and pending TX */ if (reset_type == ATH_RESET_NOLOSS) { for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { @@ -2497,6 +3012,14 @@ ath_reset(struct ifnet *ifp, ATH_RESET_TYPE reset_type) ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; IF_UNLOCK(&ifp->if_snd); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + ATH_PCU_LOCK(sc); + sc->sc_txstart_cnt--; + ATH_PCU_UNLOCK(sc); + /* Handle any frames in the TX queue */ /* * XXX should this be done by the caller, rather than @@ -2637,6 +3160,7 @@ ath_buf_clone(struct ath_softc *sc, struct ath_buf *bf) tbf->bf_status = bf->bf_status; tbf->bf_m = bf->bf_m; tbf->bf_node = bf->bf_node; + KASSERT((bf->bf_node != NULL), ("%s: bf_node=NULL!", __func__)); /* will be setup by the chain/setup function */ tbf->bf_lastds = NULL; /* for now, last == self */ @@ -2738,6 +3262,11 @@ ath_transmit(struct ifnet *ifp, struct mbuf *m) sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: start"); /* * Grab the TX lock - it's ok to do this here; we haven't @@ -2846,7 +3375,7 @@ ath_transmit(struct ifnet *ifp, struct mbuf *m) DPRINTF(sc, ATH_DEBUG_XMIT, "%s: out of txfrag buffers\n", __func__); sc->sc_stats.ast_tx_nofrag++; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ath_freetx(m); goto bad; } @@ -2894,7 +3423,7 @@ ath_transmit(struct ifnet *ifp, struct mbuf *m) * * XXX should use atomics? */ - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); nextfrag: /* * Pass the frame to the h/w for transmission. @@ -2914,7 +3443,7 @@ nextfrag: next = m->m_nextpkt; if (ath_tx_start(sc, ni, bf, m)) { bad: - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); reclaim: bf->bf_m = NULL; bf->bf_node = NULL; @@ -2971,6 +3500,11 @@ finish: sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + /* Sleep the hardware if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TX, 0, "ath_transmit: finished"); return (retval); @@ -2998,7 +3532,6 @@ ath_key_update_begin(struct ieee80211vap *vap) DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); taskqueue_block(sc->sc_tq); - IF_LOCK(&ifp->if_snd); /* NB: doesn't block mgmt frames */ } static void @@ -3008,7 +3541,6 @@ ath_key_update_end(struct ieee80211vap *vap) struct ath_softc *sc = ifp->if_softc; DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s:\n", __func__); - IF_UNLOCK(&ifp->if_snd); taskqueue_unblock(sc->sc_tq); } @@ -3019,16 +3551,25 @@ ath_update_promisc(struct ifnet *ifp) u_int32_t rfilt; /* configure rx filter */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); rfilt = ath_calcrxfilter(sc); ath_hal_setrxfilter(sc->sc_ah, rfilt); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); DPRINTF(sc, ATH_DEBUG_MODE, "%s: RX filter 0x%x\n", __func__, rfilt); } +/* + * Driver-internal mcast update call. + * + * Assumes the hardware is already awake. + */ static void -ath_update_mcast(struct ifnet *ifp) +ath_update_mcast_hw(struct ath_softc *sc) { - struct ath_softc *sc = ifp->if_softc; + struct ifnet *ifp = sc->sc_ifp; u_int32_t mfilt[2]; /* calculate and install multicast filter */ @@ -3056,11 +3597,33 @@ ath_update_mcast(struct ifnet *ifp) if_maddr_runlock(ifp); } else mfilt[0] = mfilt[1] = ~0; + ath_hal_setmcastfilter(sc->sc_ah, mfilt[0], mfilt[1]); + DPRINTF(sc, ATH_DEBUG_MODE, "%s: MC filter %08x:%08x\n", __func__, mfilt[0], mfilt[1]); } +/* + * Called from the net80211 layer - force the hardware + * awake before operating. + */ +static void +ath_update_mcast(struct ifnet *ifp) +{ + struct ath_softc *sc = ifp->if_softc; + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + + ath_update_mcast_hw(sc); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); +} + void ath_mode_init(struct ath_softc *sc) { @@ -3086,7 +3649,7 @@ ath_mode_init(struct ath_softc *sc) ath_hal_setmac(ah, IF_LLADDR(ifp)); /* calculate and install multicast filter */ - ath_update_mcast(ifp); + ath_update_mcast_hw(sc); } /* @@ -3118,8 +3681,13 @@ ath_setslottime(struct ath_softc *sc) __func__, ic->ic_curchan->ic_freq, ic->ic_curchan->ic_flags, ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", usec); + /* Wake up the hardware first before updating the slot time */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_setslottime(ah, usec); + ath_power_restore_power_state(sc); sc->sc_updateslot = OK; + ATH_UNLOCK(sc); } /* @@ -3136,6 +3704,8 @@ ath_updateslot(struct ifnet *ifp) * When not coordinating the BSS, change the hardware * immediately. For other operation we defer the change * until beacon updates have propagated to the stations. + * + * XXX sc_updateslot isn't changed behind a lock? */ if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) @@ -4041,14 +4611,12 @@ ath_tx_process_buf_completion(struct ath_softc *sc, struct ath_txq *txq, struct ath_tx_status *ts, struct ath_buf *bf) { struct ieee80211_node *ni = bf->bf_node; - struct ath_node *an = NULL; ATH_TX_UNLOCK_ASSERT(sc); ATH_TXQ_UNLOCK_ASSERT(txq); /* If unicast frame, update general statistics */ if (ni != NULL) { - an = ATH_NODE(ni); /* update statistics */ ath_tx_update_stats(sc, ts, bf); } @@ -4257,6 +4825,10 @@ ath_tx_proc_q0(void *arg, int npending) sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0: txqs=0x%08x", txqs); @@ -4277,6 +4849,10 @@ ath_tx_proc_q0(void *arg, int npending) sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } @@ -4298,6 +4874,10 @@ ath_tx_proc_q0123(void *arg, int npending) sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc_q0123: txqs=0x%08x", txqs); @@ -4330,6 +4910,10 @@ ath_tx_proc_q0123(void *arg, int npending) sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } @@ -4350,6 +4934,10 @@ ath_tx_proc(void *arg, int npending) sc->sc_txq_active &= ~txqs; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_KTR(sc, ATH_KTR_TXCOMP, 1, "ath_tx_proc: txqs=0x%08x", txqs); /* @@ -4375,6 +4963,10 @@ ath_tx_proc(void *arg, int npending) sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ath_tx_kick(sc); } #undef TXQACTIVE @@ -4401,6 +4993,10 @@ ath_txq_sched_tasklet(void *arg, int npending) sc->sc_txproc_cnt++; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_TX_LOCK(sc); for (i = 0; i < HAL_NUM_TX_QUEUES; i++) { if (ATH_TXQ_SETUP(sc, i)) { @@ -4409,6 +5005,10 @@ ath_txq_sched_tasklet(void *arg, int npending) } ATH_TX_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); sc->sc_txproc_cnt--; ATH_PCU_UNLOCK(sc); @@ -4916,14 +5516,15 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) ATH_PCU_LOCK(sc); + /* Disable interrupts */ + ath_hal_intrset(ah, 0); + /* Stop new RX/TX/interrupt completion */ if (ath_reset_grablock(sc, 1) == 0) { device_printf(sc->sc_dev, "%s: concurrent reset! Danger!\n", __func__); } - ath_hal_intrset(ah, 0); - /* Stop pending RX/TX completion */ ath_txrx_stop_locked(sc); @@ -4967,6 +5568,11 @@ ath_chan_set(struct ath_softc *sc, struct ieee80211_channel *chan) } sc->sc_diversity = ath_hal_getdiversity(ah); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + sc->sc_rx_resetted = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, chan); @@ -5056,6 +5662,17 @@ ath_calibrate(void *arg) HAL_BOOL aniCal, shortCal = AH_FALSE; int nextcal; + ATH_LOCK_ASSERT(sc); + + /* + * Force the hardware awake for ANI work. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + + /* Skip trying to do this if we're in reset */ + if (sc->sc_inreset_cnt) + goto restart; + if (ic->ic_flags & IEEE80211_F_SCAN) /* defer, off channel */ goto restart; longCal = (ticks - sc->sc_lastlongcal >= ath_longcalinterval*hz); @@ -5085,6 +5702,7 @@ ath_calibrate(void *arg) sc->sc_doresetcal = AH_TRUE; taskqueue_enqueue(sc->sc_tq, &sc->sc_resettask); callout_reset(&sc->sc_cal_ch, 1, ath_calibrate, sc); + ath_power_restore_power_state(sc); return; } /* @@ -5156,6 +5774,10 @@ restart: __func__); /* NB: don't rearm timer */ } + /* + * Restore power state now that we're done. + */ + ath_power_restore_power_state(sc); } static void @@ -5241,6 +5863,10 @@ ath_set_channel(struct ieee80211com *ic) struct ifnet *ifp = ic->ic_ifp; struct ath_softc *sc = ifp->if_softc; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + (void) ath_chan_set(sc, ic->ic_curchan); /* * If we are returning to our bss channel then mark state @@ -5251,6 +5877,7 @@ ath_set_channel(struct ieee80211com *ic) ATH_LOCK(sc); if (!sc->sc_scanning && ic->ic_curchan == ic->ic_bsschan) sc->sc_syncbeacon = 1; + ath_power_restore_power_state(sc); ATH_UNLOCK(sc); } @@ -5283,6 +5910,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) int i, error, stamode; u_int32_t rfilt; int csa_run_transition = 0; + enum ieee80211_state ostate = vap->iv_state; static const HAL_LED_STATE leds[] = { HAL_LED_INIT, /* IEEE80211_S_INIT */ @@ -5296,7 +5924,7 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) }; DPRINTF(sc, ATH_DEBUG_STATE, "%s: %s -> %s\n", __func__, - ieee80211_state_name[vap->iv_state], + ieee80211_state_name[ostate], ieee80211_state_name[nstate]); /* @@ -5308,10 +5936,34 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) */ IEEE80211_LOCK_ASSERT(ic); - if (vap->iv_state == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) + /* Before we touch the hardware - wake it up */ + ATH_LOCK(sc); + /* + * If the NIC is in anything other than SLEEP state, + * we need to ensure that self-generated frames are + * set for PWRMGT=0. Otherwise we may end up with + * strange situations. + * + * XXX TODO: is this actually the case? :-) + */ + if (nstate != IEEE80211_S_SLEEP) + ath_power_setselfgen(sc, HAL_PM_AWAKE); + + /* + * Now, wake the thing up. + */ + ath_power_set_power_state(sc, HAL_PM_AWAKE); + + /* + * And stop the calibration callout whilst we have + * ATH_LOCK held. + */ + callout_stop(&sc->sc_cal_ch); + ATH_UNLOCK(sc); + + if (ostate == IEEE80211_S_CSA && nstate == IEEE80211_S_RUN) csa_run_transition = 1; - callout_drain(&sc->sc_cal_ch); ath_hal_setledstate(ah, leds[nstate]); /* set LED */ if (nstate == IEEE80211_S_SCAN) { @@ -5321,6 +5973,13 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * [re]setup beacons. Unblock the task q thread so * deferred interrupt processing is done. */ + + /* Ensure we stay awake during scan */ + ATH_LOCK(sc); + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_hal_intrset(ah, sc->sc_imask &~ (HAL_INT_SWBA | HAL_INT_BMISS)); sc->sc_imask &= ~(HAL_INT_SWBA | HAL_INT_BMISS); @@ -5333,6 +5992,11 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) stamode = (vap->iv_opmode == IEEE80211_M_STA || vap->iv_opmode == IEEE80211_M_AHDEMO || vap->iv_opmode == IEEE80211_M_IBSS); + + /* + * XXX Dont need to do this (and others) if we've transitioned + * from SLEEP->RUN. + */ if (stamode && nstate == IEEE80211_S_RUN) { sc->sc_curaid = ni->ni_associd; IEEE80211_ADDR_COPY(sc->sc_curbssid, ni->ni_bssid); @@ -5435,11 +6099,14 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * beacon to update the beacon timer and thus we * won't get notified of the missing beacons. */ - sc->sc_syncbeacon = 1; -#if 0 - if (csa_run_transition) -#endif - ath_beacon_config(sc, vap); + if (ostate != IEEE80211_S_RUN && + ostate != IEEE80211_S_SLEEP) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: STA; syncbeacon=1\n", __func__); + sc->sc_syncbeacon = 1; + + if (csa_run_transition) + ath_beacon_config(sc, vap); /* * PR: kern/175227 @@ -5453,7 +6120,8 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) * timer fires (too often), leading to a STA * disassociation. */ - sc->sc_beacons = 1; + sc->sc_beacons = 1; + } break; case IEEE80211_M_MONITOR: /* @@ -5479,6 +6147,14 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) sc->sc_halstats.ns_avgbrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgrssi = ATH_RSSI_DUMMY_MARKER; sc->sc_halstats.ns_avgtxrssi = ATH_RSSI_DUMMY_MARKER; + + /* + * Force awake for RUN mode. + */ + ATH_LOCK(sc); + ath_power_setselfgen(sc, HAL_PM_AWAKE); + ath_power_setpower(sc, HAL_PM_AWAKE); + /* * Finally, start any timers and the task q thread * (in case we didn't go through SCAN state). @@ -5490,6 +6166,8 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) DPRINTF(sc, ATH_DEBUG_CALIBRATE, "%s: calibration disabled\n", __func__); } + ATH_UNLOCK(sc); + taskqueue_unblock(sc->sc_tq); } else if (nstate == IEEE80211_S_INIT) { /* @@ -5509,9 +6187,43 @@ ath_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) #ifdef IEEE80211_SUPPORT_TDMA ath_hal_setcca(ah, AH_TRUE); #endif + } else if (nstate == IEEE80211_S_SLEEP) { + /* We're going to sleep, so transition appropriately */ + /* For now, only do this if we're a single STA vap */ + if (sc->sc_nvaps == 1 && + vap->iv_opmode == IEEE80211_M_STA) { + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: syncbeacon=%d\n", __func__, sc->sc_syncbeacon); + ATH_LOCK(sc); + /* + * Always at least set the self-generated + * frame config to set PWRMGT=1. + */ + ath_power_setselfgen(sc, HAL_PM_NETWORK_SLEEP); + + /* + * If we're not syncing beacons, transition + * to NETWORK_SLEEP. + * + * We stay awake if syncbeacon > 0 in case + * we need to listen for some beacons otherwise + * our beacon timer config may be wrong. + */ + if (sc->sc_syncbeacon == 0) { + ath_power_setpower(sc, HAL_PM_NETWORK_SLEEP); + } + ATH_UNLOCK(sc); + } } bad: ieee80211_free_node(ni); + + /* + * Restore the power state - either to what it was, or + * to network_sleep if it's alright. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return error; } @@ -5566,7 +6278,16 @@ ath_newassoc(struct ieee80211_node *ni, int isnew) an->an_mcastrix = ath_tx_findrix(sc, tp->mcastrate); an->an_mgmtrix = ath_tx_findrix(sc, tp->mgmtrate); + DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: reassoc; isnew=%d, is_powersave=%d\n", + __func__, + ni->ni_macaddr, + ":", + isnew, + an->an_is_powersave); + + ATH_NODE_LOCK(an); ath_rate_newassoc(sc, an, isnew); + ATH_NODE_UNLOCK(an); if (isnew && (vap->iv_flags & IEEE80211_F_PRIVACY) == 0 && sc->sc_hasclrkey && @@ -5806,10 +6527,14 @@ ath_watchdog(void *arg) struct ath_softc *sc = arg; int do_reset = 0; + ATH_LOCK_ASSERT(sc); + if (sc->sc_wd_timer != 0 && --sc->sc_wd_timer == 0) { struct ifnet *ifp = sc->sc_ifp; uint32_t hangs; + ath_power_set_power_state(sc, HAL_PM_AWAKE); + if (ath_hal_gethangstate(sc->sc_ah, 0xffff, &hangs) && hangs != 0) { if_printf(ifp, "%s hang detected (0x%x)\n", @@ -5817,8 +6542,10 @@ ath_watchdog(void *arg) } else if_printf(ifp, "device timeout\n"); do_reset = 1; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); sc->sc_stats.ast_watchdog++; + + ath_power_restore_power_state(sc); } /* @@ -5916,6 +6643,13 @@ ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) goto bad; } } + + + ATH_LOCK(sc); + if (id != HAL_DIAG_REGS) + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + if (ath_hal_getdiagstate(ah, id, indata, insize, &outdata, &outsize)) { if (outsize < ad->ad_out_size) ad->ad_out_size = outsize; @@ -5925,6 +6659,12 @@ ath_ioctl_diag(struct ath_softc *sc, struct ath_diag *ad) } else { error = EINVAL; } + + ATH_LOCK(sc); + if (id != HAL_DIAG_REGS) + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + bad: if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL) free(indata, M_TEMP); @@ -5947,14 +6687,17 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) switch (cmd) { case SIOCSIFFLAGS: - ATH_LOCK(sc); if (IS_RUNNING(ifp)) { /* * To avoid rescanning another access point, * do not call ath_init() here. Instead, * only reflect promisc mode settings. */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_mode_init(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } else if (ifp->if_flags & IFF_UP) { /* * Beware of being called during attach/detach @@ -5968,14 +6711,12 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) if (!sc->sc_invalid) ath_init(sc); /* XXX lose error */ } else { + ATH_LOCK(sc); ath_stop_locked(ifp); -#ifdef notyet - /* XXX must wakeup in places like ath_vap_delete */ if (!sc->sc_invalid) - ath_hal_setpower(sc->sc_ah, HAL_PM_FULL_SLEEP); -#endif + ath_power_setpower(sc, HAL_PM_FULL_SLEEP); + ATH_UNLOCK(sc); } - ATH_UNLOCK(sc); break; case SIOCGIFMEDIA: case SIOCSIFMEDIA: @@ -5983,8 +6724,10 @@ ath_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) break; case SIOCGATHSTATS: /* NB: embed these numbers to get a consistent view */ - sc->sc_stats.ast_tx_packets = ifp->if_opackets; - sc->sc_stats.ast_rx_packets = ifp->if_ipackets; + sc->sc_stats.ast_tx_packets = if_get_counter_default(ifp, + IFCOUNTER_OPACKETS); + sc->sc_stats.ast_rx_packets = if_get_counter_default(ifp, + IFCOUNTER_IPACKETS); sc->sc_stats.ast_tx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgtxrssi); sc->sc_stats.ast_rx_rssi = ATH_RSSI(sc->sc_halstats.ns_avgrssi); #ifdef IEEE80211_SUPPORT_TDMA @@ -6341,7 +7084,7 @@ ath_tx_update_tim(struct ath_softc *sc, struct ieee80211_node *ni, /* * Don't bother grabbing the lock unless the queue is empty. */ - if (&an->an_swq_depth != 0) + if (an->an_swq_depth != 0) return; if (an->an_is_powersave && @@ -6511,6 +7254,6 @@ ath_node_recv_pspoll(struct ieee80211_node *ni, struct mbuf *m) MODULE_VERSION(if_ath, 1); MODULE_DEPEND(if_ath, wlan, 1, 1, 1); /* 802.11 media layer */ -#if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) +#if defined(IEEE80211_ALQ) || defined(AH_DEBUG_ALQ) || defined(ATH_DEBUG_ALQ) MODULE_DEPEND(if_ath, alq, 1, 1, 1); #endif diff --git a/sys/dev/ath/if_ath_ahb.c b/sys/dev/ath/if_ath_ahb.c index 59593b6..ffe825d 100644 --- a/sys/dev/ath/if_ath_ahb.c +++ b/sys/dev/ath/if_ath_ahb.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/lock.h> @@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/if_media.h> #include <net/if_arp.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> @@ -151,19 +153,30 @@ ath_ahb_attach(device_t dev) eepromsize = ATH_EEPROM_DATA_SIZE * 2; } - rid = 0; device_printf(sc->sc_dev, "eeprom @ %p (%d bytes)\n", (void *) eepromaddr, eepromsize); - psc->sc_eeprom = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, (uintptr_t) eepromaddr, - (uintptr_t) eepromaddr + (uintptr_t) (eepromsize - 1), 0, RF_ACTIVE); + /* + * XXX this assumes that the parent device is the nexus + * and will just pass through requests for all of memory. + * + * Later on, when this has to attach off of the actual + * AHB, this won't work. + * + * Ideally this would be done in machdep code in mips/atheros/ + * and it'd expose the EEPROM via the firmware interface, + * so the ath/ath_ahb drivers can be loaded as modules + * after boot-time. + */ + psc->sc_eeprom = bus_alloc_resource(dev, SYS_RES_MEMORY, + &rid, (uintptr_t) eepromaddr, + (uintptr_t) eepromaddr + (uintptr_t) (eepromsize - 1), 0, RF_ACTIVE); if (psc->sc_eeprom == NULL) { device_printf(dev, "cannot map eeprom space\n"); goto bad0; } - /* XXX uintptr_t is a bandaid for ia64; to be fixed */ - sc->sc_st = (HAL_BUS_TAG)(uintptr_t) rman_get_bustag(psc->sc_sr); + sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) @@ -348,6 +361,7 @@ static driver_t ath_ahb_driver = { }; static devclass_t ath_devclass; DRIVER_MODULE(ath, nexus, ath_ahb_driver, ath_devclass, 0, 0); +DRIVER_MODULE(ath, apb, ath_ahb_driver, ath_devclass, 0, 0); MODULE_VERSION(ath, 1); MODULE_DEPEND(ath, wlan, 1, 1, 1); /* 802.11 media layer */ MODULE_DEPEND(ath, if_ath, 1, 1, 1); /* if_ath driver */ diff --git a/sys/dev/ath/if_ath_beacon.c b/sys/dev/ath/if_ath_beacon.c index 11b0426..a672c71 100644 --- a/sys/dev/ath/if_ath_beacon.c +++ b/sys/dev/ath/if_ath_beacon.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -381,7 +382,7 @@ ath_beacon_update(struct ieee80211vap *vap, int item) /* * Handle a beacon miss. */ -static void +void ath_beacon_miss(struct ath_softc *sc) { HAL_SURVEY_SAMPLE hs; @@ -748,6 +749,11 @@ ath_beacon_generate(struct ath_softc *sc, struct ieee80211vap *vap) * * More thought is required here. */ + /* + * XXX can we even stop TX DMA here? Check what the + * reference driver does for cabq for beacons, given + * that stopping TX requires RX is paused. + */ ath_tx_draintxq(sc, cabq); } } @@ -915,7 +921,7 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) struct ieee80211_node *ni; u_int32_t nexttbtt, intval, tsftu; u_int32_t nexttbtt_u8, intval_u8; - u_int64_t tsf; + u_int64_t tsf, tsf_beacon; if (vap == NULL) vap = TAILQ_FIRST(&ic->ic_vaps); /* XXX */ @@ -931,9 +937,17 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) ni = ieee80211_ref_node(vap->iv_bss); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + /* extract tstamp from last beacon and convert to TU */ nexttbtt = TSF_TO_TU(LE_READ_4(ni->ni_tstamp.data + 4), LE_READ_4(ni->ni_tstamp.data)); + + tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= LE_READ_4(ni->ni_tstamp.data); + if (ic->ic_opmode == IEEE80211_M_HOSTAP || ic->ic_opmode == IEEE80211_M_MBSS) { /* @@ -979,14 +993,63 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) */ tsf = ath_hal_gettsf64(ah); tsftu = TSF_TO_TU(tsf>>32, tsf) + FUDGE; - do { - nexttbtt += intval; - if (--dtimcount < 0) { - dtimcount = dtimperiod - 1; - if (--cfpcount < 0) - cfpcount = cfpperiod - 1; + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: beacon tsf=%llu, hw tsf=%llu, nexttbtt=%u, tsftu=%u\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf, + nexttbtt, + tsftu); + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: beacon tsf=%llu, hw tsf=%llu, tsf delta=%lld\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf, + (long long) tsf - + (long long) tsf_beacon); + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: nexttbtt=%llu, beacon tsf delta=%lld\n", + __func__, + (unsigned long long) nexttbtt, + (long long) ((long long) nexttbtt * 1024LL) - (long long) tsf_beacon); + + /* XXX cfpcount? */ + + if (nexttbtt > tsftu) { + uint32_t countdiff, oldtbtt, remainder; + + oldtbtt = nexttbtt; + remainder = (nexttbtt - tsftu) % intval; + nexttbtt = tsftu + remainder; + + countdiff = (oldtbtt - nexttbtt) / intval % dtimperiod; + if (dtimcount > countdiff) { + dtimcount -= countdiff; + } else { + dtimcount += dtimperiod - countdiff; + } + } else { //nexttbtt <= tsftu + uint32_t countdiff, oldtbtt, remainder; + + oldtbtt = nexttbtt; + remainder = (tsftu - nexttbtt) % intval; + nexttbtt = tsftu - remainder + intval; + countdiff = (nexttbtt - oldtbtt) / intval % dtimperiod; + if (dtimcount > countdiff) { + dtimcount -= countdiff; + } else { + dtimcount += dtimperiod - countdiff; } - } while (nexttbtt < tsftu); + } + + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: adj nexttbtt=%llu, rx tsf delta=%lld\n", + __func__, + (unsigned long long) nexttbtt, + (long long) ((long long)nexttbtt * 1024LL) - (long long)tsf); + memset(&bs, 0, sizeof(bs)); bs.bs_intval = intval; bs.bs_nexttbtt = nexttbtt; @@ -1033,9 +1096,12 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) bs.bs_sleepduration = roundup(bs.bs_sleepduration, bs.bs_dtimperiod); DPRINTF(sc, ATH_DEBUG_BEACON, - "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u nextdtim %u bmiss %u sleep %u cfp:period %u maxdur %u next %u timoffset %u\n" + "%s: tsf %ju tsf:tu %u intval %u nexttbtt %u dtim %u " + "nextdtim %u bmiss %u sleep %u cfp:period %u " + "maxdur %u next %u timoffset %u\n" , __func__ - , tsf, tsftu + , tsf + , tsftu , bs.bs_intval , bs.bs_nexttbtt , bs.bs_dtimperiod @@ -1112,8 +1178,11 @@ ath_beacon_config(struct ath_softc *sc, struct ieee80211vap *vap) if (ic->ic_opmode == IEEE80211_M_IBSS && sc->sc_hasveol) ath_beacon_start_adhoc(sc, vap); } - sc->sc_syncbeacon = 0; ieee80211_free_node(ni); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); #undef FUDGE #undef TSF_TO_TU } diff --git a/sys/dev/ath/if_ath_beacon.h b/sys/dev/ath/if_ath_beacon.h index f3f73d7..a940268 100644 --- a/sys/dev/ath/if_ath_beacon.h +++ b/sys/dev/ath/if_ath_beacon.h @@ -48,5 +48,7 @@ extern int ath_beacon_alloc(struct ath_softc *sc, struct ieee80211_node *ni); extern void ath_beacon_return(struct ath_softc *sc, struct ath_buf *bf); extern void ath_beacon_free(struct ath_softc *sc); extern void ath_beacon_proc(void *arg, int pending); +extern void ath_beacon_miss(struct ath_softc *sc); #endif + diff --git a/sys/dev/ath/if_ath_btcoex.c b/sys/dev/ath/if_ath_btcoex.c index fff6f3b..b78f866 100644 --- a/sys/dev/ath/if_ath_btcoex.c +++ b/sys/dev/ath/if_ath_btcoex.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/kernel.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/errno.h> @@ -54,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_arp.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ @@ -186,6 +188,72 @@ ath_btcoex_cfg_wb225(struct ath_softc *sc) return (0); } +/* + * Initial AR9462 / (WB222) bluetooth coexistence settings, + * just for experimentation. + * + * Return 0 for OK; errno for error. + */ +static int +ath_btcoex_cfg_wb222(struct ath_softc *sc) +{ + HAL_BT_COEX_INFO btinfo; + HAL_BT_COEX_CONFIG btconfig; + struct ath_hal *ah = sc->sc_ah; + + if (! ath_hal_btcoex_supported(ah)) + return (EINVAL); + + bzero(&btinfo, sizeof(btinfo)); + bzero(&btconfig, sizeof(btconfig)); + + device_printf(sc->sc_dev, "Enabling WB222 BTCOEX\n"); + + btinfo.bt_module = HAL_BT_MODULE_JANUS; /* XXX not used? */ + btinfo.bt_coex_config = HAL_BT_COEX_CFG_MCI; + + /* + * MCI uses a completely different interface to speak + * to the bluetooth module - it's a command based + * thing over a serial line, rather than + * state pins to/from the bluetooth module. + * + * So, the GPIO configuration, polarity, etc + * doesn't matter on MCI devices; it's just + * completely ignored by the HAL. + */ + btinfo.bt_gpio_bt_active = 4; + btinfo.bt_gpio_bt_priority = 8; + btinfo.bt_gpio_wlan_active = 5; + + btinfo.bt_active_polarity = 1; /* XXX not used */ + btinfo.bt_single_ant = 0; /* 2 antenna on WB222 */ + btinfo.bt_isolation = 0; /* in dB, not used */ + + ath_hal_btcoex_set_info(ah, &btinfo); + + btconfig.bt_time_extend = 0; + btconfig.bt_txstate_extend = 1; /* true */ + btconfig.bt_txframe_extend = 1; /* true */ + btconfig.bt_mode = HAL_BT_COEX_MODE_SLOTTED; + btconfig.bt_quiet_collision = 1; /* true */ + btconfig.bt_rxclear_polarity = 1; /* true */ + btconfig.bt_priority_time = 2; + btconfig.bt_first_slot_time = 5; + btconfig.bt_hold_rxclear = 1; /* true */ + + ath_hal_btcoex_set_config(ah, &btconfig); + + /* + * Enable antenna diversity. + */ + ath_hal_btcoex_set_parameter(ah, HAL_BT_COEX_ANTENNA_DIVERSITY, 1); + + return (0); +} + + + #if 0 /* @@ -241,6 +309,8 @@ ath_btcoex_attach(struct ath_softc *sc) if (strncmp(profname, "wb195", 5) == 0) { ret = ath_btcoex_cfg_wb195(sc); + } else if (strncmp(profname, "wb222", 5) == 0) { + ret = ath_btcoex_cfg_wb222(sc); } else if (strncmp(profname, "wb225", 5) == 0) { ret = ath_btcoex_cfg_wb225(sc); } else { diff --git a/sys/dev/ath/if_ath_debug.c b/sys/dev/ath/if_ath_debug.c index e3c73f5..d21ad6f 100644 --- a/sys/dev/ath/if_ath_debug.c +++ b/sys/dev/ath/if_ath_debug.c @@ -92,9 +92,8 @@ __FBSDID("$FreeBSD$"); uint64_t ath_debug = 0; SYSCTL_DECL(_hw_ath); -SYSCTL_QUAD(_hw_ath, OID_AUTO, debug, CTLFLAG_RW, &ath_debug, +SYSCTL_QUAD(_hw_ath, OID_AUTO, debug, CTLFLAG_RWTUN, &ath_debug, 0, "control debugging printfs"); -TUNABLE_QUAD("hw.ath.debug", &ath_debug); void ath_printrxbuf(struct ath_softc *sc, const struct ath_buf *bf, diff --git a/sys/dev/ath/if_ath_debug.h b/sys/dev/ath/if_ath_debug.h index 83597af..40c0b9a 100644 --- a/sys/dev/ath/if_ath_debug.h +++ b/sys/dev/ath/if_ath_debug.h @@ -68,6 +68,7 @@ enum { ATH_DEBUG_SW_TX_FILT = 0x400000000ULL, /* SW TX FF */ ATH_DEBUG_NODE_PWRSAVE = 0x800000000ULL, /* node powersave */ ATH_DEBUG_DIVERSITY = 0x1000000000ULL, /* Diversity logic */ + ATH_DEBUG_PWRSAVE = 0x2000000000ULL, ATH_DEBUG_ANY = 0xffffffffffffffffULL }; diff --git a/sys/dev/ath/if_ath_keycache.c b/sys/dev/ath/if_ath_keycache.c index 6c2749f..fe99f10 100644 --- a/sys/dev/ath/if_ath_keycache.c +++ b/sys/dev/ath/if_ath_keycache.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -77,6 +78,7 @@ __FBSDID("$FreeBSD$"); #include <dev/ath/if_ath_debug.h> #include <dev/ath/if_ath_keycache.h> +#include <dev/ath/if_ath_misc.h> #ifdef ATH_DEBUG static void @@ -197,6 +199,7 @@ ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, u_int8_t gmac[IEEE80211_ADDR_LEN]; const u_int8_t *mac; HAL_KEYVAL hk; + int ret; memset(&hk, 0, sizeof(hk)); /* @@ -250,13 +253,19 @@ ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, } else mac = k->wk_macaddr; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); if (hk.kv_type == HAL_CIPHER_TKIP && (k->wk_flags & IEEE80211_KEY_SWMIC) == 0) { - return ath_keyset_tkip(sc, k, &hk, mac); + ret = ath_keyset_tkip(sc, k, &hk, mac); } else { KEYPRINTF(sc, k->wk_keyix, &hk, mac); - return ath_hal_keyset(ah, k->wk_keyix, &hk, mac); + ret = ath_hal_keyset(ah, k->wk_keyix, &hk, mac); } + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return (ret); #undef N } @@ -491,6 +500,8 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) DPRINTF(sc, ATH_DEBUG_KEYCACHE, "%s: delete key %u\n", __func__, keyix); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); ath_hal_keyreset(ah, keyix); /* * Handle split tx/rx keying required for TKIP with h/w MIC. @@ -514,6 +525,8 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) } } } + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); return 1; } diff --git a/sys/dev/ath/if_ath_led.c b/sys/dev/ath/if_ath_led.c index 33cc512..a55e036 100644 --- a/sys/dev/ath/if_ath_led.c +++ b/sys/dev/ath/if_ath_led.c @@ -122,6 +122,11 @@ __FBSDID("$FreeBSD$"); void ath_led_config(struct ath_softc *sc) { + + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + /* Software LED blinking - GPIO controlled LED */ if (sc->sc_softled) { ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_ledpin, @@ -144,6 +149,10 @@ ath_led_config(struct ath_softc *sc) ath_hal_gpioCfgOutput(sc->sc_ah, sc->sc_led_net_pin, HAL_GPIO_OUTPUT_MUX_MAC_NETWORK_LED); } + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); } static void diff --git a/sys/dev/ath/if_ath_lna_div.c b/sys/dev/ath/if_ath_lna_div.c index 4ae81a3..f0a33a5 100644 --- a/sys/dev/ath/if_ath_lna_div.c +++ b/sys/dev/ath/if_ath_lna_div.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/kernel.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/errno.h> @@ -54,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_arp.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ @@ -207,6 +209,10 @@ bad: return (error); } +/* + * XXX need to low_rssi_thresh config from ath9k, to support CUS198 + * antenna diversity correctly. + */ static HAL_BOOL ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta, int main_rssi_avg, int alt_rssi_avg, int pkt_count) diff --git a/sys/dev/ath/if_ath_misc.h b/sys/dev/ath/if_ath_misc.h index 0c99bc7..711e69e8 100644 --- a/sys/dev/ath/if_ath_misc.h +++ b/sys/dev/ath/if_ath_misc.h @@ -128,6 +128,19 @@ extern void ath_start_task(void *arg, int npending); extern void ath_tx_dump(struct ath_softc *sc, struct ath_txq *txq); /* + * Power state tracking. + */ +extern void _ath_power_setpower(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_set_selfgen(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_set_power_state(struct ath_softc *sc, int power_state, const char *file, int line); +extern void _ath_power_restore_power_state(struct ath_softc *sc, const char *file, int line); + +#define ath_power_setpower(sc, ps) _ath_power_setpower(sc, ps, __FILE__, __LINE__) +#define ath_power_setselfgen(sc, ps) _ath_power_set_selfgen(sc, ps, __FILE__, __LINE__) +#define ath_power_set_power_state(sc, ps) _ath_power_set_power_state(sc, ps, __FILE__, __LINE__) +#define ath_power_restore_power_state(sc) _ath_power_restore_power_state(sc, __FILE__, __LINE__) + +/* * Kick the frame TX task. */ static inline void diff --git a/sys/dev/ath/if_ath_pci.c b/sys/dev/ath/if_ath_pci.c index 91cb425..5610882 100644 --- a/sys/dev/ath/if_ath_pci.c +++ b/sys/dev/ath/if_ath_pci.c @@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/systm.h> +#include <sys/malloc.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/lock.h> @@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include <net/if.h> #include <net/if_media.h> #include <net/if_arp.h> +#include <net/ethernet.h> #include <net80211/ieee80211_var.h> @@ -78,6 +80,98 @@ struct ath_pci_softc { void *sc_ih; /* interrupt handler */ }; +/* + * XXX eventually this should be some system level definition + * so modules will hvae probe/attach information like USB. + * But for now.. + */ +struct pci_device_id { + int vendor_id; + int device_id; + + int sub_vendor_id; + int sub_device_id; + + int driver_data; + + int match_populated:1; + int match_vendor_id:1; + int match_device_id:1; + int match_sub_vendor_id:1; + int match_sub_device_id:1; +}; + +#define PCI_VDEVICE(v, s) \ + .vendor_id = (v), \ + .device_id = (s), \ + .match_populated = 1, \ + .match_vendor_id = 1, \ + .match_device_id = 1 + +#define PCI_DEVICE_SUB(v, d, dv, ds) \ + .match_populated = 1, \ + .vendor_id = (v), .match_vendor_id = 1, \ + .device_id = (d), .match_device_id = 1, \ + .sub_vendor_id = (dv), .match_sub_vendor_id = 1, \ + .sub_device_id = (ds), .match_sub_device_id = 1 + +#define PCI_VENDOR_ID_ATHEROS 0x168c +#define PCI_VENDOR_ID_SAMSUNG 0x144d +#define PCI_VENDOR_ID_AZWAVE 0x1a3b +#define PCI_VENDOR_ID_FOXCONN 0x105b +#define PCI_VENDOR_ID_ATTANSIC 0x1969 +#define PCI_VENDOR_ID_ASUSTEK 0x1043 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_QMI 0x1a32 +#define PCI_VENDOR_ID_LENOVO 0x17aa +#define PCI_VENDOR_ID_HP 0x103c + +#include "if_ath_pci_devlist.h" + +/* + * Attempt to find a match for the given device in + * the given device table. + * + * Returns the device structure or NULL if no matching + * PCI device is found. + */ +static const struct pci_device_id * +ath_pci_probe_device(device_t dev, const struct pci_device_id *dev_table, int nentries) +{ + int i; + int vendor_id, device_id; + int sub_vendor_id, sub_device_id; + + vendor_id = pci_get_vendor(dev); + device_id = pci_get_device(dev); + sub_vendor_id = pci_get_subvendor(dev); + sub_device_id = pci_get_subdevice(dev); + + for (i = 0; i < nentries; i++) { + /* Don't match on non-populated (eg empty) entries */ + if (! dev_table[i].match_populated) + continue; + + if (dev_table[i].match_vendor_id && + (dev_table[i].vendor_id != vendor_id)) + continue; + if (dev_table[i].match_device_id && + (dev_table[i].device_id != device_id)) + continue; + if (dev_table[i].match_sub_vendor_id && + (dev_table[i].sub_vendor_id != sub_vendor_id)) + continue; + if (dev_table[i].match_sub_device_id && + (dev_table[i].sub_device_id != sub_device_id)) + continue; + + /* Match */ + return (&dev_table[i]); + } + + return (NULL); +} + #define BS_BAR 0x10 #define PCIR_RETRY_TIMEOUT 0x41 #define PCIR_CFG_PMCSR 0x48 @@ -148,9 +242,15 @@ ath_pci_attach(device_t dev) const struct firmware *fw = NULL; const char *buf; #endif + const struct pci_device_id *pd; sc->sc_dev = dev; + /* Do this lookup anyway; figure out what to do with it later */ + pd = ath_pci_probe_device(dev, ath_pci_id_table, nitems(ath_pci_id_table)); + if (pd) + sc->sc_pci_devinfo = pd->driver_data; + /* * Enable bus mastering. */ @@ -171,8 +271,7 @@ ath_pci_attach(device_t dev) device_printf(dev, "cannot map register space\n"); goto bad; } - /* XXX uintptr_t is a bandaid for ia64; to be fixed */ - sc->sc_st = (HAL_BUS_TAG)(uintptr_t) rman_get_bustag(psc->sc_sr); + sc->sc_st = (HAL_BUS_TAG) rman_get_bustag(psc->sc_sr); sc->sc_sh = (HAL_BUS_HANDLE) rman_get_bushandle(psc->sc_sr); /* * Mark device invalid so any interrupts (shared or otherwise) @@ -180,6 +279,13 @@ ath_pci_attach(device_t dev) */ sc->sc_invalid = 1; + ATH_LOCK_INIT(sc); + ATH_PCU_LOCK_INIT(sc); + ATH_RX_LOCK_INIT(sc); + ATH_TX_LOCK_INIT(sc); + ATH_TX_IC_LOCK_INIT(sc); + ATH_TXSTATUS_LOCK_INIT(sc); + /* * Arrange interrupt line. */ @@ -230,7 +336,7 @@ ath_pci_attach(device_t dev) if (fw == NULL) { device_printf(dev, "%s: couldn't find firmware\n", __func__); - goto bad3; + goto bad4; } device_printf(dev, "%s: EEPROM firmware @ %p\n", @@ -240,30 +346,20 @@ ath_pci_attach(device_t dev) if (! sc->sc_eepromdata) { device_printf(dev, "%s: can't malloc eepromdata\n", __func__); - goto bad3; + goto bad4; } memcpy(sc->sc_eepromdata, fw->data, fw->datasize); firmware_put(fw, 0); } #endif /* ATH_EEPROM_FIRMWARE */ - ATH_LOCK_INIT(sc); - ATH_PCU_LOCK_INIT(sc); - ATH_RX_LOCK_INIT(sc); - ATH_TX_LOCK_INIT(sc); - ATH_TX_IC_LOCK_INIT(sc); - ATH_TXSTATUS_LOCK_INIT(sc); - error = ath_attach(pci_get_device(dev), sc); if (error == 0) /* success */ return 0; - ATH_TXSTATUS_LOCK_DESTROY(sc); - ATH_PCU_LOCK_DESTROY(sc); - ATH_RX_LOCK_DESTROY(sc); - ATH_TX_IC_LOCK_DESTROY(sc); - ATH_TX_LOCK_DESTROY(sc); - ATH_LOCK_DESTROY(sc); +#ifdef ATH_EEPROM_FIRMWARE +bad4: +#endif bus_dma_tag_destroy(sc->sc_dmat); bad3: bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih); @@ -271,6 +367,14 @@ bad2: bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq); bad1: bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR, psc->sc_sr); + + ATH_TXSTATUS_LOCK_DESTROY(sc); + ATH_PCU_LOCK_DESTROY(sc); + ATH_RX_LOCK_DESTROY(sc); + ATH_TX_IC_LOCK_DESTROY(sc); + ATH_TX_LOCK_DESTROY(sc); + ATH_LOCK_DESTROY(sc); + bad: return (error); } diff --git a/sys/dev/ath/if_ath_pci_devlist.h b/sys/dev/ath/if_ath_pci_devlist.h new file mode 100644 index 0000000..ae65909 --- /dev/null +++ b/sys/dev/ath/if_ath_pci_devlist.h @@ -0,0 +1,669 @@ +/*- + * Copyright (c) 2014 Qualcomm Atheros. + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * $FreeBSD$ + */ + +static const struct pci_device_id ath_pci_id_table[] = { + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0023) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0024) }, /* PCI-E */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0027) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0029) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002A) }, /* PCI-E */ + + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_AZWAVE, + 0x1C71), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_FOXCONN, + 0xE01F), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x11AD, /* LITEON */ + 0x6632), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x11AD, /* LITEON */ + 0x6642), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + PCI_VENDOR_ID_QMI, + 0x0306), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x185F, /* WNC */ + 0x309D), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x147C), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x147D), + .driver_data = ATH_PCI_D3_L1_WAR }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002A, + 0x10CF, /* Fujitsu */ + 0x1536), + .driver_data = ATH_PCI_D3_L1_WAR }, + + /* AR9285 card for Asus */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x002B, + PCI_VENDOR_ID_AZWAVE, + 0x2C37), + .driver_data = ATH_PCI_BT_ANT_DIV }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002B) }, /* PCI-E */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002C) }, /* PCI-E 802.11n bonded out */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002D) }, /* PCI */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x002E) }, /* PCI-E */ + + /* Killer Wireless (3x3) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2000), + .driver_data = ATH_PCI_KILLER }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2001), + .driver_data = ATH_PCI_KILLER }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0030) }, /* PCI-E AR9300 */ + + /* PCI-E CUS198 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2086), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1237), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2126), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x126A), + .driver_data = ATH_PCI_CUS198 | ATH_PCI_BT_ANT_DIV }, + + /* PCI-E CUS230 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2152), + .driver_data = ATH_PCI_CUS230 | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE075), + .driver_data = ATH_PCI_CUS230 | ATH_PCI_BT_ANT_DIV }, + + /* WB225 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ATHEROS, + 0x3119), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ATHEROS, + 0x3122), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x185F, /* WNC */ + 0x3119), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x4105), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x4106), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410D), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410E), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0x410F), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC706), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC680), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_SAMSUNG, + 0xC708), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_LENOVO, + 0x3218), + .driver_data = ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_LENOVO, + 0x3219), + .driver_data = ATH_PCI_BT_ANT_DIV }, + + /* AR9485 cards with PLL power-save disabled by default. */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2C97), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x2100), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1C56, /* ASKEY */ + 0x4001), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x11AD, /* LITEON */ + 0x6627), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x11AD, /* LITEON */ + 0x6628), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE04E), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_FOXCONN, + 0xE04F), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x144F, /* ASKEY */ + 0x7197), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x2000), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x2001), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1186), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1F86), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1195), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_AZWAVE, + 0x1F95), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x1C00), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + 0x1B9A, /* XAVI */ + 0x1C01), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0032, + PCI_VENDOR_ID_ASUSTEK, + 0x850D), + .driver_data = ATH_PCI_NO_PLL_PWRSAVE }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0032) }, /* PCI-E AR9485 */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0033) }, /* PCI-E AR9580 */ + + /* PCI-E CUS217 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_AZWAVE, + 0x2116), + .driver_data = ATH_PCI_CUS217 }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6661), + .driver_data = ATH_PCI_CUS217 }, + + /* AR9462 with WoW support */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ATHEROS, + 0x3117), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_LENOVO, + 0x3214), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ATTANSIC, + 0x0091), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_AZWAVE, + 0x2110), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_ASUSTEK, + 0x850E), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6631), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x11AD, /* LITEON */ + 0x6641), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + PCI_VENDOR_ID_HP, + 0x1864), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x14CD, /* USI */ + 0x0063), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x14CD, /* USI */ + 0x0064), + .driver_data = ATH_PCI_WOW }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0034, + 0x10CF, /* Fujitsu */ + 0x1783), + .driver_data = ATH_PCI_WOW }, + + /* Killer Wireless (2x2) */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0030, + 0x1A56, + 0x2003), + .driver_data = ATH_PCI_KILLER }, + + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0034) }, /* PCI-E AR9462 */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */ + + /* CUS252 */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3028), + .driver_data = ATH_PCI_CUS252 | + ATH_PCI_AR9565_2ANT | + ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2176), + .driver_data = ATH_PCI_CUS252 | + ATH_PCI_AR9565_2ANT | + ATH_PCI_BT_ANT_DIV }, + + /* WB335 1-ANT */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE068), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA119), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0632), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x06B2), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0842), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x6671), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2811), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2812), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x28A1), + .driver_data = ATH_PCI_AR9565_1ANT }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x218A), + .driver_data = ATH_PCI_AR9565_1ANT }, + + /* WB335 1-ANT / Antenna Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3025), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3026), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302B), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE069), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3028), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0622), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0672), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0662), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x06A2), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0682), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213A), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x18E3), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x217F), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_HP, + 0x2005), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020C), + .driver_data = ATH_PCI_AR9565_1ANT | ATH_PCI_BT_ANT_DIV }, + + /* WB335 2-ANT / Antenna-Diversity */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411A), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411B), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411C), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411D), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_SAMSUNG, + 0x411E), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x3027), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ATHEROS, + 0x302C), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0642), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0652), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0612), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0832), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x11AD, /* LITEON */ + 0x0692), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2130), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x213B), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_AZWAVE, + 0x2182), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x144F, /* ASKEY */ + 0x7202), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x2810), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x1B9A, /* XAVI */ + 0x28A2), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0x3027), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + 0x185F, /* WNC */ + 0xA120), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE07F), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_FOXCONN, + 0xE081), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x3026), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_LENOVO, + 0x4026), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_ASUSTEK, + 0x85F2), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + { PCI_DEVICE_SUB(PCI_VENDOR_ID_ATHEROS, + 0x0036, + PCI_VENDOR_ID_DELL, + 0x020E), + .driver_data = ATH_PCI_AR9565_2ANT | ATH_PCI_BT_ANT_DIV }, + + /* PCI-E AR9565 (WB335) */ + { PCI_VDEVICE(PCI_VENDOR_ID_ATHEROS, 0x0036), + .driver_data = ATH_PCI_BT_ANT_DIV }, + + { 0 } +}; + diff --git a/sys/dev/ath/if_ath_rx.c b/sys/dev/ath/if_ath_rx.c index d9e212b..bed9488 100644 --- a/sys/dev/ath/if_ath_rx.c +++ b/sys/dev/ath/if_ath_rx.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -165,10 +166,22 @@ ath_calcrxfilter(struct ath_softc *sc) /* XXX ic->ic_monvaps != 0? */ if (ic->ic_opmode == IEEE80211_M_MONITOR || (ifp->if_flags & IFF_PROMISC)) rfilt |= HAL_RX_FILTER_PROM; + + /* + * Only listen to all beacons if we're scanning. + * + * Otherwise we only really need to hear beacons from + * our own BSSID. + */ if (ic->ic_opmode == IEEE80211_M_STA || - ic->ic_opmode == IEEE80211_M_IBSS || - sc->sc_swbmiss || sc->sc_scanning) - rfilt |= HAL_RX_FILTER_BEACON; + ic->ic_opmode == IEEE80211_M_IBSS || sc->sc_swbmiss) { + if (sc->sc_do_mybeacon && ! sc->sc_scanning) { + rfilt |= HAL_RX_FILTER_MYBEACON; + } else { /* scanning, non-mybeacon chips */ + rfilt |= HAL_RX_FILTER_BEACON; + } + } + /* * NB: We don't recalculate the rx filter when * ic_protmode changes; otherwise we could do @@ -232,6 +245,8 @@ ath_legacy_rxbuf_init(struct ath_softc *sc, struct ath_buf *bf) struct mbuf *m; struct ath_desc *ds; + /* XXX TODO: ATH_RX_LOCK_ASSERT(sc); */ + m = bf->bf_m; if (m == NULL) { /* @@ -316,6 +331,23 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, { struct ieee80211vap *vap = ni->ni_vap; struct ath_softc *sc = vap->iv_ic->ic_ifp->if_softc; + uint64_t tsf_beacon_old, tsf_beacon; + uint64_t nexttbtt; + int64_t tsf_delta; + int32_t tsf_delta_bmiss; + int32_t tsf_remainder; + uint64_t tsf_beacon_target; + int tsf_intval; + + tsf_beacon_old = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon_old |= LE_READ_4(ni->ni_tstamp.data); + +#define TU_TO_TSF(_tu) (((u_int64_t)(_tu)) << 10) + tsf_intval = 1; + if (ni->ni_intval > 0) { + tsf_intval = TU_TO_TSF(ni->ni_intval); + } +#undef TU_TO_TSF /* * Call up first so subsequent work can use information @@ -327,14 +359,79 @@ ath_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, /* update rssi statistics for use by the hal */ /* XXX unlocked check against vap->iv_bss? */ ATH_RSSI_LPF(sc->sc_halstats.ns_avgbrssi, rssi); + + tsf_beacon = ((uint64_t) LE_READ_4(ni->ni_tstamp.data + 4)) << 32; + tsf_beacon |= LE_READ_4(ni->ni_tstamp.data); + + nexttbtt = ath_hal_getnexttbtt(sc->sc_ah); + + /* + * Let's calculate the delta and remainder, so we can see + * if the beacon timer from the AP is varying by more than + * a few TU. (Which would be a huge, huge problem.) + */ + tsf_delta = (long long) tsf_beacon - (long long) tsf_beacon_old; + + tsf_delta_bmiss = tsf_delta / tsf_intval; + + /* + * If our delta is greater than half the beacon interval, + * let's round the bmiss value up to the next beacon + * interval. Ie, we're running really, really early + * on the next beacon. + */ + if (tsf_delta % tsf_intval > (tsf_intval / 2)) + tsf_delta_bmiss ++; + + tsf_beacon_target = tsf_beacon_old + + (((unsigned long long) tsf_delta_bmiss) * (long long) tsf_intval); + + /* + * The remainder using '%' is between 0 .. intval-1. + * If we're actually running too fast, then the remainder + * will be some large number just under intval-1. + * So we need to look at whether we're running + * before or after the target beacon interval + * and if we are, modify how we do the remainder + * calculation. + */ + if (tsf_beacon < tsf_beacon_target) { + tsf_remainder = + -(tsf_intval - ((tsf_beacon - tsf_beacon_old) % tsf_intval)); + } else { + tsf_remainder = (tsf_beacon - tsf_beacon_old) % tsf_intval; + } + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: old_tsf=%llu, new_tsf=%llu, target_tsf=%llu, delta=%lld, bmiss=%d, remainder=%d\n", + __func__, + (unsigned long long) tsf_beacon_old, + (unsigned long long) tsf_beacon, + (unsigned long long) tsf_beacon_target, + (long long) tsf_delta, + tsf_delta_bmiss, + tsf_remainder); + + DPRINTF(sc, ATH_DEBUG_BEACON, "%s: tsf=%llu, nexttbtt=%llu, delta=%d\n", + __func__, + (unsigned long long) tsf_beacon, + (unsigned long long) nexttbtt, + (int32_t) tsf_beacon - (int32_t) nexttbtt + tsf_intval); + if (sc->sc_syncbeacon && - ni == vap->iv_bss && vap->iv_state == IEEE80211_S_RUN) { + ni == vap->iv_bss && + (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP)) { + DPRINTF(sc, ATH_DEBUG_BEACON, + "%s: syncbeacon=1; syncing\n", + __func__); /* * Resync beacon timers using the tsf of the beacon * frame we just received. */ ath_beacon_config(sc, vap); + sc->sc_syncbeacon = 0; } + + /* fall thru... */ case IEEE80211_FC0_SUBTYPE_PROBE_RESP: if (vap->iv_opmode == IEEE80211_M_IBSS && @@ -607,7 +704,7 @@ ath_rx_pkt(struct ath_softc *sc, struct ath_rx_status *rs, HAL_STATUS status, rs->rs_keyix-32 : rs->rs_keyix); } } - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); rx_error: /* * Cleanup any pending partial frame. @@ -733,7 +830,7 @@ rx_accept: rs->rs_antenna |= 0x4; } - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); sc->sc_stats.ast_ant_rx[rs->rs_antenna]++; /* @@ -879,6 +976,14 @@ rx_next: #define ATH_RX_MAX 128 +/* + * XXX TODO: break out the "get buffers" from "call ath_rx_pkt()" like + * the EDMA code does. + * + * XXX TODO: then, do all of the RX list management stuff inside + * ATH_RX_LOCK() so we don't end up potentially racing. The EDMA + * code is doing it right. + */ static void ath_rx_proc(struct ath_softc *sc, int resched) { @@ -900,6 +1005,7 @@ ath_rx_proc(struct ath_softc *sc, int resched) u_int64_t tsf; int npkts = 0; int kickpcu = 0; + int ret; /* XXX we must not hold the ATH_LOCK here */ ATH_UNLOCK_ASSERT(sc); @@ -910,6 +1016,10 @@ ath_rx_proc(struct ath_softc *sc, int resched) kickpcu = sc->sc_kickpcu; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + DPRINTF(sc, ATH_DEBUG_RX_PROC, "%s: called\n", __func__); ngood = 0; nf = ath_hal_getchannoise(ah, sc->sc_curchan); @@ -995,8 +1105,26 @@ ath_rx_proc(struct ath_softc *sc, int resched) if (ath_rx_pkt(sc, rs, status, tsf, nf, HAL_RX_QUEUE_HP, bf, m)) ngood++; rx_proc_next: - TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); - } while (ath_rxbuf_init(sc, bf) == 0); + /* + * If there's a holding buffer, insert that onto + * the RX list; the hardware is now definitely not pointing + * to it now. + */ + ret = 0; + if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf != NULL) { + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf, + bf_list); + ret = ath_rxbuf_init(sc, + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf); + } + /* + * Next, throw our buffer into the holding entry. The hardware + * may use the descriptor to read the link pointer before + * DMAing the next descriptor in to write out a packet. + */ + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = bf; + } while (ret == 0); /* rx signal state monitoring */ ath_hal_rxmonitor(ah, &sc->sc_halstats, sc->sc_curchan); @@ -1028,6 +1156,13 @@ rx_proc_next: * constantly write over the same frame, leading * the RX driver code here to get heavily confused. */ + /* + * XXX Has RX DMA stopped enough here to just call + * ath_startrecv()? + * XXX Do we need to use the holding buffer to restart + * RX DMA by appending entries to the final + * descriptor? Quite likely. + */ #if 1 ath_startrecv(sc); #else @@ -1065,6 +1200,13 @@ rx_proc_next: #undef PA2DESC /* + * Put the hardware to sleep again if we're done with it. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + /* * If we hit the maximum number of frames in this round, * reschedule for another immediate pass. This gives * the TX and TX completion routines time to run, which @@ -1111,6 +1253,58 @@ ath_legacy_flushrecv(struct ath_softc *sc) ath_rx_proc(sc, 0); } +static void +ath_legacy_flush_rxpending(struct ath_softc *sc) +{ + + /* XXX ATH_RX_LOCK_ASSERT(sc); */ + + if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) { + m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending); + sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; + } + if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) { + m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending); + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; + } +} + +static int +ath_legacy_flush_rxholdbf(struct ath_softc *sc) +{ + struct ath_buf *bf; + + /* XXX ATH_RX_LOCK_ASSERT(sc); */ + /* + * If there are RX holding buffers, free them here and return + * them to the list. + * + * XXX should just verify that bf->bf_m is NULL, as it must + * be at this point! + */ + bf = sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf; + if (bf != NULL) { + if (bf->bf_m != NULL) + m_freem(bf->bf_m); + bf->bf_m = NULL; + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + (void) ath_rxbuf_init(sc, bf); + } + sc->sc_rxedma[HAL_RX_QUEUE_HP].m_holdbf = NULL; + + bf = sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf; + if (bf != NULL) { + if (bf->bf_m != NULL) + m_freem(bf->bf_m); + bf->bf_m = NULL; + TAILQ_INSERT_TAIL(&sc->sc_rxbuf, bf, bf_list); + (void) ath_rxbuf_init(sc, bf); + } + sc->sc_rxedma[HAL_RX_QUEUE_LP].m_holdbf = NULL; + + return (0); +} + /* * Disable the receive h/w in preparation for a reset. */ @@ -1122,6 +1316,8 @@ ath_legacy_stoprecv(struct ath_softc *sc, int dodelay) ((_pa) - (_sc)->sc_rxdma.dd_desc_paddr))) struct ath_hal *ah = sc->sc_ah; + ATH_RX_LOCK(sc); + ath_hal_stoppcurecv(ah); /* disable PCU */ ath_hal_setrxfilter(ah, 0); /* clear recv filter */ ath_hal_stopdmarecv(ah); /* disable DMA engine */ @@ -1155,22 +1351,23 @@ ath_legacy_stoprecv(struct ath_softc *sc, int dodelay) } } #endif - /* - * Free both high/low RX pending, just in case. - */ - if (sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending != NULL) { - m_freem(sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending); - sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; - } - if (sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending != NULL) { - m_freem(sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending); - sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; - } + + (void) ath_legacy_flush_rxpending(sc); + (void) ath_legacy_flush_rxholdbf(sc); + sc->sc_rxlink = NULL; /* just in case */ + + ATH_RX_UNLOCK(sc); #undef PA2DESC } /* + * XXX TODO: something was calling startrecv without calling + * stoprecv. Let's figure out what/why. It was showing up + * as a mbuf leak (rxpending) and ath_buf leak (holdbf.) + */ + +/* * Enable the receive h/w following a reset. */ static int @@ -1179,9 +1376,18 @@ ath_legacy_startrecv(struct ath_softc *sc) struct ath_hal *ah = sc->sc_ah; struct ath_buf *bf; + ATH_RX_LOCK(sc); + + /* + * XXX should verify these are already all NULL! + */ sc->sc_rxlink = NULL; - sc->sc_rxedma[HAL_RX_QUEUE_LP].m_rxpending = NULL; - sc->sc_rxedma[HAL_RX_QUEUE_HP].m_rxpending = NULL; + (void) ath_legacy_flush_rxpending(sc); + (void) ath_legacy_flush_rxholdbf(sc); + + /* + * Re-chain all of the buffers in the RX buffer list. + */ TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { int error = ath_rxbuf_init(sc, bf); if (error != 0) { @@ -1197,6 +1403,8 @@ ath_legacy_startrecv(struct ath_softc *sc) ath_hal_rxena(ah); /* enable recv descriptors */ ath_mode_init(sc); /* set filters, etc. */ ath_hal_startpcurecv(ah); /* re-enable PCU/DMA engine */ + + ATH_RX_UNLOCK(sc); return 0; } diff --git a/sys/dev/ath/if_ath_rx_edma.c b/sys/dev/ath/if_ath_rx_edma.c index 2be8627..7aa818f 100644 --- a/sys/dev/ath/if_ath_rx_edma.c +++ b/sys/dev/ath/if_ath_rx_edma.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -159,10 +160,20 @@ ath_edma_stoprecv(struct ath_softc *sc, int dodelay) struct ath_hal *ah = sc->sc_ah; ATH_RX_LOCK(sc); + ath_hal_stoppcurecv(ah); ath_hal_setrxfilter(ah, 0); - ath_hal_stopdmarecv(ah); + /* + * + */ + if (ath_hal_stopdmarecv(ah) == AH_TRUE) + sc->sc_rx_stopped = 1; + + /* + * Give the various bus FIFOs (not EDMA descriptor FIFO) + * time to finish flushing out data. + */ DELAY(3000); /* Flush RX pending for each queue */ @@ -217,10 +228,6 @@ ath_edma_reinit_fifo(struct ath_softc *sc, HAL_RX_QUEUE qtype) /* * Start receive. - * - * XXX TODO: this needs to reallocate the FIFO entries when a reset - * occurs, in case the FIFO is filled up and no new descriptors get - * thrown into the FIFO. */ static int ath_edma_startrecv(struct ath_softc *sc) @@ -229,35 +236,31 @@ ath_edma_startrecv(struct ath_softc *sc) ATH_RX_LOCK(sc); + /* + * Sanity check - are we being called whilst RX + * isn't stopped? If so, we may end up pushing + * too many entries into the RX FIFO and + * badness occurs. + */ + /* Enable RX FIFO */ ath_hal_rxena(ah); /* - * Entries should only be written out if the - * FIFO is empty. - * - * XXX This isn't correct. I should be looking - * at the value of AR_RXDP_SIZE (0x0070) to determine - * how many entries are in here. - * - * A warm reset will clear the registers but not the FIFO. - * - * And I believe this is actually the address of the last - * handled buffer rather than the current FIFO pointer. - * So if no frames have been (yet) seen, we'll reinit the - * FIFO. - * - * I'll chase that up at some point. + * In theory the hardware has been initialised, right? */ - if (ath_hal_getrxbuf(sc->sc_ah, HAL_RX_QUEUE_HP) == 0) { + if (sc->sc_rx_resetted == 1) { DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Re-initing HP FIFO\n", __func__); ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_HP); - } - if (ath_hal_getrxbuf(sc->sc_ah, HAL_RX_QUEUE_LP) == 0) { DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Re-initing LP FIFO\n", __func__); ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_LP); + sc->sc_rx_resetted = 0; + } else { + device_printf(sc->sc_dev, + "%s: called without resetting chip?\n", + __func__); } /* Add up to m_fifolen entries in each queue */ @@ -265,6 +268,9 @@ ath_edma_startrecv(struct ath_softc *sc) * These must occur after the above write so the FIFO buffers * are pushed/tracked in the same order as the hardware will * process them. + * + * XXX TODO: is this really necessary? We should've stopped + * the hardware already and reinitialised it, so it's a no-op. */ ath_edma_rxfifo_alloc(sc, HAL_RX_QUEUE_HP, sc->sc_rxedma[HAL_RX_QUEUE_HP].m_fifolen); @@ -275,6 +281,11 @@ ath_edma_startrecv(struct ath_softc *sc) ath_mode_init(sc); ath_hal_startpcurecv(ah); + /* + * We're now doing RX DMA! + */ + sc->sc_rx_stopped = 0; + ATH_RX_UNLOCK(sc); return (0); @@ -285,7 +296,16 @@ ath_edma_recv_sched_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, int dosched) { + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, qtype, dosched); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } @@ -293,8 +313,17 @@ static void ath_edma_recv_sched(struct ath_softc *sc, int dosched) { + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, dosched); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, dosched); + + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + taskqueue_enqueue(sc->sc_tq, &sc->sc_rxtask); } @@ -308,6 +337,10 @@ ath_edma_recv_flush(struct ath_softc *sc) sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + /* * Flush any active frames from FIFO -> deferred list */ @@ -317,9 +350,18 @@ ath_edma_recv_flush(struct ath_softc *sc) /* * Process what's in the deferred queue */ + /* + * XXX: If we read the tsf/channoise here and then pass it in, + * we could restore the power state before processing + * the deferred queue. + */ ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 0); ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 0); + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + ATH_PCU_LOCK(sc); sc->sc_rxproc_cnt--; ATH_PCU_UNLOCK(sc); @@ -348,6 +390,21 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, ATH_RX_LOCK(sc); +#if 1 + if (sc->sc_rx_resetted == 1) { + /* + * XXX We shouldn't ever be scheduled if + * receive has been stopped - so complain + * loudly! + */ + device_printf(sc->sc_dev, + "%s: sc_rx_resetted=1! Bad!\n", + __func__); + ATH_RX_UNLOCK(sc); + return; + } +#endif + do { bf = re->m_fifo[re->m_fifo_head]; /* This shouldn't occur! */ @@ -419,24 +476,6 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, "ath edma rx proc: npkts=%d\n", npkts); - /* Handle resched and kickpcu appropriately */ - ATH_PCU_LOCK(sc); - if (dosched && sc->sc_kickpcu) { - ATH_KTR(sc, ATH_KTR_ERROR, 0, - "ath_edma_recv_proc_queue(): kickpcu"); - if (npkts > 0) - device_printf(sc->sc_dev, - "%s: handled npkts %d\n", - __func__, npkts); - - /* - * XXX TODO: what should occur here? Just re-poke and - * re-enable the RX FIFO? - */ - sc->sc_kickpcu = 0; - } - ATH_PCU_UNLOCK(sc); - return; } @@ -449,18 +488,20 @@ ath_edma_recv_proc_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, static void ath_edma_flush_deferred_queue(struct ath_softc *sc) { - struct ath_buf *bf, *next; + struct ath_buf *bf; ATH_RX_LOCK_ASSERT(sc); /* Free in one set, inside the lock */ - TAILQ_FOREACH_SAFE(bf, - &sc->sc_rx_rxlist[HAL_RX_QUEUE_LP], bf_list, next) { + while (! TAILQ_EMPTY(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP])) { + bf = TAILQ_FIRST(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP]); + TAILQ_REMOVE(&sc->sc_rx_rxlist[HAL_RX_QUEUE_LP], bf, bf_list); /* Free the buffer/mbuf */ ath_edma_rxbuf_free(sc, bf); } - TAILQ_FOREACH_SAFE(bf, - &sc->sc_rx_rxlist[HAL_RX_QUEUE_HP], bf_list, next) { + while (! TAILQ_EMPTY(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP])) { + bf = TAILQ_FIRST(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP]); + TAILQ_REMOVE(&sc->sc_rx_rxlist[HAL_RX_QUEUE_HP], bf, bf_list); /* Free the buffer/mbuf */ ath_edma_rxbuf_free(sc, bf); } @@ -494,6 +535,10 @@ ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, ATH_RX_UNLOCK(sc); /* Handle the completed descriptors */ + /* + * XXX is this SAFE call needed? The ath_buf entries + * aren't modified by ath_rx_pkt, right? + */ TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { /* * Skip the RX descriptor status - start at the data offset @@ -519,7 +564,9 @@ ath_edma_recv_proc_deferred_queue(struct ath_softc *sc, HAL_RX_QUEUE qtype, /* Free in one set, inside the lock */ ATH_RX_LOCK(sc); - TAILQ_FOREACH_SAFE(bf, &rxlist, bf_list, next) { + while (! TAILQ_EMPTY(&rxlist)) { + bf = TAILQ_FIRST(&rxlist); + TAILQ_REMOVE(&rxlist, bf, bf_list); /* Free the buffer/mbuf */ ath_edma_rxbuf_free(sc, bf); } @@ -551,12 +598,25 @@ ath_edma_recv_tasklet(void *arg, int npending) sc->sc_rxproc_cnt++; ATH_PCU_UNLOCK(sc); + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_queue(sc, HAL_RX_QUEUE_LP, 1); ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_HP, 1); ath_edma_recv_proc_deferred_queue(sc, HAL_RX_QUEUE_LP, 1); + /* + * XXX: If we read the tsf/channoise here and then pass it in, + * we could restore the power state before processing + * the deferred queue. + */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + /* XXX inside IF_LOCK ? */ if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) == 0) { #ifdef IEEE80211_SUPPORT_SUPERG @@ -835,10 +895,13 @@ ath_edma_setup_rxfifo(struct ath_softc *sc, HAL_RX_QUEUE qtype) qtype); return (-EINVAL); } - device_printf(sc->sc_dev, "%s: type=%d, FIFO depth = %d entries\n", - __func__, - qtype, - re->m_fifolen); + + if (bootverbose) + device_printf(sc->sc_dev, + "%s: type=%d, FIFO depth = %d entries\n", + __func__, + qtype, + re->m_fifolen); /* Allocate ath_buf FIFO array, pre-zero'ed */ re->m_fifo = malloc(sizeof(struct ath_buf *) * re->m_fifolen, @@ -929,10 +992,12 @@ ath_recv_setup_edma(struct ath_softc *sc) (void) ath_hal_setrxbufsize(sc->sc_ah, sc->sc_edma_bufsize - sc->sc_rx_statuslen); - device_printf(sc->sc_dev, "RX status length: %d\n", - sc->sc_rx_statuslen); - device_printf(sc->sc_dev, "RX buffer size: %d\n", - sc->sc_edma_bufsize); + if (bootverbose) { + device_printf(sc->sc_dev, "RX status length: %d\n", + sc->sc_rx_statuslen); + device_printf(sc->sc_dev, "RX buffer size: %d\n", + sc->sc_edma_bufsize); + } sc->sc_rx.recv_stop = ath_edma_stoprecv; sc->sc_rx.recv_start = ath_edma_startrecv; diff --git a/sys/dev/ath/if_ath_spectral.c b/sys/dev/ath/if_ath_spectral.c index 5cfb1a9..e4afdae 100644 --- a/sys/dev/ath/if_ath_spectral.c +++ b/sys/dev/ath/if_ath_spectral.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/kernel.h> #include <sys/lock.h> +#include <sys/malloc.h> #include <sys/mutex.h> #include <sys/errno.h> @@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$"); #include <sys/socket.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_media.h> #include <net/if_arp.h> #include <net/ethernet.h> /* XXX for ether_sprintf */ diff --git a/sys/dev/ath/if_ath_sysctl.c b/sys/dev/ath/if_ath_sysctl.c index 0a5719a..45c8ae4 100644 --- a/sys/dev/ath/if_ath_sysctl.c +++ b/sys/dev/ath/if_ath_sysctl.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -107,13 +108,26 @@ static int ath_sysctl_slottime(SYSCTL_HANDLER_ARGS) { struct ath_softc *sc = arg1; - u_int slottime = ath_hal_getslottime(sc->sc_ah); + u_int slottime; int error; + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + slottime = ath_hal_getslottime(sc->sc_ah); + ATH_UNLOCK(sc); + error = sysctl_handle_int(oidp, &slottime, 0, req); if (error || !req->newptr) - return error; - return !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + goto finish; + + error = !ath_hal_setslottime(sc->sc_ah, slottime) ? EINVAL : 0; + +finish: + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + + return error; } static int @@ -399,12 +413,14 @@ ath_sysctl_txagg(SYSCTL_HANDLER_ARGS) ATH_RX_LOCK(sc); for (i = 0; i < 2; i++) { - printf("%d: fifolen: %d/%d; head=%d; tail=%d\n", + printf("%d: fifolen: %d/%d; head=%d; tail=%d; m_pending=%p, m_holdbf=%p\n", i, sc->sc_rxedma[i].m_fifo_depth, sc->sc_rxedma[i].m_fifolen, sc->sc_rxedma[i].m_fifo_head, - sc->sc_rxedma[i].m_fifo_tail); + sc->sc_rxedma[i].m_fifo_tail, + sc->sc_rxedma[i].m_rxpending, + sc->sc_rxedma[i].m_holdbf); } i = 0; TAILQ_FOREACH(bf, &sc->sc_rxbuf, bf_list) { @@ -430,7 +446,15 @@ ath_sysctl_rfsilent(SYSCTL_HANDLER_ARGS) return error; if (!ath_hal_setrfsilent(sc->sc_ah, rfsilent)) return EINVAL; - sc->sc_rfsilentpin = rfsilent & 0x1c; + /* + * Earlier chips (< AR5212) have up to 8 GPIO + * pins exposed. + * + * AR5416 and later chips have many more GPIO + * pins (up to 16) so the mask is expanded to + * four bits. + */ + sc->sc_rfsilentpin = rfsilent & 0x3c; sc->sc_rfsilentpol = (rfsilent & 0x2) != 0; return 0; } diff --git a/sys/dev/ath/if_ath_tdma.c b/sys/dev/ath/if_ath_tdma.c index c075d01..de1a91c 100644 --- a/sys/dev/ath/if_ath_tdma.c +++ b/sys/dev/ath/if_ath_tdma.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -476,16 +477,19 @@ ath_tdma_update(struct ieee80211_node *ni, DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, "rs->rstamp %llu rstamp %llu tsf %llu txtime %d, nextslot %llu, " "nextslottu %d, nextslottume %d\n", - (unsigned long long) rs->rs_tstamp, rstamp, tsf, txtime, - nextslot, nextslottu, TSF_TO_TU(nextslot >> 32, nextslot)); + (unsigned long long) rs->rs_tstamp, + (unsigned long long) rstamp, + (unsigned long long) tsf, txtime, + (unsigned long long) nextslot, + nextslottu, TSF_TO_TU(nextslot >> 32, nextslot)); DPRINTF(sc, ATH_DEBUG_TDMA, " beacon tstamp: %llu (0x%016llx)\n", - le64toh(ni->ni_tstamp.tsf), - le64toh(ni->ni_tstamp.tsf)); + (unsigned long long) le64toh(ni->ni_tstamp.tsf), + (unsigned long long) le64toh(ni->ni_tstamp.tsf)); DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, "nexttbtt %llu (0x%08llx) tsfdelta %d avg +%d/-%d\n", - nexttbtt, + (unsigned long long) nexttbtt, (long long) nexttbtt, tsfdelta, TDMA_AVG(sc->sc_avgtsfdeltap), TDMA_AVG(sc->sc_avgtsfdeltam)); @@ -579,7 +583,7 @@ ath_tdma_update(struct ieee80211_node *ni, DPRINTF(sc, ATH_DEBUG_TDMA_TIMER, "%s: calling ath_hal_adjusttsf: TSF=%llu, tsfdelta=%d\n", __func__, - tsf, + (unsigned long long) tsf, tsfdelta); #ifdef ATH_DEBUG_ALQ diff --git a/sys/dev/ath/if_ath_tx.c b/sys/dev/ath/if_ath_tx.c index 8bacd92..096278e 100644 --- a/sys/dev/ath/if_ath_tx.c +++ b/sys/dev/ath/if_ath_tx.c @@ -59,10 +59,12 @@ __FBSDID("$FreeBSD$"); #include <sys/kthread.h> #include <sys/taskqueue.h> #include <sys/priv.h> +#include <sys/ktr.h> #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -759,37 +761,21 @@ ath_tx_handoff_hw(struct ath_softc *sc, struct ath_txq *txq, ("ath_tx_handoff_hw called for mcast queue")); /* - * XXX racy, should hold the PCU lock when checking this, - * and also should ensure that the TX counter is >0! + * XXX We should instead just verify that sc_txstart_cnt + * or ath_txproc_cnt > 0. That would mean that + * the reset is going to be waiting for us to complete. */ - KASSERT((sc->sc_inreset_cnt == 0), - ("%s: TX during reset?\n", __func__)); + if (sc->sc_txproc_cnt == 0 && sc->sc_txstart_cnt == 0) { + device_printf(sc->sc_dev, + "%s: TX dispatch without holding txcount/txstart refcnt!\n", + __func__); + } -#if 0 /* - * This causes a LOR. Find out where the PCU lock is being - * held whilst the TXQ lock is grabbed - that shouldn't - * be occuring. + * XXX .. this is going to cause the hardware to get upset; + * so we really should find some way to drop or queue + * things. */ - ATH_PCU_LOCK(sc); - if (sc->sc_inreset_cnt) { - ATH_PCU_UNLOCK(sc); - DPRINTF(sc, ATH_DEBUG_RESET, - "%s: called with sc_in_reset != 0\n", - __func__); - DPRINTF(sc, ATH_DEBUG_XMIT, - "%s: queued: TXDP[%u] = %p (%p) depth %d\n", - __func__, txq->axq_qnum, - (caddr_t)bf->bf_daddr, bf->bf_desc, - txq->axq_depth); - /* XXX axq_link needs to be set and updated! */ - ATH_TXQ_INSERT_TAIL(txq, bf, bf_list); - if (bf->bf_state.bfs_aggr) - txq->axq_aggr_depth++; - return; - } - ATH_PCU_UNLOCK(sc); -#endif ATH_TXQ_LOCK(txq); @@ -1613,6 +1599,7 @@ ath_tx_normal_setup(struct ath_softc *sc, struct ieee80211_node *ni, error = ath_tx_dmasetup(sc, bf, m0); if (error != 0) return error; + KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); @@ -2104,6 +2091,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, int do_override; uint8_t type, subtype; int queue_to_head; + struct ath_node *an = ATH_NODE(ni); ATH_TX_LOCK_ASSERT(sc); @@ -2163,6 +2151,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, return error; m0 = bf->bf_m; /* NB: may have changed */ wh = mtod(m0, struct ieee80211_frame *); + KASSERT((ni != NULL), ("%s: ni=NULL!", __func__)); bf->bf_node = ni; /* NB: held reference */ /* Always enable CLRDMASK for raw frames for now.. */ @@ -2181,12 +2170,24 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, rt = sc->sc_currates; KASSERT(rt != NULL, ("no rate table, mode %u", sc->sc_curmode)); + + /* Fetch first rate information */ rix = ath_tx_findrix(sc, params->ibp_rate0); + try0 = params->ibp_try0; + + /* + * Override EAPOL rate as appropriate. + */ + if (m0->m_flags & M_EAPOL) { + /* XXX? maybe always use long preamble? */ + rix = an->an_mgmtrix; + try0 = ATH_TXMAXTRY; /* XXX?too many? */ + } + txrate = rt->info[rix].rateCode; if (params->ibp_flags & IEEE80211_BPF_SHORTPRE) txrate |= rt->info[rix].shortPreamble; sc->sc_txrix = rix; - try0 = params->ibp_try0; ismrr = (params->ibp_try1 != 0); txantenna = params->ibp_pri >> 2; if (txantenna == 0) /* XXX? */ @@ -2259,8 +2260,7 @@ ath_tx_raw_start(struct ath_softc *sc, struct ieee80211_node *ni, /* Blank the legacy rate array */ bzero(&bf->bf_state.bfs_rc, sizeof(bf->bf_state.bfs_rc)); - bf->bf_state.bfs_rc[0].rix = - ath_tx_findrix(sc, params->ibp_rate0); + bf->bf_state.bfs_rc[0].rix = rix; bf->bf_state.bfs_rc[0].tries = try0; bf->bf_state.bfs_rc[0].ratecode = txrate; @@ -2352,11 +2352,16 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, "%s: sc_inreset_cnt > 0; bailing\n", __func__); error = EIO; ATH_PCU_UNLOCK(sc); - goto bad0; + goto badbad; } sc->sc_txstart_cnt++; ATH_PCU_UNLOCK(sc); + /* Wake the hardware up already */ + ATH_LOCK(sc); + ath_power_set_power_state(sc, HAL_PM_AWAKE); + ATH_UNLOCK(sc); + ATH_TX_LOCK(sc); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || sc->sc_invalid) { @@ -2419,7 +2424,7 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, } } sc->sc_wd_timer = 5; - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); sc->sc_stats.ast_tx_raw++; /* @@ -2435,7 +2440,14 @@ ath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + return 0; + bad2: ATH_KTR(sc, ATH_KTR_TX, 3, "ath_raw_xmit: bad2: m=%p, params=%p, " "bf=%p", @@ -2445,17 +2457,23 @@ bad2: ATH_TXBUF_LOCK(sc); ath_returnbuf_head(sc, bf); ATH_TXBUF_UNLOCK(sc); -bad: +bad: ATH_TX_UNLOCK(sc); ATH_PCU_LOCK(sc); sc->sc_txstart_cnt--; ATH_PCU_UNLOCK(sc); -bad0: + + /* Put the hardware back to sleep if required */ + ATH_LOCK(sc); + ath_power_restore_power_state(sc); + ATH_UNLOCK(sc); + +badbad: ATH_KTR(sc, ATH_KTR_TX, 2, "ath_raw_xmit: bad0: m=%p, params=%p", m, params); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); sc->sc_stats.ast_tx_raw_fail++; ieee80211_free_node(ni); @@ -2748,8 +2766,8 @@ ath_tx_update_baw(struct ath_softc *sc, struct ath_node *an, INCR(tid->baw_head, ATH_TID_MAX_BUFS); } DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, - "%s: baw is now %d:%d, baw head=%d\n", - __func__, tap->txa_start, tap->txa_wnd, tid->baw_head); + "%s: tid=%d: baw is now %d:%d, baw head=%d\n", + __func__, tid->tid, tap->txa_start, tap->txa_wnd, tid->baw_head); } static void @@ -3242,8 +3260,11 @@ ath_tx_tid_pause(struct ath_softc *sc, struct ath_tid *tid) ATH_TX_LOCK_ASSERT(sc); tid->paused++; - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: paused = %d\n", - __func__, tid->paused); + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: [%6D]: tid=%d, paused = %d\n", + __func__, + tid->an->an_node.ni_macaddr, ":", + tid->tid, + tid->paused); } /* @@ -3260,15 +3281,21 @@ ath_tx_tid_resume(struct ath_softc *sc, struct ath_tid *tid) * until it's actually resolved. */ if (tid->paused == 0) { - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, - "%s: %6D: paused=0?\n", __func__, - tid->an->an_node.ni_macaddr, ":"); + device_printf(sc->sc_dev, + "%s: [%6D]: tid=%d, paused=0?\n", + __func__, + tid->an->an_node.ni_macaddr, ":", + tid->tid); } else { tid->paused--; } - DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: unpaused = %d\n", - __func__, tid->paused); + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: [%6D]: tid=%d, unpaused = %d\n", + __func__, + tid->an->an_node.ni_macaddr, ":", + tid->tid, + tid->paused); if (tid->paused) return; @@ -3334,8 +3361,8 @@ ath_tx_tid_filt_comp_buf(struct ath_softc *sc, struct ath_tid *tid, ATH_TX_LOCK_ASSERT(sc); if (! tid->isfiltered) { - DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: filter transition\n", - __func__); + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d; filter transition\n", + __func__, tid->tid); tid->isfiltered = 1; ath_tx_tid_pause(sc, tid); } @@ -3355,15 +3382,20 @@ static void ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid) { struct ath_buf *bf; + int do_resume = 0; ATH_TX_LOCK_ASSERT(sc); if (tid->hwq_depth != 0) return; - DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: hwq=0, transition back\n", - __func__); - tid->isfiltered = 0; + DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: tid=%d, hwq=0, transition back\n", + __func__, tid->tid); + if (tid->isfiltered == 1) { + tid->isfiltered = 0; + do_resume = 1; + } + /* XXX ath_tx_tid_resume() also calls ath_tx_set_clrdmask()! */ ath_tx_set_clrdmask(sc, tid->an); @@ -3373,16 +3405,21 @@ ath_tx_tid_filt_comp_complete(struct ath_softc *sc, struct ath_tid *tid) ATH_TID_INSERT_HEAD(tid, bf, bf_list); } - ath_tx_tid_resume(sc, tid); + /* And only resume if we had paused before */ + if (do_resume) + ath_tx_tid_resume(sc, tid); } /* * Called when a single (aggregate or otherwise) frame is completed. * - * Returns 1 if the buffer could be added to the filtered list - * (cloned or otherwise), 0 if the buffer couldn't be added to the + * Returns 0 if the buffer could be added to the filtered list + * (cloned or otherwise), 1 if the buffer couldn't be added to the * filtered list (failed clone; expired retry) and the caller should * free it and handle it like a failure (eg by sending a BAR.) + * + * since the buffer may be cloned, bf must be not touched after this + * if the return value is 0. */ static int ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, @@ -3402,8 +3439,9 @@ ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, "%s: bf=%p, seqno=%d, exceeded retries\n", __func__, bf, - bf->bf_state.bfs_seqno); - return (0); + SEQNO(bf->bf_state.bfs_seqno)); + retval = 1; /* error */ + goto finish; } /* @@ -3423,11 +3461,12 @@ ath_tx_tid_filt_comp_single(struct ath_softc *sc, struct ath_tid *tid, DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, "%s: busy buffer couldn't be cloned (%p)!\n", __func__, bf); - retval = 1; + retval = 1; /* error */ } else { ath_tx_tid_filt_comp_buf(sc, tid, nbf); - retval = 0; + retval = 0; /* ok */ } +finish: ath_tx_tid_filt_comp_complete(sc, tid); return (retval); @@ -3452,10 +3491,11 @@ ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, if (bf->bf_state.bfs_retries > SWMAX_RETRIES) { sc->sc_stats.ast_tx_swretrymax++; DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, - "%s: bf=%p, seqno=%d, exceeded retries\n", + "%s: tid=%d, bf=%p, seqno=%d, exceeded retries\n", __func__, + tid->tid, bf, - bf->bf_state.bfs_seqno); + SEQNO(bf->bf_state.bfs_seqno)); TAILQ_INSERT_TAIL(bf_q, bf, bf_list); goto next; } @@ -3463,8 +3503,8 @@ ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, if (bf->bf_flags & ATH_BUF_BUSY) { nbf = ath_tx_retry_clone(sc, tid->an, tid, bf); DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, - "%s: busy buffer cloned: %p -> %p", - __func__, bf, nbf); + "%s: tid=%d, busy buffer cloned: %p -> %p, seqno=%d\n", + __func__, tid->tid, bf, nbf, SEQNO(bf->bf_state.bfs_seqno)); } else { nbf = bf; } @@ -3475,8 +3515,8 @@ ath_tx_tid_filt_comp_aggr(struct ath_softc *sc, struct ath_tid *tid, */ if (nbf == NULL) { DPRINTF(sc, ATH_DEBUG_SW_TX_FILT, - "%s: buffer couldn't be cloned! (%p)\n", - __func__, bf); + "%s: tid=%d, buffer couldn't be cloned! (%p) seqno=%d\n", + __func__, tid->tid, bf, SEQNO(bf->bf_state.bfs_seqno)); TAILQ_INSERT_TAIL(bf_q, bf, bf_list); } else { ath_tx_tid_filt_comp_buf(sc, tid, nbf); @@ -3717,7 +3757,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, txq = sc->sc_ac2q[tid->ac]; tap = ath_tx_get_tx_tid(an, tid->tid); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: bf=%p: addbaw=%d, dobaw=%d, " "seqno=%d, retry=%d\n", __func__, @@ -3729,7 +3769,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, bf->bf_state.bfs_dobaw, SEQNO(bf->bf_state.bfs_seqno), bf->bf_state.bfs_retries); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: bf=%p: txq[%d] axq_depth=%d, axq_aggr_depth=%d\n", __func__, pfx, @@ -3739,7 +3779,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, txq->axq_qnum, txq->axq_depth, txq->axq_aggr_depth); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: bf=%p: tid txq_depth=%d hwq_depth=%d, bar_wait=%d, " "isfiltered=%d\n", __func__, @@ -3751,7 +3791,7 @@ ath_tx_tid_drain_print(struct ath_softc *sc, struct ath_node *an, tid->hwq_depth, tid->bar_wait, tid->isfiltered); - DPRINTF(sc, ATH_DEBUG_SW_TX, + DPRINTF(sc, ATH_DEBUG_SW_TX | ATH_DEBUG_RESET, "%s: %s: %6D: tid %d: " "sched=%d, paused=%d, " "incomp=%d, baw_head=%d, " @@ -3811,7 +3851,7 @@ ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, if (t == 0) { ath_tx_tid_drain_print(sc, an, "norm", tid, bf); - t = 1; +// t = 1; } ATH_TID_REMOVE(tid, bf, bf_list); @@ -3827,7 +3867,7 @@ ath_tx_tid_drain(struct ath_softc *sc, struct ath_node *an, if (t == 0) { ath_tx_tid_drain_print(sc, an, "filt", tid, bf); - t = 1; +// t = 1; } ATH_TID_FILT_REMOVE(tid, bf, bf_list); @@ -4084,6 +4124,19 @@ ath_tx_normal_comp(struct ath_softc *sc, struct ath_buf *bf, int fail) DPRINTF(sc, ATH_DEBUG_SW_TX, "%s: hwq_depth < 0: %d\n", __func__, atid->hwq_depth); + /* If the TID is being cleaned up, track things */ + /* XXX refactor! */ + if (atid->cleanup_inprogress) { + atid->incomp--; + if (atid->incomp == 0) { + DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, + "%s: TID %d: cleaned up! resume!\n", + __func__, tid); + atid->cleanup_inprogress = 0; + ath_tx_tid_resume(sc, atid); + } + } + /* * If the queue is filtered, potentially mark it as complete * and reschedule it as needed. @@ -4131,6 +4184,16 @@ ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) ATH_TX_LOCK(sc); atid->incomp--; + + /* XXX refactor! */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } + if (atid->incomp == 0) { DPRINTF(sc, ATH_DEBUG_SW_TX_CTRL, "%s: TID %d: cleaned up! resume!\n", @@ -4143,14 +4206,72 @@ ath_tx_comp_cleanup_unaggr(struct ath_softc *sc, struct ath_buf *bf) ath_tx_default_comp(sc, bf, 0); } + +/* + * This as it currently stands is a bit dumb. Ideally we'd just + * fail the frame the normal way and have it permanently fail + * via the normal aggregate completion path. + */ +static void +ath_tx_tid_cleanup_frame(struct ath_softc *sc, struct ath_node *an, + int tid, struct ath_buf *bf_head, ath_bufhead *bf_cq) +{ + struct ath_tid *atid = &an->an_tid[tid]; + struct ath_buf *bf, *bf_next; + + ATH_TX_LOCK_ASSERT(sc); + + /* + * Remove this frame from the queue. + */ + ATH_TID_REMOVE(atid, bf_head, bf_list); + + /* + * Loop over all the frames in the aggregate. + */ + bf = bf_head; + while (bf != NULL) { + bf_next = bf->bf_next; /* next aggregate frame, or NULL */ + + /* + * If it's been added to the BAW we need to kick + * it out of the BAW before we continue. + * + * XXX if it's an aggregate, assert that it's in the + * BAW - we shouldn't have it be in an aggregate + * otherwise! + */ + if (bf->bf_state.bfs_addedbaw) { + ath_tx_update_baw(sc, an, atid, bf); + bf->bf_state.bfs_dobaw = 0; + } + + /* + * Give it the default completion handler. + */ + bf->bf_comp = ath_tx_normal_comp; + bf->bf_next = NULL; + + /* + * Add it to the list to free. + */ + TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); + + /* + * Now advance to the next frame in the aggregate. + */ + bf = bf_next; + } +} + /* * Performs transmit side cleanup when TID changes from aggregated to - * unaggregated. + * unaggregated and during reassociation. * - * - Discard all retry frames from the s/w queue. - * - Fix the tx completion function for all buffers in s/w queue. - * - Count the number of unacked frames, and let transmit completion - * handle it later. + * For now, this just tosses everything from the TID software queue + * whether or not it has been retried and marks the TID as + * pending completion if there's anything for this TID queued to + * the hardware. * * The caller is responsible for pausing the TID and unpausing the * TID if no cleanup was required. Otherwise the cleanup path will @@ -4161,18 +4282,19 @@ ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, ath_bufhead *bf_cq) { struct ath_tid *atid = &an->an_tid[tid]; - struct ieee80211_tx_ampdu *tap; struct ath_buf *bf, *bf_next; ATH_TX_LOCK_ASSERT(sc); DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, - "%s: TID %d: called\n", __func__, tid); + "%s: TID %d: called; inprogress=%d\n", __func__, tid, + atid->cleanup_inprogress); /* * Move the filtered frames to the TX queue, before * we run off and discard/process things. */ + /* XXX this is really quite inefficient */ while ((bf = ATH_TID_FILT_LAST(atid, ath_bufhead_s)) != NULL) { ATH_TID_FILT_REMOVE(atid, bf, bf_list); @@ -4187,47 +4309,35 @@ ath_tx_tid_cleanup(struct ath_softc *sc, struct ath_node *an, int tid, */ bf = ATH_TID_FIRST(atid); while (bf) { - if (bf->bf_state.bfs_isretried) { - bf_next = TAILQ_NEXT(bf, bf_list); - ATH_TID_REMOVE(atid, bf, bf_list); - if (bf->bf_state.bfs_dobaw) { - ath_tx_update_baw(sc, an, atid, bf); - if (!bf->bf_state.bfs_addedbaw) - DPRINTF(sc, ATH_DEBUG_SW_TX_BAW, - "%s: wasn't added: seqno %d\n", - __func__, - SEQNO(bf->bf_state.bfs_seqno)); - } - bf->bf_state.bfs_dobaw = 0; - /* - * Call the default completion handler with "fail" just - * so upper levels are suitably notified about this. - */ - TAILQ_INSERT_TAIL(bf_cq, bf, bf_list); - bf = bf_next; - continue; - } - /* Give these the default completion handler */ - bf->bf_comp = ath_tx_normal_comp; - bf = TAILQ_NEXT(bf, bf_list); + /* + * Grab the next frame in the list, we may + * be fiddling with the list. + */ + bf_next = TAILQ_NEXT(bf, bf_list); + + /* + * Free the frame and all subframes. + */ + ath_tx_tid_cleanup_frame(sc, an, tid, bf, bf_cq); + + /* + * Next frame! + */ + bf = bf_next; } /* - * Calculate what hardware-queued frames exist based - * on the current BAW size. Ie, what frames have been - * added to the TX hardware queue for this TID but - * not yet ACKed. + * If there's anything in the hardware queue we wait + * for the TID HWQ to empty. */ - tap = ath_tx_get_tx_tid(an, tid); - /* Need the lock - fiddling with BAW */ - while (atid->baw_head != atid->baw_tail) { - if (atid->tx_buf[atid->baw_head]) { - atid->incomp++; - atid->cleanup_inprogress = 1; - atid->tx_buf[atid->baw_head] = NULL; - } - INCR(atid->baw_head, ATH_TID_MAX_BUFS); - INCR(tap->txa_start, IEEE80211_SEQ_RANGE); + if (atid->hwq_depth > 0) { + /* + * XXX how about we kill atid->incomp, and instead + * replace it with a macro that checks that atid->hwq_depth + * is 0? + */ + atid->incomp = atid->hwq_depth; + atid->cleanup_inprogress = 1; } if (atid->cleanup_inprogress) @@ -4560,9 +4670,19 @@ ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first) ATH_TX_LOCK(sc); /* update incomp */ + atid->incomp--; + + /* Update the BAW */ bf = bf_first; while (bf) { - atid->incomp--; + /* XXX refactor! */ + if (bf->bf_state.bfs_dobaw) { + ath_tx_update_baw(sc, an, atid, bf); + if (!bf->bf_state.bfs_addedbaw) + DPRINTF(sc, ATH_DEBUG_SW_TX, + "%s: wasn't added: seqno %d\n", + __func__, SEQNO(bf->bf_state.bfs_seqno)); + } bf = bf->bf_next; } @@ -4585,10 +4705,11 @@ ath_tx_comp_cleanup_aggr(struct ath_softc *sc, struct ath_buf *bf_first) ATH_TX_UNLOCK(sc); - /* Handle frame completion */ + /* Handle frame completion as individual frames */ bf = bf_first; while (bf) { bf_next = bf->bf_next; + bf->bf_next = NULL; ath_tx_default_comp(sc, bf, 1); bf = bf_next; } @@ -5030,6 +5151,10 @@ ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail) "%s: isfiltered=1, fail=%d\n", __func__, fail); freeframe = ath_tx_tid_filt_comp_single(sc, atid, bf); + /* + * If freeframe=0 then bf is no longer ours; don't + * touch it. + */ if (freeframe) { /* Remove from BAW */ if (bf->bf_state.bfs_addedbaw) @@ -5065,7 +5190,6 @@ ath_tx_aggr_comp_unaggr(struct ath_softc *sc, struct ath_buf *bf, int fail) if (freeframe) ath_tx_default_comp(sc, bf, fail); - return; } /* @@ -5495,7 +5619,7 @@ ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq) * a frame; be careful. */ if (! ath_tx_tid_can_tx_or_sched(sc, tid)) { - continue; + goto loop_done; } if (ath_tx_ampdu_running(sc, tid->an, tid->tid)) ath_tx_tid_hw_queue_aggr(sc, tid->an, tid); @@ -5518,7 +5642,7 @@ ath_txq_sched(struct ath_softc *sc, struct ath_txq *txq) if (txq->axq_depth >= sc->sc_hwq_limit_nonaggr) { break; } - +loop_done: /* * If this was the last entry on the original list, stop. * Otherwise nodes that have been rescheduled onto the end @@ -5771,12 +5895,26 @@ ath_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) */ TAILQ_INIT(&bf_cq); ATH_TX_LOCK(sc); - ath_tx_tid_cleanup(sc, an, tid, &bf_cq); + /* - * Unpause the TID if no cleanup is required. + * In case there's a followup call to this, only call it + * if we don't have a cleanup in progress. + * + * Since we've paused the queue above, we need to make + * sure we unpause if there's already a cleanup in + * progress - it means something else is also doing + * this stuff, so we don't need to also keep it paused. */ - if (! atid->cleanup_inprogress) + if (atid->cleanup_inprogress) { ath_tx_tid_resume(sc, atid); + } else { + ath_tx_tid_cleanup(sc, an, tid, &bf_cq); + /* + * Unpause the TID if no cleanup is required. + */ + if (! atid->cleanup_inprogress) + ath_tx_tid_resume(sc, atid); + } ATH_TX_UNLOCK(sc); /* Handle completing frames and fail them */ @@ -5810,19 +5948,25 @@ ath_tx_node_reassoc(struct ath_softc *sc, struct ath_node *an) tid = &an->an_tid[i]; if (tid->hwq_depth == 0) continue; - ath_tx_tid_pause(sc, tid); DPRINTF(sc, ATH_DEBUG_NODE, "%s: %6D: TID %d: cleaning up TID\n", __func__, an->an_node.ni_macaddr, ":", i); - ath_tx_tid_cleanup(sc, an, i, &bf_cq); /* - * Unpause the TID if no cleanup is required. + * In case there's a followup call to this, only call it + * if we don't have a cleanup in progress. */ - if (! tid->cleanup_inprogress) - ath_tx_tid_resume(sc, tid); + if (! tid->cleanup_inprogress) { + ath_tx_tid_pause(sc, tid); + ath_tx_tid_cleanup(sc, an, i, &bf_cq); + /* + * Unpause the TID if no cleanup is required. + */ + if (! tid->cleanup_inprogress) + ath_tx_tid_resume(sc, tid); + } } ATH_TX_UNLOCK(sc); @@ -5852,19 +5996,43 @@ ath_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, struct ath_node *an = ATH_NODE(ni); struct ath_tid *atid = &an->an_tid[tid]; int attempts = tap->txa_attempts; + int old_txa_start; DPRINTF(sc, ATH_DEBUG_SW_TX_BAR, - "%s: %6D: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d\n", + "%s: %6D: called; txa_tid=%d, atid->tid=%d, status=%d, attempts=%d, txa_start=%d, txa_seqpending=%d\n", __func__, ni->ni_macaddr, ":", tap->txa_tid, atid->tid, status, - attempts); + attempts, + tap->txa_start, + tap->txa_seqpending); /* Note: This may update the BAW details */ + /* + * XXX What if this does slide the BAW along? We need to somehow + * XXX either fix things when it does happen, or prevent the + * XXX seqpending value to be anything other than exactly what + * XXX the hell we want! + * + * XXX So for now, how I do this inside the TX lock for now + * XXX and just correct it afterwards? The below condition should + * XXX never happen and if it does I need to fix all kinds of things. + */ + ATH_TX_LOCK(sc); + old_txa_start = tap->txa_start; sc->sc_bar_response(ni, tap, status); + if (tap->txa_start != old_txa_start) { + device_printf(sc->sc_dev, "%s: tid=%d; txa_start=%d, old=%d, adjusting\n", + __func__, + tid, + tap->txa_start, + old_txa_start); + } + tap->txa_start = old_txa_start; + ATH_TX_UNLOCK(sc); /* Unpause the TID */ /* diff --git a/sys/dev/ath/if_ath_tx_edma.c b/sys/dev/ath/if_ath_tx_edma.c index 5498dd5..7d14920 100644 --- a/sys/dev/ath/if_ath_tx_edma.c +++ b/sys/dev/ath/if_ath_tx_edma.c @@ -73,6 +73,7 @@ __FBSDID("$FreeBSD$"); #include <machine/bus.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> @@ -865,12 +866,14 @@ ath_xmit_setup_edma(struct ath_softc *sc) (void) ath_hal_gettxstatuslen(sc->sc_ah, &sc->sc_tx_statuslen); (void) ath_hal_getntxmaps(sc->sc_ah, &sc->sc_tx_nmaps); - device_printf(sc->sc_dev, "TX descriptor length: %d\n", - sc->sc_tx_desclen); - device_printf(sc->sc_dev, "TX status length: %d\n", - sc->sc_tx_statuslen); - device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", - sc->sc_tx_nmaps); + if (bootverbose) { + device_printf(sc->sc_dev, "TX descriptor length: %d\n", + sc->sc_tx_desclen); + device_printf(sc->sc_dev, "TX status length: %d\n", + sc->sc_tx_statuslen); + device_printf(sc->sc_dev, "TX buffers per descriptor: %d\n", + sc->sc_tx_nmaps); + } sc->sc_tx.xmit_setup = ath_edma_dma_txsetup; sc->sc_tx.xmit_teardown = ath_edma_dma_txteardown; diff --git a/sys/dev/ath/if_athvar.h b/sys/dev/ath/if_athvar.h index 6b074d6..e888ca2 100644 --- a/sys/dev/ath/if_athvar.h +++ b/sys/dev/ath/if_athvar.h @@ -82,6 +82,25 @@ #define ATH_BEACON_CWMAX_DEFAULT 0 /* default cwmax for ap beacon q */ /* + * The following bits can be set during the PCI (and perhaps non-PCI + * later) device probe path. + * + * It controls some of the driver and HAL behaviour. + */ + +#define ATH_PCI_CUS198 0x0001 +#define ATH_PCI_CUS230 0x0002 +#define ATH_PCI_CUS217 0x0004 +#define ATH_PCI_CUS252 0x0008 +#define ATH_PCI_WOW 0x0010 +#define ATH_PCI_BT_ANT_DIV 0x0020 +#define ATH_PCI_D3_L1_WAR 0x0040 +#define ATH_PCI_AR9565_1ANT 0x0080 +#define ATH_PCI_AR9565_2ANT 0x0100 +#define ATH_PCI_NO_PLL_PWRSAVE 0x0200 +#define ATH_PCI_KILLER 0x0400 + +/* * The key cache is used for h/w cipher state and also for * tracking station state such as the current tx antenna. * We also setup a mapping table between key cache slot indices @@ -510,6 +529,7 @@ struct ath_rx_edma { int m_fifo_tail; int m_fifo_depth; struct mbuf *m_rxpending; + struct ath_buf *m_holdbf; }; struct ath_tx_edma_fifo { @@ -565,6 +585,8 @@ struct ath_softc { int sc_tx_statuslen; int sc_tx_nmaps; /* Number of TX maps */ int sc_edma_bufsize; + int sc_rx_stopped; /* XXX only for EDMA */ + int sc_rx_resetted; /* XXX only for EDMA */ void (*sc_node_cleanup)(struct ieee80211_node *); void (*sc_node_free)(struct ieee80211_node *); @@ -621,7 +643,8 @@ struct ath_softc { sc_resetcal : 1,/* reset cal state next trip */ sc_rxslink : 1,/* do self-linked final descriptor */ sc_rxtsf32 : 1,/* RX dec TSF is 32 bits */ - sc_isedma : 1;/* supports EDMA */ + sc_isedma : 1,/* supports EDMA */ + sc_do_mybeacon : 1; /* supports mybeacon */ /* * Second set of flags. @@ -864,6 +887,25 @@ struct ath_softc { void (*sc_bar_response)(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); + + /* + * Powersave state tracking. + * + * target/cur powerstate is the chip power state. + * target selfgen state is the self-generated frames + * state. The chip can be awake but transmitted frames + * can have the PWRMGT bit set to 1 so the destination + * thinks the node is asleep. + */ + HAL_POWER_MODE sc_target_powerstate; + HAL_POWER_MODE sc_target_selfgen_state; + + HAL_POWER_MODE sc_cur_powerstate; + + int sc_powersave_refcnt; + + /* ATH_PCI_* flags */ + uint32_t sc_pci_devinfo; }; #define ATH_LOCK_INIT(_sc) \ @@ -1038,6 +1080,8 @@ void ath_intr(void *); ((*(_ah)->ah_updateTxTrigLevel)((_ah), (_inc))) #define ath_hal_setpower(_ah, _mode) \ ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_TRUE)) +#define ath_hal_setselfgenpower(_ah, _mode) \ + ((*(_ah)->ah_setPowerMode)((_ah), (_mode), AH_FALSE)) #define ath_hal_keycachesize(_ah) \ ((*(_ah)->ah_getKeyCacheSize)((_ah))) #define ath_hal_keyreset(_ah, _ix) \ @@ -1266,6 +1310,8 @@ void ath_intr(void *); #define ath_hal_setintmit(_ah, _v) \ ath_hal_setcapability(_ah, HAL_CAP_INTMIT, \ HAL_CAP_INTMIT_ENABLE, _v, NULL) +#define ath_hal_hasmybeacon(_ah) \ + (ath_hal_getcapability(_ah, HAL_CAP_DO_MYBEACON, 1, NULL) == HAL_OK) #define ath_hal_hasenforcetxop(_ah) \ (ath_hal_getcapability(_ah, HAL_CAP_ENFORCE_TXOP, 0, NULL) == HAL_OK) diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c index e6ee051..b6de4b6 100644 --- a/sys/dev/iwi/if_iwi.c +++ b/sys/dev/iwi/if_iwi.c @@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -83,6 +84,7 @@ __FBSDID("$FreeBSD$"); #include <dev/iwi/if_iwireg.h> #include <dev/iwi/if_iwivar.h> +#include <dev/iwi/if_iwi_ioctl.h> #define IWI_DEBUG #ifdef IWI_DEBUG @@ -936,10 +938,13 @@ iwi_media_status(struct ifnet *ifp, struct ifmediareq *imr) struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct iwi_softc *sc = ic->ic_ifp->if_softc; + struct ieee80211_node *ni; /* read current transmission rate from adapter */ - vap->iv_bss->ni_txrate = + ni = ieee80211_ref_node(vap->iv_bss); + ni->ni_txrate = iwi_cvtrate(CSR_READ_4(sc, IWI_CSR_CURRENT_TX_RATE)); + ieee80211_free_node(ni); ieee80211_media_status(ifp, imr); } @@ -1230,7 +1235,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, */ mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } @@ -1251,7 +1256,7 @@ iwi_frame_intr(struct iwi_softc *sc, struct iwi_rx_data *data, int i, panic("%s: could not load old rx mbuf", device_get_name(sc->sc_dev)); } - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } @@ -1358,16 +1363,44 @@ iwi_checkforqos(struct ieee80211vap *vap, frm += frm[1] + 2; } - ni = vap->iv_bss; + ni = ieee80211_ref_node(vap->iv_bss); ni->ni_capinfo = capinfo; ni->ni_associd = associd & 0x3fff; if (wme != NULL) ni->ni_flags |= IEEE80211_NODE_QOS; else ni->ni_flags &= ~IEEE80211_NODE_QOS; + ieee80211_free_node(ni); #undef SUBTYPE } +static void +iwi_notif_link_quality(struct iwi_softc *sc, struct iwi_notif *notif) +{ + struct iwi_notif_link_quality *lq; + int len; + + len = le16toh(notif->len); + + DPRINTFN(5, ("Notification (%u) - len=%d, sizeof=%zu\n", + notif->type, + len, + sizeof(struct iwi_notif_link_quality) + )); + + /* enforce length */ + if (len != sizeof(struct iwi_notif_link_quality)) { + DPRINTFN(5, ("Notification: (%u) too short (%d)\n", + notif->type, + len)); + return; + } + + lq = (struct iwi_notif_link_quality *)(notif + 1); + memcpy(&sc->sc_linkqual, lq, sizeof(sc->sc_linkqual)); + sc->sc_linkqual_valid = 1; +} + /* * Task queue callbacks for iwi_notification_intr used to avoid LOR's. */ @@ -1537,9 +1570,12 @@ iwi_notification_intr(struct iwi_softc *sc, struct iwi_notif *notif) case IWI_NOTIF_TYPE_CALIBRATION: case IWI_NOTIF_TYPE_NOISE: - case IWI_NOTIF_TYPE_LINK_QUALITY: + /* XXX handle? */ DPRINTFN(5, ("Notification (%u)\n", notif->type)); break; + case IWI_NOTIF_TYPE_LINK_QUALITY: + iwi_notif_link_quality(sc, notif); + break; default: DPRINTF(("unknown notification type %u flags 0x%x len %u\n", @@ -1615,7 +1651,7 @@ iwi_tx_intr(struct iwi_softc *sc, struct iwi_tx_ring *txq) DPRINTFN(15, ("tx done idx=%u\n", txq->next)); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); txq->queued--; txq->next = (txq->next + 1) % IWI_TX_RING_COUNT; @@ -1816,7 +1852,7 @@ iwi_tx_start(struct ifnet *ifp, struct mbuf *m0, struct ieee80211_node *ni, /* h/w table is full */ m_freem(m0); ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return 0; } iwi_write_ibssnode(sc, @@ -1971,7 +2007,7 @@ iwi_start_locked(struct ifnet *ifp) ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (iwi_tx_start(ifp, m, ni, ac) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; } @@ -2002,7 +2038,7 @@ iwi_watchdog(void *arg) if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_runtask(ic, &sc->sc_restarttask); } } @@ -2058,11 +2094,25 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCGIFADDR: error = ether_ioctl(ifp, cmd, data); break; + case SIOCGIWISTATS: + IWI_LOCK(sc); + /* XXX validate permissions/memory/etc? */ + error = copyout(&sc->sc_linkqual, ifr->ifr_data, + sizeof(struct iwi_notif_link_quality)); + IWI_UNLOCK(sc); + break; + case SIOCZIWISTATS: + IWI_LOCK(sc); + memset(&sc->sc_linkqual, 0, + sizeof(struct iwi_notif_link_quality)); + IWI_UNLOCK(sc); + error = 0; + break; default: error = EINVAL; break; } - return error; + return error; } static void @@ -2803,7 +2853,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; - struct ieee80211_node *ni = vap->iv_bss; + struct ieee80211_node *ni; struct iwi_configuration config; struct iwi_associate *assoc = &sc->assoc; struct iwi_rateset rs; @@ -2813,6 +2863,8 @@ iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) IWI_LOCK_ASSERT(sc); + ni = ieee80211_ref_node(vap->iv_bss); + if (sc->flags & IWI_FLAG_ASSOCIATED) { DPRINTF(("Already associated\n")); return (-1); @@ -2971,6 +3023,7 @@ iwi_auth_and_assoc(struct iwi_softc *sc, struct ieee80211vap *vap) le16toh(assoc->intval))); error = iwi_cmd(sc, IWI_CMD_ASSOCIATE, assoc, sizeof *assoc); done: + ieee80211_free_node(ni); if (error) IWI_STATE_END(sc, IWI_FW_ASSOCIATING); diff --git a/sys/dev/iwi/if_iwi_ioctl.h b/sys/dev/iwi/if_iwi_ioctl.h new file mode 100644 index 0000000..0f4f447 --- /dev/null +++ b/sys/dev/iwi/if_iwi_ioctl.h @@ -0,0 +1,25 @@ +/*- + * Copyright (c) 2014 Adrian Chadd <adrian@FreeBSD.org> + * + * 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. + * + * $FreeBSD$ + */ +#ifndef __IF_IWI_IOCTL_H__ +#define __IF_IWI_IOCTL_H__ + +/* XXX how should I pick appropriate ioctl numbers? */ +#define SIOCGIWISTATS _IOWR('i', 147, struct ifreq) +#define SIOCZIWISTATS _IOWR('i', 148, struct ifreq) + +#endif /* __IF_IWI_IOCTL_H__ */ diff --git a/sys/dev/iwi/if_iwireg.h b/sys/dev/iwi/if_iwireg.h index fb56fa5..bc05ad2 100644 --- a/sys/dev/iwi/if_iwireg.h +++ b/sys/dev/iwi/if_iwireg.h @@ -221,6 +221,7 @@ struct iwi_notif_association { /* structure for notification IWI_NOTIF_TYPE_SCAN_CHANNEL */ struct iwi_notif_scan_channel { uint8_t nchan; + /* XXX this is iwi_cmd_stats, and a u8 reserved field */ uint8_t reserved[47]; } __packed; @@ -239,6 +240,68 @@ struct iwi_notif_beacon_state { uint32_t number; } __packed; +/* structure(s) for notification IWI_NOTIF_TYPE_LINK_QUALITY */ + +#define RX_FREE_BUFFERS 32 +#define RX_LOW_WATERMARK 8 + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +// Used for passing to driver number of successes and failures per rate +struct iwi_rate_histogram { + union { + uint32_t a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + uint32_t b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + uint32_t g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + uint32_t a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + uint32_t b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + uint32_t g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __packed; + +/* statistics command response */ +struct iwi_cmd_stats { + uint8_t cmd_id; + uint8_t seq_num; + uint16_t good_sfd; + uint16_t bad_plcp; + uint16_t wrong_bssid; + uint16_t valid_mpdu; + uint16_t bad_mac_header; + uint16_t reserved_frame_types; + uint16_t rx_ina; + uint16_t bad_crc32; + uint16_t invalid_cts; + uint16_t invalid_acks; + uint16_t long_distance_ina_fina; + uint16_t dsp_silence_unreachable; + uint16_t accumulated_rssi; + uint16_t rx_ovfl_frame_tossed; + uint16_t rssi_silence_threshold; + uint16_t rx_ovfl_frame_supplied; + uint16_t last_rx_frame_signal; + uint16_t last_rx_frame_noise; + uint16_t rx_autodetec_no_ofdm; + uint16_t rx_autodetec_no_barker; + uint16_t reserved; +} __packed; + +#define SILENCE_OVER_THRESH (1) +#define SILENCE_UNDER_THRESH (2) + +struct iwi_notif_link_quality { + struct iwi_cmd_stats stats; + uint8_t rate; + uint8_t modulation; + struct iwi_rate_histogram histogram; + uint8_t silence_notification_type; /* SILENCE_OVER/UNDER_THRESH */ + uint16_t silence_count; +} __packed; + /* received frame header */ struct iwi_frame { uint32_t reserved1[2]; diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h index 42ca92e..b38bbd9 100644 --- a/sys/dev/iwi/if_iwivar.h +++ b/sys/dev/iwi/if_iwivar.h @@ -215,6 +215,9 @@ struct iwi_softc { struct iwi_rx_radiotap_header sc_rxtap; struct iwi_tx_radiotap_header sc_txtap; + + struct iwi_notif_link_quality sc_linkqual; + int sc_linkqual_valid; }; #define IWI_STATE_BEGIN(_sc, _state) do { \ diff --git a/sys/dev/iwn/if_iwn.c b/sys/dev/iwn/if_iwn.c index 806a408..ab98204 100644 --- a/sys/dev/iwn/if_iwn.c +++ b/sys/dev/iwn/if_iwn.c @@ -1,11 +1,10 @@ /*- - * Copyright (c) 2013 Cedric GROSS <c.gross@kreiz-it.fr> - * Copyright (c) 2011 Intel Corporation - * Copyright (c) 2007-2009 - * Damien Bergamini <damien.bergamini@free.fr> - * Copyright (c) 2008 - * Benjamin Close <benjsc@FreeBSD.org> + * Copyright (c) 2007-2009 Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2008 Benjamin Close <benjsc@FreeBSD.org> * Copyright (c) 2008 Sam Leffler, Errno Consulting + * Copyright (c) 2011 Intel Corporation + * Copyright (c) 2013 Cedric GROSS <c.gross@kreiz-it.fr> + * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -57,6 +56,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -77,6 +77,9 @@ __FBSDID("$FreeBSD$"); #include <dev/iwn/if_iwnreg.h> #include <dev/iwn/if_iwnvar.h> #include <dev/iwn/if_iwn_devid.h> +#include <dev/iwn/if_iwn_chip_cfg.h> +#include <dev/iwn/if_iwn_debug.h> +#include <dev/iwn/if_iwn_ioctl.h> struct iwn_ident { uint16_t vendor; @@ -97,12 +100,19 @@ static const struct iwn_ident iwn_ident_table[] = { { 0x8086, IWN_DID_x030_4, "Intel Centrino Advanced-N 6230" }, { 0x8086, IWN_DID_6150_1, "Intel Centrino Wireless-N + WiMAX 6150" }, { 0x8086, IWN_DID_6150_2, "Intel Centrino Wireless-N + WiMAX 6150" }, + { 0x8086, IWN_DID_2x00_1, "Intel(R) Centrino(R) Wireless-N 2200 BGN" }, + { 0x8086, IWN_DID_2x00_2, "Intel(R) Centrino(R) Wireless-N 2200 BGN" }, + /* XXX 2200D is IWN_SDID_2x00_4; there's no way to express this here! */ { 0x8086, IWN_DID_2x30_1, "Intel Centrino Wireless-N 2230" }, { 0x8086, IWN_DID_2x30_2, "Intel Centrino Wireless-N 2230" }, { 0x8086, IWN_DID_130_1, "Intel Centrino Wireless-N 130" }, { 0x8086, IWN_DID_130_2, "Intel Centrino Wireless-N 130" }, { 0x8086, IWN_DID_100_1, "Intel Centrino Wireless-N 100" }, { 0x8086, IWN_DID_100_2, "Intel Centrino Wireless-N 100" }, + { 0x8086, IWN_DID_105_1, "Intel Centrino Wireless-N 105" }, + { 0x8086, IWN_DID_105_2, "Intel Centrino Wireless-N 105" }, + { 0x8086, IWN_DID_135_1, "Intel Centrino Wireless-N 135" }, + { 0x8086, IWN_DID_135_2, "Intel Centrino Wireless-N 135" }, { 0x8086, IWN_DID_4965_1, "Intel Wireless WiFi Link 4965" }, { 0x8086, IWN_DID_6x00_1, "Intel Centrino Ultimate-N 6300" }, { 0x8086, IWN_DID_6x00_2, "Intel Centrino Advanced-N 6200" }, @@ -119,6 +129,8 @@ static const struct iwn_ident iwn_ident_table[] = { { 0x8086, IWN_DID_5x50_2, "Intel WiMAX/WiFi Link 5350" }, { 0x8086, IWN_DID_5x50_3, "Intel WiMAX/WiFi Link 5150" }, { 0x8086, IWN_DID_5x50_4, "Intel WiMAX/WiFi Link 5150" }, + { 0x8086, IWN_DID_6035_1, "Intel Centrino Advanced 6235" }, + { 0x8086, IWN_DID_6035_2, "Intel Centrino Advanced 6235" }, { 0, 0, NULL } }; @@ -126,6 +138,7 @@ static int iwn_probe(device_t); static int iwn_attach(device_t); static int iwn4965_attach(struct iwn_softc *, uint16_t); static int iwn5000_attach(struct iwn_softc *, uint16_t); +static int iwn_config_specific(struct iwn_softc *, uint16_t); static void iwn_radiotap_attach(struct iwn_softc *); static void iwn_sysctlattach(struct iwn_softc *); static struct ieee80211vap *iwn_vap_create(struct ieee80211com *, @@ -200,7 +213,7 @@ static void iwn5000_tx_done(struct iwn_softc *, struct iwn_rx_desc *, struct iwn_rx_data *); static void iwn_tx_done(struct iwn_softc *, struct iwn_rx_desc *, int, uint8_t); -static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, void *); +static void iwn_ampdu_tx_done(struct iwn_softc *, int, int, int, int, void *); static void iwn_cmd_done(struct iwn_softc *, struct iwn_rx_desc *); static void iwn_notif_intr(struct iwn_softc *); static void iwn_wakeup_intr(struct iwn_softc *); @@ -257,14 +270,17 @@ static int iwn4965_set_gains(struct iwn_softc *); static int iwn5000_set_gains(struct iwn_softc *); static void iwn_tune_sensitivity(struct iwn_softc *, const struct iwn_rx_stats *); +static void iwn_save_stats_counters(struct iwn_softc *, + const struct iwn_stats *); static int iwn_send_sensitivity(struct iwn_softc *); +static void iwn_check_rx_recovery(struct iwn_softc *, struct iwn_stats *); static int iwn_set_pslevel(struct iwn_softc *, int, int, int); static int iwn_send_btcoex(struct iwn_softc *); static int iwn_send_advanced_btcoex(struct iwn_softc *); static int iwn5000_runtime_calib(struct iwn_softc *); static int iwn_config(struct iwn_softc *); -static uint8_t *ieee80211_add_ssid(uint8_t *, const uint8_t *, u_int); -static int iwn_scan(struct iwn_softc *); +static int iwn_scan(struct iwn_softc *, struct ieee80211vap *, + struct ieee80211_scan_state *, struct ieee80211_channel *); static int iwn_auth(struct iwn_softc *, struct ieee80211vap *vap); static int iwn_run(struct iwn_softc *, struct ieee80211vap *vap); static int iwn_ampdu_rx_start(struct ieee80211_node *, @@ -292,6 +308,7 @@ static int iwn5000_send_calibration(struct iwn_softc *); static int iwn5000_send_wimax_coex(struct iwn_softc *); static int iwn5000_crystal_calib(struct iwn_softc *); static int iwn5000_temp_offset_calib(struct iwn_softc *); +static int iwn5000_temp_offset_calibv2(struct iwn_softc *); static int iwn4965_post_alive(struct iwn_softc *); static int iwn5000_post_alive(struct iwn_softc *); static int iwn4965_load_bootcode(struct iwn_softc *, const uint8_t *, @@ -316,6 +333,7 @@ static int iwn_hw_init(struct iwn_softc *); static void iwn_hw_stop(struct iwn_softc *); static void iwn_radio_on(void *, int); static void iwn_radio_off(void *, int); +static void iwn_panicked(void *, int); static void iwn_init_locked(struct iwn_softc *); static void iwn_init(void *); static void iwn_stop_locked(struct iwn_softc *); @@ -331,80 +349,6 @@ static char *iwn_get_csr_string(int); static void iwn_debug_register(struct iwn_softc *); #endif -#ifdef IWN_DEBUG -enum { - IWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ - IWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ - IWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ - IWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ - IWN_DEBUG_RESET = 0x00000010, /* reset processing */ - IWN_DEBUG_OPS = 0x00000020, /* iwn_ops processing */ - IWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ - IWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ - IWN_DEBUG_INTR = 0x00000100, /* ISR */ - IWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ - IWN_DEBUG_NODE = 0x00000400, /* node management */ - IWN_DEBUG_LED = 0x00000800, /* led management */ - IWN_DEBUG_CMD = 0x00001000, /* cmd submission */ - IWN_DEBUG_TXRATE = 0x00002000, /* TX rate debugging */ - IWN_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */ - IWN_DEBUG_REGISTER = 0x20000000, /* print chipset register */ - IWN_DEBUG_TRACE = 0x40000000, /* Print begin and start driver function */ - IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ - IWN_DEBUG_ANY = 0xffffffff -}; - -#define DPRINTF(sc, m, fmt, ...) do { \ - if (sc->sc_debug & (m)) \ - printf(fmt, __VA_ARGS__); \ -} while (0) - -static const char * -iwn_intr_str(uint8_t cmd) -{ - switch (cmd) { - /* Notifications */ - case IWN_UC_READY: return "UC_READY"; - case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; - case IWN_TX_DONE: return "TX_DONE"; - case IWN_START_SCAN: return "START_SCAN"; - case IWN_STOP_SCAN: return "STOP_SCAN"; - case IWN_RX_STATISTICS: return "RX_STATS"; - case IWN_BEACON_STATISTICS: return "BEACON_STATS"; - case IWN_STATE_CHANGED: return "STATE_CHANGED"; - case IWN_BEACON_MISSED: return "BEACON_MISSED"; - case IWN_RX_PHY: return "RX_PHY"; - case IWN_MPDU_RX_DONE: return "MPDU_RX_DONE"; - case IWN_RX_DONE: return "RX_DONE"; - - /* Command Notifications */ - case IWN_CMD_RXON: return "IWN_CMD_RXON"; - case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; - case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; - case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; - case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; - case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; - case IWN5000_CMD_WIMAX_COEX: return "IWN5000_CMD_WIMAX_COEX"; - case IWN5000_CMD_CALIB_CONFIG: return "IWN5000_CMD_CALIB_CONFIG"; - case IWN5000_CMD_CALIB_RESULT: return "IWN5000_CMD_CALIB_RESULT"; - case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE"; - case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; - case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; - case IWN_CMD_SCAN_RESULTS: return "IWN_CMD_SCAN_RESULTS"; - case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; - case IWN_CMD_TXPOWER_DBM: return "IWN_CMD_TXPOWER_DBM"; - case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG"; - case IWN_CMD_BT_COEX: return "IWN_CMD_BT_COEX"; - case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; - case IWN_CMD_SET_SENSITIVITY: return "IWN_CMD_SET_SENSITIVITY"; - case IWN_CMD_PHY_CALIB: return "IWN_CMD_PHY_CALIB"; - } - return "UNKNOWN INTR NOTIF/CMD"; -} -#else -#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) -#endif - static device_method_t iwn_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iwn_probe), @@ -448,6 +392,15 @@ iwn_probe(device_t dev) } static int +iwn_is_3stream_device(struct iwn_softc *sc) +{ + /* XXX for now only 5300, until the 5350 can be tested */ + if (sc->hw_type == IWN_HW_REV_TYPE_5300) + return (1); + return (0); +} + +static int iwn_attach(device_t dev) { struct iwn_softc *sc = (struct iwn_softc *)device_get_softc(dev); @@ -515,6 +468,11 @@ iwn_attach(device_t dev) sc->hw_type = (IWN_READ(sc, IWN_HW_REV) >> IWN_HW_REV_TYPE_SHIFT) & IWN_HW_REV_TYPE_MASK; sc->subdevice_id = pci_get_subdevice(dev); + + /* + * 4965 versus 5000 and later have different methods. + * Let's set those up first. + */ if (sc->hw_type == IWN_HW_REV_TYPE_4965) error = iwn4965_attach(sc, pci_get_device(dev)); else @@ -525,6 +483,16 @@ iwn_attach(device_t dev) goto fail; } + /* + * Next, let's setup the various parameters of each NIC. + */ + error = iwn_config_specific(sc, pci_get_device(dev)); + if (error != 0) { + device_printf(dev, "could not attach device, error %d\n", + error); + goto fail; + } + if ((error = iwn_hw_prepare(sc)) != 0) { device_printf(dev, "hardware not ready, error %d\n", error); goto fail; @@ -634,17 +602,16 @@ iwn_attach(device_t dev) ic->ic_txstream = sc->ntxchains; /* - * The NICs we currently support cap out at 2x2 support - * separate from the chains being used. - * - * This is a total hack to work around that until some - * per-device method is implemented to return the - * actual stream support. + * Some of the 3 antenna devices (ie, the 4965) only supports + * 2x2 operation. So correct the number of streams if + * it's not a 3-stream device. */ - if (ic->ic_rxstream > 2) - ic->ic_rxstream = 2; - if (ic->ic_txstream > 2) - ic->ic_txstream = 2; + if (! iwn_is_3stream_device(sc)) { + if (ic->ic_rxstream > 2) + ic->ic_rxstream = 2; + if (ic->ic_txstream > 2) + ic->ic_txstream = 2; + } ic->ic_htcaps = IEEE80211_HTCAP_SMPS_OFF /* SMPS mode disabled */ @@ -710,6 +677,15 @@ iwn_attach(device_t dev) TASK_INIT(&sc->sc_reinit_task, 0, iwn_hw_reset, sc); TASK_INIT(&sc->sc_radioon_task, 0, iwn_radio_on, sc); TASK_INIT(&sc->sc_radiooff_task, 0, iwn_radio_off, sc); + TASK_INIT(&sc->sc_panic_task, 0, iwn_panicked, sc); + + sc->sc_tq = taskqueue_create("iwn_taskq", M_WAITOK, + taskqueue_thread_enqueue, &sc->sc_tq); + error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwn_taskq"); + if (error != 0) { + device_printf(dev, "can't start threads, error %d\n", error); + goto fail; + } iwn_sysctlattach(sc); @@ -724,6 +700,13 @@ iwn_attach(device_t dev) goto fail; } +#if 0 + device_printf(sc->sc_dev, "%s: rx_stats=%d, rx_stats_bt=%d\n", + __func__, + sizeof(struct iwn_stats), + sizeof(struct iwn_stats_bt)); +#endif + if (bootverbose) ieee80211_announce(ic); DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); @@ -734,6 +717,488 @@ fail: return error; } +/* + * Define specific configuration based on device id and subdevice id + * pid : PCI device id + */ +static int +iwn_config_specific(struct iwn_softc *sc, uint16_t pid) +{ + + switch (pid) { +/* 4965 series */ + case IWN_DID_4965_1: + case IWN_DID_4965_2: + case IWN_DID_4965_3: + case IWN_DID_4965_4: + sc->base_params = &iwn4965_base_params; + sc->limits = &iwn4965_sensitivity_limits; + sc->fwname = "iwn4965fw"; + /* Override chains masks, ROM is known to be broken. */ + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_ABC; + /* Enable normal btcoex */ + sc->sc_flags |= IWN_FLAG_BTCOEX; + break; +/* 1000 Series */ + case IWN_DID_1000_1: + case IWN_DID_1000_2: + switch(sc->subdevice_id) { + case IWN_SDID_1000_1: + case IWN_SDID_1000_2: + case IWN_SDID_1000_3: + case IWN_SDID_1000_4: + case IWN_SDID_1000_5: + case IWN_SDID_1000_6: + case IWN_SDID_1000_7: + case IWN_SDID_1000_8: + case IWN_SDID_1000_9: + case IWN_SDID_1000_10: + case IWN_SDID_1000_11: + case IWN_SDID_1000_12: + sc->limits = &iwn1000_sensitivity_limits; + sc->base_params = &iwn1000_base_params; + sc->fwname = "iwn1000fw"; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 6x00 Series */ + case IWN_DID_6x00_2: + case IWN_DID_6x00_4: + case IWN_DID_6x00_1: + case IWN_DID_6x00_3: + sc->fwname = "iwn6000fw"; + sc->limits = &iwn6000_sensitivity_limits; + switch(sc->subdevice_id) { + case IWN_SDID_6x00_1: + case IWN_SDID_6x00_2: + case IWN_SDID_6x00_8: + //iwl6000_3agn_cfg + sc->base_params = &iwn_6000_base_params; + break; + case IWN_SDID_6x00_3: + case IWN_SDID_6x00_6: + case IWN_SDID_6x00_9: + ////iwl6000i_2agn + case IWN_SDID_6x00_4: + case IWN_SDID_6x00_7: + case IWN_SDID_6x00_10: + //iwl6000i_2abg_cfg + case IWN_SDID_6x00_5: + //iwl6000i_2bg_cfg + sc->base_params = &iwn_6000i_base_params; + sc->sc_flags |= IWN_FLAG_INTERNAL_PA; + sc->txchainmask = IWN_ANT_BC; + sc->rxchainmask = IWN_ANT_BC; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 6x05 Series */ + case IWN_DID_6x05_1: + case IWN_DID_6x05_2: + switch(sc->subdevice_id) { + case IWN_SDID_6x05_1: + case IWN_SDID_6x05_4: + case IWN_SDID_6x05_6: + //iwl6005_2agn_cfg + case IWN_SDID_6x05_2: + case IWN_SDID_6x05_5: + case IWN_SDID_6x05_7: + //iwl6005_2abg_cfg + case IWN_SDID_6x05_3: + //iwl6005_2bg_cfg + case IWN_SDID_6x05_8: + case IWN_SDID_6x05_9: + //iwl6005_2agn_sff_cfg + case IWN_SDID_6x05_10: + //iwl6005_2agn_d_cfg + case IWN_SDID_6x05_11: + //iwl6005_2agn_mow1_cfg + case IWN_SDID_6x05_12: + //iwl6005_2agn_mow2_cfg + sc->fwname = "iwn6000g2afw"; + sc->limits = &iwn6000_sensitivity_limits; + sc->base_params = &iwn_6000g2_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 6x35 Series */ + case IWN_DID_6035_1: + case IWN_DID_6035_2: + switch(sc->subdevice_id) { + case IWN_SDID_6035_1: + case IWN_SDID_6035_2: + case IWN_SDID_6035_3: + case IWN_SDID_6035_4: + sc->fwname = "iwn6000g2bfw"; + sc->limits = &iwn6235_sensitivity_limits; + sc->base_params = &iwn_6235_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 6x50 WiFi/WiMax Series */ + case IWN_DID_6050_1: + case IWN_DID_6050_2: + switch(sc->subdevice_id) { + case IWN_SDID_6050_1: + case IWN_SDID_6050_3: + case IWN_SDID_6050_5: + //iwl6050_2agn_cfg + case IWN_SDID_6050_2: + case IWN_SDID_6050_4: + case IWN_SDID_6050_6: + //iwl6050_2abg_cfg + sc->fwname = "iwn6050fw"; + sc->txchainmask = IWN_ANT_AB; + sc->rxchainmask = IWN_ANT_AB; + sc->limits = &iwn6000_sensitivity_limits; + sc->base_params = &iwn_6050_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 6150 WiFi/WiMax Series */ + case IWN_DID_6150_1: + case IWN_DID_6150_2: + switch(sc->subdevice_id) { + case IWN_SDID_6150_1: + case IWN_SDID_6150_3: + case IWN_SDID_6150_5: + // iwl6150_bgn_cfg + case IWN_SDID_6150_2: + case IWN_SDID_6150_4: + case IWN_SDID_6150_6: + //iwl6150_bg_cfg + sc->fwname = "iwn6050fw"; + sc->limits = &iwn6000_sensitivity_limits; + sc->base_params = &iwn_6150_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 6030 Series and 1030 Series */ + case IWN_DID_x030_1: + case IWN_DID_x030_2: + case IWN_DID_x030_3: + case IWN_DID_x030_4: + switch(sc->subdevice_id) { + case IWN_SDID_x030_1: + case IWN_SDID_x030_3: + case IWN_SDID_x030_5: + // iwl1030_bgn_cfg + case IWN_SDID_x030_2: + case IWN_SDID_x030_4: + case IWN_SDID_x030_6: + //iwl1030_bg_cfg + case IWN_SDID_x030_7: + case IWN_SDID_x030_10: + case IWN_SDID_x030_14: + //iwl6030_2agn_cfg + case IWN_SDID_x030_8: + case IWN_SDID_x030_11: + case IWN_SDID_x030_15: + // iwl6030_2bgn_cfg + case IWN_SDID_x030_9: + case IWN_SDID_x030_12: + case IWN_SDID_x030_16: + // iwl6030_2abg_cfg + case IWN_SDID_x030_13: + //iwl6030_2bg_cfg + sc->fwname = "iwn6000g2bfw"; + sc->limits = &iwn6000_sensitivity_limits; + sc->base_params = &iwn_6000g2b_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 130 Series WiFi */ +/* XXX: This series will need adjustment for rate. + * see rx_with_siso_diversity in linux kernel + */ + case IWN_DID_130_1: + case IWN_DID_130_2: + switch(sc->subdevice_id) { + case IWN_SDID_130_1: + case IWN_SDID_130_3: + case IWN_SDID_130_5: + //iwl130_bgn_cfg + case IWN_SDID_130_2: + case IWN_SDID_130_4: + case IWN_SDID_130_6: + //iwl130_bg_cfg + sc->fwname = "iwn6000g2bfw"; + sc->limits = &iwn6000_sensitivity_limits; + sc->base_params = &iwn_6000g2b_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 100 Series WiFi */ + case IWN_DID_100_1: + case IWN_DID_100_2: + switch(sc->subdevice_id) { + case IWN_SDID_100_1: + case IWN_SDID_100_2: + case IWN_SDID_100_3: + case IWN_SDID_100_4: + case IWN_SDID_100_5: + case IWN_SDID_100_6: + sc->limits = &iwn1000_sensitivity_limits; + sc->base_params = &iwn1000_base_params; + sc->fwname = "iwn100fw"; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; + +/* 105 Series */ +/* XXX: This series will need adjustment for rate. + * see rx_with_siso_diversity in linux kernel + */ + case IWN_DID_105_1: + case IWN_DID_105_2: + switch(sc->subdevice_id) { + case IWN_SDID_105_1: + case IWN_SDID_105_2: + case IWN_SDID_105_3: + //iwl105_bgn_cfg + case IWN_SDID_105_4: + //iwl105_bgn_d_cfg + sc->limits = &iwn2030_sensitivity_limits; + sc->base_params = &iwn2000_base_params; + sc->fwname = "iwn105fw"; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; + +/* 135 Series */ +/* XXX: This series will need adjustment for rate. + * see rx_with_siso_diversity in linux kernel + */ + case IWN_DID_135_1: + case IWN_DID_135_2: + switch(sc->subdevice_id) { + case IWN_SDID_135_1: + case IWN_SDID_135_2: + case IWN_SDID_135_3: + sc->limits = &iwn2030_sensitivity_limits; + sc->base_params = &iwn2030_base_params; + sc->fwname = "iwn135fw"; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; + +/* 2x00 Series */ + case IWN_DID_2x00_1: + case IWN_DID_2x00_2: + switch(sc->subdevice_id) { + case IWN_SDID_2x00_1: + case IWN_SDID_2x00_2: + case IWN_SDID_2x00_3: + //iwl2000_2bgn_cfg + case IWN_SDID_2x00_4: + //iwl2000_2bgn_d_cfg + sc->limits = &iwn2030_sensitivity_limits; + sc->base_params = &iwn2000_base_params; + sc->fwname = "iwn2000fw"; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice) \n", + pid, sc->subdevice_id, sc->hw_type); + return ENOTSUP; + } + break; +/* 2x30 Series */ + case IWN_DID_2x30_1: + case IWN_DID_2x30_2: + switch(sc->subdevice_id) { + case IWN_SDID_2x30_1: + case IWN_SDID_2x30_3: + case IWN_SDID_2x30_5: + //iwl100_bgn_cfg + case IWN_SDID_2x30_2: + case IWN_SDID_2x30_4: + case IWN_SDID_2x30_6: + //iwl100_bg_cfg + sc->limits = &iwn2030_sensitivity_limits; + sc->base_params = &iwn2030_base_params; + sc->fwname = "iwn2030fw"; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 5x00 Series */ + case IWN_DID_5x00_1: + case IWN_DID_5x00_2: + case IWN_DID_5x00_3: + case IWN_DID_5x00_4: + sc->limits = &iwn5000_sensitivity_limits; + sc->base_params = &iwn5000_base_params; + sc->fwname = "iwn5000fw"; + switch(sc->subdevice_id) { + case IWN_SDID_5x00_1: + case IWN_SDID_5x00_2: + case IWN_SDID_5x00_3: + case IWN_SDID_5x00_4: + case IWN_SDID_5x00_9: + case IWN_SDID_5x00_10: + case IWN_SDID_5x00_11: + case IWN_SDID_5x00_12: + case IWN_SDID_5x00_17: + case IWN_SDID_5x00_18: + case IWN_SDID_5x00_19: + case IWN_SDID_5x00_20: + //iwl5100_agn_cfg + sc->txchainmask = IWN_ANT_B; + sc->rxchainmask = IWN_ANT_AB; + break; + case IWN_SDID_5x00_5: + case IWN_SDID_5x00_6: + case IWN_SDID_5x00_13: + case IWN_SDID_5x00_14: + case IWN_SDID_5x00_21: + case IWN_SDID_5x00_22: + //iwl5100_bgn_cfg + sc->txchainmask = IWN_ANT_B; + sc->rxchainmask = IWN_ANT_AB; + break; + case IWN_SDID_5x00_7: + case IWN_SDID_5x00_8: + case IWN_SDID_5x00_15: + case IWN_SDID_5x00_16: + case IWN_SDID_5x00_23: + case IWN_SDID_5x00_24: + //iwl5100_abg_cfg + sc->txchainmask = IWN_ANT_B; + sc->rxchainmask = IWN_ANT_AB; + break; + case IWN_SDID_5x00_25: + case IWN_SDID_5x00_26: + case IWN_SDID_5x00_27: + case IWN_SDID_5x00_28: + case IWN_SDID_5x00_29: + case IWN_SDID_5x00_30: + case IWN_SDID_5x00_31: + case IWN_SDID_5x00_32: + case IWN_SDID_5x00_33: + case IWN_SDID_5x00_34: + case IWN_SDID_5x00_35: + case IWN_SDID_5x00_36: + //iwl5300_agn_cfg + sc->txchainmask = IWN_ANT_ABC; + sc->rxchainmask = IWN_ANT_ABC; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; +/* 5x50 Series */ + case IWN_DID_5x50_1: + case IWN_DID_5x50_2: + case IWN_DID_5x50_3: + case IWN_DID_5x50_4: + sc->limits = &iwn5000_sensitivity_limits; + sc->base_params = &iwn5000_base_params; + sc->fwname = "iwn5000fw"; + switch(sc->subdevice_id) { + case IWN_SDID_5x50_1: + case IWN_SDID_5x50_2: + case IWN_SDID_5x50_3: + //iwl5350_agn_cfg + sc->limits = &iwn5000_sensitivity_limits; + sc->base_params = &iwn5000_base_params; + sc->fwname = "iwn5000fw"; + break; + case IWN_SDID_5x50_4: + case IWN_SDID_5x50_5: + case IWN_SDID_5x50_8: + case IWN_SDID_5x50_9: + case IWN_SDID_5x50_10: + case IWN_SDID_5x50_11: + //iwl5150_agn_cfg + case IWN_SDID_5x50_6: + case IWN_SDID_5x50_7: + case IWN_SDID_5x50_12: + case IWN_SDID_5x50_13: + //iwl5150_abg_cfg + sc->limits = &iwn5000_sensitivity_limits; + sc->fwname = "iwn5150fw"; + sc->base_params = &iwn_5x50_base_params; + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id :" + "0x%04x rev %d not supported (subdevice)\n", pid, + sc->subdevice_id,sc->hw_type); + return ENOTSUP; + } + break; + default: + device_printf(sc->sc_dev, "adapter type id : 0x%04x sub id : 0x%04x" + "rev 0x%08x not supported (device)\n", pid, sc->subdevice_id, + sc->hw_type); + return ENOTSUP; + } + return 0; +} + static int iwn4965_attach(struct iwn_softc *sc, uint16_t pid) { @@ -769,6 +1234,8 @@ iwn4965_attach(struct iwn_softc *sc, uint16_t pid) /* Override chains masks, ROM is known to be broken. */ sc->txchainmask = IWN_ANT_AB; sc->rxchainmask = IWN_ANT_ABC; + /* Enable normal btcoex */ + sc->sc_flags |= IWN_FLAG_BTCOEX; DPRINTF(sc, IWN_DEBUG_TRACE, "%s: end\n",__func__); @@ -809,58 +1276,6 @@ iwn5000_attach(struct iwn_softc *sc, uint16_t pid) sc->reset_noise_gain = IWN5000_PHY_CALIB_RESET_NOISE_GAIN; sc->noise_gain = IWN5000_PHY_CALIB_NOISE_GAIN; - switch (sc->hw_type) { - case IWN_HW_REV_TYPE_5100: - sc->limits = &iwn5000_sensitivity_limits; - sc->fwname = "iwn5000fw"; - /* Override chains masks, ROM is known to be broken. */ - sc->txchainmask = IWN_ANT_B; - sc->rxchainmask = IWN_ANT_AB; - break; - case IWN_HW_REV_TYPE_5150: - sc->limits = &iwn5150_sensitivity_limits; - sc->fwname = "iwn5150fw"; - break; - case IWN_HW_REV_TYPE_5300: - case IWN_HW_REV_TYPE_5350: - sc->limits = &iwn5000_sensitivity_limits; - sc->fwname = "iwn5000fw"; - break; - case IWN_HW_REV_TYPE_1000: - sc->limits = &iwn1000_sensitivity_limits; - sc->fwname = "iwn1000fw"; - break; - case IWN_HW_REV_TYPE_6000: - sc->limits = &iwn6000_sensitivity_limits; - sc->fwname = "iwn6000fw"; - if (pid == 0x422c || pid == 0x4239) { - sc->sc_flags |= IWN_FLAG_INTERNAL_PA; - /* Override chains masks, ROM is known to be broken. */ - sc->txchainmask = IWN_ANT_BC; - sc->rxchainmask = IWN_ANT_BC; - } - break; - case IWN_HW_REV_TYPE_6050: - sc->limits = &iwn6000_sensitivity_limits; - sc->fwname = "iwn6050fw"; - /* Override chains masks, ROM is known to be broken. */ - sc->txchainmask = IWN_ANT_AB; - sc->rxchainmask = IWN_ANT_AB; - break; - case IWN_HW_REV_TYPE_6005: - sc->limits = &iwn6000_sensitivity_limits; - if (pid != 0x0082 && pid != 0x0085) { - sc->fwname = "iwn6000g2bfw"; - sc->sc_flags |= IWN_FLAG_ADV_BTCOEX; - } else - sc->fwname = "iwn6000g2afw"; - break; - default: - device_printf(sc->sc_dev, "adapter type %d not supported\n", - sc->hw_type); - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end in error\n",__func__); - return ENOTSUP; - } return 0; } @@ -959,6 +1374,10 @@ iwn_detach(device_t dev) ieee80211_draintask(ic, &sc->sc_radiooff_task); iwn_stop(sc); + + taskqueue_drain_all(sc->sc_tq); + taskqueue_free(sc->sc_tq); + callout_drain(&sc->watchdog_to); callout_drain(&sc->calib_to); ieee80211_ifdetach(ic); @@ -1184,7 +1603,7 @@ iwn_init_otprom(struct iwn_softc *sc) iwn_nic_unlock(sc); /* Set auto clock gate disable bit for HW with OTP shadow RAM. */ - if (sc->hw_type != IWN_HW_REV_TYPE_1000) { + if (sc->base_params->shadow_ram_support) { IWN_SETBITS(sc, IWN_DBG_LINK_PWR_MGMT, IWN_RESET_LINK_PWR_MGMT_DIS); } @@ -1197,11 +1616,12 @@ iwn_init_otprom(struct iwn_softc *sc) * Find the block before last block (contains the EEPROM image) * for HW without OTP shadow RAM. */ - if (sc->hw_type == IWN_HW_REV_TYPE_1000) { + if (! sc->base_params->shadow_ram_support) { /* Switch to absolute addressing mode. */ IWN_CLRBITS(sc, IWN_OTP_GP, IWN_OTP_GP_RELATIVE_ACCESS); base = prev = 0; - for (count = 0; count < IWN1000_OTP_NBLOCKS; count++) { + for (count = 0; count < sc->base_params->max_ll_items; + count++) { error = iwn_read_prom_data(sc, base, &next, 2); if (error != 0) return error; @@ -1210,7 +1630,7 @@ iwn_init_otprom(struct iwn_softc *sc) prev = base; base = le16toh(next); } - if (count == 0 || count == IWN1000_OTP_NBLOCKS) + if (count == 0 || count == sc->base_params->max_ll_items) return EIO; /* Skip "next" word. */ sc->prom_base = prev + 1; @@ -1316,16 +1736,12 @@ fail: iwn_dma_contig_free(dma); static void iwn_dma_contig_free(struct iwn_dma_info *dma) { - if (dma->map != NULL) { - if (dma->vaddr != NULL) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - bus_dmamem_free(dma->tag, dma->vaddr, dma->map); - dma->vaddr = NULL; - } - bus_dmamap_destroy(dma->tag, dma->map); - dma->map = NULL; + if (dma->vaddr != NULL) { + bus_dmamap_sync(dma->tag, dma->map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->tag, dma->map); + bus_dmamem_free(dma->tag, dma->vaddr, dma->map); + dma->vaddr = NULL; } if (dma->tag != NULL) { bus_dma_tag_destroy(dma->tag); @@ -1459,7 +1875,7 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, struct iwn_rx_ring *ring) &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, - "%s: can't not map mbuf, error %d\n", __func__, + "%s: can't map mbuf, error %d\n", __func__, error); goto fail; } @@ -1620,6 +2036,10 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_ring *ring) m_freem(data->m); data->m = NULL; } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } } /* Clear TX descriptors. */ memset(ring->desc, 0, ring->desc_dma.size); @@ -1768,7 +2188,7 @@ iwn4965_read_eeprom(struct iwn_softc *sc) iwn_read_prom_data(sc, IWN4965_EEPROM_DOMAIN, sc->eeprom_domain, 4); /* Read the list of authorized channels (20MHz ones only). */ - for (i = 0; i < 7; i++) { + for (i = 0; i < IWN_NBANDS - 1; i++) { addr = iwn4965_regulatory_bands[i]; iwn_read_eeprom_channels(sc, i, addr); } @@ -1798,7 +2218,7 @@ iwn4965_read_eeprom(struct iwn_softc *sc) #ifdef IWN_DEBUG /* Print samples. */ if (sc->sc_debug & IWN_DEBUG_ANY) { - for (i = 0; i < IWN_NBANDS; i++) + for (i = 0; i < IWN_NBANDS - 1; i++) iwn4965_print_power_group(sc, i); } #endif @@ -1859,16 +2279,13 @@ iwn5000_read_eeprom(struct iwn_softc *sc) sc->eeprom_domain, 4); /* Read the list of authorized channels (20MHz ones only). */ - for (i = 0; i < 7; i++) { - if (sc->hw_type >= IWN_HW_REV_TYPE_6000) - addr = base + iwn6000_regulatory_bands[i]; - else - addr = base + iwn5000_regulatory_bands[i]; + for (i = 0; i < IWN_NBANDS - 1; i++) { + addr = base + sc->base_params->regulatory_bands[i]; iwn_read_eeprom_channels(sc, i, addr); } /* Read enhanced TX power information for 6000 Series. */ - if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + if (sc->base_params->enhanced_TX_power) iwn_read_eeprom_enhinfo(sc); iwn_read_prom_data(sc, IWN5000_EEPROM_CAL, &val, 2); @@ -1879,6 +2296,14 @@ iwn5000_read_eeprom(struct iwn_softc *sc) hdr.version, hdr.pa_type, le16toh(hdr.volt)); sc->calib_ver = hdr.version; + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2) { + sc->eeprom_voltage = le16toh(hdr.volt); + iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); + sc->eeprom_temp_high=le16toh(val); + iwn_read_prom_data(sc, base + IWN5000_EEPROM_VOLT, &val, 2); + sc->eeprom_temp = le16toh(val); + } + if (sc->hw_type == IWN_HW_REV_TYPE_5150) { /* Compute temperature offset. */ iwn_read_prom_data(sc, base + IWN5000_EEPROM_TEMP, &val, 2); @@ -2215,6 +2640,52 @@ rate2plcp(int rate) return 0; } +static int +iwn_get_1stream_tx_antmask(struct iwn_softc *sc) +{ + + return IWN_LSB(sc->txchainmask); +} + +static int +iwn_get_2stream_tx_antmask(struct iwn_softc *sc) +{ + int tx; + + /* + * The '2 stream' setup is a bit .. odd. + * + * For NICs that support only 1 antenna, default to IWN_ANT_AB or + * the firmware panics (eg Intel 5100.) + * + * For NICs that support two antennas, we use ANT_AB. + * + * For NICs that support three antennas, we use the two that + * wasn't the default one. + * + * XXX TODO: if bluetooth (full concurrent) is enabled, restrict + * this to only one antenna. + */ + + /* Default - transmit on the other antennas */ + tx = (sc->txchainmask & ~IWN_LSB(sc->txchainmask)); + + /* Now, if it's zero, set it to IWN_ANT_AB, so to not panic firmware */ + if (tx == 0) + tx = IWN_ANT_AB; + + /* + * If the NIC is a two-stream TX NIC, configure the TX mask to + * the default chainmask + */ + else if (sc->ntxchains == 2) + tx = sc->txchainmask; + + return (tx); +} + + + /* * Calculate the required PLCP value from the given rate, * to the given node. @@ -2228,19 +2699,14 @@ iwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni, { #define RV(v) ((v) & IEEE80211_RATE_VAL) struct ieee80211com *ic = ni->ni_ic; - uint8_t txant1, txant2; uint32_t plcp = 0; int ridx; - /* Use the first valid TX antenna. */ - txant1 = IWN_LSB(sc->txchainmask); - txant2 = IWN_LSB(sc->txchainmask & ~txant1); - /* * If it's an MCS rate, let's set the plcp correctly * and set the relevant flags based on the node config. */ - if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + if (rate & IEEE80211_RATE_MCS) { /* * Set the initial PLCP value to be between 0->31 for * MCS 0 -> MCS 31, then set the "I'm an MCS rate!" @@ -2267,15 +2733,15 @@ iwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni, } /* - * If it's a two stream rate, enable TX on both - * antennas. - * - * XXX three stream rates? + * Ensure the selected rate matches the link quality + * table entries being used. */ - if (rate > 0x87) - plcp |= IWN_RFLAG_ANT(txant1 | txant2); + if (rate > 0x8f) + plcp |= IWN_RFLAG_ANT(sc->txchainmask); + else if (rate > 0x87) + plcp |= IWN_RFLAG_ANT(iwn_get_2stream_tx_antmask(sc)); else - plcp |= IWN_RFLAG_ANT(txant1); + plcp |= IWN_RFLAG_ANT(iwn_get_1stream_tx_antmask(sc)); } else { /* * Set the initial PLCP - fine for both @@ -2297,7 +2763,8 @@ iwn_rate_to_plcp(struct iwn_softc *sc, struct ieee80211_node *ni, plcp |= IWN_RFLAG_CCK; /* Set antenna configuration */ - plcp |= IWN_RFLAG_ANT(txant1); + /* XXX TODO: is this the right antenna to use for legacy? */ + plcp |= IWN_RFLAG_ANT(iwn_get_1stream_tx_antmask(sc)); } DPRINTF(sc, IWN_DEBUG_TXRATE, "%s: rate=0x%02x, plcp=0x%08x\n", @@ -2502,14 +2969,14 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, if ((flags & IWN_RX_NOERROR) != IWN_RX_NOERROR) { DPRINTF(sc, IWN_DEBUG_RECV, "%s: RX flags error %x\n", __func__, flags); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } /* Discard frames that are too short. */ - if (len < sizeof (*wh)) { + if (len < sizeof (struct ieee80211_frame_ack)) { DPRINTF(sc, IWN_DEBUG_RECV, "%s: frame too short: %d\n", __func__, len); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } @@ -2517,7 +2984,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, if (m1 == NULL) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", __func__); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } bus_dmamap_unload(ring->data_dmat, data->map); @@ -2540,7 +3007,7 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, ring->desc[ring->cur] = htole32(paddr >> 8); bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } @@ -2558,7 +3025,10 @@ iwn_rx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, /* Grab a reference to the source node. */ wh = mtod(m, struct ieee80211_frame *); - ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + if (len >= sizeof(struct ieee80211_frame_min)) + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + else + ni = NULL; nf = (ni != NULL && ni->ni_vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_F_SCAN) == 0) ? sc->noise : -95; @@ -2629,8 +3099,9 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, uint16_t ssn; uint8_t tid; int ackfailcnt = 0, i, lastidx, qid, *res, shift; + int tx_ok = 0, tx_err = 0; - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s begin\n", __func__); bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); @@ -2660,6 +3131,7 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, KASSERT(ni != NULL, ("no node")); KASSERT(m != NULL, ("no mbuf")); + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m); ieee80211_tx_complete(ni, m, 1); txq->queued--; @@ -2685,22 +3157,32 @@ iwn_rx_compressed_ba(struct iwn_softc *sc, struct iwn_rx_desc *desc, if (wn->agg[tid].nframes > (64 - shift)) return; + /* + * Walk the bitmap and calculate how many successful and failed + * attempts are made. + * + * Yes, the rate control code doesn't know these are A-MPDU + * subframes and that it's okay to fail some of these. + */ ni = tap->txa_ni; bitmap = (le64toh(ba->bitmap) >> shift) & wn->agg[tid].bitmap; for (i = 0; bitmap; i++) { if ((bitmap & 1) == 0) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + tx_err ++; ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + tx_ok ++; ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); } bitmap >>= 1; } - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, + "->%s: end; %d ok; %d err\n",__func__, tx_ok, tx_err); } @@ -2728,25 +3210,24 @@ iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, switch (calib->code) { case IWN5000_PHY_CALIB_DC: - if ((sc->sc_flags & IWN_FLAG_INTERNAL_PA) == 0 && - (sc->hw_type == IWN_HW_REV_TYPE_5150 || - sc->hw_type >= IWN_HW_REV_TYPE_6000) && - sc->hw_type != IWN_HW_REV_TYPE_6050) + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_DC) idx = 0; break; case IWN5000_PHY_CALIB_LO: - idx = 1; + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_LO) + idx = 1; break; case IWN5000_PHY_CALIB_TX_IQ: - idx = 2; + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TX_IQ) + idx = 2; break; case IWN5000_PHY_CALIB_TX_IQ_PERIODIC: - if (sc->hw_type < IWN_HW_REV_TYPE_6000 && - sc->hw_type != IWN_HW_REV_TYPE_5150) + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TX_IQ_PERIODIC) idx = 3; break; case IWN5000_PHY_CALIB_BASE_BAND: - idx = 4; + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_BASE_BAND) + idx = 4; break; } if (idx == -1) /* Ignore other results. */ @@ -2763,11 +3244,72 @@ iwn5000_rx_calib_results(struct iwn_softc *sc, struct iwn_rx_desc *desc, return; } DPRINTF(sc, IWN_DEBUG_CALIBRATE, - "saving calibration result code=%d len=%d\n", calib->code, len); + "saving calibration result idx=%d, code=%d len=%d\n", idx, calib->code, len); sc->calibcmd[idx].len = len; memcpy(sc->calibcmd[idx].buf, calib, len); } +static void +iwn_stats_update(struct iwn_softc *sc, struct iwn_calib_state *calib, + struct iwn_stats *stats, int len) +{ + struct iwn_stats_bt *stats_bt; + struct iwn_stats *lstats; + + /* + * First - check whether the length is the bluetooth or normal. + * + * If it's normal - just copy it and bump out. + * Otherwise we have to convert things. + */ + + if (len == sizeof(struct iwn_stats) + 4) { + memcpy(&sc->last_stat, stats, sizeof(struct iwn_stats)); + sc->last_stat_valid = 1; + return; + } + + /* + * If it's not the bluetooth size - log, then just copy. + */ + if (len != sizeof(struct iwn_stats_bt) + 4) { + DPRINTF(sc, IWN_DEBUG_STATS, + "%s: size of rx statistics (%d) not an expected size!\n", + __func__, + len); + memcpy(&sc->last_stat, stats, sizeof(struct iwn_stats)); + sc->last_stat_valid = 1; + return; + } + + /* + * Ok. Time to copy. + */ + stats_bt = (struct iwn_stats_bt *) stats; + lstats = &sc->last_stat; + + /* flags */ + lstats->flags = stats_bt->flags; + /* rx_bt */ + memcpy(&lstats->rx.ofdm, &stats_bt->rx_bt.ofdm, + sizeof(struct iwn_rx_phy_stats)); + memcpy(&lstats->rx.cck, &stats_bt->rx_bt.cck, + sizeof(struct iwn_rx_phy_stats)); + memcpy(&lstats->rx.general, &stats_bt->rx_bt.general_bt.common, + sizeof(struct iwn_rx_general_stats)); + memcpy(&lstats->rx.ht, &stats_bt->rx_bt.ht, + sizeof(struct iwn_rx_ht_phy_stats)); + /* tx */ + memcpy(&lstats->tx, &stats_bt->tx, + sizeof(struct iwn_tx_stats)); + /* general */ + memcpy(&lstats->general, &stats_bt->general, + sizeof(struct iwn_general_stats)); + + /* XXX TODO: Squirrel away the extra bluetooth stats somewhere */ + sc->last_stat_valid = 1; +} + /* * Process an RX_STATISTICS or BEACON_STATISTICS firmware notification. * The latter is sent by the firmware after each received beacon. @@ -2782,6 +3324,7 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwn_calib_state *calib = &sc->calib; struct iwn_stats *stats = (struct iwn_stats *)(desc + 1); + struct iwn_stats *lstats; int temp; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); @@ -2796,12 +3339,26 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: received statistics, cmd %d\n", - __func__, desc->type); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_STATS, + "%s: received statistics, cmd %d, len %d\n", + __func__, desc->type, le16toh(desc->len)); sc->calib_cnt = 0; /* Reset TX power calibration timeout. */ + /* + * Collect/track general statistics for reporting. + * + * This takes care of ensuring that the bluetooth sized message + * will be correctly converted to the legacy sized message. + */ + iwn_stats_update(sc, calib, stats, le16toh(desc->len)); + + /* + * And now, let's take a reference of it to use! + */ + lstats = &sc->last_stat; + /* Test if temperature has changed. */ - if (stats->general.temp != sc->rawtemp) { + if (lstats->general.temp != sc->rawtemp) { /* Convert "raw" temperature to degC. */ sc->rawtemp = stats->general.temp; temp = ops->get_temperature(sc); @@ -2816,25 +3373,51 @@ iwn_rx_statistics(struct iwn_softc *sc, struct iwn_rx_desc *desc, if (desc->type != IWN_BEACON_STATISTICS) return; /* Reply to a statistics request. */ - sc->noise = iwn_get_noise(&stats->rx.general); + sc->noise = iwn_get_noise(&lstats->rx.general); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: noise %d\n", __func__, sc->noise); /* Test that RSSI and noise are present in stats report. */ - if (le32toh(stats->rx.general.flags) != 1) { + if (le32toh(lstats->rx.general.flags) != 1) { DPRINTF(sc, IWN_DEBUG_ANY, "%s\n", "received statistics without RSSI"); return; } if (calib->state == IWN_CALIB_STATE_ASSOC) - iwn_collect_noise(sc, &stats->rx.general); - else if (calib->state == IWN_CALIB_STATE_RUN) - iwn_tune_sensitivity(sc, &stats->rx); + iwn_collect_noise(sc, &lstats->rx.general); + else if (calib->state == IWN_CALIB_STATE_RUN) { + iwn_tune_sensitivity(sc, &lstats->rx); + /* + * XXX TODO: Only run the RX recovery if we're associated! + */ + iwn_check_rx_recovery(sc, lstats); + iwn_save_stats_counters(sc, lstats); + } DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); } /* + * Save the relevant statistic counters for the next calibration + * pass. + */ +static void +iwn_save_stats_counters(struct iwn_softc *sc, const struct iwn_stats *rs) +{ + struct iwn_calib_state *calib = &sc->calib; + + /* Save counters values for next call. */ + calib->bad_plcp_cck = le32toh(rs->rx.cck.bad_plcp); + calib->fa_cck = le32toh(rs->rx.cck.fa); + calib->bad_plcp_ht = le32toh(rs->rx.ht.bad_plcp); + calib->bad_plcp_ofdm = le32toh(rs->rx.ofdm.bad_plcp); + calib->fa_ofdm = le32toh(rs->rx.ofdm.fa); + + /* Last time we received these tick values */ + sc->last_calib_ticks = ticks; +} + +/* * Process a TX_DONE firmware notification. Unfortunately, the 4965AGN * and 5000 adapters have different incompatible TX status formats. */ @@ -2850,15 +3433,18 @@ iwn4965_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, ring = &sc->txq[qid]; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " - "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", - __func__, desc->qid, desc->idx, stat->ackfailcnt, - stat->btkillcnt, stat->rate, le16toh(stat->duration), + "qid %d idx %d RTS retries %d ACK retries %d nkill %d rate %x duration %d status %x\n", + __func__, desc->qid, desc->idx, + stat->rtsfailcnt, + stat->ackfailcnt, + stat->btkillcnt, + stat->rate, le16toh(stat->duration), le32toh(stat->status)); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); if (qid >= sc->firstaggqueue) { iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, - &stat->status); + stat->ackfailcnt, &stat->status); } else { iwn_tx_done(sc, desc, stat->ackfailcnt, le32toh(stat->status) & 0xff); @@ -2877,9 +3463,12 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, ring = &sc->txq[qid]; DPRINTF(sc, IWN_DEBUG_XMIT, "%s: " - "qid %d idx %d retries %d nkill %d rate %x duration %d status %x\n", - __func__, desc->qid, desc->idx, stat->ackfailcnt, - stat->btkillcnt, stat->rate, le16toh(stat->duration), + "qid %d idx %d RTS retries %d ACK retries %d nkill %d rate %x duration %d status %x\n", + __func__, desc->qid, desc->idx, + stat->rtsfailcnt, + stat->ackfailcnt, + stat->btkillcnt, + stat->rate, le16toh(stat->duration), le32toh(stat->status)); #ifdef notyet @@ -2890,7 +3479,7 @@ iwn5000_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); if (qid >= sc->firstaggqueue) { iwn_ampdu_tx_done(sc, qid, desc->idx, stat->nframes, - &stat->status); + stat->ackfailcnt, &stat->status); } else { iwn_tx_done(sc, desc, stat->ackfailcnt, le16toh(stat->status) & 0xff); @@ -2926,11 +3515,11 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, * Update rate control statistics for the node. */ if (status & IWN_TX_FAIL) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); } else { - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); } @@ -2977,12 +3566,19 @@ iwn_tx_done(struct iwn_softc *sc, struct iwn_rx_desc *desc, int ackfailcnt, static void iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) { - struct iwn_tx_ring *ring = &sc->txq[4]; + struct iwn_tx_ring *ring; struct iwn_tx_data *data; + int cmd_queue_num; - if ((desc->qid & 0xf) != 4) + if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) + cmd_queue_num = IWN_PAN_CMD_QUEUE; + else + cmd_queue_num = IWN_CMD_QUEUE_NUM; + + if ((desc->qid & IWN_RX_DESC_QID_MSK) != cmd_queue_num) return; /* Not a command ack. */ + ring = &sc->txq[cmd_queue_num]; data = &ring->data[desc->idx]; /* If the command was mapped in an mbuf, free it. */ @@ -2998,7 +3594,7 @@ iwn_cmd_done(struct iwn_softc *sc, struct iwn_rx_desc *desc) static void iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, - void *stat) + int ackfailcnt, void *stat) { struct iwn_ops *ops = &sc->ops; struct ifnet *ifp = sc->sc_ifp; @@ -3015,14 +3611,60 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, uint8_t tid; int bit, i, lastidx, *res, seqno, shift, start; + /* XXX TODO: status is le16 field! Grr */ + DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: nframes=%d, status=0x%08x\n", + __func__, + nframes, + *status); -#ifdef NOT_YET + tap = sc->qid2tap[qid]; + tid = tap->txa_tid; + wn = (void *)tap->txa_ni; + ni = tap->txa_ni; + + /* + * XXX TODO: ACK and RTS failures would be nice here! + */ + + /* + * A-MPDU single frame status - if we failed to transmit it + * in A-MPDU, then it may be a permanent failure. + * + * XXX TODO: check what the Linux iwlwifi driver does here; + * there's some permanent and temporary failures that may be + * handled differently. + */ if (nframes == 1) { - if ((*status & 0xff) != 1 && (*status & 0xff) != 2) + if ((*status & 0xff) != 1 && (*status & 0xff) != 2) { +#ifdef NOT_YET printf("ieee80211_send_bar()\n"); - } #endif + /* + * If we completely fail a transmit, make sure a + * notification is pushed up to the rate control + * layer. + */ + ieee80211_ratectl_tx_complete(ni->ni_vap, + ni, + IEEE80211_RATECTL_TX_FAILURE, + &ackfailcnt, + NULL); + } else { + /* + * If nframes=1, then we won't be getting a BA for + * this frame. Ensure that we correctly update the + * rate control code with how many retries were + * needed to send it. + */ + ieee80211_ratectl_tx_complete(ni->ni_vap, + ni, + IEEE80211_RATECTL_TX_SUCCESS, + &ackfailcnt, + NULL); + } + } bitmap = 0; start = idx; @@ -3061,6 +3703,7 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, ssn = tap->txa_start & 0xfff; } + /* This is going nframes DWORDS into the descriptor? */ seqno = le32toh(*(status + nframes)) & 0xfff; for (lastidx = (seqno & 0xff); ring->read != lastidx;) { data = &ring->data[ring->read]; @@ -3074,7 +3717,7 @@ iwn_ampdu_tx_done(struct iwn_softc *sc, int qid, int idx, int nframes, KASSERT(ni != NULL, ("no node")); KASSERT(m != NULL, ("no mbuf")); - + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: freeing m=%p\n", __func__, m); ieee80211_tx_complete(ni, m, 1); ring->queued--; @@ -3129,12 +3772,12 @@ iwn_notif_intr(struct iwn_softc *sc) desc = mtod(data->m, struct iwn_rx_desc *); DPRINTF(sc, IWN_DEBUG_RECV, - "%s: qid %x idx %d flags %x type %d(%s) len %d\n", - __func__, desc->qid & 0xf, desc->idx, desc->flags, + "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n", + __func__, sc->rxq.cur, desc->qid & 0xf, desc->idx, desc->flags, desc->type, iwn_intr_str(desc->type), le16toh(desc->len)); - if (!(desc->qid & 0x80)) /* Reply to a command. */ + if (!(desc->qid & IWN_UNSOLICITED_RX_NOTIF)) /* Reply to a command. */ iwn_cmd_done(sc, desc); switch (desc->type) { @@ -3229,7 +3872,8 @@ iwn_notif_intr(struct iwn_softc *sc) BUS_DMASYNC_POSTREAD); #ifdef IWN_DEBUG uint32_t *status = (uint32_t *)(desc + 1); - DPRINTF(sc, IWN_DEBUG_INTR, "state changed to %x\n", + DPRINTF(sc, IWN_DEBUG_INTR | IWN_DEBUG_STATE, + "state changed to %x\n", le32toh(*status)); #endif break; @@ -3254,11 +3898,11 @@ iwn_notif_intr(struct iwn_softc *sc) #ifdef IWN_DEBUG struct iwn_stop_scan *scan = (struct iwn_stop_scan *)(desc + 1); - DPRINTF(sc, IWN_DEBUG_STATE, + DPRINTF(sc, IWN_DEBUG_STATE | IWN_DEBUG_SCAN, "scan finished nchan=%d status=%d chan=%d\n", scan->nchan, scan->status, scan->chan); #endif - + sc->sc_is_scanning = 0; IWN_UNLOCK(sc); ieee80211_scan_next(vap); IWN_LOCK(sc); @@ -3410,8 +4054,10 @@ iwn_intr(void *arg) r2 = 0; /* Unused. */ } else { r1 = IWN_READ(sc, IWN_INT); - if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) { + IWN_UNLOCK(sc); return; /* Hardware gone! */ + } r2 = IWN_READ(sc, IWN_FH_INT); } @@ -3442,8 +4088,8 @@ iwn_intr(void *arg) #endif /* Dump firmware error log and stop. */ iwn_fatal_intr(sc); - ifp->if_flags &= ~IFF_UP; - iwn_stop_locked(sc); + + taskqueue_enqueue(sc->sc_tq, &sc->sc_panic_task); goto done; } if ((r1 & (IWN_INT_FH_RX | IWN_INT_SW_RX | IWN_INT_RX_PERIODIC)) || @@ -3541,6 +4187,115 @@ iwn5000_reset_sched(struct iwn_softc *sc, int qid, int idx) } #endif +/* + * Check whether OFDM 11g protection will be enabled for the given rate. + * + * The original driver code only enabled protection for OFDM rates. + * It didn't check to see whether it was operating in 11a or 11bg mode. + */ +static int +iwn_check_rate_needs_protection(struct iwn_softc *sc, + struct ieee80211vap *vap, uint8_t rate) +{ + struct ieee80211com *ic = vap->iv_ic; + + /* + * Not in 2GHz mode? Then there's no need to enable OFDM + * 11bg protection. + */ + if (! IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { + return (0); + } + + /* + * 11bg protection not enabled? Then don't use it. + */ + if ((ic->ic_flags & IEEE80211_F_USEPROT) == 0) + return (0); + + /* + * If it's an 11n rate - no protection. + * We'll do it via a specific 11n check. + */ + if (rate & IEEE80211_RATE_MCS) { + return (0); + } + + /* + * Do a rate table lookup. If the PHY is CCK, + * don't do protection. + */ + if (ieee80211_rate2phytype(ic->ic_rt, rate) == IEEE80211_T_CCK) + return (0); + + /* + * Yup, enable protection. + */ + return (1); +} + +/* + * return a value between 0 and IWN_MAX_TX_RETRIES-1 as an index into + * the link quality table that reflects this particular entry. + */ +static int +iwn_tx_rate_to_linkq_offset(struct iwn_softc *sc, struct ieee80211_node *ni, + uint8_t rate) +{ + struct ieee80211_rateset *rs; + int is_11n; + int nr; + int i; + uint8_t cmp_rate; + + /* + * Figure out if we're using 11n or not here. + */ + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates > 0) + is_11n = 1; + else + is_11n = 0; + + /* + * Use the correct rate table. + */ + if (is_11n) { + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + nr = ni->ni_htrates.rs_nrates; + } else { + rs = &ni->ni_rates; + nr = rs->rs_nrates; + } + + /* + * Find the relevant link quality entry in the table. + */ + for (i = 0; i < nr && i < IWN_MAX_TX_RETRIES - 1 ; i++) { + /* + * The link quality table index starts at 0 == highest + * rate, so we walk the rate table backwards. + */ + cmp_rate = rs->rs_rates[(nr - 1) - i]; + if (rate & IEEE80211_RATE_MCS) + cmp_rate |= IEEE80211_RATE_MCS; + +#if 0 + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: idx %d: nr=%d, rate=0x%02x, rateentry=0x%02x\n", + __func__, + i, + nr, + rate, + cmp_rate); +#endif + + if (cmp_rate == rate) + return (i); + } + + /* Failed? Start at the end */ + return (IWN_MAX_TX_RETRIES - 1); +} + static int iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { @@ -3561,7 +4316,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) uint16_t qos; u_int hdrlen; bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; - uint8_t tid, ridx, txant, type; + uint8_t tid, type; int ac, i, totlen, error, pad, nsegs = 0, rate; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); @@ -3582,6 +4337,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) } ac = M_WME_GETAC(m); if (m->m_flags & M_AMPDU_MPDU) { + uint16_t seqno; struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[ac]; if (!IEEE80211_AMPDU_RUNNING(tap)) { @@ -3589,9 +4345,27 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) return EINVAL; } + /* + * Queue this frame to the hardware ring that we've + * negotiated AMPDU TX on. + * + * Note that the sequence number must match the TX slot + * being used! + */ ac = *(int *)tap->txa_private; + seqno = ni->ni_txseqs[tid]; *(uint16_t *)wh->i_seq = - htole16(ni->ni_txseqs[tid] << IEEE80211_SEQ_SEQ_SHIFT); + htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); + ring = &sc->txq[ac]; + if ((seqno % 256) != ring->cur) { + device_printf(sc->sc_dev, + "%s: m=%p: seqno (%d) (%d) != ring index (%d) !\n", + __func__, + m, + seqno, + seqno % 256, + ring->cur); + } ni->ni_txseqs[tid]++; } ring = &sc->txq[ac]; @@ -3606,13 +4380,13 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) rate = tp->mcastrate; else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) rate = tp->ucastrate; + else if (m->m_flags & M_EAPOL) + rate = tp->mgmtrate; else { /* XXX pass pktlen */ (void) ieee80211_ratectl_rate(ni, NULL, 0); rate = ni->ni_txrate; } - ridx = ieee80211_legacy_rate_lookup(ic->ic_rt, - rate & IEEE80211_RATE_VAL); /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { @@ -3669,13 +4443,18 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) /* NB: Group frames are sent using CCK in 802.11b/g. */ if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { flags |= IWN_TX_NEED_RTS; - } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && - ridx >= IWN_RIDX_OFDM6) { + } else if (iwn_check_rate_needs_protection(sc, vap, rate)) { if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) flags |= IWN_TX_NEED_CTS; else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) flags |= IWN_TX_NEED_RTS; + } else if ((rate & IEEE80211_RATE_MCS) && + (ic->ic_htprotmode == IEEE80211_PROT_RTSCTS)) { + flags |= IWN_TX_NEED_RTS; } + + /* XXX HT protection? */ + if (flags & (IWN_TX_NEED_RTS | IWN_TX_NEED_CTS)) { if (sc->hw_type != IWN_HW_REV_TYPE_4965) { /* 5000 autoselects RTS/CTS or CTS-to-self. */ @@ -3722,13 +4501,11 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) if (tx->id == sc->broadcast_id) { /* Group or management frame. */ tx->linkq = 0; - /* XXX Alternate between antenna A and B? */ - txant = IWN_LSB(sc->txchainmask); - tx->rate |= htole32(IWN_RFLAG_ANT(txant)); } else { - tx->linkq = ni->ni_rates.rs_nrates - ridx - 1; + tx->linkq = iwn_tx_rate_to_linkq_offset(sc, ni, rate); flags |= IWN_TX_LINKQ; /* enable MRR */ } + /* Set physical address of "scratch area". */ tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); tx->hiaddr = IWN_HIADDR(data->scratch_paddr); @@ -3751,7 +4528,7 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) return error; } /* Too many DMA segments, linearize mbuf. */ - m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER); + m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER - 1); if (m1 == NULL) { device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); @@ -3773,8 +4550,16 @@ iwn_tx_data(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni) data->m = m; data->ni = ni; - DPRINTF(sc, IWN_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", - __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); + DPRINTF(sc, IWN_DEBUG_XMIT, + "%s: qid %d idx %d len %d nsegs %d flags 0x%08x rate 0x%04x plcp 0x%08x\n", + __func__, + ring->qid, + ring->cur, + m->m_pkthdr.len, + nsegs, + flags, + rate, + tx->rate); /* Fill TX descriptor. */ desc->nsegs = 1; @@ -3821,9 +4606,9 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) { struct iwn_ops *ops = &sc->ops; - struct ifnet *ifp = sc->sc_ifp; +// struct ifnet *ifp = sc->sc_ifp; struct ieee80211vap *vap = ni->ni_vap; - struct ieee80211com *ic = ifp->if_l2com; +// struct ieee80211com *ic = ifp->if_l2com; struct iwn_tx_cmd *cmd; struct iwn_cmd_data *tx; struct ieee80211_frame *wh; @@ -3835,7 +4620,7 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, uint32_t flags; u_int hdrlen; int ac, totlen, error, pad, nsegs = 0, i, rate; - uint8_t ridx, type, txant; + uint8_t type; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); @@ -3851,16 +4636,8 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; - /* Choose a TX rate index. */ + /* Choose a TX rate. */ rate = params->ibp_rate0; - ridx = ieee80211_legacy_rate_lookup(ic->ic_rt, - rate & IEEE80211_RATE_VAL); - if (ridx == (uint8_t)-1) { - /* XXX fall back to mcast/mgmt rate? */ - m_freem(m); - return EINVAL; - } - totlen = m->m_pkthdr.len; /* Prepare TX firmware command. */ @@ -3930,17 +4707,10 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, tx->rts_ntries = params->ibp_try1; tx->data_ntries = params->ibp_try0; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - - /* XXX should just use iwn_rate_to_plcp() */ - tx->rate = htole32(rate2plcp(rate)); - if (ridx < IWN_RIDX_OFDM6 && - IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) - tx->rate |= htole32(IWN_RFLAG_CCK); + tx->rate = iwn_rate_to_plcp(sc, ni, rate); /* Group or management frame. */ tx->linkq = 0; - txant = IWN_LSB(sc->txchainmask); - tx->rate |= htole32(IWN_RFLAG_ANT(txant)); /* Set physical address of "scratch area". */ tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); @@ -3964,7 +4734,7 @@ iwn_tx_data_raw(struct iwn_softc *sc, struct mbuf *m, return error; } /* Too many DMA segments, linearize mbuf. */ - m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER); + m1 = m_collapse(m, M_NOWAIT, IWN_MAX_SCATTER - 1); if (m1 == NULL) { device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); @@ -4038,7 +4808,7 @@ iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, struct iwn_softc *sc = ifp->if_softc; int error = 0; - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + DPRINTF(sc, IWN_DEBUG_XMIT | IWN_DEBUG_TRACE, "->%s begin\n", __func__); if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { ieee80211_free_node(ni); @@ -4063,13 +4833,13 @@ iwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, if (error != 0) { /* NB: m is reclaimed on tx failure */ ieee80211_free_node(ni); - ifp->if_oerrors++; - } - sc->sc_tx_timer = 5; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + } else + sc->sc_tx_timer = 5; IWN_UNLOCK(sc); - DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); + DPRINTF(sc, IWN_DEBUG_TRACE | IWN_DEBUG_XMIT, "->%s: end\n",__func__); return error; } @@ -4093,6 +4863,8 @@ iwn_start_locked(struct ifnet *ifp) IWN_LOCK_ASSERT(sc); + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: called\n", __func__); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || (ifp->if_drv_flags & IFF_DRV_OACTIVE)) return; @@ -4108,11 +4880,12 @@ iwn_start_locked(struct ifnet *ifp) ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (iwn_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; - continue; - } - sc->sc_tx_timer = 5; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + } else + sc->sc_tx_timer = 5; } + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: done\n", __func__); } static void @@ -4174,6 +4947,18 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; + case SIOCGIWNSTATS: + IWN_LOCK(sc); + /* XXX validate permissions/memory/etc? */ + error = copyout(&sc->last_stat, ifr->ifr_data, + sizeof(struct iwn_stats)); + IWN_UNLOCK(sc); + break; + case SIOCZIWNSTATS: + IWN_LOCK(sc); + memset(&sc->last_stat, 0, sizeof(struct iwn_stats)); + IWN_UNLOCK(sc); + break; default: error = EINVAL; break; @@ -4187,19 +4972,26 @@ iwn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) static int iwn_cmd(struct iwn_softc *sc, int code, const void *buf, int size, int async) { - struct iwn_tx_ring *ring = &sc->txq[4]; + struct iwn_tx_ring *ring; struct iwn_tx_desc *desc; struct iwn_tx_data *data; struct iwn_tx_cmd *cmd; struct mbuf *m; bus_addr_t paddr; int totlen, error; + int cmd_queue_num; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); if (async == 0) IWN_LOCK_ASSERT(sc); + if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) + cmd_queue_num = IWN_PAN_CMD_QUEUE; + else + cmd_queue_num = IWN_CMD_QUEUE_NUM; + + ring = &sc->txq[cmd_queue_num]; desc = &ring->desc[ring->cur]; data = &ring->data[ring->cur]; totlen = 4 + size; @@ -4293,42 +5085,87 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) { #define RV(v) ((v) & IEEE80211_RATE_VAL) struct iwn_node *wn = (void *)ni; - struct ieee80211_rateset *rs = &ni->ni_rates; + struct ieee80211_rateset *rs; struct iwn_cmd_link_quality linkq; - uint8_t txant; int i, rate, txrate; + int is_11n; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); - /* Use the first valid TX antenna. */ - txant = IWN_LSB(sc->txchainmask); - memset(&linkq, 0, sizeof linkq); linkq.id = wn->id; - linkq.antmsk_1stream = txant; - linkq.antmsk_2stream = IWN_ANT_AB; - linkq.ampdu_max = 64; + linkq.antmsk_1stream = iwn_get_1stream_tx_antmask(sc); + linkq.antmsk_2stream = iwn_get_2stream_tx_antmask(sc); + + linkq.ampdu_max = 32; /* XXX negotiated? */ linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ + DPRINTF(sc, IWN_DEBUG_XMIT, + "%s: 1stream antenna=0x%02x, 2stream antenna=0x%02x, ntxstreams=%d\n", + __func__, + linkq.antmsk_1stream, + linkq.antmsk_2stream, + sc->ntxchains); + + /* + * Are we using 11n rates? Ensure the channel is + * 11n _and_ we have some 11n rates, or don't + * try. + */ + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && ni->ni_htrates.rs_nrates > 0) { + rs = (struct ieee80211_rateset *) &ni->ni_htrates; + is_11n = 1; + } else { + rs = &ni->ni_rates; + is_11n = 0; + } + /* Start at highest available bit-rate. */ - if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) + /* + * XXX this is all very dirty! + */ + if (is_11n) txrate = ni->ni_htrates.rs_nrates - 1; else txrate = rs->rs_nrates - 1; for (i = 0; i < IWN_MAX_TX_RETRIES; i++) { uint32_t plcp; - if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) - rate = IEEE80211_RATE_MCS | txrate; + /* + * XXX TODO: ensure the last two slots are the two lowest + * rate entries, just for now. + */ + if (i == 14 || i == 15) + txrate = 0; + + if (is_11n) + rate = IEEE80211_RATE_MCS | rs->rs_rates[txrate]; else rate = RV(rs->rs_rates[txrate]); /* Do rate -> PLCP config mapping */ plcp = iwn_rate_to_plcp(sc, ni, rate); linkq.retry[i] = plcp; + DPRINTF(sc, IWN_DEBUG_XMIT, + "%s: i=%d, txrate=%d, rate=0x%02x, plcp=0x%08x\n", + __func__, + i, + txrate, + rate, + le32toh(plcp)); - /* Special case for dual-stream rates? */ + /* + * The mimo field is an index into the table which + * indicates the first index where it and subsequent entries + * will not be using MIMO. + * + * Since we're filling linkq from 0..15 and we're filling + * from the higest MCS rates to the lowest rates, if we + * _are_ doing a dual-stream rate, set mimo to idx+1 (ie, + * the next entry.) That way if the next entry is a non-MIMO + * entry, we're already pointing at it. + */ if ((le32toh(plcp) & IWN_RFLAG_MCS) && RV(le32toh(plcp)) > 7) linkq.mimo = i + 1; @@ -4337,6 +5174,15 @@ iwn_set_link_quality(struct iwn_softc *sc, struct ieee80211_node *ni) if (txrate > 0) txrate--; } + /* + * If we reached the end of the list and indeed we hit + * all MIMO rates (eg 5300 doing MCS23-15) then yes, + * set mimo to 15. Setting it to 16 panics the firmware. + */ + if (linkq.mimo > 15) + linkq.mimo = 15; + + DPRINTF(sc, IWN_DEBUG_XMIT, "%s: mimo = %d\n", __func__, linkq.mimo); DPRINTF(sc, IWN_DEBUG_TRACE, "->%s: end\n",__func__); @@ -4374,13 +5220,14 @@ iwn_add_broadcast_node(struct iwn_softc *sc, int async) memset(&linkq, 0, sizeof linkq); linkq.id = sc->broadcast_id; - linkq.antmsk_1stream = txant; - linkq.antmsk_2stream = IWN_ANT_AB; + linkq.antmsk_1stream = iwn_get_1stream_tx_antmask(sc); + linkq.antmsk_2stream = iwn_get_2stream_tx_antmask(sc); linkq.ampdu_max = 64; linkq.ampdu_threshold = 3; linkq.ampdu_limit = htole16(4000); /* 4ms */ /* Use lowest mandatory bit-rate. */ + /* XXX rate table lookup? */ if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) linkq.retry[0] = htole32(0xd); else @@ -4442,6 +5289,12 @@ iwn_set_led(struct iwn_softc *sc, uint8_t which, uint8_t off, uint8_t on) DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__); +#if 0 + /* XXX don't set LEDs during scan? */ + if (sc->sc_is_scanning) + return; +#endif + /* Clear microcode LED ownership. */ IWN_CLRBITS(sc, IWN_LED, IWN_LED_BSM_CTRL); @@ -4681,6 +5534,7 @@ iwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, int async) { struct iwn5000_cmd_txpower cmd; + int cmdid; DPRINTF(sc, IWN_DEBUG_TRACE, "->Doing %s\n", __func__); @@ -4692,8 +5546,15 @@ iwn5000_set_txpower(struct iwn_softc *sc, struct ieee80211_channel *ch, cmd.global_limit = 2 * IWN5000_TXPOWER_MAX_DBM; /* 16 dBm */ cmd.flags = IWN5000_TXPOWER_NO_CLOSED; cmd.srv_limit = IWN5000_TXPOWER_AUTO; - DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: setting TX power\n", __func__); - return iwn_cmd(sc, IWN_CMD_TXPOWER_DBM, &cmd, sizeof cmd, async); + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT, + "%s: setting TX power; rev=%d\n", + __func__, + IWN_UCODE_API(sc->ucode_rev)); + if (IWN_UCODE_API(sc->ucode_rev) == 1) + cmdid = IWN_CMD_TXPOWER_DBM_V1; + else + cmdid = IWN_CMD_TXPOWER_DBM; + return iwn_cmd(sc, cmdid, &cmd, sizeof cmd, async); } /* @@ -4893,7 +5754,7 @@ iwn_collect_noise(struct iwn_softc *sc, for (i = 0; i < 3; i++) if (val - calib->rssi[i] > 15 * 20) sc->chainmask &= ~(1 << i); - DPRINTF(sc, IWN_DEBUG_CALIBRATE, + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT, "%s: RX chains mask: theoretical=0x%x, actual=0x%x\n", __func__, sc->rxchainmask, sc->chainmask); @@ -4907,6 +5768,10 @@ iwn_collect_noise(struct iwn_softc *sc, #ifdef notyet /* XXX Disable RX chains with no antennas connected. */ sc->rxon->rxchain = htole16(IWN_RXCHAIN_SEL(sc->chainmask)); + if (sc->sc_is_scanning) + device_printf(sc->sc_dev, + "%s: is_scanning set, before RXON\n", + __func__); (void)iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); #endif @@ -5014,7 +5879,7 @@ iwn5000_set_gains(struct iwn_softc *sc) cmd.gain[i - 1] |= 1 << 2; /* sign bit */ } } - DPRINTF(sc, IWN_DEBUG_CALIBRATE, + DPRINTF(sc, IWN_DEBUG_CALIBRATE | IWN_DEBUG_XMIT, "setting differential gains Ant B/C: %x/%x (%x)\n", cmd.gain[0], cmd.gain[1], sc->chainmask); return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 1); @@ -5064,10 +5929,6 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) fa += le32toh(stats->ofdm.fa) - calib->fa_ofdm; fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ - /* Save counters values for next call. */ - calib->bad_plcp_ofdm = le32toh(stats->ofdm.bad_plcp); - calib->fa_ofdm = le32toh(stats->ofdm.fa); - if (fa > 50 * rxena) { /* High false alarm count, decrease sensitivity. */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, @@ -5121,10 +5982,6 @@ iwn_tune_sensitivity(struct iwn_softc *sc, const struct iwn_rx_stats *stats) fa += le32toh(stats->cck.fa) - calib->fa_cck; fa *= 200 * IEEE80211_DUR_TU; /* 200TU */ - /* Save counters values for next call. */ - calib->bad_plcp_cck = le32toh(stats->cck.bad_plcp); - calib->fa_cck = le32toh(stats->cck.fa); - if (fa > 50 * rxena) { /* High false alarm count, decrease sensitivity. */ DPRINTF(sc, IWN_DEBUG_CALIBRATE, @@ -5205,7 +6062,7 @@ iwn_send_sensitivity(struct iwn_softc *sc) cmd.energy_cck = htole16(calib->energy_cck); /* Barker modulation: use default values. */ cmd.corr_barker = htole16(190); - cmd.corr_barker_mrc = htole16(390); + cmd.corr_barker_mrc = htole16(sc->limits->barker_mrc); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: set sensitivity %d/%d/%d/%d/%d/%d/%d\n", __func__, @@ -5230,6 +6087,86 @@ send: } /* + * Look at the increase of PLCP errors over time; if it exceeds + * a programmed threshold then trigger an RF retune. + */ +static void +iwn_check_rx_recovery(struct iwn_softc *sc, struct iwn_stats *rs) +{ + int32_t delta_ofdm, delta_ht, delta_cck; + struct iwn_calib_state *calib = &sc->calib; + int delta_ticks, cur_ticks; + int delta_msec; + int thresh; + + /* + * Calculate the difference between the current and + * previous statistics. + */ + delta_cck = le32toh(rs->rx.cck.bad_plcp) - calib->bad_plcp_cck; + delta_ofdm = le32toh(rs->rx.ofdm.bad_plcp) - calib->bad_plcp_ofdm; + delta_ht = le32toh(rs->rx.ht.bad_plcp) - calib->bad_plcp_ht; + + /* + * Calculate the delta in time between successive statistics + * messages. Yes, it can roll over; so we make sure that + * this doesn't happen. + * + * XXX go figure out what to do about rollover + * XXX go figure out what to do if ticks rolls over to -ve instead! + * XXX go stab signed integer overflow undefined-ness in the face. + */ + cur_ticks = ticks; + delta_ticks = cur_ticks - sc->last_calib_ticks; + + /* + * If any are negative, then the firmware likely reset; so just + * bail. We'll pick this up next time. + */ + if (delta_cck < 0 || delta_ofdm < 0 || delta_ht < 0 || delta_ticks < 0) + return; + + /* + * delta_ticks is in ticks; we need to convert it up to milliseconds + * so we can do some useful math with it. + */ + delta_msec = ticks_to_msecs(delta_ticks); + + /* + * Calculate what our threshold is given the current delta_msec. + */ + thresh = sc->base_params->plcp_err_threshold * delta_msec; + + DPRINTF(sc, IWN_DEBUG_STATE, + "%s: time delta: %d; cck=%d, ofdm=%d, ht=%d, total=%d, thresh=%d\n", + __func__, + delta_msec, + delta_cck, + delta_ofdm, + delta_ht, + (delta_msec + delta_cck + delta_ofdm + delta_ht), + thresh); + + /* + * If we need a retune, then schedule a single channel scan + * to a channel that isn't the currently active one! + * + * The math from linux iwlwifi: + * + * if ((delta * 100 / msecs) > threshold) + */ + if (thresh > 0 && (delta_cck + delta_ofdm + delta_ht) * 100 > thresh) { + DPRINTF(sc, IWN_DEBUG_ANY, + "%s: PLCP error threshold raw (%d) comparison (%d) " + "over limit (%d); retune!\n", + __func__, + (delta_cck + delta_ofdm + delta_ht), + (delta_cck + delta_ofdm + delta_ht) * 100, + thresh); + } +} + +/* * Set STA mode power saving level (between 0 and 5). * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. */ @@ -5314,25 +6251,73 @@ iwn_send_advanced_btcoex(struct iwn_softc *sc) 0xc0004000, 0x00004000, 0xf0005000, 0xf0005000, }; struct iwn6000_btcoex_config btconfig; + struct iwn2000_btcoex_config btconfig2k; struct iwn_btcoex_priotable btprio; struct iwn_btcoex_prot btprot; int error, i; + uint8_t flags; memset(&btconfig, 0, sizeof btconfig); - btconfig.flags = 145; - btconfig.max_kill = 5; - btconfig.bt3_t7_timer = 1; - btconfig.kill_ack = htole32(0xffff0000); - btconfig.kill_cts = htole32(0xffff0000); - btconfig.sample_time = 2; - btconfig.bt3_t2_timer = 0xc; - for (i = 0; i < 12; i++) - btconfig.lookup_table[i] = htole32(btcoex_3wire[i]); - btconfig.valid = htole16(0xff); - btconfig.prio_boost = 0xf0; - DPRINTF(sc, IWN_DEBUG_RESET, - "%s: configuring advanced bluetooth coexistence\n", __func__); - error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, sizeof(btconfig), 1); + memset(&btconfig2k, 0, sizeof btconfig2k); + + flags = IWN_BT_FLAG_COEX6000_MODE_3W << + IWN_BT_FLAG_COEX6000_MODE_SHIFT; // Done as is in linux kernel 3.2 + + if (sc->base_params->bt_sco_disable) + flags &= ~IWN_BT_FLAG_SYNC_2_BT_DISABLE; + else + flags |= IWN_BT_FLAG_SYNC_2_BT_DISABLE; + + flags |= IWN_BT_FLAG_COEX6000_CHAN_INHIBITION; + + /* Default flags result is 145 as old value */ + + /* + * Flags value has to be review. Values must change if we + * which to disable it + */ + if (sc->base_params->bt_session_2) { + btconfig2k.flags = flags; + btconfig2k.max_kill = 5; + btconfig2k.bt3_t7_timer = 1; + btconfig2k.kill_ack = htole32(0xffff0000); + btconfig2k.kill_cts = htole32(0xffff0000); + btconfig2k.sample_time = 2; + btconfig2k.bt3_t2_timer = 0xc; + + for (i = 0; i < 12; i++) + btconfig2k.lookup_table[i] = htole32(btcoex_3wire[i]); + btconfig2k.valid = htole16(0xff); + btconfig2k.prio_boost = htole32(0xf0); + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: configuring advanced bluetooth coexistence" + " session 2, flags : 0x%x\n", + __func__, + flags); + error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig2k, + sizeof(btconfig2k), 1); + } else { + btconfig.flags = flags; + btconfig.max_kill = 5; + btconfig.bt3_t7_timer = 1; + btconfig.kill_ack = htole32(0xffff0000); + btconfig.kill_cts = htole32(0xffff0000); + btconfig.sample_time = 2; + btconfig.bt3_t2_timer = 0xc; + + for (i = 0; i < 12; i++) + btconfig.lookup_table[i] = htole32(btcoex_3wire[i]); + btconfig.valid = htole16(0xff); + btconfig.prio_boost = 0xf0; + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: configuring advanced bluetooth coexistence," + " flags : 0x%x\n", + __func__, + flags); + error = iwn_cmd(sc, IWN_CMD_BT_COEX, &btconfig, + sizeof(btconfig), 1); + } + if (error != 0) return error; @@ -5387,13 +6372,32 @@ iwn_config(struct iwn_softc *sc) DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); - if (sc->hw_type == IWN_HW_REV_TYPE_6005) { - /* Set radio temperature sensor offset. */ + if ((sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET) + && (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2)) { + device_printf(sc->sc_dev,"%s: temp_offset and temp_offsetv2 are" + " exclusive each together. Review NIC config file. Conf" + " : 0x%08x Flags : 0x%08x \n", __func__, + sc->base_params->calib_need, + (IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET | + IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2)); + return (EINVAL); + } + + /* Compute temperature calib if needed. Will be send by send calib */ + if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET) { error = iwn5000_temp_offset_calib(sc); if (error != 0) { device_printf(sc->sc_dev, "%s: could not set temperature offset\n", __func__); - return error; + return (error); + } + } else if (sc->base_params->calib_need & IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2) { + error = iwn5000_temp_offset_calibv2(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not compute temperature offset v2\n", + __func__); + return (error); } } @@ -5409,9 +6413,10 @@ iwn_config(struct iwn_softc *sc) } /* Configure valid TX chains for >=5000 Series. */ - if (sc->hw_type != IWN_HW_REV_TYPE_4965) { + if (sc->hw_type != IWN_HW_REV_TYPE_4965 && + IWN_UCODE_API(sc->ucode_rev) > 1) { txmask = htole32(sc->txchainmask); - DPRINTF(sc, IWN_DEBUG_RESET, + DPRINTF(sc, IWN_DEBUG_RESET | IWN_DEBUG_XMIT, "%s: configuring valid TX chains 0x%x\n", __func__, txmask); error = iwn_cmd(sc, IWN5000_CMD_TX_ANT_CONFIG, &txmask, sizeof txmask, 0); @@ -5424,10 +6429,14 @@ iwn_config(struct iwn_softc *sc) } /* Configure bluetooth coexistence. */ - if (sc->sc_flags & IWN_FLAG_ADV_BTCOEX) + error = 0; + + /* Configure bluetooth coexistence if needed. */ + if (sc->base_params->bt_mode == IWN_BT_ADVANCED) error = iwn_send_advanced_btcoex(sc); - else + if (sc->base_params->bt_mode == IWN_BT_SIMPLE) error = iwn_send_btcoex(sc); + if (error != 0) { device_printf(sc->sc_dev, "%s: could not configure bluetooth coexistence, error %d\n", @@ -5463,12 +6472,29 @@ iwn_config(struct iwn_softc *sc) sc->rxon->ht_single_mask = 0xff; sc->rxon->ht_dual_mask = 0xff; sc->rxon->ht_triple_mask = 0xff; + /* + * In active association mode, ensure that + * all the receive chains are enabled. + * + * Since we're not yet doing SMPS, don't allow the + * number of idle RX chains to be less than the active + * number. + */ rxchain = IWN_RXCHAIN_VALID(sc->rxchainmask) | - IWN_RXCHAIN_MIMO_COUNT(2) | - IWN_RXCHAIN_IDLE_COUNT(2); + IWN_RXCHAIN_MIMO_COUNT(sc->nrxchains) | + IWN_RXCHAIN_IDLE_COUNT(sc->nrxchains); sc->rxon->rxchain = htole16(rxchain); + DPRINTF(sc, IWN_DEBUG_RESET | IWN_DEBUG_XMIT, + "%s: rxchainmask=0x%x, nrxchains=%d\n", + __func__, + sc->rxchainmask, + sc->nrxchains); DPRINTF(sc, IWN_DEBUG_RESET, "%s: setting configuration\n", __func__); + if (sc->sc_is_scanning) + device_printf(sc->sc_dev, + "%s: is_scanning set, before RXON\n", + __func__); error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 0); if (error != 0) { device_printf(sc->sc_dev, "%s: RXON command failed\n", @@ -5507,39 +6533,109 @@ iwn_config(struct iwn_softc *sc) return 0; } +static uint16_t +iwn_get_active_dwell_time(struct iwn_softc *sc, + struct ieee80211_channel *c, uint8_t n_probes) +{ + /* No channel? Default to 2GHz settings */ + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { + return (IWN_ACTIVE_DWELL_TIME_2GHZ + + IWN_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1)); + } + + /* 5GHz dwell time */ + return (IWN_ACTIVE_DWELL_TIME_5GHZ + + IWN_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1)); +} + /* - * Add an ssid element to a frame. + * Limit the total dwell time to 85% of the beacon interval. + * + * Returns the dwell time in milliseconds. */ -static uint8_t * -ieee80211_add_ssid(uint8_t *frm, const uint8_t *ssid, u_int len) +static uint16_t +iwn_limit_dwell(struct iwn_softc *sc, uint16_t dwell_time) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211vap *vap = NULL; + int bintval = 0; + + /* bintval is in TU (1.024mS) */ + if (! TAILQ_EMPTY(&ic->ic_vaps)) { + vap = TAILQ_FIRST(&ic->ic_vaps); + bintval = vap->iv_bss->ni_intval; + } + + /* + * If it's non-zero, we should calculate the minimum of + * it and the DWELL_BASE. + * + * XXX Yes, the math should take into account that bintval + * is 1.024mS, not 1mS.. + */ + if (bintval > 0) { + DPRINTF(sc, IWN_DEBUG_SCAN, + "%s: bintval=%d\n", + __func__, + bintval); + return (MIN(IWN_PASSIVE_DWELL_BASE, ((bintval * 85) / 100))); + } + + /* No association context? Default */ + return (IWN_PASSIVE_DWELL_BASE); +} + +static uint16_t +iwn_get_passive_dwell_time(struct iwn_softc *sc, struct ieee80211_channel *c) { - *frm++ = IEEE80211_ELEMID_SSID; - *frm++ = len; - memcpy(frm, ssid, len); - return frm + len; + uint16_t passive; + + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { + passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_2GHZ; + } else { + passive = IWN_PASSIVE_DWELL_BASE + IWN_PASSIVE_DWELL_TIME_5GHZ; + } + + /* Clamp to the beacon interval if we're associated */ + return (iwn_limit_dwell(sc, passive)); } static int -iwn_scan(struct iwn_softc *sc) +iwn_scan(struct iwn_softc *sc, struct ieee80211vap *vap, + struct ieee80211_scan_state *ss, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211_scan_state *ss = ic->ic_scan; /*XXX*/ - struct ieee80211_node *ni = ss->ss_vap->iv_bss; + struct ieee80211_node *ni = vap->iv_bss; struct iwn_scan_hdr *hdr; struct iwn_cmd_data *tx; struct iwn_scan_essid *essid; struct iwn_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; - struct ieee80211_channel *c; uint8_t *buf, *frm; uint16_t rxchain; uint8_t txant; int buflen, error; + int is_active; + uint16_t dwell_active, dwell_passive; + uint32_t extra, scan_service_time; DPRINTF(sc, IWN_DEBUG_TRACE, "->%s begin\n", __func__); + /* + * We are absolutely not allowed to send a scan command when another + * scan command is pending. + */ + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, "%s: called whilst scanning!\n", + __func__); + return (EAGAIN); + } + + /* Assign the scan channel */ + c = ic->ic_curchan; + sc->rxon = &sc->rx_on[IWN_RXON_BSS_CTX]; buf = malloc(IWN_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); if (buf == NULL) { @@ -5555,13 +6651,29 @@ iwn_scan(struct iwn_softc *sc) */ hdr->quiet_time = htole16(10); /* timeout in milliseconds */ hdr->quiet_threshold = htole16(1); /* min # of packets */ + /* + * Max needs to be greater than active and passive and quiet! + * It's also in microseconds! + */ + hdr->max_svc = htole32(250 * 1024); + + /* + * Reset scan: interval=100 + * Normal scan: interval=becaon interval + * suspend_time: 100 (TU) + * + */ + extra = (100 /* suspend_time */ / 100 /* beacon interval */) << 22; + //scan_service_time = extra | ((100 /* susp */ % 100 /* int */) * 1024); + scan_service_time = (4 << 22) | (100 * 1024); /* Hardcode for now! */ + hdr->pause_svc = htole32(scan_service_time); /* Select antennas for scanning. */ rxchain = IWN_RXCHAIN_VALID(sc->rxchainmask) | IWN_RXCHAIN_FORCE_MIMO_SEL(sc->rxchainmask) | IWN_RXCHAIN_DRIVER_FORCE; - if (IEEE80211_IS_CHAN_A(ic->ic_curchan) && + if (IEEE80211_IS_CHAN_A(c) && sc->hw_type == IWN_HW_REV_TYPE_4965) { /* Ant A must be avoided in 5GHz because of an HW bug. */ rxchain |= IWN_RXCHAIN_FORCE_SEL(IWN_ANT_B); @@ -5575,7 +6687,7 @@ iwn_scan(struct iwn_softc *sc) tx->id = sc->broadcast_id; tx->lifetime = htole32(IWN_LIFETIME_INFINITE); - if (IEEE80211_IS_CHAN_5GHZ(ic->ic_curchan)) { + if (IEEE80211_IS_CHAN_5GHZ(c)) { /* Send probe requests at 6Mbps. */ tx->rate = htole32(0xd); rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; @@ -5594,12 +6706,35 @@ iwn_scan(struct iwn_softc *sc) txant = IWN_LSB(sc->txchainmask); tx->rate |= htole32(IWN_RFLAG_ANT(txant)); + /* + * Only do active scanning if we're announcing a probe request + * for a given SSID (or more, if we ever add it to the driver.) + */ + is_active = 0; + + /* + * If we're scanning for a specific SSID, add it to the command. + * + * XXX maybe look at adding support for scanning multiple SSIDs? + */ essid = (struct iwn_scan_essid *)(tx + 1); - if (ss->ss_ssid[0].len != 0) { - essid[0].id = IEEE80211_ELEMID_SSID; - essid[0].len = ss->ss_ssid[0].len; - memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); + if (ss != NULL) { + if (ss->ss_ssid[0].len != 0) { + essid[0].id = IEEE80211_ELEMID_SSID; + essid[0].len = ss->ss_ssid[0].len; + memcpy(essid[0].data, ss->ss_ssid[0].ssid, ss->ss_ssid[0].len); + } + + DPRINTF(sc, IWN_DEBUG_SCAN, "%s: ssid_len=%d, ssid=%*s\n", + __func__, + ss->ss_ssid[0].len, + ss->ss_ssid[0].len, + ss->ss_ssid[0].ssid); + + if (ss->ss_nssid > 0) + is_active = 1; } + /* * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. @@ -5625,53 +6760,96 @@ iwn_scan(struct iwn_softc *sc) /* Set length of probe request. */ tx->len = htole16(frm - (uint8_t *)wh); - c = ic->ic_curchan; + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + * + * This was fixed in later versions along with some other + * scan changes, and the threshold behaves as a flag in those + * versions. + */ + + /* + * If we're doing active scanning, set the crc_threshold + * to a suitable value. This is different to active veruss + * passive scanning depending upon the channel flags; the + * firmware will obey that particular check for us. + */ + if (sc->tlv_feature_flags & IWN_UCODE_TLV_FLAGS_NEWSCAN) + hdr->crc_threshold = is_active ? + IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_DISABLED; + else + hdr->crc_threshold = is_active ? + IWN_GOOD_CRC_TH_DEFAULT : IWN_GOOD_CRC_TH_NEVER; + chan = (struct iwn_scan_chan *)frm; chan->chan = htole16(ieee80211_chan2ieee(ic, c)); chan->flags = 0; if (ss->ss_nssid > 0) chan->flags |= htole32(IWN_CHAN_NPBREQS(1)); chan->dsp_gain = 0x6e; - if (IEEE80211_IS_CHAN_5GHZ(c) && - !(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->rf_gain = 0x3b; - chan->active = htole16(24); - chan->passive = htole16(110); + + /* + * Set the passive/active flag depending upon the channel mode. + * XXX TODO: take the is_active flag into account as well? + */ + if (c->ic_flags & IEEE80211_CHAN_PASSIVE) + chan->flags |= htole32(IWN_CHAN_PASSIVE); + else chan->flags |= htole32(IWN_CHAN_ACTIVE); - } else if (IEEE80211_IS_CHAN_5GHZ(c)) { + + /* + * Calculate the active/passive dwell times. + */ + + dwell_active = iwn_get_active_dwell_time(sc, c, ss->ss_nssid); + dwell_passive = iwn_get_passive_dwell_time(sc, c); + + /* Make sure they're valid */ + if (dwell_passive <= dwell_active) + dwell_passive = dwell_active + 1; + + chan->active = htole16(dwell_active); + chan->passive = htole16(dwell_passive); + + if (IEEE80211_IS_CHAN_5GHZ(c)) chan->rf_gain = 0x3b; - chan->active = htole16(24); - if (sc->rxon->associd) - chan->passive = htole16(78); - else - chan->passive = htole16(110); - hdr->crc_threshold = 0xffff; - } else if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->rf_gain = 0x28; - chan->active = htole16(36); - chan->passive = htole16(120); - chan->flags |= htole32(IWN_CHAN_ACTIVE); - } else { + else chan->rf_gain = 0x28; - chan->active = htole16(36); - if (sc->rxon->associd) - chan->passive = htole16(88); - else - chan->passive = htole16(120); - hdr->crc_threshold = 0xffff; - } DPRINTF(sc, IWN_DEBUG_STATE, "%s: chan %u flags 0x%x rf_gain 0x%x " - "dsp_gain 0x%x active 0x%x passive 0x%x\n", __func__, + "dsp_gain 0x%x active %d passive %d scan_svc_time %d crc 0x%x " + "isactive=%d numssid=%d\n", __func__, chan->chan, chan->flags, chan->rf_gain, chan->dsp_gain, - chan->active, chan->passive); + dwell_active, dwell_passive, scan_service_time, + hdr->crc_threshold, is_active, ss->ss_nssid); hdr->nchan++; chan++; buflen = (uint8_t *)chan - buf; hdr->len = htole16(buflen); + if (sc->sc_is_scanning) { + device_printf(sc->sc_dev, + "%s: called with is_scanning set!\n", + __func__); + } + sc->sc_is_scanning = 1; + DPRINTF(sc, IWN_DEBUG_STATE, "sending scan command nchan=%d\n", hdr->nchan); error = iwn_cmd(sc, IWN_CMD_SCAN, buf, buflen, 1); @@ -5712,12 +6890,16 @@ iwn_auth(struct iwn_softc *sc, struct ieee80211vap *vap) sc->rxon->ofdm_mask = 0; } else { /* Assume 802.11b/g. */ - sc->rxon->cck_mask = 0x0f; + sc->rxon->cck_mask = 0x03; sc->rxon->ofdm_mask = 0x15; } DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", sc->rxon->chan, sc->rxon->flags, sc->rxon->cck_mask, sc->rxon->ofdm_mask); + if (sc->sc_is_scanning) + device_printf(sc->sc_dev, + "%s: is_scanning set, before RXON\n", + __func__); error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, "%s: RXON command failed, error %d\n", @@ -5813,6 +6995,10 @@ iwn_run(struct iwn_softc *sc, struct ieee80211vap *vap) sc->rxon->filter |= htole32(IWN_FILTER_BSS); DPRINTF(sc, IWN_DEBUG_STATE, "rxon chan %d flags %x\n", sc->rxon->chan, sc->rxon->flags); + if (sc->sc_is_scanning) + device_printf(sc->sc_dev, + "%s: is_scanning set, before RXON\n", + __func__); error = iwn_cmd(sc, IWN_CMD_RXON, sc->rxon, sc->rxonsz, 1); if (error != 0) { device_printf(sc->sc_dev, @@ -6211,10 +7397,10 @@ iwn5000_query_calibration(struct iwn_softc *sc) int error; memset(&cmd, 0, sizeof cmd); - cmd.ucode.once.enable = 0xffffffff; - cmd.ucode.once.start = 0xffffffff; - cmd.ucode.once.send = 0xffffffff; - cmd.ucode.flags = 0xffffffff; + cmd.ucode.once.enable = htole32(0xffffffff); + cmd.ucode.once.start = htole32(0xffffffff); + cmd.ucode.once.send = htole32(0xffffffff); + cmd.ucode.flags = htole32(0xffffffff); DPRINTF(sc, IWN_DEBUG_CALIBRATE, "%s: sending calibration query\n", __func__); error = iwn_cmd(sc, IWN5000_CMD_CALIB_CONFIG, &cmd, sizeof cmd, 0); @@ -6236,9 +7422,20 @@ iwn5000_send_calibration(struct iwn_softc *sc) { int idx, error; - for (idx = 0; idx < 5; idx++) { - if (sc->calibcmd[idx].buf == NULL) - continue; /* No results available. */ + for (idx = 0; idx < IWN5000_PHY_CALIB_MAX_RESULT; idx++) { + if (!(sc->base_params->calib_need & (1<<idx))) { + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "No need of calib %d\n", + idx); + continue; /* no need for this calib */ + } + if (sc->calibcmd[idx].buf == NULL) { + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "Need calib idx : %d but no available data\n", + idx); + continue; + } + DPRINTF(sc, IWN_DEBUG_CALIBRATE, "send calibration result idx=%d len=%d\n", idx, sc->calibcmd[idx].len); @@ -6259,7 +7456,7 @@ iwn5000_send_wimax_coex(struct iwn_softc *sc) { struct iwn5000_wimax_coex wimax; -#ifdef notyet +#if 0 if (sc->hw_type == IWN_HW_REV_TYPE_6050) { /* Enable WiMAX coexistence for combo adapters. */ wimax.flags = @@ -6315,6 +7512,33 @@ iwn5000_temp_offset_calib(struct iwn_softc *sc) return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); } +static int +iwn5000_temp_offset_calibv2(struct iwn_softc *sc) +{ + struct iwn5000_phy_calib_temp_offsetv2 cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.code = IWN5000_PHY_CALIB_TEMP_OFFSET; + cmd.ngroups = 1; + cmd.isvalid = 1; + if (sc->eeprom_temp != 0) { + cmd.offset_low = htole16(sc->eeprom_temp); + cmd.offset_high = htole16(sc->eeprom_temp_high); + } else { + cmd.offset_low = htole16(IWN_DEFAULT_TEMP_OFFSET); + cmd.offset_high = htole16(IWN_DEFAULT_TEMP_OFFSET); + } + cmd.burnt_voltage_ref = htole16(sc->eeprom_voltage); + + DPRINTF(sc, IWN_DEBUG_CALIBRATE, + "setting radio sensor low offset to %d, high offset to %d, voltage to %d\n", + le16toh(cmd.offset_low), + le16toh(cmd.offset_high), + le16toh(cmd.burnt_voltage_ref)); + + return iwn_cmd(sc, IWN_CMD_PHY_CALIB, &cmd, sizeof cmd, 0); +} + /* * This function is called after the runtime firmware notifies us of its * readiness (called in a process context). @@ -6400,7 +7624,10 @@ iwn5000_post_alive(struct iwn_softc *sc) IWN_SETBITS(sc, IWN_FH_TX_CHICKEN, IWN_FH_TX_CHICKEN_SCHED_RETRY); /* Enable chain mode for all queues, except command queue. */ - iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); + if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) + iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffdf); + else + iwn_prph_write(sc, IWN5000_SCHED_QCHAIN_SEL, 0xfffef); iwn_prph_write(sc, IWN5000_SCHED_AGGR_SEL, 0); for (qid = 0; qid < IWN5000_NTXQUEUES; qid++) { @@ -6421,10 +7648,20 @@ iwn5000_post_alive(struct iwn_softc *sc) iwn_prph_write(sc, IWN5000_SCHED_TXFACT, 0xff); /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ - for (qid = 0; qid < 7; qid++) { - static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; - iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), - IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]); + if (sc->sc_flags & IWN_FLAG_PAN_SUPPORT) { + /* Mark TX rings as active. */ + for (qid = 0; qid < 11; qid++) { + static uint8_t qid2fifo[] = { 3, 2, 1, 0, 0, 4, 2, 5, 4, 7, 5 }; + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]); + } + } else { + /* Mark TX rings (4 EDCA + cmd + 2 HCCA) as active. */ + for (qid = 0; qid < 7; qid++) { + static uint8_t qid2fifo[] = { 3, 2, 1, 0, 7, 5, 6 }; + iwn_prph_write(sc, IWN5000_SCHED_QUEUE_STATUS(qid), + IWN5000_TXQ_STATUS_ACTIVE | qid2fifo[qid]); + } } iwn_nic_unlock(sc); @@ -6669,6 +7906,8 @@ iwn_read_firmware_leg(struct iwn_softc *sc, struct iwn_fw_info *fw) ptr = (const uint32_t *)fw->data; rev = le32toh(*ptr++); + sc->ucode_rev = rev; + /* Check firmware API version. */ if (IWN_FW_API(rev) <= 1) { device_printf(sc->sc_dev, @@ -6734,6 +7973,7 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, } DPRINTF(sc, IWN_DEBUG_RESET, "FW: \"%.64s\", build 0x%x\n", hdr->descr, le32toh(hdr->build)); + sc->ucode_rev = le32toh(hdr->rev); /* * Select the closest supported alternative that is less than @@ -6789,7 +8029,7 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, sc->sc_flags |= IWN_FLAG_ENH_SENS; break; case IWN_FW_TLV_PHY_CALIB: - tmp = htole32(*ptr); + tmp = le32toh(*ptr); if (tmp < 253) { sc->reset_noise_gain = tmp; sc->noise_gain = tmp + 1; @@ -6800,8 +8040,16 @@ iwn_read_firmware_tlv(struct iwn_softc *sc, struct iwn_fw_info *fw, DPRINTF(sc, IWN_DEBUG_RESET, "PAN Support found: %d\n", 1); break; - case IWN_FW_TLV_FLAGS : - sc->tlv_feature_flags = htole32(*ptr); + case IWN_FW_TLV_FLAGS: + if (len < sizeof(uint32_t)) + break; + if (len % sizeof(uint32_t)) + break; + sc->tlv_feature_flags = le32toh(*ptr); + DPRINTF(sc, IWN_DEBUG_RESET, + "%s: feature: 0x%08x\n", + __func__, + sc->tlv_feature_flags); break; case IWN_FW_TLV_PBREQ_MAXLEN: case IWN_FW_TLV_RUNT_EVTLOG_PTR: @@ -6873,6 +8121,8 @@ iwn_read_firmware(struct iwn_softc *sc) return error; } + device_printf(sc->sc_dev, "%s: ucode rev=0x%08x\n", __func__, sc->ucode_rev); + /* Make sure text and data sections fit in hardware memory. */ if (fw->main.textsz > sc->fw_text_maxsz || fw->main.datasz > sc->fw_data_maxsz || @@ -6937,9 +8187,8 @@ iwn_apm_init(struct iwn_softc *sc) else IWN_CLRBITS(sc, IWN_GIO, IWN_GIO_L0S_ENA); - if (sc->hw_type != IWN_HW_REV_TYPE_4965 && - sc->hw_type <= IWN_HW_REV_TYPE_1000) - IWN_SETBITS(sc, IWN_ANA_PLL, IWN_ANA_PLL_INIT); + if (sc->base_params->pll_cfg_val) + IWN_SETBITS(sc, IWN_ANA_PLL, sc->base_params->pll_cfg_val); /* Wait for clock stabilization before accessing prph. */ if ((error = iwn_clock_wait(sc)) != 0) @@ -7051,13 +8300,13 @@ iwn5000_nic_config(struct iwn_softc *sc) /* Use internal power amplifier only. */ IWN_WRITE(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_RADIO_2X2_IPA); } - if ((sc->hw_type == IWN_HW_REV_TYPE_6050 || - sc->hw_type == IWN_HW_REV_TYPE_6005) && sc->calib_ver >= 6) { + if (sc->base_params->additional_nic_config && sc->calib_ver >= 6) { /* Indicate that ROM calibration version is >=6. */ IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_CALIB_VER6); } - if (sc->hw_type == IWN_HW_REV_TYPE_6005) - IWN_SETBITS(sc, IWN_GP_DRIVER, IWN_GP_DRIVER_6050_1X2); + if (sc->base_params->additional_gp_drv_bit) + IWN_SETBITS(sc, IWN_GP_DRIVER, + sc->base_params->additional_gp_drv_bit); return 0; } @@ -7192,7 +8441,7 @@ iwn_hw_init(struct iwn_softc *sc) IWN_WRITE(sc, IWN_UCODE_GP1_CLR, IWN_UCODE_GP1_RFKILL); /* Enable shadow registers. */ - if (sc->hw_type >= IWN_HW_REV_TYPE_6000) + if (sc->base_params->shadow_reg_enable) IWN_SETBITS(sc, IWN_SHADOW_REG_CTRL, 0x800fffff); if ((error = ops->load_firmware(sc)) != 0) { @@ -7305,6 +8554,44 @@ iwn_radio_off(void *arg0, int pending) } static void +iwn_panicked(void *arg0, int pending) +{ + struct iwn_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + int error; + + if (vap == NULL) { + printf("%s: null vap\n", __func__); + return; + } + + device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " + "resetting...\n", __func__, vap->iv_state); + + IWN_LOCK(sc); + + iwn_stop_locked(sc); + iwn_init_locked(sc); + if (vap->iv_state >= IEEE80211_S_AUTH && + (error = iwn_auth(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to auth state\n", __func__); + } + if (vap->iv_state >= IEEE80211_S_RUN && + (error = iwn_run(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to run state\n", __func__); + } + + /* Only run start once the NIC is in a useful state, like associated */ + iwn_start_locked(sc->sc_ifp); + + IWN_UNLOCK(sc); +} + +static void iwn_init_locked(struct iwn_softc *sc) { struct ifnet *ifp = sc->sc_ifp; @@ -7396,6 +8683,7 @@ iwn_stop_locked(struct iwn_softc *sc) IWN_LOCK_ASSERT(sc); + sc->sc_is_scanning = 0; sc->sc_tx_timer = 0; callout_stop(&sc->watchdog_to); callout_stop(&sc->calib_to); @@ -7486,10 +8774,11 @@ iwn_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; struct iwn_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; int error; IWN_LOCK(sc); - error = iwn_scan(sc); + error = iwn_scan(sc, vap, ss, ic->ic_curchan); IWN_UNLOCK(sc); if (error != 0) ieee80211_cancel_scan(vap); @@ -7521,7 +8810,6 @@ iwn_hw_reset(void *arg0, int pending) } #ifdef IWN_DEBUG #define IWN_DESC(x) case x: return #x -#define COUNTOF(array) (sizeof(array) / sizeof(array[0])) /* * Translate CSR code to string @@ -7592,7 +8880,7 @@ iwn_debug_register(struct iwn_softc *sc) DPRINTF(sc, IWN_DEBUG_REGISTER, "CSR values: (2nd byte of IWN_INT_COALESCING is IWN_INT_PERIODIC)%s", "\n"); - for (i = 0; i < COUNTOF(csr_tbl); i++){ + for (i = 0; i < nitems(csr_tbl); i++){ DPRINTF(sc, IWN_DEBUG_REGISTER," %10s: 0x%08x ", iwn_get_csr_string(csr_tbl[i]), IWN_READ(sc, csr_tbl[i])); if ((i+1) % 3 == 0) diff --git a/sys/dev/iwn/if_iwn_chip_cfg.h b/sys/dev/iwn/if_iwn_chip_cfg.h new file mode 100644 index 0000000..ea6f3e1 --- /dev/null +++ b/sys/dev/iwn/if_iwn_chip_cfg.h @@ -0,0 +1,413 @@ +/*- + * Copyright (c) 2013 Cedric GROSS <cg@cgross.info> + * Copyright (c) 2011 Intel Corporation + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef __IF_IWN_CHIP_CFG_H__ +#define __IF_IWN_CHIP_CFG_H__ + +/* ========================================================================== + * NIC PARAMETERS + * + * ========================================================================== + */ + +/* + * Flags for managing calibration result. See calib_need + * in iwn_base_params struct + * + * These are bitmasks that determine which indexes in the calibcmd + * array are pushed up. + */ +#define IWN_FLG_NEED_PHY_CALIB_DC (1<<0) +#define IWN_FLG_NEED_PHY_CALIB_LO (1<<1) +#define IWN_FLG_NEED_PHY_CALIB_TX_IQ (1<<2) +#define IWN_FLG_NEED_PHY_CALIB_TX_IQ_PERIODIC (1<<3) +#define IWN_FLG_NEED_PHY_CALIB_BASE_BAND (1<<4) +/* + * These aren't (yet) included in the calibcmd array, but + * are used as flags for which calibrations to use. + * + * XXX I think they should be named differently and + * stuffed in a different member in the config struct! + */ +#define IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET (1<<5) +#define IWN_FLG_NEED_PHY_CALIB_CRYSTAL (1<<6) +#define IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2 (1<<7) + +/* + * Each chip has a different threshold for PLCP errors that should trigger a + * retune. + */ +#define IWN_PLCP_ERR_DEFAULT_THRESHOLD 50 +#define IWN_PLCP_ERR_LONG_THRESHOLD 100 +#define IWN_PLCP_ERR_EXT_LONG_THRESHOLD 200 + +/* + * Define some parameters for managing different NIC. + * Refer to linux specific file like iwl-xxxx.c to determine correct value + * for NIC. + * + * @max_ll_items: max number of OTP blocks + * @shadow_ram_support: shadow support for OTP memory + * @shadow_reg_enable: HW shadhow register bit + * @no_idle_support: do not support idle mode + * @advanced_bt_coexist : Advanced BT management + * @bt_session_2 : NIC need a new struct for configure BT coexistence. Needed + * only if advanced_bt_coexist is true + * @bt_sco_disable : + * @additional_nic_config: For 6005 series + * @iq_invert : ? But need it for N 2000 series + * @regulatory_bands : XXX + * @enhanced_TX_power : EEPROM Has advanced TX power options. Set 'True' + * if update_enhanced_txpower = iwl_eeprom_enhanced_txpower. + * See iwl-agn-devices.c file to determine that(enhanced_txpower) + * @need_temp_offset_calib : Need to compute some temp offset for calibration. + * @calib_need : Use IWN_FLG_NEED_PHY_CALIB_* flags to specify which + * calibration data ucode need. See calib_init_cfg in iwl-xxxx.c + * linux kernel file + * @support_hostap: Define IEEE80211_C_HOSTAP for ic_caps + * @no_multi_vaps: See iwn_vap_create + * @additional_gp_drv_bit : Specific bit to defined during nic_config + * @bt_mode: BT configuration mode + */ +enum bt_mode_enum { + IWN_BT_NONE, + IWN_BT_SIMPLE, + IWN_BT_ADVANCED +}; + +struct iwn_base_params { + uint32_t pll_cfg_val; + const uint16_t max_ll_items; +#define IWN_OTP_MAX_LL_ITEMS_1000 (3) /* OTP blocks for 1000 */ +#define IWN_OTP_MAX_LL_ITEMS_6x00 (4) /* OTP blocks for 6x00 */ +#define IWN_OTP_MAX_LL_ITEMS_6x50 (7) /* OTP blocks for 6x50 */ +#define IWN_OTP_MAX_LL_ITEMS_2x00 (4) /* OTP blocks for 2x00 */ + const bool shadow_ram_support; + const bool shadow_reg_enable; + const bool bt_session_2; + const bool bt_sco_disable; + const bool additional_nic_config; + const uint32_t *regulatory_bands; + const bool enhanced_TX_power; + const uint16_t calib_need; + const bool support_hostap; + const bool no_multi_vaps; + uint8_t additional_gp_drv_bit; + enum bt_mode_enum bt_mode; + uint32_t plcp_err_threshold; +}; + +static const struct iwn_base_params iwn5000_base_params = { + .pll_cfg_val = IWN_ANA_PLL_INIT, /* pll_cfg_val; */ + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, /* max_ll_items */ + .shadow_ram_support = false, /* shadow_ram_support */ + .shadow_reg_enable = false, /* shadow_reg_enable */ + .bt_session_2 = false, /* bt_session_2 */ + .bt_sco_disable = true, /* bt_sco_disable */ + .additional_nic_config = false, /* additional_nic_config */ + .regulatory_bands = iwn5000_regulatory_bands, /* regulatory_bands */ + .enhanced_TX_power = false, /* enhanced_TX_power */ + .calib_need = + ( IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ_PERIODIC + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND ), + .support_hostap = false, /* support_hostap */ + .no_multi_vaps = true, /* no_multi_vaps */ + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, /* additional_gp_drv_bit */ + .bt_mode = IWN_BT_NONE, /* bt_mode */ + .plcp_err_threshold = IWN_PLCP_ERR_LONG_THRESHOLD, +}; + +/* + * 4965 support + */ +static const struct iwn_base_params iwn4965_base_params = { + .pll_cfg_val = 0, /* pll_cfg_val; */ + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, /* max_ll_items - ignored for 4965 */ + .shadow_ram_support = true, /* shadow_ram_support */ + .shadow_reg_enable = false, /* shadow_reg_enable */ + .bt_session_2 = false, /* bt_session_2 XXX unknown? */ + .bt_sco_disable = true, /* bt_sco_disable XXX unknown? */ + .additional_nic_config = false, /* additional_nic_config - not for 4965 */ + .regulatory_bands = iwn5000_regulatory_bands, /* regulatory_bands */ + .enhanced_TX_power = false, /* enhanced_TX_power - not for 4965 */ + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ_PERIODIC + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND ), + .support_hostap = false, /* support_hostap - XXX should work on fixing! */ + .no_multi_vaps = true, /* no_multi_vaps - XXX should work on fixing! */ + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, /* additional_gp_drv_bit */ + .bt_mode = IWN_BT_SIMPLE, /* bt_mode */ + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + + +static const struct iwn_base_params iwn2000_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_2x00, + .shadow_ram_support = true, + .shadow_reg_enable = false, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = false, + .regulatory_bands = iwn2030_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND + | IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2 ), + .support_hostap = true, + .no_multi_vaps = false, + .additional_gp_drv_bit = IWN_GP_DRIVER_REG_BIT_RADIO_IQ_INVERT, + .bt_mode = IWN_BT_NONE, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + +static const struct iwn_base_params iwn2030_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_2x00, + .shadow_ram_support = true, + .shadow_reg_enable = false, /* XXX check? */ + .bt_session_2 = true, + .bt_sco_disable = true, + .additional_nic_config = false, + .regulatory_bands = iwn2030_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND + | IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSETv2 ), + .support_hostap = true, + .no_multi_vaps = false, + .additional_gp_drv_bit = IWN_GP_DRIVER_REG_BIT_RADIO_IQ_INVERT, + .bt_mode = IWN_BT_ADVANCED, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + +static const struct iwn_base_params iwn1000_base_params = { + .pll_cfg_val = IWN_ANA_PLL_INIT, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_1000, + .shadow_ram_support = false, + .shadow_reg_enable = false, /* XXX check? */ + .bt_session_2 = false, + .bt_sco_disable = false, + .additional_nic_config = false, + .regulatory_bands = iwn5000_regulatory_bands, + .enhanced_TX_power = false, + .calib_need = + ( IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ_PERIODIC + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND + ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, + /* XXX 1000 - no BT */ + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_EXT_LONG_THRESHOLD, +}; +static const struct iwn_base_params iwn_6000_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = false, + .additional_nic_config = false, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; +static const struct iwn_base_params iwn_6000i_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = false, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; +static const struct iwn_base_params iwn_6000g2_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = false, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND + | IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = 0, + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + +static const struct iwn_base_params iwn_6050_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x50, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = true, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; +static const struct iwn_base_params iwn_6150_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x50, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = true, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_6050_1X2, + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + +/* IWL_DEVICE_6035 & IWL_DEVICE_6030 */ +static const struct iwn_base_params iwn_6000g2b_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = false, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND + | IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, + .bt_mode = IWN_BT_ADVANCED, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + +/* + * 6235 series NICs. + */ +static const struct iwn_base_params iwn_6235_base_params = { + .pll_cfg_val = 0, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .shadow_reg_enable = true, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = true, + .regulatory_bands = iwn6000_regulatory_bands, + .enhanced_TX_power = true, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND + | IWN_FLG_NEED_PHY_CALIB_TEMP_OFFSET ), + .support_hostap = false, + .no_multi_vaps = true, + /* XXX 1x2? This NIC is 2x2, right? */ + .additional_gp_drv_bit = IWN_GP_DRIVER_6050_1X2, + .bt_mode = IWN_BT_ADVANCED, + .plcp_err_threshold = IWN_PLCP_ERR_DEFAULT_THRESHOLD, +}; + +static const struct iwn_base_params iwn_5x50_base_params = { + .pll_cfg_val = IWN_ANA_PLL_INIT, + .max_ll_items = IWN_OTP_MAX_LL_ITEMS_6x00, + .shadow_ram_support = true, + .shadow_reg_enable = false, + .bt_session_2 = false, + .bt_sco_disable = true, + .additional_nic_config = false, + .regulatory_bands = iwn5000_regulatory_bands, + .enhanced_TX_power =false, + .calib_need = + (IWN_FLG_NEED_PHY_CALIB_DC + | IWN_FLG_NEED_PHY_CALIB_LO + | IWN_FLG_NEED_PHY_CALIB_TX_IQ + | IWN_FLG_NEED_PHY_CALIB_BASE_BAND ), + .support_hostap = false, + .no_multi_vaps = true, + .additional_gp_drv_bit = IWN_GP_DRIVER_NONE, + .bt_mode = IWN_BT_SIMPLE, + .plcp_err_threshold = IWN_PLCP_ERR_LONG_THRESHOLD, +}; + +#endif /* __IF_IWN_CHIP_CFG_H__ */ diff --git a/sys/dev/iwn/if_iwn_debug.h b/sys/dev/iwn/if_iwn_debug.h new file mode 100644 index 0000000..2932c7e --- /dev/null +++ b/sys/dev/iwn/if_iwn_debug.h @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2013 Cedric GROSS <c.gross@kreiz-it.fr> + * Copyright (c) 2011 Intel Corporation + * Copyright (c) 2007-2009 + * Damien Bergamini <damien.bergamini@free.fr> + * Copyright (c) 2008 + * Benjamin Close <benjsc@FreeBSD.org> + * Copyright (c) 2008 Sam Leffler, Errno Consulting + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef __IF_IWN_DEBUG_H__ +#define __IF_IWN_DEBUG_H__ + +#ifdef IWN_DEBUG +enum { + IWN_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + IWN_DEBUG_RECV = 0x00000002, /* basic recv operation */ + IWN_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ + IWN_DEBUG_TXPOW = 0x00000008, /* tx power processing */ + IWN_DEBUG_RESET = 0x00000010, /* reset processing */ + IWN_DEBUG_OPS = 0x00000020, /* iwn_ops processing */ + IWN_DEBUG_BEACON = 0x00000040, /* beacon handling */ + IWN_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ + IWN_DEBUG_INTR = 0x00000100, /* ISR */ + IWN_DEBUG_CALIBRATE = 0x00000200, /* periodic calibration */ + IWN_DEBUG_NODE = 0x00000400, /* node management */ + IWN_DEBUG_LED = 0x00000800, /* led management */ + IWN_DEBUG_CMD = 0x00001000, /* cmd submission */ + IWN_DEBUG_TXRATE = 0x00002000, /* TX rate debugging */ + IWN_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */ + IWN_DEBUG_SCAN = 0x00008000, /* Scan related operations */ + IWN_DEBUG_STATS = 0x00010000, /* Statistics updates */ + IWN_DEBUG_REGISTER = 0x20000000, /* print chipset register */ + IWN_DEBUG_TRACE = 0x40000000, /* Print begin and start driver function */ + IWN_DEBUG_FATAL = 0x80000000, /* fatal errors */ + IWN_DEBUG_ANY = 0xffffffff +}; + +#define DPRINTF(sc, m, fmt, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(fmt, __VA_ARGS__); \ +} while (0) + +static const char * +iwn_intr_str(uint8_t cmd) +{ + switch (cmd) { + /* Notifications */ + case IWN_UC_READY: return "UC_READY"; + case IWN_ADD_NODE_DONE: return "ADD_NODE_DONE"; + case IWN_TX_DONE: return "TX_DONE"; + case IWN_START_SCAN: return "START_SCAN"; + case IWN_STOP_SCAN: return "STOP_SCAN"; + case IWN_RX_STATISTICS: return "RX_STATS"; + case IWN_BEACON_STATISTICS: return "BEACON_STATS"; + case IWN_STATE_CHANGED: return "STATE_CHANGED"; + case IWN_BEACON_MISSED: return "BEACON_MISSED"; + case IWN_RX_PHY: return "RX_PHY"; + case IWN_MPDU_RX_DONE: return "MPDU_RX_DONE"; + case IWN_RX_DONE: return "RX_DONE"; + + /* Command Notifications */ + case IWN_CMD_RXON: return "IWN_CMD_RXON"; + case IWN_CMD_RXON_ASSOC: return "IWN_CMD_RXON_ASSOC"; + case IWN_CMD_EDCA_PARAMS: return "IWN_CMD_EDCA_PARAMS"; + case IWN_CMD_TIMING: return "IWN_CMD_TIMING"; + case IWN_CMD_LINK_QUALITY: return "IWN_CMD_LINK_QUALITY"; + case IWN_CMD_SET_LED: return "IWN_CMD_SET_LED"; + case IWN5000_CMD_WIMAX_COEX: return "IWN5000_CMD_WIMAX_COEX"; + case IWN5000_CMD_CALIB_CONFIG: return "IWN5000_CMD_CALIB_CONFIG"; + case IWN5000_CMD_CALIB_RESULT: return "IWN5000_CMD_CALIB_RESULT"; + case IWN5000_CMD_CALIB_COMPLETE: return "IWN5000_CMD_CALIB_COMPLETE"; + case IWN_CMD_SET_POWER_MODE: return "IWN_CMD_SET_POWER_MODE"; + case IWN_CMD_SCAN: return "IWN_CMD_SCAN"; + case IWN_CMD_SCAN_RESULTS: return "IWN_CMD_SCAN_RESULTS"; + case IWN_CMD_TXPOWER: return "IWN_CMD_TXPOWER"; + case IWN_CMD_TXPOWER_DBM: return "IWN_CMD_TXPOWER_DBM"; + case IWN5000_CMD_TX_ANT_CONFIG: return "IWN5000_CMD_TX_ANT_CONFIG"; + case IWN_CMD_BT_COEX: return "IWN_CMD_BT_COEX"; + case IWN_CMD_SET_CRITICAL_TEMP: return "IWN_CMD_SET_CRITICAL_TEMP"; + case IWN_CMD_SET_SENSITIVITY: return "IWN_CMD_SET_SENSITIVITY"; + case IWN_CMD_PHY_CALIB: return "IWN_CMD_PHY_CALIB"; + + /* Bluetooth commands */ + case IWN_CMD_BT_COEX_PRIOTABLE: return "IWN_CMD_BT_COEX_PRIOTABLE"; + case IWN_CMD_BT_COEX_PROT: return "IWN_CMD_BT_COEX_PROT"; + case IWN_CMD_BT_COEX_NOTIF: return "IWN_CMD_BT_COEX_NOTIF"; + + /* PAN commands */ + case IWN_CMD_WIPAN_PARAMS: return "IWN_CMD_WIPAN_PARAMS"; + case IWN_CMD_WIPAN_RXON: return "IWN_CMD_WIPAN_RXON"; + case IWN_CMD_WIPAN_RXON_TIMING: return "IWN_CMD_WIPAN_RXON_TIMING"; + case IWN_CMD_WIPAN_RXON_ASSOC: return "IWN_CMD_WIPAN_RXON_ASSOC"; + case IWN_CMD_WIPAN_QOS_PARAM: return "IWN_CMD_WIPAN_QOS_PARAM"; + case IWN_CMD_WIPAN_WEPKEY: return "IWN_CMD_WIPAN_WEPKEY"; + case IWN_CMD_WIPAN_P2P_CHANNEL_SWITCH: + return "IWN_CMD_WIPAN_P2P_CHANNEL_SWITCH"; + case IWN_CMD_WIPAN_NOA_NOTIFICATION: + return "IWN_CMD_WIPAN_NOA_NOTIFICATION"; + case IWN_CMD_WIPAN_DEACTIVATION_COMPLETE: + return "IWN_CMD_WIPAN_DEACTIVATION_COMPLETE"; + } + return "UNKNOWN INTR NOTIF/CMD"; +} +#else +#define DPRINTF(sc, m, fmt, ...) do { (void) sc; } while (0) +#endif + +#endif /* __IF_IWN_DEBUG_H__ */ diff --git a/sys/dev/iwn/if_iwn_devid.h b/sys/dev/iwn/if_iwn_devid.h index 23fd0a1..dd08737 100644 --- a/sys/dev/iwn/if_iwn_devid.h +++ b/sys/dev/iwn/if_iwn_devid.h @@ -41,6 +41,20 @@ * DEVICE ID BLOCK * ========================================================================== */ + +/* + * -------------------------------------------------------------------------- + * Device ID for 2x00 series + * -------------------------------------------------------------------------- + */ +#define IWN_DID_2x00_1 0x0890 +#define IWN_DID_2x00_2 0x0891 +/* SubDevice ID */ +#define IWN_SDID_2x00_1 0x4022 +#define IWN_SDID_2x00_2 0x4222 +#define IWN_SDID_2x00_3 0x4422 +#define IWN_SDID_2x00_4 0x4822 + /* * -------------------------------------------------------------------------- * Device ID for 2x30 series @@ -214,6 +228,31 @@ /* * -------------------------------------------------------------------------- + * Device ID for 105 Series + * -------------------------------------------------------------------------- + */ +#define IWN_DID_105_1 0x0894 +#define IWN_DID_105_2 0x0895 +/* SubDevice ID */ +#define IWN_SDID_105_1 0x0022 +#define IWN_SDID_105_2 0x0222 +#define IWN_SDID_105_3 0x0422 +#define IWN_SDID_105_4 0x0822 + +/* + * -------------------------------------------------------------------------- + * Device ID for 135 Series + * -------------------------------------------------------------------------- + */ +#define IWN_DID_135_1 0x0892 +#define IWN_DID_135_2 0x0893 +/* SubDevice ID */ +#define IWN_SDID_135_1 0x0062 +#define IWN_SDID_135_2 0x0262 +#define IWN_SDID_135_3 0x0462 + +/* + * -------------------------------------------------------------------------- * Device ID for 5x00 Series * -------------------------------------------------------------------------- */ diff --git a/sys/dev/iwn/if_iwn_ioctl.h b/sys/dev/iwn/if_iwn_ioctl.h new file mode 100644 index 0000000..1acf464 --- /dev/null +++ b/sys/dev/iwn/if_iwn_ioctl.h @@ -0,0 +1,25 @@ +/*- + * Copyright (c) 2014 Adrian Chadd <adrian@FreeBSD.org> + * + * 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. + * + * $FreeBSD$ + */ +#ifndef __IF_IWN_IOCTL_H__ +#define __IF_IWN_IOCTL_H__ + +/* XXX how should I pick appropriate ioctl numbers? */ +#define SIOCGIWNSTATS _IOWR('i', 145, struct ifreq) +#define SIOCZIWNSTATS _IOWR('i', 146, struct ifreq) + +#endif /* __IF_IWN_IOCTL_H__ */ diff --git a/sys/dev/iwn/if_iwnreg.h b/sys/dev/iwn/if_iwnreg.h index e61d0fd..ed65c0b 100644 --- a/sys/dev/iwn/if_iwnreg.h +++ b/sys/dev/iwn/if_iwnreg.h @@ -17,6 +17,8 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifndef __IF_IWNREG_H__ +#define __IF_IWNREG_H__ #define IWN_CT_KILL_THRESHOLD 114 /* in Celsius */ #define IWN_CT_KILL_EXIT_THRESHOLD 95 /* in Celsius */ @@ -222,6 +224,7 @@ #define IWN_GP_DRIVER_CALIB_VER6 (1 << 2) #define IWN_GP_DRIVER_6050_1X2 (1 << 3) #define IWN_GP_DRIVER_REG_BIT_RADIO_IQ_INVERT (1 << 7) +#define IWN_GP_DRIVER_NONE 0 /* Possible flags for register IWN_UCODE_GP1_CLR. */ #define IWN_UCODE_GP1_RFKILL (1 << 1) @@ -486,6 +489,7 @@ struct iwn_tx_cmd { #define IWN_CMD_TXPOWER_DBM 149 #define IWN_CMD_TXPOWER 151 #define IWN5000_CMD_TX_ANT_CONFIG 152 +#define IWN_CMD_TXPOWER_DBM_V1 152 #define IWN_CMD_BT_COEX 155 #define IWN_CMD_GET_STATISTICS 156 #define IWN_CMD_SET_CRITICAL_TEMP 164 @@ -882,7 +886,7 @@ struct iwn_scan_essid { struct iwn_scan_hdr { uint16_t len; - uint8_t reserved1; + uint8_t scan_flags; uint8_t nchan; uint16_t quiet_time; uint16_t quiet_threshold; @@ -919,17 +923,53 @@ struct iwn_scan_chan { /* Maximum size of a scan command. */ #define IWN_SCAN_MAXSZ (MCLBYTES - 4) -#define IWN_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ -#define IWN_ACTIVE_DWELL_TIME_52 (20) -#define IWN_ACTIVE_DWELL_FACTOR_24 (3) -#define IWN_ACTIVE_DWELL_FACTOR_52 (2) +/* + * For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after + * sending probe req. This should be set long enough to hear probe responses + * from more than one AP. + */ +#define IWN_ACTIVE_DWELL_TIME_2GHZ (30) /* all times in msec */ +#define IWN_ACTIVE_DWELL_TIME_5GHZ (20) +#define IWN_ACTIVE_DWELL_FACTOR_2GHZ (3) +#define IWN_ACTIVE_DWELL_FACTOR_5GHZ (2) -#define IWN_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ -#define IWN_PASSIVE_DWELL_TIME_52 (10) +/* + * For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. + * Must be set longer than active dwell time. + * For the most reliable scan, set > AP beacon interval (typically 100msec). + */ +#define IWN_PASSIVE_DWELL_TIME_2GHZ (20) /* all times in msec */ +#define IWN_PASSIVE_DWELL_TIME_5GHZ (10) #define IWN_PASSIVE_DWELL_BASE (100) #define IWN_CHANNEL_TUNE_TIME (5) #define IWN_SCAN_CHAN_TIMEOUT 2 +#define IWN_MAX_SCAN_CHANNEL 50 + +/* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + * + * This was fixed in later versions along with some other + * scan changes, and the threshold behaves as a flag in those + * versions. + */ +#define IWN_GOOD_CRC_TH_DISABLED 0 +#define IWN_GOOD_CRC_TH_DEFAULT htole16(1) +#define IWN_GOOD_CRC_TH_NEVER htole16(0xffff) /* Structure for command IWN_CMD_TXPOWER (4965AGN only.) */ #define IWN_RIDX_MAX 32 @@ -1102,6 +1142,12 @@ struct iwn_enhanced_sensitivity_cmd { uint16_t reserved; } __packed; +/* + * Define maximal number of calib result send to runtime firmware + * PS: TEMP_OFFSET count for 2 (std and v2) + */ +#define IWN5000_PHY_CALIB_MAX_RESULT 8 + /* Structures for command IWN_CMD_PHY_CALIB. */ struct iwn_phy_calib { uint8_t code; @@ -1221,17 +1267,91 @@ struct iwn_ucode_info { } __packed; /* Structures for IWN_TX_DONE notification. */ -#define IWN_TX_STATUS_MSK 0xff -#define TX_STATUS_SUCCESS 0x01 -#define TX_STATUS_DIRECT_DONE 0x02 - -#define IWN_TX_SUCCESS 0x00 -#define IWN_TX_FAIL 0x80 /* all failures have 0x80 set */ -#define IWN_TX_FAIL_SHORT_LIMIT 0x82 /* too many RTS retries */ -#define IWN_TX_FAIL_LONG_LIMIT 0x83 /* too many retries */ -#define IWN_TX_FAIL_FIFO_UNDERRRUN 0x84 /* tx fifo not kept running */ -#define IWN_TX_FAIL_DEST_IN_PS 0x88 /* sta found in power save */ -#define IWN_TX_FAIL_TX_LOCKED 0x90 /* waiting to see traffic */ + +/* + * TX command response is sent after *agn* transmission attempts. + * + * both postpone and abort status are expected behavior from uCode. there is + * no special operation required from driver; except for RFKILL_FLUSH, + * which required tx flush host command to flush all the tx frames in queues + */ +#define IWN_TX_STATUS_MSK 0x000000ff +#define IWN_TX_STATUS_DELAY_MSK 0x00000040 +#define IWN_TX_STATUS_ABORT_MSK 0x00000080 +#define IWN_TX_PACKET_MODE_MSK 0x0000ff00 +#define IWN_TX_FIFO_NUMBER_MSK 0x00070000 +#define IWN_TX_RESERVED 0x00780000 +#define IWN_TX_POWER_PA_DETECT_MSK 0x7f800000 +#define IWN_TX_ABORT_REQUIRED_MSK 0x80000000 + +/* Success status */ +#define IWN_TX_STATUS_SUCCESS 0x01 +#define IWN_TX_STATUS_DIRECT_DONE 0x02 + +/* postpone TX */ +#define IWN_TX_STATUS_POSTPONE_DELAY 0x40 +#define IWN_TX_STATUS_POSTPONE_FEW_BYTES 0x41 +#define IWN_TX_STATUS_POSTPONE_BT_PRIO 0x42 +#define IWN_TX_STATUS_POSTPONE_QUIET_PERIOD 0x43 +#define IWN_TX_STATUS_POSTPONE_CALC_TTAK 0x44 + +/* Failures */ +#define IWN_TX_FAIL 0x80 /* all failures have 0x80 set */ +#define IWN_TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY 0x81 +#define IWN_TX_FAIL_SHORT_LIMIT 0x82 /* too many RTS retries */ +#define IWN_TX_FAIL_LONG_LIMIT 0x83 /* too many retries */ +#define IWN_TX_FAIL_FIFO_UNDERRRUN 0x84 /* tx fifo not kept running */ +#define IWN_TX_STATUS_FAIL_DRAIN_FLOW 0x85 +#define IWN_TX_STATUS_FAIL_RFKILL_FLUSH 0x86 +#define IWN_TX_STATUS_FAIL_LIFE_EXPIRE 0x87 +#define IWN_TX_FAIL_DEST_IN_PS 0x88 /* sta found in power save */ +#define IWN_TX_STATUS_FAIL_HOST_ABORTED 0x89 +#define IWN_TX_STATUS_FAIL_BT_RETRY 0x8a +#define IWN_TX_FAIL_STA_INVALID 0x8b /* XXX STA invalid (???) */ +#define IWN_TX_STATUS_FAIL_FRAG_DROPPED 0x8c +#define IWN_TX_STATUS_FAIL_TID_DISABLE 0x8d +#define IWN_TX_STATUS_FAIL_FIFO_FLUSHED 0x8e +#define IWN_TX_STATUS_FAIL_INSUFFICIENT_CF_POLL 0x8f +#define IWN_TX_FAIL_TX_LOCKED 0x90 /* waiting to see traffic */ +#define IWN_TX_STATUS_FAIL_NO_BEACON_ON_RADAR 0x91 + +/* + * TX command response for A-MPDU packet responses. + * + * The status response is different to the non A-MPDU responses. + * In addition, the sequence number is treated as the sequence + * number of the TX command, NOT the 802.11 sequence number! + */ +#define IWN_AGG_TX_STATE_TRANSMITTED 0x00 +#define IWN_AGG_TX_STATE_UNDERRUN_MSK 0x01 +#define IWN_AGG_TX_STATE_FEW_BYTES_MSK 0x04 +#define IWN_AGG_TX_STATE_ABORT_MSK 0x08 + +#define IWN_AGG_TX_STATE_LAST_SENT_TTL_MSK 0x10 +#define IWN_AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK 0x20 + +#define IWN_AGG_TX_STATE_SCD_QUERY_MSK 0x80 + +#define IWN_AGG_TX_STATE_TEST_BAD_CRC32_MSK 0x100 + +#define IWN_AGG_TX_STATE_RESPONSE_MSK 0x1ff +#define IWN_AGG_TX_STATE_DUMP_TX_MSK 0x200 +#define IWN_AGG_TX_STATE_DELAY_TX_MSK 0x400 + +#define IWN_AGG_TX_STATUS_MSK 0x00000fff +#define IWN_AGG_TX_TRY_MSK 0x0000f000 + +#define IWN_AGG_TX_STATE_LAST_SENT_MSK \ + (IWN_AGG_TX_STATE_LAST_SENT_TTL_MSK | \ + IWN_AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK) + +/* # tx attempts for first frame in aggregation */ +#define IWN_AGG_TX_STATE_TRY_CNT_POS 12 +#define IWN_AGG_TX_STATE_TRY_CNT_MSK 0xf000 + +/* Command ID and sequence number of Tx command for this frame */ +#define IWN_AGG_TX_STATE_SEQ_NUM_POS 16 +#define IWN_AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 struct iwn4965_tx_stat { uint8_t nframes; @@ -1358,6 +1478,12 @@ struct iwn_compressed_ba { uint64_t bitmap; uint16_t qid; uint16_t ssn; + /* extra fields starting with iwn5000 */ +#if 0 + uint8_t txed; /* number of frames sent */ + uint8_t txed_2_done; /* number of frames acked */ + uint16_t reserved1; +#endif } __packed; /* Structure for IWN_START_SCAN notification. */ @@ -1463,7 +1589,7 @@ struct iwn_rx_ht_phy_stats { uint32_t good_ampdu_crc32; uint32_t ampdu; uint32_t fragment; - uint32_t reserved; + uint32_t unsupport_mcs; } __packed; struct iwn_rx_stats { @@ -1473,6 +1599,20 @@ struct iwn_rx_stats { struct iwn_rx_ht_phy_stats ht; } __packed; +struct iwn_rx_general_stats_bt { + struct iwn_rx_general_stats common; + /* additional stats for bt */ + uint32_t num_bt_kills; + uint32_t reserved[2]; +} __packed; + +struct iwn_rx_stats_bt { + struct iwn_rx_phy_stats ofdm; + struct iwn_rx_phy_stats cck; + struct iwn_rx_general_stats_bt general_bt; + struct iwn_rx_ht_phy_stats ht; +} __packed; + struct iwn_tx_stats { uint32_t preamble; uint32_t rx_detected; @@ -1484,7 +1624,7 @@ struct iwn_tx_stats { uint32_t exp_ack; uint32_t ack; uint32_t msdu; - uint32_t busrt_err1; + uint32_t burst_err1; uint32_t burst_err2; uint32_t cts_collision; uint32_t ack_collision; @@ -1498,15 +1638,21 @@ struct iwn_tx_stats { uint32_t underrun; uint32_t bt_ht_kill; uint32_t rx_ba_resp; - uint32_t reserved[2]; + /* + * 6000 series only - LSB=ant A, ant B, ant C, MSB=reserved + * TX power on chain in 1/2 dBm. + */ + uint32_t tx_power; + uint32_t reserved[1]; } __packed; struct iwn_general_stats { - uint32_t temp; - uint32_t temp_m; + uint32_t temp; /* radio temperature */ + uint32_t temp_m; /* radio voltage */ uint32_t burst_check; uint32_t burst; - uint32_t reserved1[4]; + uint32_t wait_for_silence_timeout_cnt; + uint32_t reserved1[3]; uint32_t sleep; uint32_t slot_out; uint32_t slot_idle; @@ -1517,7 +1663,11 @@ struct iwn_general_stats { uint32_t probe; uint32_t reserved2[2]; uint32_t rx_enabled; - uint32_t reserved3[3]; + /* + * This is the number of times we have to re-tune + * in order to get out of bad PHY status. + */ + uint32_t num_of_sos_states; } __packed; struct iwn_stats { @@ -1525,8 +1675,30 @@ struct iwn_stats { struct iwn_rx_stats rx; struct iwn_tx_stats tx; struct iwn_general_stats general; + uint32_t reserved1[2]; } __packed; +struct iwn_bt_activity_stats { + /* Tx statistics */ + uint32_t hi_priority_tx_req_cnt; + uint32_t hi_priority_tx_denied_cnt; + uint32_t lo_priority_tx_req_cnt; + uint32_t lo_priority_tx_denied_cnt; + /* Rx statistics */ + uint32_t hi_priority_rx_req_cnt; + uint32_t hi_priority_rx_denied_cnt; + uint32_t lo_priority_rx_req_cnt; + uint32_t lo_priority_rx_denied_cnt; +} __packed; + +struct iwn_stats_bt { + uint32_t flags; + struct iwn_rx_stats_bt rx_bt; + struct iwn_tx_stats tx; + struct iwn_general_stats general; + struct iwn_bt_activity_stats activity; + uint32_t reserved1[2]; +}; /* Firmware error dump. */ struct iwn_fw_dump { @@ -1564,7 +1736,7 @@ struct iwn_fw_tlv { #define IWN_FW_TLV_INIT_DATA 4 #define IWN_FW_TLV_BOOT_TEXT 5 #define IWN_FW_TLV_PBREQ_MAXLEN 6 -#define IWN_FW_TLV_PAN 7 +#define IWN_FW_TLV_PAN 7 #define IWN_FW_TLV_RUNT_EVTLOG_PTR 8 #define IWN_FW_TLV_RUNT_EVTLOG_SIZE 9 #define IWN_FW_TLV_RUNT_ERRLOG_PTR 10 @@ -1575,7 +1747,7 @@ struct iwn_fw_tlv { #define IWN_FW_TLV_PHY_CALIB 15 #define IWN_FW_TLV_WOWLAN_INST 16 #define IWN_FW_TLV_WOWLAN_DATA 17 -#define IWN_FW_TLV_FLAGS 18 +#define IWN_FW_TLV_FLAGS 18 uint16_t alt; uint32_t len; @@ -1590,6 +1762,60 @@ struct iwn_fw_tlv { #define IWN5000_FWSZ IWN5000_FW_TEXT_MAXSZ /* + * Microcode flags TLV (18.) + */ + +/** + * enum iwn_ucode_tlv_flag - ucode API flags + * @IWN_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously + * was a separate TLV but moved here to save space. + * @IWN_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID, + * treats good CRC threshold as a boolean + * @IWN_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). + * @IWN_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. + * @IWN_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS + * @IWN_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD + * @IWN_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan + * offload profile config command. + * @IWN_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api + * @IWN_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API. + * @IWN_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six + * (rather than two) IPv6 addresses + * @IWN_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API + * @IWN_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element + * from the probe request template. + * @IWN_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping + * connection when going back to D0 + * @IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) + * @IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) + * @IWN_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. + * @IWN_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API + * @IWN_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command + * containing CAM (Continuous Active Mode) indication. + */ +enum iwn_ucode_tlv_flag { + IWN_UCODE_TLV_FLAGS_PAN = (1 << 0), + IWN_UCODE_TLV_FLAGS_NEWSCAN = (1 << 1), + IWN_UCODE_TLV_FLAGS_MFP = (1 << 2), + IWN_UCODE_TLV_FLAGS_P2P = (1 << 3), + IWN_UCODE_TLV_FLAGS_DW_BC_TABLE = (1 << 4), + IWN_UCODE_TLV_FLAGS_NEWBT_COEX = (1 << 5), + IWN_UCODE_TLV_FLAGS_UAPSD = (1 << 6), + IWN_UCODE_TLV_FLAGS_SHORT_BL = (1 << 7), + IWN_UCODE_TLV_FLAGS_RX_ENERGY_API = (1 << 8), + IWN_UCODE_TLV_FLAGS_TIME_EVENT_API_V2 = (1 << 9), + IWN_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = (1 << 10), + IWN_UCODE_TLV_FLAGS_BF_UPDATED = (1 << 11), + IWN_UCODE_TLV_FLAGS_NO_BASIC_SSID = (1 << 12), + IWN_UCODE_TLV_FLAGS_D3_CONTINUITY_API = (1 << 14), + IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = (1 << 15), + IWN_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = (1 << 16), + IWN_UCODE_TLV_FLAGS_SCHED_SCAN = (1 << 17), + IWN_UCODE_TLV_FLAGS_STA_KEY_CMD = (1 << 19), + IWN_UCODE_TLV_FLAGS_DEVICE_PS_CMD = (1 << 20), +}; + +/* * Offsets into EEPROM. */ #define IWN_EEPROM_MAC 0x015 @@ -1731,6 +1957,16 @@ static const uint32_t iwn1000_regulatory_bands[IWN_NBANDS] = { IWN5000_EEPROM_NO_HT40, }; +static const uint32_t iwn2030_regulatory_bands[IWN_NBANDS] = { + IWN5000_EEPROM_BAND1, + IWN5000_EEPROM_BAND2, + IWN5000_EEPROM_BAND3, + IWN5000_EEPROM_BAND4, + IWN5000_EEPROM_BAND5, + IWN6000_EEPROM_BAND6, + IWN5000_EEPROM_BAND7 +}; + #define IWN_CHAN_BANDS_COUNT 7 #define IWN_MAX_CHAN_PER_BAND 14 static const struct iwn_chan_band { @@ -1757,8 +1993,8 @@ static const uint8_t iwn_bss_ac_to_queue[] = { static const uint8_t iwn_pan_ac_to_queue[] = { 5, 4, 6, 7, }; -#define IWN1000_OTP_NBLOCKS 3 -#define IWN6000_OTP_NBLOCKS 4 +#define IWN1000_OTP_NBLOCKS 3 +#define IWN6000_OTP_NBLOCKS 4 #define IWN6050_OTP_NBLOCKS 7 /* HW rate indices. */ @@ -1891,6 +2127,7 @@ struct iwn_sensitivity_limits { uint32_t min_energy_cck; uint32_t energy_cck; uint32_t energy_ofdm; + uint32_t barker_mrc; }; /* @@ -1905,7 +2142,8 @@ static const struct iwn_sensitivity_limits iwn4965_sensitivity_limits = { 200, 400, 97, 100, - 100 + 100, + 390 }; static const struct iwn_sensitivity_limits iwn5000_sensitivity_limits = { @@ -1917,7 +2155,8 @@ static const struct iwn_sensitivity_limits iwn5000_sensitivity_limits = { 170, 400, 95, 95, - 95 + 95, + 390 }; static const struct iwn_sensitivity_limits iwn5150_sensitivity_limits = { @@ -1929,7 +2168,8 @@ static const struct iwn_sensitivity_limits iwn5150_sensitivity_limits = { 170, 400, 95, 95, - 95 + 95, + 390, }; static const struct iwn_sensitivity_limits iwn1000_sensitivity_limits = { @@ -1941,7 +2181,8 @@ static const struct iwn_sensitivity_limits iwn1000_sensitivity_limits = { 170, 400, 95, 95, - 95 + 95, + 390, }; static const struct iwn_sensitivity_limits iwn6000_sensitivity_limits = { @@ -1953,9 +2194,24 @@ static const struct iwn_sensitivity_limits iwn6000_sensitivity_limits = { 160, 310, 97, 97, - 100 + 100, + 390 +}; + +static const struct iwn_sensitivity_limits iwn6235_sensitivity_limits = { + 105, 110, + 192, 232, + 80, 145, + 128, 232, + 125, 175, + 160, 310, + 100, + 110, + 110, + 336 }; + /* Get value from linux kernel 3.2.+ in Drivers/net/wireless/iwlwifi/iwl-2000.c*/ static const struct iwn_sensitivity_limits iwn2030_sensitivity_limits = { 105,110, @@ -2052,3 +2308,5 @@ static const char * const iwn_fw_errmsg[] = { #define IWN_BARRIER_READ_WRITE(sc) \ bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) + +#endif /* __IF_IWNREG_H__ */ diff --git a/sys/dev/iwn/if_iwnvar.h b/sys/dev/iwn/if_iwnvar.h index 11a233d..b14158b 100644 --- a/sys/dev/iwn/if_iwnvar.h +++ b/sys/dev/iwn/if_iwnvar.h @@ -163,6 +163,7 @@ struct iwn_calib_state { uint32_t bad_plcp_cck; uint32_t fa_cck; uint32_t low_fa; + uint32_t bad_plcp_ht; uint8_t cck_state; #define IWN_CCK_STATE_INIT 0 #define IWN_CCK_STATE_LOFA 1 @@ -249,6 +250,7 @@ struct iwn_softc { #define IWN_FLAG_ENH_SENS (1 << 7) #define IWN_FLAG_ADV_BTCOEX (1 << 8) #define IWN_FLAG_PAN_SUPPORT (1 << 9) +#define IWN_FLAG_BTCOEX (1 << 10) uint8_t hw_type; /* subdevice_id used to adjust configuration */ @@ -306,14 +308,20 @@ struct iwn_softc { struct task sc_reinit_task; struct task sc_radioon_task; struct task sc_radiooff_task; + struct task sc_panic_task; + /* Taskqueue */ + struct taskqueue *sc_tq; + + /* Calibration information */ struct callout calib_to; int calib_cnt; struct iwn_calib_state calib; + int last_calib_ticks; struct callout watchdog_to; struct callout ct_kill_exit_to; struct iwn_fw_info fw; - struct iwn_calib_info calibcmd[5]; + struct iwn_calib_info calibcmd[IWN5000_PHY_CALIB_MAX_RESULT]; uint32_t errptr; struct iwn_rx_stat last_rx_stat; @@ -324,6 +332,22 @@ struct iwn_softc { int ctx; struct ieee80211vap *ivap[IWN_NUM_RXON_CTX]; + /* General statistics */ + /* + * The statistics are reset after each channel + * change. So it may be zeroed after things like + * a background scan. + * + * So for now, this is just a cheap hack to + * expose the last received statistics dump + * via an ioctl(). Later versions of this + * could expose the last 'n' messages, or just + * provide a pipeline for the firmware responses + * via something like BPF. + */ + struct iwn_stats last_stat; + int last_stat_valid; + uint8_t uc_scan_progress; uint32_t rawtemp; int temp; @@ -358,6 +382,9 @@ struct iwn_softc { int sc_tx_timer; int sc_scan_timer; + /* Are we doing a scan? */ + int sc_is_scanning; + struct ieee80211_tx_ampdu *qid2tap[IWN5000_NTXQUEUES]; int (*sc_ampdu_rx_start)(struct ieee80211_node *, @@ -385,8 +412,11 @@ struct iwn_softc { */ int current_pwrsave_level; - /* For specifique params */ - struct iwn_base_params *base_params; + /* For specific params */ + const struct iwn_base_params *base_params; + +#define IWN_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) + uint32_t ucode_rev; }; #define IWN_LOCK_INIT(_sc) \ diff --git a/sys/dev/ral/rt2560.c b/sys/dev/ral/rt2560.c index 1bda391..02c9310 100644 --- a/sys/dev/ral/rt2560.c +++ b/sys/dev/ral/rt2560.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -958,7 +959,7 @@ rt2560_tx_intr(struct rt2560_softc *sc) ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); break; case RT2560_TX_SUCCESS_RETRY: @@ -970,7 +971,7 @@ rt2560_tx_intr(struct rt2560_softc *sc) ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); break; case RT2560_TX_FAIL_RETRY: @@ -982,7 +983,7 @@ rt2560_tx_intr(struct rt2560_softc *sc) ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; case RT2560_TX_FAIL_INVALID: @@ -990,7 +991,7 @@ rt2560_tx_intr(struct rt2560_softc *sc) default: device_printf(sc->sc_dev, "sending data frame failed " "0x%08x\n", flags); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } bus_dmamap_sync(sc->txq.data_dmat, data->map, @@ -1143,13 +1144,13 @@ rt2560_decryption_intr(struct rt2560_softc *sc) break; if (data->drop) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } if ((le32toh(desc->flags) & RT2560_RX_CIPHER_MASK) != 0 && (le32toh(desc->flags) & RT2560_RX_ICV_ERROR)) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1162,7 +1163,7 @@ rt2560_decryption_intr(struct rt2560_softc *sc) */ mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1185,7 +1186,7 @@ rt2560_decryption_intr(struct rt2560_softc *sc) panic("%s: could not load old rx mbuf", device_get_name(sc->sc_dev)); } - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1935,7 +1936,7 @@ rt2560_start_locked(struct ifnet *ifp) ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (rt2560_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; } @@ -1972,7 +1973,7 @@ rt2560_watchdog(void *arg) if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); rt2560_init_locked(sc); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* NB: callout is reset in rt2560_init() */ return; } @@ -2795,7 +2796,7 @@ rt2560_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return ENOBUFS; /* XXX */ } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (params == NULL) { /* @@ -2818,7 +2819,7 @@ rt2560_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return 0; bad: - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); RAL_UNLOCK(sc); return EIO; /* XXX */ diff --git a/sys/dev/ral/rt2661.c b/sys/dev/ral/rt2661.c index a0fb158..448ba57 100644 --- a/sys/dev/ral/rt2661.c +++ b/sys/dev/ral/rt2661.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -908,7 +909,7 @@ rt2661_tx_intr(struct rt2661_softc *sc) ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &retrycnt, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); break; case RT2661_TX_RETRY_FAIL: @@ -920,14 +921,14 @@ rt2661_tx_intr(struct rt2661_softc *sc) ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; default: /* other failure */ device_printf(sc->sc_dev, "sending data frame failed 0x%08x\n", val); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } DPRINTFN(sc, 15, "tx done q=%d idx=%u\n", qid, txq->stat); @@ -1014,12 +1015,12 @@ rt2661_rx_intr(struct rt2661_softc *sc) */ DPRINTFN(sc, 5, "PHY or CRC error flags 0x%08x\n", le32toh(desc->flags)); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } if ((le32toh(desc->flags) & RT2661_RX_CIPHER_MASK) != 0) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1032,7 +1033,7 @@ rt2661_rx_intr(struct rt2661_softc *sc) */ mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1055,7 +1056,7 @@ rt2661_rx_intr(struct rt2661_softc *sc) panic("%s: could not load old rx mbuf", device_get_name(sc->sc_dev)); } - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1630,7 +1631,7 @@ rt2661_start_locked(struct ifnet *ifp) ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (rt2661_tx_data(sc, m, ni, ac) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; } @@ -1673,7 +1674,7 @@ rt2661_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return ENOBUFS; /* XXX */ } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* * Legacy path; interpret frame contents to decide @@ -1688,7 +1689,7 @@ rt2661_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return 0; bad: - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); RAL_UNLOCK(sc); return EIO; /* XXX */ @@ -1710,7 +1711,7 @@ rt2661_watchdog(void *arg) if (sc->sc_tx_timer > 0 && --sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); rt2661_init_locked(sc); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* NB: callout is reset in rt2661_init() */ return; } diff --git a/sys/dev/ral/rt2860.c b/sys/dev/ral/rt2860.c index 3eb7187..b29a0eb 100644 --- a/sys/dev/ral/rt2860.c +++ b/sys/dev/ral/rt2860.c @@ -46,6 +46,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -1134,7 +1135,7 @@ rt2860_drain_stats_fifo(struct rt2860_softc *sc) } else { ieee80211_ratectl_tx_complete(ni->ni_vap, ni, IEEE80211_RATECTL_TX_FAILURE, &retrycnt, NULL); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } } } @@ -1168,7 +1169,7 @@ rt2860_tx_intr(struct rt2860_softc *sc, int qid) SLIST_INSERT_HEAD(&sc->data_pool, data, next); ring->data[ring->next] = NULL; - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } ring->queued--; ring->next = (ring->next + 1) % RT2860_TX_RING_COUNT; @@ -1231,7 +1232,7 @@ rt2860_rx_intr(struct rt2860_softc *sc) if (__predict_false(rxd->flags & htole32(RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1240,14 +1241,14 @@ rt2860_rx_intr(struct rt2860_softc *sc) /* report MIC failures to net80211 for TKIP */ ic->ic_stats.is_rx_locmicfail++; ieee80211_michael_mic_failure(ic, 0/* XXX */); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } #endif m1 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m1 == NULL)) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1271,7 +1272,7 @@ rt2860_rx_intr(struct rt2860_softc *sc) } /* physical address may have changed */ rxd->sdp0 = htole32(physaddr); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto skip; } @@ -1751,7 +1752,7 @@ rt2860_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, if (error != 0) { /* NB: m is reclaimed on tx failure */ ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } sc->sc_tx_timer = 5; RAL_UNLOCK(sc); @@ -2004,7 +2005,7 @@ rt2860_start_locked(struct ifnet *ifp) ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (rt2860_tx(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); continue; } sc->sc_tx_timer = 5; @@ -2028,7 +2029,7 @@ rt2860_watchdog(void *arg) if_printf(ifp, "device timeout\n"); rt2860_stop_locked(sc); rt2860_init_locked(sc); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } callout_reset(&sc->watchdog_ch, hz, rt2860_watchdog, sc); diff --git a/sys/dev/usb/wlan/if_rsu.c b/sys/dev/usb/wlan/if_rsu.c index 4ad6ab5..a6b5ebd 100644 --- a/sys/dev/usb/wlan/if_rsu.c +++ b/sys/dev/usb/wlan/if_rsu.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/if_dl.h> #include <net/if_media.h> @@ -71,7 +72,7 @@ __FBSDID("$FreeBSD$"); #ifdef USB_DEBUG static int rsu_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, rsu, CTLFLAG_RW, 0, "USB rsu"); -SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RW, &rsu_debug, 0, +SYSCTL_INT(_hw_usb_rsu, OID_AUTO, debug, CTLFLAG_RWTUN, &rsu_debug, 0, "Debug level"); #endif @@ -120,6 +121,7 @@ static const STRUCT_USB_HOST_ID rsu_devs[] = { RSU_DEV_HT(SITECOMEU, WL349V1), RSU_DEV_HT(SITECOMEU, WL353), RSU_DEV_HT(SWEEX2, LW154), + //RSU_DEV_HT(TRENDNET, TEW646UBH), #undef RSU_DEV_HT #undef RSU_DEV }; @@ -1153,16 +1155,9 @@ rsu_event_survey(struct rsu_softc *sc, uint8_t *buf, int len) pktlen = sizeof(*wh) + le32toh(bss->ieslen); if (__predict_false(pktlen > MCLBYTES)) return; - MGETHDR(m, M_NOWAIT, MT_DATA); + m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) return; - if (pktlen > MHLEN) { - MCLGET(m, M_NOWAIT); - if (!(m->m_flags & M_EXT)) { - m_free(m); - return; - } - } wh = mtod(m, struct ieee80211_frame *); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_BEACON; @@ -1358,11 +1353,11 @@ rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi) rxdw3 = le32toh(stat->rxdw3); if (__predict_false(rxdw0 & R92S_RXDW0_CRCERR)) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return NULL; } if (__predict_false(pktlen < sizeof(*wh) || pktlen > MCLBYTES)) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return NULL; } @@ -1378,19 +1373,11 @@ rsu_rx_frame(struct rsu_softc *sc, uint8_t *buf, int pktlen, int *rssi) DPRINTFN(5, "Rx frame len=%d rate=%d infosz=%d rssi=%d\n", pktlen, rate, infosz, *rssi); - MGETHDR(m, M_NOWAIT, MT_DATA); + m = m_get2(pktlen, M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m == NULL)) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return NULL; } - if (pktlen > MHLEN) { - MCLGET(m, M_NOWAIT); - if (__predict_false(!(m->m_flags & M_EXT))) { - ifp->if_ierrors++; - m_freem(m); - return NULL; - } - } /* Finalize mbuf. */ m->m_pkthdr.rcvif = ifp; /* Hardware does Rx TCP checksum offload. */ @@ -1497,7 +1484,7 @@ rsu_rxeof(struct usb_xfer *xfer, struct rsu_data *data, int *rssi) if (__predict_false(len < sizeof(*stat))) { DPRINTF("xfer too short %d\n", len); - sc->sc_ifp->if_ierrors++; + if_inc_counter(sc->sc_ifp, IFCOUNTER_IERRORS, 1); return (NULL); } /* Determine if it is a firmware C2H event or an 802.11 frame. */ @@ -1575,7 +1562,7 @@ tr_setup: } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } break; @@ -1610,7 +1597,7 @@ rsu_txeof(struct usb_xfer *xfer, struct rsu_data *data) ieee80211_free_node(data->ni); data->ni = NULL; } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } @@ -1654,7 +1641,7 @@ tr_setup: rsu_txeof(xfer, data); STAILQ_INSERT_TAIL(&sc->sc_tx_inactive, data, next); } - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); @@ -1815,11 +1802,11 @@ rsu_start_locked(struct ifnet *ifp) bf = rsu_getbuf(sc); if (bf == NULL) { - ifp->if_iqdrops++; + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); m_freem(m); ieee80211_free_node(ni); } else if (rsu_tx_start(sc, ni, m, bf) != 0) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); ieee80211_free_node(ni); } @@ -2325,10 +2312,10 @@ rsu_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, RSU_UNLOCK(sc); return (ENOBUFS); } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (rsu_tx_start(sc, ni, m, bf) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); RSU_UNLOCK(sc); return (EIO); diff --git a/sys/dev/usb/wlan/if_rum.c b/sys/dev/usb/wlan/if_rum.c index e1e8f2e..a3dc562 100644 --- a/sys/dev/usb/wlan/if_rum.c +++ b/sys/dev/usb/wlan/if_rum.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -81,7 +82,7 @@ __FBSDID("$FreeBSD$"); static int rum_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, rum, CTLFLAG_RW, 0, "USB rum"); -SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RW, &rum_debug, 0, +SYSCTL_INT(_hw_usb_rum, OID_AUTO, debug, CTLFLAG_RWTUN, &rum_debug, 0, "Debug level"); #endif @@ -796,7 +797,7 @@ rum_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) rum_tx_free(data, 0); usbd_xfer_set_priv(xfer, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ @@ -850,7 +851,7 @@ tr_setup: DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); data = usbd_xfer_get_priv(xfer); if (data != NULL) { rum_tx_free(data, error); @@ -896,7 +897,7 @@ rum_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) if (len < (int)(RT2573_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { DPRINTF("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } @@ -913,14 +914,14 @@ rum_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) * filled RUM_TXRX_CSR2: */ DPRINTFN(5, "PHY or CRC error\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } usbd_copy_out(pc, RT2573_RX_DESC_SIZE, @@ -1320,7 +1321,7 @@ rum_start(struct ifnet *ifp) ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (rum_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; } } @@ -2204,7 +2205,7 @@ rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return EIO; } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (params == NULL) { /* @@ -2225,7 +2226,7 @@ rum_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return 0; bad: - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); RUM_UNLOCK(sc); ieee80211_free_node(ni); return EIO; @@ -2280,7 +2281,7 @@ rum_ratectl_task(void *arg, int pending) (void) ieee80211_ratectl_rate(ni, NULL, 0); ieee80211_free_node(ni); - ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, fail); /* count TX retry-fail as Tx errors */ usb_callout_reset(&rvp->ratectl_ch, hz, rum_ratectl_timeout, rvp); RUM_UNLOCK(sc); diff --git a/sys/dev/usb/wlan/if_run.c b/sys/dev/usb/wlan/if_run.c index 8218f17..22cc180 100644 --- a/sys/dev/usb/wlan/if_run.c +++ b/sys/dev/usb/wlan/if_run.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -83,7 +84,7 @@ __FBSDID("$FreeBSD$"); #ifdef RUN_DEBUG int run_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, run, CTLFLAG_RW, 0, "USB run"); -SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RW, &run_debug, 0, +SYSCTL_INT(_hw_usb_run, OID_AUTO, debug, CTLFLAG_RWTUN, &run_debug, 0, "run debug level"); #endif @@ -317,6 +318,7 @@ static const STRUCT_USB_HOST_ID run_devs[] = { RUN_DEV(ZINWELL, RT3072_2), RUN_DEV(ZYXEL, RT2870_1), RUN_DEV(ZYXEL, RT2870_2), + RUN_DEV(ZYXEL, RT3070), RUN_DEV_EJECT(ZYXEL, NWD2705), RUN_DEV_EJECT(RALINK, RT_STOR), #undef RUN_DEV_EJECT @@ -2573,7 +2575,7 @@ run_drain_fifo(void *arg) if (stat & RT2860_TXQ_OK) (*wstat)[RUN_SUCCESS]++; else - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* * Check if there were retries, ie if the Tx success rate is * different from the requested rate. Note that it works only @@ -2619,7 +2621,7 @@ run_iter_func(void *arg, struct ieee80211_node *ni) goto fail; /* count failed TX as errors */ - ifp->if_oerrors += le16toh(sta[0].error.fail); + if_inc_counter(ifp, IFCOUNTER_OERRORS, le16toh(sta[0].error.fail)); retrycnt = le16toh(sta[1].tx.retry); success = le16toh(sta[1].tx.success); @@ -2785,7 +2787,7 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen) rxwisize += sizeof(uint32_t); if (__predict_false(len > dmalen)) { m_freem(m); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF("bad RXWI length %u > %u\n", len, dmalen); return; } @@ -2795,7 +2797,7 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen) if (__predict_false(flags & (RT2860_RX_CRCERR | RT2860_RX_ICVERR))) { m_freem(m); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF("%s error.\n", (flags & RT2860_RX_CRCERR)?"CRC":"ICV"); return; } @@ -2824,7 +2826,7 @@ run_rx_frame(struct run_softc *sc, struct mbuf *m, uint32_t dmalen) ieee80211_notify_michael_failure(ni->ni_vap, wh, rxwi->keyidx); m_freem(m); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF("MIC error. Someone is lying.\n"); return; } @@ -2924,7 +2926,7 @@ tr_setup: } if (sc->rx_m == NULL) { DPRINTF("could not allocate mbuf - idle with stall\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); usbd_xfer_set_stall(xfer); usbd_xfer_set_frames(xfer, 0); } else { @@ -2948,7 +2950,7 @@ tr_setup: if (error == USB_ERR_TIMEOUT) device_printf(sc->sc_dev, "device timeout\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } @@ -2997,7 +2999,7 @@ tr_setup: m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (__predict_false(m0 == NULL)) { DPRINTF("could not allocate mbuf\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); break; } m_copydata(m, 4 /* skip 32-bit DMA-len header */, @@ -3069,7 +3071,7 @@ run_bulk_tx_callbackN(struct usb_xfer *xfer, usb_error_t error, u_int index) usbd_xfer_set_priv(xfer, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); /* FALLTHROUGH */ case USB_ST_SETUP: @@ -3088,7 +3090,7 @@ tr_setup: DPRINTF("data overflow, %u bytes\n", m->m_pkthdr.len); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); run_tx_free(pq, data, 1); @@ -3143,7 +3145,7 @@ tr_setup: data = usbd_xfer_get_priv(xfer); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); if (data != NULL) { if(data->ni != NULL) @@ -3566,7 +3568,7 @@ run_sendprot(struct run_softc *sc, mprot = ieee80211_alloc_cts(ic, ni->ni_vap->iv_myaddr, dur); } if (mprot == NULL) { - sc->sc_ifp->if_oerrors++; + if_inc_counter(sc->sc_ifp, IFCOUNTER_OERRORS, 1); DPRINTF("could not allocate mbuf\n"); return (ENOBUFS); } @@ -3702,20 +3704,20 @@ run_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, if (params == NULL) { /* tx mgt packet */ if ((error = run_tx_mgt(sc, m, ni)) != 0) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); DPRINTF("mgt tx failed\n"); goto done; } } else { /* tx raw packet with param */ if ((error = run_tx_param(sc, m, ni, params)) != 0) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); DPRINTF("tx with param failed\n"); goto done; } } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); done: RUN_UNLOCK(sc); diff --git a/sys/dev/usb/wlan/if_runreg.h b/sys/dev/usb/wlan/if_runreg.h index 3ca01cd..aa54a72 100644 --- a/sys/dev/usb/wlan/if_runreg.h +++ b/sys/dev/usb/wlan/if_runreg.h @@ -962,31 +962,6 @@ struct rt2860_rxwi { #define RT2860_RIDX_MAX 12 /* - * Control and status registers access macros. - */ -#define RAL_READ(sc, reg) \ - bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) - -#define RAL_WRITE(sc, reg, val) \ - bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) - -#define RAL_BARRIER_WRITE(sc) \ - bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, 0x1800, \ - BUS_SPACE_BARRIER_WRITE) - -#define RAL_BARRIER_READ_WRITE(sc) \ - bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, 0x1800, \ - BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) - -#define RAL_WRITE_REGION_1(sc, offset, datap, count) \ - bus_space_write_region_1((sc)->sc_st, (sc)->sc_sh, (offset), \ - (datap), (count)) - -#define RAL_SET_REGION_4(sc, offset, val, count) \ - bus_space_set_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ - (val), (count)) - -/* * EEPROM access macro. */ #define RT2860_EEPROM_CTL(sc, val) do { \ diff --git a/sys/dev/usb/wlan/if_uath.c b/sys/dev/usb/wlan/if_uath.c index 6806c33..0796fbe 100644 --- a/sys/dev/usb/wlan/if_uath.c +++ b/sys/dev/usb/wlan/if_uath.c @@ -86,6 +86,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -114,18 +115,16 @@ __FBSDID("$FreeBSD$"); static SYSCTL_NODE(_hw_usb, OID_AUTO, uath, CTLFLAG_RW, 0, "USB Atheros"); static int uath_countrycode = CTRY_DEFAULT; /* country code */ -SYSCTL_INT(_hw_usb_uath, OID_AUTO, countrycode, CTLFLAG_RW | CTLFLAG_TUN, &uath_countrycode, +SYSCTL_INT(_hw_usb_uath, OID_AUTO, countrycode, CTLFLAG_RWTUN, &uath_countrycode, 0, "country code"); -TUNABLE_INT("hw.usb.uath.countrycode", &uath_countrycode); static int uath_regdomain = 0; /* regulatory domain */ SYSCTL_INT(_hw_usb_uath, OID_AUTO, regdomain, CTLFLAG_RD, &uath_regdomain, 0, "regulatory domain"); #ifdef UATH_DEBUG int uath_debug = 0; -SYSCTL_INT(_hw_usb_uath, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &uath_debug, 0, +SYSCTL_INT(_hw_usb_uath, OID_AUTO, debug, CTLFLAG_RWTUN, &uath_debug, 0, "uath debug level"); -TUNABLE_INT("hw.usb.uath.debug", &uath_debug); enum { UATH_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ UATH_DEBUG_XMIT_DUMP = 0x00000002, /* xmit dump */ @@ -1334,7 +1333,7 @@ uath_watchdog(void *arg) if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); /*uath_init(ifp); XXX needs a process context! */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } callout_reset(&sc->watchdog_ch, hz, uath_watchdog, sc); @@ -1815,7 +1814,7 @@ uath_start(struct ifnet *ifp) next = m->m_nextpkt; if (uath_tx_start(sc, m, ni, bf) != 0) { bad: - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); reclaim: STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); @@ -1879,7 +1878,7 @@ uath_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, sc->sc_seqnum = 0; if (uath_tx_start(sc, m, ni, bf) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); UATH_STAT_INC(sc, st_tx_inactive); UATH_UNLOCK(sc); @@ -2554,14 +2553,14 @@ uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, if (actlen < (int)UATH_MIN_RXBUFSZ) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: wrong xfer size (len=%d)\n", __func__, actlen); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } chunk = (struct uath_chunk *)data->buf; if (chunk->seqnum == 0 && chunk->flags == 0 && chunk->length == 0) { device_printf(sc->sc_dev, "%s: strange response\n", __func__); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); UATH_RESET_INTRX(sc); return (NULL); } @@ -2594,7 +2593,7 @@ uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, if ((sc->sc_intrx_len + sizeof(struct uath_rx_desc) + chunklen) > UATH_MAX_INTRX_SIZE) { UATH_STAT_INC(sc, st_invalidlen); - ifp->if_iqdrops++; + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); @@ -2619,7 +2618,7 @@ uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, if (mnew == NULL) { DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: can't get new mbuf, drop frame\n", __func__); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); UATH_RESET_INTRX(sc); @@ -2660,7 +2659,7 @@ uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, DPRINTF(sc, UATH_DEBUG_RECV | UATH_DEBUG_RECV_ALL, "%s: bad descriptor (len=%d)\n", __func__, be32toh(desc->len)); - ifp->if_iqdrops++; + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); UATH_STAT_INC(sc, st_toobigrxpkt); if (sc->sc_intrx_head != NULL) m_freem(sc->sc_intrx_head); @@ -2704,7 +2703,7 @@ uath_data_rxeof(struct usb_xfer *xfer, struct uath_data *data, tap->wr_antnoise = -95; } - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); UATH_RESET_INTRX(sc); return (m); @@ -2791,7 +2790,7 @@ setup: } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto setup; } break; @@ -2827,7 +2826,7 @@ uath_data_txeof(struct usb_xfer *xfer, struct uath_data *data) data->ni = NULL; } sc->sc_tx_timer = 0; - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } @@ -2879,7 +2878,7 @@ setup: if ((sc->sc_flags & UATH_FLAG_INVALID) == 0) ieee80211_free_node(data->ni); data->ni = NULL; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); diff --git a/sys/dev/usb/wlan/if_upgt.c b/sys/dev/usb/wlan/if_upgt.c index 1f60ebc..dc6dc1b 100644 --- a/sys/dev/usb/wlan/if_upgt.c +++ b/sys/dev/usb/wlan/if_upgt.c @@ -31,6 +31,7 @@ #include <sys/sysctl.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -75,9 +76,8 @@ static SYSCTL_NODE(_hw, OID_AUTO, upgt, CTLFLAG_RD, 0, #ifdef UPGT_DEBUG int upgt_debug = 0; -SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &upgt_debug, +SYSCTL_INT(_hw_upgt, OID_AUTO, debug, CTLFLAG_RWTUN, &upgt_debug, 0, "control debugging printfs"); -TUNABLE_INT("hw.upgt.debug", &upgt_debug); enum { UPGT_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ UPGT_DEBUG_RECV = 0x00000002, /* basic recv operation */ @@ -182,7 +182,7 @@ static const STRUCT_USB_HOST_ID upgt_devs[] = { UPGT_DEV(FSC, E5400), UPGT_DEV(GLOBESPAN, PRISM_GT_1), UPGT_DEV(GLOBESPAN, PRISM_GT_2), - UPGT_DEV(NETGEAR, WG111V2_2), + //UPGT_DEV(NETGEAR, WG111V1_2), UPGT_DEV(INTERSIL, PRISM_GT), UPGT_DEV(SMC, 2862WG), UPGT_DEV(USR, USR5422), @@ -416,7 +416,7 @@ upgt_txeof(struct usb_xfer *xfer, struct upgt_data *data) ieee80211_free_node(data->ni); data->ni = NULL; } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } static void @@ -854,7 +854,7 @@ upgt_start(struct ifnet *ifp) STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next); UPGT_STAT_INC(sc, st_tx_inactive); ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); continue; } sc->sc_tx_timer = 5; @@ -891,7 +891,7 @@ upgt_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, data_tx, next); UPGT_STAT_INC(sc, st_tx_inactive); ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); UPGT_UNLOCK(sc); return (EIO); } @@ -911,7 +911,7 @@ upgt_watchdog(void *arg) if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "watchdog timeout\n"); /* upgt_init(ifp); XXX needs a process context ? */ - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } callout_reset(&sc->sc_watchdog_ch, hz, upgt_watchdog, sc); @@ -1552,7 +1552,7 @@ upgt_rx(struct upgt_softc *sc, uint8_t *data, int pkglen, int *rssi) tap->wr_rate = upgt_rx_rate(sc, rxdesc->rate); tap->wr_antsignal = rxdesc->rssi; } - ifp->if_ipackets++; + if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); DPRINTF(sc, UPGT_DEBUG_RX_PROC, "%s: RX done\n", __func__); *rssi = rxdesc->rssi; @@ -2293,7 +2293,8 @@ done: * will stall. It's strange, but it works, so we keep reading * the statistics here. *shrug* */ - if (!(ifp->if_opackets % UPGT_TX_STAT_INTERVAL)) + if (!(if_get_counter_default(ifp, IFCOUNTER_OPACKETS) % + UPGT_TX_STAT_INTERVAL)) upgt_get_stats(sc); return (error); @@ -2366,7 +2367,7 @@ setup: } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto setup; } break; @@ -2418,7 +2419,7 @@ setup: if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); diff --git a/sys/dev/usb/wlan/if_ural.c b/sys/dev/usb/wlan/if_ural.c index 69b41dd..c81b708 100644 --- a/sys/dev/usb/wlan/if_ural.c +++ b/sys/dev/usb/wlan/if_ural.c @@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -82,7 +83,7 @@ __FBSDID("$FreeBSD$"); static int ural_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, ural, CTLFLAG_RW, 0, "USB ural"); -SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RW, &ural_debug, 0, +SYSCTL_INT(_hw_usb_ural, OID_AUTO, debug, CTLFLAG_RWTUN, &ural_debug, 0, "Debug level"); #endif @@ -804,7 +805,7 @@ ural_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) ural_tx_free(data, 0); usbd_xfer_set_priv(xfer, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ @@ -858,7 +859,7 @@ tr_setup: DPRINTFN(11, "transfer error, %s\n", usbd_errstr(error)); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); data = usbd_xfer_get_priv(xfer); if (data != NULL) { ural_tx_free(data, error); @@ -899,7 +900,7 @@ ural_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) if (len < (int)(RAL_RX_DESC_SIZE + IEEE80211_MIN_LEN)) { DPRINTF("%s: xfer too short %d\n", device_get_nameunit(sc->sc_dev), len); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } @@ -918,14 +919,14 @@ ural_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) * filled RAL_TXRX_CSR2: */ DPRINTFN(5, "PHY or CRC error\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (m == NULL) { DPRINTF("could not allocate mbuf\n"); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } usbd_copy_out(pc, 0, mtod(m, uint8_t *), len); @@ -1369,7 +1370,7 @@ ural_start(struct ifnet *ifp) ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; if (ural_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; } } @@ -2209,7 +2210,7 @@ ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return EIO; } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (params == NULL) { /* @@ -2229,7 +2230,7 @@ ural_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, RAL_UNLOCK(sc); return 0; bad: - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); RAL_UNLOCK(sc); ieee80211_free_node(ni); return EIO; /* XXX */ @@ -2283,7 +2284,7 @@ ural_ratectl_task(void *arg, int pending) ieee80211_ratectl_tx_update(vap, ni, &sum, &ok, &retrycnt); (void) ieee80211_ratectl_rate(ni, NULL, 0); - ifp->if_oerrors += fail; /* count TX retry-fail as Tx errors */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, fail); /* count TX retry-fail as Tx errors */ usb_callout_reset(&uvp->ratectl_ch, hz, ural_ratectl_timeout, uvp); RAL_UNLOCK(sc); diff --git a/sys/dev/usb/wlan/if_urtw.c b/sys/dev/usb/wlan/if_urtw.c index 5612f19..d2c6ef9 100644 --- a/sys/dev/usb/wlan/if_urtw.c +++ b/sys/dev/usb/wlan/if_urtw.c @@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$"); #include <sys/rman.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -64,9 +65,8 @@ __FBSDID("$FreeBSD$"); static SYSCTL_NODE(_hw_usb, OID_AUTO, urtw, CTLFLAG_RW, 0, "USB Realtek 8187L"); #ifdef URTW_DEBUG int urtw_debug = 0; -SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_TUN, &urtw_debug, 0, +SYSCTL_INT(_hw_usb_urtw, OID_AUTO, debug, CTLFLAG_RWTUN, &urtw_debug, 0, "control debugging printfs"); -TUNABLE_INT("hw.usb.urtw.debug", &urtw_debug); enum { URTW_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ URTW_DEBUG_RECV = 0x00000002, /* basic recv operation */ @@ -89,9 +89,8 @@ enum { } while (0) #endif static int urtw_preamble_mode = URTW_PREAMBLE_MODE_LONG; -SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RW | CTLFLAG_TUN, +SYSCTL_INT(_hw_usb_urtw, OID_AUTO, preamble_mode, CTLFLAG_RWTUN, &urtw_preamble_mode, 0, "set the preable mode (long or short)"); -TUNABLE_INT("hw.usb.urtw.preamble_mode", &urtw_preamble_mode); /* recognized device vendors/products */ #define urtw_lookup(v, p) \ @@ -1473,7 +1472,7 @@ urtw_start(struct ifnet *ifp) m->m_pkthdr.rcvif = NULL; if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_NORMAL) != 0) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); ieee80211_free_node(ni); break; @@ -1583,10 +1582,10 @@ urtw_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return (ENOBUFS); /* XXX */ } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (urtw_tx_start(sc, ni, m, bf, URTW_PRIORITY_LOW) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); URTW_UNLOCK(sc); return (EIO); @@ -1919,7 +1918,7 @@ urtw_watchdog(void *arg) if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { device_printf(sc->sc_dev, "device timeout\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } callout_reset(&sc->sc_watchdog_ch, hz, urtw_watchdog, sc); @@ -3994,7 +3993,7 @@ urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); if (actlen < (int)URTW_MIN_RXBUFSZ) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } @@ -4005,7 +4004,7 @@ urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, (actlen - (sizeof(struct urtw_8187b_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } rate = (le32toh(rx->flag) >> URTW_RX_FLAG_RXRATE_SHIFT) & 0xf; @@ -4019,7 +4018,7 @@ urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, (actlen - (sizeof(struct urtw_8187l_rxhdr)))); flen = le32toh(rx->flag) & 0xfff; if (flen > actlen) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } @@ -4031,7 +4030,7 @@ urtw_rxeof(struct usb_xfer *xfer, struct urtw_data *data, int *rssi_p, mnew = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); if (mnew == NULL) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } @@ -4128,7 +4127,7 @@ setup: } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto setup; } break; @@ -4157,7 +4156,7 @@ urtw_txstatus_eof(struct usb_xfer *xfer) pktretry = val & 0xff; seq = (val >> 16) & 0xff; if (pktretry == URTW_TX_MAXRETRY) - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); DPRINTF(sc, URTW_DEBUG_TXSTATUS, "pktretry %d seq %#x\n", pktretry, seq); } @@ -4185,7 +4184,7 @@ setup: default: if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto setup; } break; @@ -4219,7 +4218,7 @@ urtw_txeof(struct usb_xfer *xfer, struct urtw_data *data) data->ni = NULL; } sc->sc_txtimer = 0; - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } @@ -4266,7 +4265,7 @@ setup: if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); diff --git a/sys/dev/usb/wlan/if_urtwn.c b/sys/dev/usb/wlan/if_urtwn.c index 2475727..28bf713 100644 --- a/sys/dev/usb/wlan/if_urtwn.c +++ b/sys/dev/usb/wlan/if_urtwn.c @@ -47,6 +47,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -77,7 +78,7 @@ __FBSDID("$FreeBSD$"); static int urtwn_debug = 0; SYSCTL_NODE(_hw_usb, OID_AUTO, urtwn, CTLFLAG_RW, 0, "USB urtwn"); -SYSCTL_INT(_hw_usb_urtwn, OID_AUTO, debug, CTLFLAG_RW, &urtwn_debug, 0, +SYSCTL_INT(_hw_usb_urtwn, OID_AUTO, debug, CTLFLAG_RWTUN, &urtwn_debug, 0, "Debug level"); #endif @@ -152,7 +153,9 @@ static const STRUCT_USB_HOST_ID urtwn_devs[] = { URTWN_DEV(TRENDNET, RTL8192CU), URTWN_DEV(ZYXEL, RTL8192CU), /* URTWN_RTL8188E */ + //URTWN_RTL8188E_DEV(DLINK, DWA123D1), URTWN_RTL8188E_DEV(DLINK, DWA125D1), + //URTWN_RTL8188E_DEV(ELECOM, WDC150SU2M), URTWN_RTL8188E_DEV(REALTEK, RTL8188ETV), URTWN_RTL8188E_DEV(REALTEK, RTL8188EU), #undef URTWN_RTL8188E_DEV @@ -173,12 +176,12 @@ static struct ieee80211vap *urtwn_vap_create(struct ieee80211com *, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void urtwn_vap_delete(struct ieee80211vap *); -static struct mbuf * urtwn_rx_frame(struct urtwn_softc *, uint8_t *, int, +static struct mbuf * urtwn_rx_frame(struct urtwn_softc *, uint8_t *, int, int *); -static struct mbuf * urtwn_rxeof(struct usb_xfer *, struct urtwn_data *, +static struct mbuf * urtwn_rxeof(struct usb_xfer *, struct urtwn_data *, int *, int8_t *); static void urtwn_txeof(struct usb_xfer *, struct urtwn_data *); -static int urtwn_alloc_list(struct urtwn_softc *, +static int urtwn_alloc_list(struct urtwn_softc *, struct urtwn_data[], int, int); static int urtwn_alloc_rx_list(struct urtwn_softc *); static int urtwn_alloc_tx_list(struct urtwn_softc *); @@ -188,24 +191,24 @@ static void urtwn_free_list(struct urtwn_softc *, struct urtwn_data data[], int); static struct urtwn_data * _urtwn_getbuf(struct urtwn_softc *); static struct urtwn_data * urtwn_getbuf(struct urtwn_softc *); -static int urtwn_write_region_1(struct urtwn_softc *, uint16_t, +static int urtwn_write_region_1(struct urtwn_softc *, uint16_t, uint8_t *, int); static void urtwn_write_1(struct urtwn_softc *, uint16_t, uint8_t); static void urtwn_write_2(struct urtwn_softc *, uint16_t, uint16_t); static void urtwn_write_4(struct urtwn_softc *, uint16_t, uint32_t); -static int urtwn_read_region_1(struct urtwn_softc *, uint16_t, +static int urtwn_read_region_1(struct urtwn_softc *, uint16_t, uint8_t *, int); static uint8_t urtwn_read_1(struct urtwn_softc *, uint16_t); static uint16_t urtwn_read_2(struct urtwn_softc *, uint16_t); static uint32_t urtwn_read_4(struct urtwn_softc *, uint16_t); -static int urtwn_fw_cmd(struct urtwn_softc *, uint8_t, +static int urtwn_fw_cmd(struct urtwn_softc *, uint8_t, const void *, int); static void urtwn_r92c_rf_write(struct urtwn_softc *, int, uint8_t, uint32_t); -static void urtwn_r88e_rf_write(struct urtwn_softc *, int, +static void urtwn_r88e_rf_write(struct urtwn_softc *, int, uint8_t, uint32_t); static uint32_t urtwn_rf_read(struct urtwn_softc *, int, uint8_t); -static int urtwn_llt_write(struct urtwn_softc *, uint32_t, +static int urtwn_llt_write(struct urtwn_softc *, uint32_t, uint32_t); static uint8_t urtwn_efuse_read_1(struct urtwn_softc *, uint16_t); static void urtwn_efuse_read(struct urtwn_softc *); @@ -216,7 +219,7 @@ static void urtwn_r88e_read_rom(struct urtwn_softc *); static int urtwn_ra_init(struct urtwn_softc *); static void urtwn_tsf_sync_enable(struct urtwn_softc *); static void urtwn_set_led(struct urtwn_softc *, int, int); -static int urtwn_newstate(struct ieee80211vap *, +static int urtwn_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void urtwn_watchdog(void *); static void urtwn_update_avgrssi(struct urtwn_softc *, int, int8_t); @@ -234,7 +237,7 @@ static int urtwn_r88e_power_on(struct urtwn_softc *); static int urtwn_llt_init(struct urtwn_softc *); static void urtwn_fw_reset(struct urtwn_softc *); static void urtwn_r88e_fw_reset(struct urtwn_softc *); -static int urtwn_fw_loadpage(struct urtwn_softc *, int, +static int urtwn_fw_loadpage(struct urtwn_softc *, int, const uint8_t *, int); static int urtwn_load_firmware(struct urtwn_softc *); static int urtwn_r92c_dma_init(struct urtwn_softc *); @@ -246,22 +249,22 @@ static void urtwn_cam_init(struct urtwn_softc *); static void urtwn_pa_bias_init(struct urtwn_softc *); static void urtwn_rxfilter_init(struct urtwn_softc *); static void urtwn_edca_init(struct urtwn_softc *); -static void urtwn_write_txpower(struct urtwn_softc *, int, +static void urtwn_write_txpower(struct urtwn_softc *, int, uint16_t[]); static void urtwn_get_txpower(struct urtwn_softc *, int, - struct ieee80211_channel *, + struct ieee80211_channel *, struct ieee80211_channel *, uint16_t[]); static void urtwn_r88e_get_txpower(struct urtwn_softc *, int, - struct ieee80211_channel *, + struct ieee80211_channel *, struct ieee80211_channel *, uint16_t[]); static void urtwn_set_txpower(struct urtwn_softc *, - struct ieee80211_channel *, + struct ieee80211_channel *, struct ieee80211_channel *); static void urtwn_scan_start(struct ieee80211com *); static void urtwn_scan_end(struct ieee80211com *); static void urtwn_set_channel(struct ieee80211com *); static void urtwn_set_chan(struct urtwn_softc *, - struct ieee80211_channel *, + struct ieee80211_channel *, struct ieee80211_channel *); static void urtwn_update_mcast(struct ifnet *); static void urtwn_iq_calib(struct urtwn_softc *); @@ -467,7 +470,7 @@ urtwn_attach(device_t self) ic->ic_vap_delete = urtwn_vap_delete; ic->ic_update_mcast = urtwn_update_mcast; - ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, + ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), URTWN_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), URTWN_RX_RADIOTAP_PRESENT); @@ -489,7 +492,7 @@ urtwn_detach(device_t self) struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; unsigned int x; - + /* Prevent further ioctls. */ URTWN_LOCK(sc); sc->sc_flags |= URTWN_DETACHED; @@ -601,7 +604,7 @@ urtwn_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, vap = &uvp->vap; /* enable s/w bmiss handling for sta mode */ - if (ieee80211_vap_setup(ic, vap, name, unit, opmode, + if (ieee80211_vap_setup(ic, vap, name, unit, opmode, flags | IEEE80211_CLONE_NOBEACONS, bssid, mac) != 0) { /* out of memory */ free(uvp, M_80211_VAP); @@ -657,7 +660,11 @@ urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, int *rssi_p) * This should not happen since we setup our Rx filter * to not receive these frames. */ - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + return (NULL); + } + if (pktlen < sizeof(*wh) || pktlen > MCLBYTES) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } @@ -666,7 +673,7 @@ urtwn_rx_frame(struct urtwn_softc *sc, uint8_t *buf, int pktlen, int *rssi_p) /* Get RSSI from PHY status descriptor if present. */ if (infosz != 0 && (rxdw0 & R92C_RXDW0_PHYST)) { - if (sc->chip & URTWN_CHIP_88E) + if (sc->chip & URTWN_CHIP_88E) rssi = urtwn_r88e_get_rssi(sc, rate, &stat[1]); else rssi = urtwn_get_rssi(sc, rate, &stat[1]); @@ -742,7 +749,7 @@ urtwn_rxeof(struct usb_xfer *xfer, struct urtwn_data *data, int *rssi, usbd_xfer_status(xfer, &len, NULL, NULL, NULL); if (len < sizeof(*stat)) { - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return (NULL); } @@ -857,7 +864,7 @@ tr_setup: } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); goto tr_setup; } break; @@ -891,7 +898,7 @@ urtwn_txeof(struct usb_xfer *xfer, struct urtwn_data *data) data->ni = NULL; } sc->sc_txtimer = 0; - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; } @@ -933,7 +940,7 @@ tr_setup: if (data->ni != NULL) { ieee80211_free_node(data->ni); data->ni = NULL; - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } if (error != USB_ERR_CANCELLED) { usbd_xfer_set_stall(xfer); @@ -1177,7 +1184,7 @@ urtwn_efuse_read_1(struct urtwn_softc *sc, uint16_t addr) return (MS(reg, R92C_EFUSE_CTRL_DATA)); urtwn_ms_delay(sc); } - device_printf(sc->sc_dev, + device_printf(sc->sc_dev, "could not read efuse byte at address 0x%x\n", addr); return (0xff); } @@ -1188,7 +1195,7 @@ urtwn_efuse_read(struct urtwn_softc *sc) uint8_t *rom = (uint8_t *)&sc->rom; uint16_t addr = 0; uint32_t reg; - uint8_t off, msk, vol; + uint8_t off, msk; int i; urtwn_efuse_switch_power(sc); @@ -1221,18 +1228,15 @@ urtwn_efuse_read(struct urtwn_softc *sc) printf("\n"); } #endif - /* Disable LDO 2.5V. */ - vol = urtwn_read_1(sc, R92C_EFUSE_TEST + 3); - urtwn_write_1(sc, R92C_EFUSE_TEST + 3, vol & ~(0x80)); - + urtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_OFF); } + static void urtwn_efuse_switch_power(struct urtwn_softc *sc) { uint32_t reg; - if (sc->chip & URTWN_CHIP_88E) - urtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_ON); + urtwn_write_1(sc, R92C_EFUSE_ACCESS, R92C_EFUSE_ACCESS_ON); reg = urtwn_read_2(sc, R92C_SYS_ISO_CTRL); if (!(reg & R92C_SYS_ISO_CTRL_PWC_EV12V)) { @@ -1250,16 +1254,6 @@ urtwn_efuse_switch_power(struct urtwn_softc *sc) urtwn_write_2(sc, R92C_SYS_CLKR, reg | R92C_SYS_CLKR_LOADER_EN | R92C_SYS_CLKR_ANA8M); } - - if (!(sc->chip & URTWN_CHIP_88E)) { - uint8_t vol; - - /* Enable LDO 2.5V. */ - vol = urtwn_read_1(sc, R92C_EFUSE_TEST + 3); - vol &= 0x0f; - vol |= 0x30; - urtwn_write_1(sc, R92C_EFUSE_TEST + 3, (vol | 0x80)); - } } static int @@ -1489,7 +1483,7 @@ static void urtwn_set_led(struct urtwn_softc *sc, int led, int on) { uint8_t reg; - + if (led == URTWN_LED_LINK) { if (sc->chip & URTWN_CHIP_88E) { reg = urtwn_read_1(sc, R92C_LEDCFG2) & 0xf0; @@ -1650,7 +1644,7 @@ urtwn_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) if (sc->chip & URTWN_CHIP_88E) ni->ni_txrate = ni->ni_rates.rs_rates[ni->ni_rates.rs_nrates-1]; - else + else urtwn_ra_init(sc); /* Turn link LED on. */ urtwn_set_led(sc, URTWN_LED_LINK, 1); @@ -1678,7 +1672,7 @@ urtwn_watchdog(void *arg) if (sc->sc_txtimer > 0) { if (--sc->sc_txtimer == 0) { device_printf(sc->sc_dev, "device timeout\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return; } callout_reset(&sc->sc_watchdog_ch, hz, urtwn_watchdog, sc); @@ -1761,7 +1755,7 @@ urtwn_r88e_get_rssi(struct urtwn_softc *sc, int rate, void *physt) cck = (struct r88e_rx_cck *)physt; cck_agc_rpt = cck->agc_rpt; lna_idx = (cck_agc_rpt & 0xe0) >> 5; - vga_idx = cck_agc_rpt & 0x1f; + vga_idx = cck_agc_rpt & 0x1f; switch (lna_idx) { case 7: if (vga_idx <= 27) @@ -1801,7 +1795,7 @@ urtwn_r88e_get_rssi(struct urtwn_softc *sc, int rate, void *physt) static int -urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni, +urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni, struct mbuf *m0, struct urtwn_data *data) { struct ifnet *ifp = sc->sc_ifp; @@ -1842,7 +1836,7 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni, /* in case packet header moved, reset pointer */ wh = mtod(m0, struct ieee80211_frame *); } - + switch (type) { case IEEE80211_FC0_TYPE_CTL: case IEEE80211_FC0_TYPE_MGT: @@ -1854,7 +1848,7 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni, xfer = urtwn_pipes[M_WME_GETAC(m0)]; break; } - + hasqos = 0; /* Fill Tx descriptor. */ @@ -1898,10 +1892,7 @@ urtwn_tx_start(struct urtwn_softc *sc, struct ieee80211_node *ni, txd->txdw4 |= htole32(SM(R92C_TXDW4_RTSRATE, 8)); txd->txdw5 |= htole32(0x0001ff00); /* Send data at OFDM54. */ - if (sc->chip & URTWN_CHIP_88E) - txd->txdw5 |= htole32(0x13 & 0x3f); - else - txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11)); + txd->txdw5 |= htole32(SM(R92C_TXDW5_DATARATE, 11)); } else { txd->txdw1 |= htole32( SM(R92C_TXDW1_MACID, 0) | @@ -1982,7 +1973,7 @@ urtwn_start_locked(struct ifnet *ifp, struct urtwn_softc *sc) m->m_pkthdr.rcvif = NULL; if (urtwn_tx_start(sc, ni, m, bf) != 0) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); ieee80211_free_node(ni); break; @@ -2332,7 +2323,7 @@ urtwn_fw_loadpage(struct urtwn_softc *sc, int page, const uint8_t *buf, int len) else mlen = 1; /* XXX fix this deconst */ - error = urtwn_write_region_1(sc, off, + error = urtwn_write_region_1(sc, off, __DECONST(uint8_t *, buf), mlen); if (error != 0) break; @@ -2467,7 +2458,7 @@ fail: static __inline int urtwn_dma_init(struct urtwn_softc *sc) { - + return sc->sc_dma_init(sc); } @@ -3235,10 +3226,10 @@ urtwn_set_chan(struct urtwn_softc *sc, struct ieee80211_channel *c, urtwn_bb_read(sc, R92C_FPGA0_ANAPARAM2) | R92C_FPGA0_ANAPARAM2_CBW20); } - + /* Select 20MHz bandwidth. */ urtwn_rf_write(sc, 0, R92C_RF_CHNLBW, - (sc->rf_chnlbw[0] & ~0xfff) | chan | + (sc->rf_chnlbw[0] & ~0xfff) | chan | ((sc->chip & URTWN_CHIP_88E) ? R88E_RF_CHNLBW_BW20 : R92C_RF_CHNLBW_BW20)); } @@ -3313,7 +3304,7 @@ urtwn_init_locked(void *arg) error = urtwn_alloc_rx_list(sc); if (error != 0) goto fail; - + error = urtwn_alloc_tx_list(sc); if (error != 0) goto fail; @@ -3357,6 +3348,7 @@ urtwn_init_locked(void *arg) urtwn_rxfilter_init(sc); + /* Set response rate. */ reg = urtwn_read_4(sc, R92C_RRSR); reg = RW(reg, R92C_RRSR_RATE_BITMAP, R92C_RRSR_RATE_CCK_ONLY_1M); urtwn_write_4(sc, R92C_RRSR, reg); @@ -3548,10 +3540,10 @@ urtwn_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, return (ENOBUFS); } - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); if (urtwn_tx_start(sc, ni, m, bf) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); STAILQ_INSERT_HEAD(&sc->sc_tx_inactive, bf, next); URTWN_UNLOCK(sc); return (EIO); diff --git a/sys/dev/usb/wlan/if_urtwnreg.h b/sys/dev/usb/wlan/if_urtwnreg.h index 4eef029..2bbec7a 100644 --- a/sys/dev/usb/wlan/if_urtwnreg.h +++ b/sys/dev/usb/wlan/if_urtwnreg.h @@ -177,13 +177,13 @@ #define R92C_RD_NAV_NXT 0x544 #define R92C_NAV_PROT_LEN 0x546 #define R92C_BCN_CTRL 0x550 -#define R92C_USTIME_TSF 0x551 #define R92C_MBID_NUM 0x552 #define R92C_DUAL_TSF_RST 0x553 #define R92C_BCN_INTERVAL 0x554 #define R92C_DRVERLYINT 0x558 #define R92C_BCNDMATIM 0x559 #define R92C_ATIMWND 0x55a +#define R92C_USTIME_TSF 0x55c #define R92C_BCN_MAX_ERR 0x55d #define R92C_RXTSF_OFFSET_CCK 0x55e #define R92C_RXTSF_OFFSET_OFDM 0x55f diff --git a/sys/dev/usb/wlan/if_zyd.c b/sys/dev/usb/wlan/if_zyd.c index 35a064b..ca1fb06 100644 --- a/sys/dev/usb/wlan/if_zyd.c +++ b/sys/dev/usb/wlan/if_zyd.c @@ -48,6 +48,7 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -79,7 +80,7 @@ __FBSDID("$FreeBSD$"); static int zyd_debug = 0; static SYSCTL_NODE(_hw_usb, OID_AUTO, zyd, CTLFLAG_RW, 0, "USB zyd"); -SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RW, &zyd_debug, 0, +SYSCTL_INT(_hw_usb_zyd, OID_AUTO, debug, CTLFLAG_RWTUN, &zyd_debug, 0, "zyd debug level"); enum { @@ -678,7 +679,7 @@ zyd_intr_read_callback(struct usb_xfer *xfer, usb_error_t error) ieee80211_free_node(ni); } if (le16toh(retry->count) & 0x100) - ifp->if_oerrors++; /* too many retries */ + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); /* too many retries */ break; } case ZYD_NOTIF_IORD: @@ -2187,7 +2188,7 @@ zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len) if (len < ZYD_MIN_FRAGSZ) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too short (length=%d)\n", device_get_nameunit(sc->sc_dev), len); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } pc = usbd_xfer_get_frame(xfer, 0); @@ -2198,7 +2199,7 @@ zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len) DPRINTF(sc, ZYD_DEBUG_RECV, "%s: RX status indicated error (%x)\n", device_get_nameunit(sc->sc_dev), stat.flags); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } @@ -2210,7 +2211,7 @@ zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len) if (rlen > (int)MCLBYTES) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: frame too long (length=%d)\n", device_get_nameunit(sc->sc_dev), rlen); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } else if (rlen > (int)MHLEN) m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); @@ -2219,7 +2220,7 @@ zyd_rx_data(struct usb_xfer *xfer, int offset, uint16_t len) if (m == NULL) { DPRINTF(sc, ZYD_DEBUG_RECV, "%s: could not allocate rx mbuf\n", device_get_nameunit(sc->sc_dev)); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); return; } m->m_pkthdr.rcvif = ifp; @@ -2402,7 +2403,7 @@ zyd_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) zyd_tx_free(data, 0); usbd_xfer_set_priv(xfer, NULL); - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; /* FALLTHROUGH */ @@ -2446,7 +2447,7 @@ tr_setup: DPRINTF(sc, ZYD_DEBUG_ANY, "transfer error, %s\n", usbd_errstr(error)); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); data = usbd_xfer_get_priv(xfer); usbd_xfer_set_priv(xfer, NULL); if (data != NULL) @@ -2607,7 +2608,7 @@ zyd_start(struct ifnet *ifp) ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (zyd_tx_start(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); break; } } @@ -2645,7 +2646,7 @@ zyd_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, */ if (zyd_tx_start(sc, m, ni) != 0) { ZYD_UNLOCK(sc); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); return (EIO); } diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c index 5442d3d..6d58c5c 100644 --- a/sys/dev/wi/if_wi.c +++ b/sys/dev/wi/if_wi.c @@ -87,6 +87,7 @@ __FBSDID("$FreeBSD$"); #include <sys/rman.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> @@ -131,6 +132,7 @@ static int wi_reset(struct wi_softc *); static void wi_watchdog(void *); static int wi_ioctl(struct ifnet *, u_long, caddr_t); static void wi_media_status(struct ifnet *, struct ifmediareq *); +static uint64_t wi_get_counter(struct ifnet *, ift_counter); static void wi_rx_intr(struct wi_softc *); static void wi_tx_intr(struct wi_softc *); @@ -336,6 +338,7 @@ wi_attach(device_t dev) ifp->if_ioctl = wi_ioctl; ifp->if_start = wi_start; ifp->if_init = wi_init; + //ifp->if_get_counter = wi_get_counter; IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; IFQ_SET_READY(&ifp->if_snd); @@ -1027,7 +1030,7 @@ wi_start_locked(struct ifnet *ifp) continue; sc->sc_txnext = cur = (cur + 1) % sc->sc_ntxbuf; - ifp->if_opackets++; + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); } } @@ -1054,7 +1057,7 @@ wi_start_tx(struct ifnet *ifp, struct wi_frame *frmhdr, struct mbuf *m0) || wi_mwrite_bap(sc, fid, off, m0, m0->m_pkthdr.len) != 0; m_freem(m0); if (error) { - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); return -1; } sc->sc_txd[cur].d_len = off; @@ -1181,7 +1184,7 @@ wi_watchdog(void *arg) if (sc->sc_tx_timer && --sc->sc_tx_timer == 0) { if_printf(ifp, "device timeout\n"); - ifp->if_oerrors++; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); wi_init_locked(ifp->if_softc); return; } @@ -1326,7 +1329,7 @@ wi_rx_intr(struct wi_softc *sc) /* First read in the frame header */ if (wi_read_bap(sc, fid, 0, &frmhdr, sizeof(frmhdr))) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF(("wi_rx_intr: read fid %x failed\n", fid)); return; } @@ -1337,7 +1340,7 @@ wi_rx_intr(struct wi_softc *sc) status = le16toh(frmhdr.wi_status); if (status & WI_STAT_ERRSTAT) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF(("wi_rx_intr: fid %x error status %x\n", fid, status)); return; } @@ -1352,7 +1355,7 @@ wi_rx_intr(struct wi_softc *sc) if (off + len > MCLBYTES) { if (ic->ic_opmode != IEEE80211_M_MONITOR) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF(("wi_rx_intr: oversized packet\n")); return; } else @@ -1365,7 +1368,7 @@ wi_rx_intr(struct wi_softc *sc) m = m_gethdr(M_NOWAIT, MT_DATA); if (m == NULL) { CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_RX); - ifp->if_ierrors++; + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); DPRINTF(("wi_rx_intr: MGET failed\n")); return; } @@ -1449,13 +1452,13 @@ wi_tx_ex_intr(struct wi_softc *sc) printf(", status=0x%x", status); printf("\n"); } - ifp->if_oerrors++; - } else { + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + } else DPRINTF(("port disconnected\n")); - ifp->if_collisions++; /* XXX */ - } - } else + } else { DPRINTF(("wi_tx_ex_intr: read fid %x failed\n", fid)); + ifp->if_collisions++; /* XXX */ + } CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_TX_EXC); } @@ -1496,8 +1499,7 @@ wi_tx_intr(struct wi_softc *sc) static __noinline void wi_info_intr(struct wi_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211com *ic = sc->sc_ifp->if_l2com; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); int i, fid, len, off; u_int16_t ltbuf[2]; @@ -1561,9 +1563,9 @@ wi_info_intr(struct wi_softc *sc) #endif *ptr += stat; } - ifp->if_collisions = sc->sc_stats.wi_tx_single_retries + - sc->sc_stats.wi_tx_multi_retries + - sc->sc_stats.wi_tx_retry_limit; + sc->sc_ifp->if_collisions = sc->sc_stats.wi_tx_single_retries + + sc->sc_stats.wi_tx_multi_retries + + sc->sc_stats.wi_tx_retry_limit; break; default: DPRINTF(("wi_info_intr: got fid %x type %x len %d\n", fid, @@ -1574,6 +1576,27 @@ finish: CSR_WRITE_2(sc, WI_EVENT_ACK, WI_EV_INFO); } +static uint64_t +wi_get_counter(struct ifnet *ifp, ift_counter cnt) +{ +#if 0 + struct wi_softc *sc; + + sc = if_getsoftc(ifp); + + switch (cnt) { + case IFCOUNTER_COLLISIONS: + return (sc->sc_stats.wi_tx_single_retries + + sc->sc_stats.wi_tx_multi_retries + + sc->sc_stats.wi_tx_retry_limit); + default: + return (if_get_counter_default(ifp, cnt)); + } +#else + return 0; +#endif +} + static int wi_write_multi(struct wi_softc *sc) { diff --git a/sys/dev/wi/if_wi_pccard.c b/sys/dev/wi/if_wi_pccard.c index 382a3a3..fd70e77 100644 --- a/sys/dev/wi/if_wi_pccard.c +++ b/sys/dev/wi/if_wi_pccard.c @@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/socket.h> #include <sys/systm.h> #include <sys/module.h> @@ -62,7 +63,6 @@ __FBSDID("$FreeBSD$"); #include <net80211/ieee80211_var.h> #include <net80211/ieee80211_radiotap.h> -#define PCCARD_API_LEVEL 6 #include <dev/pccard/pccardvar.h> #include <dev/pccard/pccard_cis.h> diff --git a/sys/dev/wi/if_wi_pci.c b/sys/dev/wi/if_wi_pci.c index 30cdbc4..198c599 100644 --- a/sys/dev/wi/if_wi_pci.c +++ b/sys/dev/wi/if_wi_pci.c @@ -55,6 +55,7 @@ #include <dev/pci/pcivar.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_media.h> diff --git a/sys/dev/wpi/if_wpi.c b/sys/dev/wpi/if_wpi.c index 6b3929e..7b438c7 100644 --- a/sys/dev/wpi/if_wpi.c +++ b/sys/dev/wpi/if_wpi.c @@ -16,8 +16,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define VERSION "20071127" - #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); @@ -44,7 +42,7 @@ __FBSDID("$FreeBSD$"); * * A similar thing happens with the tx rings. The difference is the firmware * stop processing buffers once the queue is full and until confirmation - * of a successful transmition (tx_intr) has occurred. + * of a successful transmition (tx_done) has occurred. * * The command ring operates in the same manner as the tx queues. * @@ -60,6 +58,7 @@ __FBSDID("$FreeBSD$"); */ #include "opt_wlan.h" +#include "opt_wpi.h" #include <sys/param.h> #include <sys/sysctl.h> @@ -86,58 +85,27 @@ __FBSDID("$FreeBSD$"); #include <net/bpf.h> #include <net/if.h> +#include <net/if_var.h> #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> -#include <net80211/ieee80211_var.h> -#include <net80211/ieee80211_radiotap.h> -#include <net80211/ieee80211_regdomain.h> -#include <net80211/ieee80211_ratectl.h> - #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/in_var.h> -#include <netinet/ip.h> #include <netinet/if_ether.h> +#include <netinet/ip.h> + +#include <net80211/ieee80211_var.h> +#include <net80211/ieee80211_radiotap.h> +#include <net80211/ieee80211_regdomain.h> +#include <net80211/ieee80211_ratectl.h> #include <dev/wpi/if_wpireg.h> #include <dev/wpi/if_wpivar.h> - -#define WPI_DEBUG - -#ifdef WPI_DEBUG -#define DPRINTF(x) do { if (wpi_debug != 0) printf x; } while (0) -#define DPRINTFN(n, x) do { if (wpi_debug & n) printf x; } while (0) -#define WPI_DEBUG_SET (wpi_debug != 0) - -enum { - WPI_DEBUG_UNUSED = 0x00000001, /* Unused */ - WPI_DEBUG_HW = 0x00000002, /* Stage 1 (eeprom) debugging */ - WPI_DEBUG_TX = 0x00000004, /* Stage 2 TX intrp debugging*/ - WPI_DEBUG_RX = 0x00000008, /* Stage 2 RX intrp debugging */ - WPI_DEBUG_CMD = 0x00000010, /* Stage 2 CMD intrp debugging*/ - WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ - WPI_DEBUG_DMA = 0x00000040, /* DMA (de)allocations/syncs */ - WPI_DEBUG_SCANNING = 0x00000080, /* Stage 2 Scanning debugging */ - WPI_DEBUG_NOTIFY = 0x00000100, /* State 2 Noftif intr debug */ - WPI_DEBUG_TEMP = 0x00000200, /* TXPower/Temp Calibration */ - WPI_DEBUG_OPS = 0x00000400, /* wpi_ops taskq debug */ - WPI_DEBUG_WATCHDOG = 0x00000800, /* Watch dog debug */ - WPI_DEBUG_ANY = 0xffffffff -}; - -static int wpi_debug = 0; -SYSCTL_INT(_debug, OID_AUTO, wpi, CTLFLAG_RW, &wpi_debug, 0, "wpi debug level"); -TUNABLE_INT("debug.wpi", &wpi_debug); - -#else -#define DPRINTF(x) -#define DPRINTFN(n, x) -#define WPI_DEBUG_SET 0 -#endif +#include <dev/wpi/if_wpi_debug.h> struct wpi_ident { uint16_t vendor; @@ -158,99 +126,160 @@ static const struct wpi_ident wpi_ident_table[] = { { 0, 0, 0, NULL } }; +static int wpi_probe(device_t); +static int wpi_attach(device_t); +static void wpi_radiotap_attach(struct wpi_softc *); +static void wpi_sysctlattach(struct wpi_softc *); +static void wpi_init_beacon(struct wpi_vap *); static struct ieee80211vap *wpi_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void wpi_vap_delete(struct ieee80211vap *); +static int wpi_detach(device_t); +static int wpi_shutdown(device_t); +static int wpi_suspend(device_t); +static int wpi_resume(device_t); +static int wpi_nic_lock(struct wpi_softc *); +static int wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); +static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int wpi_dma_contig_alloc(struct wpi_softc *, struct wpi_dma_info *, - void **, bus_size_t, bus_size_t, int); + void **, bus_size_t, bus_size_t); static void wpi_dma_contig_free(struct wpi_dma_info *); -static void wpi_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int wpi_alloc_shared(struct wpi_softc *); static void wpi_free_shared(struct wpi_softc *); -static int wpi_alloc_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); -static void wpi_reset_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); -static void wpi_free_rx_ring(struct wpi_softc *, struct wpi_rx_ring *); +static int wpi_alloc_fwmem(struct wpi_softc *); +static void wpi_free_fwmem(struct wpi_softc *); +static int wpi_alloc_rx_ring(struct wpi_softc *); +static void wpi_update_rx_ring(struct wpi_softc *); +static void wpi_update_rx_ring_ps(struct wpi_softc *); +static void wpi_reset_rx_ring(struct wpi_softc *); +static void wpi_free_rx_ring(struct wpi_softc *); static int wpi_alloc_tx_ring(struct wpi_softc *, struct wpi_tx_ring *, - int, int); + int); +static void wpi_update_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +static void wpi_update_tx_ring_ps(struct wpi_softc *, + struct wpi_tx_ring *); static void wpi_reset_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); static void wpi_free_tx_ring(struct wpi_softc *, struct wpi_tx_ring *); +static int wpi_read_eeprom(struct wpi_softc *, + uint8_t macaddr[IEEE80211_ADDR_LEN]); +static uint32_t wpi_eeprom_channel_flags(struct wpi_eeprom_chan *); +static void wpi_read_eeprom_band(struct wpi_softc *, int); +static int wpi_read_eeprom_channels(struct wpi_softc *, int); +static struct wpi_eeprom_chan *wpi_find_eeprom_channel(struct wpi_softc *, + struct ieee80211_channel *); +static int wpi_setregdomain(struct ieee80211com *, + struct ieee80211_regdomain *, int, + struct ieee80211_channel[]); +static int wpi_read_eeprom_group(struct wpi_softc *, int); +static int wpi_add_node_entry_adhoc(struct wpi_softc *); +static struct ieee80211_node *wpi_node_alloc(struct ieee80211vap *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static void wpi_node_free(struct ieee80211_node *); +static void wpi_recv_mgmt(struct ieee80211_node *, struct mbuf *, int, int, + int); +static void wpi_restore_node(void *, struct ieee80211_node *); +static void wpi_restore_node_table(struct wpi_softc *, struct wpi_vap *); static int wpi_newstate(struct ieee80211vap *, enum ieee80211_state, int); -static void wpi_mem_lock(struct wpi_softc *); -static void wpi_mem_unlock(struct wpi_softc *); -static uint32_t wpi_mem_read(struct wpi_softc *, uint16_t); -static void wpi_mem_write(struct wpi_softc *, uint16_t, uint32_t); -static void wpi_mem_write_region_4(struct wpi_softc *, uint16_t, - const uint32_t *, int); -static uint16_t wpi_read_prom_data(struct wpi_softc *, uint32_t, void *, int); -static int wpi_alloc_fwmem(struct wpi_softc *); -static void wpi_free_fwmem(struct wpi_softc *); -static int wpi_load_firmware(struct wpi_softc *); -static void wpi_unload_firmware(struct wpi_softc *); -static int wpi_load_microcode(struct wpi_softc *, const uint8_t *, int); -static void wpi_rx_intr(struct wpi_softc *, struct wpi_rx_desc *, +static void wpi_calib_timeout(void *); +static void wpi_rx_done(struct wpi_softc *, struct wpi_rx_desc *, + struct wpi_rx_data *); +static void wpi_rx_statistics(struct wpi_softc *, struct wpi_rx_desc *, struct wpi_rx_data *); -static void wpi_tx_intr(struct wpi_softc *, struct wpi_rx_desc *); -static void wpi_cmd_intr(struct wpi_softc *, struct wpi_rx_desc *); +static void wpi_tx_done(struct wpi_softc *, struct wpi_rx_desc *); +static void wpi_cmd_done(struct wpi_softc *, struct wpi_rx_desc *); static void wpi_notif_intr(struct wpi_softc *); +static void wpi_wakeup_intr(struct wpi_softc *); +#ifdef WPI_DEBUG +static void wpi_debug_registers(struct wpi_softc *); +#endif +static void wpi_fatal_intr(struct wpi_softc *); static void wpi_intr(void *); -static uint8_t wpi_plcp_signal(int); -static void wpi_watchdog(void *); +static int wpi_cmd2(struct wpi_softc *, struct wpi_buf *); static int wpi_tx_data(struct wpi_softc *, struct mbuf *, - struct ieee80211_node *, int); -static void wpi_start(struct ifnet *); -static void wpi_start_locked(struct ifnet *); + struct ieee80211_node *); +static int wpi_tx_data_raw(struct wpi_softc *, struct mbuf *, + struct ieee80211_node *, + const struct ieee80211_bpf_params *); static int wpi_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); -static void wpi_scan_start(struct ieee80211com *); -static void wpi_scan_end(struct ieee80211com *); -static void wpi_set_channel(struct ieee80211com *); -static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); -static void wpi_scan_mindwell(struct ieee80211_scan_state *); +static void wpi_start(struct ifnet *); +static void wpi_start_task(void *, int); +static void wpi_watchdog_rfkill(void *); +static void wpi_scan_timeout(void *); +static void wpi_tx_timeout(void *); static int wpi_ioctl(struct ifnet *, u_long, caddr_t); -static void wpi_read_eeprom(struct wpi_softc *, - uint8_t macaddr[IEEE80211_ADDR_LEN]); -static void wpi_read_eeprom_channels(struct wpi_softc *, int); -static void wpi_read_eeprom_group(struct wpi_softc *, int); -static int wpi_cmd(struct wpi_softc *, int, const void *, int, int); -static int wpi_wme_update(struct ieee80211com *); +static int wpi_cmd(struct wpi_softc *, int, const void *, size_t, int); static int wpi_mrr_setup(struct wpi_softc *); +static int wpi_add_node(struct wpi_softc *, struct ieee80211_node *); +static int wpi_add_broadcast_node(struct wpi_softc *, int); +static int wpi_add_ibss_node(struct wpi_softc *, struct ieee80211_node *); +static void wpi_del_node(struct wpi_softc *, struct ieee80211_node *); +static int wpi_updateedca(struct ieee80211com *); +static void wpi_set_promisc(struct wpi_softc *); +static void wpi_update_promisc(struct ifnet *); +static void wpi_update_mcast(struct ifnet *); static void wpi_set_led(struct wpi_softc *, uint8_t, uint8_t, uint8_t); -static void wpi_enable_tsf(struct wpi_softc *, struct ieee80211_node *); -#if 0 -static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); -#endif +static int wpi_set_timing(struct wpi_softc *, struct ieee80211_node *); +static void wpi_power_calibration(struct wpi_softc *); +static int wpi_set_txpower(struct wpi_softc *, int); +static int wpi_get_power_index(struct wpi_softc *, + struct wpi_power_group *, uint8_t, int, int); +static int wpi_set_pslevel(struct wpi_softc *, uint8_t, int, int); +static int wpi_send_btcoex(struct wpi_softc *); +static int wpi_send_rxon(struct wpi_softc *, int, int); +static int wpi_config(struct wpi_softc *); +static uint16_t wpi_get_active_dwell_time(struct wpi_softc *, + struct ieee80211_channel *, uint8_t); +static uint16_t wpi_limit_dwell(struct wpi_softc *, uint16_t); +static uint16_t wpi_get_passive_dwell_time(struct wpi_softc *, + struct ieee80211_channel *); +static uint32_t wpi_get_scan_pause_time(uint32_t, uint16_t); +static int wpi_scan(struct wpi_softc *, struct ieee80211_channel *); static int wpi_auth(struct wpi_softc *, struct ieee80211vap *); +static int wpi_config_beacon(struct wpi_vap *); +static int wpi_setup_beacon(struct wpi_softc *, struct ieee80211_node *); +static void wpi_update_beacon(struct ieee80211vap *, int); +static void wpi_newassoc(struct ieee80211_node *, int); static int wpi_run(struct wpi_softc *, struct ieee80211vap *); -static int wpi_scan(struct wpi_softc *); -static int wpi_config(struct wpi_softc *); -static void wpi_stop_master(struct wpi_softc *); -static int wpi_power_up(struct wpi_softc *); -static int wpi_reset(struct wpi_softc *); -static void wpi_hwreset(void *, int); -static void wpi_rfreset(void *, int); -static void wpi_hw_config(struct wpi_softc *); +static int wpi_load_key(struct ieee80211_node *, + const struct ieee80211_key *); +static void wpi_load_key_cb(void *, struct ieee80211_node *); +static int wpi_set_global_keys(struct ieee80211_node *); +static int wpi_del_key(struct ieee80211_node *, + const struct ieee80211_key *); +static void wpi_del_key_cb(void *, struct ieee80211_node *); +static int wpi_process_key(struct ieee80211vap *, + const struct ieee80211_key *, int); +static int wpi_key_set(struct ieee80211vap *, + const struct ieee80211_key *, + const uint8_t mac[IEEE80211_ADDR_LEN]); +static int wpi_key_delete(struct ieee80211vap *, + const struct ieee80211_key *); +static int wpi_post_alive(struct wpi_softc *); +static int wpi_load_bootcode(struct wpi_softc *, const uint8_t *, int); +static int wpi_load_firmware(struct wpi_softc *); +static int wpi_read_firmware(struct wpi_softc *); +static void wpi_unload_firmware(struct wpi_softc *); +static int wpi_clock_wait(struct wpi_softc *); +static int wpi_apm_init(struct wpi_softc *); +static void wpi_apm_stop_master(struct wpi_softc *); +static void wpi_apm_stop(struct wpi_softc *); +static void wpi_nic_config(struct wpi_softc *); +static int wpi_hw_init(struct wpi_softc *); +static void wpi_hw_stop(struct wpi_softc *); +static void wpi_radio_on(void *, int); +static void wpi_radio_off(void *, int); static void wpi_init(void *); -static void wpi_init_locked(struct wpi_softc *, int); -static void wpi_stop(struct wpi_softc *); static void wpi_stop_locked(struct wpi_softc *); - -static int wpi_set_txpower(struct wpi_softc *, struct ieee80211_channel *, - int); -static void wpi_calib_timeout(void *); -static void wpi_power_calibration(struct wpi_softc *, int); -static int wpi_get_power_index(struct wpi_softc *, - struct wpi_power_group *, struct ieee80211_channel *, int); -#ifdef WPI_DEBUG -static const char *wpi_cmd_str(int); -#endif -static int wpi_probe(device_t); -static int wpi_attach(device_t); -static int wpi_detach(device_t); -static int wpi_shutdown(device_t); -static int wpi_suspend(device_t); -static int wpi_resume(device_t); +static void wpi_stop(struct wpi_softc *); +static void wpi_scan_start(struct ieee80211com *); +static void wpi_scan_end(struct ieee80211com *); +static void wpi_set_channel(struct ieee80211com *); +static void wpi_scan_curchan(struct ieee80211_scan_state *, unsigned long); +static void wpi_scan_mindwell(struct ieee80211_scan_state *); +static void wpi_hw_reset(void *, int); static device_method_t wpi_methods[] = { /* Device interface */ @@ -269,25 +298,15 @@ static driver_t wpi_driver = { wpi_methods, sizeof (struct wpi_softc) }; - static devclass_t wpi_devclass; DRIVER_MODULE(wpi, pci, wpi_driver, wpi_devclass, NULL, NULL); MODULE_VERSION(wpi, 1); -static const uint8_t wpi_ridx_to_plcp[] = { - /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ - /* R1-R4 (ral/ural is R4-R1) */ - 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, - /* CCK: device-dependent */ - 10, 20, 55, 110 -}; - -static const uint8_t wpi_ridx_to_rate[] = { - 12, 18, 24, 36, 48, 72, 96, 108, /* OFDM */ - 2, 4, 11, 22 /*CCK */ -}; +MODULE_DEPEND(wpi, pci, 1, 1, 1); +MODULE_DEPEND(wpi, wlan, 1, 1, 1); +MODULE_DEPEND(wpi, firmware, 1, 1, 1); static int wpi_probe(device_t dev) @@ -304,208 +323,48 @@ wpi_probe(device_t dev) return ENXIO; } -/** - * Load the firmare image from disk to the allocated dma buffer. - * we also maintain the reference to the firmware pointer as there - * is times where we may need to reload the firmware but we are not - * in a context that can access the filesystem (ie taskq cause by restart) - * - * @return 0 on success, an errno on failure - */ -static int -wpi_load_firmware(struct wpi_softc *sc) -{ - const struct firmware *fp; - struct wpi_dma_info *dma = &sc->fw_dma; - const struct wpi_firmware_hdr *hdr; - const uint8_t *itext, *idata, *rtext, *rdata, *btext; - uint32_t itextsz, idatasz, rtextsz, rdatasz, btextsz; - int error; - - DPRINTFN(WPI_DEBUG_FIRMWARE, - ("Attempting Loading Firmware from wpi_fw module\n")); - - WPI_UNLOCK(sc); - - if (sc->fw_fp == NULL && (sc->fw_fp = firmware_get("wpifw")) == NULL) { - device_printf(sc->sc_dev, - "could not load firmware image 'wpifw'\n"); - error = ENOENT; - WPI_LOCK(sc); - goto fail; - } - - fp = sc->fw_fp; - - WPI_LOCK(sc); - - /* Validate the firmware is minimum a particular version */ - if (fp->version < WPI_FW_MINVERSION) { - device_printf(sc->sc_dev, - "firmware version is too old. Need %d, got %d\n", - WPI_FW_MINVERSION, - fp->version); - error = ENXIO; - goto fail; - } - - if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { - device_printf(sc->sc_dev, - "firmware file too short: %zu bytes\n", fp->datasize); - error = ENXIO; - goto fail; - } - - hdr = (const struct wpi_firmware_hdr *)fp->data; - - /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | - |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ - - rtextsz = le32toh(hdr->rtextsz); - rdatasz = le32toh(hdr->rdatasz); - itextsz = le32toh(hdr->itextsz); - idatasz = le32toh(hdr->idatasz); - btextsz = le32toh(hdr->btextsz); - - /* check that all firmware segments are present */ - if (fp->datasize < sizeof (struct wpi_firmware_hdr) + - rtextsz + rdatasz + itextsz + idatasz + btextsz) { - device_printf(sc->sc_dev, - "firmware file too short: %zu bytes\n", fp->datasize); - error = ENXIO; /* XXX appropriate error code? */ - goto fail; - } - - /* get pointers to firmware segments */ - rtext = (const uint8_t *)(hdr + 1); - rdata = rtext + rtextsz; - itext = rdata + rdatasz; - idata = itext + itextsz; - btext = idata + idatasz; - - DPRINTFN(WPI_DEBUG_FIRMWARE, - ("Firmware Version: Major %d, Minor %d, Driver %d, \n" - "runtime (text: %u, data: %u) init (text: %u, data %u) boot (text %u)\n", - (le32toh(hdr->version) & 0xff000000) >> 24, - (le32toh(hdr->version) & 0x00ff0000) >> 16, - (le32toh(hdr->version) & 0x0000ffff), - rtextsz, rdatasz, - itextsz, idatasz, btextsz)); - - DPRINTFN(WPI_DEBUG_FIRMWARE,("rtext 0x%x\n", *(const uint32_t *)rtext)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("rdata 0x%x\n", *(const uint32_t *)rdata)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("itext 0x%x\n", *(const uint32_t *)itext)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("idata 0x%x\n", *(const uint32_t *)idata)); - DPRINTFN(WPI_DEBUG_FIRMWARE,("btext 0x%x\n", *(const uint32_t *)btext)); - - /* sanity checks */ - if (rtextsz > WPI_FW_MAIN_TEXT_MAXSZ || - rdatasz > WPI_FW_MAIN_DATA_MAXSZ || - itextsz > WPI_FW_INIT_TEXT_MAXSZ || - idatasz > WPI_FW_INIT_DATA_MAXSZ || - btextsz > WPI_FW_BOOT_TEXT_MAXSZ || - (btextsz & 3) != 0) { - device_printf(sc->sc_dev, "firmware invalid\n"); - error = EINVAL; - goto fail; - } - - /* copy initialization images into pre-allocated DMA-safe memory */ - memcpy(dma->vaddr, idata, idatasz); - memcpy(dma->vaddr + WPI_FW_INIT_DATA_MAXSZ, itext, itextsz); - - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - /* tell adapter where to find initialization images */ - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); - wpi_mem_write(sc, WPI_MEM_DATA_SIZE, idatasz); - wpi_mem_write(sc, WPI_MEM_TEXT_BASE, - dma->paddr + WPI_FW_INIT_DATA_MAXSZ); - wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, itextsz); - wpi_mem_unlock(sc); - - /* load firmware boot code */ - if ((error = wpi_load_microcode(sc, btext, btextsz)) != 0) { - device_printf(sc->sc_dev, "Failed to load microcode\n"); - goto fail; - } - - /* now press "execute" */ - WPI_WRITE(sc, WPI_RESET, 0); - - /* wait at most one second for the first alive notification */ - if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize\n"); - goto fail; - } - - /* copy runtime images into pre-allocated DMA-sage memory */ - memcpy(dma->vaddr, rdata, rdatasz); - memcpy(dma->vaddr + WPI_FW_MAIN_DATA_MAXSZ, rtext, rtextsz); - bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - - /* tell adapter where to find runtime images */ - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_DATA_BASE, dma->paddr); - wpi_mem_write(sc, WPI_MEM_DATA_SIZE, rdatasz); - wpi_mem_write(sc, WPI_MEM_TEXT_BASE, - dma->paddr + WPI_FW_MAIN_DATA_MAXSZ); - wpi_mem_write(sc, WPI_MEM_TEXT_SIZE, WPI_FW_UPDATED | rtextsz); - wpi_mem_unlock(sc); - - /* wait at most one second for the first alive notification */ - if ((error = msleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { - device_printf(sc->sc_dev, - "timeout waiting for adapter to initialize2\n"); - goto fail; - } - - DPRINTFN(WPI_DEBUG_FIRMWARE, - ("Firmware loaded to driver successfully\n")); - return error; -fail: - wpi_unload_firmware(sc); - return error; -} - -/** - * Free the referenced firmware image - */ -static void -wpi_unload_firmware(struct wpi_softc *sc) -{ - - if (sc->fw_fp) { - WPI_UNLOCK(sc); - firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); - WPI_LOCK(sc); - sc->fw_fp = NULL; - } -} - static int wpi_attach(device_t dev) { - struct wpi_softc *sc = device_get_softc(dev); - struct ifnet *ifp; + struct wpi_softc *sc = (struct wpi_softc *)device_get_softc(dev); struct ieee80211com *ic; - int ac, error, rid, supportsa = 1; - uint32_t tmp; + struct ifnet *ifp; + int i, error, rid; +#ifdef WPI_DEBUG + int supportsa = 1; const struct wpi_ident *ident; +#endif uint8_t macaddr[IEEE80211_ADDR_LEN]; sc->sc_dev = dev; - if (bootverbose || WPI_DEBUG_SET) - device_printf(sc->sc_dev,"Driver Revision %s\n", VERSION); +#ifdef WPI_DEBUG + error = resource_int_value(device_get_name(sc->sc_dev), + device_get_unit(sc->sc_dev), "debug", &(sc->sc_debug)); + if (error != 0) + sc->sc_debug = 0; +#else + sc->sc_debug = 0; +#endif + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* + * Get the offset of the PCI Express Capability Structure in PCI + * Configuration Space. + */ + error = pci_find_cap(dev, PCIY_EXPRESS, &sc->sc_cap_off); + if (error != 0) { + device_printf(dev, "PCIe capability structure not found!\n"); + return error; + } /* * Some card's only support 802.11b/g not a, check to see if * this is one such card. A 0x0 in the subdevice table indicates * the entire subdevice range is to be ignored. */ +#ifdef WPI_DEBUG for (ident = wpi_ident_table; ident->name != NULL; ident++) { if (ident->subdevice && pci_get_subdevice(dev) == ident->subdevice) { @@ -513,143 +372,133 @@ wpi_attach(device_t dev) break; } } +#endif - /* Create the tasks that can be queued */ - TASK_INIT(&sc->sc_restarttask, 0, wpi_hwreset, sc); - TASK_INIT(&sc->sc_radiotask, 0, wpi_rfreset, sc); - - WPI_LOCK_INIT(sc); - - callout_init_mtx(&sc->calib_to, &sc->sc_mtx, 0); - callout_init_mtx(&sc->watchdog_to, &sc->sc_mtx, 0); - - /* disable the retry timeout register */ + /* Clear device-specific "PCI retry timeout" register (41h). */ pci_write_config(dev, 0x41, 0, 1); - /* enable bus-mastering */ + /* Enable bus-mastering. */ pci_enable_busmaster(dev); rid = PCIR_BAR(0); sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->mem == NULL) { - device_printf(dev, "could not allocate memory resource\n"); - error = ENOMEM; - goto fail; + device_printf(dev, "can't map mem space\n"); + return ENOMEM; } - sc->sc_st = rman_get_bustag(sc->mem); sc->sc_sh = rman_get_bushandle(sc->mem); + i = 1; rid = 0; - sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, - RF_ACTIVE | RF_SHAREABLE); + if (pci_alloc_msi(dev, &i) == 0) + rid = 1; + /* Install interrupt handler. */ + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | + (rid != 0 ? 0 : RF_SHAREABLE)); if (sc->irq == NULL) { - device_printf(dev, "could not allocate interrupt resource\n"); + device_printf(dev, "can't map interrupt\n"); error = ENOMEM; goto fail; } - /* - * Allocate DMA memory for firmware transfers. - */ - if ((error = wpi_alloc_fwmem(sc)) != 0) { - printf(": could not allocate firmware memory\n"); - error = ENOMEM; - goto fail; - } + WPI_LOCK_INIT(sc); + WPI_TX_LOCK_INIT(sc); + WPI_RXON_LOCK_INIT(sc); + WPI_NT_LOCK_INIT(sc); + WPI_TXQ_LOCK_INIT(sc); + WPI_TXQ_STATE_LOCK_INIT(sc); - /* - * Put adapter into a known state. - */ - if ((error = wpi_reset(sc)) != 0) { - device_printf(dev, "could not reset adapter\n"); + /* Allocate DMA memory for firmware transfers. */ + if ((error = wpi_alloc_fwmem(sc)) != 0) { + device_printf(dev, + "could not allocate memory for firmware, error %d\n", + error); goto fail; } - wpi_mem_lock(sc); - tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); - if (bootverbose || WPI_DEBUG_SET) - device_printf(sc->sc_dev, "Hardware Revision (0x%X)\n", tmp); - - wpi_mem_unlock(sc); - - /* Allocate shared page */ + /* Allocate shared page. */ if ((error = wpi_alloc_shared(sc)) != 0) { device_printf(dev, "could not allocate shared page\n"); goto fail; } - /* tx data queues - 4 for QoS purposes */ - for (ac = 0; ac < WME_NUM_AC; ac++) { - error = wpi_alloc_tx_ring(sc, &sc->txq[ac], WPI_TX_RING_COUNT, ac); - if (error != 0) { - device_printf(dev, "could not allocate Tx ring %d\n",ac); - goto fail; + /* Allocate TX rings - 4 for QoS purposes, 1 for commands. */ + for (i = 0; i < WPI_NTXQUEUES; i++) { + if ((error = wpi_alloc_tx_ring(sc, &sc->txq[i], i)) != 0) { + device_printf(dev, + "could not allocate TX ring %d, error %d\n", i, + error); + goto fail; } } - /* command queue to talk to the card's firmware */ - error = wpi_alloc_tx_ring(sc, &sc->cmdq, WPI_CMD_RING_COUNT, 4); - if (error != 0) { - device_printf(dev, "could not allocate command ring\n"); + /* Allocate RX ring. */ + if ((error = wpi_alloc_rx_ring(sc)) != 0) { + device_printf(dev, "could not allocate RX ring, error %d\n", + error); goto fail; } - /* receive data queue */ - error = wpi_alloc_rx_ring(sc, &sc->rxq); - if (error != 0) { - device_printf(dev, "could not allocate Rx ring\n"); - goto fail; - } + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); ifp = sc->sc_ifp = if_alloc(IFT_IEEE80211); if (ifp == NULL) { - device_printf(dev, "can not if_alloc()\n"); - error = ENOMEM; + device_printf(dev, "can not allocate ifnet structure\n"); goto fail; } - ic = ifp->if_l2com; + ic = ifp->if_l2com; ic->ic_ifp = ifp; ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ - /* set device capabilities */ + /* Set device capabilities. */ ic->ic_caps = IEEE80211_C_STA /* station mode supported */ + | IEEE80211_C_IBSS /* IBSS mode supported */ + | IEEE80211_C_HOSTAP /* Host access point mode */ | IEEE80211_C_MONITOR /* monitor mode supported */ + | IEEE80211_C_AHDEMO /* adhoc demo mode */ + | IEEE80211_C_BGSCAN /* capable of bg scanning */ | IEEE80211_C_TXPMGT /* tx power management */ | IEEE80211_C_SHSLOT /* short slot time supported */ - | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_WPA /* 802.11i */ -/* XXX looks like WME is partly supported? */ -#if 0 - | IEEE80211_C_IBSS /* IBSS mode support */ - | IEEE80211_C_BGSCAN /* capable of bg scanning */ + | IEEE80211_C_SHPREAMBLE /* short preamble supported */ | IEEE80211_C_WME /* 802.11e */ - | IEEE80211_C_HOSTAP /* Host access point mode */ -#endif + | IEEE80211_C_PMGT /* Station-side power mgmt */ ; + ic->ic_cryptocaps = + IEEE80211_CRYPTO_AES_CCM; + /* * Read in the eeprom and also setup the channels for * net80211. We don't set the rates as net80211 does this for us */ - wpi_read_eeprom(sc, macaddr); - - if (bootverbose || WPI_DEBUG_SET) { - device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", sc->domain); - device_printf(sc->sc_dev, "Hardware Type: %c\n", - sc->type > 1 ? 'B': '?'); - device_printf(sc->sc_dev, "Hardware Revision: %c\n", - ((le16toh(sc->rev) & 0xf0) == 0xd0) ? 'D': '?'); - device_printf(sc->sc_dev, "SKU %s support 802.11a\n", - supportsa ? "does" : "does not"); + if ((error = wpi_read_eeprom(sc, macaddr)) != 0) { + device_printf(dev, "could not read EEPROM, error %d\n", + error); + goto fail; + } - /* XXX hw_config uses the PCIDEV for the Hardware rev. Must check - what sc->rev really represents - benjsc 20070615 */ +#ifdef WPI_DEBUG + if (bootverbose) { + device_printf(sc->sc_dev, "Regulatory Domain: %.4s\n", + sc->domain); + device_printf(sc->sc_dev, "Hardware Type: %c\n", + sc->type > 1 ? 'B': '?'); + device_printf(sc->sc_dev, "Hardware Revision: %c\n", + ((sc->rev & 0xf0) == 0xd0) ? 'D': '?'); + device_printf(sc->sc_dev, "SKU %s support 802.11a\n", + supportsa ? "does" : "does not"); + + /* XXX hw_config uses the PCIDEV for the Hardware rev. Must + check what sc->rev really represents - benjsc 20070615 */ } +#endif if_initname(ifp, device_get_name(dev), device_get_unit(dev)); ifp->if_softc = sc; @@ -662,43 +511,186 @@ wpi_attach(device_t dev) IFQ_SET_READY(&ifp->if_snd); ieee80211_ifattach(ic, macaddr); - /* override default methods */ + ic->ic_vap_create = wpi_vap_create; + ic->ic_vap_delete = wpi_vap_delete; ic->ic_raw_xmit = wpi_raw_xmit; - ic->ic_wme.wme_update = wpi_wme_update; + ic->ic_node_alloc = wpi_node_alloc; + sc->sc_node_free = ic->ic_node_free; + ic->ic_node_free = wpi_node_free; + ic->ic_wme.wme_update = wpi_updateedca; + ic->ic_update_promisc = wpi_update_promisc; + ic->ic_update_mcast = wpi_update_mcast; + ic->ic_newassoc = wpi_newassoc; ic->ic_scan_start = wpi_scan_start; ic->ic_scan_end = wpi_scan_end; ic->ic_set_channel = wpi_set_channel; ic->ic_scan_curchan = wpi_scan_curchan; ic->ic_scan_mindwell = wpi_scan_mindwell; + ic->ic_setregdomain = wpi_setregdomain; - ic->ic_vap_create = wpi_vap_create; - ic->ic_vap_delete = wpi_vap_delete; + sc->sc_update_rx_ring = wpi_update_rx_ring; + sc->sc_update_tx_ring = wpi_update_tx_ring; - ieee80211_radiotap_attach(ic, - &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), - WPI_TX_RADIOTAP_PRESENT, - &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), - WPI_RX_RADIOTAP_PRESENT); + wpi_radiotap_attach(sc); + + callout_init_mtx(&sc->calib_to, &sc->rxon_mtx, 0); + callout_init_mtx(&sc->scan_timeout, &sc->rxon_mtx, 0); + callout_init_mtx(&sc->tx_timeout, &sc->txq_state_mtx, 0); + callout_init_mtx(&sc->watchdog_rfkill, &sc->sc_mtx, 0); + TASK_INIT(&sc->sc_reinittask, 0, wpi_hw_reset, sc); + TASK_INIT(&sc->sc_radiooff_task, 0, wpi_radio_off, sc); + TASK_INIT(&sc->sc_radioon_task, 0, wpi_radio_on, sc); + TASK_INIT(&sc->sc_start_task, 0, wpi_start_task, sc); + + sc->sc_tq = taskqueue_create("wpi_taskq", M_WAITOK, + taskqueue_thread_enqueue, &sc->sc_tq); + error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "wpi_taskq"); + if (error != 0) { + device_printf(dev, "can't start threads, error %d\n", error); + goto fail; + } + + wpi_sysctlattach(sc); /* * Hook our interrupt after all initialization is complete. */ - error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET |INTR_MPSAFE, + error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, wpi_intr, sc, &sc->sc_ih); if (error != 0) { - device_printf(dev, "could not set up interrupt\n"); + device_printf(dev, "can't establish interrupt, error %d\n", + error); goto fail; } if (bootverbose) ieee80211_announce(ic); -#ifdef XXX_DEBUG - ieee80211_announce_channels(ic); + +#ifdef WPI_DEBUG + if (sc->sc_debug & WPI_DEBUG_HW) + ieee80211_announce_channels(ic); #endif + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; fail: wpi_detach(dev); - return ENXIO; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; +} + +/* + * Attach the interface to 802.11 radiotap. + */ +static void +wpi_radiotap_attach(struct wpi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + ieee80211_radiotap_attach(ic, + &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), + WPI_TX_RADIOTAP_PRESENT, + &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), + WPI_RX_RADIOTAP_PRESENT); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); +} + +static void +wpi_sysctlattach(struct wpi_softc *sc) +{ +#ifdef WPI_DEBUG + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->sc_dev); + struct sysctl_oid *tree = device_get_sysctl_tree(sc->sc_dev); + + SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, + "debug", CTLFLAG_RW, &sc->sc_debug, sc->sc_debug, + "control debugging printfs"); +#endif +} + +static void +wpi_init_beacon(struct wpi_vap *wvp) +{ + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data; + + cmd->id = WPI_ID_BROADCAST; + cmd->ofdm_mask = 0xff; + cmd->cck_mask = 0x0f; + cmd->lifetime = htole32(WPI_LIFETIME_INFINITE); + + /* + * XXX WPI_TX_AUTO_SEQ seems to be ignored - workaround this issue + * XXX by using WPI_TX_NEED_ACK instead (with some side effects). + */ + cmd->flags = htole32(WPI_TX_NEED_ACK | WPI_TX_INSERT_TSTAMP); + + bcn->code = WPI_CMD_SET_BEACON; + bcn->ac = WPI_CMD_QUEUE_NUM; + bcn->size = sizeof(struct wpi_cmd_beacon); +} + +static struct ieee80211vap * +wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, + enum ieee80211_opmode opmode, int flags, + const uint8_t bssid[IEEE80211_ADDR_LEN], + const uint8_t mac[IEEE80211_ADDR_LEN]) +{ + struct wpi_vap *wvp; + struct ieee80211vap *vap; + + if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ + return NULL; + + wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), + M_80211_VAP, M_NOWAIT | M_ZERO); + if (wvp == NULL) + return NULL; + vap = &wvp->wv_vap; + ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); + + if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) { + WPI_VAP_LOCK_INIT(wvp); + wpi_init_beacon(wvp); + } + + /* Override with driver methods. */ + vap->iv_key_set = wpi_key_set; + vap->iv_key_delete = wpi_key_delete; + wvp->wv_recv_mgmt = vap->iv_recv_mgmt; + vap->iv_recv_mgmt = wpi_recv_mgmt; + wvp->wv_newstate = vap->iv_newstate; + vap->iv_newstate = wpi_newstate; + vap->iv_update_beacon = wpi_update_beacon; + vap->iv_max_aid = WPI_ID_IBSS_MAX - WPI_ID_IBSS_MIN + 1; + + ieee80211_ratectl_init(vap); + /* Complete setup. */ + ieee80211_vap_attach(vap, ieee80211_media_change, + ieee80211_media_status); + ic->ic_opmode = opmode; + return vap; +} + +static void +wpi_vap_delete(struct ieee80211vap *vap) +{ + struct wpi_vap *wvp = WPI_VAP(vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + enum ieee80211_opmode opmode = vap->iv_opmode; + + ieee80211_ratectl_deinit(vap); + ieee80211_vap_detach(vap); + + if (opmode == IEEE80211_M_IBSS || opmode == IEEE80211_M_HOSTAP) { + if (bcn->m != NULL) + m_freem(bcn->m); + + WPI_VAP_LOCK_DESTROY(wvp); + } + + free(wvp, M_80211_VAP); } static int @@ -707,43 +699,48 @@ wpi_detach(device_t dev) struct wpi_softc *sc = device_get_softc(dev); struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic; - int ac; + int qid; - if (sc->irq != NULL) - bus_teardown_intr(dev, sc->irq, sc->sc_ih); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); if (ifp != NULL) { ic = ifp->if_l2com; - ieee80211_draintask(ic, &sc->sc_restarttask); - ieee80211_draintask(ic, &sc->sc_radiotask); + ieee80211_draintask(ic, &sc->sc_radioon_task); + ieee80211_draintask(ic, &sc->sc_start_task); + wpi_stop(sc); - callout_drain(&sc->watchdog_to); + + taskqueue_drain_all(sc->sc_tq); + taskqueue_free(sc->sc_tq); + + callout_drain(&sc->watchdog_rfkill); + callout_drain(&sc->tx_timeout); + callout_drain(&sc->scan_timeout); callout_drain(&sc->calib_to); ieee80211_ifdetach(ic); } - WPI_LOCK(sc); + /* Uninstall interrupt handler. */ + if (sc->irq != NULL) { + bus_teardown_intr(dev, sc->irq, sc->sc_ih); + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), + sc->irq); + pci_release_msi(dev); + } + if (sc->txq[0].data_dmat) { - for (ac = 0; ac < WME_NUM_AC; ac++) - wpi_free_tx_ring(sc, &sc->txq[ac]); + /* Free DMA resources. */ + for (qid = 0; qid < WPI_NTXQUEUES; qid++) + wpi_free_tx_ring(sc, &sc->txq[qid]); - wpi_free_tx_ring(sc, &sc->cmdq); - wpi_free_rx_ring(sc, &sc->rxq); + wpi_free_rx_ring(sc); wpi_free_shared(sc); } - if (sc->fw_fp != NULL) { - wpi_unload_firmware(sc); - } - if (sc->fw_dma.tag) wpi_free_fwmem(sc); - WPI_UNLOCK(sc); - - if (sc->irq != NULL) - bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq), - sc->irq); + if (sc->mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->mem), sc->mem); @@ -751,47 +748,169 @@ wpi_detach(device_t dev) if (ifp != NULL) if_free(ifp); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + WPI_TXQ_STATE_LOCK_DESTROY(sc); + WPI_TXQ_LOCK_DESTROY(sc); + WPI_NT_LOCK_DESTROY(sc); + WPI_RXON_LOCK_DESTROY(sc); + WPI_TX_LOCK_DESTROY(sc); WPI_LOCK_DESTROY(sc); + return 0; +} +static int +wpi_shutdown(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + + wpi_stop(sc); return 0; } -static struct ieee80211vap * -wpi_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, - enum ieee80211_opmode opmode, int flags, - const uint8_t bssid[IEEE80211_ADDR_LEN], - const uint8_t mac[IEEE80211_ADDR_LEN]) +static int +wpi_suspend(device_t dev) { - struct wpi_vap *wvp; - struct ieee80211vap *vap; + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = sc->sc_ifp->if_l2com; - if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ - return NULL; - wvp = (struct wpi_vap *) malloc(sizeof(struct wpi_vap), - M_80211_VAP, M_NOWAIT | M_ZERO); - if (wvp == NULL) - return NULL; - vap = &wvp->vap; - ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid, mac); - /* override with driver methods */ - wvp->newstate = vap->iv_newstate; - vap->iv_newstate = wpi_newstate; + ieee80211_suspend_all(ic); + return 0; +} - ieee80211_ratectl_init(vap); - /* complete setup */ - ieee80211_vap_attach(vap, ieee80211_media_change, ieee80211_media_status); - ic->ic_opmode = opmode; - return vap; +static int +wpi_resume(device_t dev) +{ + struct wpi_softc *sc = device_get_softc(dev); + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + + /* Clear device-specific "PCI retry timeout" register (41h). */ + pci_write_config(dev, 0x41, 0, 1); + + ieee80211_resume_all(ic); + return 0; } -static void -wpi_vap_delete(struct ieee80211vap *vap) +/* + * Grab exclusive access to NIC memory. + */ +static int +wpi_nic_lock(struct wpi_softc *sc) { - struct wpi_vap *wvp = WPI_VAP(vap); + int ntries; - ieee80211_ratectl_deinit(vap); - ieee80211_vap_detach(vap); - free(wvp, M_80211_VAP); + /* Request exclusive access to NIC. */ + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + + /* Spin until we actually get the lock. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((WPI_READ(sc, WPI_GP_CNTRL) & + (WPI_GP_CNTRL_MAC_ACCESS_ENA | WPI_GP_CNTRL_SLEEP)) == + WPI_GP_CNTRL_MAC_ACCESS_ENA) + return 0; + DELAY(10); + } + + device_printf(sc->sc_dev, "could not lock memory\n"); + + return ETIMEDOUT; +} + +/* + * Release lock on NIC memory. + */ +static __inline void +wpi_nic_unlock(struct wpi_softc *sc) +{ + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); +} + +static __inline uint32_t +wpi_prph_read(struct wpi_softc *sc, uint32_t addr) +{ + WPI_WRITE(sc, WPI_PRPH_RADDR, WPI_PRPH_DWORD | addr); + WPI_BARRIER_READ_WRITE(sc); + return WPI_READ(sc, WPI_PRPH_RDATA); +} + +static __inline void +wpi_prph_write(struct wpi_softc *sc, uint32_t addr, uint32_t data) +{ + WPI_WRITE(sc, WPI_PRPH_WADDR, WPI_PRPH_DWORD | addr); + WPI_BARRIER_WRITE(sc); + WPI_WRITE(sc, WPI_PRPH_WDATA, data); +} + +static __inline void +wpi_prph_setbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) +{ + wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) | mask); +} + +static __inline void +wpi_prph_clrbits(struct wpi_softc *sc, uint32_t addr, uint32_t mask) +{ + wpi_prph_write(sc, addr, wpi_prph_read(sc, addr) & ~mask); +} + +static __inline void +wpi_prph_write_region_4(struct wpi_softc *sc, uint32_t addr, + const uint32_t *data, int count) +{ + for (; count > 0; count--, data++, addr += 4) + wpi_prph_write(sc, addr, *data); +} + +static __inline uint32_t +wpi_mem_read(struct wpi_softc *sc, uint32_t addr) +{ + WPI_WRITE(sc, WPI_MEM_RADDR, addr); + WPI_BARRIER_READ_WRITE(sc); + return WPI_READ(sc, WPI_MEM_RDATA); +} + +static __inline void +wpi_mem_read_region_4(struct wpi_softc *sc, uint32_t addr, uint32_t *data, + int count) +{ + for (; count > 0; count--, addr += 4) + *data++ = wpi_mem_read(sc, addr); +} + +static int +wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int count) +{ + uint8_t *out = data; + uint32_t val; + int error, ntries; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + for (; count > 0; count -= 2, addr++) { + WPI_WRITE(sc, WPI_EEPROM, addr << 2); + for (ntries = 0; ntries < 10; ntries++) { + val = WPI_READ(sc, WPI_EEPROM); + if (val & WPI_EEPROM_READ_VALID) + break; + DELAY(5); + } + if (ntries == 10) { + device_printf(sc->sc_dev, + "timeout reading ROM at 0x%x\n", addr); + return ETIMEDOUT; + } + *out++= val >> 16; + if (count > 1) + *out ++= val >> 24; + } + + wpi_nic_unlock(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; } static void @@ -799,116 +918,63 @@ wpi_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; - KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); - *(bus_addr_t *)arg = segs[0].ds_addr; } /* * Allocates a contiguous block of dma memory of the requested size and - * alignment. Due to limitations of the FreeBSD dma subsystem as of 20071217, - * allocations greater than 4096 may fail. Hence if the requested alignment is - * greater we allocate 'alignment' size extra memory and shift the vaddr and - * paddr after the dma load. This bypasses the problem at the cost of a little - * more memory. + * alignment. */ static int wpi_dma_contig_alloc(struct wpi_softc *sc, struct wpi_dma_info *dma, - void **kvap, bus_size_t size, bus_size_t alignment, int flags) + void **kvap, bus_size_t size, bus_size_t alignment) { int error; - bus_size_t align; - bus_size_t reqsize; - DPRINTFN(WPI_DEBUG_DMA, - ("Size: %zd - alignment %zd\n", size, alignment)); - - dma->size = size; dma->tag = NULL; + dma->size = size; - if (alignment > 4096) { - align = PAGE_SIZE; - reqsize = size + alignment; - } else { - align = alignment; - reqsize = size; - } - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), align, - 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, - NULL, NULL, reqsize, - 1, reqsize, flags, - NULL, NULL, &dma->tag); - if (error != 0) { - device_printf(sc->sc_dev, - "could not create shared page DMA tag\n"); - goto fail; - } - error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr_start, - flags | BUS_DMA_ZERO, &dma->map); - if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate shared page DMA memory\n"); + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, + 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, + 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); + if (error != 0) goto fail; - } - - error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr_start, - reqsize, wpi_dma_map_addr, &dma->paddr_start, flags); - - /* Save the original pointers so we can free all the memory */ - dma->paddr = dma->paddr_start; - dma->vaddr = dma->vaddr_start; - - /* - * Check the alignment and increment by 4096 until we get the - * requested alignment. Fail if can't obtain the alignment - * we requested. - */ - if ((dma->paddr & (alignment -1 )) != 0) { - int i; - for (i = 0; i < alignment / 4096; i++) { - if ((dma->paddr & (alignment - 1 )) == 0) - break; - dma->paddr += 4096; - dma->vaddr += 4096; - } - if (i == alignment / 4096) { - device_printf(sc->sc_dev, - "alignment requirement was not satisfied\n"); - goto fail; - } - } + error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, + BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); + if (error != 0) + goto fail; - if (error != 0) { - device_printf(sc->sc_dev, - "could not load shared page DMA map\n"); + error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, + wpi_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); + if (error != 0) goto fail; - } + + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); if (kvap != NULL) *kvap = dma->vaddr; return 0; -fail: - wpi_dma_contig_free(dma); +fail: wpi_dma_contig_free(dma); return error; } static void wpi_dma_contig_free(struct wpi_dma_info *dma) { - if (dma->tag) { - if (dma->map != NULL) { - if (dma->paddr_start != 0) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - } - bus_dmamem_free(dma->tag, &dma->vaddr_start, dma->map); - } + if (dma->vaddr != NULL) { + bus_dmamap_sync(dma->tag, dma->map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->tag, dma->map); + bus_dmamem_free(dma->tag, dma->vaddr, dma->map); + dma->vaddr = NULL; + } + if (dma->tag != NULL) { bus_dma_tag_destroy(dma->tag); + dma->tag = NULL; } } @@ -918,19 +984,9 @@ wpi_dma_contig_free(struct wpi_dma_info *dma) static int wpi_alloc_shared(struct wpi_softc *sc) { - int error; - - error = wpi_dma_contig_alloc(sc, &sc->shared_dma, - (void **)&sc->shared, sizeof (struct wpi_shared), - PAGE_SIZE, - BUS_DMA_NOWAIT); - - if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate shared area DMA memory\n"); - } - - return error; + /* Shared buffer must be aligned on a 4KB boundary. */ + return wpi_dma_contig_alloc(sc, &sc->shared_dma, + (void **)&sc->shared, sizeof (struct wpi_shared), 4096); } static void @@ -939,114 +995,167 @@ wpi_free_shared(struct wpi_softc *sc) wpi_dma_contig_free(&sc->shared_dma); } +/* + * Allocate DMA-safe memory for firmware transfer. + */ static int -wpi_alloc_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +wpi_alloc_fwmem(struct wpi_softc *sc) +{ + /* Must be aligned on a 16-byte boundary. */ + return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, + WPI_FW_TEXT_MAXSZ + WPI_FW_DATA_MAXSZ, 16); +} + +static void +wpi_free_fwmem(struct wpi_softc *sc) { + wpi_dma_contig_free(&sc->fw_dma); +} +static int +wpi_alloc_rx_ring(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + bus_size_t size; int i, error; ring->cur = 0; + ring->update = 0; - error = wpi_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, WPI_RX_RING_COUNT * sizeof (uint32_t), - WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + /* Allocate RX descriptors (16KB aligned.) */ + size = WPI_RX_RING_COUNT * sizeof (uint32_t); + error = wpi_dma_contig_alloc(sc, &ring->desc_dma, + (void **)&ring->desc, size, WPI_RING_DMA_ALIGN); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not allocate rx ring DMA memory, error %d\n", + "%s: could not allocate RX ring DMA memory, error %d\n", __func__, error); goto fail; } - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", + /* Create RX buffer DMA tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + MJUMPAGESIZE, 1, MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, + &ring->data_dmat); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not create RX buf DMA tag, error %d\n", __func__, error); - goto fail; - } + goto fail; + } /* - * Setup Rx buffers. + * Allocate and map RX buffers. */ for (i = 0; i < WPI_RX_RING_COUNT; i++) { struct wpi_rx_data *data = &ring->data[i]; - struct mbuf *m; bus_addr_t paddr; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_create failed, error %d\n", + "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } - m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (m == NULL) { + + data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (data->m == NULL) { device_printf(sc->sc_dev, - "%s: could not allocate rx mbuf\n", __func__); - error = ENOMEM; + "%s: could not allocate RX mbuf\n", __func__); + error = ENOBUFS; goto fail; } - /* map page */ + error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(m, caddr_t), MJUMPAGESIZE, - wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", - __func__, error); - m_freem(m); - error = ENOMEM; /* XXX unique code */ + "%s: can't map mbuf (error %d)\n", __func__, + error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); - data->m = m; + /* Set physical address of RX buffer. */ ring->desc[i] = htole32(paddr); } + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + return 0; -fail: - wpi_free_rx_ring(sc, ring); + +fail: wpi_free_rx_ring(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; } static void -wpi_reset_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +wpi_update_rx_ring(struct wpi_softc *sc) { - int ntries; + WPI_WRITE(sc, WPI_FH_RX_WPTR, sc->rxq.cur & ~7); +} - wpi_mem_lock(sc); +static void +wpi_update_rx_ring_ps(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; - WPI_WRITE(sc, WPI_RX_CONFIG, 0); + if (ring->update != 0) { + /* Wait for INT_WAKEUP event. */ + return; + } - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_RX_STATUS) & WPI_RX_IDLE) - break; - DELAY(10); + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) { + DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s: wakeup request\n", + __func__); + ring->update = 1; + } else { + wpi_update_rx_ring(sc); + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); } +} + +static void +wpi_reset_rx_ring(struct wpi_softc *sc) +{ + struct wpi_rx_ring *ring = &sc->rxq; + int ntries; - wpi_mem_unlock(sc); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); -#ifdef WPI_DEBUG - if (ntries == 100 && wpi_debug > 0) - device_printf(sc->sc_dev, "timeout resetting Rx ring\n"); -#endif + if (wpi_nic_lock(sc) == 0) { + WPI_WRITE(sc, WPI_FH_RX_CONFIG, 0); + for (ntries = 0; ntries < 1000; ntries++) { + if (WPI_READ(sc, WPI_FH_RX_STATUS) & + WPI_FH_RX_STATUS_IDLE) + break; + DELAY(10); + } + wpi_nic_unlock(sc); + } ring->cur = 0; + ring->update = 0; } static void -wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) +wpi_free_rx_ring(struct wpi_softc *sc) { + struct wpi_rx_ring *ring = &sc->rxq; int i; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + wpi_dma_contig_free(&ring->desc_dma); for (i = 0; i < WPI_RX_RING_COUNT; i++) { @@ -1057,52 +1166,64 @@ wpi_free_rx_ring(struct wpi_softc *sc, struct wpi_rx_ring *ring) BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); + data->m = NULL; } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } } static int -wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, - int qid) +wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int qid) { - struct wpi_tx_data *data; + bus_addr_t paddr; + bus_size_t size; int i, error; ring->qid = qid; - ring->count = count; ring->queued = 0; ring->cur = 0; - ring->data = NULL; + ring->update = 0; - error = wpi_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, count * sizeof (struct wpi_tx_desc), - WPI_RING_DMA_ALIGN, BUS_DMA_NOWAIT); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + /* Allocate TX descriptors (16KB aligned.) */ + size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_desc); + error = wpi_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, + size, WPI_RING_DMA_ALIGN); if (error != 0) { - device_printf(sc->sc_dev, "could not allocate tx dma memory\n"); - goto fail; + device_printf(sc->sc_dev, + "%s: could not allocate TX ring DMA memory, error %d\n", + __func__, error); + goto fail; } - /* update shared page with ring's base address */ + /* Update shared area with ring physical address. */ sc->shared->txbase[qid] = htole32(ring->desc_dma.paddr); + bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, + BUS_DMASYNC_PREWRITE); - error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, - count * sizeof (struct wpi_tx_cmd), WPI_RING_DMA_ALIGN, - BUS_DMA_NOWAIT); - - if (error != 0) { - device_printf(sc->sc_dev, - "could not allocate tx command DMA memory\n"); - goto fail; + /* + * We only use rings 0 through 4 (4 EDCA + cmd) so there is no need + * to allocate commands space for other rings. + * XXX Do we really need to allocate descriptors for other rings? + */ + if (qid > WPI_CMD_QUEUE_NUM) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + return 0; } - ring->data = malloc(count * sizeof (struct wpi_tx_data), M_DEVBUF, - M_NOWAIT | M_ZERO); - if (ring->data == NULL) { + size = WPI_TX_RING_COUNT * sizeof (struct wpi_tx_cmd); + error = wpi_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, + size, 4); + if (error != 0) { device_printf(sc->sc_dev, - "could not allocate tx data slots\n"); + "%s: could not allocate TX cmd DMA memory, error %d\n", + __func__, error); goto fail; } @@ -1111,339 +1232,706 @@ wpi_alloc_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring, int count, WPI_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { - device_printf(sc->sc_dev, "could not create data DMA tag\n"); + device_printf(sc->sc_dev, + "%s: could not create TX buf DMA tag, error %d\n", + __func__, error); goto fail; } - for (i = 0; i < count; i++) { - data = &ring->data[i]; + paddr = ring->cmd_dma.paddr; + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; + + data->cmd_paddr = paddr; + paddr += sizeof (struct wpi_tx_cmd); error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, - "could not create tx buf DMA map\n"); + "%s: could not create TX buf DMA map, error %d\n", + __func__, error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); } + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + return 0; -fail: - wpi_free_tx_ring(sc, ring); +fail: wpi_free_tx_ring(sc, ring); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); return error; } static void -wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +wpi_update_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { - struct wpi_tx_data *data; - int i, ntries; + WPI_WRITE(sc, WPI_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); +} - wpi_mem_lock(sc); +static void +wpi_update_tx_ring_ps(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ - WPI_WRITE(sc, WPI_TX_CONFIG(ring->qid), 0); - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_TX_STATUS) & WPI_TX_IDLE(ring->qid)) - break; - DELAY(10); + if (ring->update != 0) { + /* Wait for INT_WAKEUP event. */ + return; } -#ifdef WPI_DEBUG - if (ntries == 100 && wpi_debug > 0) - device_printf(sc->sc_dev, "timeout resetting Tx ring %d\n", - ring->qid); -#endif - wpi_mem_unlock(sc); - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_SLEEP) { + DPRINTF(sc, WPI_DEBUG_PWRSAVE, "%s (%d): requesting wakeup\n", + __func__, ring->qid); + ring->update = 1; + } else { + wpi_update_tx_ring(sc, ring); + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + } +} + +static void +wpi_reset_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) +{ + int i; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } + if (data->ni != NULL) { + ieee80211_free_node(data->ni); + data->ni = NULL; + } } - + /* Clear TX descriptors. */ + memset(ring->desc, 0, ring->desc_dma.size); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + sc->qfullmsk &= ~(1 << ring->qid); ring->queued = 0; ring->cur = 0; + ring->update = 0; } static void wpi_free_tx_ring(struct wpi_softc *sc, struct wpi_tx_ring *ring) { - struct wpi_tx_data *data; int i; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + wpi_dma_contig_free(&ring->desc_dma); wpi_dma_contig_free(&ring->cmd_dma); - if (ring->data != NULL) { - for (i = 0; i < ring->count; i++) { - data = &ring->data[i]; + for (i = 0; i < WPI_TX_RING_COUNT; i++) { + struct wpi_tx_data *data = &ring->data[i]; - if (data->m != NULL) { - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, data->map); - m_freem(data->m); - data->m = NULL; - } + if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m_freem(data->m); } - free(ring->data, M_DEVBUF); + if (data->map != NULL) + bus_dmamap_destroy(ring->data_dmat, data->map); } - - if (ring->data_dmat != NULL) + if (ring->data_dmat != NULL) { bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } } +/* + * Extract various information from EEPROM. + */ static int -wpi_shutdown(device_t dev) +wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) { - struct wpi_softc *sc = device_get_softc(dev); +#define WPI_CHK(res) do { \ + if ((error = res) != 0) \ + goto fail; \ +} while (0) + int error, i; - WPI_LOCK(sc); - wpi_stop_locked(sc); - wpi_unload_firmware(sc); - WPI_UNLOCK(sc); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - return 0; + /* Adapter has to be powered on for EEPROM access to work. */ + if ((error = wpi_apm_init(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not power ON adapter, error %d\n", __func__, + error); + return error; + } + + if ((WPI_READ(sc, WPI_EEPROM_GP) & 0x6) == 0) { + device_printf(sc->sc_dev, "bad EEPROM signature\n"); + error = EIO; + goto fail; + } + /* Clear HW ownership of EEPROM. */ + WPI_CLRBITS(sc, WPI_EEPROM_GP, WPI_EEPROM_GP_IF_OWNER); + + /* Read the hardware capabilities, revision and SKU type. */ + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_SKU_CAP, &sc->cap, + sizeof(sc->cap))); + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev, + sizeof(sc->rev))); + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, + sizeof(sc->type))); + + sc->rev = le16toh(sc->rev); + DPRINTF(sc, WPI_DEBUG_EEPROM, "cap=%x rev=%x type=%x\n", sc->cap, + sc->rev, sc->type); + + /* Read the regulatory domain (4 ASCII characters.) */ + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, + sizeof(sc->domain))); + + /* Read MAC address. */ + WPI_CHK(wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, + IEEE80211_ADDR_LEN)); + + /* Read the list of authorized channels. */ + for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) + WPI_CHK(wpi_read_eeprom_channels(sc, i)); + + /* Read the list of TX power groups. */ + for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) + WPI_CHK(wpi_read_eeprom_group(sc, i)); + +fail: wpi_apm_stop(sc); /* Power OFF adapter. */ + + DPRINTF(sc, WPI_DEBUG_TRACE, error ? TRACE_STR_END_ERR : TRACE_STR_END, + __func__); + + return error; +#undef WPI_CHK } -static int -wpi_suspend(device_t dev) +/* + * Translate EEPROM flags to net80211. + */ +static uint32_t +wpi_eeprom_channel_flags(struct wpi_eeprom_chan *channel) { - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = sc->sc_ifp->if_l2com; + uint32_t nflags; - ieee80211_suspend_all(ic); - return 0; + nflags = 0; + if ((channel->flags & WPI_EEPROM_CHAN_ACTIVE) == 0) + nflags |= IEEE80211_CHAN_PASSIVE; + if ((channel->flags & WPI_EEPROM_CHAN_IBSS) == 0) + nflags |= IEEE80211_CHAN_NOADHOC; + if (channel->flags & WPI_EEPROM_CHAN_RADAR) { + nflags |= IEEE80211_CHAN_DFS; + /* XXX apparently IBSS may still be marked */ + nflags |= IEEE80211_CHAN_NOADHOC; + } + + /* XXX HOSTAP uses WPI_MODE_IBSS */ + if (nflags & IEEE80211_CHAN_NOADHOC) + nflags |= IEEE80211_CHAN_NOHOSTAP; + + return nflags; } +static void +wpi_read_eeprom_band(struct wpi_softc *sc, int n) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct wpi_eeprom_chan *channels = sc->eeprom_channels[n]; + const struct wpi_chan_band *band = &wpi_bands[n]; + struct ieee80211_channel *c; + uint8_t chan; + int i, nflags; + + for (i = 0; i < band->nchan; i++) { + if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { + DPRINTF(sc, WPI_DEBUG_EEPROM, + "Channel Not Valid: %d, band %d\n", + band->chan[i],n); + continue; + } + + chan = band->chan[i]; + nflags = wpi_eeprom_channel_flags(&channels[i]); + + c = &ic->ic_channels[ic->ic_nchans++]; + c->ic_ieee = chan; + c->ic_maxregpower = channels[i].maxpwr; + c->ic_maxpower = 2*c->ic_maxregpower; + + if (n == 0) { /* 2GHz band */ + c->ic_freq = ieee80211_ieee2mhz(chan, + IEEE80211_CHAN_G); + + /* G =>'s B is supported */ + c->ic_flags = IEEE80211_CHAN_B | nflags; + c = &ic->ic_channels[ic->ic_nchans++]; + c[0] = c[-1]; + c->ic_flags = IEEE80211_CHAN_G | nflags; + } else { /* 5GHz band */ + c->ic_freq = ieee80211_ieee2mhz(chan, + IEEE80211_CHAN_A); + + c->ic_flags = IEEE80211_CHAN_A | nflags; + } + + /* Save maximum allowed TX power for this channel. */ + sc->maxpwr[chan] = channels[i].maxpwr; + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "adding chan %d (%dMHz) flags=0x%x maxpwr=%d passive=%d," + " offset %d\n", chan, c->ic_freq, + channels[i].flags, sc->maxpwr[chan], + IEEE80211_IS_CHAN_PASSIVE(c), ic->ic_nchans); + } +} + +/** + * Read the eeprom to find out what channels are valid for the given + * band and update net80211 with what we find. + */ static int -wpi_resume(device_t dev) +wpi_read_eeprom_channels(struct wpi_softc *sc, int n) { - struct wpi_softc *sc = device_get_softc(dev); - struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + const struct wpi_chan_band *band = &wpi_bands[n]; + int error; - pci_write_config(dev, 0x41, 0, 1); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + error = wpi_read_prom_data(sc, band->addr, &sc->eeprom_channels[n], + band->nchan * sizeof (struct wpi_eeprom_chan)); + if (error != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; + } + + wpi_read_eeprom_band(sc, n); + + ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - ieee80211_resume_all(ic); return 0; } -/** - * Called by net80211 when ever there is a change to 80211 state machine +static struct wpi_eeprom_chan * +wpi_find_eeprom_channel(struct wpi_softc *sc, struct ieee80211_channel *c) +{ + int i, j; + + for (j = 0; j < WPI_CHAN_BANDS_COUNT; j++) + for (i = 0; i < wpi_bands[j].nchan; i++) + if (wpi_bands[j].chan[i] == c->ic_ieee) + return &sc->eeprom_channels[j][i]; + + return NULL; +} + +/* + * Enforce flags read from EEPROM. */ static int -wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) +wpi_setregdomain(struct ieee80211com *ic, struct ieee80211_regdomain *rd, + int nchan, struct ieee80211_channel chans[]) { - struct wpi_vap *wvp = WPI_VAP(vap); - struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; - int error; + int i; - DPRINTF(("%s: %s -> %s flags 0x%x\n", __func__, - ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate], sc->flags)); + for (i = 0; i < nchan; i++) { + struct ieee80211_channel *c = &chans[i]; + struct wpi_eeprom_chan *channel; - IEEE80211_UNLOCK(ic); - WPI_LOCK(sc); - if (nstate == IEEE80211_S_SCAN && vap->iv_state != IEEE80211_S_INIT) { - /* - * On !INIT -> SCAN transitions, we need to clear any possible - * knowledge about associations. - */ - error = wpi_config(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: device config failed, error %d\n", - __func__, error); - } - } - if (nstate == IEEE80211_S_AUTH || - (nstate == IEEE80211_S_ASSOC && vap->iv_state == IEEE80211_S_RUN)) { - /* - * The node must be registered in the firmware before auth. - * Also the associd must be cleared on RUN -> ASSOC - * transitions. - */ - error = wpi_auth(sc, vap); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not move to auth state, error %d\n", - __func__, error); + channel = wpi_find_eeprom_channel(sc, c); + if (channel == NULL) { + if_printf(ic->ic_ifp, + "%s: invalid channel %u freq %u/0x%x\n", + __func__, c->ic_ieee, c->ic_freq, c->ic_flags); + return EINVAL; } + c->ic_flags |= wpi_eeprom_channel_flags(channel); } - if (nstate == IEEE80211_S_RUN && vap->iv_state != IEEE80211_S_RUN) { - error = wpi_run(sc, vap); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: could not move to run state, error %d\n", - __func__, error); - } + + return 0; +} + +static int +wpi_read_eeprom_group(struct wpi_softc *sc, int n) +{ + struct wpi_power_group *group = &sc->groups[n]; + struct wpi_eeprom_group rgroup; + int i, error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if ((error = wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, + &rgroup, sizeof rgroup)) != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; } - if (nstate == IEEE80211_S_RUN) { - /* RUN -> RUN transition; just restart the timers */ - wpi_calib_timeout(sc); - /* XXX split out rate control timer */ + + /* Save TX power group information. */ + group->chan = rgroup.chan; + group->maxpwr = rgroup.maxpwr; + /* Retrieve temperature at which the samples were taken. */ + group->temp = (int16_t)le16toh(rgroup.temp); + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "power group %d: chan=%d maxpwr=%d temp=%d\n", n, group->chan, + group->maxpwr, group->temp); + + for (i = 0; i < WPI_SAMPLES_COUNT; i++) { + group->samples[i].index = rgroup.samples[i].index; + group->samples[i].power = rgroup.samples[i].power; + + DPRINTF(sc, WPI_DEBUG_EEPROM, + "\tsample %d: index=%d power=%d\n", i, + group->samples[i].index, group->samples[i].power); } - WPI_UNLOCK(sc); - IEEE80211_LOCK(ic); - return wvp->newstate(vap, nstate, arg); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; } -/* - * Grab exclusive access to NIC memory. - */ -static void -wpi_mem_lock(struct wpi_softc *sc) +static int +wpi_add_node_entry_adhoc(struct wpi_softc *sc) { - int ntries; - uint32_t tmp; - - tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_MAC); + int newid = WPI_ID_IBSS_MIN; - /* spin until we actually get the lock */ - for (ntries = 0; ntries < 100; ntries++) { - if ((WPI_READ(sc, WPI_GPIO_CTL) & - (WPI_GPIO_CLOCK | WPI_GPIO_SLEEP)) == WPI_GPIO_CLOCK) - break; - DELAY(10); + for (; newid <= WPI_ID_IBSS_MAX; newid++) { + if ((sc->nodesmsk & (1 << newid)) == 0) { + sc->nodesmsk |= 1 << newid; + return newid; + } } - if (ntries == 100) - device_printf(sc->sc_dev, "could not lock memory\n"); + + return WPI_ID_UNDEFINED; } -/* - * Release lock on NIC memory. - */ -static void -wpi_mem_unlock(struct wpi_softc *sc) +static __inline int +wpi_add_node_entry_sta(struct wpi_softc *sc) { - uint32_t tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp & ~WPI_GPIO_MAC); + sc->nodesmsk |= 1 << WPI_ID_BSS; + + return WPI_ID_BSS; } -static uint32_t -wpi_mem_read(struct wpi_softc *sc, uint16_t addr) +static __inline int +wpi_check_node_entry(struct wpi_softc *sc, uint8_t id) { - WPI_WRITE(sc, WPI_READ_MEM_ADDR, WPI_MEM_4 | addr); - return WPI_READ(sc, WPI_READ_MEM_DATA); + if (id == WPI_ID_UNDEFINED) + return 0; + + return (sc->nodesmsk >> id) & 1; } -static void -wpi_mem_write(struct wpi_softc *sc, uint16_t addr, uint32_t data) +static __inline void +wpi_clear_node_table(struct wpi_softc *sc) { - WPI_WRITE(sc, WPI_WRITE_MEM_ADDR, WPI_MEM_4 | addr); - WPI_WRITE(sc, WPI_WRITE_MEM_DATA, data); + sc->nodesmsk = 0; } -static void -wpi_mem_write_region_4(struct wpi_softc *sc, uint16_t addr, - const uint32_t *data, int wlen) +static __inline void +wpi_del_node_entry(struct wpi_softc *sc, uint8_t id) { - for (; wlen > 0; wlen--, data++, addr+=4) - wpi_mem_write(sc, addr, *data); + sc->nodesmsk &= ~(1 << id); } -/* - * Read data from the EEPROM. We access EEPROM through the MAC instead of - * using the traditional bit-bang method. Data is read up until len bytes have - * been obtained. - */ -static uint16_t -wpi_read_prom_data(struct wpi_softc *sc, uint32_t addr, void *data, int len) +static struct ieee80211_node * +wpi_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { - int ntries; - uint32_t val; - uint8_t *out = data; + struct wpi_node *wn; + + wn = malloc(sizeof (struct wpi_node), M_80211_NODE, + M_NOWAIT | M_ZERO); - wpi_mem_lock(sc); + if (wn == NULL) + return NULL; - for (; len > 0; len -= 2, addr++) { - WPI_WRITE(sc, WPI_EEPROM_CTL, addr << 2); + wn->id = WPI_ID_UNDEFINED; - for (ntries = 0; ntries < 10; ntries++) { - if ((val = WPI_READ(sc, WPI_EEPROM_CTL)) & WPI_EEPROM_READY) - break; - DELAY(5); + return &wn->ni; +} + +static void +wpi_node_free(struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + + if (wn->id != WPI_ID_UNDEFINED) { + WPI_NT_LOCK(sc); + if (wpi_check_node_entry(sc, wn->id)) { + wpi_del_node_entry(sc, wn->id); + wpi_del_node(sc, ni); } + WPI_NT_UNLOCK(sc); + } - if (ntries == 10) { - device_printf(sc->sc_dev, "could not read EEPROM\n"); - return ETIMEDOUT; + sc->sc_node_free(ni); +} + +static __inline int +wpi_check_bss_filter(struct wpi_softc *sc) +{ + return (sc->rxon.filter & htole32(WPI_FILTER_BSS)) != 0; +} + +static void +wpi_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m, int subtype, int rssi, + int nf) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + uint64_t ni_tstamp, rx_tstamp; + + wvp->wv_recv_mgmt(ni, m, subtype, rssi, nf); + + if (vap->iv_opmode == IEEE80211_M_IBSS && + vap->iv_state == IEEE80211_S_RUN && + (subtype == IEEE80211_FC0_SUBTYPE_BEACON || + subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP)) { + ni_tstamp = le64toh(ni->ni_tstamp.tsf); + rx_tstamp = le64toh(sc->rx_tstamp); + + if (ni_tstamp >= rx_tstamp) { + DPRINTF(sc, WPI_DEBUG_STATE, + "ibss merge, tsf %ju tstamp %ju\n", + (uintmax_t)rx_tstamp, (uintmax_t)ni_tstamp); + (void) ieee80211_ibss_merge(ni); } + } +} - *out++= val >> 16; - if (len > 1) - *out ++= val >> 24; +static void +wpi_restore_node(void *arg, struct ieee80211_node *ni) +{ + struct wpi_softc *sc = arg; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + WPI_NT_LOCK(sc); + if (wn->id != WPI_ID_UNDEFINED) { + wn->id = WPI_ID_UNDEFINED; + if ((error = wpi_add_ibss_node(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not add IBSS node, error %d\n", + __func__, error); + } } + WPI_NT_UNLOCK(sc); +} - wpi_mem_unlock(sc); +static void +wpi_restore_node_table(struct wpi_softc *sc, struct wpi_vap *wvp) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; - return 0; + /* Set group keys once. */ + WPI_NT_LOCK(sc); + wvp->wv_gtk = 0; + WPI_NT_UNLOCK(sc); + + ieee80211_iterate_nodes(&ic->ic_sta, wpi_restore_node, sc); + ieee80211_crypto_reload_keys(ic); } -/* - * The firmware text and data segments are transferred to the NIC using DMA. - * The driver just copies the firmware into DMA-safe memory and tells the NIC - * where to find it. Once the NIC has copied the firmware into its internal - * memory, we can free our local copy in the driver. +/** + * Called by net80211 when ever there is a change to 80211 state machine */ static int -wpi_load_microcode(struct wpi_softc *sc, const uint8_t *fw, int size) +wpi_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { - int error, ntries; - - DPRINTFN(WPI_DEBUG_HW,("Loading microcode size 0x%x\n", size)); + struct wpi_vap *wvp = WPI_VAP(vap); + struct ieee80211com *ic = vap->iv_ic; + struct ifnet *ifp = ic->ic_ifp; + struct wpi_softc *sc = ifp->if_softc; + int error = 0; - size /= sizeof(uint32_t); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - wpi_mem_lock(sc); + DPRINTF(sc, WPI_DEBUG_STATE, "%s: %s -> %s\n", __func__, + ieee80211_state_name[vap->iv_state], + ieee80211_state_name[nstate]); - wpi_mem_write_region_4(sc, WPI_MEM_UCODE_BASE, - (const uint32_t *)fw, size); + if (vap->iv_state == IEEE80211_S_RUN && nstate < IEEE80211_S_RUN) { + if ((error = wpi_set_pslevel(sc, 0, 0, 1)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set power saving level\n", + __func__); + return error; + } - wpi_mem_write(sc, WPI_MEM_UCODE_SRC, 0); - wpi_mem_write(sc, WPI_MEM_UCODE_DST, WPI_FW_TEXT); - wpi_mem_write(sc, WPI_MEM_UCODE_SIZE, size); + wpi_set_led(sc, WPI_LED_LINK, 1, 0); + } - /* run microcode */ - wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_RUN); + switch (nstate) { + case IEEE80211_S_SCAN: + WPI_RXON_LOCK(sc); + if (wpi_check_bss_filter(sc) != 0) { + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, + "%s: could not send RXON\n", __func__); + } + } + WPI_RXON_UNLOCK(sc); + break; - /* wait while the adapter is busy copying the firmware */ - for (error = 0, ntries = 0; ntries < 1000; ntries++) { - uint32_t status = WPI_READ(sc, WPI_TX_STATUS); - DPRINTFN(WPI_DEBUG_HW, - ("firmware status=0x%x, val=0x%x, result=0x%x\n", status, - WPI_TX_IDLE(6), status & WPI_TX_IDLE(6))); - if (status & WPI_TX_IDLE(6)) { - DPRINTFN(WPI_DEBUG_HW, - ("Status Match! - ntries = %d\n", ntries)); + case IEEE80211_S_ASSOC: + if (vap->iv_state != IEEE80211_S_RUN) break; + /* FALLTHROUGH */ + case IEEE80211_S_AUTH: + /* + * NB: do not optimize AUTH -> AUTH state transmission - + * this will break powersave with non-QoS AP! + */ + + /* + * The node must be registered in the firmware before auth. + * Also the associd must be cleared on RUN -> ASSOC + * transitions. + */ + if ((error = wpi_auth(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to AUTH state, error %d\n", + __func__, error); } - DELAY(10); + break; + + case IEEE80211_S_RUN: + /* + * RUN -> RUN transition: + * STA mode: Just restart the timers. + * IBSS mode: Process IBSS merge. + */ + if (vap->iv_state == IEEE80211_S_RUN) { + if (vap->iv_opmode != IEEE80211_M_IBSS) { + WPI_RXON_LOCK(sc); + wpi_calib_timeout(sc); + WPI_RXON_UNLOCK(sc); + break; + } else { + /* + * Drop the BSS_FILTER bit + * (there is no another way to change bssid). + */ + WPI_RXON_LOCK(sc); + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, + "%s: could not send RXON\n", + __func__); + } + WPI_RXON_UNLOCK(sc); + + /* Restore all what was lost. */ + wpi_restore_node_table(sc, wvp); + + /* XXX set conditionally? */ + wpi_updateedca(ic); + } + } + + /* + * !RUN -> RUN requires setting the association id + * which is done with a firmware cmd. We also defer + * starting the timers until that work is done. + */ + if ((error = wpi_run(sc, vap)) != 0) { + device_printf(sc->sc_dev, + "%s: could not move to RUN state\n", __func__); + } + break; + + default: + break; } - if (ntries == 1000) { - device_printf(sc->sc_dev, "timeout transferring firmware\n"); - error = ETIMEDOUT; + if (error != 0) { + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + return error; } - /* start the microcode executing */ - wpi_mem_write(sc, WPI_MEM_UCODE_CTL, WPI_UC_ENABLE); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return wvp->wv_newstate(vap, nstate, arg); +} + +static void +wpi_calib_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + + if (wpi_check_bss_filter(sc) == 0) + return; - wpi_mem_unlock(sc); + wpi_power_calibration(sc); - return (error); + callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); } +static __inline uint8_t +rate2plcp(const uint8_t rate) +{ + switch (rate) { + case 12: return 0xd; + case 18: return 0xf; + case 24: return 0x5; + case 36: return 0x7; + case 48: return 0x9; + case 72: return 0xb; + case 96: return 0x1; + case 108: return 0x3; + case 2: return 10; + case 4: return 20; + case 11: return 55; + case 22: return 110; + default: return 0; + } +} + +static __inline uint8_t +plcp2rate(const uint8_t plcp) +{ + switch (plcp) { + case 0xd: return 12; + case 0xf: return 18; + case 0x5: return 24; + case 0x7: return 36; + case 0x9: return 48; + case 0xb: return 72; + case 0x1: return 96; + case 0x3: return 108; + case 10: return 2; + case 20: return 4; + case 55: return 11; + case 110: return 22; + default: return 0; + } +} + +/* Quickly determine if a given rate is CCK or OFDM. */ +#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + static void -wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, - struct wpi_rx_data *data) +wpi_rx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; @@ -1451,195 +1939,261 @@ wpi_rx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc, struct wpi_rx_stat *stat; struct wpi_rx_head *head; struct wpi_rx_tail *tail; + struct ieee80211_frame *wh; struct ieee80211_node *ni; - struct mbuf *m, *mnew; + struct mbuf *m, *m1; bus_addr_t paddr; + uint32_t flags; + uint16_t len; int error; stat = (struct wpi_rx_stat *)(desc + 1); if (stat->len > WPI_STAT_MAXLEN) { - device_printf(sc->sc_dev, "invalid rx statistic header\n"); - ifp->if_ierrors++; - return; + device_printf(sc->sc_dev, "invalid RX statistic header\n"); + goto fail1; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); head = (struct wpi_rx_head *)((caddr_t)(stat + 1) + stat->len); - tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + le16toh(head->len)); - - DPRINTFN(WPI_DEBUG_RX, ("rx intr: idx=%d len=%d stat len=%d rssi=%d " - "rate=%x chan=%d tstamp=%ju\n", ring->cur, le32toh(desc->len), - le16toh(head->len), (int8_t)stat->rssi, head->rate, head->chan, - (uintmax_t)le64toh(tail->tstamp))); - - /* discard Rx frames with bad CRC early */ - if ((le32toh(tail->flags) & WPI_RX_NOERROR) != WPI_RX_NOERROR) { - DPRINTFN(WPI_DEBUG_RX, ("%s: rx flags error %x\n", __func__, - le32toh(tail->flags))); - ifp->if_ierrors++; - return; - } - if (le16toh(head->len) < sizeof (struct ieee80211_frame)) { - DPRINTFN(WPI_DEBUG_RX, ("%s: frame too short: %d\n", __func__, - le16toh(head->len))); - ifp->if_ierrors++; - return; - } - - /* XXX don't need mbuf, just dma buffer */ - mnew = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); - if (mnew == NULL) { - DPRINTFN(WPI_DEBUG_RX, ("%s: no mbuf to restock ring\n", - __func__)); - ifp->if_ierrors++; - return; + len = le16toh(head->len); + tail = (struct wpi_rx_tail *)((caddr_t)(head + 1) + len); + flags = le32toh(tail->flags); + + DPRINTF(sc, WPI_DEBUG_RECV, "%s: idx %d len %d stat len %u rssi %d" + " rate %x chan %d tstamp %ju\n", __func__, ring->cur, + le32toh(desc->len), len, (int8_t)stat->rssi, + head->plcp, head->chan, (uintmax_t)le64toh(tail->tstamp)); + + /* Discard frames with a bad FCS early. */ + if ((flags & WPI_RX_NOERROR) != WPI_RX_NOERROR) { + DPRINTF(sc, WPI_DEBUG_RECV, "%s: RX flags error %x\n", + __func__, flags); + goto fail1; + } + /* Discard frames that are too short. */ + if (len < sizeof (struct ieee80211_frame_ack)) { + DPRINTF(sc, WPI_DEBUG_RECV, "%s: frame too short: %d\n", + __func__, len); + goto fail1; + } + + m1 = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m1 == NULL) { + DPRINTF(sc, WPI_DEBUG_ANY, "%s: no mbuf to restock ring\n", + __func__); + goto fail1; } bus_dmamap_unload(ring->data_dmat, data->map); - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(mnew, caddr_t), MJUMPAGESIZE, - wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), + MJUMPAGESIZE, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); - m_freem(mnew); - ifp->if_ierrors++; - return; + m_freem(m1); + + /* Try to reload the old mbuf. */ + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), MJUMPAGESIZE, wpi_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + panic("%s: could not load old RX mbuf", __func__); + } + /* Physical address may have changed. */ + ring->desc[ring->cur] = htole32(paddr); + bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + goto fail1; } - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - /* finalize mbuf and swap in new one */ m = data->m; + data->m = m1; + /* Update RX descriptor. */ + ring->desc[ring->cur] = htole32(paddr); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + + /* Finalize mbuf. */ m->m_pkthdr.rcvif = ifp; m->m_data = (caddr_t)(head + 1); - m->m_pkthdr.len = m->m_len = le16toh(head->len); + m->m_pkthdr.len = m->m_len = len; + + /* Grab a reference to the source node. */ + wh = mtod(m, struct ieee80211_frame *); + + if ((wh->i_fc[1] & IEEE80211_FC1_PROTECTED) && + (flags & WPI_RX_CIPHER_MASK) == WPI_RX_CIPHER_CCMP) { + /* Check whether decryption was successful or not. */ + if ((flags & WPI_RX_DECRYPT_MASK) != WPI_RX_DECRYPT_OK) { + DPRINTF(sc, WPI_DEBUG_RECV, + "CCMP decryption failed 0x%x\n", flags); + goto fail2; + } + m->m_flags |= M_WEP; + } - data->m = mnew; - /* update Rx descriptor */ - ring->desc[ring->cur] = htole32(paddr); + if (len >= sizeof(struct ieee80211_frame_min)) + ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); + else + ni = NULL; + + sc->rx_tstamp = tail->tstamp; if (ieee80211_radiotap_active(ic)) { struct wpi_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; - tap->wr_chan_freq = - htole16(ic->ic_channels[head->chan].ic_freq); - tap->wr_chan_flags = - htole16(ic->ic_channels[head->chan].ic_flags); - tap->wr_dbm_antsignal = (int8_t)(stat->rssi - WPI_RSSI_OFFSET); - tap->wr_dbm_antnoise = (int8_t)le16toh(stat->noise); + if (head->flags & htole16(WPI_STAT_FLAG_SHPREAMBLE)) + tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + tap->wr_dbm_antsignal = (int8_t)(stat->rssi + WPI_RSSI_OFFSET); + tap->wr_dbm_antnoise = WPI_RSSI_OFFSET; tap->wr_tsft = tail->tstamp; tap->wr_antenna = (le16toh(head->flags) >> 4) & 0xf; - switch (head->rate) { - /* CCK rates */ - case 10: tap->wr_rate = 2; break; - case 20: tap->wr_rate = 4; break; - case 55: tap->wr_rate = 11; break; - case 110: tap->wr_rate = 22; break; - /* OFDM rates */ - case 0xd: tap->wr_rate = 12; break; - case 0xf: tap->wr_rate = 18; break; - case 0x5: tap->wr_rate = 24; break; - case 0x7: tap->wr_rate = 36; break; - case 0x9: tap->wr_rate = 48; break; - case 0xb: tap->wr_rate = 72; break; - case 0x1: tap->wr_rate = 96; break; - case 0x3: tap->wr_rate = 108; break; - /* unknown rate: should not happen */ - default: tap->wr_rate = 0; - } - if (le16toh(head->flags) & 0x4) - tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; + tap->wr_rate = plcp2rate(head->plcp); } WPI_UNLOCK(sc); - ni = ieee80211_find_rxnode(ic, mtod(m, struct ieee80211_frame_min *)); + /* Send the frame to the 802.11 layer. */ if (ni != NULL) { - (void) ieee80211_input(ni, m, stat->rssi, 0); + (void)ieee80211_input(ni, m, stat->rssi, WPI_RSSI_OFFSET); + /* Node is no longer needed. */ ieee80211_free_node(ni); } else - (void) ieee80211_input_all(ic, m, stat->rssi, 0); + (void)ieee80211_input_all(ic, m, stat->rssi, WPI_RSSI_OFFSET); WPI_LOCK(sc); + + return; + +fail2: m_freem(m); + +fail1: if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); +} + +static void +wpi_rx_statistics(struct wpi_softc *sc, struct wpi_rx_desc *desc, + struct wpi_rx_data *data) +{ + /* Ignore */ } static void -wpi_tx_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) +wpi_tx_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) { struct ifnet *ifp = sc->sc_ifp; struct wpi_tx_ring *ring = &sc->txq[desc->qid & 0x3]; - struct wpi_tx_data *txdata = &ring->data[desc->idx]; + struct wpi_tx_data *data = &ring->data[desc->idx]; struct wpi_tx_stat *stat = (struct wpi_tx_stat *)(desc + 1); - struct ieee80211_node *ni = txdata->ni; - struct ieee80211vap *vap = ni->ni_vap; - int retrycnt = 0; + struct mbuf *m; + struct ieee80211_node *ni; + struct ieee80211vap *vap; + struct ieee80211com *ic; + uint32_t status = le32toh(stat->status); + int ackfailcnt = stat->ackfailcnt / WPI_NTRIES_DEFAULT; - DPRINTFN(WPI_DEBUG_TX, ("tx done: qid=%d idx=%d retries=%d nkill=%d " - "rate=%x duration=%d status=%x\n", desc->qid, desc->idx, - stat->ntries, stat->nkill, stat->rate, le32toh(stat->duration), - le32toh(stat->status))); + KASSERT(data->ni != NULL, ("no node")); + KASSERT(data->m != NULL, ("no mbuf")); - /* - * Update rate control statistics for the node. - * XXX we should not count mgmt frames since they're always sent at - * the lowest available bit-rate. - * XXX frames w/o ACK shouldn't be used either - */ - if (stat->ntries > 0) { - DPRINTFN(WPI_DEBUG_TX, ("%d retries\n", stat->ntries)); - retrycnt = 1; - } - ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, - &retrycnt, NULL); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - /* XXX oerrors should only count errors !maxtries */ - if ((le32toh(stat->status) & 0xff) != 1) - ifp->if_oerrors++; - else - ifp->if_opackets++; + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: " + "qid %d idx %d retries %d btkillcnt %d rate %x duration %d " + "status %x\n", __func__, desc->qid, desc->idx, stat->ackfailcnt, + stat->btkillcnt, stat->rate, le32toh(stat->duration), status); - bus_dmamap_sync(ring->data_dmat, txdata->map, BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(ring->data_dmat, txdata->map); - /* XXX handle M_TXCB? */ - m_freem(txdata->m); - txdata->m = NULL; - ieee80211_free_node(txdata->ni); - txdata->ni = NULL; + /* Unmap and free mbuf. */ + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(ring->data_dmat, data->map); + m = data->m, data->m = NULL; + ni = data->ni, data->ni = NULL; + vap = ni->ni_vap; + ic = vap->iv_ic; - ring->queued--; + /* + * Update rate control statistics for the node. + */ + if (status & WPI_TX_STATUS_FAIL) { + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + ieee80211_ratectl_tx_complete(vap, ni, + IEEE80211_RATECTL_TX_FAILURE, &ackfailcnt, NULL); + } else { + if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); + ieee80211_ratectl_tx_complete(vap, ni, + IEEE80211_RATECTL_TX_SUCCESS, &ackfailcnt, NULL); + } + + ieee80211_tx_complete(ni, m, (status & WPI_TX_STATUS_FAIL) != 0); + + WPI_TXQ_STATE_LOCK(sc); + ring->queued -= 1; + if (ring->queued > 0) { + callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, sc); + + if (sc->qfullmsk != 0 && + ring->queued < WPI_TX_RING_LOMARK) { + sc->qfullmsk &= ~(1 << ring->qid); + IF_LOCK(&ifp->if_snd); + if (sc->qfullmsk == 0 && + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + IF_UNLOCK(&ifp->if_snd); + ieee80211_runtask(ic, &sc->sc_start_task); + } else + IF_UNLOCK(&ifp->if_snd); + } + } else + callout_stop(&sc->tx_timeout); + WPI_TXQ_STATE_UNLOCK(sc); - sc->sc_tx_timer = 0; - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - wpi_start_locked(ifp); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); } +/* + * Process a "command done" firmware notification. This is where we wakeup + * processes waiting for a synchronous command completion. + */ static void -wpi_cmd_intr(struct wpi_softc *sc, struct wpi_rx_desc *desc) +wpi_cmd_done(struct wpi_softc *sc, struct wpi_rx_desc *desc) { - struct wpi_tx_ring *ring = &sc->cmdq; + struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM]; struct wpi_tx_data *data; - DPRINTFN(WPI_DEBUG_CMD, ("cmd notification qid=%x idx=%d flags=%x " - "type=%s len=%d\n", desc->qid, desc->idx, - desc->flags, wpi_cmd_str(desc->type), - le32toh(desc->len))); + DPRINTF(sc, WPI_DEBUG_CMD, "cmd notification qid %x idx %d flags %x " + "type %s len %d\n", desc->qid, desc->idx, + desc->flags, wpi_cmd_str(desc->type), + le32toh(desc->len)); + + if ((desc->qid & WPI_RX_DESC_QID_MSK) != WPI_CMD_QUEUE_NUM) + return; /* Not a command ack. */ - if ((desc->qid & 7) != 4) - return; /* not a command ack */ + KASSERT(ring->queued == 0, ("ring->queued must be 0")); data = &ring->data[desc->idx]; - /* if the command was mapped in a mbuf, free it */ + /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } - sc->flags &= ~WPI_FLAG_BUSY; wakeup(&ring->cmd[desc->idx]); + + if (desc->type == WPI_CMD_SET_POWER_MODE) { + WPI_TXQ_LOCK(sc); + if (sc->sc_flags & WPI_PS_PATH) { + sc->sc_update_rx_ring = wpi_update_rx_ring_ps; + sc->sc_update_tx_ring = wpi_update_tx_ring_ps; + } else { + sc->sc_update_rx_ring = wpi_update_rx_ring; + sc->sc_update_tx_ring = wpi_update_tx_ring; + } + WPI_TXQ_UNLOCK(sc); + } } static void @@ -1647,374 +2201,876 @@ wpi_notif_intr(struct wpi_softc *sc) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct wpi_rx_desc *desc; - struct wpi_rx_data *data; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); uint32_t hw; bus_dmamap_sync(sc->shared_dma.tag, sc->shared_dma.map, BUS_DMASYNC_POSTREAD); - hw = le32toh(sc->shared->next); + hw = le32toh(sc->shared->next) & 0xfff; + hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; + while (sc->rxq.cur != hw) { - data = &sc->rxq.data[sc->rxq.cur]; + sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; + + struct wpi_rx_data *data = &sc->rxq.data[sc->rxq.cur]; + struct wpi_rx_desc *desc; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); - desc = (void *)data->m->m_ext.ext_buf; + desc = mtod(data->m, struct wpi_rx_desc *); - DPRINTFN(WPI_DEBUG_NOTIFY, - ("notify qid=%x idx=%d flags=%x type=%d len=%d\n", - desc->qid, - desc->idx, - desc->flags, - desc->type, - le32toh(desc->len))); + DPRINTF(sc, WPI_DEBUG_NOTIFY, + "%s: cur=%d; qid %x idx %d flags %x type %d(%s) len %d\n", + __func__, sc->rxq.cur, desc->qid, desc->idx, desc->flags, + desc->type, wpi_cmd_str(desc->type), le32toh(desc->len)); - if (!(desc->qid & 0x80)) /* reply to a command */ - wpi_cmd_intr(sc, desc); + if (!(desc->qid & WPI_UNSOLICITED_RX_NOTIF)) { + /* Reply to a command. */ + wpi_cmd_done(sc, desc); + } switch (desc->type) { case WPI_RX_DONE: - /* a 802.11 frame was received */ - wpi_rx_intr(sc, desc, data); + /* An 802.11 frame has been received. */ + wpi_rx_done(sc, desc, data); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + /* wpi_stop() was called. */ + return; + } + break; case WPI_TX_DONE: - /* a 802.11 frame has been transmitted */ - wpi_tx_intr(sc, desc); + /* An 802.11 frame has been transmitted. */ + wpi_tx_done(sc, desc); break; + case WPI_RX_STATISTICS: + case WPI_BEACON_STATISTICS: + wpi_rx_statistics(sc, desc, data); + break; + + case WPI_BEACON_MISSED: + { + struct wpi_beacon_missed *miss = + (struct wpi_beacon_missed *)(desc + 1); + uint32_t expected, misses, received, threshold; + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + misses = le32toh(miss->consecutive); + expected = le32toh(miss->expected); + received = le32toh(miss->received); + threshold = MAX(2, vap->iv_bmissthreshold); + + DPRINTF(sc, WPI_DEBUG_BMISS, + "%s: beacons missed %u(%u) (received %u/%u)\n", + __func__, misses, le32toh(miss->total), received, + expected); + + if (misses >= threshold || + (received == 0 && expected >= threshold)) { + WPI_RXON_LOCK(sc); + if (callout_pending(&sc->scan_timeout)) { + wpi_cmd(sc, WPI_CMD_SCAN_ABORT, NULL, + 0, 1); + } + WPI_RXON_UNLOCK(sc); + if (vap->iv_state == IEEE80211_S_RUN && + (ic->ic_flags & IEEE80211_F_SCAN) == 0) + ieee80211_beacon_miss(ic); + } + + break; + } +#ifdef WPI_DEBUG + case WPI_BEACON_SENT: + { + struct wpi_tx_stat *stat = + (struct wpi_tx_stat *)(desc + 1); + uint64_t *tsf = (uint64_t *)(stat + 1); + uint32_t *mode = (uint32_t *)(tsf + 1); + + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + + DPRINTF(sc, WPI_DEBUG_BEACON, + "beacon sent: rts %u, ack %u, btkill %u, rate %u, " + "duration %u, status %x, tsf %ju, mode %x\n", + stat->rtsfailcnt, stat->ackfailcnt, + stat->btkillcnt, stat->rate, le32toh(stat->duration), + le32toh(stat->status), *tsf, *mode); + + break; + } +#endif case WPI_UC_READY: { struct wpi_ucode_info *uc = - (struct wpi_ucode_info *)(desc + 1); + (struct wpi_ucode_info *)(desc + 1); - /* the microcontroller is ready */ - DPRINTF(("microcode alive notification version %x " - "alive %x\n", le32toh(uc->version), - le32toh(uc->valid))); + /* The microcontroller is ready. */ + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + DPRINTF(sc, WPI_DEBUG_RESET, + "microcode alive notification version=%d.%d " + "subtype=%x alive=%x\n", uc->major, uc->minor, + uc->subtype, le32toh(uc->valid)); if (le32toh(uc->valid) != 1) { device_printf(sc->sc_dev, "microcontroller initialization failed\n"); wpi_stop_locked(sc); } + /* Save the address of the error log in SRAM. */ + sc->errptr = le32toh(uc->errptr); break; } case WPI_STATE_CHANGED: { + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + uint32_t *status = (uint32_t *)(desc + 1); - /* enabled/disabled notification */ - DPRINTF(("state changed to %x\n", le32toh(*status))); + DPRINTF(sc, WPI_DEBUG_STATE, "state changed to %x\n", + le32toh(*status)); if (le32toh(*status) & 1) { - device_printf(sc->sc_dev, - "Radio transmitter is switched off\n"); - sc->flags |= WPI_FLAG_HW_RADIO_OFF; - ifp->if_drv_flags &= ~IFF_DRV_RUNNING; - /* Disable firmware commands */ - WPI_WRITE(sc, WPI_UCODE_SET, WPI_DISABLE_CMD); + WPI_NT_LOCK(sc); + wpi_clear_node_table(sc); + WPI_NT_UNLOCK(sc); + taskqueue_enqueue(sc->sc_tq, + &sc->sc_radiooff_task); + return; } break; } +#ifdef WPI_DEBUG case WPI_START_SCAN: { -#ifdef WPI_DEBUG + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); + struct wpi_start_scan *scan = - (struct wpi_start_scan *)(desc + 1); -#endif + (struct wpi_start_scan *)(desc + 1); + DPRINTF(sc, WPI_DEBUG_SCAN, + "%s: scanning channel %d status %x\n", + __func__, scan->chan, le32toh(scan->status)); - DPRINTFN(WPI_DEBUG_SCANNING, - ("scanning channel %d status %x\n", - scan->chan, le32toh(scan->status))); break; } +#endif case WPI_STOP_SCAN: { -#ifdef WPI_DEBUG - struct wpi_stop_scan *scan = - (struct wpi_stop_scan *)(desc + 1); -#endif - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - DPRINTFN(WPI_DEBUG_SCANNING, - ("scan finished nchan=%d status=%d chan=%d\n", - scan->nchan, scan->status, scan->chan)); + bus_dmamap_sync(sc->rxq.data_dmat, data->map, + BUS_DMASYNC_POSTREAD); - sc->sc_scan_timer = 0; - ieee80211_scan_next(vap); + struct wpi_stop_scan *scan = + (struct wpi_stop_scan *)(desc + 1); + + DPRINTF(sc, WPI_DEBUG_SCAN, + "scan finished nchan=%d status=%d chan=%d\n", + scan->nchan, scan->status, scan->chan); + + WPI_RXON_LOCK(sc); + callout_stop(&sc->scan_timeout); + WPI_RXON_UNLOCK(sc); + if (scan->status == WPI_SCAN_ABORTED) + ieee80211_cancel_scan(vap); + else + ieee80211_scan_next(vap); break; } - case WPI_MISSED_BEACON: - { - struct wpi_missed_beacon *beacon = - (struct wpi_missed_beacon *)(desc + 1); - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - - if (le32toh(beacon->consecutive) >= - vap->iv_bmissthreshold) { - DPRINTF(("Beacon miss: %u >= %u\n", - le32toh(beacon->consecutive), - vap->iv_bmissthreshold)); - ieee80211_beacon_miss(ic); - } - break; } + + if (sc->rxq.cur % 8 == 0) { + /* Tell the firmware what we have processed. */ + sc->sc_update_rx_ring(sc); } + } +} - sc->rxq.cur = (sc->rxq.cur + 1) % WPI_RX_RING_COUNT; +/* + * Process an INT_WAKEUP interrupt raised when the microcontroller wakes up + * from power-down sleep mode. + */ +static void +wpi_wakeup_intr(struct wpi_softc *sc) +{ + int qid; + + DPRINTF(sc, WPI_DEBUG_PWRSAVE, + "%s: ucode wakeup from power-down sleep\n", __func__); + + /* Wakeup RX and TX rings. */ + if (sc->rxq.update) { + sc->rxq.update = 0; + wpi_update_rx_ring(sc); } + WPI_TXQ_LOCK(sc); + for (qid = 0; qid < WPI_DRV_NTXQUEUES; qid++) { + struct wpi_tx_ring *ring = &sc->txq[qid]; - /* tell the firmware what we have processed */ - hw = (hw == 0) ? WPI_RX_RING_COUNT - 1 : hw - 1; - WPI_WRITE(sc, WPI_RX_WIDX, hw & ~7); + if (ring->update) { + ring->update = 0; + wpi_update_tx_ring(sc, ring); + } + } + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_MAC_ACCESS_REQ); + WPI_TXQ_UNLOCK(sc); +} + +/* + * This function prints firmware registers + */ +#ifdef WPI_DEBUG +static void +wpi_debug_registers(struct wpi_softc *sc) +{ + size_t i; + static const uint32_t csr_tbl[] = { + WPI_HW_IF_CONFIG, + WPI_INT, + WPI_INT_MASK, + WPI_FH_INT, + WPI_GPIO_IN, + WPI_RESET, + WPI_GP_CNTRL, + WPI_EEPROM, + WPI_EEPROM_GP, + WPI_GIO, + WPI_UCODE_GP1, + WPI_UCODE_GP2, + WPI_GIO_CHICKEN, + WPI_ANA_PLL, + WPI_DBG_HPET_MEM, + }; + static const uint32_t prph_tbl[] = { + WPI_APMG_CLK_CTRL, + WPI_APMG_PS, + WPI_APMG_PCI_STT, + WPI_APMG_RFKILL, + }; + + DPRINTF(sc, WPI_DEBUG_REGISTER,"%s","\n"); + + for (i = 0; i < nitems(csr_tbl); i++) { + DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ", + wpi_get_csr_string(csr_tbl[i]), WPI_READ(sc, csr_tbl[i])); + + if ((i + 1) % 2 == 0) + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); + } + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n\n"); + + if (wpi_nic_lock(sc) == 0) { + for (i = 0; i < nitems(prph_tbl); i++) { + DPRINTF(sc, WPI_DEBUG_REGISTER, " %-18s: 0x%08x ", + wpi_get_prph_string(prph_tbl[i]), + wpi_prph_read(sc, prph_tbl[i])); + + if ((i + 1) % 2 == 0) + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); + } + DPRINTF(sc, WPI_DEBUG_REGISTER, "\n"); + wpi_nic_unlock(sc); + } else { + DPRINTF(sc, WPI_DEBUG_REGISTER, + "Cannot access internal registers.\n"); + } +} +#endif + +/* + * Dump the error log of the firmware when a firmware panic occurs. Although + * we can't debug the firmware because it is neither open source nor free, it + * can help us to identify certain classes of problems. + */ +static void +wpi_fatal_intr(struct wpi_softc *sc) +{ + struct wpi_fw_dump dump; + uint32_t i, offset, count; + + /* Check that the error log address is valid. */ + if (sc->errptr < WPI_FW_DATA_BASE || + sc->errptr + sizeof (dump) > + WPI_FW_DATA_BASE + WPI_FW_DATA_MAXSZ) { + printf("%s: bad firmware error log address 0x%08x\n", __func__, + sc->errptr); + return; + } + if (wpi_nic_lock(sc) != 0) { + printf("%s: could not read firmware error log\n", __func__); + return; + } + /* Read number of entries in the log. */ + count = wpi_mem_read(sc, sc->errptr); + if (count == 0 || count * sizeof (dump) > WPI_FW_DATA_MAXSZ) { + printf("%s: invalid count field (count = %u)\n", __func__, + count); + wpi_nic_unlock(sc); + return; + } + /* Skip "count" field. */ + offset = sc->errptr + sizeof (uint32_t); + printf("firmware error log (count = %u):\n", count); + for (i = 0; i < count; i++) { + wpi_mem_read_region_4(sc, offset, (uint32_t *)&dump, + sizeof (dump) / sizeof (uint32_t)); + + printf(" error type = \"%s\" (0x%08X)\n", + (dump.desc < nitems(wpi_fw_errmsg)) ? + wpi_fw_errmsg[dump.desc] : "UNKNOWN", + dump.desc); + printf(" error data = 0x%08X\n", + dump.data); + printf(" branch link = 0x%08X%08X\n", + dump.blink[0], dump.blink[1]); + printf(" interrupt link = 0x%08X%08X\n", + dump.ilink[0], dump.ilink[1]); + printf(" time = %u\n", dump.time); + + offset += sizeof (dump); + } + wpi_nic_unlock(sc); + /* Dump driver status (TX and RX rings) while we're here. */ + printf("driver status:\n"); + WPI_TXQ_LOCK(sc); + for (i = 0; i < WPI_DRV_NTXQUEUES; i++) { + struct wpi_tx_ring *ring = &sc->txq[i]; + printf(" tx ring %2d: qid=%-2d cur=%-3d queued=%-3d\n", + i, ring->qid, ring->cur, ring->queued); + } + WPI_TXQ_UNLOCK(sc); + printf(" rx ring: cur=%d\n", sc->rxq.cur); } static void wpi_intr(void *arg) { struct wpi_softc *sc = arg; - uint32_t r; + struct ifnet *ifp = sc->sc_ifp; + uint32_t r1, r2; WPI_LOCK(sc); - r = WPI_READ(sc, WPI_INTR); - if (r == 0 || r == 0xffffffff) { - WPI_UNLOCK(sc); - return; - } + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, 0); + + r1 = WPI_READ(sc, WPI_INT); + + if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) + goto end; /* Hardware gone! */ - /* disable interrupts */ - WPI_WRITE(sc, WPI_MASK, 0); - /* ack interrupts */ - WPI_WRITE(sc, WPI_INTR, r); + r2 = WPI_READ(sc, WPI_FH_INT); - if (r & (WPI_SW_ERROR | WPI_HW_ERROR)) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + DPRINTF(sc, WPI_DEBUG_INTR, "%s: reg1=0x%08x reg2=0x%08x\n", __func__, + r1, r2); + if (r1 == 0 && r2 == 0) + goto done; /* Interrupt not for us. */ + + /* Acknowledge interrupts. */ + WPI_WRITE(sc, WPI_INT, r1); + WPI_WRITE(sc, WPI_FH_INT, r2); + + if (r1 & (WPI_INT_SW_ERR | WPI_INT_HW_ERR)) { device_printf(sc->sc_dev, "fatal firmware error\n"); - DPRINTFN(6,("(%s)\n", (r & WPI_SW_ERROR) ? "(Software Error)" : - "(Hardware Error)")); - if (vap != NULL) - ieee80211_cancel_scan(vap); - ieee80211_runtask(ic, &sc->sc_restarttask); - sc->flags &= ~WPI_FLAG_BUSY; - WPI_UNLOCK(sc); - return; +#ifdef WPI_DEBUG + wpi_debug_registers(sc); +#endif + wpi_fatal_intr(sc); + DPRINTF(sc, WPI_DEBUG_HW, + "(%s)\n", (r1 & WPI_INT_SW_ERR) ? "(Software Error)" : + "(Hardware Error)"); + taskqueue_enqueue(sc->sc_tq, &sc->sc_reinittask); + goto end; } - if (r & WPI_RX_INTR) + if ((r1 & (WPI_INT_FH_RX | WPI_INT_SW_RX)) || + (r2 & WPI_FH_INT_RX)) wpi_notif_intr(sc); - if (r & WPI_ALIVE_INTR) /* firmware initialized */ - wakeup(sc); + if (r1 & WPI_INT_ALIVE) + wakeup(sc); /* Firmware is alive. */ - /* re-enable interrupts */ - if (sc->sc_ifp->if_flags & IFF_UP) - WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); + if (r1 & WPI_INT_WAKEUP) + wpi_wakeup_intr(sc); - WPI_UNLOCK(sc); +done: + /* Re-enable interrupts. */ + if (ifp->if_flags & IFF_UP) + WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); + +end: WPI_UNLOCK(sc); } -static uint8_t -wpi_plcp_signal(int rate) +static int +wpi_cmd2(struct wpi_softc *sc, struct wpi_buf *buf) { - switch (rate) { - /* CCK rates (returned values are device-dependent) */ - case 2: return 10; - case 4: return 20; - case 11: return 55; - case 22: return 110; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211_frame *wh; + struct wpi_tx_cmd *cmd; + struct wpi_tx_data *data; + struct wpi_tx_desc *desc; + struct wpi_tx_ring *ring; + struct mbuf *m1; + bus_dma_segment_t *seg, segs[WPI_MAX_SCATTER]; + int error, i, hdrlen, nsegs, totlen, pad; - /* OFDM rates (cf IEEE Std 802.11a-1999, pp. 14 Table 80) */ - /* R1-R4 (ral/ural is R4-R1) */ - case 12: return 0xd; - case 18: return 0xf; - case 24: return 0x5; - case 36: return 0x7; - case 48: return 0x9; - case 72: return 0xb; - case 96: return 0x1; - case 108: return 0x3; + WPI_TXQ_LOCK(sc); - /* unsupported rates (should not get there) */ - default: return 0; + KASSERT(buf->size <= sizeof(buf->data), ("buffer overflow")); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (sc->txq_active == 0) { + /* wpi_stop() was called */ + error = ENETDOWN; + goto fail; } -} -/* quickly determine if a given rate is CCK or OFDM */ -#define WPI_RATE_IS_OFDM(rate) ((rate) >= 12 && (rate) != 22) + wh = mtod(buf->m, struct ieee80211_frame *); + hdrlen = ieee80211_anyhdrsize(wh); + totlen = buf->m->m_pkthdr.len; + + if (hdrlen & 3) { + /* First segment length must be a multiple of 4. */ + pad = 4 - (hdrlen & 3); + } else + pad = 0; + + ring = &sc->txq[buf->ac]; + desc = &ring->desc[ring->cur]; + data = &ring->data[ring->cur]; + + /* Prepare TX firmware command. */ + cmd = &ring->cmd[ring->cur]; + cmd->code = buf->code; + cmd->flags = 0; + cmd->qid = ring->qid; + cmd->idx = ring->cur; + + memcpy(cmd->data, buf->data, buf->size); + + /* Save and trim IEEE802.11 header. */ + memcpy((uint8_t *)(cmd->data + buf->size), wh, hdrlen); + m_adj(buf->m, hdrlen); + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, buf->m, + segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + goto fail; + } + if (error != 0) { + /* Too many DMA segments, linearize mbuf. */ + m1 = m_collapse(buf->m, M_NOWAIT, WPI_MAX_SCATTER - 1); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + error = ENOBUFS; + goto fail; + } + buf->m = m1; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, + buf->m, segs, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, + error); + goto fail; + } + } + + KASSERT(nsegs < WPI_MAX_SCATTER, + ("too many DMA segments, nsegs (%d) should be less than %d", + nsegs, WPI_MAX_SCATTER)); + + data->m = buf->m; + data->ni = buf->ni; + + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: qid %d idx %d len %d nsegs %d\n", + __func__, ring->qid, ring->cur, totlen, nsegs); + + /* Fill TX descriptor. */ + desc->nsegs = WPI_PAD32(totlen + pad) << 4 | (1 + nsegs); + /* First DMA segment is used by the TX command. */ + desc->segs[0].addr = htole32(data->cmd_paddr); + desc->segs[0].len = htole32(4 + buf->size + hdrlen + pad); + /* Other DMA segments are for data payload. */ + seg = &segs[0]; + for (i = 1; i <= nsegs; i++) { + desc->segs[i].addr = htole32(seg->ds_addr); + desc->segs[i].len = htole32(seg->ds_len); + seg++; + } + + bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, + BUS_DMASYNC_PREWRITE); + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); + + /* Kick TX ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + sc->sc_update_tx_ring(sc, ring); + + if (ring->qid < WPI_CMD_QUEUE_NUM) { + /* Mark TX ring as full if we reach a certain threshold. */ + WPI_TXQ_STATE_LOCK(sc); + if (++ring->queued > WPI_TX_RING_HIMARK) { + sc->qfullmsk |= 1 << ring->qid; + + IF_LOCK(&ifp->if_snd); + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + IF_UNLOCK(&ifp->if_snd); + } + + callout_reset(&sc->tx_timeout, 5*hz, wpi_tx_timeout, sc); + WPI_TXQ_STATE_UNLOCK(sc); + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + WPI_TXQ_UNLOCK(sc); + + return 0; + +fail: m_freem(buf->m); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + WPI_TXQ_UNLOCK(sc); + + return error; +} /* - * Construct the data packet for a transmit buffer and acutally put - * the buffer onto the transmit ring, kicking the card to process the - * the buffer. + * Construct the data packet for a transmit buffer. */ static int -wpi_tx_data(struct wpi_softc *sc, struct mbuf *m0, struct ieee80211_node *ni, - int ac) +wpi_tx_data(struct wpi_softc *sc, struct mbuf *m, struct ieee80211_node *ni) { + const struct ieee80211_txparam *tp; struct ieee80211vap *vap = ni->ni_vap; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - const struct chanAccParams *cap = &ic->ic_wme.wme_chanParams; - struct wpi_tx_ring *ring = &sc->txq[ac]; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - struct wpi_cmd_data *tx; + struct ieee80211com *ic = ni->ni_ic; + struct wpi_node *wn = WPI_NODE(ni); + struct ieee80211_channel *chan; struct ieee80211_frame *wh; - const struct ieee80211_txparam *tp; - struct ieee80211_key *k; - struct mbuf *mnew; - int i, error, nsegs, rate, hdrlen, ismcast; - bus_dma_segment_t segs[WPI_MAX_SCATTER]; + struct ieee80211_key *k = NULL; + struct wpi_buf tx_data; + struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data; + uint32_t flags; + uint16_t qos; + uint8_t tid, type; + int ac, error, swcrypt, rate, ismcast, totlen; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + /* Select EDCA Access Category and TX ring for this frame. */ + if (IEEE80211_QOS_HAS_SEQ(wh)) { + qos = ((const struct ieee80211_qosframe *)wh)->i_qos[0]; + tid = qos & IEEE80211_QOS_TID; + } else { + qos = 0; + tid = 0; + } + ac = M_WME_GETAC(m); - wh = mtod(m0, struct ieee80211_frame *); + chan = (ni->ni_chan != IEEE80211_CHAN_ANYC) ? + ni->ni_chan : ic->ic_curchan; + tp = &vap->iv_txparms[ieee80211_chan2mode(chan)]; - hdrlen = ieee80211_hdrsize(wh); - ismcast = IEEE80211_IS_MULTICAST(wh->i_addr1); + /* Choose a TX rate index. */ + if (type == IEEE80211_FC0_TYPE_MGT) + rate = tp->mgmtrate; + else if (ismcast) + rate = tp->mcastrate; + else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) + rate = tp->ucastrate; + else if (m->m_flags & M_EAPOL) + rate = tp->mgmtrate; + else { + /* XXX pass pktlen */ + (void) ieee80211_ratectl_rate(ni, NULL, 0); + rate = ni->ni_txrate; + } + /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { - k = ieee80211_crypto_encap(ni, m0); + /* Retrieve key for TX. */ + k = ieee80211_crypto_encap(ni, m); if (k == NULL) { - m_freem(m0); - return ENOBUFS; + error = ENOBUFS; + goto fail; } - /* packet header may have moved, reset our local pointer */ - wh = mtod(m0, struct ieee80211_frame *); + swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT; + + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); } + totlen = m->m_pkthdr.len; - cmd = &ring->cmd[ring->cur]; - cmd->code = WPI_CMD_TX_DATA; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; + if (ieee80211_radiotap_active_vap(vap)) { + struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; - tx = (struct wpi_cmd_data *)cmd->data; - tx->flags = htole32(WPI_TX_AUTO_SEQ); - tx->timeout = htole16(0); - tx->ofdm_mask = 0xff; - tx->cck_mask = 0x0f; - tx->lifetime = htole32(WPI_LIFETIME_INFINITE); - tx->id = ismcast ? WPI_ID_BROADCAST : WPI_ID_BSS; - tx->len = htole16(m0->m_pkthdr.len); + tap->wt_flags = 0; + tap->wt_rate = rate; + if (k != NULL) + tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; + + ieee80211_radiotap_tx(vap, m); + } + + flags = 0; + if (!ismcast) { + /* Unicast frame, check if an ACK is expected. */ + if (!qos || (qos & IEEE80211_QOS_ACKPOLICY) != + IEEE80211_QOS_ACKPOLICY_NOACK) + flags |= WPI_TX_NEED_ACK; + } + + if (!IEEE80211_QOS_HAS_SEQ(wh)) + flags |= WPI_TX_AUTO_SEQ; + if (wh->i_fc[1] & IEEE80211_FC1_MORE_FRAG) + flags |= WPI_TX_MORE_FRAG; /* Cannot happen yet. */ + /* Check if frame must be protected using RTS/CTS or CTS-to-self. */ if (!ismcast) { - if ((ni->ni_flags & IEEE80211_NODE_QOS) == 0 || - !cap->cap_wmeParams[ac].wmep_noackPolicy) - tx->flags |= htole32(WPI_TX_NEED_ACK); - if (m0->m_pkthdr.len + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { - tx->flags |= htole32(WPI_TX_NEED_RTS|WPI_TX_FULL_TXOP); - tx->rts_ntries = 7; + /* NB: Group frames are sent using CCK in 802.11b/g. */ + if (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) { + flags |= WPI_TX_NEED_RTS; + } else if ((ic->ic_flags & IEEE80211_F_USEPROT) && + WPI_RATE_IS_OFDM(rate)) { + if (ic->ic_protmode == IEEE80211_PROT_CTSONLY) + flags |= WPI_TX_NEED_CTS; + else if (ic->ic_protmode == IEEE80211_PROT_RTSCTS) + flags |= WPI_TX_NEED_RTS; } + + if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) + flags |= WPI_TX_FULL_TXOP; } - /* pick a rate */ - tp = &vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)]; - if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_MGT) { + + memset(tx, 0, sizeof (struct wpi_cmd_data)); + if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - /* tell h/w to set timestamp in probe responses */ + + /* Tell HW to set timestamp in probe responses. */ if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) - tx->flags |= htole32(WPI_TX_INSERT_TSTAMP); + flags |= WPI_TX_INSERT_TSTAMP; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->timeout = htole16(3); else tx->timeout = htole16(2); - rate = tp->mgmtrate; - } else if (ismcast) { - rate = tp->mcastrate; - } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { - rate = tp->ucastrate; - } else { - (void) ieee80211_ratectl_rate(ni, NULL, 0); - rate = ni->ni_txrate; } - tx->rate = wpi_plcp_signal(rate); - /* be very persistant at sending frames out */ -#if 0 + if (ismcast || type != IEEE80211_FC0_TYPE_DATA) + tx->id = WPI_ID_BROADCAST; + else { + if (wn->id == WPI_ID_UNDEFINED) { + device_printf(sc->sc_dev, + "%s: undefined node id\n", __func__); + error = EINVAL; + goto fail; + } + + tx->id = wn->id; + } + + if (k != NULL && !swcrypt) { + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + tx->security = WPI_CIPHER_CCMP; + break; + + default: + break; + } + + memcpy(tx->key, k->wk_key, k->wk_keylen); + } + + tx->len = htole16(totlen); + tx->flags = htole32(flags); + tx->plcp = rate2plcp(rate); + tx->tid = tid; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + tx->ofdm_mask = 0xff; + tx->cck_mask = 0x0f; + tx->rts_ntries = 7; tx->data_ntries = tp->maxretry; -#else - tx->data_ntries = 15; /* XXX way too high */ -#endif + + tx_data.ni = ni; + tx_data.m = m; + tx_data.size = sizeof(struct wpi_cmd_data); + tx_data.code = WPI_CMD_TX_DATA; + tx_data.ac = ac; + + return wpi_cmd2(sc, &tx_data); + +fail: m_freem(m); + return error; +} + +static int +wpi_tx_data_raw(struct wpi_softc *sc, struct mbuf *m, + struct ieee80211_node *ni, const struct ieee80211_bpf_params *params) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_key *k = NULL; + struct ieee80211_frame *wh; + struct wpi_buf tx_data; + struct wpi_cmd_data *tx = (struct wpi_cmd_data *)&tx_data.data; + uint32_t flags; + uint8_t type; + int ac, rate, swcrypt, totlen; + + wh = mtod(m, struct ieee80211_frame *); + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + + ac = params->ibp_pri & 3; + + /* Choose a TX rate index. */ + rate = params->ibp_rate0; + + flags = 0; + if (!IEEE80211_QOS_HAS_SEQ(wh)) + flags |= WPI_TX_AUTO_SEQ; + if ((params->ibp_flags & IEEE80211_BPF_NOACK) == 0) + flags |= WPI_TX_NEED_ACK; + if (params->ibp_flags & IEEE80211_BPF_RTS) + flags |= WPI_TX_NEED_RTS; + if (params->ibp_flags & IEEE80211_BPF_CTS) + flags |= WPI_TX_NEED_CTS; + if (flags & (WPI_TX_NEED_RTS | WPI_TX_NEED_CTS)) + flags |= WPI_TX_FULL_TXOP; + + /* Encrypt the frame if need be. */ + if (params->ibp_flags & IEEE80211_BPF_CRYPTO) { + /* Retrieve key for TX. */ + k = ieee80211_crypto_encap(ni, m); + if (k == NULL) { + m_freem(m); + return ENOBUFS; + } + swcrypt = k->wk_flags & IEEE80211_KEY_SWCRYPT; + + /* 802.11 header may have moved. */ + wh = mtod(m, struct ieee80211_frame *); + } + totlen = m->m_pkthdr.len; if (ieee80211_radiotap_active_vap(vap)) { struct wpi_tx_radiotap_header *tap = &sc->sc_txtap; + tap->wt_flags = 0; tap->wt_rate = rate; - tap->wt_hwqueue = ac; - if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) + if (params->ibp_flags & IEEE80211_BPF_CRYPTO) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; - ieee80211_radiotap_tx(vap, m0); + ieee80211_radiotap_tx(vap, m); } - /* save and trim IEEE802.11 header */ - m_copydata(m0, 0, hdrlen, (caddr_t)&tx->wh); - m_adj(m0, hdrlen); + memset(tx, 0, sizeof (struct wpi_cmd_data)); + if (type == IEEE80211_FC0_TYPE_MGT) { + uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m0, segs, - &nsegs, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { - device_printf(sc->sc_dev, "could not map mbuf (error %d)\n", - error); - m_freem(m0); - return error; + /* Tell HW to set timestamp in probe responses. */ + if (subtype == IEEE80211_FC0_SUBTYPE_PROBE_RESP) + flags |= WPI_TX_INSERT_TSTAMP; + if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || + subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) + tx->timeout = htole16(3); + else + tx->timeout = htole16(2); } - if (error != 0) { - /* XXX use m_collapse */ - mnew = m_defrag(m0, M_NOWAIT); - if (mnew == NULL) { - device_printf(sc->sc_dev, - "could not defragment mbuf\n"); - m_freem(m0); - return ENOBUFS; - } - m0 = mnew; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m0, segs, &nsegs, BUS_DMA_NOWAIT); - if (error != 0) { - device_printf(sc->sc_dev, - "could not map mbuf (error %d)\n", error); - m_freem(m0); - return error; + if (k != NULL && !swcrypt) { + switch (k->wk_cipher->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + tx->security = WPI_CIPHER_CCMP; + break; + + default: + break; } + + memcpy(tx->key, k->wk_key, k->wk_keylen); } - data->m = m0; - data->ni = ni; + tx->len = htole16(totlen); + tx->flags = htole32(flags); + tx->plcp = rate2plcp(rate); + tx->id = WPI_ID_BROADCAST; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); + tx->rts_ntries = params->ibp_try1; + tx->data_ntries = params->ibp_try0; - DPRINTFN(WPI_DEBUG_TX, ("sending data: qid=%d idx=%d len=%d nsegs=%d\n", - ring->qid, ring->cur, m0->m_pkthdr.len, nsegs)); + tx_data.ni = ni; + tx_data.m = m; + tx_data.size = sizeof(struct wpi_cmd_data); + tx_data.code = WPI_CMD_TX_DATA; + tx_data.ac = ac; - /* first scatter/gather segment is used by the tx data command */ - desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | - (1 + nsegs) << 24); - desc->segs[0].addr = htole32(ring->cmd_dma.paddr + - ring->cur * sizeof (struct wpi_tx_cmd)); - desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_data)); - for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(segs[i - 1].ds_addr); - desc->segs[i].len = htole32(segs[i - 1].ds_len); + return wpi_cmd2(sc, &tx_data); +} + +static int +wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, + const struct ieee80211_bpf_params *params) +{ + struct ieee80211com *ic = ni->ni_ic; + struct ifnet *ifp = ic->ic_ifp; + struct wpi_softc *sc = ifp->if_softc; + int error = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + ieee80211_free_node(ni); + m_freem(m); + return ENETDOWN; } - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); + WPI_TX_LOCK(sc); + if (params == NULL) { + /* + * Legacy path; interpret frame contents to decide + * precisely how to send the frame. + */ + error = wpi_tx_data(sc, m, ni); + } else { + /* + * Caller supplied explicit parameters to use in + * sending the frame. + */ + error = wpi_tx_data_raw(sc, m, ni, params); + } + WPI_TX_UNLOCK(sc); - ring->queued++; + if (error != 0) { + /* NB: m is reclaimed on tx failure */ + ieee80211_free_node(ni); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); - /* kick ring */ - ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + return error; + } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); return 0; } @@ -2026,84 +3082,81 @@ static void wpi_start(struct ifnet *ifp) { struct wpi_softc *sc = ifp->if_softc; - - WPI_LOCK(sc); - wpi_start_locked(ifp); - WPI_UNLOCK(sc); -} - -static void -wpi_start_locked(struct ifnet *ifp) -{ - struct wpi_softc *sc = ifp->if_softc; struct ieee80211_node *ni; struct mbuf *m; - int ac; - WPI_LOCK_ASSERT(sc); - - if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) - return; + WPI_TX_LOCK(sc); + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: called\n", __func__); for (;;) { + IF_LOCK(&ifp->if_snd); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 || + (ifp->if_drv_flags & IFF_DRV_OACTIVE)) { + IF_UNLOCK(&ifp->if_snd); + break; + } + IF_UNLOCK(&ifp->if_snd); + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); if (m == NULL) break; - ac = M_WME_GETAC(m); - if (sc->txq[ac].queued > sc->txq[ac].count - 8) { - /* there is no place left in this ring */ - IFQ_DRV_PREPEND(&ifp->if_snd, m); - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - break; - } - ni = (struct ieee80211_node *) m->m_pkthdr.rcvif; - if (wpi_tx_data(sc, m, ni, ac) != 0) { + ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; + if (wpi_tx_data(sc, m, ni) != 0) { ieee80211_free_node(ni); - ifp->if_oerrors++; - break; + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); } - sc->sc_tx_timer = 5; } + + DPRINTF(sc, WPI_DEBUG_XMIT, "%s: done\n", __func__); + WPI_TX_UNLOCK(sc); } -static int -wpi_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, - const struct ieee80211_bpf_params *params) +static void +wpi_start_task(void *arg0, int pending) { - struct ieee80211com *ic = ni->ni_ic; - struct ifnet *ifp = ic->ic_ifp; - struct wpi_softc *sc = ifp->if_softc; + struct wpi_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; - /* prevent management frames from being sent if we're not ready */ - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - m_freem(m); - ieee80211_free_node(ni); - return ENETDOWN; - } - WPI_LOCK(sc); + wpi_start(ifp); +} - /* management frames go into ring 0 */ - if (sc->txq[0].queued > sc->txq[0].count - 8) { - ifp->if_drv_flags |= IFF_DRV_OACTIVE; - m_freem(m); - WPI_UNLOCK(sc); - ieee80211_free_node(ni); - return ENOBUFS; /* XXX */ - } +static void +wpi_watchdog_rfkill(void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; - ifp->if_opackets++; - if (wpi_tx_data(sc, m, ni, 0) != 0) - goto bad; - sc->sc_tx_timer = 5; - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); + DPRINTF(sc, WPI_DEBUG_WATCHDOG, "RFkill Watchdog: tick\n"); - WPI_UNLOCK(sc); - return 0; -bad: - ifp->if_oerrors++; - WPI_UNLOCK(sc); - ieee80211_free_node(ni); - return EIO; /* XXX */ + /* No need to lock firmware memory. */ + if ((wpi_prph_read(sc, WPI_APMG_RFKILL) & 0x1) == 0) { + /* Radio kill switch is still off. */ + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, + sc); + } else + ieee80211_runtask(ic, &sc->sc_radioon_task); +} + +static void +wpi_scan_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + + if_printf(ifp, "scan timeout\n"); + taskqueue_enqueue(sc->sc_tq, &sc->sc_reinittask); +} + +static void +wpi_tx_timeout(void *arg) +{ + struct wpi_softc *sc = arg; + struct ifnet *ifp = sc->sc_ifp; + + if_printf(ifp, "device timeout\n"); + if_inc_counter(ifp, IFCOUNTER_OERRORS, 1); + taskqueue_enqueue(sc->sc_tq, &sc->sc_reinittask); } static int @@ -2111,30 +3164,27 @@ wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct wpi_softc *sc = ifp->if_softc; struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ifreq *ifr = (struct ifreq *) data; - int error = 0, startall = 0; + int error = 0; switch (cmd) { + case SIOCGIFADDR: + error = ether_ioctl(ifp, cmd, data); + break; case SIOCSIFFLAGS: - WPI_LOCK(sc); - if ((ifp->if_flags & IFF_UP)) { - if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) { - wpi_init_locked(sc, 0); - startall = 1; - } - } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) || - (sc->flags & WPI_FLAG_HW_RADIO_OFF)) - wpi_stop_locked(sc); - WPI_UNLOCK(sc); - if (startall) - ieee80211_start_all(ic); + if (ifp->if_flags & IFF_UP) { + wpi_init(sc); + + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0 && + vap != NULL) + ieee80211_stop(vap); + } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + wpi_stop(sc); break; case SIOCGIFMEDIA: error = ifmedia_ioctl(ifp, ifr, &ic->ic_media, cmd); break; - case SIOCGIFADDR: - error = ether_ioctl(ifp, cmd, data); - break; default: error = EINVAL; break; @@ -2143,64 +3193,63 @@ wpi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) } /* - * Extract various information from EEPROM. - */ -static void -wpi_read_eeprom(struct wpi_softc *sc, uint8_t macaddr[IEEE80211_ADDR_LEN]) -{ - int i; - - /* read the hardware capabilities, revision and SKU type */ - wpi_read_prom_data(sc, WPI_EEPROM_CAPABILITIES, &sc->cap,1); - wpi_read_prom_data(sc, WPI_EEPROM_REVISION, &sc->rev,2); - wpi_read_prom_data(sc, WPI_EEPROM_TYPE, &sc->type, 1); - - /* read the regulatory domain */ - wpi_read_prom_data(sc, WPI_EEPROM_DOMAIN, sc->domain, 4); - - /* read in the hw MAC address */ - wpi_read_prom_data(sc, WPI_EEPROM_MAC, macaddr, 6); - - /* read the list of authorized channels */ - for (i = 0; i < WPI_CHAN_BANDS_COUNT; i++) - wpi_read_eeprom_channels(sc,i); - - /* read the power level calibration info for each group */ - for (i = 0; i < WPI_POWER_GROUPS_COUNT; i++) - wpi_read_eeprom_group(sc,i); -} - -/* * Send a command to the firmware. */ static int -wpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) +wpi_cmd(struct wpi_softc *sc, int code, const void *buf, size_t size, + int async) { - struct wpi_tx_ring *ring = &sc->cmdq; + struct wpi_tx_ring *ring = &sc->txq[WPI_CMD_QUEUE_NUM]; struct wpi_tx_desc *desc; + struct wpi_tx_data *data; struct wpi_tx_cmd *cmd; + struct mbuf *m; + bus_addr_t paddr; + int totlen, error; -#ifdef WPI_DEBUG - if (!async) { - WPI_LOCK_ASSERT(sc); - } -#endif + WPI_TXQ_LOCK(sc); - DPRINTFN(WPI_DEBUG_CMD,("wpi_cmd %d size %d async %d\n", code, size, - async)); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - if (sc->flags & WPI_FLAG_BUSY) { - device_printf(sc->sc_dev, "%s: cmd %d not sent, busy\n", - __func__, code); - return EAGAIN; + if (sc->txq_active == 0) { + /* wpi_stop() was called */ + error = 0; + goto fail; } - sc->flags|= WPI_FLAG_BUSY; - KASSERT(size <= sizeof cmd->data, ("command %d too large: %d bytes", - code, size)); + if (async == 0) + WPI_LOCK_ASSERT(sc); + + DPRINTF(sc, WPI_DEBUG_CMD, "%s: cmd %s size %zu async %d\n", + __func__, wpi_cmd_str(code), size, async); desc = &ring->desc[ring->cur]; - cmd = &ring->cmd[ring->cur]; + data = &ring->data[ring->cur]; + totlen = 4 + size; + + if (size > sizeof cmd->data) { + /* Command is too large to fit in a descriptor. */ + if (totlen > MCLBYTES) { + error = EINVAL; + goto fail; + } + m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + if (m == NULL) { + error = ENOMEM; + goto fail; + } + cmd = mtod(m, struct wpi_tx_cmd *); + error = bus_dmamap_load(ring->data_dmat, data->map, cmd, + totlen, wpi_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + if (error != 0) { + m_freem(m); + goto fail; + } + data->m = m; + } else { + cmd = &ring->cmd[ring->cur]; + paddr = data->cmd_paddr; + } cmd->code = code; cmd->flags = 0; @@ -2208,56 +3257,42 @@ wpi_cmd(struct wpi_softc *sc, int code, const void *buf, int size, int async) cmd->idx = ring->cur; memcpy(cmd->data, buf, size); - desc->flags = htole32(WPI_PAD32(size) << 28 | 1 << 24); - desc->segs[0].addr = htole32(ring->cmd_dma.paddr + - ring->cur * sizeof (struct wpi_tx_cmd)); - desc->segs[0].len = htole32(4 + size); - - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + desc->nsegs = 1 + (WPI_PAD32(size) << 4); + desc->segs[0].addr = htole32(paddr); + desc->segs[0].len = htole32(totlen); - if (async) { - sc->flags &= ~ WPI_FLAG_BUSY; - return 0; + if (size > sizeof cmd->data) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_PREWRITE); + } else { + bus_dmamap_sync(ring->data_dmat, ring->cmd_dma.map, + BUS_DMASYNC_PREWRITE); } + bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); - return msleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); -} + /* Kick command ring. */ + ring->cur = (ring->cur + 1) % WPI_TX_RING_COUNT; + sc->sc_update_tx_ring(sc, ring); -static int -wpi_wme_update(struct ieee80211com *ic) -{ -#define WPI_EXP2(v) htole16((1 << (v)) - 1) -#define WPI_USEC(v) htole16(IEEE80211_TXOP_TO_US(v)) - struct wpi_softc *sc = ic->ic_ifp->if_softc; - const struct wmeParams *wmep; - struct wpi_wme_setup wme; - int ac; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + WPI_TXQ_UNLOCK(sc); - /* don't override default WME values if WME is not actually enabled */ - if (!(ic->ic_flags & IEEE80211_F_WME)) + if (async) return 0; - wme.flags = 0; - for (ac = 0; ac < WME_NUM_AC; ac++) { - wmep = &ic->ic_wme.wme_chanParams.cap_wmeParams[ac]; - wme.ac[ac].aifsn = wmep->wmep_aifsn; - wme.ac[ac].cwmin = WPI_EXP2(wmep->wmep_logcwmin); - wme.ac[ac].cwmax = WPI_EXP2(wmep->wmep_logcwmax); - wme.ac[ac].txop = WPI_USEC(wmep->wmep_txopLimit); + return mtx_sleep(cmd, &sc->sc_mtx, PCATCH, "wpicmd", hz); - DPRINTF(("setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " - "txop=%d\n", ac, wme.ac[ac].aifsn, wme.ac[ac].cwmin, - wme.ac[ac].cwmax, wme.ac[ac].txop)); - } - return wpi_cmd(sc, WPI_CMD_SET_WME, &wme, sizeof wme, 1); -#undef WPI_USEC -#undef WPI_EXP2 +fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + WPI_TXQ_UNLOCK(sc); + + return error; } /* - * Configure h/w multi-rate retries. + * Configure HW multi-rate retries. */ static int wpi_mrr_setup(struct wpi_softc *sc) @@ -2267,360 +3302,858 @@ wpi_mrr_setup(struct wpi_softc *sc) struct wpi_mrr_setup mrr; int i, error; - memset(&mrr, 0, sizeof (struct wpi_mrr_setup)); - - /* CCK rates (not used with 802.11a) */ - for (i = WPI_CCK1; i <= WPI_CCK11; i++) { + /* CCK rates (not used with 802.11a). */ + for (i = WPI_RIDX_CCK1; i <= WPI_RIDX_CCK11; i++) { mrr.rates[i].flags = 0; - mrr.rates[i].signal = wpi_ridx_to_plcp[i]; - /* fallback to the immediate lower CCK rate (if any) */ - mrr.rates[i].next = (i == WPI_CCK1) ? WPI_CCK1 : i - 1; - /* try one time at this rate before falling back to "next" */ - mrr.rates[i].ntries = 1; - } - - /* OFDM rates (not used with 802.11b) */ - for (i = WPI_OFDM6; i <= WPI_OFDM54; i++) { + mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; + /* Fallback to the immediate lower CCK rate (if any.) */ + mrr.rates[i].next = + (i == WPI_RIDX_CCK1) ? WPI_RIDX_CCK1 : i - 1; + /* Try twice at this rate before falling back to "next". */ + mrr.rates[i].ntries = WPI_NTRIES_DEFAULT; + } + /* OFDM rates (not used with 802.11b). */ + for (i = WPI_RIDX_OFDM6; i <= WPI_RIDX_OFDM54; i++) { mrr.rates[i].flags = 0; - mrr.rates[i].signal = wpi_ridx_to_plcp[i]; - /* fallback to the immediate lower OFDM rate (if any) */ - /* we allow fallback from OFDM/6 to CCK/2 in 11b/g mode */ - mrr.rates[i].next = (i == WPI_OFDM6) ? + mrr.rates[i].plcp = wpi_ridx_to_plcp[i]; + /* Fallback to the immediate lower rate (if any.) */ + /* We allow fallback from OFDM/6 to CCK/2 in 11b/g mode. */ + mrr.rates[i].next = (i == WPI_RIDX_OFDM6) ? ((ic->ic_curmode == IEEE80211_MODE_11A) ? - WPI_OFDM6 : WPI_CCK2) : + WPI_RIDX_OFDM6 : WPI_RIDX_CCK2) : i - 1; - /* try one time at this rate before falling back to "next" */ - mrr.rates[i].ntries = 1; + /* Try twice at this rate before falling back to "next". */ + mrr.rates[i].ntries = WPI_NTRIES_DEFAULT; } - - /* setup MRR for control frames */ - mrr.which = WPI_MRR_CTL; + /* Setup MRR for control frames. */ + mrr.which = htole32(WPI_MRR_CTL); error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR for control frames\n"); return error; } - - /* setup MRR for data frames */ - mrr.which = WPI_MRR_DATA; + /* Setup MRR for data frames. */ + mrr.which = htole32(WPI_MRR_DATA); error = wpi_cmd(sc, WPI_CMD_MRR_SETUP, &mrr, sizeof mrr, 0); if (error != 0) { device_printf(sc->sc_dev, "could not setup MRR for data frames\n"); return error; } + return 0; +} + +static int +wpi_add_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct ieee80211com *ic = ni->ni_ic; + struct wpi_vap *wvp = WPI_VAP(ni->ni_vap); + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_node_info node; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wn->id == WPI_ID_UNDEFINED) + return EINVAL; + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); + node.id = wn->id; + node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + node.action = htole32(WPI_ACTION_SET_RATE); + node.antenna = WPI_ANTENNA_BOTH; + + DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding node %d (%s)\n", __func__, + wn->id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: wpi_cmd() call failed with error code %d\n", __func__, + error); + return error; + } + + if (wvp->wv_gtk != 0) { + error = wpi_set_global_keys(ni); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: error while setting global keys\n", __func__); + return ENXIO; + } + } return 0; } +/* + * Broadcast node is used to send group-addressed and management frames. + */ +static int +wpi_add_broadcast_node(struct wpi_softc *sc, int async) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct wpi_node_info node; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ifp->if_broadcastaddr); + node.id = WPI_ID_BROADCAST; + node.plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + node.action = htole32(WPI_ACTION_SET_RATE); + node.antenna = WPI_ANTENNA_BOTH; + + DPRINTF(sc, WPI_DEBUG_NODE, "%s: adding broadcast node\n", __func__); + + return wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, async); +} + +static int +wpi_add_sta_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = WPI_NODE(ni); + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + wn->id = wpi_add_node_entry_sta(sc); + + if ((error = wpi_add_node(sc, ni)) != 0) { + wpi_del_node_entry(sc, wn->id); + wn->id = WPI_ID_UNDEFINED; + return error; + } + + return 0; +} + +static int +wpi_add_ibss_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = WPI_NODE(ni); + int error; + + KASSERT(wn->id == WPI_ID_UNDEFINED, + ("the node %d was added before", wn->id)); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if ((wn->id = wpi_add_node_entry_adhoc(sc)) == WPI_ID_UNDEFINED) { + device_printf(sc->sc_dev, "%s: h/w table is full\n", __func__); + return ENOMEM; + } + + if ((error = wpi_add_node(sc, ni)) != 0) { + wpi_del_node_entry(sc, wn->id); + wn->id = WPI_ID_UNDEFINED; + return error; + } + + return 0; +} + +static void +wpi_del_node(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_cmd_del_node node; + int error; + + KASSERT(wn->id != WPI_ID_UNDEFINED, ("undefined node id passed")); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&node, 0, sizeof node); + IEEE80211_ADDR_COPY(node.macaddr, ni->ni_macaddr); + node.count = 1; + + DPRINTF(sc, WPI_DEBUG_NODE, "%s: deleting node %d (%s)\n", __func__, + wn->id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_DEL_NODE, &node, sizeof node, 1); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not delete node %u, error %d\n", __func__, + wn->id, error); + } +} + +static int +wpi_updateedca(struct ieee80211com *ic) +{ +#define WPI_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_edca_params cmd; + int aci, error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + memset(&cmd, 0, sizeof cmd); + cmd.flags = htole32(WPI_EDCA_UPDATE); + for (aci = 0; aci < WME_NUM_AC; aci++) { + const struct wmeParams *ac = + &ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; + cmd.ac[aci].aifsn = ac->wmep_aifsn; + cmd.ac[aci].cwmin = htole16(WPI_EXP2(ac->wmep_logcwmin)); + cmd.ac[aci].cwmax = htole16(WPI_EXP2(ac->wmep_logcwmax)); + cmd.ac[aci].txoplimit = + htole16(IEEE80211_TXOP_TO_US(ac->wmep_txopLimit)); + + DPRINTF(sc, WPI_DEBUG_EDCA, + "setting WME for queue %d aifsn=%d cwmin=%d cwmax=%d " + "txoplimit=%d\n", aci, cmd.ac[aci].aifsn, + cmd.ac[aci].cwmin, cmd.ac[aci].cwmax, + cmd.ac[aci].txoplimit); + } + error = wpi_cmd(sc, WPI_CMD_EDCA_PARAMS, &cmd, sizeof cmd, 1); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return error; +#undef WPI_EXP2 +} + +static void +wpi_set_promisc(struct wpi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + uint32_t promisc_filter; + + promisc_filter = WPI_FILTER_CTL; + if (vap != NULL && vap->iv_opmode != IEEE80211_M_HOSTAP) + promisc_filter |= WPI_FILTER_PROMISC; + + if (ifp->if_flags & IFF_PROMISC) + sc->rxon.filter |= htole32(promisc_filter); + else + sc->rxon.filter &= ~htole32(promisc_filter); +} + +static void +wpi_update_promisc(struct ifnet *ifp) +{ + struct wpi_softc *sc = ifp->if_softc; + + WPI_RXON_LOCK(sc); + wpi_set_promisc(sc); + + if (wpi_send_rxon(sc, 1, 1) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + } + WPI_RXON_UNLOCK(sc); +} + +static void +wpi_update_mcast(struct ifnet *ifp) +{ + /* Ignore */ +} + static void wpi_set_led(struct wpi_softc *sc, uint8_t which, uint8_t off, uint8_t on) { struct wpi_cmd_led led; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + led.which = which; led.unit = htole32(100000); /* on/off in unit of 100ms */ led.off = off; led.on = on; - (void)wpi_cmd(sc, WPI_CMD_SET_LED, &led, sizeof led, 1); } -static void -wpi_enable_tsf(struct wpi_softc *sc, struct ieee80211_node *ni) +static int +wpi_set_timing(struct wpi_softc *sc, struct ieee80211_node *ni) { - struct wpi_cmd_tsf tsf; + struct wpi_cmd_timing cmd; uint64_t val, mod; - memset(&tsf, 0, sizeof tsf); - memcpy(&tsf.tstamp, ni->ni_tstamp.data, 8); - tsf.bintval = htole16(ni->ni_intval); - tsf.lintval = htole16(10); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + memset(&cmd, 0, sizeof cmd); + memcpy(&cmd.tstamp, ni->ni_tstamp.data, sizeof (uint64_t)); + cmd.bintval = htole16(ni->ni_intval); + cmd.lintval = htole16(10); + + /* Compute remaining time until next beacon. */ + val = (uint64_t)ni->ni_intval * IEEE80211_DUR_TU; + mod = le64toh(cmd.tstamp) % val; + cmd.binitval = htole32((uint32_t)(val - mod)); - /* compute remaining time until next beacon */ - val = (uint64_t)ni->ni_intval * 1024; /* msec -> usec */ - mod = le64toh(tsf.tstamp) % val; - tsf.binitval = htole32((uint32_t)(val - mod)); + DPRINTF(sc, WPI_DEBUG_RESET, "timing bintval=%u tstamp=%ju, init=%u\n", + ni->ni_intval, le64toh(cmd.tstamp), (uint32_t)(val - mod)); - if (wpi_cmd(sc, WPI_CMD_TSF, &tsf, sizeof tsf, 1) != 0) - device_printf(sc->sc_dev, "could not enable TSF\n"); + return wpi_cmd(sc, WPI_CMD_TIMING, &cmd, sizeof cmd, 1); } -#if 0 /* - * Build a beacon frame that the firmware will broadcast periodically in - * IBSS or HostAP modes. + * This function is called periodically (every 60 seconds) to adjust output + * power to temperature changes. + */ +static void +wpi_power_calibration(struct wpi_softc *sc) +{ + int temp; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* Update sensor data. */ + temp = (int)WPI_READ(sc, WPI_UCODE_GP2); + DPRINTF(sc, WPI_DEBUG_TEMP, "Temp in calibration is: %d\n", temp); + + /* Sanity-check read value. */ + if (temp < -260 || temp > 25) { + /* This can't be correct, ignore. */ + DPRINTF(sc, WPI_DEBUG_TEMP, + "out-of-range temperature reported: %d\n", temp); + return; + } + + DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d->%d\n", sc->temp, temp); + + /* Adjust Tx power if need be. */ + if (abs(temp - sc->temp) <= 6) + return; + + sc->temp = temp; + + if (wpi_set_txpower(sc, 1) != 0) { + /* just warn, too bad for the automatic calibration... */ + device_printf(sc->sc_dev,"could not adjust Tx power\n"); + } +} + +/* + * Set TX power for current channel. */ static int -wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) +wpi_set_txpower(struct wpi_softc *sc, int async) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_tx_ring *ring = &sc->cmdq; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; - struct wpi_cmd_beacon *bcn; - struct ieee80211_beacon_offsets bo; - struct mbuf *m0; - bus_addr_t physaddr; - int error; + struct wpi_power_group *group; + struct wpi_cmd_txpower cmd; + uint8_t chan; + int idx, is_chan_5ghz, i; - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + /* Retrieve current channel from last RXON. */ + chan = sc->rxon.chan; + is_chan_5ghz = (sc->rxon.flags & htole32(WPI_RXON_24GHZ)) == 0; - m0 = ieee80211_beacon_alloc(ic, ni, &bo); - if (m0 == NULL) { - device_printf(sc->sc_dev, "could not allocate beacon frame\n"); - return ENOMEM; + /* Find the TX power group to which this channel belongs. */ + if (is_chan_5ghz) { + for (group = &sc->groups[1]; group < &sc->groups[4]; group++) + if (chan <= group->chan) + break; + } else + group = &sc->groups[0]; + + memset(&cmd, 0, sizeof cmd); + cmd.band = is_chan_5ghz ? WPI_BAND_5GHZ : WPI_BAND_2GHZ; + cmd.chan = htole16(chan); + + /* Set TX power for all OFDM and CCK rates. */ + for (i = 0; i <= WPI_RIDX_MAX ; i++) { + /* Retrieve TX power for this channel/rate. */ + idx = wpi_get_power_index(sc, group, chan, is_chan_5ghz, i); + + cmd.rates[i].plcp = wpi_ridx_to_plcp[i]; + + if (is_chan_5ghz) { + cmd.rates[i].rf_gain = wpi_rf_gain_5ghz[idx]; + cmd.rates[i].dsp_gain = wpi_dsp_gain_5ghz[idx]; + } else { + cmd.rates[i].rf_gain = wpi_rf_gain_2ghz[idx]; + cmd.rates[i].dsp_gain = wpi_dsp_gain_2ghz[idx]; + } + DPRINTF(sc, WPI_DEBUG_TEMP, + "chan %d/ridx %d: power index %d\n", chan, i, idx); } - cmd = &ring->cmd[ring->cur]; - cmd->code = WPI_CMD_SET_BEACON; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; + return wpi_cmd(sc, WPI_CMD_TXPOWER, &cmd, sizeof cmd, async); +} - bcn = (struct wpi_cmd_beacon *)cmd->data; - memset(bcn, 0, sizeof (struct wpi_cmd_beacon)); - bcn->id = WPI_ID_BROADCAST; - bcn->ofdm_mask = 0xff; - bcn->cck_mask = 0x0f; - bcn->lifetime = htole32(WPI_LIFETIME_INFINITE); - bcn->len = htole16(m0->m_pkthdr.len); - bcn->rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_plcp_signal(12) : wpi_plcp_signal(2); - bcn->flags = htole32(WPI_TX_AUTO_SEQ | WPI_TX_INSERT_TSTAMP); - - /* save and trim IEEE802.11 header */ - m_copydata(m0, 0, sizeof (struct ieee80211_frame), (caddr_t)&bcn->wh); - m_adj(m0, sizeof (struct ieee80211_frame)); - - /* assume beacon frame is contiguous */ - error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m0, void *), - m0->m_pkthdr.len, wpi_dma_map_addr, &physaddr, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not map beacon\n"); - m_freem(m0); - return error; +/* + * Determine Tx power index for a given channel/rate combination. + * This takes into account the regulatory information from EEPROM and the + * current temperature. + */ +static int +wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, + uint8_t chan, int is_chan_5ghz, int ridx) +{ +/* Fixed-point arithmetic division using a n-bit fractional part. */ +#define fdivround(a, b, n) \ + ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) + +/* Linear interpolation. */ +#define interpolate(x, x1, y1, x2, y2, n) \ + ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) + + struct wpi_power_sample *sample; + int pwr, idx; + + /* Default TX power is group maximum TX power minus 3dB. */ + pwr = group->maxpwr / 2; + + /* Decrease TX power for highest OFDM rates to reduce distortion. */ + switch (ridx) { + case WPI_RIDX_OFDM36: + pwr -= is_chan_5ghz ? 5 : 0; + break; + case WPI_RIDX_OFDM48: + pwr -= is_chan_5ghz ? 10 : 7; + break; + case WPI_RIDX_OFDM54: + pwr -= is_chan_5ghz ? 12 : 9; + break; } - data->m = m0; + /* Never exceed the channel maximum allowed TX power. */ + pwr = min(pwr, sc->maxpwr[chan]); - /* first scatter/gather segment is used by the beacon command */ - desc->flags = htole32(WPI_PAD32(m0->m_pkthdr.len) << 28 | 2 << 24); - desc->segs[0].addr = htole32(ring->cmd_dma.paddr + - ring->cur * sizeof (struct wpi_tx_cmd)); - desc->segs[0].len = htole32(4 + sizeof (struct wpi_cmd_beacon)); - desc->segs[1].addr = htole32(physaddr); - desc->segs[1].len = htole32(m0->m_pkthdr.len); + /* Retrieve TX power index into gain tables from samples. */ + for (sample = group->samples; sample < &group->samples[3]; sample++) + if (pwr > sample[1].power) + break; + /* Fixed-point linear interpolation using a 19-bit fractional part. */ + idx = interpolate(pwr, sample[0].power, sample[0].index, + sample[1].power, sample[1].index, 19); - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + /*- + * Adjust power index based on current temperature: + * - if cooler than factory-calibrated: decrease output power + * - if warmer than factory-calibrated: increase output power + */ + idx -= (sc->temp - group->temp) * 11 / 100; - return 0; + /* Decrease TX power for CCK rates (-5dB). */ + if (ridx >= WPI_RIDX_CCK1) + idx += 10; + + /* Make sure idx stays in a valid range. */ + if (idx < 0) + return 0; + if (idx > WPI_MAX_PWR_INDEX) + return WPI_MAX_PWR_INDEX; + return idx; + +#undef interpolate +#undef fdivround } -#endif +/* + * Set STA mode power saving level (between 0 and 5). + * Level 0 is CAM (Continuously Aware Mode), 5 is for maximum power saving. + */ static int -wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) +wpi_set_pslevel(struct wpi_softc *sc, uint8_t dtim, int level, int async) +{ + struct wpi_pmgt_cmd cmd; + const struct wpi_pmgt *pmgt; + uint32_t max, skip_dtim; + uint32_t reg; + int i; + + DPRINTF(sc, WPI_DEBUG_PWRSAVE, + "%s: dtim=%d, level=%d, async=%d\n", + __func__, dtim, level, async); + + /* Select which PS parameters to use. */ + if (dtim <= 10) + pmgt = &wpi_pmgt[0][level]; + else + pmgt = &wpi_pmgt[1][level]; + + memset(&cmd, 0, sizeof cmd); + WPI_TXQ_LOCK(sc); + if (level != 0) { /* not CAM */ + cmd.flags |= htole16(WPI_PS_ALLOW_SLEEP); + sc->sc_flags |= WPI_PS_PATH; + } else + sc->sc_flags &= ~WPI_PS_PATH; + WPI_TXQ_UNLOCK(sc); + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + if (!(reg & 0x1)) /* L0s Entry disabled. */ + cmd.flags |= htole16(WPI_PS_PCI_PMGT); + + cmd.rxtimeout = htole32(pmgt->rxtimeout * IEEE80211_DUR_TU); + cmd.txtimeout = htole32(pmgt->txtimeout * IEEE80211_DUR_TU); + + if (dtim == 0) { + dtim = 1; + skip_dtim = 0; + } else + skip_dtim = pmgt->skip_dtim; + + if (skip_dtim != 0) { + cmd.flags |= htole16(WPI_PS_SLEEP_OVER_DTIM); + max = pmgt->intval[4]; + if (max == (uint32_t)-1) + max = dtim * (skip_dtim + 1); + else if (max > dtim) + max = (max / dtim) * dtim; + } else + max = dtim; + + for (i = 0; i < 5; i++) + cmd.intval[i] = htole32(MIN(max, pmgt->intval[i])); + + return wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &cmd, sizeof cmd, async); +} + +static int +wpi_send_btcoex(struct wpi_softc *sc) +{ + struct wpi_bluetooth cmd; + + memset(&cmd, 0, sizeof cmd); + cmd.flags = WPI_BT_COEX_MODE_4WIRE; + cmd.lead_time = WPI_BT_LEAD_TIME_DEF; + cmd.max_kill = WPI_BT_MAX_KILL_DEF; + DPRINTF(sc, WPI_DEBUG_RESET, "%s: configuring bluetooth coexistence\n", + __func__); + return wpi_cmd(sc, WPI_CMD_BT_COEX, &cmd, sizeof(cmd), 0); +} + +static int +wpi_send_rxon(struct wpi_softc *sc, int assoc, int async) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; - struct wpi_node_info node; int error; + if (async) + WPI_RXON_LOCK_ASSERT(sc); - /* update adapter's configuration */ - sc->config.associd = 0; - sc->config.filter &= ~htole32(WPI_FILTER_BSS); - IEEE80211_ADDR_COPY(sc->config.bssid, ni->ni_bssid); - sc->config.chan = ieee80211_chan2ieee(ic, ni->ni_chan); - if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { - sc->config.flags |= htole32(WPI_CONFIG_AUTO | - WPI_CONFIG_24GHZ); - } else { - sc->config.flags &= ~htole32(WPI_CONFIG_AUTO | - WPI_CONFIG_24GHZ); - } - if (IEEE80211_IS_CHAN_A(ni->ni_chan)) { - sc->config.cck_mask = 0; - sc->config.ofdm_mask = 0x15; - } else if (IEEE80211_IS_CHAN_B(ni->ni_chan)) { - sc->config.cck_mask = 0x03; - sc->config.ofdm_mask = 0; + if (assoc && wpi_check_bss_filter(sc) != 0) { + struct wpi_assoc rxon_assoc; + + rxon_assoc.flags = sc->rxon.flags; + rxon_assoc.filter = sc->rxon.filter; + rxon_assoc.ofdm_mask = sc->rxon.ofdm_mask; + rxon_assoc.cck_mask = sc->rxon.cck_mask; + rxon_assoc.reserved = 0; + + error = wpi_cmd(sc, WPI_CMD_RXON_ASSOC, &rxon_assoc, + sizeof (struct wpi_assoc), async); + if (error != 0) { + device_printf(sc->sc_dev, + "RXON_ASSOC command failed, error %d\n", error); + return error; + } } else { - /* XXX assume 802.11b/g */ - sc->config.cck_mask = 0x0f; - sc->config.ofdm_mask = 0x15; - } + if (async) { + WPI_NT_LOCK(sc); + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, + sizeof (struct wpi_rxon), async); + if (error == 0) + wpi_clear_node_table(sc); + WPI_NT_UNLOCK(sc); + } else { + error = wpi_cmd(sc, WPI_CMD_RXON, &sc->rxon, + sizeof (struct wpi_rxon), async); + if (error == 0) + wpi_clear_node_table(sc); + } - DPRINTF(("config chan %d flags %x cck %x ofdm %x\n", sc->config.chan, - sc->config.flags, sc->config.cck_mask, sc->config.ofdm_mask)); - error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, - sizeof (struct wpi_config), 1); - if (error != 0) { - device_printf(sc->sc_dev, "could not configure\n"); - return error; + if (error != 0) { + device_printf(sc->sc_dev, + "RXON command failed, error %d\n", error); + return error; + } + + /* Add broadcast node. */ + error = wpi_add_broadcast_node(sc, async); + if (error != 0) { + device_printf(sc->sc_dev, + "could not add broadcast node, error %d\n", error); + return error; + } } - /* configuration has changed, set Tx power accordingly */ - if ((error = wpi_set_txpower(sc, ni->ni_chan, 1)) != 0) { - device_printf(sc->sc_dev, "could not set Tx power\n"); + /* Configuration has changed, set Tx power accordingly. */ + if ((error = wpi_set_txpower(sc, async)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set TX power, error %d\n", __func__, error); return error; } - /* add default node */ - memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.bssid, ni->ni_bssid); - node.id = WPI_ID_BSS; - node.rate = (ic->ic_curmode == IEEE80211_MODE_11A) ? - wpi_plcp_signal(12) : wpi_plcp_signal(2); - node.action = htole32(WPI_ACTION_SET_RATE); - node.antenna = WPI_ANTENNA_BOTH; - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); - if (error != 0) - device_printf(sc->sc_dev, "could not add BSS node\n"); - - return (error); + return 0; } +/** + * Configure the card to listen to a particular channel, this transisions the + * card in to being able to receive frames from remote devices. + */ static int -wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) +wpi_config(struct wpi_softc *sc) { - struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_node *ni = vap->iv_bss; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_channel *c = ic->ic_curchan; int error; - if (vap->iv_opmode == IEEE80211_M_MONITOR) { - /* link LED blinks while monitoring */ - wpi_set_led(sc, WPI_LED_LINK, 5, 5); - return 0; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Set power saving level to CAM during initialization. */ + if ((error = wpi_set_pslevel(sc, 0, 0, 0)) != 0) { + device_printf(sc->sc_dev, + "%s: could not set power saving level\n", __func__); + return error; } - wpi_enable_tsf(sc, ni); + /* Configure bluetooth coexistence. */ + if ((error = wpi_send_btcoex(sc)) != 0) { + device_printf(sc->sc_dev, + "could not configure bluetooth coexistence\n"); + return error; + } - /* update adapter's configuration */ - sc->config.associd = htole16(ni->ni_associd & ~0xc000); - /* short preamble/slot time are negotiated when associating */ - sc->config.flags &= ~htole32(WPI_CONFIG_SHPREAMBLE | - WPI_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHSLOT) - sc->config.flags |= htole32(WPI_CONFIG_SHSLOT); - if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) - sc->config.flags |= htole32(WPI_CONFIG_SHPREAMBLE); - sc->config.filter |= htole32(WPI_FILTER_BSS); + /* Configure adapter. */ + memset(&sc->rxon, 0, sizeof (struct wpi_rxon)); + IEEE80211_ADDR_COPY(sc->rxon.myaddr, vap->iv_myaddr); - /* XXX put somewhere HC_QOS_SUPPORT_ASSOC + HC_IBSS_START */ + /* Set default channel. */ + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(c)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); - DPRINTF(("config chan %d flags %x\n", sc->config.chan, - sc->config.flags)); - error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, sizeof (struct - wpi_config), 1); - if (error != 0) { - device_printf(sc->sc_dev, "could not update configuration\n"); + sc->rxon.filter = WPI_FILTER_MULTICAST; + switch (ic->ic_opmode) { + case IEEE80211_M_STA: + sc->rxon.mode = WPI_MODE_STA; + break; + case IEEE80211_M_IBSS: + sc->rxon.mode = WPI_MODE_IBSS; + sc->rxon.filter |= WPI_FILTER_BEACON; + break; + case IEEE80211_M_HOSTAP: + /* XXX workaround for beaconing */ + sc->rxon.mode = WPI_MODE_IBSS; + sc->rxon.filter |= WPI_FILTER_ASSOC | WPI_FILTER_PROMISC; + break; + case IEEE80211_M_AHDEMO: + sc->rxon.mode = WPI_MODE_HOSTAP; + break; + case IEEE80211_M_MONITOR: + sc->rxon.mode = WPI_MODE_MONITOR; + break; + default: + device_printf(sc->sc_dev, "unknown opmode %d\n", + ic->ic_opmode); + return EINVAL; + } + sc->rxon.filter = htole32(sc->rxon.filter); + wpi_set_promisc(sc); + sc->rxon.cck_mask = 0x0f; /* not yet negotiated */ + sc->rxon.ofdm_mask = 0xff; /* not yet negotiated */ + + /* XXX Current configuration may be unusable. */ + if (IEEE80211_IS_CHAN_NOADHOC(c) && sc->rxon.mode == WPI_MODE_IBSS) { + device_printf(sc->sc_dev, + "%s: invalid channel (%d) selected for IBSS mode\n", + __func__, ieee80211_chan2ieee(ic, c)); + return EINVAL; + } + + if ((error = wpi_send_rxon(sc, 0, 0)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); return error; } - error = wpi_set_txpower(sc, ni->ni_chan, 1); - if (error != 0) { - device_printf(sc->sc_dev, "could set txpower\n"); + /* Setup rate scalling. */ + if ((error = wpi_mrr_setup(sc)) != 0) { + device_printf(sc->sc_dev, "could not setup MRR, error %d\n", + error); return error; } - /* link LED always on while associated */ - wpi_set_led(sc, WPI_LED_LINK, 0, 1); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - /* start automatic rate control timer */ - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); + return 0; +} + +static uint16_t +wpi_get_active_dwell_time(struct wpi_softc *sc, + struct ieee80211_channel *c, uint8_t n_probes) +{ + /* No channel? Default to 2GHz settings. */ + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) { + return (WPI_ACTIVE_DWELL_TIME_2GHZ + + WPI_ACTIVE_DWELL_FACTOR_2GHZ * (n_probes + 1)); + } - return (error); + /* 5GHz dwell time. */ + return (WPI_ACTIVE_DWELL_TIME_5GHZ + + WPI_ACTIVE_DWELL_FACTOR_5GHZ * (n_probes + 1)); } /* - * Send a scan request to the firmware. Since this command is huge, we map it - * into a mbufcluster instead of using the pre-allocated set of commands. Note, - * much of this code is similar to that in wpi_cmd but because we must manually - * construct the probe & channels, we duplicate what's needed here. XXX In the - * future, this function should be modified to use wpi_cmd to help cleanup the - * code base. + * Limit the total dwell time. + * + * Returns the dwell time in milliseconds. + */ +static uint16_t +wpi_limit_dwell(struct wpi_softc *sc, uint16_t dwell_time) +{ + struct ieee80211com *ic = sc->sc_ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + int bintval = 0; + + /* bintval is in TU (1.024mS) */ + if (vap != NULL) + bintval = vap->iv_bss->ni_intval; + + /* + * If it's non-zero, we should calculate the minimum of + * it and the DWELL_BASE. + * + * XXX Yes, the math should take into account that bintval + * is 1.024mS, not 1mS.. + */ + if (bintval > 0) { + DPRINTF(sc, WPI_DEBUG_SCAN, "%s: bintval=%d\n", __func__, + bintval); + return (MIN(dwell_time, bintval - WPI_CHANNEL_TUNE_TIME * 2)); + } + + /* No association context? Default. */ + return dwell_time; +} + +static uint16_t +wpi_get_passive_dwell_time(struct wpi_softc *sc, struct ieee80211_channel *c) +{ + uint16_t passive; + + if (c == NULL || IEEE80211_IS_CHAN_2GHZ(c)) + passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_2GHZ; + else + passive = WPI_PASSIVE_DWELL_BASE + WPI_PASSIVE_DWELL_TIME_5GHZ; + + /* Clamp to the beacon interval if we're associated. */ + return (wpi_limit_dwell(sc, passive)); +} + +static uint32_t +wpi_get_scan_pause_time(uint32_t time, uint16_t bintval) +{ + uint32_t mod = (time % bintval) * IEEE80211_DUR_TU; + uint32_t nbeacons = time / bintval; + + if (mod > WPI_PAUSE_MAX_TIME) + mod = WPI_PAUSE_MAX_TIME; + + return WPI_PAUSE_SCAN(nbeacons, mod); +} + +/* + * Send a scan request to the firmware. */ static int -wpi_scan(struct wpi_softc *sc) +wpi_scan(struct wpi_softc *sc, struct ieee80211_channel *c) { struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; struct ieee80211_scan_state *ss = ic->ic_scan; - struct wpi_tx_ring *ring = &sc->cmdq; - struct wpi_tx_desc *desc; - struct wpi_tx_data *data; - struct wpi_tx_cmd *cmd; + struct ieee80211vap *vap = ss->ss_vap; struct wpi_scan_hdr *hdr; + struct wpi_cmd_data *tx; + struct wpi_scan_essid *essids; struct wpi_scan_chan *chan; struct ieee80211_frame *wh; struct ieee80211_rateset *rs; - struct ieee80211_channel *c; - enum ieee80211_phymode mode; - uint8_t *frm; - int nrates, pktlen, error, i, nssid; - bus_addr_t physaddr; + uint16_t dwell_active, dwell_passive; + uint8_t *buf, *frm; + int bgscan, bintval, buflen, error, i, nssid; - desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - data->m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); - if (data->m == NULL) { - device_printf(sc->sc_dev, - "could not allocate mbuf for scan command\n"); - return ENOMEM; + /* + * We are absolutely not allowed to send a scan command when another + * scan command is pending. + */ + if (callout_pending(&sc->scan_timeout)) { + device_printf(sc->sc_dev, "%s: called whilst scanning!\n", + __func__); + error = EAGAIN; + goto fail; } - cmd = mtod(data->m, struct wpi_tx_cmd *); - cmd->code = WPI_CMD_SCAN; - cmd->flags = 0; - cmd->qid = ring->qid; - cmd->idx = ring->cur; + bgscan = wpi_check_bss_filter(sc); + bintval = vap->iv_bss->ni_intval; + if (bgscan != 0 && + bintval < WPI_QUIET_TIME_DEFAULT + WPI_CHANNEL_TUNE_TIME * 2) { + error = EOPNOTSUPP; + goto fail; + } - hdr = (struct wpi_scan_hdr *)cmd->data; - memset(hdr, 0, sizeof(struct wpi_scan_hdr)); + buf = malloc(WPI_SCAN_MAXSZ, M_DEVBUF, M_NOWAIT | M_ZERO); + if (buf == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate buffer for scan command\n", + __func__); + error = ENOMEM; + goto fail; + } + hdr = (struct wpi_scan_hdr *)buf; /* - * Move to the next channel if no packets are received within 5 msecs - * after sending the probe request (this helps to reduce the duration - * of active scans). + * Move to the next channel if no packets are received within 10 msecs + * after sending the probe request. */ - hdr->quiet = htole16(5); - hdr->threshold = htole16(1); + hdr->quiet_time = htole16(WPI_QUIET_TIME_DEFAULT); + hdr->quiet_threshold = htole16(1); - if (IEEE80211_IS_CHAN_A(ic->ic_curchan)) { - /* send probe requests at 6Mbps */ - hdr->tx.rate = wpi_ridx_to_plcp[WPI_OFDM6]; + if (bgscan != 0) { + /* + * Max needs to be greater than active and passive and quiet! + * It's also in microseconds! + */ + hdr->max_svc = htole32(250 * IEEE80211_DUR_TU); + hdr->pause_svc = htole32(wpi_get_scan_pause_time(100, + bintval)); + } + + hdr->filter = htole32(WPI_FILTER_MULTICAST | WPI_FILTER_BEACON); + + tx = (struct wpi_cmd_data *)(hdr + 1); + tx->flags = htole32(WPI_TX_AUTO_SEQ); + tx->id = WPI_ID_BROADCAST; + tx->lifetime = htole32(WPI_LIFETIME_INFINITE); - /* Enable crc checking */ - hdr->promotion = htole16(1); + if (IEEE80211_IS_CHAN_5GHZ(c)) { + /* Send probe requests at 6Mbps. */ + tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_OFDM6]; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; } else { - hdr->flags = htole32(WPI_CONFIG_24GHZ | WPI_CONFIG_AUTO); - /* send probe requests at 1Mbps */ - hdr->tx.rate = wpi_ridx_to_plcp[WPI_CCK1]; + hdr->flags = htole32(WPI_RXON_24GHZ | WPI_RXON_AUTO); + /* Send probe requests at 1Mbps. */ + tx->plcp = wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; } - hdr->tx.id = WPI_ID_BROADCAST; - hdr->tx.lifetime = htole32(WPI_LIFETIME_INFINITE); - hdr->tx.flags = htole32(WPI_TX_AUTO_SEQ); - memset(hdr->scan_essids, 0, sizeof(hdr->scan_essids)); + essids = (struct wpi_scan_essid *)(tx + 1); nssid = MIN(ss->ss_nssid, WPI_SCAN_MAX_ESSIDS); for (i = 0; i < nssid; i++) { - hdr->scan_essids[i].id = IEEE80211_ELEMID_SSID; - hdr->scan_essids[i].esslen = MIN(ss->ss_ssid[i].len, 32); - memcpy(hdr->scan_essids[i].essid, ss->ss_ssid[i].ssid, - hdr->scan_essids[i].esslen); + essids[i].id = IEEE80211_ELEMID_SSID; + essids[i].len = MIN(ss->ss_ssid[i].len, IEEE80211_NWID_LEN); + memcpy(essids[i].data, ss->ss_ssid[i].ssid, essids[i].len); #ifdef WPI_DEBUG - if (wpi_debug & WPI_DEBUG_SCANNING) { + if (sc->sc_debug & WPI_DEBUG_SCAN) { printf("Scanning Essid: "); - ieee80211_print_essid(hdr->scan_essids[i].essid, - hdr->scan_essids[i].esslen); + ieee80211_print_essid(essids[i].data, essids[i].len); printf("\n"); } #endif @@ -2630,916 +4163,1400 @@ wpi_scan(struct wpi_softc *sc) * Build a probe request frame. Most of the following code is a * copy & paste of what is done in net80211. */ - wh = (struct ieee80211_frame *)&hdr->scan_essids[4]; + wh = (struct ieee80211_frame *)(essids + WPI_SCAN_MAX_ESSIDS); wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; IEEE80211_ADDR_COPY(wh->i_addr1, ifp->if_broadcastaddr); - IEEE80211_ADDR_COPY(wh->i_addr2, IF_LLADDR(ifp)); + IEEE80211_ADDR_COPY(wh->i_addr2, vap->iv_myaddr); IEEE80211_ADDR_COPY(wh->i_addr3, ifp->if_broadcastaddr); - *(u_int16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ - *(u_int16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ + *(uint16_t *)&wh->i_dur[0] = 0; /* filled by h/w */ + *(uint16_t *)&wh->i_seq[0] = 0; /* filled by h/w */ frm = (uint8_t *)(wh + 1); + frm = ieee80211_add_ssid(frm, NULL, 0); + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); - /* add essid IE, the hardware will fill this in for us */ - *frm++ = IEEE80211_ELEMID_SSID; - *frm++ = 0; - - mode = ieee80211_chan2mode(ic->ic_curchan); - rs = &ic->ic_sup_rates[mode]; - - /* add supported rates IE */ - *frm++ = IEEE80211_ELEMID_RATES; - nrates = rs->rs_nrates; - if (nrates > IEEE80211_RATE_SIZE) - nrates = IEEE80211_RATE_SIZE; - *frm++ = nrates; - memcpy(frm, rs->rs_rates, nrates); - frm += nrates; - - /* add supported xrates IE */ - if (rs->rs_nrates > IEEE80211_RATE_SIZE) { - nrates = rs->rs_nrates - IEEE80211_RATE_SIZE; - *frm++ = IEEE80211_ELEMID_XRATES; - *frm++ = nrates; - memcpy(frm, rs->rs_rates + IEEE80211_RATE_SIZE, nrates); - frm += nrates; - } - - /* setup length of probe request */ - hdr->tx.len = htole16(frm - (uint8_t *)wh); + /* Set length of probe request. */ + tx->len = htole16(frm - (uint8_t *)wh); /* * Construct information about the channel that we * want to scan. The firmware expects this to be directly * after the scan probe request */ - c = ic->ic_curchan; chan = (struct wpi_scan_chan *)frm; - chan->chan = ieee80211_chan2ieee(ic, c); + chan->chan = htole16(ieee80211_chan2ieee(ic, c)); chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { + if (nssid) { + hdr->crc_threshold = WPI_SCAN_CRC_TH_DEFAULT; + chan->flags |= WPI_CHAN_NPBREQS(nssid); + } else + hdr->crc_threshold = WPI_SCAN_CRC_TH_NEVER; + + if (!IEEE80211_IS_CHAN_PASSIVE(c)) chan->flags |= WPI_CHAN_ACTIVE; - if (nssid != 0) - chan->flags |= WPI_CHAN_DIRECT; + + /* + * Calculate the active/passive dwell times. + */ + + dwell_active = wpi_get_active_dwell_time(sc, c, nssid); + dwell_passive = wpi_get_passive_dwell_time(sc, c); + + /* Make sure they're valid. */ + if (dwell_active > dwell_passive) + dwell_active = dwell_passive; + + chan->active = htole16(dwell_active); + chan->passive = htole16(dwell_passive); + + chan->dsp_gain = 0x6e; /* Default level */ + + if (IEEE80211_IS_CHAN_5GHZ(c)) + chan->rf_gain = 0x3b; + else + chan->rf_gain = 0x28; + + DPRINTF(sc, WPI_DEBUG_SCAN, "Scanning %u Passive: %d\n", + chan->chan, IEEE80211_IS_CHAN_PASSIVE(c)); + + hdr->nchan++; + + if (hdr->nchan == 1 && sc->rxon.chan == chan->chan) { + /* XXX Force probe request transmission. */ + memcpy(chan + 1, chan, sizeof (struct wpi_scan_chan)); + + chan++; + + /* Reduce unnecessary delay. */ + chan->flags = 0; + chan->passive = chan->active = hdr->quiet_time; + + hdr->nchan++; } - chan->gain_dsp = 0x6e; /* Default level */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - chan->active = htole16(10); - chan->passive = htole16(ss->ss_maxdwell); - chan->gain_radio = 0x3b; + + chan++; + + buflen = (uint8_t *)chan - buf; + hdr->len = htole16(buflen); + + DPRINTF(sc, WPI_DEBUG_CMD, "sending scan command nchan=%d\n", + hdr->nchan); + error = wpi_cmd(sc, WPI_CMD_SCAN, buf, buflen, 1); + free(buf, M_DEVBUF); + + if (error != 0) + goto fail; + + callout_reset(&sc->scan_timeout, 5*hz, wpi_scan_timeout, sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; + +fail: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + + return error; +} + +static int +wpi_auth(struct wpi_softc *sc, struct ieee80211vap *vap) +{ + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct ieee80211_channel *c = ni->ni_chan; + int error; + + WPI_RXON_LOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + /* Update adapter configuration. */ + sc->rxon.associd = 0; + sc->rxon.filter &= ~htole32(WPI_FILTER_BSS); + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(c)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + if (ic->ic_flags & IEEE80211_F_SHSLOT) + sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); + if (IEEE80211_IS_CHAN_A(c)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(c)) { + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; } else { - chan->active = htole16(20); - chan->passive = htole16(ss->ss_maxdwell); - chan->gain_radio = 0x28; + /* Assume 802.11b/g. */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; } - DPRINTFN(WPI_DEBUG_SCANNING, - ("Scanning %u Passive: %d\n", - chan->chan, - c->ic_flags & IEEE80211_CHAN_PASSIVE)); + DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x cck %x ofdm %x\n", + sc->rxon.chan, sc->rxon.flags, sc->rxon.cck_mask, + sc->rxon.ofdm_mask); - hdr->nchan++; - chan++; + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + } - frm += sizeof (struct wpi_scan_chan); -#if 0 - // XXX All Channels.... - for (c = &ic->ic_channels[1]; - c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; c++) { - if ((c->ic_flags & ic->ic_curchan->ic_flags) != ic->ic_curchan->ic_flags) - continue; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - chan->chan = ieee80211_chan2ieee(ic, c); - chan->flags = 0; - if (!(c->ic_flags & IEEE80211_CHAN_PASSIVE)) { - chan->flags |= WPI_CHAN_ACTIVE; - if (ic->ic_des_ssid[0].len != 0) - chan->flags |= WPI_CHAN_DIRECT; - } - chan->gain_dsp = 0x6e; /* Default level */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - chan->active = htole16(10); - chan->passive = htole16(110); - chan->gain_radio = 0x3b; - } else { - chan->active = htole16(20); - chan->passive = htole16(120); - chan->gain_radio = 0x28; - } + WPI_RXON_UNLOCK(sc); + + return error; +} + +static int +wpi_config_beacon(struct wpi_vap *wvp) +{ + struct ieee80211com *ic = wvp->wv_vap.iv_ic; + struct ieee80211_beacon_offsets *bo = &wvp->wv_boff; + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_cmd_beacon *cmd = (struct wpi_cmd_beacon *)&bcn->data; + struct ieee80211_tim_ie *tie; + struct mbuf *m; + uint8_t *ptr; + int error; - DPRINTFN(WPI_DEBUG_SCANNING, - ("Scanning %u Passive: %d\n", - chan->chan, - c->ic_flags & IEEE80211_CHAN_PASSIVE)); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - hdr->nchan++; - chan++; + WPI_VAP_LOCK_ASSERT(wvp); - frm += sizeof (struct wpi_scan_chan); + cmd->len = htole16(bcn->m->m_pkthdr.len); + cmd->plcp = (ic->ic_curmode == IEEE80211_MODE_11A) ? + wpi_ridx_to_plcp[WPI_RIDX_OFDM6] : wpi_ridx_to_plcp[WPI_RIDX_CCK1]; + + /* XXX seems to be unused */ + if (*(bo->bo_tim) == IEEE80211_ELEMID_TIM) { + tie = (struct ieee80211_tim_ie *) bo->bo_tim; + ptr = mtod(bcn->m, uint8_t *); + + cmd->tim = htole16(bo->bo_tim - ptr); + cmd->timsz = tie->tim_len; } -#endif - hdr->len = htole16(frm - (uint8_t *)hdr); - pktlen = frm - (uint8_t *)cmd; + /* Necessary for recursion in ieee80211_beacon_update(). */ + m = bcn->m; + bcn->m = m_dup(m, M_NOWAIT); + if (bcn->m == NULL) { + device_printf(sc->sc_dev, + "%s: could not copy beacon frame\n", __func__); + error = ENOMEM; + goto end; + } - error = bus_dmamap_load(ring->data_dmat, data->map, cmd, pktlen, - wpi_dma_map_addr, &physaddr, BUS_DMA_NOWAIT); - if (error != 0) { - device_printf(sc->sc_dev, "could not map scan command\n"); - m_freem(data->m); - data->m = NULL; - return error; + if ((error = wpi_cmd2(sc, bcn)) != 0) { + device_printf(sc->sc_dev, + "%s: could not update beacon frame, error %d", __func__, + error); } - desc->flags = htole32(WPI_PAD32(pktlen) << 28 | 1 << 24); - desc->segs[0].addr = htole32(physaddr); - desc->segs[0].len = htole32(pktlen); + /* Restore mbuf. */ +end: bcn->m = m; - bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, - BUS_DMASYNC_PREWRITE); - bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); + return error; +} + +static int +wpi_setup_beacon(struct wpi_softc *sc, struct ieee80211_node *ni) +{ + struct wpi_vap *wvp = WPI_VAP(ni->ni_vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct ieee80211_beacon_offsets *bo = &wvp->wv_boff; + struct mbuf *m; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (ni->ni_chan == IEEE80211_CHAN_ANYC) + return EINVAL; + + m = ieee80211_beacon_alloc(ni, bo); + if (m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + return ENOMEM; + } - /* kick cmd ring */ - ring->cur = (ring->cur + 1) % WPI_CMD_RING_COUNT; - WPI_WRITE(sc, WPI_TX_WIDX, ring->qid << 8 | ring->cur); + WPI_VAP_LOCK(wvp); + if (bcn->m != NULL) + m_freem(bcn->m); - sc->sc_scan_timer = 5; - return 0; /* will be notified async. of failure/success */ + bcn->m = m; + + error = wpi_config_beacon(wvp); + WPI_VAP_UNLOCK(wvp); + + return error; +} + +static void +wpi_update_beacon(struct ieee80211vap *vap, int item) +{ + struct wpi_softc *sc = vap->iv_ic->ic_ifp->if_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + struct wpi_buf *bcn = &wvp->wv_bcbuf; + struct ieee80211_beacon_offsets *bo = &wvp->wv_boff; + struct ieee80211_node *ni = vap->iv_bss; + int mcast = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + WPI_VAP_LOCK(wvp); + if (bcn->m == NULL) { + bcn->m = ieee80211_beacon_alloc(ni, bo); + if (bcn->m == NULL) { + device_printf(sc->sc_dev, + "%s: could not allocate beacon frame\n", __func__); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, + __func__); + + WPI_VAP_UNLOCK(wvp); + return; + } + } + WPI_VAP_UNLOCK(wvp); + + if (item == IEEE80211_BEACON_TIM) + mcast = 1; /* TODO */ + + setbit(bo->bo_flags, item); + ieee80211_beacon_update(ni, bo, bcn->m, mcast); + + WPI_VAP_LOCK(wvp); + wpi_config_beacon(wvp); + WPI_VAP_UNLOCK(wvp); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); +} + +static void +wpi_newassoc(struct ieee80211_node *ni, int isnew) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + WPI_NT_LOCK(sc); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (vap->iv_opmode != IEEE80211_M_STA && wn->id == WPI_ID_UNDEFINED) { + if ((error = wpi_add_ibss_node(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not add IBSS node, error %d\n", + __func__, error); + } + } + WPI_NT_UNLOCK(sc); } -/** - * Configure the card to listen to a particular channel, this transisions the - * card in to being able to receive frames from remote devices. - */ static int -wpi_config(struct wpi_softc *sc) +wpi_run(struct wpi_softc *sc, struct ieee80211vap *vap) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power power; - struct wpi_bluetooth bluetooth; - struct wpi_node_info node; + struct ieee80211com *ic = vap->iv_ic; + struct ieee80211_node *ni = vap->iv_bss; + struct ieee80211_channel *c = ni->ni_chan; int error; - /* set power mode */ - memset(&power, 0, sizeof power); - power.flags = htole32(WPI_POWER_CAM|0x8); - error = wpi_cmd(sc, WPI_CMD_SET_POWER_MODE, &power, sizeof power, 0); - if (error != 0) { - device_printf(sc->sc_dev, "could not set power mode\n"); - return error; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); + + if (vap->iv_opmode == IEEE80211_M_MONITOR) { + /* Link LED blinks while monitoring. */ + wpi_set_led(sc, WPI_LED_LINK, 5, 5); + return 0; } - /* configure bluetooth coexistence */ - memset(&bluetooth, 0, sizeof bluetooth); - bluetooth.flags = 3; - bluetooth.lead = 0xaa; - bluetooth.kill = 1; - error = wpi_cmd(sc, WPI_CMD_BLUETOOTH, &bluetooth, sizeof bluetooth, - 0); - if (error != 0) { + /* XXX kernel panic workaround */ + if (c == IEEE80211_CHAN_ANYC) { + device_printf(sc->sc_dev, "%s: incomplete configuration\n", + __func__); + return EINVAL; + } + + if ((error = wpi_set_timing(sc, ni)) != 0) { device_printf(sc->sc_dev, - "could not configure bluetooth coexistence\n"); + "%s: could not set timing, error %d\n", __func__, error); return error; } - /* configure adapter */ - memset(&sc->config, 0, sizeof (struct wpi_config)); - IEEE80211_ADDR_COPY(sc->config.myaddr, IF_LLADDR(ifp)); - /*set default channel*/ - sc->config.chan = htole16(ieee80211_chan2ieee(ic, ic->ic_curchan)); - sc->config.flags = htole32(WPI_CONFIG_TSF); - if (IEEE80211_IS_CHAN_2GHZ(ic->ic_curchan)) { - sc->config.flags |= htole32(WPI_CONFIG_AUTO | - WPI_CONFIG_24GHZ); + /* Update adapter configuration. */ + WPI_RXON_LOCK(sc); + IEEE80211_ADDR_COPY(sc->rxon.bssid, ni->ni_bssid); + sc->rxon.associd = htole16(IEEE80211_NODE_AID(ni)); + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + sc->rxon.flags = htole32(WPI_RXON_TSF | WPI_RXON_CTS_TO_SELF); + if (IEEE80211_IS_CHAN_2GHZ(c)) + sc->rxon.flags |= htole32(WPI_RXON_AUTO | WPI_RXON_24GHZ); + if (ic->ic_flags & IEEE80211_F_SHSLOT) + sc->rxon.flags |= htole32(WPI_RXON_SHSLOT); + if (ic->ic_flags & IEEE80211_F_SHPREAMBLE) + sc->rxon.flags |= htole32(WPI_RXON_SHPREAMBLE); + if (IEEE80211_IS_CHAN_A(c)) { + sc->rxon.cck_mask = 0; + sc->rxon.ofdm_mask = 0x15; + } else if (IEEE80211_IS_CHAN_B(c)) { + sc->rxon.cck_mask = 0x03; + sc->rxon.ofdm_mask = 0; + } else { + /* Assume 802.11b/g. */ + sc->rxon.cck_mask = 0x0f; + sc->rxon.ofdm_mask = 0x15; } - sc->config.filter = 0; - switch (ic->ic_opmode) { - case IEEE80211_M_STA: - case IEEE80211_M_WDS: /* No know setup, use STA for now */ - sc->config.mode = WPI_MODE_STA; - sc->config.filter |= htole32(WPI_FILTER_MULTICAST); - break; - case IEEE80211_M_IBSS: - case IEEE80211_M_AHDEMO: - sc->config.mode = WPI_MODE_IBSS; - sc->config.filter |= htole32(WPI_FILTER_BEACON | - WPI_FILTER_MULTICAST); - break; - case IEEE80211_M_HOSTAP: - sc->config.mode = WPI_MODE_HOSTAP; - break; - case IEEE80211_M_MONITOR: - sc->config.mode = WPI_MODE_MONITOR; - sc->config.filter |= htole32(WPI_FILTER_MULTICAST | - WPI_FILTER_CTL | WPI_FILTER_PROMISC); + sc->rxon.filter |= htole32(WPI_FILTER_BSS); + + DPRINTF(sc, WPI_DEBUG_STATE, "rxon chan %d flags %x\n", + sc->rxon.chan, sc->rxon.flags); + + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) { + device_printf(sc->sc_dev, "%s: could not send RXON\n", + __func__); + return error; + } + + /* Start periodic calibration timer. */ + callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); + + WPI_RXON_UNLOCK(sc); + + if (vap->iv_opmode == IEEE80211_M_IBSS || + vap->iv_opmode == IEEE80211_M_HOSTAP) { + if ((error = wpi_setup_beacon(sc, ni)) != 0) { + device_printf(sc->sc_dev, + "%s: could not setup beacon, error %d\n", __func__, + error); + return error; + } + } + + if (vap->iv_opmode == IEEE80211_M_STA) { + /* Add BSS node. */ + WPI_NT_LOCK(sc); + error = wpi_add_sta_node(sc, ni); + WPI_NT_UNLOCK(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not add BSS node, error %d\n", __func__, + error); + return error; + } + } + + /* Link LED always on while associated. */ + wpi_set_led(sc, WPI_LED_LINK, 0, 1); + + /* Enable power-saving mode if requested by user. */ + if ((vap->iv_flags & IEEE80211_F_PMGTON) && + vap->iv_opmode != IEEE80211_M_IBSS) + (void)wpi_set_pslevel(sc, 0, 3, 1); + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + return 0; +} + +static int +wpi_load_key(struct ieee80211_node *ni, const struct ieee80211_key *k) +{ + const struct ieee80211_cipher *cip = k->wk_cipher; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_node_info node; + uint16_t kflags; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wpi_check_node_entry(sc, wn->id) == 0) { + device_printf(sc->sc_dev, "%s: node does not exist\n", + __func__); + return 0; + } + + switch (cip->ic_cipher) { + case IEEE80211_CIPHER_AES_CCM: + kflags = WPI_KFLAG_CCMP; break; + default: - device_printf(sc->sc_dev, "unknown opmode %d\n", ic->ic_opmode); - return EINVAL; + device_printf(sc->sc_dev, "%s: unknown cipher %d\n", __func__, + cip->ic_cipher); + return 0; } - sc->config.cck_mask = 0x0f; /* not yet negotiated */ - sc->config.ofdm_mask = 0xff; /* not yet negotiated */ - error = wpi_cmd(sc, WPI_CMD_CONFIGURE, &sc->config, - sizeof (struct wpi_config), 0); + + kflags |= WPI_KFLAG_KID(k->wk_keyix); + if (k->wk_flags & IEEE80211_KEY_GROUP) + kflags |= WPI_KFLAG_MULTICAST; + + memset(&node, 0, sizeof node); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_KEY_SET; + node.kflags = htole16(kflags); + memcpy(node.key, k->wk_key, k->wk_keylen); +again: + DPRINTF(sc, WPI_DEBUG_KEY, + "%s: setting %s key id %d for node %d (%s)\n", __func__, + (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", k->wk_keyix, + node.id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { - device_printf(sc->sc_dev, "configure command failed\n"); - return error; + device_printf(sc->sc_dev, "can't update node info, error %d\n", + error); + return !error; } - /* configuration has changed, set Tx power accordingly */ - if ((error = wpi_set_txpower(sc, ic->ic_curchan, 0)) != 0) { - device_printf(sc->sc_dev, "could not set Tx power\n"); - return error; + if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + kflags |= WPI_KFLAG_MULTICAST; + node.kflags = htole16(kflags); + + goto again; } - /* add broadcast node */ + return 1; +} + +static void +wpi_load_key_cb(void *arg, struct ieee80211_node *ni) +{ + const struct ieee80211_key *k = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; + + if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) + return; + + WPI_NT_LOCK(sc); + error = wpi_load_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (error == 0) { + device_printf(sc->sc_dev, "%s: error while setting key\n", + __func__); + } +} + +static int +wpi_set_global_keys(struct ieee80211_node *ni) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct ieee80211_key *wk = &vap->iv_nw_keys[0]; + int error = 1; + + for (; wk < &vap->iv_nw_keys[IEEE80211_WEP_NKID] && error; wk++) + if (wk->wk_keyix != IEEE80211_KEYIX_NONE) + error = wpi_load_key(ni, wk); + + return !error; +} + +static int +wpi_del_key(struct ieee80211_node *ni, const struct ieee80211_key *k) +{ + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + struct wpi_node_info node; + uint16_t kflags; + int error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (wpi_check_node_entry(sc, wn->id) == 0) { + DPRINTF(sc, WPI_DEBUG_KEY, "%s: node was removed\n", __func__); + return 1; /* Nothing to do. */ + } + + kflags = WPI_KFLAG_KID(k->wk_keyix); + if (k->wk_flags & IEEE80211_KEY_GROUP) + kflags |= WPI_KFLAG_MULTICAST; + memset(&node, 0, sizeof node); - IEEE80211_ADDR_COPY(node.bssid, ifp->if_broadcastaddr); - node.id = WPI_ID_BROADCAST; - node.rate = wpi_plcp_signal(2); - error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 0); + node.id = wn->id; + node.control = WPI_NODE_UPDATE; + node.flags = WPI_FLAG_KEY_SET; + node.kflags = htole16(kflags); +again: + DPRINTF(sc, WPI_DEBUG_KEY, "%s: deleting %s key %d for node %d (%s)\n", + __func__, (kflags & WPI_KFLAG_MULTICAST) ? "group" : "ucast", + k->wk_keyix, node.id, ether_sprintf(ni->ni_macaddr)); + + error = wpi_cmd(sc, WPI_CMD_ADD_NODE, &node, sizeof node, 1); if (error != 0) { - device_printf(sc->sc_dev, "could not add broadcast node\n"); - return error; + device_printf(sc->sc_dev, "can't update node info, error %d\n", + error); + return !error; } - /* Setup rate scalling */ - error = wpi_mrr_setup(sc); - if (error != 0) { - device_printf(sc->sc_dev, "could not setup MRR\n"); - return error; + if (!(kflags & WPI_KFLAG_MULTICAST) && &vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + kflags |= WPI_KFLAG_MULTICAST; + node.kflags = htole16(kflags); + + goto again; } - return 0; + return 1; } static void -wpi_stop_master(struct wpi_softc *sc) +wpi_del_key_cb(void *arg, struct ieee80211_node *ni) { - uint32_t tmp; - int ntries; + const struct ieee80211_key *k = arg; + struct ieee80211vap *vap = ni->ni_vap; + struct wpi_softc *sc = ni->ni_ic->ic_ifp->if_softc; + struct wpi_node *wn = WPI_NODE(ni); + int error; - DPRINTFN(WPI_DEBUG_HW,("Disabling Firmware execution\n")); + if (vap->iv_bss == ni && wn->id == WPI_ID_UNDEFINED) + return; - tmp = WPI_READ(sc, WPI_RESET); - WPI_WRITE(sc, WPI_RESET, tmp | WPI_STOP_MASTER | WPI_NEVO_RESET); + WPI_NT_LOCK(sc); + error = wpi_del_key(ni, k); + WPI_NT_UNLOCK(sc); - tmp = WPI_READ(sc, WPI_GPIO_CTL); - if ((tmp & WPI_GPIO_PWR_STATUS) == WPI_GPIO_PWR_SLEEP) - return; /* already asleep */ + if (error == 0) { + device_printf(sc->sc_dev, "%s: error while deleting key\n", + __func__); + } +} - for (ntries = 0; ntries < 100; ntries++) { - if (WPI_READ(sc, WPI_RESET) & WPI_MASTER_DISABLED) - break; - DELAY(10); +static int +wpi_process_key(struct ieee80211vap *vap, const struct ieee80211_key *k, + int set) +{ + struct ieee80211com *ic = vap->iv_ic; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + struct wpi_vap *wvp = WPI_VAP(vap); + struct ieee80211_node *ni; + int error, ni_ref = 0; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + if (k->wk_flags & IEEE80211_KEY_SWCRYPT) { + /* Not for us. */ + return 1; + } + + if (!(k->wk_flags & IEEE80211_KEY_RECV)) { + /* XMIT keys are handled in wpi_tx_data(). */ + return 1; + } + + /* Handle group keys. */ + if (&vap->iv_nw_keys[0] <= k && + k < &vap->iv_nw_keys[IEEE80211_WEP_NKID]) { + WPI_NT_LOCK(sc); + if (set) + wvp->wv_gtk |= WPI_VAP_KEY(k->wk_keyix); + else + wvp->wv_gtk &= ~WPI_VAP_KEY(k->wk_keyix); + WPI_NT_UNLOCK(sc); + + if (vap->iv_state == IEEE80211_S_RUN) { + ieee80211_iterate_nodes(&ic->ic_sta, + set ? wpi_load_key_cb : wpi_del_key_cb, + __DECONST(void *, k)); + } + + return 1; } - if (ntries == 100) { - device_printf(sc->sc_dev, "timeout waiting for master\n"); + + switch (vap->iv_opmode) { + case IEEE80211_M_STA: + ni = vap->iv_bss; + break; + + case IEEE80211_M_IBSS: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + ni = ieee80211_find_vap_node(&ic->ic_sta, vap, k->wk_macaddr); + if (ni == NULL) + return 0; /* should not happen */ + + ni_ref = 1; + break; + + default: + device_printf(sc->sc_dev, "%s: unknown opmode %d\n", __func__, + vap->iv_opmode); + return 0; } + + WPI_NT_LOCK(sc); + if (set) + error = wpi_load_key(ni, k); + else + error = wpi_del_key(ni, k); + WPI_NT_UNLOCK(sc); + + if (ni_ref) + ieee80211_node_decref(ni); + + return error; } static int -wpi_power_up(struct wpi_softc *sc) +wpi_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k, + const uint8_t mac[IEEE80211_ADDR_LEN]) { - uint32_t tmp; - int ntries; + return wpi_process_key(vap, k, 1); +} - wpi_mem_lock(sc); - tmp = wpi_mem_read(sc, WPI_MEM_POWER); - wpi_mem_write(sc, WPI_MEM_POWER, tmp & ~0x03000000); - wpi_mem_unlock(sc); +static int +wpi_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) +{ + return wpi_process_key(vap, k, 0); +} - for (ntries = 0; ntries < 5000; ntries++) { - if (WPI_READ(sc, WPI_GPIO_STATUS) & WPI_POWERED) +/* + * This function is called after the runtime firmware notifies us of its + * readiness (called in a process context). + */ +static int +wpi_post_alive(struct wpi_softc *sc) +{ + int ntries, error; + + /* Check (again) that the radio is not disabled. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + /* NB: Runtime firmware must be up and running. */ + if (!(wpi_prph_read(sc, WPI_APMG_RFKILL) & 1)) { + device_printf(sc->sc_dev, + "RF switch: radio disabled (%s)\n", __func__); + wpi_nic_unlock(sc); + return EPERM; /* :-) */ + } + wpi_nic_unlock(sc); + + /* Wait for thermal sensor to calibrate. */ + for (ntries = 0; ntries < 1000; ntries++) { + if ((sc->temp = (int)WPI_READ(sc, WPI_UCODE_GP2)) != 0) break; DELAY(10); } - if (ntries == 5000) { + + if (ntries == 1000) { device_printf(sc->sc_dev, - "timeout waiting for NIC to power up\n"); + "timeout waiting for thermal sensor calibration\n"); return ETIMEDOUT; } + + DPRINTF(sc, WPI_DEBUG_TEMP, "temperature %d\n", sc->temp); return 0; } +/* + * The firmware boot code is small and is intended to be copied directly into + * the NIC internal memory (no DMA transfer). + */ static int -wpi_reset(struct wpi_softc *sc) +wpi_load_bootcode(struct wpi_softc *sc, const uint8_t *ucode, int size) { - uint32_t tmp; - int ntries; + int error, ntries; - DPRINTFN(WPI_DEBUG_HW, - ("Resetting the card - clearing any uploaded firmware\n")); + DPRINTF(sc, WPI_DEBUG_HW, "Loading microcode size 0x%x\n", size); - /* clear any pending interrupts */ - WPI_WRITE(sc, WPI_INTR, 0xffffffff); + size /= sizeof (uint32_t); - tmp = WPI_READ(sc, WPI_PLL_CTL); - WPI_WRITE(sc, WPI_PLL_CTL, tmp | WPI_PLL_INIT); + if ((error = wpi_nic_lock(sc)) != 0) + return error; + + /* Copy microcode image into NIC memory. */ + wpi_prph_write_region_4(sc, WPI_BSM_SRAM_BASE, + (const uint32_t *)ucode, size); - tmp = WPI_READ(sc, WPI_CHICKEN); - WPI_WRITE(sc, WPI_CHICKEN, tmp | WPI_CHICKEN_RXNOLOS); + wpi_prph_write(sc, WPI_BSM_WR_MEM_SRC, 0); + wpi_prph_write(sc, WPI_BSM_WR_MEM_DST, WPI_FW_TEXT_BASE); + wpi_prph_write(sc, WPI_BSM_WR_DWCOUNT, size); - tmp = WPI_READ(sc, WPI_GPIO_CTL); - WPI_WRITE(sc, WPI_GPIO_CTL, tmp | WPI_GPIO_INIT); + /* Start boot load now. */ + wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START); - /* wait for clock stabilization */ - for (ntries = 0; ntries < 25000; ntries++) { - if (WPI_READ(sc, WPI_GPIO_CTL) & WPI_GPIO_CLOCK) + /* Wait for transfer to complete. */ + for (ntries = 0; ntries < 1000; ntries++) { + uint32_t status = WPI_READ(sc, WPI_FH_TX_STATUS); + DPRINTF(sc, WPI_DEBUG_HW, + "firmware status=0x%x, val=0x%x, result=0x%x\n", status, + WPI_FH_TX_STATUS_IDLE(6), + status & WPI_FH_TX_STATUS_IDLE(6)); + if (status & WPI_FH_TX_STATUS_IDLE(6)) { + DPRINTF(sc, WPI_DEBUG_HW, + "Status Match! - ntries = %d\n", ntries); break; + } DELAY(10); } - if (ntries == 25000) { - device_printf(sc->sc_dev, - "timeout waiting for clock stabilization\n"); + if (ntries == 1000) { + device_printf(sc->sc_dev, "%s: could not load boot firmware\n", + __func__); + wpi_nic_unlock(sc); return ETIMEDOUT; } - /* initialize EEPROM */ - tmp = WPI_READ(sc, WPI_EEPROM_STATUS); - - if ((tmp & WPI_EEPROM_VERSION) == 0) { - device_printf(sc->sc_dev, "EEPROM not found\n"); - return EIO; - } - WPI_WRITE(sc, WPI_EEPROM_STATUS, tmp & ~WPI_EEPROM_LOCKED); + /* Enable boot after power up. */ + wpi_prph_write(sc, WPI_BSM_WR_CTRL, WPI_BSM_WR_CTRL_START_EN); + wpi_nic_unlock(sc); return 0; } -static void -wpi_hw_config(struct wpi_softc *sc) +static int +wpi_load_firmware(struct wpi_softc *sc) { - uint32_t rev, hw; + struct wpi_fw_info *fw = &sc->fw; + struct wpi_dma_info *dma = &sc->fw_dma; + int error; - /* voodoo from the Linux "driver".. */ - hw = WPI_READ(sc, WPI_HWCONFIG); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); - if ((rev & 0xc0) == 0x40) - hw |= WPI_HW_ALM_MB; - else if (!(rev & 0x80)) - hw |= WPI_HW_ALM_MM; + /* Copy initialization sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->init.data, fw->init.datasz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->init.text, fw->init.textsz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - if (sc->cap == 0x80) - hw |= WPI_HW_SKU_MRC; + /* Tell adapter where to find initialization sections. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->init.datasz); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, + dma->paddr + WPI_FW_DATA_MAXSZ); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, fw->init.textsz); + wpi_nic_unlock(sc); + + /* Load firmware boot code. */ + error = wpi_load_bootcode(sc, fw->boot.text, fw->boot.textsz); + if (error != 0) { + device_printf(sc->sc_dev, "%s: could not load boot firmware\n", + __func__); + return error; + } - hw &= ~WPI_HW_REV_D; - if ((le16toh(sc->rev) & 0xf0) == 0xd0) - hw |= WPI_HW_REV_D; + /* Now press "execute". */ + WPI_WRITE(sc, WPI_RESET, 0); - if (sc->type > 1) - hw |= WPI_HW_TYPE_B; + /* Wait at most one second for first alive notification. */ + if ((error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { + device_printf(sc->sc_dev, + "%s: timeout waiting for adapter to initialize, error %d\n", + __func__, error); + return error; + } - WPI_WRITE(sc, WPI_HWCONFIG, hw); + /* Copy runtime sections into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, fw->main.data, fw->main.datasz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + memcpy(dma->vaddr + WPI_FW_DATA_MAXSZ, fw->main.text, fw->main.textsz); + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); + + /* Tell adapter where to find runtime sections. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_ADDR, dma->paddr); + wpi_prph_write(sc, WPI_BSM_DRAM_DATA_SIZE, fw->main.datasz); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_ADDR, + dma->paddr + WPI_FW_DATA_MAXSZ); + wpi_prph_write(sc, WPI_BSM_DRAM_TEXT_SIZE, + WPI_FW_UPDATED | fw->main.textsz); + wpi_nic_unlock(sc); + + return 0; } -static void -wpi_rfkill_resume(struct wpi_softc *sc) +static int +wpi_read_firmware(struct wpi_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - int ntries; + const struct firmware *fp; + struct wpi_fw_info *fw = &sc->fw; + const struct wpi_firmware_hdr *hdr; + int error; - /* enable firmware again */ - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - /* wait for thermal sensors to calibrate */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) - break; - DELAY(10); - } + DPRINTF(sc, WPI_DEBUG_FIRMWARE, + "Attempting Loading Firmware from %s module\n", WPI_FW_NAME); - if (ntries == 1000) { + WPI_UNLOCK(sc); + fp = firmware_get(WPI_FW_NAME); + WPI_LOCK(sc); + + if (fp == NULL) { device_printf(sc->sc_dev, - "timeout waiting for thermal calibration\n"); - return; + "could not load firmware image '%s'\n", WPI_FW_NAME); + return EINVAL; } - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); - if (wpi_config(sc) != 0) { - device_printf(sc->sc_dev, "device config failed\n"); - return; + sc->fw_fp = fp; + + if (fp->datasize < sizeof (struct wpi_firmware_hdr)) { + device_printf(sc->sc_dev, + "firmware file too short: %zu bytes\n", fp->datasize); + error = EINVAL; + goto fail; } - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; + fw->size = fp->datasize; + fw->data = (const uint8_t *)fp->data; - if (vap != NULL) { - if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { - if (vap->iv_opmode != IEEE80211_M_MONITOR) { - ieee80211_beacon_miss(ic); - wpi_set_led(sc, WPI_LED_LINK, 0, 1); - } else - wpi_set_led(sc, WPI_LED_LINK, 5, 5); - } else { - ieee80211_scan_next(vap); - wpi_set_led(sc, WPI_LED_LINK, 20, 2); - } + /* Extract firmware header information. */ + hdr = (const struct wpi_firmware_hdr *)fw->data; + + /* | RUNTIME FIRMWARE | INIT FIRMWARE | BOOT FW | + |HDR|<--TEXT-->|<--DATA-->|<--TEXT-->|<--DATA-->|<--TEXT-->| */ + + fw->main.textsz = le32toh(hdr->rtextsz); + fw->main.datasz = le32toh(hdr->rdatasz); + fw->init.textsz = le32toh(hdr->itextsz); + fw->init.datasz = le32toh(hdr->idatasz); + fw->boot.textsz = le32toh(hdr->btextsz); + fw->boot.datasz = 0; + + /* Sanity-check firmware header. */ + if (fw->main.textsz > WPI_FW_TEXT_MAXSZ || + fw->main.datasz > WPI_FW_DATA_MAXSZ || + fw->init.textsz > WPI_FW_TEXT_MAXSZ || + fw->init.datasz > WPI_FW_DATA_MAXSZ || + fw->boot.textsz > WPI_FW_BOOT_TEXT_MAXSZ || + (fw->boot.textsz & 3) != 0) { + device_printf(sc->sc_dev, "invalid firmware header\n"); + error = EINVAL; + goto fail; } - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -} + /* Check that all firmware sections fit. */ + if (fw->size < sizeof (*hdr) + fw->main.textsz + fw->main.datasz + + fw->init.textsz + fw->init.datasz + fw->boot.textsz) { + device_printf(sc->sc_dev, + "firmware file too short: %zu bytes\n", fw->size); + error = EINVAL; + goto fail; + } -static void -wpi_init_locked(struct wpi_softc *sc, int force) -{ - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - int ntries, qid; + /* Get pointers to firmware sections. */ + fw->main.text = (const uint8_t *)(hdr + 1); + fw->main.data = fw->main.text + fw->main.textsz; + fw->init.text = fw->main.data + fw->main.datasz; + fw->init.data = fw->init.text + fw->init.textsz; + fw->boot.text = fw->init.data + fw->init.datasz; - wpi_stop_locked(sc); - (void)wpi_reset(sc); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, + "Firmware Version: Major %d, Minor %d, Driver %d, \n" + "runtime (text: %u, data: %u) init (text: %u, data %u) " + "boot (text %u)\n", hdr->major, hdr->minor, le32toh(hdr->driver), + fw->main.textsz, fw->main.datasz, + fw->init.textsz, fw->init.datasz, fw->boot.textsz); - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_CLOCK1, 0xa00); - DELAY(20); - tmp = wpi_mem_read(sc, WPI_MEM_PCIDEV); - wpi_mem_write(sc, WPI_MEM_PCIDEV, tmp | 0x800); - wpi_mem_unlock(sc); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.text %p\n", fw->main.text); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->main.data %p\n", fw->main.data); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.text %p\n", fw->init.text); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->init.data %p\n", fw->init.data); + DPRINTF(sc, WPI_DEBUG_FIRMWARE, "fw->boot.text %p\n", fw->boot.text); - (void)wpi_power_up(sc); - wpi_hw_config(sc); + return 0; - /* init Rx ring */ - wpi_mem_lock(sc); - WPI_WRITE(sc, WPI_RX_BASE, sc->rxq.desc_dma.paddr); - WPI_WRITE(sc, WPI_RX_RIDX_PTR, sc->shared_dma.paddr + - offsetof(struct wpi_shared, next)); - WPI_WRITE(sc, WPI_RX_WIDX, (WPI_RX_RING_COUNT - 1) & ~7); - WPI_WRITE(sc, WPI_RX_CONFIG, 0xa9601010); - wpi_mem_unlock(sc); - - /* init Tx rings */ - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_MODE, 2); /* bypass mode */ - wpi_mem_write(sc, WPI_MEM_RA, 1); /* enable RA0 */ - wpi_mem_write(sc, WPI_MEM_TXCFG, 0x3f); /* enable all 6 Tx rings */ - wpi_mem_write(sc, WPI_MEM_BYPASS1, 0x10000); - wpi_mem_write(sc, WPI_MEM_BYPASS2, 0x30002); - wpi_mem_write(sc, WPI_MEM_MAGIC4, 4); - wpi_mem_write(sc, WPI_MEM_MAGIC5, 5); - - WPI_WRITE(sc, WPI_TX_BASE_PTR, sc->shared_dma.paddr); - WPI_WRITE(sc, WPI_MSG_CONFIG, 0xffff05a5); - - for (qid = 0; qid < 6; qid++) { - WPI_WRITE(sc, WPI_TX_CTL(qid), 0); - WPI_WRITE(sc, WPI_TX_BASE(qid), 0); - WPI_WRITE(sc, WPI_TX_CONFIG(qid), 0x80200008); - } - wpi_mem_unlock(sc); - - /* clear "radio off" and "disable command" bits (reversed logic) */ - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_DISABLE_CMD); - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; - - /* clear any pending interrupts */ - WPI_WRITE(sc, WPI_INTR, 0xffffffff); - - /* enable interrupts */ - WPI_WRITE(sc, WPI_MASK, WPI_INTR_MASK); - - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - WPI_WRITE(sc, WPI_UCODE_CLR, WPI_RADIO_OFF); - - if ((wpi_load_firmware(sc)) != 0) { - device_printf(sc->sc_dev, - "A problem occurred loading the firmware to the driver\n"); - return; - } - - /* At this point the firmware is up and running. If the hardware - * RF switch is turned off thermal calibration will fail, though - * the card is still happy to continue to accept commands, catch - * this case and schedule a task to watch for it to be turned on. - */ - wpi_mem_lock(sc); - tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); - wpi_mem_unlock(sc); +fail: wpi_unload_firmware(sc); + return error; +} - if (!(tmp & 0x1)) { - sc->flags |= WPI_FLAG_HW_RADIO_OFF; - device_printf(sc->sc_dev,"Radio Transmitter is switched off\n"); - goto out; +/** + * Free the referenced firmware image + */ +static void +wpi_unload_firmware(struct wpi_softc *sc) +{ + if (sc->fw_fp != NULL) { + firmware_put(sc->fw_fp, FIRMWARE_UNLOAD); + sc->fw_fp = NULL; } +} - /* wait for thermal sensors to calibrate */ - for (ntries = 0; ntries < 1000; ntries++) { - if ((sc->temp = (int)WPI_READ(sc, WPI_TEMPERATURE)) != 0) - break; - DELAY(10); - } +static int +wpi_clock_wait(struct wpi_softc *sc) +{ + int ntries; - if (ntries == 1000) { - device_printf(sc->sc_dev, - "timeout waiting for thermal sensors calibration\n"); - return; - } - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d\n", sc->temp)); + /* Set "initialization complete" bit. */ + WPI_SETBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); - if (wpi_config(sc) != 0) { - device_printf(sc->sc_dev, "device config failed\n"); - return; + /* Wait for clock stabilization. */ + for (ntries = 0; ntries < 2500; ntries++) { + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_MAC_CLOCK_READY) + return 0; + DELAY(100); } + device_printf(sc->sc_dev, + "%s: timeout waiting for clock stabilization\n", __func__); - ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; - ifp->if_drv_flags |= IFF_DRV_RUNNING; -out: - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); + return ETIMEDOUT; } -static void -wpi_init(void *arg) +static int +wpi_apm_init(struct wpi_softc *sc) { - struct wpi_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; + uint32_t reg; + int error; - WPI_LOCK(sc); - wpi_init_locked(sc, 0); - WPI_UNLOCK(sc); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - ieee80211_start_all(ic); /* start all vaps */ -} + /* Disable L0s exit timer (NMI bug workaround). */ + WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_DIS_L0S_TIMER); + /* Don't wait for ICH L0s (ICH bug workaround). */ + WPI_SETBITS(sc, WPI_GIO_CHICKEN, WPI_GIO_CHICKEN_L1A_NO_L0S_RX); -static void -wpi_stop_locked(struct wpi_softc *sc) -{ - struct ifnet *ifp = sc->sc_ifp; - uint32_t tmp; - int ac; + /* Set FH wait threshold to max (HW bug under stress workaround). */ + WPI_SETBITS(sc, WPI_DBG_HPET_MEM, 0xffff0000); - sc->sc_tx_timer = 0; - sc->sc_scan_timer = 0; - ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); - sc->flags &= ~WPI_FLAG_HW_RADIO_OFF; - callout_stop(&sc->watchdog_to); - callout_stop(&sc->calib_to); + /* Retrieve PCIe Active State Power Management (ASPM). */ + reg = pci_read_config(sc->sc_dev, sc->sc_cap_off + 0x10, 1); + /* Workaround for HW instability in PCIe L0->L0s->L1 transition. */ + if (reg & 0x02) /* L1 Entry enabled. */ + WPI_SETBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); + else + WPI_CLRBITS(sc, WPI_GIO, WPI_GIO_L0S_ENA); - /* disable interrupts */ - WPI_WRITE(sc, WPI_MASK, 0); - WPI_WRITE(sc, WPI_INTR, WPI_INTR_MASK); - WPI_WRITE(sc, WPI_INTR_STATUS, 0xff); - WPI_WRITE(sc, WPI_INTR_STATUS, 0x00070000); + WPI_SETBITS(sc, WPI_ANA_PLL, WPI_ANA_PLL_INIT); - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_MODE, 0); - wpi_mem_unlock(sc); + /* Wait for clock stabilization before accessing prph. */ + if ((error = wpi_clock_wait(sc)) != 0) + return error; - /* reset all Tx rings */ - for (ac = 0; ac < 4; ac++) - wpi_reset_tx_ring(sc, &sc->txq[ac]); - wpi_reset_tx_ring(sc, &sc->cmdq); + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* Cleanup. */ + wpi_prph_write(sc, WPI_APMG_CLK_DIS, 0x00000400); + wpi_prph_clrbits(sc, WPI_APMG_PS, 0x00000200); - /* reset Rx ring */ - wpi_reset_rx_ring(sc, &sc->rxq); + /* Enable DMA and BSM (Bootstrap State Machine). */ + wpi_prph_write(sc, WPI_APMG_CLK_EN, + WPI_APMG_CLK_CTRL_DMA_CLK_RQT | WPI_APMG_CLK_CTRL_BSM_CLK_RQT); + DELAY(20); + /* Disable L1-Active. */ + wpi_prph_setbits(sc, WPI_APMG_PCI_STT, WPI_APMG_PCI_STT_L1A_DIS); + wpi_nic_unlock(sc); - wpi_mem_lock(sc); - wpi_mem_write(sc, WPI_MEM_CLOCK2, 0x200); - wpi_mem_unlock(sc); + return 0; +} - DELAY(5); +static void +wpi_apm_stop_master(struct wpi_softc *sc) +{ + int ntries; + + /* Stop busmaster DMA activity. */ + WPI_SETBITS(sc, WPI_RESET, WPI_RESET_STOP_MASTER); - wpi_stop_master(sc); + if ((WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_PS_MASK) == + WPI_GP_CNTRL_MAC_PS) + return; /* Already asleep. */ - tmp = WPI_READ(sc, WPI_RESET); - WPI_WRITE(sc, WPI_RESET, tmp | WPI_SW_RESET); - sc->flags &= ~WPI_FLAG_BUSY; + for (ntries = 0; ntries < 100; ntries++) { + if (WPI_READ(sc, WPI_RESET) & WPI_RESET_MASTER_DISABLED) + return; + DELAY(10); + } + device_printf(sc->sc_dev, "%s: timeout waiting for master\n", + __func__); } static void -wpi_stop(struct wpi_softc *sc) +wpi_apm_stop(struct wpi_softc *sc) { - WPI_LOCK(sc); - wpi_stop_locked(sc); - WPI_UNLOCK(sc); + wpi_apm_stop_master(sc); + + /* Reset the entire device. */ + WPI_SETBITS(sc, WPI_RESET, WPI_RESET_SW); + DELAY(10); + /* Clear "initialization complete" bit. */ + WPI_CLRBITS(sc, WPI_GP_CNTRL, WPI_GP_CNTRL_INIT_DONE); } static void -wpi_calib_timeout(void *arg) +wpi_nic_config(struct wpi_softc *sc) { - struct wpi_softc *sc = arg; - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - int temp; + uint32_t rev; - if (vap->iv_state != IEEE80211_S_RUN) - return; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - /* update sensor data */ - temp = (int)WPI_READ(sc, WPI_TEMPERATURE); - DPRINTFN(WPI_DEBUG_TEMP,("Temp in calibration is: %d\n", temp)); + /* voodoo from the Linux "driver".. */ + rev = pci_read_config(sc->sc_dev, PCIR_REVID, 1); + if ((rev & 0xc0) == 0x40) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MB); + else if (!(rev & 0x80)) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_ALM_MM); - wpi_power_calibration(sc, temp); + if (sc->cap == 0x80) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_SKU_MRC); - callout_reset(&sc->calib_to, 60*hz, wpi_calib_timeout, sc); + if ((sc->rev & 0xf0) == 0xd0) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); + else + WPI_CLRBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_REV_D); + + if (sc->type > 1) + WPI_SETBITS(sc, WPI_HW_IF_CONFIG, WPI_HW_IF_CONFIG_TYPE_B); } -/* - * This function is called periodically (every 60 seconds) to adjust output - * power to temperature changes. - */ -static void -wpi_power_calibration(struct wpi_softc *sc, int temp) +static int +wpi_hw_init(struct wpi_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + int chnl, ntries, error; - /* sanity-check read value */ - if (temp < -260 || temp > 25) { - /* this can't be correct, ignore */ - DPRINTFN(WPI_DEBUG_TEMP, - ("out-of-range temperature reported: %d\n", temp)); - return; - } + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - DPRINTFN(WPI_DEBUG_TEMP,("temperature %d->%d\n", sc->temp, temp)); + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); - /* adjust Tx power if need be */ - if (abs(temp - sc->temp) <= 6) - return; + if ((error = wpi_apm_init(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not power ON adapter, error %d\n", __func__, + error); + return error; + } - sc->temp = temp; + /* Select VMAIN power source. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_clrbits(sc, WPI_APMG_PS, WPI_APMG_PS_PWR_SRC_MASK); + wpi_nic_unlock(sc); + /* Spin until VMAIN gets selected. */ + for (ntries = 0; ntries < 5000; ntries++) { + if (WPI_READ(sc, WPI_GPIO_IN) & WPI_GPIO_IN_VMAIN) + break; + DELAY(10); + } + if (ntries == 5000) { + device_printf(sc->sc_dev, "timeout selecting power source\n"); + return ETIMEDOUT; + } - if (wpi_set_txpower(sc, vap->iv_bss->ni_chan, 1) != 0) { - /* just warn, too bad for the automatic calibration... */ - device_printf(sc->sc_dev,"could not adjust Tx power\n"); + /* Perform adapter initialization. */ + wpi_nic_config(sc); + + /* Initialize RX ring. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + /* Set physical address of RX ring. */ + WPI_WRITE(sc, WPI_FH_RX_BASE, sc->rxq.desc_dma.paddr); + /* Set physical address of RX read pointer. */ + WPI_WRITE(sc, WPI_FH_RX_RPTR_ADDR, sc->shared_dma.paddr + + offsetof(struct wpi_shared, next)); + WPI_WRITE(sc, WPI_FH_RX_WPTR, 0); + /* Enable RX. */ + WPI_WRITE(sc, WPI_FH_RX_CONFIG, + WPI_FH_RX_CONFIG_DMA_ENA | + WPI_FH_RX_CONFIG_RDRBD_ENA | + WPI_FH_RX_CONFIG_WRSTATUS_ENA | + WPI_FH_RX_CONFIG_MAXFRAG | + WPI_FH_RX_CONFIG_NRBD(WPI_RX_RING_COUNT_LOG) | + WPI_FH_RX_CONFIG_IRQ_DST_HOST | + WPI_FH_RX_CONFIG_IRQ_TIMEOUT(1)); + (void)WPI_READ(sc, WPI_FH_RSSR_TBL); /* barrier */ + wpi_nic_unlock(sc); + WPI_WRITE(sc, WPI_FH_RX_WPTR, (WPI_RX_RING_COUNT - 1) & ~7); + + /* Initialize TX rings. */ + if ((error = wpi_nic_lock(sc)) != 0) + return error; + wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 2); /* bypass mode */ + wpi_prph_write(sc, WPI_ALM_SCHED_ARASTAT, 1); /* enable RA0 */ + /* Enable all 6 TX rings. */ + wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0x3f); + wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE1, 0x10000); + wpi_prph_write(sc, WPI_ALM_SCHED_SBYPASS_MODE2, 0x30002); + wpi_prph_write(sc, WPI_ALM_SCHED_TXF4MF, 4); + wpi_prph_write(sc, WPI_ALM_SCHED_TXF5MF, 5); + /* Set physical address of TX rings. */ + WPI_WRITE(sc, WPI_FH_TX_BASE, sc->shared_dma.paddr); + WPI_WRITE(sc, WPI_FH_MSG_CONFIG, 0xffff05a5); + + /* Enable all DMA channels. */ + for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { + WPI_WRITE(sc, WPI_FH_CBBC_CTRL(chnl), 0); + WPI_WRITE(sc, WPI_FH_CBBC_BASE(chnl), 0); + WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0x80200008); + } + wpi_nic_unlock(sc); + (void)WPI_READ(sc, WPI_FH_TX_BASE); /* barrier */ + + /* Clear "radio off" and "commands blocked" bits. */ + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_CMD_BLOCKED); + + /* Clear pending interrupts. */ + WPI_WRITE(sc, WPI_INT, 0xffffffff); + /* Enable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, WPI_INT_MASK_DEF); + + /* _Really_ make sure "radio off" bit is cleared! */ + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); + WPI_WRITE(sc, WPI_UCODE_GP1_CLR, WPI_UCODE_GP1_RFKILL); + + if ((error = wpi_load_firmware(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not load firmware, error %d\n", __func__, + error); + return error; + } + /* Wait at most one second for firmware alive notification. */ + if ((error = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "wpiinit", hz)) != 0) { + device_printf(sc->sc_dev, + "%s: timeout waiting for adapter to initialize, error %d\n", + __func__, error); + return error; } + + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); + + /* Do post-firmware initialization. */ + return wpi_post_alive(sc); } -/** - * Read the eeprom to find out what channels are valid for the given - * band and update net80211 with what we find. - */ static void -wpi_read_eeprom_channels(struct wpi_softc *sc, int n) +wpi_hw_stop(struct wpi_softc *sc) { - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - const struct wpi_chan_band *band = &wpi_bands[n]; - struct wpi_eeprom_chan channels[WPI_MAX_CHAN_PER_BAND]; - struct ieee80211_channel *c; - int chan, i, passive; + int chnl, qid, ntries; - wpi_read_prom_data(sc, band->addr, channels, - band->nchan * sizeof (struct wpi_eeprom_chan)); + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); - for (i = 0; i < band->nchan; i++) { - if (!(channels[i].flags & WPI_EEPROM_CHAN_VALID)) { - DPRINTFN(WPI_DEBUG_HW, - ("Channel Not Valid: %d, band %d\n", - band->chan[i],n)); - continue; - } + if (WPI_READ(sc, WPI_UCODE_GP1) & WPI_UCODE_GP1_MAC_SLEEP) + wpi_nic_lock(sc); - passive = 0; - chan = band->chan[i]; - c = &ic->ic_channels[ic->ic_nchans++]; + WPI_WRITE(sc, WPI_RESET, WPI_RESET_NEVO); - /* is active scan allowed on this channel? */ - if (!(channels[i].flags & WPI_EEPROM_CHAN_ACTIVE)) { - passive = IEEE80211_CHAN_PASSIVE; - } + /* Disable interrupts. */ + WPI_WRITE(sc, WPI_INT_MASK, 0); + WPI_WRITE(sc, WPI_INT, 0xffffffff); + WPI_WRITE(sc, WPI_FH_INT, 0xffffffff); - if (n == 0) { /* 2GHz band */ - c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_2GHZ); - c->ic_flags = IEEE80211_CHAN_B | passive; + /* Make sure we no longer hold the NIC lock. */ + wpi_nic_unlock(sc); - c = &ic->ic_channels[ic->ic_nchans++]; - c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_2GHZ); - c->ic_flags = IEEE80211_CHAN_G | passive; + if (wpi_nic_lock(sc) == 0) { + /* Stop TX scheduler. */ + wpi_prph_write(sc, WPI_ALM_SCHED_MODE, 0); + wpi_prph_write(sc, WPI_ALM_SCHED_TXFACT, 0); - } else { /* 5GHz band */ - /* - * Some 3945ABG adapters support channels 7, 8, 11 - * and 12 in the 2GHz *and* 5GHz bands. - * Because of limitations in our net80211(9) stack, - * we can't support these channels in 5GHz band. - * XXX not true; just need to map to proper frequency - */ - if (chan <= 14) - continue; - - c->ic_ieee = chan; - c->ic_freq = ieee80211_ieee2mhz(chan, - IEEE80211_CHAN_5GHZ); - c->ic_flags = IEEE80211_CHAN_A | passive; + /* Stop all DMA channels. */ + for (chnl = 0; chnl < WPI_NDMACHNLS; chnl++) { + WPI_WRITE(sc, WPI_FH_TX_CONFIG(chnl), 0); + for (ntries = 0; ntries < 200; ntries++) { + if (WPI_READ(sc, WPI_FH_TX_STATUS) & + WPI_FH_TX_STATUS_IDLE(chnl)) + break; + DELAY(10); + } } + wpi_nic_unlock(sc); + } - /* save maximum allowed power for this channel */ - sc->maxpwr[chan] = channels[i].maxpwr; + /* Stop RX ring. */ + wpi_reset_rx_ring(sc); -#if 0 - // XXX We can probably use this an get rid of maxpwr - ben 20070617 - ic->ic_channels[chan].ic_maxpower = channels[i].maxpwr; - //ic->ic_channels[chan].ic_minpower... - //ic->ic_channels[chan].ic_maxregtxpower... -#endif + /* Reset all TX rings. */ + for (qid = 0; qid < WPI_NTXQUEUES; qid++) + wpi_reset_tx_ring(sc, &sc->txq[qid]); - DPRINTF(("adding chan %d (%dMHz) flags=0x%x maxpwr=%d" - " passive=%d, offset %d\n", chan, c->ic_freq, - channels[i].flags, sc->maxpwr[chan], - (c->ic_flags & IEEE80211_CHAN_PASSIVE) != 0, - ic->ic_nchans)); + if (wpi_nic_lock(sc) == 0) { + wpi_prph_write(sc, WPI_APMG_CLK_DIS, + WPI_APMG_CLK_CTRL_DMA_CLK_RQT); + wpi_nic_unlock(sc); } + DELAY(5); + /* Power OFF adapter. */ + wpi_apm_stop(sc); } static void -wpi_read_eeprom_group(struct wpi_softc *sc, int n) +wpi_radio_on(void *arg0, int pending) { - struct wpi_power_group *group = &sc->groups[n]; - struct wpi_eeprom_group rgroup; - int i; + struct wpi_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - wpi_read_prom_data(sc, WPI_EEPROM_POWER_GRP + n * 32, &rgroup, - sizeof rgroup); + device_printf(sc->sc_dev, "RF switch: radio enabled\n"); - /* save power group information */ - group->chan = rgroup.chan; - group->maxpwr = rgroup.maxpwr; - /* temperature at which the samples were taken */ - group->temp = (int16_t)le16toh(rgroup.temp); + if (vap != NULL) { + wpi_init(sc); + ieee80211_init(vap); + } - DPRINTF(("power group %d: chan=%d maxpwr=%d temp=%d\n", n, - group->chan, group->maxpwr, group->temp)); + if (WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL) { + WPI_LOCK(sc); + callout_stop(&sc->watchdog_rfkill); + WPI_UNLOCK(sc); + } +} - for (i = 0; i < WPI_SAMPLES_COUNT; i++) { - group->samples[i].index = rgroup.samples[i].index; - group->samples[i].power = rgroup.samples[i].power; +static void +wpi_radio_off(void *arg0, int pending) +{ + struct wpi_softc *sc = arg0; + struct ifnet *ifp = sc->sc_ifp; + struct ieee80211com *ic = ifp->if_l2com; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - DPRINTF(("\tsample %d: index=%d power=%d\n", i, - group->samples[i].index, group->samples[i].power)); - } + device_printf(sc->sc_dev, "RF switch: radio disabled\n"); + + wpi_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + + WPI_LOCK(sc); + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, sc); + WPI_UNLOCK(sc); } -/* - * Update Tx power to match what is defined for channel `c'. - */ -static int -wpi_set_txpower(struct wpi_softc *sc, struct ieee80211_channel *c, int async) +static void +wpi_init(void *arg) { + struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power_group *group; - struct wpi_cmd_txpower txpower; - u_int chan; - int i; + int error; - /* get channel number */ - chan = ieee80211_chan2ieee(ic, c); + WPI_LOCK(sc); - /* find the power group to which this channel belongs */ - if (IEEE80211_IS_CHAN_5GHZ(c)) { - for (group = &sc->groups[1]; group < &sc->groups[4]; group++) - if (chan <= group->chan) - break; - } else - group = &sc->groups[0]; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_BEGIN, __func__); - memset(&txpower, 0, sizeof txpower); - txpower.band = IEEE80211_IS_CHAN_5GHZ(c) ? 0 : 1; - txpower.channel = htole16(chan); + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + goto end; - /* set Tx power for all OFDM and CCK rates */ - for (i = 0; i <= 11 ; i++) { - /* retrieve Tx power for this channel/rate combination */ - int idx = wpi_get_power_index(sc, group, c, - wpi_ridx_to_rate[i]); + /* Check that the radio is not disabled by hardware switch. */ + if (!(WPI_READ(sc, WPI_GP_CNTRL) & WPI_GP_CNTRL_RFKILL)) { + device_printf(sc->sc_dev, + "RF switch: radio disabled (%s)\n", __func__); + callout_reset(&sc->watchdog_rfkill, hz, wpi_watchdog_rfkill, + sc); + goto end; + } - txpower.rates[i].rate = wpi_ridx_to_plcp[i]; + /* Read firmware images from the filesystem. */ + if ((error = wpi_read_firmware(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not read firmware, error %d\n", __func__, + error); + goto fail; + } - if (IEEE80211_IS_CHAN_5GHZ(c)) { - txpower.rates[i].gain_radio = wpi_rf_gain_5ghz[idx]; - txpower.rates[i].gain_dsp = wpi_dsp_gain_5ghz[idx]; - } else { - txpower.rates[i].gain_radio = wpi_rf_gain_2ghz[idx]; - txpower.rates[i].gain_dsp = wpi_dsp_gain_2ghz[idx]; - } - DPRINTFN(WPI_DEBUG_TEMP,("chan %d/rate %d: power index %d\n", - chan, wpi_ridx_to_rate[i], idx)); + /* Initialize hardware and upload firmware. */ + error = wpi_hw_init(sc); + wpi_unload_firmware(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: could not initialize hardware, error %d\n", __func__, + error); + goto fail; } - return wpi_cmd(sc, WPI_CMD_TXPOWER, &txpower, sizeof txpower, async); -} + /* Configure adapter now that it is ready. */ + sc->txq_active = 1; + if ((error = wpi_config(sc)) != 0) { + device_printf(sc->sc_dev, + "%s: could not configure device, error %d\n", __func__, + error); + goto fail; + } -/* - * Determine Tx power index for a given channel/rate combination. - * This takes into account the regulatory information from EEPROM and the - * current temperature. - */ -static int -wpi_get_power_index(struct wpi_softc *sc, struct wpi_power_group *group, - struct ieee80211_channel *c, int rate) -{ -/* fixed-point arithmetic division using a n-bit fractional part */ -#define fdivround(a, b, n) \ - ((((1 << n) * (a)) / (b) + (1 << n) / 2) / (1 << n)) + IF_LOCK(&ifp->if_snd); + ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + ifp->if_drv_flags |= IFF_DRV_RUNNING; + IF_UNLOCK(&ifp->if_snd); -/* linear interpolation */ -#define interpolate(x, x1, y1, x2, y2, n) \ - ((y1) + fdivround(((x) - (x1)) * ((y2) - (y1)), (x2) - (x1), n)) + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END, __func__); - struct ifnet *ifp = sc->sc_ifp; - struct ieee80211com *ic = ifp->if_l2com; - struct wpi_power_sample *sample; - int pwr, idx; - u_int chan; + WPI_UNLOCK(sc); - /* get channel number */ - chan = ieee80211_chan2ieee(ic, c); + ieee80211_start_all(ic); - /* default power is group's maximum power - 3dB */ - pwr = group->maxpwr / 2; + return; - /* decrease power for highest OFDM rates to reduce distortion */ - switch (rate) { - case 72: /* 36Mb/s */ - pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 0 : 5; - break; - case 96: /* 48Mb/s */ - pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 7 : 10; - break; - case 108: /* 54Mb/s */ - pwr -= IEEE80211_IS_CHAN_2GHZ(c) ? 9 : 12; - break; - } +fail: wpi_stop_locked(sc); +end: DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_END_ERR, __func__); + WPI_UNLOCK(sc); +} - /* never exceed channel's maximum allowed Tx power */ - pwr = min(pwr, sc->maxpwr[chan]); +static void +wpi_stop_locked(struct wpi_softc *sc) +{ + struct ifnet *ifp = sc->sc_ifp; - /* retrieve power index into gain tables from samples */ - for (sample = group->samples; sample < &group->samples[3]; sample++) - if (pwr > sample[1].power) - break; - /* fixed-point linear interpolation using a 19-bit fractional part */ - idx = interpolate(pwr, sample[0].power, sample[0].index, - sample[1].power, sample[1].index, 19); + WPI_LOCK_ASSERT(sc); - /* - * Adjust power index based on current temperature - * - if colder than factory-calibrated: decreate output power - * - if warmer than factory-calibrated: increase output power - */ - idx -= (sc->temp - group->temp) * 11 / 100; + WPI_TXQ_LOCK(sc); + sc->txq_active = 0; + WPI_TXQ_UNLOCK(sc); - /* decrease power for CCK rates (-5dB) */ - if (!WPI_RATE_IS_OFDM(rate)) - idx += 10; + WPI_TXQ_STATE_LOCK(sc); + callout_stop(&sc->tx_timeout); + WPI_TXQ_STATE_UNLOCK(sc); - /* keep power index in a valid range */ - if (idx < 0) - return 0; - if (idx > WPI_MAX_PWR_INDEX) - return WPI_MAX_PWR_INDEX; - return idx; + WPI_RXON_LOCK(sc); + callout_stop(&sc->scan_timeout); + callout_stop(&sc->calib_to); + WPI_RXON_UNLOCK(sc); -#undef interpolate -#undef fdivround + IF_LOCK(&ifp->if_snd); + ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); + IF_UNLOCK(&ifp->if_snd); + + /* Power OFF hardware. */ + wpi_hw_stop(sc); } -/** - * Called by net80211 framework to indicate that a scan - * is starting. This function doesn't actually do the scan, - * wpi_scan_curchan starts things off. This function is more - * of an early warning from the framework we should get ready - * for the scan. +static void +wpi_stop(struct wpi_softc *sc) +{ + WPI_LOCK(sc); + wpi_stop_locked(sc); + WPI_UNLOCK(sc); +} + +/* + * Callback from net80211 to start a scan. */ static void wpi_scan_start(struct ieee80211com *ic) { - struct ifnet *ifp = ic->ic_ifp; - struct wpi_softc *sc = ifp->if_softc; + struct wpi_softc *sc = ic->ic_ifp->if_softc; - WPI_LOCK(sc); wpi_set_led(sc, WPI_LED_LINK, 20, 2); - WPI_UNLOCK(sc); } -/** - * Called by the net80211 framework, indicates that the - * scan has ended. If there is a scan in progress on the card - * then it should be aborted. +/* + * Callback from net80211 to terminate a scan. */ static void wpi_scan_end(struct ieee80211com *ic) { - /* XXX ignore */ + struct ifnet *ifp = ic->ic_ifp; + struct wpi_softc *sc = ifp->if_softc; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + if (vap->iv_state == IEEE80211_S_RUN) + wpi_set_led(sc, WPI_LED_LINK, 0, 1); } /** @@ -3549,21 +5566,41 @@ wpi_scan_end(struct ieee80211com *ic) static void wpi_set_channel(struct ieee80211com *ic) { + const struct ieee80211_channel *c = ic->ic_curchan; struct ifnet *ifp = ic->ic_ifp; struct wpi_softc *sc = ifp->if_softc; int error; + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); + + WPI_LOCK(sc); + sc->sc_rxtap.wr_chan_freq = htole16(c->ic_freq); + sc->sc_rxtap.wr_chan_flags = htole16(c->ic_flags); + WPI_UNLOCK(sc); + WPI_TX_LOCK(sc); + sc->sc_txtap.wt_chan_freq = htole16(c->ic_freq); + sc->sc_txtap.wt_chan_flags = htole16(c->ic_flags); + WPI_TX_UNLOCK(sc); + /* * Only need to set the channel in Monitor mode. AP scanning and auth * are already taken care of by their respective firmware commands. */ if (ic->ic_opmode == IEEE80211_M_MONITOR) { - WPI_LOCK(sc); - error = wpi_config(sc); - WPI_UNLOCK(sc); - if (error != 0) + WPI_RXON_LOCK(sc); + sc->rxon.chan = ieee80211_chan2ieee(ic, c); + if (IEEE80211_IS_CHAN_2GHZ(c)) { + sc->rxon.flags |= htole32(WPI_RXON_AUTO | + WPI_RXON_24GHZ); + } else { + sc->rxon.flags &= ~htole32(WPI_RXON_AUTO | + WPI_RXON_24GHZ); + } + if ((error = wpi_send_rxon(sc, 0, 1)) != 0) device_printf(sc->sc_dev, - "error %d settting channel\n", error); + "%s: error %d setting channel\n", __func__, + error); + WPI_RXON_UNLOCK(sc); } } @@ -3576,13 +5613,15 @@ static void wpi_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { struct ieee80211vap *vap = ss->ss_vap; - struct ifnet *ifp = vap->iv_ic->ic_ifp; - struct wpi_softc *sc = ifp->if_softc; + struct ieee80211com *ic = vap->iv_ic; + struct wpi_softc *sc = ic->ic_ifp->if_softc; + int error; - WPI_LOCK(sc); - if (wpi_scan(sc)) + WPI_RXON_LOCK(sc); + error = wpi_scan(sc, ic->ic_curchan); + WPI_RXON_UNLOCK(sc); + if (error != 0) ieee80211_cancel_scan(vap); - WPI_UNLOCK(sc); } /** @@ -3598,118 +5637,22 @@ wpi_scan_mindwell(struct ieee80211_scan_state *ss) } static void -wpi_hwreset(void *arg, int pending) -{ - struct wpi_softc *sc = arg; - - WPI_LOCK(sc); - wpi_init_locked(sc, 0); - WPI_UNLOCK(sc); -} - -static void -wpi_rfreset(void *arg, int pending) -{ - struct wpi_softc *sc = arg; - - WPI_LOCK(sc); - wpi_rfkill_resume(sc); - WPI_UNLOCK(sc); -} - -/* - * Allocate DMA-safe memory for firmware transfer. - */ -static int -wpi_alloc_fwmem(struct wpi_softc *sc) -{ - /* allocate enough contiguous space to store text and data */ - return wpi_dma_contig_alloc(sc, &sc->fw_dma, NULL, - WPI_FW_MAIN_TEXT_MAXSZ + WPI_FW_MAIN_DATA_MAXSZ, 1, - BUS_DMA_NOWAIT); -} - -static void -wpi_free_fwmem(struct wpi_softc *sc) -{ - wpi_dma_contig_free(&sc->fw_dma); -} - -/** - * Called every second, wpi_watchdog used by the watch dog timer - * to check that the card is still alive - */ -static void -wpi_watchdog(void *arg) +wpi_hw_reset(void *arg, int pending) { struct wpi_softc *sc = arg; struct ifnet *ifp = sc->sc_ifp; struct ieee80211com *ic = ifp->if_l2com; - uint32_t tmp; - - DPRINTFN(WPI_DEBUG_WATCHDOG,("Watchdog: tick\n")); - - if (sc->flags & WPI_FLAG_HW_RADIO_OFF) { - /* No need to lock firmware memory */ - tmp = wpi_mem_read(sc, WPI_MEM_HW_RADIO_OFF); - - if ((tmp & 0x1) == 0) { - /* Radio kill switch is still off */ - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); - return; - } - - device_printf(sc->sc_dev, "Hardware Switch Enabled\n"); - ieee80211_runtask(ic, &sc->sc_radiotask); - return; - } - - if (sc->sc_tx_timer > 0) { - if (--sc->sc_tx_timer == 0) { - device_printf(sc->sc_dev,"device timeout\n"); - ifp->if_oerrors++; - ieee80211_runtask(ic, &sc->sc_restarttask); - } - } - if (sc->sc_scan_timer > 0) { - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - if (--sc->sc_scan_timer == 0 && vap != NULL) { - device_printf(sc->sc_dev,"scan timeout\n"); - ieee80211_cancel_scan(vap); - ieee80211_runtask(ic, &sc->sc_restarttask); - } - } + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - if (ifp->if_drv_flags & IFF_DRV_RUNNING) - callout_reset(&sc->watchdog_to, hz, wpi_watchdog, sc); -} + DPRINTF(sc, WPI_DEBUG_TRACE, TRACE_STR_DOING, __func__); -#ifdef WPI_DEBUG -static const char *wpi_cmd_str(int cmd) -{ - switch (cmd) { - case WPI_DISABLE_CMD: return "WPI_DISABLE_CMD"; - case WPI_CMD_CONFIGURE: return "WPI_CMD_CONFIGURE"; - case WPI_CMD_ASSOCIATE: return "WPI_CMD_ASSOCIATE"; - case WPI_CMD_SET_WME: return "WPI_CMD_SET_WME"; - case WPI_CMD_TSF: return "WPI_CMD_TSF"; - case WPI_CMD_ADD_NODE: return "WPI_CMD_ADD_NODE"; - case WPI_CMD_TX_DATA: return "WPI_CMD_TX_DATA"; - case WPI_CMD_MRR_SETUP: return "WPI_CMD_MRR_SETUP"; - case WPI_CMD_SET_LED: return "WPI_CMD_SET_LED"; - case WPI_CMD_SET_POWER_MODE: return "WPI_CMD_SET_POWER_MODE"; - case WPI_CMD_SCAN: return "WPI_CMD_SCAN"; - case WPI_CMD_SET_BEACON:return "WPI_CMD_SET_BEACON"; - case WPI_CMD_TXPOWER: return "WPI_CMD_TXPOWER"; - case WPI_CMD_BLUETOOTH: return "WPI_CMD_BLUETOOTH"; + if (vap != NULL && (ic->ic_flags & IEEE80211_F_SCAN)) + ieee80211_cancel_scan(vap); - default: - KASSERT(1, ("Unknown Command: %d\n", cmd)); - return "UNKNOWN CMD"; /* Make the compiler happy */ - } + wpi_stop(sc); + if (vap != NULL) + ieee80211_stop(vap); + wpi_init(sc); + if (vap != NULL) + ieee80211_init(vap); } -#endif - -MODULE_DEPEND(wpi, pci, 1, 1, 1); -MODULE_DEPEND(wpi, wlan, 1, 1, 1); -MODULE_DEPEND(wpi, firmware, 1, 1, 1); diff --git a/sys/dev/wpi/if_wpi_debug.h b/sys/dev/wpi/if_wpi_debug.h new file mode 100644 index 0000000..6b78ace --- /dev/null +++ b/sys/dev/wpi/if_wpi_debug.h @@ -0,0 +1,143 @@ +/*- + * Copyright (c) 2006,2007 + * Damien Bergamini <damien.bergamini@free.fr> + * Benjamin Close <Benjamin.Close@clearchain.com> + * + * 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. + * + * $FreeBSD$ + */ + +#ifndef __IF_WPI_DEBUG_H__ +#define __IF_WPI_DEBUG_H__ + +#ifdef WPI_DEBUG +enum { + WPI_DEBUG_XMIT = 0x00000001, /* basic xmit operation */ + WPI_DEBUG_RECV = 0x00000002, /* basic recv operation */ + WPI_DEBUG_STATE = 0x00000004, /* 802.11 state transitions */ + WPI_DEBUG_HW = 0x00000008, /* Stage 1 (eeprom) debugging */ + WPI_DEBUG_RESET = 0x00000010, /* reset processing */ + WPI_DEBUG_FIRMWARE = 0x00000020, /* firmware(9) loading debug */ + WPI_DEBUG_BEACON = 0x00000040, /* beacon handling */ + WPI_DEBUG_WATCHDOG = 0x00000080, /* watchdog timeout */ + WPI_DEBUG_INTR = 0x00000100, /* ISR */ + WPI_DEBUG_SCAN = 0x00000200, /* Scan related operations */ + WPI_DEBUG_NOTIFY = 0x00000400, /* State 2 Notif intr debug */ + WPI_DEBUG_TEMP = 0x00000800, /* TXPower/Temp Calibration */ + WPI_DEBUG_CMD = 0x00001000, /* cmd submission */ + WPI_DEBUG_TRACE = 0x00002000, /* Print begin and start driver function */ + WPI_DEBUG_PWRSAVE = 0x00004000, /* Power save operations */ + WPI_DEBUG_EEPROM = 0x00008000, /* EEPROM info */ + WPI_DEBUG_NODE = 0x00010000, /* node addition/removal */ + WPI_DEBUG_KEY = 0x00020000, /* node key management */ + WPI_DEBUG_EDCA = 0x00040000, /* WME info */ + WPI_DEBUG_REGISTER = 0x00080000, /* print chipset register */ + WPI_DEBUG_BMISS = 0x00100000, /* print number of missed beacons */ + WPI_DEBUG_ANY = 0xffffffff +}; + +#define DPRINTF(sc, m, ...) do { \ + if (sc->sc_debug & (m)) \ + printf(__VA_ARGS__); \ +} while (0) + +#define TRACE_STR_BEGIN "->%s: begin\n" +#define TRACE_STR_DOING "->Doing %s\n" +#define TRACE_STR_END "->%s: end\n" +#define TRACE_STR_END_ERR "->%s: end in error\n" + +#define WPI_DESC(x) case x: return #x + +static const char *wpi_cmd_str(int cmd) +{ + switch (cmd) { + /* Notifications. */ + WPI_DESC(WPI_UC_READY); + WPI_DESC(WPI_RX_DONE); + WPI_DESC(WPI_START_SCAN); + WPI_DESC(WPI_SCAN_RESULTS); + WPI_DESC(WPI_STOP_SCAN); + WPI_DESC(WPI_BEACON_SENT); + WPI_DESC(WPI_RX_STATISTICS); + WPI_DESC(WPI_BEACON_STATISTICS); + WPI_DESC(WPI_STATE_CHANGED); + WPI_DESC(WPI_BEACON_MISSED); + + /* Command notifications. */ + WPI_DESC(WPI_CMD_RXON); + WPI_DESC(WPI_CMD_RXON_ASSOC); + WPI_DESC(WPI_CMD_EDCA_PARAMS); + WPI_DESC(WPI_CMD_TIMING); + WPI_DESC(WPI_CMD_ADD_NODE); + WPI_DESC(WPI_CMD_DEL_NODE); + WPI_DESC(WPI_CMD_TX_DATA); + WPI_DESC(WPI_CMD_MRR_SETUP); + WPI_DESC(WPI_CMD_SET_LED); + WPI_DESC(WPI_CMD_SET_POWER_MODE); + WPI_DESC(WPI_CMD_SCAN); + WPI_DESC(WPI_CMD_SCAN_ABORT); + WPI_DESC(WPI_CMD_SET_BEACON); + WPI_DESC(WPI_CMD_TXPOWER); + WPI_DESC(WPI_CMD_BT_COEX); + + default: + return "UNKNOWN CMD"; + } +} + +/* + * Translate CSR code to string + */ +static const char *wpi_get_csr_string(size_t csr) +{ + switch (csr) { + WPI_DESC(WPI_HW_IF_CONFIG); + WPI_DESC(WPI_INT); + WPI_DESC(WPI_INT_MASK); + WPI_DESC(WPI_FH_INT); + WPI_DESC(WPI_GPIO_IN); + WPI_DESC(WPI_RESET); + WPI_DESC(WPI_GP_CNTRL); + WPI_DESC(WPI_EEPROM); + WPI_DESC(WPI_EEPROM_GP); + WPI_DESC(WPI_GIO); + WPI_DESC(WPI_UCODE_GP1); + WPI_DESC(WPI_UCODE_GP2); + WPI_DESC(WPI_GIO_CHICKEN); + WPI_DESC(WPI_ANA_PLL); + WPI_DESC(WPI_DBG_HPET_MEM); + default: + KASSERT(0, ("Unknown CSR: %d\n", csr)); + return "UNKNOWN CSR"; + } +} + +static const char *wpi_get_prph_string(size_t prph) +{ + switch (prph) { + WPI_DESC(WPI_APMG_CLK_CTRL); + WPI_DESC(WPI_APMG_PS); + WPI_DESC(WPI_APMG_PCI_STT); + WPI_DESC(WPI_APMG_RFKILL); + default: + KASSERT(0, ("Unknown register: %d\n", prph)); + return "UNKNOWN PRPH"; + } +} + +#else +#define DPRINTF(sc, m, ...) do { (void) sc; } while (0) +#endif + +#endif /* __IF_WPI_DEBUG_H__ */ diff --git a/sys/dev/wpi/if_wpireg.h b/sys/dev/wpi/if_wpireg.h index df71b3d..b0aa35c 100644 --- a/sys/dev/wpi/if_wpireg.h +++ b/sys/dev/wpi/if_wpireg.h @@ -18,181 +18,264 @@ */ #define WPI_TX_RING_COUNT 256 -#define WPI_CMD_RING_COUNT 256 -#define WPI_RX_RING_COUNT 64 +#define WPI_TX_RING_LOMARK 192 +#define WPI_TX_RING_HIMARK 224 + +#ifdef DIAGNOSTIC +#define WPI_RX_RING_COUNT_LOG 8 +#else +#define WPI_RX_RING_COUNT_LOG 6 +#endif + +#define WPI_RX_RING_COUNT (1 << WPI_RX_RING_COUNT_LOG) + +#define WPI_NTXQUEUES 8 +#define WPI_DRV_NTXQUEUES 5 +#define WPI_CMD_QUEUE_NUM 4 + +#define WPI_NDMACHNLS 6 + +/* Maximum scatter/gather. */ +#define WPI_MAX_SCATTER 4 /* * Rings must be aligned on a 16K boundary. */ #define WPI_RING_DMA_ALIGN 0x4000 -/* maximum scatter/gather */ -#define WPI_MAX_SCATTER 4 - -/* maximum Rx buffer size */ +/* Maximum Rx buffer size. */ #define WPI_RBUF_SIZE ( 3 * 1024 ) /* XXX 3000 but must be aligned */ /* * Control and status registers. */ -#define WPI_HWCONFIG 0x000 -#define WPI_INTR 0x008 -#define WPI_MASK 0x00c -#define WPI_INTR_STATUS 0x010 -#define WPI_GPIO_STATUS 0x018 +#define WPI_HW_IF_CONFIG 0x000 +#define WPI_INT 0x008 +#define WPI_INT_MASK 0x00c +#define WPI_FH_INT 0x010 +#define WPI_GPIO_IN 0x018 #define WPI_RESET 0x020 -#define WPI_GPIO_CTL 0x024 -#define WPI_EEPROM_CTL 0x02c -#define WPI_EEPROM_STATUS 0x030 -#define WPI_UCODE_SET 0x058 -#define WPI_UCODE_CLR 0x05c -#define WPI_TEMPERATURE 0x060 -#define WPI_CHICKEN 0x100 -#define WPI_PLL_CTL 0x20c -#define WPI_WRITE_MEM_ADDR 0x444 -#define WPI_READ_MEM_ADDR 0x448 -#define WPI_WRITE_MEM_DATA 0x44c -#define WPI_READ_MEM_DATA 0x450 -#define WPI_TX_WIDX 0x460 -#define WPI_TX_CTL(qid) (0x940 + (qid) * 8) -#define WPI_TX_BASE(qid) (0x944 + (qid) * 8) -#define WPI_TX_DESC(qid) (0x980 + (qid) * 80) -#define WPI_RX_CONFIG 0xc00 -#define WPI_RX_BASE 0xc04 -#define WPI_RX_WIDX 0xc20 -#define WPI_RX_RIDX_PTR 0xc24 -#define WPI_RX_CTL 0xcc0 -#define WPI_RX_STATUS 0xcc4 -#define WPI_TX_CONFIG(qid) (0xd00 + (qid) * 32) -#define WPI_TX_CREDIT(qid) (0xd04 + (qid) * 32) -#define WPI_TX_STATE(qid) (0xd08 + (qid) * 32) -#define WPI_TX_BASE_PTR 0xe80 -#define WPI_MSG_CONFIG 0xe88 -#define WPI_TX_STATUS 0xe90 +#define WPI_GP_CNTRL 0x024 +#define WPI_EEPROM 0x02c +#define WPI_EEPROM_GP 0x030 +#define WPI_GIO 0x03c +#define WPI_UCODE_GP1 0x054 +#define WPI_UCODE_GP1_SET 0x058 +#define WPI_UCODE_GP1_CLR 0x05c +#define WPI_UCODE_GP2 0x060 +#define WPI_GIO_CHICKEN 0x100 +#define WPI_ANA_PLL 0x20c +#define WPI_DBG_HPET_MEM 0x240 +#define WPI_MEM_RADDR 0x40c +#define WPI_MEM_WADDR 0x410 +#define WPI_MEM_WDATA 0x418 +#define WPI_MEM_RDATA 0x41c +#define WPI_PRPH_WADDR 0x444 +#define WPI_PRPH_RADDR 0x448 +#define WPI_PRPH_WDATA 0x44c +#define WPI_PRPH_RDATA 0x450 +#define WPI_HBUS_TARG_WRPTR 0x460 + +/* + * Flow-Handler registers. + */ +#define WPI_FH_CBBC_CTRL(qid) (0x940 + (qid) * 8) +#define WPI_FH_CBBC_BASE(qid) (0x944 + (qid) * 8) +#define WPI_FH_RX_CONFIG 0xc00 +#define WPI_FH_RX_BASE 0xc04 +#define WPI_FH_RX_WPTR 0xc20 +#define WPI_FH_RX_RPTR_ADDR 0xc24 +#define WPI_FH_RSSR_TBL 0xcc0 +#define WPI_FH_RX_STATUS 0xcc4 +#define WPI_FH_TX_CONFIG(qid) (0xd00 + (qid) * 32) +#define WPI_FH_TX_BASE 0xe80 +#define WPI_FH_MSG_CONFIG 0xe88 +#define WPI_FH_TX_STATUS 0xe90 /* * NIC internal memory offsets. */ -#define WPI_MEM_MODE 0x2e00 -#define WPI_MEM_RA 0x2e04 -#define WPI_MEM_TXCFG 0x2e10 -#define WPI_MEM_MAGIC4 0x2e14 -#define WPI_MEM_MAGIC5 0x2e20 -#define WPI_MEM_BYPASS1 0x2e2c -#define WPI_MEM_BYPASS2 0x2e30 -#define WPI_MEM_CLOCK1 0x3004 -#define WPI_MEM_CLOCK2 0x3008 -#define WPI_MEM_POWER 0x300c -#define WPI_MEM_PCIDEV 0x3010 -#define WPI_MEM_HW_RADIO_OFF 0x3014 -#define WPI_MEM_UCODE_CTL 0x3400 -#define WPI_MEM_UCODE_SRC 0x3404 -#define WPI_MEM_UCODE_DST 0x3408 -#define WPI_MEM_UCODE_SIZE 0x340c -#define WPI_MEM_UCODE_BASE 0x3800 - -#define WPI_MEM_TEXT_BASE 0x3490 -#define WPI_MEM_TEXT_SIZE 0x3494 -#define WPI_MEM_DATA_BASE 0x3498 -#define WPI_MEM_DATA_SIZE 0x349c - - -/* possible flags for register WPI_HWCONFIG */ -#define WPI_HW_ALM_MB (1 << 8) -#define WPI_HW_ALM_MM (1 << 9) -#define WPI_HW_SKU_MRC (1 << 10) -#define WPI_HW_REV_D (1 << 11) -#define WPI_HW_TYPE_B (1 << 12) - -/* possible flags for registers WPI_READ_MEM_ADDR/WPI_WRITE_MEM_ADDR */ -#define WPI_MEM_4 ((sizeof (uint32_t) - 1) << 24) - -/* possible values for WPI_MEM_UCODE_DST */ -#define WPI_FW_TEXT 0x00000000 - -/* possible flags for WPI_GPIO_STATUS */ -#define WPI_POWERED (1 << 9) - -/* possible flags for register WPI_RESET */ -#define WPI_NEVO_RESET (1 << 0) -#define WPI_SW_RESET (1 << 7) -#define WPI_MASTER_DISABLED (1 << 8) -#define WPI_STOP_MASTER (1 << 9) - -/* possible flags for register WPI_GPIO_CTL */ -#define WPI_GPIO_CLOCK (1 << 0) -#define WPI_GPIO_INIT (1 << 2) -#define WPI_GPIO_MAC (1 << 3) -#define WPI_GPIO_SLEEP (1 << 4) -#define WPI_GPIO_PWR_STATUS 0x07000000 -#define WPI_GPIO_PWR_SLEEP (4 << 24) - -/* possible flags for register WPI_CHICKEN */ -#define WPI_CHICKEN_RXNOLOS (1 << 23) - -/* possible flags for register WPI_PLL_CTL */ -#define WPI_PLL_INIT (1 << 24) - -/* possible flags for register WPI_UCODE_CLR */ -#define WPI_RADIO_OFF (1 << 1) -#define WPI_DISABLE_CMD (1 << 2) - -/* possible flags for WPI_RX_STATUS */ -#define WPI_RX_IDLE (1 << 24) - -/* possible flags for register WPI_UC_CTL */ -#define WPI_UC_ENABLE (1 << 30) -#define WPI_UC_RUN (1U << 31) - -/* possible flags for register WPI_INTR_CSR */ -#define WPI_ALIVE_INTR (1 << 0) -#define WPI_WAKEUP_INTR (1 << 1) -#define WPI_SW_ERROR (1 << 25) -#define WPI_TX_INTR (1 << 27) -#define WPI_HW_ERROR (1 << 29) -#define WPI_RX_INTR (1U << 31) - -#define WPI_INTR_MASK \ - (WPI_SW_ERROR | WPI_HW_ERROR | WPI_TX_INTR | WPI_RX_INTR | \ - WPI_ALIVE_INTR | WPI_WAKEUP_INTR) - -/* possible flags for register WPI_TX_STATUS */ -#define WPI_TX_IDLE(qid) (1 << ((qid) + 24) | 1 << ((qid) + 16)) - -/* possible flags for register WPI_EEPROM_CTL */ -#define WPI_EEPROM_READY (1 << 0) - -/* possible flags for register WPI_EEPROM_STATUS */ +#define WPI_ALM_SCHED_MODE 0x2e00 +#define WPI_ALM_SCHED_ARASTAT 0x2e04 +#define WPI_ALM_SCHED_TXFACT 0x2e10 +#define WPI_ALM_SCHED_TXF4MF 0x2e14 +#define WPI_ALM_SCHED_TXF5MF 0x2e20 +#define WPI_ALM_SCHED_SBYPASS_MODE1 0x2e2c +#define WPI_ALM_SCHED_SBYPASS_MODE2 0x2e30 +#define WPI_APMG_CLK_CTRL 0x3000 +#define WPI_APMG_CLK_EN 0x3004 +#define WPI_APMG_CLK_DIS 0x3008 +#define WPI_APMG_PS 0x300c +#define WPI_APMG_PCI_STT 0x3010 +#define WPI_APMG_RFKILL 0x3014 +#define WPI_BSM_WR_CTRL 0x3400 +#define WPI_BSM_WR_MEM_SRC 0x3404 +#define WPI_BSM_WR_MEM_DST 0x3408 +#define WPI_BSM_WR_DWCOUNT 0x340c +#define WPI_BSM_DRAM_TEXT_ADDR 0x3490 +#define WPI_BSM_DRAM_TEXT_SIZE 0x3494 +#define WPI_BSM_DRAM_DATA_ADDR 0x3498 +#define WPI_BSM_DRAM_DATA_SIZE 0x349c +#define WPI_BSM_SRAM_BASE 0x3800 + + +/* Possible flags for register WPI_HW_IF_CONFIG. */ +#define WPI_HW_IF_CONFIG_ALM_MB (1 << 8) +#define WPI_HW_IF_CONFIG_ALM_MM (1 << 9) +#define WPI_HW_IF_CONFIG_SKU_MRC (1 << 10) +#define WPI_HW_IF_CONFIG_REV_D (1 << 11) +#define WPI_HW_IF_CONFIG_TYPE_B (1 << 12) + +/* Possible flags for registers WPI_PRPH_RADDR/WPI_PRPH_WADDR. */ +#define WPI_PRPH_DWORD ((sizeof (uint32_t) - 1) << 24) + +/* Possible values for WPI_BSM_WR_MEM_DST. */ +#define WPI_FW_TEXT_BASE 0x00000000 +#define WPI_FW_DATA_BASE 0x00800000 + +/* Possible flags for WPI_GPIO_IN. */ +#define WPI_GPIO_IN_VMAIN (1 << 9) + +/* Possible flags for register WPI_RESET. */ +#define WPI_RESET_NEVO (1 << 0) +#define WPI_RESET_SW (1 << 7) +#define WPI_RESET_MASTER_DISABLED (1 << 8) +#define WPI_RESET_STOP_MASTER (1 << 9) + +/* Possible flags for register WPI_GP_CNTRL. */ +#define WPI_GP_CNTRL_MAC_ACCESS_ENA (1 << 0) +#define WPI_GP_CNTRL_MAC_CLOCK_READY (1 << 0) +#define WPI_GP_CNTRL_INIT_DONE (1 << 2) +#define WPI_GP_CNTRL_MAC_ACCESS_REQ (1 << 3) +#define WPI_GP_CNTRL_SLEEP (1 << 4) +#define WPI_GP_CNTRL_PS_MASK (7 << 24) +#define WPI_GP_CNTRL_MAC_PS (4 << 24) +#define WPI_GP_CNTRL_RFKILL (1 << 27) + +/* Possible flags for register WPI_GIO_CHICKEN. */ +#define WPI_GIO_CHICKEN_L1A_NO_L0S_RX (1 << 23) +#define WPI_GIO_CHICKEN_DIS_L0S_TIMER (1 << 29) + +/* Possible flags for register WPI_GIO. */ +#define WPI_GIO_L0S_ENA (1 << 1) + +/* Possible flags for register WPI_FH_RX_CONFIG. */ +#define WPI_FH_RX_CONFIG_DMA_ENA (1U << 31) +#define WPI_FH_RX_CONFIG_RDRBD_ENA (1 << 29) +#define WPI_FH_RX_CONFIG_WRSTATUS_ENA (1 << 27) +#define WPI_FH_RX_CONFIG_MAXFRAG (1 << 24) +#define WPI_FH_RX_CONFIG_NRBD(x) ((x) << 20) +#define WPI_FH_RX_CONFIG_IRQ_DST_HOST (1 << 12) +#define WPI_FH_RX_CONFIG_IRQ_TIMEOUT(x) ((x) << 4) + +/* Possible flags for register WPI_ANA_PLL. */ +#define WPI_ANA_PLL_INIT (1 << 24) + +/* Possible flags for register WPI_UCODE_GP1*. */ +#define WPI_UCODE_GP1_MAC_SLEEP (1 << 0) +#define WPI_UCODE_GP1_RFKILL (1 << 1) +#define WPI_UCODE_GP1_CMD_BLOCKED (1 << 2) + +/* Possible flags for register WPI_FH_RX_STATUS. */ +#define WPI_FH_RX_STATUS_IDLE (1 << 24) + +/* Possible flags for register WPI_BSM_WR_CTRL. */ +#define WPI_BSM_WR_CTRL_START_EN (1 << 30) +#define WPI_BSM_WR_CTRL_START (1U << 31) + +/* Possible flags for register WPI_INT. */ +#define WPI_INT_ALIVE (1 << 0) +#define WPI_INT_WAKEUP (1 << 1) +#define WPI_INT_SW_RX (1 << 3) +#define WPI_INT_SW_ERR (1 << 25) +#define WPI_INT_FH_TX (1 << 27) +#define WPI_INT_HW_ERR (1 << 29) +#define WPI_INT_FH_RX (1U << 31) + +/* Shortcut. */ +#define WPI_INT_MASK_DEF \ + (WPI_INT_SW_ERR | WPI_INT_HW_ERR | WPI_INT_FH_TX | \ + WPI_INT_FH_RX | WPI_INT_ALIVE | WPI_INT_WAKEUP | \ + WPI_INT_SW_RX) + +/* Possible flags for register WPI_FH_INT. */ +#define WPI_FH_INT_RX_CHNL(x) (1 << ((x) + 16)) +#define WPI_FH_INT_HI_PRIOR (1 << 30) +/* Shortcuts for the above. */ +#define WPI_FH_INT_RX \ + (WPI_FH_INT_RX_CHNL(0) | \ + WPI_FH_INT_RX_CHNL(1) | \ + WPI_FH_INT_RX_CHNL(2) | \ + WPI_FH_INT_HI_PRIOR) + +/* Possible flags for register WPI_FH_TX_STATUS. */ +#define WPI_FH_TX_STATUS_IDLE(qid) \ + (1 << ((qid) + 24) | 1 << ((qid) + 16)) + +/* Possible flags for register WPI_EEPROM. */ +#define WPI_EEPROM_READ_VALID (1 << 0) + +/* Possible flags for register WPI_EEPROM_GP. */ #define WPI_EEPROM_VERSION 0x00000007 -#define WPI_EEPROM_LOCKED 0x00000180 +#define WPI_EEPROM_GP_IF_OWNER 0x00000180 + +/* Possible flags for register WPI_APMG_PS. */ +#define WPI_APMG_PS_PWR_SRC_MASK (3 << 24) +/* Possible flags for registers WPI_APMG_CLK_*. */ +#define WPI_APMG_CLK_CTRL_DMA_CLK_RQT (1 << 9) +#define WPI_APMG_CLK_CTRL_BSM_CLK_RQT (1 << 11) + +/* Possible flags for register WPI_APMG_PCI_STT. */ +#define WPI_APMG_PCI_STT_L1A_DIS (1 << 11) struct wpi_shared { - uint32_t txbase[8]; + uint32_t txbase[WPI_NTXQUEUES]; uint32_t next; uint32_t reserved[2]; } __packed; #define WPI_MAX_SEG_LEN 65520 struct wpi_tx_desc { - uint32_t flags; + uint8_t reserved1[3]; + uint8_t nsegs; #define WPI_PAD32(x) (roundup2(x, 4) - (x)) struct { uint32_t addr; uint32_t len; - } __attribute__((__packed__)) segs[WPI_MAX_SCATTER]; - uint8_t reserved[28]; + } __packed segs[WPI_MAX_SCATTER]; + uint8_t reserved2[28]; } __packed; struct wpi_tx_stat { - uint8_t nrts; - uint8_t ntries; - uint8_t nkill; + uint8_t rtsfailcnt; + uint8_t ackfailcnt; + uint8_t btkillcnt; uint8_t rate; uint32_t duration; uint32_t status; +#define WPI_TX_STATUS_SUCCESS 0x01 +#define WPI_TX_STATUS_DIRECT_DONE 0x02 +#define WPI_TX_STATUS_FAIL 0x80 +#define WPI_TX_STATUS_FAIL_SHORT_LIMIT 0x82 +#define WPI_TX_STATUS_FAIL_LONG_LIMIT 0x83 +#define WPI_TX_STATUS_FAIL_FIFO_UNDERRUN 0x84 +#define WPI_TX_STATUS_FAIL_MGMNT_ABORT 0x85 +#define WPI_TX_STATUS_FAIL_NEXT_FRAG 0x86 +#define WPI_TX_STATUS_FAIL_LIFE_EXPIRE 0x87 +#define WPI_TX_STATUS_FAIL_NODE_PS 0x88 +#define WPI_TX_STATUS_FAIL_ABORTED 0x89 +#define WPI_TX_STATUS_FAIL_BT_RETRY 0x8a +#define WPI_TX_STATUS_FAIL_NODE_INVALID 0x8b +#define WPI_TX_STATUS_FAIL_FRAG_DROPPED 0x8c +#define WPI_TX_STATUS_FAIL_TID_DISABLE 0x8d +#define WPI_TX_STATUS_FAIL_FRAME_FLUSHED 0x8e +#define WPI_TX_STATUS_FAIL_INSUFFICIENT_CF_POLL 0x8f +#define WPI_TX_STATUS_FAIL_TX_LOCKED 0x90 +#define WPI_TX_STATUS_FAIL_NO_BEACON_ON_RADAR 0x91 + } __packed; struct wpi_rx_desc { @@ -204,21 +287,27 @@ struct wpi_rx_desc { #define WPI_START_SCAN 130 #define WPI_SCAN_RESULTS 131 #define WPI_STOP_SCAN 132 +#define WPI_BEACON_SENT 144 +#define WPI_RX_STATISTICS 156 +#define WPI_BEACON_STATISTICS 157 #define WPI_STATE_CHANGED 161 -#define WPI_MISSED_BEACON 162 +#define WPI_BEACON_MISSED 162 uint8_t flags; uint8_t idx; uint8_t qid; } __packed; +#define WPI_RX_DESC_QID_MSK 0x07 +#define WPI_UNSOLICITED_RX_NOTIF 0x80 + struct wpi_rx_stat { uint8_t len; #define WPI_STAT_MAXLEN 20 uint8_t id; uint8_t rssi; /* received signal strength */ -#define WPI_RSSI_OFFSET 95 +#define WPI_RSSI_OFFSET -95 uint8_t agc; /* access gain control */ uint16_t signal; @@ -228,8 +317,10 @@ struct wpi_rx_stat { struct wpi_rx_head { uint16_t chan; uint16_t flags; +#define WPI_STAT_FLAG_SHPREAMBLE (1 << 2) + uint8_t reserved; - uint8_t rate; + uint8_t plcp; uint16_t len; } __packed; @@ -239,39 +330,47 @@ struct wpi_rx_tail { #define WPI_RX_NO_OVFL_ERR (1 << 1) /* shortcut for the above */ #define WPI_RX_NOERROR (WPI_RX_NO_CRC_ERR | WPI_RX_NO_OVFL_ERR) +#define WPI_RX_CIPHER_MASK (7 << 8) +#define WPI_RX_CIPHER_CCMP (2 << 8) +#define WPI_RX_DECRYPT_MASK (3 << 11) +#define WPI_RX_DECRYPT_OK (3 << 11) + uint64_t tstamp; uint32_t tbeacon; } __packed; struct wpi_tx_cmd { uint8_t code; -#define WPI_CMD_CONFIGURE 16 -#define WPI_CMD_ASSOCIATE 17 -#define WPI_CMD_SET_WME 19 -#define WPI_CMD_TSF 20 +#define WPI_CMD_RXON 16 +#define WPI_CMD_RXON_ASSOC 17 +#define WPI_CMD_EDCA_PARAMS 19 +#define WPI_CMD_TIMING 20 #define WPI_CMD_ADD_NODE 24 +#define WPI_CMD_DEL_NODE 25 #define WPI_CMD_TX_DATA 28 #define WPI_CMD_MRR_SETUP 71 #define WPI_CMD_SET_LED 72 #define WPI_CMD_SET_POWER_MODE 119 #define WPI_CMD_SCAN 128 +#define WPI_CMD_SCAN_ABORT 129 #define WPI_CMD_SET_BEACON 145 #define WPI_CMD_TXPOWER 151 -#define WPI_CMD_BLUETOOTH 155 +#define WPI_CMD_BT_COEX 155 +#define WPI_CMD_GET_STATISTICS 156 uint8_t flags; uint8_t idx; uint8_t qid; - uint8_t data[360]; + uint8_t data[124]; } __packed; -/* structure for WPI_CMD_CONFIGURE */ -struct wpi_config { +/* Structure for command WPI_CMD_RXON. */ +struct wpi_rxon { uint8_t myaddr[IEEE80211_ADDR_LEN]; uint16_t reserved1; uint8_t bssid[IEEE80211_ADDR_LEN]; uint16_t reserved2; - uint8_t wlap_bssid_addr[6]; + uint8_t wlap[IEEE80211_ADDR_LEN]; uint16_t reserved3; uint8_t mode; #define WPI_MODE_HOSTAP 1 @@ -279,21 +378,22 @@ struct wpi_config { #define WPI_MODE_IBSS 4 #define WPI_MODE_MONITOR 6 - uint8_t air_propogation; + uint8_t air; uint16_t reserved4; uint8_t ofdm_mask; uint8_t cck_mask; uint16_t associd; uint32_t flags; -#define WPI_CONFIG_24GHZ (1 << 0) -#define WPI_CONFIG_CCK (1 << 1) -#define WPI_CONFIG_AUTO (1 << 2) -#define WPI_CONFIG_SHSLOT (1 << 4) -#define WPI_CONFIG_SHPREAMBLE (1 << 5) -#define WPI_CONFIG_NODIVERSITY (1 << 7) -#define WPI_CONFIG_ANTENNA_A (1 << 8) -#define WPI_CONFIG_ANTENNA_B (1 << 9) -#define WPI_CONFIG_TSF (1 << 15) +#define WPI_RXON_24GHZ (1 << 0) +#define WPI_RXON_CCK (1 << 1) +#define WPI_RXON_AUTO (1 << 2) +#define WPI_RXON_SHSLOT (1 << 4) +#define WPI_RXON_SHPREAMBLE (1 << 5) +#define WPI_RXON_NODIVERSITY (1 << 7) +#define WPI_RXON_ANTENNA_A (1 << 8) +#define WPI_RXON_ANTENNA_B (1 << 9) +#define WPI_RXON_TSF (1 << 15) +#define WPI_RXON_CTS_TO_SELF (1 << 30) uint32_t filter; #define WPI_FILTER_PROMISC (1 << 0) @@ -302,12 +402,13 @@ struct wpi_config { #define WPI_FILTER_NODECRYPT (1 << 3) #define WPI_FILTER_BSS (1 << 5) #define WPI_FILTER_BEACON (1 << 6) +#define WPI_FILTER_ASSOC (1 << 7) /* Accept associaton requests. */ uint8_t chan; - uint16_t reserved6; + uint16_t reserved5; } __packed; -/* structure for command WPI_CMD_ASSOCIATE */ +/* Structure for command WPI_CMD_RXON_ASSOC. */ struct wpi_assoc { uint32_t flags; uint32_t filter; @@ -316,20 +417,22 @@ struct wpi_assoc { uint16_t reserved; } __packed; -/* structure for command WPI_CMD_SET_WME */ -struct wpi_wme_setup { +/* Structure for command WPI_CMD_EDCA_PARAMS. */ +struct wpi_edca_params { uint32_t flags; +#define WPI_EDCA_UPDATE (1 << 0) + struct { uint16_t cwmin; uint16_t cwmax; uint8_t aifsn; uint8_t reserved; - uint16_t txop; + uint16_t txoplimit; } __packed ac[WME_NUM_AC]; } __packed; -/* structure for command WPI_CMD_TSF */ -struct wpi_cmd_tsf { +/* Structure for command WPI_CMD_TIMING. */ +struct wpi_cmd_timing { uint64_t tstamp; uint16_t bintval; uint16_t atim; @@ -338,77 +441,102 @@ struct wpi_cmd_tsf { uint16_t reserved; } __packed; -/* structure for WPI_CMD_ADD_NODE */ +/* Structure for command WPI_CMD_ADD_NODE. */ struct wpi_node_info { uint8_t control; -#define WPI_NODE_UPDATE (1 << 0) +#define WPI_NODE_UPDATE (1 << 0) uint8_t reserved1[3]; - uint8_t bssid[IEEE80211_ADDR_LEN]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; uint16_t reserved2; uint8_t id; #define WPI_ID_BSS 0 +#define WPI_ID_IBSS_MIN 2 +#define WPI_ID_IBSS_MAX 23 #define WPI_ID_BROADCAST 24 +#define WPI_ID_UNDEFINED (uint8_t)-1 uint8_t flags; +#define WPI_FLAG_KEY_SET (1 << 0) + uint16_t reserved3; - uint16_t key_flags; - uint8_t tkip; + uint16_t kflags; +#define WPI_KFLAG_CCMP (1 << 1) +#define WPI_KFLAG_KID(kid) ((kid) << 8) +#define WPI_KFLAG_MULTICAST (1 << 14) + + uint8_t tsc2; uint8_t reserved4; uint16_t ttak[5]; uint16_t reserved5; uint8_t key[IEEE80211_KEYBUF_SIZE]; uint32_t action; -#define WPI_ACTION_SET_RATE 4 +#define WPI_ACTION_SET_RATE (1 << 2) + uint32_t mask; uint16_t tid; - uint8_t rate; + uint8_t plcp; uint8_t antenna; -#define WPI_ANTENNA_A (1<<6) -#define WPI_ANTENNA_B (1<<7) -#define WPI_ANTENNA_BOTH (WPI_ANTENNA_A|WPI_ANTENNA_B) +#define WPI_ANTENNA_A (1 << 6) +#define WPI_ANTENNA_B (1 << 7) +#define WPI_ANTENNA_BOTH (WPI_ANTENNA_A | WPI_ANTENNA_B) + uint8_t add_imm; uint8_t del_imm; uint16_t add_imm_start; } __packed; -/* structure for command WPI_CMD_TX_DATA */ +/* Structure for command WPI_CMD_DEL_NODE. */ +struct wpi_cmd_del_node { + uint8_t count; + uint8_t reserved1[3]; + uint8_t macaddr[IEEE80211_ADDR_LEN]; + uint16_t reserved2; +} __packed; + +/* Structure for command WPI_CMD_TX_DATA. */ struct wpi_cmd_data { uint16_t len; uint16_t lnext; uint32_t flags; #define WPI_TX_NEED_RTS (1 << 1) -#define WPI_TX_NEED_CTS (1 << 2) +#define WPI_TX_NEED_CTS (1 << 2) #define WPI_TX_NEED_ACK (1 << 3) #define WPI_TX_FULL_TXOP (1 << 7) -#define WPI_TX_BT_DISABLE (1 << 12) /* bluetooth coexistence */ +#define WPI_TX_BT_DISABLE (1 << 12) /* bluetooth coexistence */ #define WPI_TX_AUTO_SEQ (1 << 13) +#define WPI_TX_MORE_FRAG (1 << 14) #define WPI_TX_INSERT_TSTAMP (1 << 16) - uint8_t rate; + uint8_t plcp; uint8_t id; uint8_t tid; uint8_t security; +#define WPI_CIPHER_WEP 1 +#define WPI_CIPHER_CCMP 2 +#define WPI_CIPHER_TKIP 3 +#define WPI_CIPHER_WEP104 9 + uint8_t key[IEEE80211_KEYBUF_SIZE]; uint8_t tkip[IEEE80211_WEP_MICLEN]; uint32_t fnext; uint32_t lifetime; #define WPI_LIFETIME_INFINITE 0xffffffff + uint8_t ofdm_mask; uint8_t cck_mask; uint8_t rts_ntries; uint8_t data_ntries; uint16_t timeout; uint16_t txop; - struct ieee80211_frame wh; } __packed; -/* structure for command WPI_CMD_SET_BEACON */ +/* Structure for command WPI_CMD_SET_BEACON. */ struct wpi_cmd_beacon { uint16_t len; uint16_t reserved1; uint32_t flags; /* same as wpi_cmd_data */ - uint8_t rate; + uint8_t plcp; uint8_t id; uint8_t reserved2[30]; uint32_t lifetime; @@ -418,41 +546,35 @@ struct wpi_cmd_beacon { uint16_t tim; uint8_t timsz; uint8_t reserved4; - struct ieee80211_frame wh; } __packed; -/* structure for notification WPI_MISSED_BEACON */ -struct wpi_missed_beacon { - uint32_t consecutive; - uint32_t total; - uint32_t expected; - uint32_t received; +/* Structure for notification WPI_BEACON_MISSED. */ +struct wpi_beacon_missed { + uint32_t consecutive; + uint32_t total; + uint32_t expected; + uint32_t received; } __packed; -/* structure for WPI_CMD_MRR_SETUP */ +/* Structure for command WPI_CMD_MRR_SETUP. */ +#define WPI_RIDX_MAX 11 struct wpi_mrr_setup { - uint8_t which; + uint32_t which; #define WPI_MRR_CTL 0 #define WPI_MRR_DATA 1 - uint8_t reserved[3]; - struct { - uint8_t signal; + uint8_t plcp; uint8_t flags; uint8_t ntries; - uint8_t next; -#define WPI_OFDM6 0 -#define WPI_OFDM54 7 -#define WPI_CCK1 8 -#define WPI_CCK2 9 -#define WPI_CCK11 11 +#define WPI_NTRIES_DEFAULT 2 - } __attribute__((__packed__)) rates[WPI_CCK11 + 1]; + uint8_t next; + } __packed rates[WPI_RIDX_MAX + 1]; } __packed; -/* structure for WPI_CMD_SET_LED */ +/* Structure for command WPI_CMD_SET_LED. */ struct wpi_cmd_led { uint32_t unit; /* multiplier (in usecs) */ uint8_t which; @@ -464,136 +586,133 @@ struct wpi_cmd_led { uint8_t reserved; } __packed; -/* structure for WPI_CMD_SET_POWER_MODE */ -struct wpi_power { - uint32_t flags; -#define WPI_POWER_CAM 0 /* constantly awake mode */ - uint32_t rx_timeout; - uint32_t tx_timeout; - uint32_t sleep[5]; +/* Structure for command WPI_CMD_SET_POWER_MODE. */ +struct wpi_pmgt_cmd { + uint16_t flags; +#define WPI_PS_ALLOW_SLEEP (1 << 0) +#define WPI_PS_NOTIFY (1 << 1) +#define WPI_PS_SLEEP_OVER_DTIM (1 << 2) +#define WPI_PS_PCI_PMGT (1 << 3) + + uint8_t reserved[2]; + uint32_t rxtimeout; + uint32_t txtimeout; + uint32_t intval[5]; +} __packed; + +/* Structures for command WPI_CMD_SCAN. */ +#define WPI_SCAN_MAX_ESSIDS 4 +struct wpi_scan_essid { + uint8_t id; + uint8_t len; + uint8_t data[IEEE80211_NWID_LEN]; } __packed; -/* structure for command WPI_CMD_SCAN */ struct wpi_scan_hdr { uint16_t len; uint8_t reserved1; uint8_t nchan; - uint16_t quiet; - uint16_t threshold; - uint16_t promotion; + uint16_t quiet_time; /* timeout in milliseconds */ +#define WPI_QUIET_TIME_DEFAULT 10 + + uint16_t quiet_threshold; /* min # of packets */ + uint16_t crc_threshold; uint16_t reserved2; - uint32_t maxtimeout; - uint32_t suspend; - uint32_t flags; - uint32_t filter; + uint32_t max_svc; /* background scans */ + uint32_t pause_svc; /* background scans */ +#define WPI_PAUSE_MAX_TIME ((1 << 20) - 1) +#define WPI_PAUSE_SCAN(nbeacons, time) ((nbeacons << 24) | time) -struct { - uint16_t len; - uint16_t lnext; uint32_t flags; - uint8_t rate; - uint8_t id; - uint8_t tid; - uint8_t security; - uint8_t key[IEEE80211_KEYBUF_SIZE]; - uint8_t tkip[IEEE80211_WEP_MICLEN]; - uint32_t fnext; - uint32_t lifetime; - uint8_t ofdm_mask; - uint8_t cck_mask; - uint8_t rts_ntries; - uint8_t data_ntries; - uint16_t timeout; - uint16_t txop; -} tx __attribute__((__packed__)); + uint32_t filter; -#define WPI_SCAN_MAX_ESSIDS 4 - struct { - uint8_t id; - uint8_t esslen; - uint8_t essid[32]; - }scan_essids[WPI_SCAN_MAX_ESSIDS]; - /* followed by probe request body */ - /* followed by nchan x wpi_scan_chan */ + /* Followed by a struct wpi_cmd_data. */ + /* Followed by an array of 4 structs wpi_scan_essid. */ + /* Followed by probe request body. */ + /* Followed by an array of ``nchan'' structs wpi_scan_chan. */ } __packed; struct wpi_scan_chan { uint8_t flags; +#define WPI_CHAN_ACTIVE (1 << 0) +#define WPI_CHAN_NPBREQS(x) (((1 << (x)) - 1) << 1) + uint8_t chan; -#define WPI_CHAN_ACTIVE (1 << 0) -#define WPI_CHAN_DIRECT (1 << 1) - uint8_t gain_radio; - uint8_t gain_dsp; + uint8_t rf_gain; + uint8_t dsp_gain; uint16_t active; /* msecs */ uint16_t passive; /* msecs */ } __packed; -/* structure for WPI_CMD_BLUETOOTH */ -struct wpi_bluetooth { - uint8_t flags; - uint8_t lead; - uint8_t kill; - uint8_t reserved; - uint32_t ack; - uint32_t cts; -} __packed; +#define WPI_SCAN_CRC_TH_DEFAULT htole16(1) +#define WPI_SCAN_CRC_TH_NEVER htole16(0xffff) -/* structure for command WPI_CMD_TXPOWER */ -struct wpi_cmd_txpower { +/* Maximum size of a scan command. */ +#define WPI_SCAN_MAXSZ (MCLBYTES - 4) + +#define WPI_ACTIVE_DWELL_TIME_2GHZ (30) /* all times in msec */ +#define WPI_ACTIVE_DWELL_TIME_5GHZ (20) +#define WPI_ACTIVE_DWELL_FACTOR_2GHZ ( 3) +#define WPI_ACTIVE_DWELL_FACTOR_5GHZ ( 2) +#define WPI_PASSIVE_DWELL_TIME_2GHZ ( 20) +#define WPI_PASSIVE_DWELL_TIME_5GHZ ( 10) +#define WPI_PASSIVE_DWELL_BASE (100) +#define WPI_CHANNEL_TUNE_TIME ( 6) + +/* Structure for command WPI_CMD_TXPOWER. */ +struct wpi_cmd_txpower { uint8_t band; -#define WPI_RATE_5GHZ 0 -#define WPI_RATE_2GHZ 1 +#define WPI_BAND_5GHZ 0 +#define WPI_BAND_2GHZ 1 + uint8_t reserved; - uint16_t channel; + uint16_t chan; -#define WPI_RATE_MAPPING_COUNT 12 struct { - uint8_t rate; - uint8_t gain_radio; - uint8_t gain_dsp; - uint8_t reserved; - } __packed rates [WPI_RATE_MAPPING_COUNT]; + uint8_t plcp; + uint8_t rf_gain; + uint8_t dsp_gain; + uint8_t reserved; + } __packed rates[WPI_RIDX_MAX + 1]; } __packed; +/* Structure for command WPI_CMD_BT_COEX. */ +struct wpi_bluetooth { + uint8_t flags; +#define WPI_BT_COEX_DISABLE 0 +#define WPI_BT_COEX_MODE_2WIRE 1 +#define WPI_BT_COEX_MODE_3WIRE 2 +#define WPI_BT_COEX_MODE_4WIRE 3 + uint8_t lead_time; +#define WPI_BT_LEAD_TIME_DEF 30 -#define WPI_FW_MAIN_TEXT_MAXSZ (80 * 1024 ) -#define WPI_FW_MAIN_DATA_MAXSZ (32 * 1024 ) -#define WPI_FW_INIT_TEXT_MAXSZ (80 * 1024 ) -#define WPI_FW_INIT_DATA_MAXSZ (32 * 1024 ) -#define WPI_FW_BOOT_TEXT_MAXSZ 1024 - -#define WPI_FW_UPDATED (1 << 31 ) - -/* firmware image header */ -struct wpi_firmware_hdr { - -#define WPI_FW_MINVERSION 2144 + uint8_t max_kill; +#define WPI_BT_MAX_KILL_DEF 5 - uint32_t version; - uint32_t rtextsz; - uint32_t rdatasz; - uint32_t itextsz; - uint32_t idatasz; - uint32_t btextsz; + uint8_t reserved; + uint32_t kill_ack; + uint32_t kill_cts; } __packed; -/* structure for WPI_UC_READY notification */ +/* Structure for WPI_UC_READY notification. */ struct wpi_ucode_info { - uint32_t version; + uint8_t minor; + uint8_t major; + uint16_t reserved1; uint8_t revision[8]; uint8_t type; uint8_t subtype; - uint16_t reserved; + uint16_t reserved2; uint32_t logptr; - uint32_t errorptr; - uint32_t timestamp; + uint32_t errptr; + uint32_t tstamp; uint32_t valid; } __packed; -/* structure for WPI_START_SCAN notification */ +/* Structure for WPI_START_SCAN notification. */ struct wpi_start_scan { uint64_t tstamp; uint32_t tbeacon; @@ -603,18 +722,126 @@ struct wpi_start_scan { uint32_t status; } __packed; -/* structure for WPI_STOP_SCAN notification */ +/* Structure for WPI_STOP_SCAN notification. */ struct wpi_stop_scan { uint8_t nchan; uint8_t status; +#define WPI_SCAN_COMPLETED 1 +#define WPI_SCAN_ABORTED 2 + uint8_t reserved; uint8_t chan; uint64_t tsf; } __packed; +/* Structures for WPI_{RX,BEACON}_STATISTICS notification. */ +struct wpi_rx_phy_stats { + uint32_t ina; + uint32_t fina; + uint32_t bad_plcp; + uint32_t bad_crc32; + uint32_t overrun; + uint32_t eoverrun; + uint32_t good_crc32; + uint32_t fa; + uint32_t bad_fina_sync; + uint32_t sfd_timeout; + uint32_t fina_timeout; + uint32_t no_rts_ack; + uint32_t rxe_limit; + uint32_t ack; + uint32_t cts; +} __packed; + +struct wpi_rx_general_stats { + uint32_t bad_cts; + uint32_t bad_ack; + uint32_t not_bss; + uint32_t filtered; + uint32_t bad_chan; +} __packed; + +struct wpi_rx_stats { + struct wpi_rx_phy_stats ofdm; + struct wpi_rx_phy_stats cck; + struct wpi_rx_general_stats general; +} __packed; + +struct wpi_tx_stats { + uint32_t preamble; + uint32_t rx_detected; + uint32_t bt_defer; + uint32_t bt_kill; + uint32_t short_len; + uint32_t cts_timeout; + uint32_t ack_timeout; + uint32_t exp_ack; + uint32_t ack; +} __packed; + +struct wpi_general_stats { + uint32_t temp; + uint32_t burst_check; + uint32_t burst; + uint32_t reserved[4]; + uint32_t sleep; + uint32_t slot_out; + uint32_t slot_idle; + uint32_t ttl_tstamp; + uint32_t tx_ant_a; + uint32_t tx_ant_b; + uint32_t exec; + uint32_t probe; +} __packed; + +struct wpi_stats { + uint32_t flags; + struct wpi_rx_stats rx; + struct wpi_tx_stats tx; + struct wpi_general_stats general; +} __packed; + +/* Possible flags for command WPI_CMD_GET_STATISTICS. */ +#define WPI_STATISTICS_BEACON_DISABLE (1 << 1) + + +/* Firmware error dump entry. */ +struct wpi_fw_dump { + uint32_t desc; + uint32_t time; + uint32_t blink[2]; + uint32_t ilink[2]; + uint32_t data; +} __packed; + +/* Firmware image file header. */ +struct wpi_firmware_hdr { + +#define WPI_FW_MINVERSION 2144 +#define WPI_FW_NAME "wpifw" + + uint16_t driver; + uint8_t minor; + uint8_t major; + uint32_t rtextsz; + uint32_t rdatasz; + uint32_t itextsz; + uint32_t idatasz; + uint32_t btextsz; +} __packed; + +#define WPI_FW_TEXT_MAXSZ ( 80 * 1024 ) +#define WPI_FW_DATA_MAXSZ ( 32 * 1024 ) +#define WPI_FW_BOOT_TEXT_MAXSZ 1024 + +#define WPI_FW_UPDATED (1U << 31 ) + +/* + * Offsets into EEPROM. + */ #define WPI_EEPROM_MAC 0x015 #define WPI_EEPROM_REVISION 0x035 -#define WPI_EEPROM_CAPABILITIES 0x045 +#define WPI_EEPROM_SKU_CAP 0x045 #define WPI_EEPROM_TYPE 0x04a #define WPI_EEPROM_DOMAIN 0x060 #define WPI_EEPROM_BAND1 0x063 @@ -626,49 +853,66 @@ struct wpi_stop_scan { struct wpi_eeprom_chan { uint8_t flags; -#define WPI_EEPROM_CHAN_VALID (1<<0) -#define WPI_EEPROM_CHAN_IBSS (1<<1) -#define WPI_EEPROM_CHAN_ACTIVE (1<<3) -#define WPI_EEPROM_CHAN_RADAR (1<<4) +#define WPI_EEPROM_CHAN_VALID (1 << 0) +#define WPI_EEPROM_CHAN_IBSS (1 << 1) +#define WPI_EEPROM_CHAN_ACTIVE (1 << 3) +#define WPI_EEPROM_CHAN_RADAR (1 << 4) int8_t maxpwr; } __packed; struct wpi_eeprom_sample { - uint8_t index; - int8_t power; - uint16_t volt; -}; + uint8_t index; + int8_t power; + uint16_t volt; +} __packed; #define WPI_POWER_GROUPS_COUNT 5 - struct wpi_eeprom_group { - struct wpi_eeprom_sample samples[5]; - int32_t coef[5]; - int32_t corr[5]; - int8_t maxpwr; - uint8_t chan; - int16_t temp; + struct wpi_eeprom_sample samples[5]; + int32_t coef[5]; + int32_t corr[5]; + int8_t maxpwr; + uint8_t chan; + int16_t temp; } __packed; -#define WPI_CHAN_BANDS_COUNT 5 +#define WPI_CHAN_BANDS_COUNT 5 #define WPI_MAX_CHAN_PER_BAND 14 - static const struct wpi_chan_band { - uint32_t addr; /* offset in EEPROM */ - uint8_t nchan; - uint8_t chan[WPI_MAX_CHAN_PER_BAND]; -} wpi_bands[5] = { - { WPI_EEPROM_BAND1, 14, - { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }}, - { WPI_EEPROM_BAND2, 13, - { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 }}, - { WPI_EEPROM_BAND3, 12, - { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 }}, - { WPI_EEPROM_BAND4, 11, - { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 }}, - { WPI_EEPROM_BAND5, 6, - { 145, 149, 153, 157, 161, 165 }} + uint32_t addr; /* offset in EEPROM */ + uint8_t nchan; + uint8_t chan[WPI_MAX_CHAN_PER_BAND]; +} wpi_bands[] = { + /* 20MHz channels, 2GHz band. */ + { WPI_EEPROM_BAND1, 14, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } }, + /* 20MHz channels, 5GHz band. */ + { WPI_EEPROM_BAND2, 13, + { 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 } }, + { WPI_EEPROM_BAND3, 12, + { 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 } }, + { WPI_EEPROM_BAND4, 11, + { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 } }, + { WPI_EEPROM_BAND5, 6, + { 145, 149, 153, 157, 161, 165 } } +}; + +/* HW rate indices. */ +#define WPI_RIDX_OFDM6 0 +#define WPI_RIDX_OFDM36 5 +#define WPI_RIDX_OFDM48 6 +#define WPI_RIDX_OFDM54 7 +#define WPI_RIDX_CCK1 8 +#define WPI_RIDX_CCK2 9 +#define WPI_RIDX_CCK11 11 + +static const uint8_t wpi_ridx_to_plcp[] = { + /* OFDM: IEEE Std 802.11a-1999, pp. 14 Table 80 */ + /* R1-R4 (ral/ural is R4-R1) */ + 0xd, 0xf, 0x5, 0x7, 0x9, 0xb, 0x1, 0x3, + /* CCK: device-dependent */ + 10, 20, 55, 110 }; #define WPI_MAX_PWR_INDEX 77 @@ -678,25 +922,25 @@ static const struct wpi_chan_band { * the reference driver.) */ static const uint8_t wpi_rf_gain_2ghz[WPI_MAX_PWR_INDEX + 1] = { - 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xbb, 0xbb, - 0xbb, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xd3, 0xd3, 0xb3, 0xb3, 0xb3, - 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x73, 0xeb, 0xeb, 0xeb, - 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xab, 0xab, 0xab, 0x8b, - 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xa3, - 0xa3, 0xa3, 0xa3, 0x83, 0x83, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63, - 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03, 0x03, 0x03, - 0x03 + 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xfb, 0xbb, 0xbb, 0xbb, + 0xbb, 0xf3, 0xf3, 0xf3, 0xf3, 0xf3, 0xd3, 0xd3, 0xb3, 0xb3, 0xb3, + 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x73, 0xeb, 0xeb, 0xeb, + 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xcb, 0xab, 0xab, 0xab, 0x8b, + 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xe3, 0xc3, 0xc3, 0xc3, 0xc3, 0xa3, + 0xa3, 0xa3, 0xa3, 0x83, 0x83, 0x83, 0x83, 0x63, 0x63, 0x63, 0x63, + 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, 0x03, 0x03, 0x03, + 0x03 }; static const uint8_t wpi_rf_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { - 0xfb, 0xfb, 0xfb, 0xdb, 0xdb, 0xbb, 0xbb, 0x9b, 0x9b, 0x7b, 0x7b, - 0x7b, 0x7b, 0x5b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x1b, 0x1b, - 0x1b, 0x73, 0x73, 0x73, 0x53, 0x53, 0x53, 0x53, 0x53, 0x33, 0x33, - 0x33, 0x33, 0x13, 0x13, 0x13, 0x13, 0x13, 0xab, 0xab, 0xab, 0x8b, - 0x8b, 0x8b, 0x8b, 0x6b, 0x6b, 0x6b, 0x6b, 0x4b, 0x4b, 0x4b, 0x4b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x0b, 0x0b, 0x0b, 0x0b, 0x83, 0x83, 0x63, - 0x63, 0x63, 0x63, 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, - 0x03 + 0xfb, 0xfb, 0xfb, 0xdb, 0xdb, 0xbb, 0xbb, 0x9b, 0x9b, 0x7b, 0x7b, + 0x7b, 0x7b, 0x5b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x1b, 0x1b, + 0x1b, 0x73, 0x73, 0x73, 0x53, 0x53, 0x53, 0x53, 0x53, 0x33, 0x33, + 0x33, 0x33, 0x13, 0x13, 0x13, 0x13, 0x13, 0xab, 0xab, 0xab, 0x8b, + 0x8b, 0x8b, 0x8b, 0x6b, 0x6b, 0x6b, 0x6b, 0x4b, 0x4b, 0x4b, 0x4b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x0b, 0x0b, 0x0b, 0x0b, 0x83, 0x83, 0x63, + 0x63, 0x63, 0x63, 0x43, 0x43, 0x43, 0x43, 0x23, 0x23, 0x23, 0x23, + 0x03 }; /* @@ -704,34 +948,89 @@ static const uint8_t wpi_rf_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { * from the reference driver.) */ static const uint8_t wpi_dsp_gain_2ghz[WPI_MAX_PWR_INDEX + 1] = { - 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x6e, 0x69, 0x62, 0x7d, 0x73, 0x6c, - 0x63, 0x77, 0x6f, 0x69, 0x61, 0x5c, 0x6a, 0x64, 0x78, 0x71, 0x6b, - 0x7d, 0x77, 0x70, 0x6a, 0x65, 0x61, 0x5b, 0x6b, 0x79, 0x73, 0x6d, - 0x7f, 0x79, 0x73, 0x6c, 0x66, 0x60, 0x5c, 0x6e, 0x68, 0x62, 0x74, - 0x7d, 0x77, 0x71, 0x6b, 0x65, 0x60, 0x71, 0x6a, 0x66, 0x5f, 0x71, - 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, - 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, - 0x5f + 0x7f, 0x7f, 0x7f, 0x7f, 0x7d, 0x6e, 0x69, 0x62, 0x7d, 0x73, 0x6c, + 0x63, 0x77, 0x6f, 0x69, 0x61, 0x5c, 0x6a, 0x64, 0x78, 0x71, 0x6b, + 0x7d, 0x77, 0x70, 0x6a, 0x65, 0x61, 0x5b, 0x6b, 0x79, 0x73, 0x6d, + 0x7f, 0x79, 0x73, 0x6c, 0x66, 0x60, 0x5c, 0x6e, 0x68, 0x62, 0x74, + 0x7d, 0x77, 0x71, 0x6b, 0x65, 0x60, 0x71, 0x6a, 0x66, 0x5f, 0x71, + 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, + 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, 0x5f, 0x71, 0x6a, 0x66, + 0x5f }; static const uint8_t wpi_dsp_gain_5ghz[WPI_MAX_PWR_INDEX + 1] = { - 0x7f, 0x78, 0x72, 0x77, 0x65, 0x71, 0x66, 0x72, 0x67, 0x75, 0x6b, - 0x63, 0x5c, 0x6c, 0x7d, 0x76, 0x6d, 0x66, 0x60, 0x5a, 0x68, 0x62, - 0x5c, 0x76, 0x6f, 0x68, 0x7e, 0x79, 0x71, 0x69, 0x63, 0x76, 0x6f, - 0x68, 0x62, 0x74, 0x6d, 0x66, 0x62, 0x5d, 0x71, 0x6b, 0x63, 0x78, - 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, - 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x6b, 0x63, 0x78, - 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, - 0x78 + 0x7f, 0x78, 0x72, 0x77, 0x65, 0x71, 0x66, 0x72, 0x67, 0x75, 0x6b, + 0x63, 0x5c, 0x6c, 0x7d, 0x76, 0x6d, 0x66, 0x60, 0x5a, 0x68, 0x62, + 0x5c, 0x76, 0x6f, 0x68, 0x7e, 0x79, 0x71, 0x69, 0x63, 0x76, 0x6f, + 0x68, 0x62, 0x74, 0x6d, 0x66, 0x62, 0x5d, 0x71, 0x6b, 0x63, 0x78, + 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, + 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x6b, 0x63, 0x78, + 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, 0x78, 0x71, 0x6b, 0x63, + 0x78 }; +/* + * Power saving settings (values obtained from the reference driver.) + */ +#define WPI_NDTIMRANGES 2 +#define WPI_NPOWERLEVELS 6 +static const struct wpi_pmgt { + uint32_t rxtimeout; + uint32_t txtimeout; + uint32_t intval[5]; + int skip_dtim; +} wpi_pmgt[WPI_NDTIMRANGES][WPI_NPOWERLEVELS] = { + /* DTIM <= 10 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 3, 4, 4 }, 0 }, /* PS level 1 */ + { 200, 300, { 2, 4, 6, 7, 7 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 6, 9, 9, 10 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 7, 9, 9, 10 }, 1 }, /* PS level 4 */ + { 25, 25, { 4, 7, 10, 10, 10 }, 1 } /* PS level 5 */ + }, + /* DTIM >= 11 */ + { + { 0, 0, { 0, 0, 0, 0, 0 }, 0 }, /* CAM */ + { 200, 500, { 1, 2, 3, 4, -1 }, 0 }, /* PS level 1 */ + { 200, 300, { 2, 4, 6, 7, -1 }, 0 }, /* PS level 2 */ + { 50, 100, { 2, 6, 9, 9, -1 }, 0 }, /* PS level 3 */ + { 50, 25, { 2, 7, 9, 9, -1 }, 0 }, /* PS level 4 */ + { 25, 25, { 4, 7, 10, 10, -1 }, 0 } /* PS level 5 */ + } +}; + +/* Firmware errors. */ +static const char * const wpi_fw_errmsg[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT", + "SYSASSERT", + "FATAL_ERROR" +}; #define WPI_READ(sc, reg) \ - bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) + bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define WPI_WRITE(sc, reg, val) \ - bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) + bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define WPI_WRITE_REGION_4(sc, offset, datap, count) \ - bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ - (datap), (count)) + bus_space_write_region_4((sc)->sc_st, (sc)->sc_sh, (offset), \ + (datap), (count)) + +#define WPI_SETBITS(sc, reg, mask) \ + WPI_WRITE(sc, reg, WPI_READ(sc, reg) | (mask)) + +#define WPI_CLRBITS(sc, reg, mask) \ + WPI_WRITE(sc, reg, WPI_READ(sc, reg) & ~(mask)) + +#define WPI_BARRIER_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ + BUS_SPACE_BARRIER_WRITE) + +#define WPI_BARRIER_READ_WRITE(sc) \ + bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) diff --git a/sys/dev/wpi/if_wpivar.h b/sys/dev/wpi/if_wpivar.h index e579264..7a65b72 100644 --- a/sys/dev/wpi/if_wpivar.h +++ b/sys/dev/wpi/if_wpivar.h @@ -16,8 +16,6 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <net80211/ieee80211_amrr.h> - struct wpi_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint64_t wr_tsft; @@ -28,7 +26,7 @@ struct wpi_rx_radiotap_header { int8_t wr_dbm_antsignal; int8_t wr_dbm_antnoise; uint8_t wr_antenna; -}; +} __packed; #define WPI_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ @@ -45,8 +43,7 @@ struct wpi_tx_radiotap_header { uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; - uint8_t wt_hwqueue; -}; +} __packed; #define WPI_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ @@ -55,16 +52,15 @@ struct wpi_tx_radiotap_header { struct wpi_dma_info { bus_dma_tag_t tag; - bus_dmamap_t map; - bus_addr_t paddr; /* aligned p address */ - bus_addr_t paddr_start; /* possibly unaligned p start*/ - caddr_t vaddr; /* aligned v address */ - caddr_t vaddr_start; /* possibly unaligned v start */ + bus_dmamap_t map; + bus_addr_t paddr; + caddr_t vaddr; bus_size_t size; }; struct wpi_tx_data { bus_dmamap_t map; + bus_addr_t cmd_paddr; struct mbuf *m; struct ieee80211_node *ni; }; @@ -74,19 +70,17 @@ struct wpi_tx_ring { struct wpi_dma_info cmd_dma; struct wpi_tx_desc *desc; struct wpi_tx_cmd *cmd; - struct wpi_tx_data *data; + struct wpi_tx_data data[WPI_TX_RING_COUNT]; bus_dma_tag_t data_dmat; int qid; - int count; int queued; int cur; + int update; }; -#define WPI_RBUF_COUNT ( WPI_RX_RING_COUNT + 16 ) - struct wpi_rx_data { - bus_dmamap_t map; - struct mbuf *m; + struct mbuf *m; + bus_dmamap_t map; }; struct wpi_rx_ring { @@ -95,16 +89,14 @@ struct wpi_rx_ring { struct wpi_rx_data data[WPI_RX_RING_COUNT]; bus_dma_tag_t data_dmat; int cur; + int update; }; -struct wpi_amrr { - struct ieee80211_node ni; /* must be the first */ - int txcnt; - int retrycnt; - int success; - int success_threshold; - int recovery; +struct wpi_node { + struct ieee80211_node ni; /* must be the first */ + uint8_t id; }; +#define WPI_NODE(ni) ((struct wpi_node *)(ni)) struct wpi_power_sample { uint8_t index; @@ -119,84 +111,189 @@ struct wpi_power_group { int16_t temp; }; +struct wpi_buf { + uint8_t data[56]; /* sizeof(struct wpi_cmd_beacon) */ + struct ieee80211_node *ni; + struct mbuf *m; + size_t size; + int code; + int ac; +}; + struct wpi_vap { - struct ieee80211vap vap; + struct ieee80211vap wv_vap; + + struct wpi_buf wv_bcbuf; + struct ieee80211_beacon_offsets wv_boff; + struct mtx wv_mtx; - int (*newstate)(struct ieee80211vap *, + uint32_t wv_gtk; +#define WPI_VAP_KEY(kid) (1 << kid) + + int (*wv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); + void (*wv_recv_mgmt)(struct ieee80211_node *, + struct mbuf *, int, int, int); }; #define WPI_VAP(vap) ((struct wpi_vap *)(vap)) +#define WPI_VAP_LOCK_INIT(_wvp) \ + mtx_init(&(_wvp)->wv_mtx, "lock for wv_bcbuf/wv_boff structures", \ + NULL, MTX_DEF) +#define WPI_VAP_LOCK(_wvp) mtx_lock(&(_wvp)->wv_mtx) +#define WPI_VAP_UNLOCK(_wvp) mtx_unlock(&(_wvp)->wv_mtx) +#define WPI_VAP_LOCK_ASSERT(_wvp) mtx_assert(&(_wvp)->wv_mtx, MA_OWNED) +#define WPI_VAP_LOCK_DESTROY(_wvp) mtx_destroy(&(_wvp)->wv_mtx) + +struct wpi_fw_part { + const uint8_t *text; + uint32_t textsz; + const uint8_t *data; + uint32_t datasz; +}; + +struct wpi_fw_info { + const uint8_t *data; + size_t size; + struct wpi_fw_part init; + struct wpi_fw_part main; + struct wpi_fw_part boot; +}; + struct wpi_softc { device_t sc_dev; + struct ifnet *sc_ifp; - struct mtx sc_mtx; + int sc_debug; + + int sc_flags; +#define WPI_PS_PATH (1 << 0) - /* Flags indicating the current state the driver - * expects the hardware to be in - */ - uint32_t flags; -#define WPI_FLAG_HW_RADIO_OFF (1 << 0) -#define WPI_FLAG_BUSY (1 << 1) -#define WPI_FLAG_AUTH (1 << 2) + struct mtx sc_mtx; + struct mtx tx_mtx; - /* shared area */ + /* Shared area. */ struct wpi_dma_info shared_dma; struct wpi_shared *shared; - struct wpi_tx_ring txq[WME_NUM_AC]; - struct wpi_tx_ring cmdq; + struct wpi_tx_ring txq[WPI_NTXQUEUES]; + struct mtx txq_mtx; + struct mtx txq_state_mtx; + uint32_t txq_active; + struct wpi_rx_ring rxq; + uint64_t rx_tstamp; - /* TX Thermal Callibration */ + /* TX Thermal Callibration. */ struct callout calib_to; int calib_cnt; - /* Watch dog timer */ - struct callout watchdog_to; - /* Hardware switch polling timer */ - struct callout hwswitch_to; + struct callout scan_timeout; + struct callout tx_timeout; + + /* Watch dog timer. */ + struct callout watchdog_rfkill; + + /* Firmware image. */ + struct wpi_fw_info fw; + uint32_t errptr; struct resource *irq; struct resource *mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; void *sc_ih; + bus_size_t sc_sz; + int sc_cap_off; /* PCIe Capabilities. */ - struct wpi_config config; - int temp; + struct wpi_rxon rxon; + struct mtx rxon_mtx; + int temp; + uint32_t qfullmsk; - int sc_tx_timer; - int sc_scan_timer; + uint32_t nodesmsk; + struct mtx nt_mtx; - struct bpf_if *sc_drvbpf; + void (*sc_node_free)(struct ieee80211_node *); + void (*sc_update_rx_ring)(struct wpi_softc *); + void (*sc_update_tx_ring)(struct wpi_softc *, + struct wpi_tx_ring *); - struct wpi_rx_radiotap_header sc_rxtap; - struct wpi_tx_radiotap_header sc_txtap; + struct wpi_rx_radiotap_header sc_rxtap; + struct wpi_tx_radiotap_header sc_txtap; - /* firmware image */ + /* Firmware image. */ const struct firmware *fw_fp; - /* firmware DMA transfer */ + /* Firmware DMA transfer. */ struct wpi_dma_info fw_dma; - /* Tasks used by the driver */ - struct task sc_restarttask; /* reset firmware task */ - struct task sc_radiotask; /* reset rf task */ + /* Tasks used by the driver. */ + struct task sc_reinittask; + struct task sc_radiooff_task; + struct task sc_radioon_task; + struct task sc_start_task; - /* Eeprom info */ + /* Taskqueue */ + struct taskqueue *sc_tq; + + /* Eeprom info. */ uint8_t cap; uint16_t rev; uint8_t type; + struct wpi_eeprom_chan + eeprom_channels[WPI_CHAN_BANDS_COUNT][WPI_MAX_CHAN_PER_BAND]; struct wpi_power_group groups[WPI_POWER_GROUPS_COUNT]; int8_t maxpwr[IEEE80211_CHAN_MAX]; - char domain[4]; /*reglatory domain XXX */ + char domain[4]; /* Regulatory domain. */ }; + +/* + * Locking order: + * 1. WPI_LOCK; + * 2. WPI_RXON_LOCK; + * 3. WPI_TX_LOCK; + * 4. WPI_NT_LOCK / WPI_VAP_LOCK; + * 5. WPI_TXQ_LOCK; + * 6. WPI_TXQ_STATE_LOCK; + */ + #define WPI_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ - MTX_NETWORK_LOCK, MTX_DEF) + MTX_NETWORK_LOCK, MTX_DEF) #define WPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define WPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define WPI_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) +#define WPI_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define WPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) + +#define WPI_RXON_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->rxon_mtx, "lock for wpi_rxon structure", NULL, MTX_DEF) +#define WPI_RXON_LOCK(_sc) mtx_lock(&(_sc)->rxon_mtx) +#define WPI_RXON_UNLOCK(_sc) mtx_unlock(&(_sc)->rxon_mtx) +#define WPI_RXON_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->rxon_mtx, MA_OWNED) +#define WPI_RXON_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->rxon_mtx) + +#define WPI_TX_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->tx_mtx, "tx path lock", NULL, MTX_DEF) +#define WPI_TX_LOCK(_sc) mtx_lock(&(_sc)->tx_mtx) +#define WPI_TX_UNLOCK(_sc) mtx_unlock(&(_sc)->tx_mtx) +#define WPI_TX_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->tx_mtx) + +#define WPI_NT_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->nt_mtx, "node table lock", NULL, MTX_DEF) +#define WPI_NT_LOCK(_sc) mtx_lock(&(_sc)->nt_mtx) +#define WPI_NT_UNLOCK(_sc) mtx_unlock(&(_sc)->nt_mtx) +#define WPI_NT_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->nt_mtx) + +#define WPI_TXQ_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->txq_mtx, "txq/cmdq lock", NULL, MTX_DEF) +#define WPI_TXQ_LOCK(_sc) mtx_lock(&(_sc)->txq_mtx) +#define WPI_TXQ_UNLOCK(_sc) mtx_unlock(&(_sc)->txq_mtx) +#define WPI_TXQ_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->txq_mtx) + +#define WPI_TXQ_STATE_LOCK_INIT(_sc) \ + mtx_init(&(_sc)->txq_state_mtx, "txq state lock", NULL, MTX_DEF) +#define WPI_TXQ_STATE_LOCK(_sc) mtx_lock(&(_sc)->txq_state_mtx) +#define WPI_TXQ_STATE_UNLOCK(_sc) mtx_unlock(&(_sc)->txq_state_mtx) +#define WPI_TXQ_STATE_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->txq_state_mtx) |