diff options
author | David S. Miller <davem@davemloft.net> | 2010-01-19 11:43:42 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-19 11:43:42 -0800 |
commit | 6373464288cab09bc641be301d8d30fc9f64ba71 (patch) | |
tree | c1bc92dc630aa15da2e12bc0d09c92169817a702 | |
parent | 6d955180b2f9ccff444df06265160868cabb289a (diff) | |
parent | 730dd70549e0ec755dd55615ba5cfc38a482a947 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Conflicts:
drivers/net/wireless/iwlwifi/iwl-core.h
92 files changed, 7260 insertions, 2757 deletions
diff --git a/Documentation/DocBook/mac80211.tmpl b/Documentation/DocBook/mac80211.tmpl index f3f37f141db..971d1c0c83e 100644 --- a/Documentation/DocBook/mac80211.tmpl +++ b/Documentation/DocBook/mac80211.tmpl @@ -144,7 +144,7 @@ usage should require reading the full document. this though and the recommendation to allow only a single interface in STA mode at first! </para> -!Finclude/net/mac80211.h ieee80211_if_init_conf +!Finclude/net/mac80211.h ieee80211_vif </chapter> <chapter id="rx-tx"> diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 6a2a9676111..66bcb506a11 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1063,6 +1063,7 @@ struct ath5k_hw { u32 ah_cw_min; u32 ah_cw_max; u32 ah_limit_tx_retries; + u8 ah_coverage_class; /* Antenna Control */ u32 ah_ant_ctl[AR5K_EEPROM_N_MODES][AR5K_ANT_MAX]; @@ -1200,6 +1201,7 @@ extern bool ath5k_eeprom_is_hb63(struct ath5k_hw *ah); /* Protocol Control Unit Functions */ extern int ath5k_hw_set_opmode(struct ath5k_hw *ah); +extern void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class); /* BSSID Functions */ extern int ath5k_hw_set_lladdr(struct ath5k_hw *ah, const u8 *mac); extern void ath5k_hw_set_associd(struct ath5k_hw *ah); @@ -1231,6 +1233,10 @@ extern int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout); extern unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah); extern int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout); extern unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah); +/* Clock rate related functions */ +unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec); +unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock); +unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah); /* Key table (WEP) functions */ extern int ath5k_hw_reset_key(struct ath5k_hw *ah, u16 entry); extern int ath5k_hw_is_key_valid(struct ath5k_hw *ah, u16 entry); @@ -1310,24 +1316,6 @@ extern int ath5k_hw_set_txpower_limit(struct ath5k_hw *ah, u8 txpower); * Functions used internaly */ -/* - * Translate usec to hw clock units - * TODO: Half/quarter rate - */ -static inline unsigned int ath5k_hw_htoclock(unsigned int usec, bool turbo) -{ - return turbo ? (usec * 80) : (usec * 40); -} - -/* - * Translate hw clock units to usec - * TODO: Half/quarter rate - */ -static inline unsigned int ath5k_hw_clocktoh(unsigned int clock, bool turbo) -{ - return turbo ? (clock / 80) : (clock / 40); -} - static inline struct ath_common *ath5k_hw_common(struct ath5k_hw *ah) { return &ah->common; diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 72e5ed51c0a..5577bcc80ea 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -254,6 +254,8 @@ static void ath5k_bss_info_changed(struct ieee80211_hw *hw, u32 changes); static void ath5k_sw_scan_start(struct ieee80211_hw *hw); static void ath5k_sw_scan_complete(struct ieee80211_hw *hw); +static void ath5k_set_coverage_class(struct ieee80211_hw *hw, + u8 coverage_class); static const struct ieee80211_ops ath5k_hw_ops = { .tx = ath5k_tx, @@ -274,6 +276,7 @@ static const struct ieee80211_ops ath5k_hw_ops = { .bss_info_changed = ath5k_bss_info_changed, .sw_scan_start = ath5k_sw_scan_start, .sw_scan_complete = ath5k_sw_scan_complete, + .set_coverage_class = ath5k_set_coverage_class, }; /* @@ -3262,3 +3265,22 @@ static void ath5k_sw_scan_complete(struct ieee80211_hw *hw) ath5k_hw_set_ledstate(sc->ah, sc->assoc ? AR5K_LED_ASSOC : AR5K_LED_INIT); } + +/** + * ath5k_set_coverage_class - Set IEEE 802.11 coverage class + * + * @hw: struct ieee80211_hw pointer + * @coverage_class: IEEE 802.11 coverage class number + * + * Mac80211 callback. Sets slot time, ACK timeout and CTS timeout for given + * coverage class. The values are persistent, they are restored after device + * reset. + */ +static void ath5k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) +{ + struct ath5k_softc *sc = hw->priv; + + mutex_lock(&sc->lock); + ath5k_hw_set_coverage_class(sc->ah, coverage_class); + mutex_unlock(&sc->lock); +} diff --git a/drivers/net/wireless/ath/ath5k/eeprom.c b/drivers/net/wireless/ath/ath5k/eeprom.c index 5d1c8677f18..6a3f4da7fb4 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.c +++ b/drivers/net/wireless/ath/ath5k/eeprom.c @@ -97,7 +97,7 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) struct ath5k_eeprom_info *ee = &ah->ah_capabilities.cap_eeprom; int ret; u16 val; - u32 cksum, offset; + u32 cksum, offset, eep_max = AR5K_EEPROM_INFO_MAX; /* * Read values from EEPROM and store them in the capability structure @@ -116,12 +116,38 @@ ath5k_eeprom_init_header(struct ath5k_hw *ah) * Validate the checksum of the EEPROM date. There are some * devices with invalid EEPROMs. */ - for (cksum = 0, offset = 0; offset < AR5K_EEPROM_INFO_MAX; offset++) { + AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_UPPER, val); + if (val) { + eep_max = (val & AR5K_EEPROM_SIZE_UPPER_MASK) << + AR5K_EEPROM_SIZE_ENDLOC_SHIFT; + AR5K_EEPROM_READ(AR5K_EEPROM_SIZE_LOWER, val); + eep_max = (eep_max | val) - AR5K_EEPROM_INFO_BASE; + + /* + * Fail safe check to prevent stupid loops due + * to busted EEPROMs. XXX: This value is likely too + * big still, waiting on a better value. + */ + if (eep_max > (3 * AR5K_EEPROM_INFO_MAX)) { + ATH5K_ERR(ah->ah_sc, "Invalid max custom EEPROM size: " + "%d (0x%04x) max expected: %d (0x%04x)\n", + eep_max, eep_max, + 3 * AR5K_EEPROM_INFO_MAX, + 3 * AR5K_EEPROM_INFO_MAX); + return -EIO; + } + } + + for (cksum = 0, offset = 0; offset < eep_max; offset++) { AR5K_EEPROM_READ(AR5K_EEPROM_INFO(offset), val); cksum ^= val; } if (cksum != AR5K_EEPROM_INFO_CKSUM) { - ATH5K_ERR(ah->ah_sc, "Invalid EEPROM checksum 0x%04x\n", cksum); + ATH5K_ERR(ah->ah_sc, "Invalid EEPROM " + "checksum: 0x%04x eep_max: 0x%04x (%s)\n", + cksum, eep_max, + eep_max == AR5K_EEPROM_INFO_MAX ? + "default size" : "custom size"); return -EIO; } diff --git a/drivers/net/wireless/ath/ath5k/eeprom.h b/drivers/net/wireless/ath/ath5k/eeprom.h index 0123f3521a0..473a483bb9c 100644 --- a/drivers/net/wireless/ath/ath5k/eeprom.h +++ b/drivers/net/wireless/ath/ath5k/eeprom.h @@ -37,6 +37,14 @@ #define AR5K_EEPROM_RFKILL_POLARITY_S 1 #define AR5K_EEPROM_REG_DOMAIN 0x00bf /* EEPROM regdom */ + +/* FLASH(EEPROM) Defines for AR531X chips */ +#define AR5K_EEPROM_SIZE_LOWER 0x1b /* size info -- lower */ +#define AR5K_EEPROM_SIZE_UPPER 0x1c /* size info -- upper */ +#define AR5K_EEPROM_SIZE_UPPER_MASK 0xfff0 +#define AR5K_EEPROM_SIZE_UPPER_SHIFT 4 +#define AR5K_EEPROM_SIZE_ENDLOC_SHIFT 12 + #define AR5K_EEPROM_CHECKSUM 0x00c0 /* EEPROM checksum */ #define AR5K_EEPROM_INFO_BASE 0x00c0 /* EEPROM header */ #define AR5K_EEPROM_INFO_MAX (0x400 - AR5K_EEPROM_INFO_BASE) diff --git a/drivers/net/wireless/ath/ath5k/pcu.c b/drivers/net/wireless/ath/ath5k/pcu.c index 64fc1eb9b6d..aefe84f9c04 100644 --- a/drivers/net/wireless/ath/ath5k/pcu.c +++ b/drivers/net/wireless/ath/ath5k/pcu.c @@ -187,8 +187,8 @@ unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) { ATH5K_TRACE(ah->ah_sc); - return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, - AR5K_TIME_OUT), AR5K_TIME_OUT_ACK), ah->ah_turbo); + return ath5k_hw_clocktoh(ah, AR5K_REG_MS(ath5k_hw_reg_read(ah, + AR5K_TIME_OUT), AR5K_TIME_OUT_ACK)); } /** @@ -200,12 +200,12 @@ unsigned int ath5k_hw_get_ack_timeout(struct ath5k_hw *ah) int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) { ATH5K_TRACE(ah->ah_sc); - if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK), - ah->ah_turbo) <= timeout) + if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_ACK)) + <= timeout) return -EINVAL; AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_ACK, - ath5k_hw_htoclock(timeout, ah->ah_turbo)); + ath5k_hw_htoclock(ah, timeout)); return 0; } @@ -218,8 +218,8 @@ int ath5k_hw_set_ack_timeout(struct ath5k_hw *ah, unsigned int timeout) unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) { ATH5K_TRACE(ah->ah_sc); - return ath5k_hw_clocktoh(AR5K_REG_MS(ath5k_hw_reg_read(ah, - AR5K_TIME_OUT), AR5K_TIME_OUT_CTS), ah->ah_turbo); + return ath5k_hw_clocktoh(ah, AR5K_REG_MS(ath5k_hw_reg_read(ah, + AR5K_TIME_OUT), AR5K_TIME_OUT_CTS)); } /** @@ -231,17 +231,97 @@ unsigned int ath5k_hw_get_cts_timeout(struct ath5k_hw *ah) int ath5k_hw_set_cts_timeout(struct ath5k_hw *ah, unsigned int timeout) { ATH5K_TRACE(ah->ah_sc); - if (ath5k_hw_clocktoh(AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS), - ah->ah_turbo) <= timeout) + if (ath5k_hw_clocktoh(ah, AR5K_REG_MS(0xffffffff, AR5K_TIME_OUT_CTS)) + <= timeout) return -EINVAL; AR5K_REG_WRITE_BITS(ah, AR5K_TIME_OUT, AR5K_TIME_OUT_CTS, - ath5k_hw_htoclock(timeout, ah->ah_turbo)); + ath5k_hw_htoclock(ah, timeout)); return 0; } /** + * ath5k_hw_htoclock - Translate usec to hw clock units + * + * @ah: The &struct ath5k_hw + * @usec: value in microseconds + */ +unsigned int ath5k_hw_htoclock(struct ath5k_hw *ah, unsigned int usec) +{ + return usec * ath5k_hw_get_clockrate(ah); +} + +/** + * ath5k_hw_clocktoh - Translate hw clock units to usec + * @clock: value in hw clock units + */ +unsigned int ath5k_hw_clocktoh(struct ath5k_hw *ah, unsigned int clock) +{ + return clock / ath5k_hw_get_clockrate(ah); +} + +/** + * ath5k_hw_get_clockrate - Get the clock rate for current mode + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_clockrate(struct ath5k_hw *ah) +{ + struct ieee80211_channel *channel = ah->ah_current_channel; + int clock; + + if (channel->hw_value & CHANNEL_5GHZ) + clock = 40; /* 802.11a */ + else if (channel->hw_value & CHANNEL_CCK) + clock = 22; /* 802.11b */ + else + clock = 44; /* 802.11g */ + + /* Clock rate in turbo modes is twice the normal rate */ + if (channel->hw_value & CHANNEL_TURBO) + clock *= 2; + + return clock; +} + +/** + * ath5k_hw_get_default_slottime - Get the default slot time for current mode + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_default_slottime(struct ath5k_hw *ah) +{ + struct ieee80211_channel *channel = ah->ah_current_channel; + + if (channel->hw_value & CHANNEL_TURBO) + return 6; /* both turbo modes */ + + if (channel->hw_value & CHANNEL_CCK) + return 20; /* 802.11b */ + + return 9; /* 802.11 a/g */ +} + +/** + * ath5k_hw_get_default_sifs - Get the default SIFS for current mode + * + * @ah: The &struct ath5k_hw + */ +unsigned int ath5k_hw_get_default_sifs(struct ath5k_hw *ah) +{ + struct ieee80211_channel *channel = ah->ah_current_channel; + + if (channel->hw_value & CHANNEL_TURBO) + return 8; /* both turbo modes */ + + if (channel->hw_value & CHANNEL_5GHZ) + return 16; /* 802.11a */ + + return 10; /* 802.11 b/g */ +} + +/** * ath5k_hw_set_lladdr - Set station id * * @ah: The &struct ath5k_hw @@ -1050,3 +1130,24 @@ int ath5k_hw_set_key_lladdr(struct ath5k_hw *ah, u16 entry, const u8 *mac) return 0; } +/** + * ath5k_hw_set_coverage_class - Set IEEE 802.11 coverage class + * + * @ah: The &struct ath5k_hw + * @coverage_class: IEEE 802.11 coverage class number + * + * Sets slot time, ACK timeout and CTS timeout for given coverage class. + */ +void ath5k_hw_set_coverage_class(struct ath5k_hw *ah, u8 coverage_class) +{ + /* As defined by IEEE 802.11-2007 17.3.8.6 */ + int slot_time = ath5k_hw_get_default_slottime(ah) + 3 * coverage_class; + int ack_timeout = ath5k_hw_get_default_sifs(ah) + slot_time; + int cts_timeout = ack_timeout; + + ath5k_hw_set_slot_time(ah, slot_time); + ath5k_hw_set_ack_timeout(ah, ack_timeout); + ath5k_hw_set_cts_timeout(ah, cts_timeout); + + ah->ah_coverage_class = coverage_class; +} diff --git a/drivers/net/wireless/ath/ath5k/qcu.c b/drivers/net/wireless/ath/ath5k/qcu.c index eeebb9aef20..abe36c0d139 100644 --- a/drivers/net/wireless/ath/ath5k/qcu.c +++ b/drivers/net/wireless/ath/ath5k/qcu.c @@ -520,12 +520,16 @@ int ath5k_hw_reset_tx_queue(struct ath5k_hw *ah, unsigned int queue) */ unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) { + unsigned int slot_time_clock; + ATH5K_TRACE(ah->ah_sc); + if (ah->ah_version == AR5K_AR5210) - return ath5k_hw_clocktoh(ath5k_hw_reg_read(ah, - AR5K_SLOT_TIME) & 0xffff, ah->ah_turbo); + slot_time_clock = ath5k_hw_reg_read(ah, AR5K_SLOT_TIME); else - return ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT) & 0xffff; + slot_time_clock = ath5k_hw_reg_read(ah, AR5K_DCU_GBL_IFS_SLOT); + + return ath5k_hw_clocktoh(ah, slot_time_clock & 0xffff); } /* @@ -533,15 +537,17 @@ unsigned int ath5k_hw_get_slot_time(struct ath5k_hw *ah) */ int ath5k_hw_set_slot_time(struct ath5k_hw *ah, unsigned int slot_time) { + u32 slot_time_clock = ath5k_hw_htoclock(ah, slot_time); + ATH5K_TRACE(ah->ah_sc); - if (slot_time < AR5K_SLOT_TIME_9 || slot_time > AR5K_SLOT_TIME_MAX) + + if (slot_time < 6 || slot_time_clock > AR5K_SLOT_TIME_MAX) return -EINVAL; if (ah->ah_version == AR5K_AR5210) - ath5k_hw_reg_write(ah, ath5k_hw_htoclock(slot_time, - ah->ah_turbo), AR5K_SLOT_TIME); + ath5k_hw_reg_write(ah, slot_time_clock, AR5K_SLOT_TIME); else - ath5k_hw_reg_write(ah, slot_time, AR5K_DCU_GBL_IFS_SLOT); + ath5k_hw_reg_write(ah, slot_time_clock, AR5K_DCU_GBL_IFS_SLOT); return 0; } diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 62954fc7786..6690923fd78 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -60,12 +60,11 @@ static inline int ath5k_hw_write_ofdm_timings(struct ath5k_hw *ah, !(channel->hw_value & CHANNEL_OFDM)); /* Get coefficient - * ALGO: coef = (5 * clock * carrier_freq) / 2) + * ALGO: coef = (5 * clock / carrier_freq) / 2 * we scale coef by shifting clock value by 24 for * better precision since we use integers */ /* TODO: Half/quarter rate */ - clock = ath5k_hw_htoclock(1, channel->hw_value & CHANNEL_TURBO); - + clock = (channel->hw_value & CHANNEL_TURBO) ? 80 : 40; coef_scaled = ((5 * (clock << 24)) / 2) / channel->center_freq; /* Get exponent @@ -1317,6 +1316,10 @@ int ath5k_hw_reset(struct ath5k_hw *ah, enum nl80211_iftype op_mode, /* Restore antenna mode */ ath5k_hw_set_antenna_mode(ah, ah->ah_ant_mode); + /* Restore slot time and ACK timeouts */ + if (ah->ah_coverage_class > 0) + ath5k_hw_set_coverage_class(ah, ah->ah_coverage_class); + /* * Configure QCUs/DCUs */ diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 03a1106ad72..5774cea23a3 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -25,7 +25,7 @@ config ATH9K config ATH9K_DEBUGFS bool "Atheros ath9k debugging" - depends on ATH9K + depends on ATH9K && DEBUG_FS ---help--- Say Y, if you need access to ath9k's statistics for interrupts, rate control, etc. diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile index 4985b2b1b0a..6b50d5eb9ec 100644 --- a/drivers/net/wireless/ath/ath9k/Makefile +++ b/drivers/net/wireless/ath/ath9k/Makefile @@ -1,4 +1,6 @@ ath9k-y += beacon.o \ + gpio.o \ + init.o \ main.o \ recv.o \ xmit.o \ diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index 329e6bc137a..9e62a569e81 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -121,16 +121,19 @@ static int ath_ahb_probe(struct platform_device *pdev) sc->mem = mem; sc->irq = irq; - ret = ath_init_device(AR5416_AR9100_DEVID, sc, 0x0, &ath_ahb_bus_ops); + /* Will be cleared in ath9k_start() */ + sc->sc_flags |= SC_OP_INVALID; + + ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { - dev_err(&pdev->dev, "failed to initialize device\n"); + dev_err(&pdev->dev, "request_irq failed\n"); goto err_free_hw; } - ret = request_irq(irq, ath_isr, IRQF_SHARED, "ath9k", sc); + ret = ath9k_init_device(AR5416_AR9100_DEVID, sc, 0x0, &ath_ahb_bus_ops); if (ret) { - dev_err(&pdev->dev, "request_irq failed\n"); - goto err_detach; + dev_err(&pdev->dev, "failed to initialize device\n"); + goto err_irq; } ah = sc->sc_ah; @@ -143,8 +146,8 @@ static int ath_ahb_probe(struct platform_device *pdev) return 0; - err_detach: - ath_detach(sc); + err_irq: + free_irq(irq, sc); err_free_hw: ieee80211_free_hw(hw); platform_set_drvdata(pdev, NULL); @@ -161,8 +164,12 @@ static int ath_ahb_remove(struct platform_device *pdev) if (hw) { struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_cleanup(sc); + ath9k_deinit_device(sc); + free_irq(sc->irq, sc); + ieee80211_free_hw(sc->hw); + ath_bus_cleanup(common); platform_set_drvdata(pdev, NULL); } diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 9f1f523e02e..bf3d4c4bfa5 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -33,11 +33,11 @@ struct ath_node; /* Macro to expand scalars to 64-bit objects */ -#define ito64(x) (sizeof(x) == 8) ? \ +#define ito64(x) (sizeof(x) == 1) ? \ (((unsigned long long int)(x)) & (0xff)) : \ - (sizeof(x) == 16) ? \ + (sizeof(x) == 2) ? \ (((unsigned long long int)(x)) & 0xffff) : \ - ((sizeof(x) == 32) ? \ + ((sizeof(x) == 4) ? \ (((unsigned long long int)(x)) & 0xffffffff) : \ (unsigned long long int)(x)) @@ -341,6 +341,12 @@ int ath_beaconq_config(struct ath_softc *sc); #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ +void ath_ani_calibrate(unsigned long data); + +/**********/ +/* BTCOEX */ +/**********/ + /* Defines the BT AR_BT_COEX_WGHT used */ enum ath_stomp_type { ATH_BTCOEX_NO_STOMP, @@ -361,6 +367,10 @@ struct ath_btcoex { struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ }; +int ath_init_btcoex_timer(struct ath_softc *sc); +void ath9k_btcoex_timer_resume(struct ath_softc *sc); +void ath9k_btcoex_timer_pause(struct ath_softc *sc); + /********************/ /* LED Control */ /********************/ @@ -385,6 +395,9 @@ struct ath_led { bool registered; }; +void ath_init_leds(struct ath_softc *sc); +void ath_deinit_leds(struct ath_softc *sc); + /********************/ /* Main driver core */ /********************/ @@ -403,26 +416,28 @@ struct ath_led { #define ATH_TXPOWER_MAX 100 /* .5 dBm units */ #define ATH_RATE_DUMMY_MARKER 0 -#define SC_OP_INVALID BIT(0) -#define SC_OP_BEACONS BIT(1) -#define SC_OP_RXAGGR BIT(2) -#define SC_OP_TXAGGR BIT(3) -#define SC_OP_FULL_RESET BIT(4) -#define SC_OP_PREAMBLE_SHORT BIT(5) -#define SC_OP_PROTECT_ENABLE BIT(6) -#define SC_OP_RXFLUSH BIT(7) -#define SC_OP_LED_ASSOCIATED BIT(8) -#define SC_OP_WAIT_FOR_BEACON BIT(12) -#define SC_OP_LED_ON BIT(13) -#define SC_OP_SCANNING BIT(14) -#define SC_OP_TSF_RESET BIT(15) -#define SC_OP_WAIT_FOR_CAB BIT(16) -#define SC_OP_WAIT_FOR_PSPOLL_DATA BIT(17) -#define SC_OP_WAIT_FOR_TX_ACK BIT(18) -#define SC_OP_BEACON_SYNC BIT(19) -#define SC_OP_BT_PRIORITY_DETECTED BIT(21) -#define SC_OP_NULLFUNC_COMPLETED BIT(22) -#define SC_OP_PS_ENABLED BIT(23) +#define SC_OP_INVALID BIT(0) +#define SC_OP_BEACONS BIT(1) +#define SC_OP_RXAGGR BIT(2) +#define SC_OP_TXAGGR BIT(3) +#define SC_OP_FULL_RESET BIT(4) +#define SC_OP_PREAMBLE_SHORT BIT(5) +#define SC_OP_PROTECT_ENABLE BIT(6) +#define SC_OP_RXFLUSH BIT(7) +#define SC_OP_LED_ASSOCIATED BIT(8) +#define SC_OP_LED_ON BIT(9) +#define SC_OP_SCANNING BIT(10) +#define SC_OP_TSF_RESET BIT(11) +#define SC_OP_BT_PRIORITY_DETECTED BIT(12) + +/* Powersave flags */ +#define PS_WAIT_FOR_BEACON BIT(0) +#define PS_WAIT_FOR_CAB BIT(1) +#define PS_WAIT_FOR_PSPOLL_DATA BIT(2) +#define PS_WAIT_FOR_TX_ACK BIT(3) +#define PS_BEACON_SYNC BIT(4) +#define PS_NULLFUNC_COMPLETED BIT(5) +#define PS_ENABLED BIT(6) struct ath_wiphy; struct ath_rate_table; @@ -458,6 +473,7 @@ struct ath_softc { u32 intrstatus; u32 sc_flags; /* SC_OP_* */ + u16 ps_flags; /* PS_* */ u16 curtxpow; u8 nbcnvifs; u16 nvifs; @@ -508,6 +524,7 @@ struct ath_wiphy { int chan_is_ht; }; +void ath9k_tasklet(unsigned long data); int ath_reset(struct ath_softc *sc, bool retry_tx); int ath_get_hal_qnum(u16 queue, struct ath_softc *sc); int ath_get_mac80211_qnum(u32 queue, struct ath_softc *sc); @@ -524,15 +541,15 @@ static inline void ath_bus_cleanup(struct ath_common *common) } extern struct ieee80211_ops ath9k_ops; +extern int modparam_nohwcrypt; irqreturn_t ath_isr(int irq, void *dev); -void ath_cleanup(struct ath_softc *sc); -int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, +int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, const struct ath_bus_ops *bus_ops); -void ath_detach(struct ath_softc *sc); +void ath9k_deinit_device(struct ath_softc *sc); const char *ath_mac_bb_name(u32 mac_bb_version); const char *ath_rf_name(u16 rf_version); -void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); +void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, struct ath9k_channel *ichan); void ath_update_chainmask(struct ath_softc *sc, int is_ht); @@ -541,6 +558,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw); void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw); +bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode); #ifdef CONFIG_PCI int ath_pci_init(void); @@ -582,4 +600,8 @@ void ath_mac80211_stop_queue(struct ath_softc *sc, u16 skb_queue); void ath_mac80211_start_queue(struct ath_softc *sc, u16 skb_queue); int ath_tx_get_qnum(struct ath_softc *sc, int qtype, int haltype); + +void ath_start_rfkill_poll(struct ath_softc *sc); +extern void ath9k_rfkill_poll_state(struct ieee80211_hw *hw); + #endif /* ATH9K_H */ diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 1660ef17aaf..422454fe4ff 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -480,7 +480,8 @@ void ath_beacon_tasklet(unsigned long data) sc->beacon.updateslot = COMMIT; /* commit next beacon */ sc->beacon.slotupdate = slot; } else if (sc->beacon.updateslot == COMMIT && sc->beacon.slotupdate == slot) { - ath9k_hw_setslottime(sc->sc_ah, sc->beacon.slottime); + ah->slottime = sc->beacon.slottime; + ath9k_hw_init_global_settings(ah); sc->beacon.updateslot = OK; } if (bfaddr != 0) { diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index 592f1b70f55..9489b6b25b5 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -580,6 +580,116 @@ static const struct file_operations fops_xmit = { .owner = THIS_MODULE }; +static ssize_t read_file_recv(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ +#define PHY_ERR(s, p) \ + len += snprintf(buf + len, size - len, "%18s : %10u\n", s, \ + sc->debug.stats.rxstats.phy_err_stats[p]); + + struct ath_softc *sc = file->private_data; + char *buf; + unsigned int len = 0, size = 1152; + ssize_t retval = 0; + + buf = kzalloc(size, GFP_KERNEL); + if (buf == NULL) + return 0; + + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "CRC ERR", + sc->debug.stats.rxstats.crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "DECRYPT CRC ERR", + sc->debug.stats.rxstats.decrypt_crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "PHY ERR", + sc->debug.stats.rxstats.phy_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "MIC ERR", + sc->debug.stats.rxstats.mic_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "PRE-DELIM CRC ERR", + sc->debug.stats.rxstats.pre_delim_crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "POST-DELIM CRC ERR", + sc->debug.stats.rxstats.post_delim_crc_err); + len += snprintf(buf + len, size - len, + "%18s : %10u\n", "DECRYPT BUSY ERR", + sc->debug.stats.rxstats.decrypt_busy_err); + + PHY_ERR("UNDERRUN", ATH9K_PHYERR_UNDERRUN); + PHY_ERR("TIMING", ATH9K_PHYERR_TIMING); + PHY_ERR("PARITY", ATH9K_PHYERR_PARITY); + PHY_ERR("RATE", ATH9K_PHYERR_RATE); + PHY_ERR("LENGTH", ATH9K_PHYERR_LENGTH); + PHY_ERR("RADAR", ATH9K_PHYERR_RADAR); + PHY_ERR("SERVICE", ATH9K_PHYERR_SERVICE); + PHY_ERR("TOR", ATH9K_PHYERR_TOR); + PHY_ERR("OFDM-TIMING", ATH9K_PHYERR_OFDM_TIMING); + PHY_ERR("OFDM-SIGNAL-PARITY", ATH9K_PHYERR_OFDM_SIGNAL_PARITY); + PHY_ERR("OFDM-RATE", ATH9K_PHYERR_OFDM_RATE_ILLEGAL); + PHY_ERR("OFDM-LENGTH", ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL); + PHY_ERR("OFDM-POWER-DROP", ATH9K_PHYERR_OFDM_POWER_DROP); + PHY_ERR("OFDM-SERVICE", ATH9K_PHYERR_OFDM_SERVICE); + PHY_ERR("OFDM-RESTART", ATH9K_PHYERR_OFDM_RESTART); + PHY_ERR("FALSE-RADAR-EXT", ATH9K_PHYERR_FALSE_RADAR_EXT); + PHY_ERR("CCK-TIMING", ATH9K_PHYERR_CCK_TIMING); + PHY_ERR("CCK-HEADER-CRC", ATH9K_PHYERR_CCK_HEADER_CRC); + PHY_ERR("CCK-RATE", ATH9K_PHYERR_CCK_RATE_ILLEGAL); + PHY_ERR("CCK-SERVICE", ATH9K_PHYERR_CCK_SERVICE); + PHY_ERR("CCK-RESTART", ATH9K_PHYERR_CCK_RESTART); + PHY_ERR("CCK-LENGTH", ATH9K_PHYERR_CCK_LENGTH_ILLEGAL); + PHY_ERR("CCK-POWER-DROP", ATH9K_PHYERR_CCK_POWER_DROP); + PHY_ERR("HT-CRC", ATH9K_PHYERR_HT_CRC_ERROR); + PHY_ERR("HT-LENGTH", ATH9K_PHYERR_HT_LENGTH_ILLEGAL); + PHY_ERR("HT-RATE", ATH9K_PHYERR_HT_RATE_ILLEGAL); + + retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); + kfree(buf); + + return retval; + +#undef PHY_ERR +} + +void ath_debug_stat_rx(struct ath_softc *sc, struct ath_buf *bf) +{ +#define RX_STAT_INC(c) sc->debug.stats.rxstats.c++ +#define RX_PHY_ERR_INC(c) sc->debug.stats.rxstats.phy_err_stats[c]++ + + struct ath_desc *ds = bf->bf_desc; + u32 phyerr; + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_CRC) + RX_STAT_INC(crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_DECRYPT) + RX_STAT_INC(decrypt_crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_MIC) + RX_STAT_INC(mic_err); + if (ds->ds_rxstat.rs_status & ATH9K_RX_DELIM_CRC_PRE) + RX_STAT_INC(pre_delim_crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RX_DELIM_CRC_POST) + RX_STAT_INC(post_delim_crc_err); + if (ds->ds_rxstat.rs_status & ATH9K_RX_DECRYPT_BUSY) + RX_STAT_INC(decrypt_busy_err); + + if (ds->ds_rxstat.rs_status & ATH9K_RXERR_PHY) { + RX_STAT_INC(phy_err); + phyerr = ds->ds_rxstat.rs_phyerr & 0x24; + RX_PHY_ERR_INC(phyerr); + } + +#undef RX_STAT_INC +#undef RX_PHY_ERR_INC +} + +static const struct file_operations fops_recv = { + .read = read_file_recv, + .open = ath9k_debugfs_open, + .owner = THIS_MODULE +}; + int ath9k_init_debug(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -632,6 +742,13 @@ int ath9k_init_debug(struct ath_hw *ah) if (!sc->debug.debugfs_xmit) goto err; + sc->debug.debugfs_recv = debugfs_create_file("recv", + S_IRUSR, + sc->debug.debugfs_phy, + sc, &fops_recv); + if (!sc->debug.debugfs_recv) + goto err; + return 0; err: ath9k_exit_debug(ah); @@ -643,6 +760,7 @@ void ath9k_exit_debug(struct ath_hw *ah) struct ath_common *common = ath9k_hw_common(ah); struct ath_softc *sc = (struct ath_softc *) common->priv; + debugfs_remove(sc->debug.debugfs_recv); debugfs_remove(sc->debug.debugfs_xmit); debugfs_remove(sc->debug.debugfs_wiphy); debugfs_remove(sc->debug.debugfs_rcstat); diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 536663e3ee1..86780e68b31 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -116,10 +116,35 @@ struct ath_tx_stats { u32 delim_underrun; }; +/** + * struct ath_rx_stats - RX Statistics + * @crc_err: No. of frames with incorrect CRC value + * @decrypt_crc_err: No. of frames whose CRC check failed after + decryption process completed + * @phy_err: No. of frames whose reception failed because the PHY + encountered an error + * @mic_err: No. of frames with incorrect TKIP MIC verification failure + * @pre_delim_crc_err: Pre-Frame delimiter CRC error detections + * @post_delim_crc_err: Post-Frame delimiter CRC error detections + * @decrypt_busy_err: Decryption interruptions counter + * @phy_err_stats: Individual PHY error statistics + */ +struct ath_rx_stats { + u32 crc_err; + u32 decrypt_crc_err; + u32 phy_err; + u32 mic_err; + u32 pre_delim_crc_err; + u32 post_delim_crc_err; + u32 decrypt_busy_err; + u32 phy_err_stats[ATH9K_PHYERR_MAX]; +}; + struct ath_stats { struct ath_interrupt_stats istats; struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; + struct ath_rx_stats rxstats; }; struct ath9k_debug { @@ -130,6 +155,7 @@ struct ath9k_debug { struct dentry *debugfs_rcstat; struct dentry *debugfs_wiphy; struct dentry *debugfs_xmit; + struct dentry *debugfs_recv; struct ath_stats stats; }; @@ -142,6 +168,7 @@ void ath_debug_stat_interrupt(struct ath_softc *sc, enum ath9k_int status); void ath_debug_stat_rc(struct ath_softc *sc, int final_rate); void ath_debug_stat_tx(struct ath_softc *sc, struct ath_txq *txq, struct ath_buf *bf); +void ath_debug_stat_rx(struct ath_softc *sc, struct ath_buf *bf); void ath_debug_stat_retries(struct ath_softc *sc, int rix, int xretries, int retries, u8 per); @@ -181,6 +208,11 @@ static inline void ath_debug_stat_tx(struct ath_softc *sc, { } +static inline void ath_debug_stat_rx(struct ath_softc *sc, + struct ath_buf *bf) +{ +} + static inline void ath_debug_stat_retries(struct ath_softc *sc, int rix, int xretries, int retries, u8 per) { diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c new file mode 100644 index 00000000000..e204bd25ff6 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +/********************************/ +/* LED functions */ +/********************************/ + +static void ath_led_blink_work(struct work_struct *work) +{ + struct ath_softc *sc = container_of(work, struct ath_softc, + ath_led_blink_work.work); + + if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) + return; + + if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || + (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); + else + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, + (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); + + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, + (sc->sc_flags & SC_OP_LED_ON) ? + msecs_to_jiffies(sc->led_off_duration) : + msecs_to_jiffies(sc->led_on_duration)); + + sc->led_on_duration = sc->led_on_cnt ? + max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : + ATH_LED_ON_DURATION_IDLE; + sc->led_off_duration = sc->led_off_cnt ? + max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : + ATH_LED_OFF_DURATION_IDLE; + sc->led_on_cnt = sc->led_off_cnt = 0; + if (sc->sc_flags & SC_OP_LED_ON) + sc->sc_flags &= ~SC_OP_LED_ON; + else + sc->sc_flags |= SC_OP_LED_ON; +} + +static void ath_led_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); + struct ath_softc *sc = led->sc; + + switch (brightness) { + case LED_OFF: + if (led->led_type == ATH_LED_ASSOC || + led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, + (led->led_type == ATH_LED_RADIO)); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + if (led->led_type == ATH_LED_RADIO) + sc->sc_flags &= ~SC_OP_LED_ON; + } else { + sc->led_off_cnt++; + } + break; + case LED_FULL: + if (led->led_type == ATH_LED_ASSOC) { + sc->sc_flags |= SC_OP_LED_ASSOCIATED; + ieee80211_queue_delayed_work(sc->hw, + &sc->ath_led_blink_work, 0); + } else if (led->led_type == ATH_LED_RADIO) { + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); + sc->sc_flags |= SC_OP_LED_ON; + } else { + sc->led_on_cnt++; + } + break; + default: + break; + } +} + +static int ath_register_led(struct ath_softc *sc, struct ath_led *led, + char *trigger) +{ + int ret; + + led->sc = sc; + led->led_cdev.name = led->name; + led->led_cdev.default_trigger = trigger; + led->led_cdev.brightness_set = ath_led_brightness; + + ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); + if (ret) + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, + "Failed to register led:%s", led->name); + else + led->registered = 1; + return ret; +} + +static void ath_unregister_led(struct ath_led *led) +{ + if (led->registered) { + led_classdev_unregister(&led->led_cdev); + led->registered = 0; + } +} + +void ath_deinit_leds(struct ath_softc *sc) +{ + ath_unregister_led(&sc->assoc_led); + sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; + ath_unregister_led(&sc->tx_led); + ath_unregister_led(&sc->rx_led); + ath_unregister_led(&sc->radio_led); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); +} + +void ath_init_leds(struct ath_softc *sc) +{ + char *trigger; + int ret; + + if (AR_SREV_9287(sc->sc_ah)) + sc->sc_ah->led_pin = ATH_LED_PIN_9287; + else + sc->sc_ah->led_pin = ATH_LED_PIN_DEF; + + /* Configure gpio 1 for output */ + ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, + AR_GPIO_OUTPUT_MUX_AS_OUTPUT); + /* LED off, active low */ + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); + + INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); + + trigger = ieee80211_get_radio_led_name(sc->hw); + snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), + "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->radio_led, trigger); + sc->radio_led.led_type = ATH_LED_RADIO; + if (ret) + goto fail; + + trigger = ieee80211_get_assoc_led_name(sc->hw); + snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), + "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->assoc_led, trigger); + sc->assoc_led.led_type = ATH_LED_ASSOC; + if (ret) + goto fail; + + trigger = ieee80211_get_tx_led_name(sc->hw); + snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), + "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->tx_led, trigger); + sc->tx_led.led_type = ATH_LED_TX; + if (ret) + goto fail; + + trigger = ieee80211_get_rx_led_name(sc->hw); + snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), + "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); + ret = ath_register_led(sc, &sc->rx_led, trigger); + sc->rx_led.led_type = ATH_LED_RX; + if (ret) + goto fail; + + return; + +fail: + cancel_delayed_work_sync(&sc->ath_led_blink_work); + ath_deinit_leds(sc); +} + +/*******************/ +/* Rfkill */ +/*******************/ + +static bool ath_is_rfkill_set(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == + ah->rfkill_polarity; +} + +void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + bool blocked = !!ath_is_rfkill_set(sc); + + wiphy_rfkill_set_hw_state(hw->wiphy, blocked); +} + +void ath_start_rfkill_poll(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + + if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) + wiphy_rfkill_start_polling(sc->hw->wiphy); +} + +/******************/ +/* BTCOEX */ +/******************/ + +/* + * Detects if there is any priority bt traffic + */ +static void ath_detect_bt_priority(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio)) + btcoex->bt_priority_cnt++; + + if (time_after(jiffies, btcoex->bt_priority_time + + msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { + if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { + ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, + "BT priority traffic detected"); + sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; + } else { + sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + } + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + } +} + +/* + * Configures appropriate weight based on stomp type. + */ +static void ath9k_btcoex_bt_stomp(struct ath_softc *sc, + enum ath_stomp_type stomp_type) +{ + struct ath_hw *ah = sc->sc_ah; + + switch (stomp_type) { + case ATH_BTCOEX_STOMP_ALL: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_ALL_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_LOW: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_LOW_WLAN_WGHT); + break; + case ATH_BTCOEX_STOMP_NONE: + ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, + AR_STOMP_NONE_WLAN_WGHT); + break; + default: + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Invalid Stomptype\n"); + break; + } + + ath9k_hw_btcoex_enable(ah); +} + +static void ath9k_gen_timer_start(struct ath_hw *ah, + struct ath_gen_timer *timer, + u32 timer_next, + u32 timer_period) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period); + + if ((sc->imask & ATH9K_INT_GENTIMER) == 0) { + ath9k_hw_set_interrupts(ah, 0); + sc->imask |= ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, sc->imask); + } +} + +static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) +{ + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; + + ath9k_hw_gen_timer_stop(ah, timer); + + /* if no timer is enabled, turn off interrupt mask */ + if (timer_table->timer_mask.val == 0) { + ath9k_hw_set_interrupts(ah, 0); + sc->imask &= ~ATH9K_INT_GENTIMER; + ath9k_hw_set_interrupts(ah, sc->imask); + } +} + +/* + * This is the master bt coex timer which runs for every + * 45ms, bt traffic will be given priority during 55% of this + * period while wlan gets remaining 45% + */ +static void ath_btcoex_period_timer(unsigned long data) +{ + struct ath_softc *sc = (struct ath_softc *) data; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + + ath_detect_bt_priority(sc); + + spin_lock_bh(&btcoex->btcoex_lock); + + ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type); + + spin_unlock_bh(&btcoex->btcoex_lock); + + if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); + + ath9k_gen_timer_start(ah, + btcoex->no_stomp_timer, + (ath9k_hw_gettsf32(ah) + + btcoex->btcoex_no_stomp), + btcoex->btcoex_no_stomp * 10); + btcoex->hw_timer_enabled = true; + } + + mod_timer(&btcoex->period_timer, jiffies + + msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); +} + +/* + * Generic tsf based hw timer which configures weight + * registers to time slice between wlan and bt traffic + */ +static void ath_btcoex_no_stomp_timer(void *arg) +{ + struct ath_softc *sc = (struct ath_softc *)arg; + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "no stomp timer running \n"); + + spin_lock_bh(&btcoex->btcoex_lock); + + if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) + ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE); + else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW); + + spin_unlock_bh(&btcoex->btcoex_lock); +} + +int ath_init_btcoex_timer(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + + btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; + btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * + btcoex->btcoex_period / 100; + + setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, + (unsigned long) sc); + + spin_lock_init(&btcoex->btcoex_lock); + + btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, + ath_btcoex_no_stomp_timer, + ath_btcoex_no_stomp_timer, + (void *) sc, AR_FIRST_NDP_TIMER); + + if (!btcoex->no_stomp_timer) + return -ENOMEM; + + return 0; +} + +/* + * (Re)start btcoex timers + */ +void ath9k_btcoex_timer_resume(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, + "Starting btcoex timers"); + + /* make sure duty cycle timer is also stopped when resuming */ + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); + + btcoex->bt_priority_cnt = 0; + btcoex->bt_priority_time = jiffies; + sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; + + mod_timer(&btcoex->period_timer, jiffies); +} + + +/* + * Pause btcoex timer and bt duty cycle timer + */ +void ath9k_btcoex_timer_pause(struct ath_softc *sc) +{ + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_hw *ah = sc->sc_ah; + + del_timer_sync(&btcoex->period_timer); + + if (btcoex->hw_timer_enabled) + ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); + + btcoex->hw_timer_enabled = false; +} diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 9474f9f6d40..0b1dd10f1d8 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -52,28 +52,6 @@ module_exit(ath9k_exit); /* Helper Functions */ /********************/ -static u32 ath9k_hw_mac_usec(struct ath_hw *ah, u32 clks) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - - if (!ah->curchan) /* should really check for CCK instead */ - return clks / ATH9K_CLOCK_RATE_CCK; - if (conf->channel->band == IEEE80211_BAND_2GHZ) - return clks / ATH9K_CLOCK_RATE_2GHZ_OFDM; - - return clks / ATH9K_CLOCK_RATE_5GHZ_OFDM; -} - -static u32 ath9k_hw_mac_to_usec(struct ath_hw *ah, u32 clks) -{ - struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; - - if (conf_is_ht40(conf)) - return ath9k_hw_mac_usec(ah, clks) / 2; - else - return ath9k_hw_mac_usec(ah, clks); -} - static u32 ath9k_hw_mac_clks(struct ath_hw *ah, u32 usecs) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; @@ -413,8 +391,6 @@ static void ath9k_hw_init_defaults(struct ath_hw *ah) ah->beacon_interval = 100; ah->enable_32kHz_clock = DONT_USE_32KHZ; ah->slottime = (u32) -1; - ah->acktimeout = (u32) -1; - ah->ctstimeout = (u32) -1; ah->globaltxtimeout = (u32) -1; ah->power_mode = ATH9K_PM_UNDEFINED; } @@ -1180,34 +1156,25 @@ static void ath9k_hw_init_interrupt_masks(struct ath_hw *ah, } } -static bool ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) +static void ath9k_hw_setslottime(struct ath_hw *ah, u32 us) { - if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_ACK))) { - ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, - "bad ack timeout %u\n", us); - ah->acktimeout = (u32) -1; - return false; - } else { - REG_RMW_FIELD(ah, AR_TIME_OUT, - AR_TIME_OUT_ACK, ath9k_hw_mac_to_clks(ah, us)); - ah->acktimeout = us; - return true; - } + u32 val = ath9k_hw_mac_to_clks(ah, us); + val = min(val, (u32) 0xFFFF); + REG_WRITE(ah, AR_D_GBL_IFS_SLOT, val); } -static bool ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) +static void ath9k_hw_set_ack_timeout(struct ath_hw *ah, u32 us) { - if (us > ath9k_hw_mac_to_usec(ah, MS(0xffffffff, AR_TIME_OUT_CTS))) { - ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, - "bad cts timeout %u\n", us); - ah->ctstimeout = (u32) -1; - return false; - } else { - REG_RMW_FIELD(ah, AR_TIME_OUT, - AR_TIME_OUT_CTS, ath9k_hw_mac_to_clks(ah, us)); - ah->ctstimeout = us; - return true; - } + u32 val = ath9k_hw_mac_to_clks(ah, us); + val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_ACK)); + REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_ACK, val); +} + +static void ath9k_hw_set_cts_timeout(struct ath_hw *ah, u32 us) +{ + u32 val = ath9k_hw_mac_to_clks(ah, us); + val = min(val, (u32) MS(0xFFFFFFFF, AR_TIME_OUT_CTS)); + REG_RMW_FIELD(ah, AR_TIME_OUT, AR_TIME_OUT_CTS, val); } static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) @@ -1224,25 +1191,37 @@ static bool ath9k_hw_set_global_txtimeout(struct ath_hw *ah, u32 tu) } } -static void ath9k_hw_init_user_settings(struct ath_hw *ah) +void ath9k_hw_init_global_settings(struct ath_hw *ah) { + struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; + int acktimeout; + int slottime; + int sifstime; + ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, "ah->misc_mode 0x%x\n", ah->misc_mode); if (ah->misc_mode != 0) REG_WRITE(ah, AR_PCU_MISC, REG_READ(ah, AR_PCU_MISC) | ah->misc_mode); - if (ah->slottime != (u32) -1) - ath9k_hw_setslottime(ah, ah->slottime); - if (ah->acktimeout != (u32) -1) - ath9k_hw_set_ack_timeout(ah, ah->acktimeout); - if (ah->ctstimeout != (u32) -1) - ath9k_hw_set_cts_timeout(ah, ah->ctstimeout); + + if (conf->channel && conf->channel->band == IEEE80211_BAND_5GHZ) + sifstime = 16; + else + sifstime = 10; + + /* As defined by IEEE 802.11-2007 17.3.8.6 */ + slottime = ah->slottime + 3 * ah->coverage_class; + acktimeout = slottime + sifstime; + ath9k_hw_setslottime(ah, slottime); + ath9k_hw_set_ack_timeout(ah, acktimeout); + ath9k_hw_set_cts_timeout(ah, acktimeout); if (ah->globaltxtimeout != (u32) -1) ath9k_hw_set_global_txtimeout(ah, ah->globaltxtimeout); } +EXPORT_SYMBOL(ath9k_hw_init_global_settings); -void ath9k_hw_detach(struct ath_hw *ah) +void ath9k_hw_deinit(struct ath_hw *ah) { struct ath_common *common = ath9k_hw_common(ah); @@ -1260,7 +1239,7 @@ free_hw: kfree(ah); ah = NULL; } -EXPORT_SYMBOL(ath9k_hw_detach); +EXPORT_SYMBOL(ath9k_hw_deinit); /*******/ /* INI */ @@ -2061,7 +2040,7 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) ath9k_enable_rfkill(ah); - ath9k_hw_init_user_settings(ah); + ath9k_hw_init_global_settings(ah); if (AR_SREV_9287_12_OR_LATER(ah)) { REG_WRITE(ah, AR_D_GBL_IFS_SIFS, @@ -3658,21 +3637,6 @@ u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp) } EXPORT_SYMBOL(ath9k_hw_extend_tsf); -bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us) -{ - if (us < ATH9K_SLOT_TIME_9 || us > ath9k_hw_mac_to_usec(ah, 0xffff)) { - ath_print(ath9k_hw_common(ah), ATH_DBG_RESET, - "bad slot time %u\n", us); - ah->slottime = (u32) -1; - return false; - } else { - REG_WRITE(ah, AR_D_GBL_IFS_SLOT, ath9k_hw_mac_to_clks(ah, us)); - ah->slottime = us; - return true; - } -} -EXPORT_SYMBOL(ath9k_hw_setslottime); - void ath9k_hw_set11nmac2040(struct ath_hw *ah) { struct ieee80211_conf *conf = &ath9k_hw_common(ah)->hw->conf; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 8849450dc59..ab1f1981d85 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -551,10 +551,9 @@ struct ath_hw { u32 *bank6Temp; int16_t txpower_indexoffset; + int coverage_class; u32 beacon_interval; u32 slottime; - u32 acktimeout; - u32 ctstimeout; u32 globaltxtimeout; /* ANI */ @@ -616,7 +615,7 @@ static inline struct ath_regulatory *ath9k_hw_regulatory(struct ath_hw *ah) /* Initialization, Detach, Reset */ const char *ath9k_hw_probe(u16 vendorid, u16 devid); -void ath9k_hw_detach(struct ath_hw *ah); +void ath9k_hw_deinit(struct ath_hw *ah); int ath9k_hw_init(struct ath_hw *ah); int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan, bool bChannelChange); @@ -668,7 +667,7 @@ void ath9k_hw_settsf64(struct ath_hw *ah, u64 tsf64); void ath9k_hw_reset_tsf(struct ath_hw *ah); void ath9k_hw_set_tsfadjust(struct ath_hw *ah, u32 setting); u64 ath9k_hw_extend_tsf(struct ath_hw *ah, u32 rstamp); -bool ath9k_hw_setslottime(struct ath_hw *ah, u32 us); +void ath9k_hw_init_global_settings(struct ath_hw *ah); void ath9k_hw_set11nmac2040(struct ath_hw *ah); void ath9k_hw_beaconinit(struct ath_hw *ah, u32 next_beacon, u32 beacon_period); void ath9k_hw_set_sta_beacon_timers(struct ath_hw *ah, diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c new file mode 100644 index 00000000000..5f78d7a5ff2 --- /dev/null +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -0,0 +1,861 @@ +/* + * Copyright (c) 2008-2009 Atheros Communications Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ath9k.h" + +static char *dev_info = "ath9k"; + +MODULE_AUTHOR("Atheros Communications"); +MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); +MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); +MODULE_LICENSE("Dual BSD/GPL"); + +static unsigned int ath9k_debug = ATH_DBG_DEFAULT; +module_param_named(debug, ath9k_debug, uint, 0); +MODULE_PARM_DESC(debug, "Debugging mask"); + +int modparam_nohwcrypt; +module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); +MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); + +/* We use the hw_value as an index into our private channel structure */ + +#define CHAN2G(_freq, _idx) { \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +#define CHAN5G(_freq, _idx) { \ + .band = IEEE80211_BAND_5GHZ, \ + .center_freq = (_freq), \ + .hw_value = (_idx), \ + .max_power = 20, \ +} + +/* Some 2 GHz radios are actually tunable on 2312-2732 + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_2ghz_chantable[] = { + CHAN2G(2412, 0), /* Channel 1 */ + CHAN2G(2417, 1), /* Channel 2 */ + CHAN2G(2422, 2), /* Channel 3 */ + CHAN2G(2427, 3), /* Channel 4 */ + CHAN2G(2432, 4), /* Channel 5 */ + CHAN2G(2437, 5), /* Channel 6 */ + CHAN2G(2442, 6), /* Channel 7 */ + CHAN2G(2447, 7), /* Channel 8 */ + CHAN2G(2452, 8), /* Channel 9 */ + CHAN2G(2457, 9), /* Channel 10 */ + CHAN2G(2462, 10), /* Channel 11 */ + CHAN2G(2467, 11), /* Channel 12 */ + CHAN2G(2472, 12), /* Channel 13 */ + CHAN2G(2484, 13), /* Channel 14 */ +}; + +/* Some 5 GHz radios are actually tunable on XXXX-YYYY + * on 5 MHz steps, we support the channels which we know + * we have calibration data for all cards though to make + * this static */ +static struct ieee80211_channel ath9k_5ghz_chantable[] = { + /* _We_ call this UNII 1 */ + CHAN5G(5180, 14), /* Channel 36 */ + CHAN5G(5200, 15), /* Channel 40 */ + CHAN5G(5220, 16), /* Channel 44 */ + CHAN5G(5240, 17), /* Channel 48 */ + /* _We_ call this UNII 2 */ + CHAN5G(5260, 18), /* Channel 52 */ + CHAN5G(5280, 19), /* Channel 56 */ + CHAN5G(5300, 20), /* Channel 60 */ + CHAN5G(5320, 21), /* Channel 64 */ + /* _We_ call this "Middle band" */ + CHAN5G(5500, 22), /* Channel 100 */ + CHAN5G(5520, 23), /* Channel 104 */ + CHAN5G(5540, 24), /* Channel 108 */ + CHAN5G(5560, 25), /* Channel 112 */ + CHAN5G(5580, 26), /* Channel 116 */ + CHAN5G(5600, 27), /* Channel 120 */ + CHAN5G(5620, 28), /* Channel 124 */ + CHAN5G(5640, 29), /* Channel 128 */ + CHAN5G(5660, 30), /* Channel 132 */ + CHAN5G(5680, 31), /* Channel 136 */ + CHAN5G(5700, 32), /* Channel 140 */ + /* _We_ call this UNII 3 */ + CHAN5G(5745, 33), /* Channel 149 */ + CHAN5G(5765, 34), /* Channel 153 */ + CHAN5G(5785, 35), /* Channel 157 */ + CHAN5G(5805, 36), /* Channel 161 */ + CHAN5G(5825, 37), /* Channel 165 */ +}; + +/* Atheros hardware rate code addition for short premble */ +#define SHPCHECK(__hw_rate, __flags) \ + ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) + +#define RATE(_bitrate, _hw_rate, _flags) { \ + .bitrate = (_bitrate), \ + .flags = (_flags), \ + .hw_value = (_hw_rate), \ + .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ +} + +static struct ieee80211_rate ath9k_legacy_rates[] = { + RATE(10, 0x1b, 0), + RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), + RATE(60, 0x0b, 0), + RATE(90, 0x0f, 0), + RATE(120, 0x0a, 0), + RATE(180, 0x0e, 0), + RATE(240, 0x09, 0), + RATE(360, 0x0d, 0), + RATE(480, 0x08, 0), + RATE(540, 0x0c, 0), +}; + +static void ath9k_deinit_softc(struct ath_softc *sc); + +/* + * Read and write, they both share the same lock. We do this to serialize + * reads and writes on Atheros 802.11n PCI devices only. This is required + * as the FIFO on these devices can only accept sanely 2 requests. + */ + +static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_serial_rw, flags); + iowrite32(val, sc->mem + reg_offset); + spin_unlock_irqrestore(&sc->sc_serial_rw, flags); + } else + iowrite32(val, sc->mem + reg_offset); +} + +static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) +{ + struct ath_hw *ah = (struct ath_hw *) hw_priv; + struct ath_common *common = ath9k_hw_common(ah); + struct ath_softc *sc = (struct ath_softc *) common->priv; + u32 val; + + if (ah->config.serialize_regmode == SER_REG_MODE_ON) { + unsigned long flags; + spin_lock_irqsave(&sc->sc_serial_rw, flags); + val = ioread32(sc->mem + reg_offset); + spin_unlock_irqrestore(&sc->sc_serial_rw, flags); + } else + val = ioread32(sc->mem + reg_offset); + return val; +} + +static const struct ath_ops ath9k_common_ops = { + .read = ath9k_ioread32, + .write = ath9k_iowrite32, +}; + +/**************************/ +/* Initialization */ +/**************************/ + +static void setup_ht_cap(struct ath_softc *sc, + struct ieee80211_sta_ht_cap *ht_info) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + u8 tx_streams, rx_streams; + + ht_info->ht_supported = true; + ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | + IEEE80211_HT_CAP_SM_PS | + IEEE80211_HT_CAP_SGI_40 | + IEEE80211_HT_CAP_DSSSCCK40; + + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; + + /* set up supported mcs set */ + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ? + 1 : 2; + rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ? + 1 : 2; + + if (tx_streams != rx_streams) { + ath_print(common, ATH_DBG_CONFIG, + "TX streams %d, RX streams: %d\n", + tx_streams, rx_streams); + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } + + ht_info->mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + ht_info->mcs.rx_mask[1] = 0xff; + + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; +} + +static int ath9k_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah); + + return ath_reg_notifier_apply(wiphy, request, reg); +} + +/* + * This function will allocate both the DMA descriptor structure, and the + * buffers it contains. These are used to contain the descriptors used + * by the system. +*/ +int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, + struct list_head *head, const char *name, + int nbuf, int ndesc) +{ +#define DS2PHYS(_dd, _ds) \ + ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) +#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) +#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_desc *ds; + struct ath_buf *bf; + int i, bsize, error; + + ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", + name, nbuf, ndesc); + + INIT_LIST_HEAD(head); + /* ath_desc must be a multiple of DWORDs */ + if ((sizeof(struct ath_desc) % 4) != 0) { + ath_print(common, ATH_DBG_FATAL, + "ath_desc not DWORD aligned\n"); + BUG_ON((sizeof(struct ath_desc) % 4) != 0); + error = -ENOMEM; + goto fail; + } + + dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; + + /* + * Need additional DMA memory because we can't use + * descriptors that cross the 4K page boundary. Assume + * one skipped descriptor per 4K page. + */ + if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { + u32 ndesc_skipped = + ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); + u32 dma_len; + + while (ndesc_skipped) { + dma_len = ndesc_skipped * sizeof(struct ath_desc); + dd->dd_desc_len += dma_len; + + ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); + }; + } + + /* allocate descriptors */ + dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, + &dd->dd_desc_paddr, GFP_KERNEL); + if (dd->dd_desc == NULL) { + error = -ENOMEM; + goto fail; + } + ds = dd->dd_desc; + ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", + name, ds, (u32) dd->dd_desc_len, + ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); + + /* allocate buffers */ + bsize = sizeof(struct ath_buf) * nbuf; + bf = kzalloc(bsize, GFP_KERNEL); + if (bf == NULL) { + error = -ENOMEM; + goto fail2; + } + dd->dd_bufptr = bf; + + for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + + if (!(sc->sc_ah->caps.hw_caps & + ATH9K_HW_CAP_4KB_SPLITTRANS)) { + /* + * Skip descriptor addresses which can cause 4KB + * boundary crossing (addr + length) with a 32 dword + * descriptor fetch. + */ + while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { + BUG_ON((caddr_t) bf->bf_desc >= + ((caddr_t) dd->dd_desc + + dd->dd_desc_len)); + + ds += ndesc; + bf->bf_desc = ds; + bf->bf_daddr = DS2PHYS(dd, ds); + } + } + list_add_tail(&bf->list, head); + } + return 0; +fail2: + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); +fail: + memset(dd, 0, sizeof(*dd)); + return error; +#undef ATH_DESC_4KB_BOUND_CHECK +#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED +#undef DS2PHYS +} + +static void ath9k_init_crypto(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + + /* Get the hardware key cache size. */ + common->keymax = sc->sc_ah->caps.keycache_size; + if (common->keymax > ATH_KEYMAX) { + ath_print(common, ATH_DBG_ANY, + "Warning, using only %u entries in %u key cache\n", + ATH_KEYMAX, common->keymax); + common->keymax = ATH_KEYMAX; + } + + /* + * Reset the key cache since some parts do not + * reset the contents on initial power up. + */ + for (i = 0; i < common->keymax; i++) + ath9k_hw_keyreset(sc->sc_ah, (u16) i); + + if (ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL)) { + /* + * Whether we should enable h/w TKIP MIC. + * XXX: if we don't support WME TKIP MIC, then we wouldn't + * report WMM capable, so it's always safe to turn on + * TKIP MIC in this case. + */ + ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, 0, 1, NULL); + } + + /* + * Check whether the separate key cache entries + * are required to handle both tx+rx MIC keys. + * With split mic keys the number of stations is limited + * to 27 otherwise 59. + */ + if (ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_TKIP, NULL) + && ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_CIPHER, + ATH9K_CIPHER_MIC, NULL) + && ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_TKIP_SPLIT, + 0, NULL)) + common->splitmic = 1; + + /* turn on mcast key search if possible */ + if (!ath9k_hw_getcapability(sc->sc_ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) + (void)ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_MCAST_KEYSRCH, + 1, 1, NULL); + +} + +static int ath9k_init_btcoex(struct ath_softc *sc) +{ + int r, qnum; + + switch (sc->sc_ah->btcoex_hw.scheme) { + case ATH_BTCOEX_CFG_NONE: + break; + case ATH_BTCOEX_CFG_2WIRE: + ath9k_hw_btcoex_init_2wire(sc->sc_ah); + break; + case ATH_BTCOEX_CFG_3WIRE: + ath9k_hw_btcoex_init_3wire(sc->sc_ah); + r = ath_init_btcoex_timer(sc); + if (r) + return -1; + qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); + ath9k_hw_init_btcoex_hw(sc->sc_ah, qnum); + sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; + break; + default: + WARN_ON(1); + break; + } + + return 0; +} + +static int ath9k_init_queues(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + + for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) + sc->tx.hwq_map[i] = -1; + + sc->beacon.beaconq = ath9k_hw_beaconq_setup(sc->sc_ah); + if (sc->beacon.beaconq == -1) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup a beacon xmit queue\n"); + goto err; + } + + sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); + if (sc->beacon.cabq == NULL) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup CAB xmit queue\n"); + goto err; + } + + sc->config.cabqReadytime = ATH_CABQ_READY_TIME; + ath_cabq_update(sc); + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BK traffic\n"); + goto err; + } + + if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for BE traffic\n"); + goto err; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VI traffic\n"); + goto err; + } + if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { + ath_print(common, ATH_DBG_FATAL, + "Unable to setup xmit queue for VO traffic\n"); + goto err; + } + + return 0; + +err: + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + return -EIO; +} + +static void ath9k_init_channels_rates(struct ath_softc *sc) +{ + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; + sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; + sc->sbands[IEEE80211_BAND_2GHZ].n_channels = + ARRAY_SIZE(ath9k_2ghz_chantable); + sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; + sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates); + } + + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { + sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; + sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; + sc->sbands[IEEE80211_BAND_5GHZ].n_channels = + ARRAY_SIZE(ath9k_5ghz_chantable); + sc->sbands[IEEE80211_BAND_5GHZ].bitrates = + ath9k_legacy_rates + 4; + sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = + ARRAY_SIZE(ath9k_legacy_rates) - 4; + } +} + +static void ath9k_init_misc(struct ath_softc *sc) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + int i = 0; + + common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; + setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); + + sc->config.txpowlimit = ATH_TXPOWER_MAX; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + sc->sc_flags |= SC_OP_TXAGGR; + sc->sc_flags |= SC_OP_RXAGGR; + } + + common->tx_chainmask = sc->sc_ah->caps.tx_chainmask; + common->rx_chainmask = sc->sc_ah->caps.rx_chainmask; + + ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); + sc->rx.defant = ath9k_hw_getdefantenna(sc->sc_ah); + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) + memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); + + sc->beacon.slottime = ATH9K_SLOT_TIME_9; + + for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { + sc->beacon.bslot[i] = NULL; + sc->beacon.bslot_aphy[i] = NULL; + } +} + +static int ath9k_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops) +{ + struct ath_hw *ah = NULL; + struct ath_common *common; + int ret = 0, i; + int csz = 0; + + ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); + if (!ah) + return -ENOMEM; + + ah->hw_version.devid = devid; + ah->hw_version.subsysid = subsysid; + sc->sc_ah = ah; + + common = ath9k_hw_common(ah); + common->ops = &ath9k_common_ops; + common->bus_ops = bus_ops; + common->ah = ah; + common->hw = sc->hw; + common->priv = sc; + common->debug_mask = ath9k_debug; + + spin_lock_init(&sc->wiphy_lock); + spin_lock_init(&sc->sc_resetlock); + spin_lock_init(&sc->sc_serial_rw); + spin_lock_init(&sc->sc_pm_lock); + mutex_init(&sc->mutex); + tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); + tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, + (unsigned long)sc); + + /* + * Cache line size is used to size and align various + * structures used to communicate with the hardware. + */ + ath_read_cachesize(common, &csz); + common->cachelsz = csz << 2; /* convert to bytes */ + + ret = ath9k_hw_init(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to initialize hardware; " + "initialization status: %d\n", ret); + goto err_hw; + } + + ret = ath9k_init_debug(ah); + if (ret) { + ath_print(common, ATH_DBG_FATAL, + "Unable to create debugfs files\n"); + goto err_debug; + } + + ret = ath9k_init_queues(sc); + if (ret) + goto err_queues; + + ret = ath9k_init_btcoex(sc); + if (ret) + goto err_btcoex; + + ath9k_init_crypto(sc); + ath9k_init_channels_rates(sc); + ath9k_init_misc(sc); + + return 0; + +err_btcoex: + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); +err_queues: + ath9k_exit_debug(ah); +err_debug: + ath9k_hw_deinit(ah); +err_hw: + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); + + kfree(ah); + sc->sc_ah = NULL; + + return ret; +} + +void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) +{ + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + + hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | + IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | + IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_PS_NULLFUNC_STACK | + IEEE80211_HW_SPECTRUM_MGMT; + + if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt) + hw->flags |= IEEE80211_HW_MFP_CAPABLE; + + hw->wiphy->interface_modes = + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_MESH_POINT); + + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->queues = 4; + hw->max_rates = 4; + hw->channel_change_time = 5000; + hw->max_listen_interval = 10; + /* Hardware supports 10 but we use 4 */ + hw->max_rate_tries = 4; + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); + + hw->rate_control_algorithm = "ath9k_rate_control"; + + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &sc->sbands[IEEE80211_BAND_2GHZ]; + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &sc->sbands[IEEE80211_BAND_5GHZ]; + + if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) { + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); + if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) + setup_ht_cap(sc, &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); + } + + SET_IEEE80211_PERM_ADDR(hw, common->macaddr); +} + +int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, + const struct ath_bus_ops *bus_ops) +{ + struct ieee80211_hw *hw = sc->hw; + struct ath_common *common; + struct ath_hw *ah; + int error = 0; + struct ath_regulatory *reg; + + /* Bring up device */ + error = ath9k_init_softc(devid, sc, subsysid, bus_ops); + if (error != 0) + goto error_init; + + ah = sc->sc_ah; + common = ath9k_hw_common(ah); + ath9k_set_hw_capab(sc, hw); + + /* Initialize regulatory */ + error = ath_regd_init(&common->regulatory, sc->hw->wiphy, + ath9k_reg_notifier); + if (error) + goto error_regd; + + reg = &common->regulatory; + + /* Setup TX DMA */ + error = ath_tx_init(sc, ATH_TXBUF); + if (error != 0) + goto error_tx; + + /* Setup RX DMA */ + error = ath_rx_init(sc, ATH_RXBUF); + if (error != 0) + goto error_rx; + + /* Register with mac80211 */ + error = ieee80211_register_hw(hw); + if (error) + goto error_register; + + /* Handle world regulatory */ + if (!ath_is_world_regd(reg)) { + error = regulatory_hint(hw->wiphy, reg->alpha2); + if (error) + goto error_world; + } + + INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); + INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); + sc->wiphy_scheduler_int = msecs_to_jiffies(500); + + ath_init_leds(sc); + ath_start_rfkill_poll(sc); + + return 0; + +error_world: + ieee80211_unregister_hw(hw); +error_register: + ath_rx_cleanup(sc); +error_rx: + ath_tx_cleanup(sc); +error_tx: + /* Nothing */ +error_regd: + ath9k_deinit_softc(sc); +error_init: + return error; +} + +/*****************************/ +/* De-Initialization */ +/*****************************/ + +static void ath9k_deinit_softc(struct ath_softc *sc) +{ + int i = 0; + + if ((sc->btcoex.no_stomp_timer) && + sc->sc_ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) + ath_gen_timer_free(sc->sc_ah, sc->btcoex.no_stomp_timer); + + for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) + if (ATH_TXQ_SETUP(sc, i)) + ath_tx_cleanupq(sc, &sc->tx.txq[i]); + + ath9k_exit_debug(sc->sc_ah); + ath9k_hw_deinit(sc->sc_ah); + + tasklet_kill(&sc->intr_tq); + tasklet_kill(&sc->bcon_tasklet); +} + +void ath9k_deinit_device(struct ath_softc *sc) +{ + struct ieee80211_hw *hw = sc->hw; + int i = 0; + + ath9k_ps_wakeup(sc); + + wiphy_rfkill_stop_polling(sc->hw->wiphy); + ath_deinit_leds(sc); + + for (i = 0; i < sc->num_sec_wiphy; i++) { + struct ath_wiphy *aphy = sc->sec_wiphy[i]; + if (aphy == NULL) + continue; + sc->sec_wiphy[i] = NULL; + ieee80211_unregister_hw(aphy->hw); + ieee80211_free_hw(aphy->hw); + } + kfree(sc->sec_wiphy); + + ieee80211_unregister_hw(hw); + ath_rx_cleanup(sc); + ath_tx_cleanup(sc); + ath9k_deinit_softc(sc); +} + +void ath_descdma_cleanup(struct ath_softc *sc, + struct ath_descdma *dd, + struct list_head *head) +{ + dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, + dd->dd_desc_paddr); + + INIT_LIST_HEAD(head); + kfree(dd->dd_bufptr); + memset(dd, 0, sizeof(*dd)); +} + +/************************/ +/* Module Hooks */ +/************************/ + +static int __init ath9k_init(void) +{ + int error; + + /* Register rate control algorithm */ + error = ath_rate_control_register(); + if (error != 0) { + printk(KERN_ERR + "ath9k: Unable to register rate control " + "algorithm: %d\n", + error); + goto err_out; + } + + error = ath9k_debug_create_root(); + if (error) { + printk(KERN_ERR + "ath9k: Unable to create debugfs root: %d\n", + error); + goto err_rate_unregister; + } + + error = ath_pci_init(); + if (error < 0) { + printk(KERN_ERR + "ath9k: No PCI devices found, driver not installed.\n"); + error = -ENODEV; + goto err_remove_root; + } + + error = ath_ahb_init(); + if (error < 0) { + error = -ENODEV; + goto err_pci_exit; + } + + return 0; + + err_pci_exit: + ath_pci_exit(); + + err_remove_root: + ath9k_debug_remove_root(); + err_rate_unregister: + ath_rate_control_unregister(); + err_out: + return error; +} +module_init(ath9k_init); + +static void __exit ath9k_exit(void) +{ + ath_ahb_exit(); + ath_pci_exit(); + ath9k_debug_remove_root(); + ath_rate_control_unregister(); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} +module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index e185479e295..29851e6376a 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -167,6 +167,40 @@ struct ath_rx_status { #define ATH9K_RXKEYIX_INVALID ((u8)-1) #define ATH9K_TXKEYIX_INVALID ((u32)-1) +enum ath9k_phyerr { + ATH9K_PHYERR_UNDERRUN = 0, /* Transmit underrun */ + ATH9K_PHYERR_TIMING = 1, /* Timing error */ + ATH9K_PHYERR_PARITY = 2, /* Illegal parity */ + ATH9K_PHYERR_RATE = 3, /* Illegal rate */ + ATH9K_PHYERR_LENGTH = 4, /* Illegal length */ + ATH9K_PHYERR_RADAR = 5, /* Radar detect */ + ATH9K_PHYERR_SERVICE = 6, /* Illegal service */ + ATH9K_PHYERR_TOR = 7, /* Transmit override receive */ + + ATH9K_PHYERR_OFDM_TIMING = 17, + ATH9K_PHYERR_OFDM_SIGNAL_PARITY = 18, + ATH9K_PHYERR_OFDM_RATE_ILLEGAL = 19, + ATH9K_PHYERR_OFDM_LENGTH_ILLEGAL = 20, + ATH9K_PHYERR_OFDM_POWER_DROP = 21, + ATH9K_PHYERR_OFDM_SERVICE = 22, + ATH9K_PHYERR_OFDM_RESTART = 23, + ATH9K_PHYERR_FALSE_RADAR_EXT = 24, + + ATH9K_PHYERR_CCK_TIMING = 25, + ATH9K_PHYERR_CCK_HEADER_CRC = 26, + ATH9K_PHYERR_CCK_RATE_ILLEGAL = 27, + ATH9K_PHYERR_CCK_SERVICE = 30, + ATH9K_PHYERR_CCK_RESTART = 31, + ATH9K_PHYERR_CCK_LENGTH_ILLEGAL = 32, + ATH9K_PHYERR_CCK_POWER_DROP = 33, + + ATH9K_PHYERR_HT_CRC_ERROR = 34, + ATH9K_PHYERR_HT_LENGTH_ILLEGAL = 35, + ATH9K_PHYERR_HT_RATE_ILLEGAL = 36, + + ATH9K_PHYERR_MAX = 37, +}; + struct ath_desc { u32 ds_link; u32 ds_data; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 79fbbda1549..c0c571c2e8c 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -18,118 +18,6 @@ #include "ath9k.h" #include "btcoex.h" -static char *dev_info = "ath9k"; - -MODULE_AUTHOR("Atheros Communications"); -MODULE_DESCRIPTION("Support for Atheros 802.11n wireless LAN cards."); -MODULE_SUPPORTED_DEVICE("Atheros 802.11n WLAN cards"); -MODULE_LICENSE("Dual BSD/GPL"); - -static int modparam_nohwcrypt; -module_param_named(nohwcrypt, modparam_nohwcrypt, int, 0444); -MODULE_PARM_DESC(nohwcrypt, "Disable hardware encryption"); - -static unsigned int ath9k_debug = ATH_DBG_DEFAULT; -module_param_named(debug, ath9k_debug, uint, 0); -MODULE_PARM_DESC(debug, "Debugging mask"); - -/* We use the hw_value as an index into our private channel structure */ - -#define CHAN2G(_freq, _idx) { \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -#define CHAN5G(_freq, _idx) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_idx), \ - .max_power = 20, \ -} - -/* Some 2 GHz radios are actually tunable on 2312-2732 - * on 5 MHz steps, we support the channels which we know - * we have calibration data for all cards though to make - * this static */ -static struct ieee80211_channel ath9k_2ghz_chantable[] = { - CHAN2G(2412, 0), /* Channel 1 */ - CHAN2G(2417, 1), /* Channel 2 */ - CHAN2G(2422, 2), /* Channel 3 */ - CHAN2G(2427, 3), /* Channel 4 */ - CHAN2G(2432, 4), /* Channel 5 */ - CHAN2G(2437, 5), /* Channel 6 */ - CHAN2G(2442, 6), /* Channel 7 */ - CHAN2G(2447, 7), /* Channel 8 */ - CHAN2G(2452, 8), /* Channel 9 */ - CHAN2G(2457, 9), /* Channel 10 */ - CHAN2G(2462, 10), /* Channel 11 */ - CHAN2G(2467, 11), /* Channel 12 */ - CHAN2G(2472, 12), /* Channel 13 */ - CHAN2G(2484, 13), /* Channel 14 */ -}; - -/* Some 5 GHz radios are actually tunable on XXXX-YYYY - * on 5 MHz steps, we support the channels which we know - * we have calibration data for all cards though to make - * this static */ -static struct ieee80211_channel ath9k_5ghz_chantable[] = { - /* _We_ call this UNII 1 */ - CHAN5G(5180, 14), /* Channel 36 */ - CHAN5G(5200, 15), /* Channel 40 */ - CHAN5G(5220, 16), /* Channel 44 */ - CHAN5G(5240, 17), /* Channel 48 */ - /* _We_ call this UNII 2 */ - CHAN5G(5260, 18), /* Channel 52 */ - CHAN5G(5280, 19), /* Channel 56 */ - CHAN5G(5300, 20), /* Channel 60 */ - CHAN5G(5320, 21), /* Channel 64 */ - /* _We_ call this "Middle band" */ - CHAN5G(5500, 22), /* Channel 100 */ - CHAN5G(5520, 23), /* Channel 104 */ - CHAN5G(5540, 24), /* Channel 108 */ - CHAN5G(5560, 25), /* Channel 112 */ - CHAN5G(5580, 26), /* Channel 116 */ - CHAN5G(5600, 27), /* Channel 120 */ - CHAN5G(5620, 28), /* Channel 124 */ - CHAN5G(5640, 29), /* Channel 128 */ - CHAN5G(5660, 30), /* Channel 132 */ - CHAN5G(5680, 31), /* Channel 136 */ - CHAN5G(5700, 32), /* Channel 140 */ - /* _We_ call this UNII 3 */ - CHAN5G(5745, 33), /* Channel 149 */ - CHAN5G(5765, 34), /* Channel 153 */ - CHAN5G(5785, 35), /* Channel 157 */ - CHAN5G(5805, 36), /* Channel 161 */ - CHAN5G(5825, 37), /* Channel 165 */ -}; - -/* Atheros hardware rate code addition for short premble */ -#define SHPCHECK(__hw_rate, __flags) \ - ((__flags & IEEE80211_RATE_SHORT_PREAMBLE) ? (__hw_rate | 0x04 ) : 0) - -#define RATE(_bitrate, _hw_rate, _flags) { \ - .bitrate = (_bitrate), \ - .flags = (_flags), \ - .hw_value = (_hw_rate), \ - .hw_value_short = (SHPCHECK(_hw_rate, _flags)) \ -} - -static struct ieee80211_rate ath9k_legacy_rates[] = { - RATE(10, 0x1b, 0), - RATE(20, 0x1a, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(55, 0x19, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(110, 0x18, IEEE80211_RATE_SHORT_PREAMBLE), - RATE(60, 0x0b, 0), - RATE(90, 0x0f, 0), - RATE(120, 0x0a, 0), - RATE(180, 0x0e, 0), - RATE(240, 0x09, 0), - RATE(360, 0x0d, 0), - RATE(480, 0x08, 0), - RATE(540, 0x0c, 0), -}; - static void ath_cache_conf_rate(struct ath_softc *sc, struct ieee80211_conf *conf) { @@ -221,7 +109,7 @@ static struct ath9k_channel *ath_get_curchannel(struct ath_softc *sc, return channel; } -static bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) +bool ath9k_setpower(struct ath_softc *sc, enum ath9k_power_mode mode) { unsigned long flags; bool ret; @@ -256,10 +144,10 @@ void ath9k_ps_restore(struct ath_softc *sc) goto unlock; if (sc->ps_enabled && - !(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK))) + !(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK))) ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_NETWORK_SLEEP); unlock: @@ -349,7 +237,7 @@ int ath_set_channel(struct ath_softc *sc, struct ieee80211_hw *hw, * When the task is complete, it reschedules itself depending on the * appropriate interval that was calculated. */ -static void ath_ani_calibrate(unsigned long data) +void ath_ani_calibrate(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; @@ -504,7 +392,7 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) ath_tx_node_cleanup(sc, an); } -static void ath9k_tasklet(unsigned long data) +void ath9k_tasklet(unsigned long data) { struct ath_softc *sc = (struct ath_softc *)data; struct ath_hw *ah = sc->sc_ah; @@ -536,7 +424,7 @@ static void ath9k_tasklet(unsigned long data) */ ath_print(common, ATH_DBG_PS, "TSFOOR - Sync with next Beacon\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_BEACON | SC_OP_BEACON_SYNC; + sc->ps_flags |= PS_WAIT_FOR_BEACON | PS_BEACON_SYNC; } if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) @@ -637,7 +525,7 @@ irqreturn_t ath_isr(int irq, void *dev) * receive frames */ ath9k_setpower(sc, ATH9K_PM_AWAKE); ath9k_hw_setrxabort(sc->sc_ah, 0); - sc->sc_flags |= SC_OP_WAIT_FOR_BEACON; + sc->ps_flags |= PS_WAIT_FOR_BEACON; } chip_reset: @@ -924,44 +812,6 @@ static void ath_key_delete(struct ath_common *common, struct ieee80211_key_conf } } -static void setup_ht_cap(struct ath_softc *sc, - struct ieee80211_sta_ht_cap *ht_info) -{ - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - u8 tx_streams, rx_streams; - - ht_info->ht_supported = true; - ht_info->cap = IEEE80211_HT_CAP_SUP_WIDTH_20_40 | - IEEE80211_HT_CAP_SM_PS | - IEEE80211_HT_CAP_SGI_40 | - IEEE80211_HT_CAP_DSSSCCK40; - - ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_8; - - /* set up supported mcs set */ - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - tx_streams = !(common->tx_chainmask & (common->tx_chainmask - 1)) ? - 1 : 2; - rx_streams = !(common->rx_chainmask & (common->rx_chainmask - 1)) ? - 1 : 2; - - if (tx_streams != rx_streams) { - ath_print(common, ATH_DBG_CONFIG, - "TX streams %d, RX streams: %d\n", - tx_streams, rx_streams); - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; - ht_info->mcs.tx_params |= ((tx_streams - 1) << - IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); - } - - ht_info->mcs.rx_mask[0] = 0xff; - if (rx_streams >= 2) - ht_info->mcs.rx_mask[1] = 0xff; - - ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_DEFINED; -} - static void ath9k_bss_assoc_info(struct ath_softc *sc, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) @@ -983,7 +833,7 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, * on the receipt of the first Beacon frame (i.e., * after time sync with the AP). */ - sc->sc_flags |= SC_OP_BEACON_SYNC; + sc->ps_flags |= PS_BEACON_SYNC; /* Configure the beacon */ ath_beacon_config(sc, vif); @@ -1000,174 +850,6 @@ static void ath9k_bss_assoc_info(struct ath_softc *sc, } } -/********************************/ -/* LED functions */ -/********************************/ - -static void ath_led_blink_work(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, - ath_led_blink_work.work); - - if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) - return; - - if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || - (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); - else - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, - (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); - - ieee80211_queue_delayed_work(sc->hw, - &sc->ath_led_blink_work, - (sc->sc_flags & SC_OP_LED_ON) ? - msecs_to_jiffies(sc->led_off_duration) : - msecs_to_jiffies(sc->led_on_duration)); - - sc->led_on_duration = sc->led_on_cnt ? - max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : - ATH_LED_ON_DURATION_IDLE; - sc->led_off_duration = sc->led_off_cnt ? - max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : - ATH_LED_OFF_DURATION_IDLE; - sc->led_on_cnt = sc->led_off_cnt = 0; - if (sc->sc_flags & SC_OP_LED_ON) - sc->sc_flags &= ~SC_OP_LED_ON; - else - sc->sc_flags |= SC_OP_LED_ON; -} - -static void ath_led_brightness(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); - struct ath_softc *sc = led->sc; - - switch (brightness) { - case LED_OFF: - if (led->led_type == ATH_LED_ASSOC || - led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, - (led->led_type == ATH_LED_RADIO)); - sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; - if (led->led_type == ATH_LED_RADIO) - sc->sc_flags &= ~SC_OP_LED_ON; - } else { - sc->led_off_cnt++; - } - break; - case LED_FULL: - if (led->led_type == ATH_LED_ASSOC) { - sc->sc_flags |= SC_OP_LED_ASSOCIATED; - ieee80211_queue_delayed_work(sc->hw, - &sc->ath_led_blink_work, 0); - } else if (led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); - sc->sc_flags |= SC_OP_LED_ON; - } else { - sc->led_on_cnt++; - } - break; - default: - break; - } -} - -static int ath_register_led(struct ath_softc *sc, struct ath_led *led, - char *trigger) -{ - int ret; - - led->sc = sc; - led->led_cdev.name = led->name; - led->led_cdev.default_trigger = trigger; - led->led_cdev.brightness_set = ath_led_brightness; - - ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); - if (ret) - ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_FATAL, - "Failed to register led:%s", led->name); - else - led->registered = 1; - return ret; -} - -static void ath_unregister_led(struct ath_led *led) -{ - if (led->registered) { - led_classdev_unregister(&led->led_cdev); - led->registered = 0; - } -} - -static void ath_deinit_leds(struct ath_softc *sc) -{ - ath_unregister_led(&sc->assoc_led); - sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; - ath_unregister_led(&sc->tx_led); - ath_unregister_led(&sc->rx_led); - ath_unregister_led(&sc->radio_led); - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); -} - -static void ath_init_leds(struct ath_softc *sc) -{ - char *trigger; - int ret; - - if (AR_SREV_9287(sc->sc_ah)) - sc->sc_ah->led_pin = ATH_LED_PIN_9287; - else - sc->sc_ah->led_pin = ATH_LED_PIN_DEF; - - /* Configure gpio 1 for output */ - ath9k_hw_cfg_output(sc->sc_ah, sc->sc_ah->led_pin, - AR_GPIO_OUTPUT_MUX_AS_OUTPUT); - /* LED off, active low */ - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); - - INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); - - trigger = ieee80211_get_radio_led_name(sc->hw); - snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), - "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->radio_led, trigger); - sc->radio_led.led_type = ATH_LED_RADIO; - if (ret) - goto fail; - - trigger = ieee80211_get_assoc_led_name(sc->hw); - snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), - "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->assoc_led, trigger); - sc->assoc_led.led_type = ATH_LED_ASSOC; - if (ret) - goto fail; - - trigger = ieee80211_get_tx_led_name(sc->hw); - snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), - "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->tx_led, trigger); - sc->tx_led.led_type = ATH_LED_TX; - if (ret) - goto fail; - - trigger = ieee80211_get_rx_led_name(sc->hw); - snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), - "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->rx_led, trigger); - sc->rx_led.led_type = ATH_LED_RX; - if (ret) - goto fail; - - return; - -fail: - cancel_delayed_work_sync(&sc->ath_led_blink_work); - ath_deinit_leds(sc); -} - void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) { struct ath_hw *ah = sc->sc_ah; @@ -1252,710 +934,6 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) ath9k_setpower(sc, ATH9K_PM_FULL_SLEEP); } -/*******************/ -/* Rfkill */ -/*******************/ - -static bool ath_is_rfkill_set(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - return ath9k_hw_gpio_get(ah, ah->rfkill_gpio) == - ah->rfkill_polarity; -} - -static void ath9k_rfkill_poll_state(struct ieee80211_hw *hw) -{ - struct ath_wiphy *aphy = hw->priv; - struct ath_softc *sc = aphy->sc; - bool blocked = !!ath_is_rfkill_set(sc); - - wiphy_rfkill_set_hw_state(hw->wiphy, blocked); -} - -static void ath_start_rfkill_poll(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - if (ah->caps.hw_caps & ATH9K_HW_CAP_RFSILENT) - wiphy_rfkill_start_polling(sc->hw->wiphy); -} - -static void ath9k_uninit_hw(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - BUG_ON(!ah); - - ath9k_exit_debug(ah); - ath9k_hw_detach(ah); - sc->sc_ah = NULL; -} - -static void ath_clean_core(struct ath_softc *sc) -{ - struct ieee80211_hw *hw = sc->hw; - struct ath_hw *ah = sc->sc_ah; - int i = 0; - - ath9k_ps_wakeup(sc); - - dev_dbg(sc->dev, "Detach ATH hw\n"); - - ath_deinit_leds(sc); - wiphy_rfkill_stop_polling(sc->hw->wiphy); - - for (i = 0; i < sc->num_sec_wiphy; i++) { - struct ath_wiphy *aphy = sc->sec_wiphy[i]; - if (aphy == NULL) - continue; - sc->sec_wiphy[i] = NULL; - ieee80211_unregister_hw(aphy->hw); - ieee80211_free_hw(aphy->hw); - } - ieee80211_unregister_hw(hw); - ath_rx_cleanup(sc); - ath_tx_cleanup(sc); - - tasklet_kill(&sc->intr_tq); - tasklet_kill(&sc->bcon_tasklet); - - if (!(sc->sc_flags & SC_OP_INVALID)) - ath9k_setpower(sc, ATH9K_PM_AWAKE); - - /* cleanup tx queues */ - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_cleanupq(sc, &sc->tx.txq[i]); - - if ((sc->btcoex.no_stomp_timer) && - ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) - ath_gen_timer_free(ah, sc->btcoex.no_stomp_timer); -} - -void ath_detach(struct ath_softc *sc) -{ - ath_clean_core(sc); - ath9k_uninit_hw(sc); -} - -void ath_cleanup(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - struct ath_common *common = ath9k_hw_common(ah); - - ath_clean_core(sc); - free_irq(sc->irq, sc); - ath_bus_cleanup(common); - kfree(sc->sec_wiphy); - ieee80211_free_hw(sc->hw); - - ath9k_uninit_hw(sc); -} - -static int ath9k_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); - struct ath_wiphy *aphy = hw->priv; - struct ath_softc *sc = aphy->sc; - struct ath_regulatory *reg = ath9k_hw_regulatory(sc->sc_ah); - - return ath_reg_notifier_apply(wiphy, request, reg); -} - -/* - * Detects if there is any priority bt traffic - */ -static void ath_detect_bt_priority(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; - - if (ath9k_hw_gpio_get(sc->sc_ah, ah->btcoex_hw.btpriority_gpio)) - btcoex->bt_priority_cnt++; - - if (time_after(jiffies, btcoex->bt_priority_time + - msecs_to_jiffies(ATH_BT_PRIORITY_TIME_THRESHOLD))) { - if (btcoex->bt_priority_cnt >= ATH_BT_CNT_THRESHOLD) { - ath_print(ath9k_hw_common(sc->sc_ah), ATH_DBG_BTCOEX, - "BT priority traffic detected"); - sc->sc_flags |= SC_OP_BT_PRIORITY_DETECTED; - } else { - sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; - } - - btcoex->bt_priority_cnt = 0; - btcoex->bt_priority_time = jiffies; - } -} - -/* - * Configures appropriate weight based on stomp type. - */ -static void ath9k_btcoex_bt_stomp(struct ath_softc *sc, - enum ath_stomp_type stomp_type) -{ - struct ath_hw *ah = sc->sc_ah; - - switch (stomp_type) { - case ATH_BTCOEX_STOMP_ALL: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_ALL_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_LOW: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_LOW_WLAN_WGHT); - break; - case ATH_BTCOEX_STOMP_NONE: - ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, - AR_STOMP_NONE_WLAN_WGHT); - break; - default: - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "Invalid Stomptype\n"); - break; - } - - ath9k_hw_btcoex_enable(ah); -} - -static void ath9k_gen_timer_start(struct ath_hw *ah, - struct ath_gen_timer *timer, - u32 timer_next, - u32 timer_period) -{ - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - - ath9k_hw_gen_timer_start(ah, timer, timer_next, timer_period); - - if ((sc->imask & ATH9K_INT_GENTIMER) == 0) { - ath9k_hw_set_interrupts(ah, 0); - sc->imask |= ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah, sc->imask); - } -} - -static void ath9k_gen_timer_stop(struct ath_hw *ah, struct ath_gen_timer *timer) -{ - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - struct ath_gen_timer_table *timer_table = &ah->hw_gen_timers; - - ath9k_hw_gen_timer_stop(ah, timer); - - /* if no timer is enabled, turn off interrupt mask */ - if (timer_table->timer_mask.val == 0) { - ath9k_hw_set_interrupts(ah, 0); - sc->imask &= ~ATH9K_INT_GENTIMER; - ath9k_hw_set_interrupts(ah, sc->imask); - } -} - -/* - * This is the master bt coex timer which runs for every - * 45ms, bt traffic will be given priority during 55% of this - * period while wlan gets remaining 45% - */ -static void ath_btcoex_period_timer(unsigned long data) -{ - struct ath_softc *sc = (struct ath_softc *) data; - struct ath_hw *ah = sc->sc_ah; - struct ath_btcoex *btcoex = &sc->btcoex; - - ath_detect_bt_priority(sc); - - spin_lock_bh(&btcoex->btcoex_lock); - - ath9k_btcoex_bt_stomp(sc, btcoex->bt_stomp_type); - - spin_unlock_bh(&btcoex->btcoex_lock); - - if (btcoex->btcoex_period != btcoex->btcoex_no_stomp) { - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - ath9k_gen_timer_start(ah, - btcoex->no_stomp_timer, - (ath9k_hw_gettsf32(ah) + - btcoex->btcoex_no_stomp), - btcoex->btcoex_no_stomp * 10); - btcoex->hw_timer_enabled = true; - } - - mod_timer(&btcoex->period_timer, jiffies + - msecs_to_jiffies(ATH_BTCOEX_DEF_BT_PERIOD)); -} - -/* - * Generic tsf based hw timer which configures weight - * registers to time slice between wlan and bt traffic - */ -static void ath_btcoex_no_stomp_timer(void *arg) -{ - struct ath_softc *sc = (struct ath_softc *)arg; - struct ath_hw *ah = sc->sc_ah; - struct ath_btcoex *btcoex = &sc->btcoex; - - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "no stomp timer running \n"); - - spin_lock_bh(&btcoex->btcoex_lock); - - if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_LOW) - ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_NONE); - else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) - ath9k_btcoex_bt_stomp(sc, ATH_BTCOEX_STOMP_LOW); - - spin_unlock_bh(&btcoex->btcoex_lock); -} - -static int ath_init_btcoex_timer(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - - btcoex->btcoex_period = ATH_BTCOEX_DEF_BT_PERIOD * 1000; - btcoex->btcoex_no_stomp = (100 - ATH_BTCOEX_DEF_DUTY_CYCLE) * - btcoex->btcoex_period / 100; - - setup_timer(&btcoex->period_timer, ath_btcoex_period_timer, - (unsigned long) sc); - - spin_lock_init(&btcoex->btcoex_lock); - - btcoex->no_stomp_timer = ath_gen_timer_alloc(sc->sc_ah, - ath_btcoex_no_stomp_timer, - ath_btcoex_no_stomp_timer, - (void *) sc, AR_FIRST_NDP_TIMER); - - if (!btcoex->no_stomp_timer) - return -ENOMEM; - - return 0; -} - -/* - * Read and write, they both share the same lock. We do this to serialize - * reads and writes on Atheros 802.11n PCI devices only. This is required - * as the FIFO on these devices can only accept sanely 2 requests. After - * that the device goes bananas. Serializing the reads/writes prevents this - * from happening. - */ - -static void ath9k_iowrite32(void *hw_priv, u32 val, u32 reg_offset) -{ - struct ath_hw *ah = (struct ath_hw *) hw_priv; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - - if (ah->config.serialize_regmode == SER_REG_MODE_ON) { - unsigned long flags; - spin_lock_irqsave(&sc->sc_serial_rw, flags); - iowrite32(val, sc->mem + reg_offset); - spin_unlock_irqrestore(&sc->sc_serial_rw, flags); - } else - iowrite32(val, sc->mem + reg_offset); -} - -static unsigned int ath9k_ioread32(void *hw_priv, u32 reg_offset) -{ - struct ath_hw *ah = (struct ath_hw *) hw_priv; - struct ath_common *common = ath9k_hw_common(ah); - struct ath_softc *sc = (struct ath_softc *) common->priv; - u32 val; - - if (ah->config.serialize_regmode == SER_REG_MODE_ON) { - unsigned long flags; - spin_lock_irqsave(&sc->sc_serial_rw, flags); - val = ioread32(sc->mem + reg_offset); - spin_unlock_irqrestore(&sc->sc_serial_rw, flags); - } else - val = ioread32(sc->mem + reg_offset); - return val; -} - -static const struct ath_ops ath9k_common_ops = { - .read = ath9k_ioread32, - .write = ath9k_iowrite32, -}; - -/* - * Initialize and fill ath_softc, ath_sofct is the - * "Software Carrier" struct. Historically it has existed - * to allow the separation between hardware specific - * variables (now in ath_hw) and driver specific variables. - */ -static int ath_init_softc(u16 devid, struct ath_softc *sc, u16 subsysid, - const struct ath_bus_ops *bus_ops) -{ - struct ath_hw *ah = NULL; - struct ath_common *common; - int r = 0, i; - int csz = 0; - int qnum; - - /* XXX: hardware will not be ready until ath_open() being called */ - sc->sc_flags |= SC_OP_INVALID; - - spin_lock_init(&sc->wiphy_lock); - spin_lock_init(&sc->sc_resetlock); - spin_lock_init(&sc->sc_serial_rw); - spin_lock_init(&sc->sc_pm_lock); - mutex_init(&sc->mutex); - tasklet_init(&sc->intr_tq, ath9k_tasklet, (unsigned long)sc); - tasklet_init(&sc->bcon_tasklet, ath_beacon_tasklet, - (unsigned long)sc); - - ah = kzalloc(sizeof(struct ath_hw), GFP_KERNEL); - if (!ah) - return -ENOMEM; - - ah->hw_version.devid = devid; - ah->hw_version.subsysid = subsysid; - sc->sc_ah = ah; - - common = ath9k_hw_common(ah); - common->ops = &ath9k_common_ops; - common->bus_ops = bus_ops; - common->ah = ah; - common->hw = sc->hw; - common->priv = sc; - common->debug_mask = ath9k_debug; - - /* - * Cache line size is used to size and align various - * structures used to communicate with the hardware. - */ - ath_read_cachesize(common, &csz); - /* XXX assert csz is non-zero */ - common->cachelsz = csz << 2; /* convert to bytes */ - - r = ath9k_hw_init(ah); - if (r) { - ath_print(common, ATH_DBG_FATAL, - "Unable to initialize hardware; " - "initialization status: %d\n", r); - goto bad_free_hw; - } - - if (ath9k_init_debug(ah) < 0) { - ath_print(common, ATH_DBG_FATAL, - "Unable to create debugfs files\n"); - goto bad_free_hw; - } - - /* Get the hardware key cache size. */ - common->keymax = ah->caps.keycache_size; - if (common->keymax > ATH_KEYMAX) { - ath_print(common, ATH_DBG_ANY, - "Warning, using only %u entries in %u key cache\n", - ATH_KEYMAX, common->keymax); - common->keymax = ATH_KEYMAX; - } - - /* - * Reset the key cache since some parts do not - * reset the contents on initial power up. - */ - for (i = 0; i < common->keymax; i++) - ath9k_hw_keyreset(ah, (u16) i); - - /* default to MONITOR mode */ - sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; - - /* - * Allocate hardware transmit queues: one queue for - * beacon frames and one data queue for each QoS - * priority. Note that the hal handles reseting - * these queues at the needed time. - */ - sc->beacon.beaconq = ath9k_hw_beaconq_setup(ah); - if (sc->beacon.beaconq == -1) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup a beacon xmit queue\n"); - r = -EIO; - goto bad2; - } - sc->beacon.cabq = ath_txq_setup(sc, ATH9K_TX_QUEUE_CAB, 0); - if (sc->beacon.cabq == NULL) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup CAB xmit queue\n"); - r = -EIO; - goto bad2; - } - - sc->config.cabqReadytime = ATH_CABQ_READY_TIME; - ath_cabq_update(sc); - - for (i = 0; i < ARRAY_SIZE(sc->tx.hwq_map); i++) - sc->tx.hwq_map[i] = -1; - - /* Setup data queues */ - /* NB: ensure BK queue is the lowest priority h/w queue */ - if (!ath_tx_setup(sc, ATH9K_WME_AC_BK)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for BK traffic\n"); - r = -EIO; - goto bad2; - } - - if (!ath_tx_setup(sc, ATH9K_WME_AC_BE)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for BE traffic\n"); - r = -EIO; - goto bad2; - } - if (!ath_tx_setup(sc, ATH9K_WME_AC_VI)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for VI traffic\n"); - r = -EIO; - goto bad2; - } - if (!ath_tx_setup(sc, ATH9K_WME_AC_VO)) { - ath_print(common, ATH_DBG_FATAL, - "Unable to setup xmit queue for VO traffic\n"); - r = -EIO; - goto bad2; - } - - /* Initializes the noise floor to a reasonable default value. - * Later on this will be updated during ANI processing. */ - - common->ani.noise_floor = ATH_DEFAULT_NOISE_FLOOR; - setup_timer(&common->ani.timer, ath_ani_calibrate, (unsigned long)sc); - - if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, - ATH9K_CIPHER_TKIP, NULL)) { - /* - * Whether we should enable h/w TKIP MIC. - * XXX: if we don't support WME TKIP MIC, then we wouldn't - * report WMM capable, so it's always safe to turn on - * TKIP MIC in this case. - */ - ath9k_hw_setcapability(sc->sc_ah, ATH9K_CAP_TKIP_MIC, - 0, 1, NULL); - } - - /* - * Check whether the separate key cache entries - * are required to handle both tx+rx MIC keys. - * With split mic keys the number of stations is limited - * to 27 otherwise 59. - */ - if (ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, - ATH9K_CIPHER_TKIP, NULL) - && ath9k_hw_getcapability(ah, ATH9K_CAP_CIPHER, - ATH9K_CIPHER_MIC, NULL) - && ath9k_hw_getcapability(ah, ATH9K_CAP_TKIP_SPLIT, - 0, NULL)) - common->splitmic = 1; - - /* turn on mcast key search if possible */ - if (!ath9k_hw_getcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 0, NULL)) - (void)ath9k_hw_setcapability(ah, ATH9K_CAP_MCAST_KEYSRCH, 1, - 1, NULL); - - sc->config.txpowlimit = ATH_TXPOWER_MAX; - - /* 11n Capabilities */ - if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - sc->sc_flags |= SC_OP_TXAGGR; - sc->sc_flags |= SC_OP_RXAGGR; - } - - common->tx_chainmask = ah->caps.tx_chainmask; - common->rx_chainmask = ah->caps.rx_chainmask; - - ath9k_hw_setcapability(ah, ATH9K_CAP_DIVERSITY, 1, true, NULL); - sc->rx.defant = ath9k_hw_getdefantenna(ah); - - if (ah->caps.hw_caps & ATH9K_HW_CAP_BSSIDMASK) - memcpy(common->bssidmask, ath_bcast_mac, ETH_ALEN); - - sc->beacon.slottime = ATH9K_SLOT_TIME_9; /* default to short slot time */ - - /* initialize beacon slots */ - for (i = 0; i < ARRAY_SIZE(sc->beacon.bslot); i++) { - sc->beacon.bslot[i] = NULL; - sc->beacon.bslot_aphy[i] = NULL; - } - - /* setup channels and rates */ - - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) { - sc->sbands[IEEE80211_BAND_2GHZ].channels = ath9k_2ghz_chantable; - sc->sbands[IEEE80211_BAND_2GHZ].band = IEEE80211_BAND_2GHZ; - sc->sbands[IEEE80211_BAND_2GHZ].n_channels = - ARRAY_SIZE(ath9k_2ghz_chantable); - sc->sbands[IEEE80211_BAND_2GHZ].bitrates = ath9k_legacy_rates; - sc->sbands[IEEE80211_BAND_2GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates); - } - - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) { - sc->sbands[IEEE80211_BAND_5GHZ].channels = ath9k_5ghz_chantable; - sc->sbands[IEEE80211_BAND_5GHZ].band = IEEE80211_BAND_5GHZ; - sc->sbands[IEEE80211_BAND_5GHZ].n_channels = - ARRAY_SIZE(ath9k_5ghz_chantable); - sc->sbands[IEEE80211_BAND_5GHZ].bitrates = - ath9k_legacy_rates + 4; - sc->sbands[IEEE80211_BAND_5GHZ].n_bitrates = - ARRAY_SIZE(ath9k_legacy_rates) - 4; - } - - switch (ah->btcoex_hw.scheme) { - case ATH_BTCOEX_CFG_NONE: - break; - case ATH_BTCOEX_CFG_2WIRE: - ath9k_hw_btcoex_init_2wire(ah); - break; - case ATH_BTCOEX_CFG_3WIRE: - ath9k_hw_btcoex_init_3wire(ah); - r = ath_init_btcoex_timer(sc); - if (r) - goto bad2; - qnum = ath_tx_get_qnum(sc, ATH9K_TX_QUEUE_DATA, ATH9K_WME_AC_BE); - ath9k_hw_init_btcoex_hw(ah, qnum); - sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; - break; - default: - WARN_ON(1); - break; - } - - return 0; -bad2: - /* cleanup tx queues */ - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_cleanupq(sc, &sc->tx.txq[i]); - -bad_free_hw: - ath9k_uninit_hw(sc); - return r; -} - -void ath_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw) -{ - hw->flags = IEEE80211_HW_RX_INCLUDES_FCS | - IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING | - IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_PS_NULLFUNC_STACK | - IEEE80211_HW_SPECTRUM_MGMT; - - if (AR_SREV_9160_10_OR_LATER(sc->sc_ah) || modparam_nohwcrypt) - hw->flags |= IEEE80211_HW_MFP_CAPABLE; - - hw->wiphy->interface_modes = - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_MESH_POINT); - - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->queues = 4; - hw->max_rates = 4; - hw->channel_change_time = 5000; - hw->max_listen_interval = 10; - /* Hardware supports 10 but we use 4 */ - hw->max_rate_tries = 4; - hw->sta_data_size = sizeof(struct ath_node); - hw->vif_data_size = sizeof(struct ath_vif); - - hw->rate_control_algorithm = "ath9k_rate_control"; - - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &sc->sbands[IEEE80211_BAND_2GHZ]; - if (test_bit(ATH9K_MODE_11A, sc->sc_ah->caps.wireless_modes)) - hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &sc->sbands[IEEE80211_BAND_5GHZ]; -} - -/* Device driver core initialization */ -int ath_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, - const struct ath_bus_ops *bus_ops) -{ - struct ieee80211_hw *hw = sc->hw; - struct ath_common *common; - struct ath_hw *ah; - int error = 0, i; - struct ath_regulatory *reg; - - dev_dbg(sc->dev, "Attach ATH hw\n"); - - error = ath_init_softc(devid, sc, subsysid, bus_ops); - if (error != 0) - return error; - - ah = sc->sc_ah; - common = ath9k_hw_common(ah); - - /* get mac address from hardware and set in mac80211 */ - - SET_IEEE80211_PERM_ADDR(hw, common->macaddr); - - ath_set_hw_capab(sc, hw); - - error = ath_regd_init(&common->regulatory, sc->hw->wiphy, - ath9k_reg_notifier); - if (error) - return error; - - reg = &common->regulatory; - - if (ah->caps.hw_caps & ATH9K_HW_CAP_HT) { - if (test_bit(ATH9K_MODE_11G, ah->caps.wireless_modes)) - setup_ht_cap(sc, - &sc->sbands[IEEE80211_BAND_2GHZ].ht_cap); - if (test_bit(ATH9K_MODE_11A, ah->caps.wireless_modes)) - setup_ht_cap(sc, - &sc->sbands[IEEE80211_BAND_5GHZ].ht_cap); - } - - /* initialize tx/rx engine */ - error = ath_tx_init(sc, ATH_TXBUF); - if (error != 0) - goto error_attach; - - error = ath_rx_init(sc, ATH_RXBUF); - if (error != 0) - goto error_attach; - - INIT_WORK(&sc->chan_work, ath9k_wiphy_chan_work); - INIT_DELAYED_WORK(&sc->wiphy_work, ath9k_wiphy_work); - sc->wiphy_scheduler_int = msecs_to_jiffies(500); - - error = ieee80211_register_hw(hw); - - if (!ath_is_world_regd(reg)) { - error = regulatory_hint(hw->wiphy, reg->alpha2); - if (error) - goto error_attach; - } - - /* Initialize LED control */ - ath_init_leds(sc); - - ath_start_rfkill_poll(sc); - - return 0; - -error_attach: - /* cleanup tx queues */ - for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) - if (ATH_TXQ_SETUP(sc, i)) - ath_tx_cleanupq(sc, &sc->tx.txq[i]); - - ath9k_uninit_hw(sc); - - return error; -} - int ath_reset(struct ath_softc *sc, bool retry_tx) { struct ath_hw *ah = sc->sc_ah; @@ -1966,6 +944,8 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) /* Stop ANI */ del_timer_sync(&common->ani.timer); + ieee80211_stop_queues(hw); + ath9k_hw_set_interrupts(ah, 0); ath_drain_all_txq(sc, retry_tx); ath_stoprecv(sc); @@ -2007,131 +987,14 @@ int ath_reset(struct ath_softc *sc, bool retry_tx) } } + ieee80211_wake_queues(hw); + /* Start ANI */ ath_start_ani(common); return r; } -/* - * This function will allocate both the DMA descriptor structure, and the - * buffers it contains. These are used to contain the descriptors used - * by the system. -*/ -int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd, - struct list_head *head, const char *name, - int nbuf, int ndesc) -{ -#define DS2PHYS(_dd, _ds) \ - ((_dd)->dd_desc_paddr + ((caddr_t)(_ds) - (caddr_t)(_dd)->dd_desc)) -#define ATH_DESC_4KB_BOUND_CHECK(_daddr) ((((_daddr) & 0xFFF) > 0xF7F) ? 1 : 0) -#define ATH_DESC_4KB_BOUND_NUM_SKIPPED(_len) ((_len) / 4096) - struct ath_common *common = ath9k_hw_common(sc->sc_ah); - struct ath_desc *ds; - struct ath_buf *bf; - int i, bsize, error; - - ath_print(common, ATH_DBG_CONFIG, "%s DMA: %u buffers %u desc/buf\n", - name, nbuf, ndesc); - - INIT_LIST_HEAD(head); - /* ath_desc must be a multiple of DWORDs */ - if ((sizeof(struct ath_desc) % 4) != 0) { - ath_print(common, ATH_DBG_FATAL, - "ath_desc not DWORD aligned\n"); - BUG_ON((sizeof(struct ath_desc) % 4) != 0); - error = -ENOMEM; - goto fail; - } - - dd->dd_desc_len = sizeof(struct ath_desc) * nbuf * ndesc; - - /* - * Need additional DMA memory because we can't use - * descriptors that cross the 4K page boundary. Assume - * one skipped descriptor per 4K page. - */ - if (!(sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_4KB_SPLITTRANS)) { - u32 ndesc_skipped = - ATH_DESC_4KB_BOUND_NUM_SKIPPED(dd->dd_desc_len); - u32 dma_len; - - while (ndesc_skipped) { - dma_len = ndesc_skipped * sizeof(struct ath_desc); - dd->dd_desc_len += dma_len; - - ndesc_skipped = ATH_DESC_4KB_BOUND_NUM_SKIPPED(dma_len); - }; - } - - /* allocate descriptors */ - dd->dd_desc = dma_alloc_coherent(sc->dev, dd->dd_desc_len, - &dd->dd_desc_paddr, GFP_KERNEL); - if (dd->dd_desc == NULL) { - error = -ENOMEM; - goto fail; - } - ds = dd->dd_desc; - ath_print(common, ATH_DBG_CONFIG, "%s DMA map: %p (%u) -> %llx (%u)\n", - name, ds, (u32) dd->dd_desc_len, - ito64(dd->dd_desc_paddr), /*XXX*/(u32) dd->dd_desc_len); - - /* allocate buffers */ - bsize = sizeof(struct ath_buf) * nbuf; - bf = kzalloc(bsize, GFP_KERNEL); - if (bf == NULL) { - error = -ENOMEM; - goto fail2; - } - dd->dd_bufptr = bf; - - for (i = 0; i < nbuf; i++, bf++, ds += ndesc) { - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); - - if (!(sc->sc_ah->caps.hw_caps & - ATH9K_HW_CAP_4KB_SPLITTRANS)) { - /* - * Skip descriptor addresses which can cause 4KB - * boundary crossing (addr + length) with a 32 dword - * descriptor fetch. - */ - while (ATH_DESC_4KB_BOUND_CHECK(bf->bf_daddr)) { - BUG_ON((caddr_t) bf->bf_desc >= - ((caddr_t) dd->dd_desc + - dd->dd_desc_len)); - - ds += ndesc; - bf->bf_desc = ds; - bf->bf_daddr = DS2PHYS(dd, ds); - } - } - list_add_tail(&bf->list, head); - } - return 0; -fail2: - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); -fail: - memset(dd, 0, sizeof(*dd)); - return error; -#undef ATH_DESC_4KB_BOUND_CHECK -#undef ATH_DESC_4KB_BOUND_NUM_SKIPPED -#undef DS2PHYS -} - -void ath_descdma_cleanup(struct ath_softc *sc, - struct ath_descdma *dd, - struct list_head *head) -{ - dma_free_coherent(sc->dev, dd->dd_desc_len, dd->dd_desc, - dd->dd_desc_paddr); - - INIT_LIST_HEAD(head); - kfree(dd->dd_bufptr); - memset(dd, 0, sizeof(*dd)); -} - int ath_get_hal_qnum(u16 queue, struct ath_softc *sc) { int qnum; @@ -2210,28 +1073,6 @@ void ath9k_update_ichannel(struct ath_softc *sc, struct ieee80211_hw *hw, /* mac80211 callbacks */ /**********************/ -/* - * (Re)start btcoex timers - */ -static void ath9k_btcoex_timer_resume(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; - - ath_print(ath9k_hw_common(ah), ATH_DBG_BTCOEX, - "Starting btcoex timers"); - - /* make sure duty cycle timer is also stopped when resuming */ - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(sc->sc_ah, btcoex->no_stomp_timer); - - btcoex->bt_priority_cnt = 0; - btcoex->bt_priority_time = jiffies; - sc->sc_flags &= ~SC_OP_BT_PRIORITY_DETECTED; - - mod_timer(&btcoex->period_timer, jiffies); -} - static int ath9k_start(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; @@ -2401,11 +1242,11 @@ static int ath9k_tx(struct ieee80211_hw *hw, if (ieee80211_is_pspoll(hdr->frame_control)) { ath_print(common, ATH_DBG_PS, "Sending PS-Poll to pick a buffered frame\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_PSPOLL_DATA; + sc->ps_flags |= PS_WAIT_FOR_PSPOLL_DATA; } else { ath_print(common, ATH_DBG_PS, "Wake up to complete TX\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_TX_ACK; + sc->ps_flags |= PS_WAIT_FOR_TX_ACK; } /* * The actual restore operation will happen only after @@ -2458,22 +1299,6 @@ exit: return 0; } -/* - * Pause btcoex timer and bt duty cycle timer - */ -static void ath9k_btcoex_timer_pause(struct ath_softc *sc) -{ - struct ath_btcoex *btcoex = &sc->btcoex; - struct ath_hw *ah = sc->sc_ah; - - del_timer_sync(&btcoex->period_timer); - - if (btcoex->hw_timer_enabled) - ath9k_gen_timer_stop(ah, btcoex->no_stomp_timer); - - btcoex->hw_timer_enabled = false; -} - static void ath9k_stop(struct ieee80211_hw *hw) { struct ath_wiphy *aphy = hw->priv; @@ -2717,7 +1542,7 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) */ if (changed & IEEE80211_CONF_CHANGE_PS) { if (conf->flags & IEEE80211_CONF_PS) { - sc->sc_flags |= SC_OP_PS_ENABLED; + sc->ps_flags |= PS_ENABLED; if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { if ((sc->imask & ATH9K_INT_TIM_TIMER) == 0) { @@ -2730,23 +1555,23 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) * At this point we know hardware has received an ACK * of a previously sent null data frame. */ - if ((sc->sc_flags & SC_OP_NULLFUNC_COMPLETED)) { - sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED; + if ((sc->ps_flags & PS_NULLFUNC_COMPLETED)) { + sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; sc->ps_enabled = true; ath9k_hw_setrxabort(sc->sc_ah, 1); } } else { sc->ps_enabled = false; - sc->sc_flags &= ~(SC_OP_PS_ENABLED | - SC_OP_NULLFUNC_COMPLETED); + sc->ps_flags &= ~(PS_ENABLED | + PS_NULLFUNC_COMPLETED); ath9k_setpower(sc, ATH9K_PM_AWAKE); if (!(ah->caps.hw_caps & ATH9K_HW_CAP_AUTOSLEEP)) { ath9k_hw_setrxabort(sc->sc_ah, 0); - sc->sc_flags &= ~(SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK); + sc->ps_flags &= ~(PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK); if (sc->imask & ATH9K_INT_TIM_TIMER) { sc->imask &= ~ATH9K_INT_TIM_TIMER; ath9k_hw_set_interrupts(sc->sc_ah, @@ -2756,6 +1581,14 @@ static int ath9k_config(struct ieee80211_hw *hw, u32 changed) } } + if (changed & IEEE80211_CONF_CHANGE_MONITOR) { + if (conf->flags & IEEE80211_CONF_MONITOR) { + ath_print(common, ATH_DBG_CONFIG, + "HW opmode set to Monitor mode\n"); + sc->sc_ah->opmode = NL80211_IFTYPE_MONITOR; + } + } + if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { struct ieee80211_channel *curchan = hw->conf.channel; int pos = curchan->hw_value; @@ -2956,6 +1789,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); struct ath_vif *avp = (void *)vif->drv_priv; + int slottime; int error; mutex_lock(&sc->mutex); @@ -2991,6 +1825,25 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, ath_beacon_config(sc, vif); } + if (changed & BSS_CHANGED_ERP_SLOT) { + if (bss_conf->use_short_slot) + slottime = 9; + else + slottime = 20; + if (vif->type == NL80211_IFTYPE_AP) { + /* + * Defer update, so that connected stations can adjust + * their settings at the same time. + * See beacon.c for more details + */ + sc->beacon.slottime = slottime; + sc->beacon.updateslot = UPDATE; + } else { + ah->slottime = slottime; + ath9k_hw_init_global_settings(ah); + } + } + /* Disable transmission of beacons */ if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); @@ -3161,6 +2014,18 @@ static void ath9k_sw_scan_complete(struct ieee80211_hw *hw) mutex_unlock(&sc->mutex); } +static void ath9k_set_coverage_class(struct ieee80211_hw *hw, u8 coverage_class) +{ + struct ath_wiphy *aphy = hw->priv; + struct ath_softc *sc = aphy->sc; + struct ath_hw *ah = sc->sc_ah; + + mutex_lock(&sc->mutex); + ah->coverage_class = coverage_class; + ath9k_hw_init_global_settings(ah); + mutex_unlock(&sc->mutex); +} + struct ieee80211_ops ath9k_ops = { .tx = ath9k_tx, .start = ath9k_start, @@ -3180,64 +2045,5 @@ struct ieee80211_ops ath9k_ops = { .sw_scan_start = ath9k_sw_scan_start, .sw_scan_complete = ath9k_sw_scan_complete, .rfkill_poll = ath9k_rfkill_poll_state, + .set_coverage_class = ath9k_set_coverage_class, }; - -static int __init ath9k_init(void) -{ - int error; - - /* Register rate control algorithm */ - error = ath_rate_control_register(); - if (error != 0) { - printk(KERN_ERR - "ath9k: Unable to register rate control " - "algorithm: %d\n", - error); - goto err_out; - } - - error = ath9k_debug_create_root(); - if (error) { - printk(KERN_ERR - "ath9k: Unable to create debugfs root: %d\n", - error); - goto err_rate_unregister; - } - - error = ath_pci_init(); - if (error < 0) { - printk(KERN_ERR - "ath9k: No PCI devices found, driver not installed.\n"); - error = -ENODEV; - goto err_remove_root; - } - - error = ath_ahb_init(); - if (error < 0) { - error = -ENODEV; - goto err_pci_exit; - } - - return 0; - - err_pci_exit: - ath_pci_exit(); - - err_remove_root: - ath9k_debug_remove_root(); - err_rate_unregister: - ath_rate_control_unregister(); - err_out: - return error; -} -module_init(ath9k_init); - -static void __exit ath9k_exit(void) -{ - ath_ahb_exit(); - ath_pci_exit(); - ath9k_debug_remove_root(); - ath_rate_control_unregister(); - printk(KERN_INFO "%s: Driver unloaded\n", dev_info); -} -module_exit(ath9k_exit); diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index ee617205cb4..fe2c3a644a6 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -113,25 +113,22 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) u16 subsysid; u32 val; int ret = 0; - struct ath_hw *ah; char hw_name[64]; if (pci_enable_device(pdev)) return -EIO; ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { printk(KERN_ERR "ath9k: 32-bit DMA not available\n"); - goto bad; + goto err_dma; } ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { printk(KERN_ERR "ath9k: 32-bit DMA consistent " "DMA enable failed\n"); - goto bad; + goto err_dma; } /* @@ -171,22 +168,22 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); ret = -ENODEV; - goto bad; + goto err_region; } mem = pci_iomap(pdev, 0, 0); if (!mem) { printk(KERN_ERR "PCI memory map error\n") ; ret = -EIO; - goto bad1; + goto err_iomap; } hw = ieee80211_alloc_hw(sizeof(struct ath_wiphy) + sizeof(struct ath_softc), &ath9k_ops); if (!hw) { - dev_err(&pdev->dev, "no memory for ieee80211_hw\n"); + dev_err(&pdev->dev, "No memory for ieee80211_hw\n"); ret = -ENOMEM; - goto bad2; + goto err_alloc_hw; } SET_IEEE80211_DEV(hw, &pdev->dev); @@ -201,25 +198,25 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) sc->dev = &pdev->dev; sc->mem = mem; - pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid); - ret = ath_init_device(id->device, sc, subsysid, &ath_pci_bus_ops); - if (ret) { - dev_err(&pdev->dev, "failed to initialize device\n"); - goto bad3; - } - - /* setup interrupt service routine */ + /* Will be cleared in ath9k_start() */ + sc->sc_flags |= SC_OP_INVALID; ret = request_irq(pdev->irq, ath_isr, IRQF_SHARED, "ath9k", sc); if (ret) { dev_err(&pdev->dev, "request_irq failed\n"); - goto bad4; + goto err_irq; } sc->irq = pdev->irq; - ah = sc->sc_ah; - ath9k_hw_name(ah, hw_name, sizeof(hw_name)); + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsysid); + ret = ath9k_init_device(id->device, sc, subsysid, &ath_pci_bus_ops); + if (ret) { + dev_err(&pdev->dev, "Failed to initialize device\n"); + goto err_init; + } + + ath9k_hw_name(sc->sc_ah, hw_name, sizeof(hw_name)); printk(KERN_INFO "%s: %s mem=0x%lx, irq=%d\n", wiphy_name(hw->wiphy), @@ -227,15 +224,18 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) (unsigned long)mem, pdev->irq); return 0; -bad4: - ath_detach(sc); -bad3: + +err_init: + free_irq(sc->irq, sc); +err_irq: ieee80211_free_hw(hw); -bad2: +err_alloc_hw: pci_iounmap(pdev, mem); -bad1: +err_iomap: pci_release_region(pdev, 0); -bad: +err_region: + /* Nothing */ +err_dma: pci_disable_device(pdev); return ret; } @@ -245,8 +245,12 @@ static void ath_pci_remove(struct pci_dev *pdev) struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + struct ath_common *common = ath9k_hw_common(sc->sc_ah); - ath_cleanup(sc); + ath9k_deinit_device(sc); + free_irq(sc->irq, sc); + ieee80211_free_hw(sc->hw); + ath_bus_cleanup(common); } #ifdef CONFIG_PM diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index 477365e5ae6..40b5d05edcc 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -364,10 +364,10 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) if (memcmp(common->curbssid, mgmt->bssid, ETH_ALEN) != 0) return; /* not from our current AP */ - sc->sc_flags &= ~SC_OP_WAIT_FOR_BEACON; + sc->ps_flags &= ~PS_WAIT_FOR_BEACON; - if (sc->sc_flags & SC_OP_BEACON_SYNC) { - sc->sc_flags &= ~SC_OP_BEACON_SYNC; + if (sc->ps_flags & PS_BEACON_SYNC) { + sc->ps_flags &= ~PS_BEACON_SYNC; ath_print(common, ATH_DBG_PS, "Reconfigure Beacon timers based on " "timestamp from the AP\n"); @@ -384,17 +384,17 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) */ ath_print(common, ATH_DBG_PS, "Received DTIM beacon indicating " "buffered broadcast/multicast frame(s)\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_BEACON; + sc->ps_flags |= PS_WAIT_FOR_CAB | PS_WAIT_FOR_BEACON; return; } - if (sc->sc_flags & SC_OP_WAIT_FOR_CAB) { + if (sc->ps_flags & PS_WAIT_FOR_CAB) { /* * This can happen if a broadcast frame is dropped or the AP * fails to send a frame indicating that all CAB frames have * been delivered. */ - sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; + sc->ps_flags &= ~PS_WAIT_FOR_CAB; ath_print(common, ATH_DBG_PS, "PS wait for CAB frames timed out\n"); } @@ -408,10 +408,10 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) hdr = (struct ieee80211_hdr *)skb->data; /* Process Beacon and CAB receive in PS state */ - if ((sc->sc_flags & SC_OP_WAIT_FOR_BEACON) && + if ((sc->ps_flags & PS_WAIT_FOR_BEACON) && ieee80211_is_beacon(hdr->frame_control)) ath_rx_ps_beacon(sc, skb); - else if ((sc->sc_flags & SC_OP_WAIT_FOR_CAB) && + else if ((sc->ps_flags & PS_WAIT_FOR_CAB) && (ieee80211_is_data(hdr->frame_control) || ieee80211_is_action(hdr->frame_control)) && is_multicast_ether_addr(hdr->addr1) && @@ -420,20 +420,20 @@ static void ath_rx_ps(struct ath_softc *sc, struct sk_buff *skb) * No more broadcast/multicast frames to be received at this * point. */ - sc->sc_flags &= ~SC_OP_WAIT_FOR_CAB; + sc->ps_flags &= ~PS_WAIT_FOR_CAB; ath_print(common, ATH_DBG_PS, "All PS CAB frames received, back to sleep\n"); - } else if ((sc->sc_flags & SC_OP_WAIT_FOR_PSPOLL_DATA) && + } else if ((sc->ps_flags & PS_WAIT_FOR_PSPOLL_DATA) && !is_multicast_ether_addr(hdr->addr1) && !ieee80211_has_morefrags(hdr->frame_control)) { - sc->sc_flags &= ~SC_OP_WAIT_FOR_PSPOLL_DATA; + sc->ps_flags &= ~PS_WAIT_FOR_PSPOLL_DATA; ath_print(common, ATH_DBG_PS, "Going back to sleep after having received " "PS-Poll data (0x%x)\n", - sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK)); + sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK)); } } @@ -571,6 +571,8 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) hw = ath_get_virt_hw(sc, hdr); rx_stats = &ds->ds_rxstat; + ath_debug_stat_rx(sc, bf); + /* * If we're asked to flush receive queue, directly * chain it back at the queue without processing it. @@ -631,9 +633,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush) sc->rx.rxotherant = 0; } - if (unlikely(sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA))) + if (unlikely(sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA))) ath_rx_ps(sc, skb); ath_rx_send_to_mac80211(hw, sc, skb, rxs); diff --git a/drivers/net/wireless/ath/ath9k/virtual.c b/drivers/net/wireless/ath/ath9k/virtual.c index cd26caaf44e..a43fbf84dab 100644 --- a/drivers/net/wireless/ath/ath9k/virtual.c +++ b/drivers/net/wireless/ath/ath9k/virtual.c @@ -152,7 +152,7 @@ int ath9k_wiphy_add(struct ath_softc *sc) SET_IEEE80211_PERM_ADDR(hw, addr); - ath_set_hw_capab(sc, hw); + ath9k_set_hw_capab(sc, hw); error = ieee80211_register_hw(hw); diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index fa12b9060b0..a821bb687b3 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1648,7 +1648,7 @@ static int ath_tx_setup_buffer(struct ieee80211_hw *hw, struct ath_buf *bf, /* tag if this is a nullfunc frame to enable PS when AP acks it */ if (ieee80211_is_nullfunc(fc) && ieee80211_has_pm(fc)) { bf->bf_isnullfunc = true; - sc->sc_flags &= ~SC_OP_NULLFUNC_COMPLETED; + sc->ps_flags &= ~PS_NULLFUNC_COMPLETED; } else bf->bf_isnullfunc = false; @@ -1858,15 +1858,15 @@ static void ath_tx_complete(struct ath_softc *sc, struct sk_buff *skb, skb_pull(skb, padsize); } - if (sc->sc_flags & SC_OP_WAIT_FOR_TX_ACK) { - sc->sc_flags &= ~SC_OP_WAIT_FOR_TX_ACK; + if (sc->ps_flags & PS_WAIT_FOR_TX_ACK) { + sc->ps_flags &= ~PS_WAIT_FOR_TX_ACK; ath_print(common, ATH_DBG_PS, "Going back to sleep after having " "received TX status (0x%x)\n", - sc->sc_flags & (SC_OP_WAIT_FOR_BEACON | - SC_OP_WAIT_FOR_CAB | - SC_OP_WAIT_FOR_PSPOLL_DATA | - SC_OP_WAIT_FOR_TX_ACK)); + sc->ps_flags & (PS_WAIT_FOR_BEACON | + PS_WAIT_FOR_CAB | + PS_WAIT_FOR_PSPOLL_DATA | + PS_WAIT_FOR_TX_ACK)); } if (unlikely(tx_info->pad[0] & ATH_TX_INFO_FRAME_TYPE_INTERNAL)) @@ -2053,11 +2053,11 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) */ if (bf->bf_isnullfunc && (ds->ds_txstat.ts_status & ATH9K_TX_ACKED)) { - if ((sc->sc_flags & SC_OP_PS_ENABLED)) { + if ((sc->ps_flags & PS_ENABLED)) { sc->ps_enabled = true; ath9k_hw_setrxabort(sc->sc_ah, 1); } else - sc->sc_flags |= SC_OP_NULLFUNC_COMPLETED; + sc->ps_flags |= PS_NULLFUNC_COMPLETED; } /* diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index 2f12a750bc9..54d6085a887 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -253,6 +253,14 @@ enum { #define B43_SHM_SH_MAXBFRAMES 0x0080 /* Maximum number of frames in a burst */ #define B43_SHM_SH_SPUWKUP 0x0094 /* pre-wakeup for synth PU in us */ #define B43_SHM_SH_PRETBTT 0x0096 /* pre-TBTT in us */ +/* SHM_SHARED tx iq workarounds */ +#define B43_SHM_SH_NPHY_TXIQW0 0x0700 +#define B43_SHM_SH_NPHY_TXIQW1 0x0702 +#define B43_SHM_SH_NPHY_TXIQW2 0x0704 +#define B43_SHM_SH_NPHY_TXIQW3 0x0706 +/* SHM_SHARED tx pwr ctrl */ +#define B43_SHM_SH_NPHY_TXPWR_INDX0 0x0708 +#define B43_SHM_SH_NPHY_TXPWR_INDX1 0x070E /* SHM_SCRATCH offsets */ #define B43_SHM_SC_MINCONT 0x0003 /* Minimum contention window */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 60290c06e95..9c5c7c9ad53 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -67,7 +67,12 @@ MODULE_AUTHOR("Gábor Stefanik"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(B43_SUPPORTED_FIRMWARE_ID); - +MODULE_FIRMWARE("b43/ucode11.fw"); +MODULE_FIRMWARE("b43/ucode13.fw"); +MODULE_FIRMWARE("b43/ucode14.fw"); +MODULE_FIRMWARE("b43/ucode15.fw"); +MODULE_FIRMWARE("b43/ucode5.fw"); +MODULE_FIRMWARE("b43/ucode9.fw"); static int modparam_bad_frames_preempt; module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444); @@ -113,6 +118,7 @@ static const struct ssb_device_id b43_ssb_tbl[] = { SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 9), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 10), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 11), + SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 12), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 13), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 15), SSB_DEVICE(SSB_VENDOR_BROADCOM, SSB_DEV_80211, 16), diff --git a/drivers/net/wireless/b43/phy_lp.c b/drivers/net/wireless/b43/phy_lp.c index 3e046ec1ff8..b58d6cf2658 100644 --- a/drivers/net/wireless/b43/phy_lp.c +++ b/drivers/net/wireless/b43/phy_lp.c @@ -80,6 +80,7 @@ static void b43_lpphy_op_free(struct b43_wldev *dev) dev->phy.lp = NULL; } +/* http://bcm-v4.sipsolutions.net/802.11/PHY/LP/ReadBandSrom */ static void lpphy_read_band_sprom(struct b43_wldev *dev) { struct b43_phy_lp *lpphy = dev->phy.lp; @@ -101,6 +102,12 @@ static void lpphy_read_band_sprom(struct b43_wldev *dev) maxpwr = bus->sprom.maxpwr_bg; lpphy->max_tx_pwr_med_band = maxpwr; cckpo = bus->sprom.cck2gpo; + /* + * We don't read SPROM's opo as specs say. On rev8 SPROMs + * opo == ofdm2gpo and we don't know any SSB with LP-PHY + * and SPROM rev below 8. + */ + B43_WARN_ON(bus->sprom.revision < 8); ofdmpo = bus->sprom.ofdm2gpo; if (cckpo) { for (i = 0; i < 4; i++) { @@ -1703,19 +1710,6 @@ static const struct lpphy_rx_iq_comp lpphy_rev2plus_iq_comp = { .c0 = 0, }; -static u8 lpphy_nbits(s32 val) -{ - u32 tmp = abs(val); - u8 nbits = 0; - - while (tmp != 0) { - nbits++; - tmp >>= 1; - } - - return nbits; -} - static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) { struct lpphy_iq_est iq_est; @@ -1742,8 +1736,8 @@ static int lpphy_calc_rx_iq_comp(struct b43_wldev *dev, u16 samples) goto out; } - prod_msb = lpphy_nbits(prod); - q_msb = lpphy_nbits(qpwr); + prod_msb = fls(abs(prod)); + q_msb = fls(abs(qpwr)); tmp1 = prod_msb - 20; if (tmp1 >= 0) { diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 992318a7807..4a817e3da16 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -28,7 +28,32 @@ #include "b43.h" #include "phy_n.h" #include "tables_nphy.h" +#include "main.h" +struct nphy_txgains { + u16 txgm[2]; + u16 pga[2]; + u16 pad[2]; + u16 ipa[2]; +}; + +struct nphy_iqcal_params { + u16 txgm; + u16 pga; + u16 pad; + u16 ipa; + u16 cal_gain; + u16 ncorr[5]; +}; + +struct nphy_iq_est { + s32 iq0_prod; + u32 i0_pwr; + u32 q0_pwr; + s32 iq1_prod; + u32 i1_pwr; + u32 q1_pwr; +}; void b43_nphy_set_rxantenna(struct b43_wldev *dev, int antenna) {//TODO @@ -197,44 +222,16 @@ void b43_nphy_radio_turn_off(struct b43_wldev *dev) ~B43_NPHY_RFCTL_CMD_EN); } -#define ntab_upload(dev, offset, data) do { \ - unsigned int i; \ - for (i = 0; i < (offset##_SIZE); i++) \ - b43_ntab_write(dev, (offset) + i, (data)[i]); \ - } while (0) - -/* Upload the N-PHY tables. */ +/* + * Upload the N-PHY tables. + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables + */ static void b43_nphy_tables_init(struct b43_wldev *dev) { - /* Static tables */ - ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); - ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); - ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); - ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); - ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); - ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); - ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); - ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); - ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); - ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); - ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); - ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); - ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); - ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); - - /* Volatile tables */ - ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); - ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); - ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); - ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); - ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); - ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); - ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); - ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); - ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); - ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); + if (dev->phy.rev < 3) + b43_nphy_rev0_1_2_tables_init(dev); + else + b43_nphy_rev3plus_tables_init(dev); } static void b43_nphy_workarounds(struct b43_wldev *dev) @@ -341,18 +338,386 @@ static void b43_nphy_workarounds(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_PHASETR_B2, 0x20); } +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PA%20override */ +static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) +{ + struct b43_phy_n *nphy = dev->phy.n; + enum ieee80211_band band; + u16 tmp; + + if (!enable) { + nphy->rfctrl_intc1_save = b43_phy_read(dev, + B43_NPHY_RFCTL_INTC1); + nphy->rfctrl_intc2_save = b43_phy_read(dev, + B43_NPHY_RFCTL_INTC2); + band = b43_current_band(dev->wl); + if (dev->phy.rev >= 3) { + if (band == IEEE80211_BAND_5GHZ) + tmp = 0x600; + else + tmp = 0x480; + } else { + if (band == IEEE80211_BAND_5GHZ) + tmp = 0x180; + else + tmp = 0x120; + } + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, tmp); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, tmp); + } else { + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, + nphy->rfctrl_intc1_save); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, + nphy->rfctrl_intc2_save); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxLpFbw */ +static void b43_nphy_tx_lp_fbw(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u16 tmp; + enum ieee80211_band band = b43_current_band(dev->wl); + bool ipa = (nphy->ipa2g_on && band == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && band == IEEE80211_BAND_5GHZ); + + if (dev->phy.rev >= 3) { + if (ipa) { + tmp = 4; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S2, + (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); + } + + tmp = 1; + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S2, + (((((tmp << 3) | tmp) << 3) | tmp) << 3) | tmp); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/BmacPhyClkFgc */ +static void b43_nphy_bmac_clock_fgc(struct b43_wldev *dev, bool force) +{ + u32 tmslow; + + if (dev->phy.type != B43_PHYTYPE_N) + return; + + tmslow = ssb_read32(dev->dev, SSB_TMSLOW); + if (force) + tmslow |= SSB_TMSLOW_FGC; + else + tmslow &= ~SSB_TMSLOW_FGC; + ssb_write32(dev->dev, SSB_TMSLOW, tmslow); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CCA */ static void b43_nphy_reset_cca(struct b43_wldev *dev) { u16 bbcfg; - ssb_write32(dev->dev, SSB_TMSLOW, - ssb_read32(dev->dev, SSB_TMSLOW) | SSB_TMSLOW_FGC); + b43_nphy_bmac_clock_fgc(dev, 1); bbcfg = b43_phy_read(dev, B43_NPHY_BBCFG); - b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTCCA); - b43_phy_write(dev, B43_NPHY_BBCFG, - bbcfg & ~B43_NPHY_BBCFG_RSTCCA); - ssb_write32(dev->dev, SSB_TMSLOW, - ssb_read32(dev->dev, SSB_TMSLOW) & ~SSB_TMSLOW_FGC); + b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg | B43_NPHY_BBCFG_RSTCCA); + udelay(1); + b43_phy_write(dev, B43_NPHY_BBCFG, bbcfg & ~B43_NPHY_BBCFG_RSTCCA); + b43_nphy_bmac_clock_fgc(dev, 0); + /* TODO: N PHY Force RF Seq with argument 2 */ +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqEst */ +static void b43_nphy_rx_iq_est(struct b43_wldev *dev, struct nphy_iq_est *est, + u16 samps, u8 time, bool wait) +{ + int i; + u16 tmp; + + b43_phy_write(dev, B43_NPHY_IQEST_SAMCNT, samps); + b43_phy_maskset(dev, B43_NPHY_IQEST_WT, ~B43_NPHY_IQEST_WT_VAL, time); + if (wait) + b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_MODE); + else + b43_phy_mask(dev, B43_NPHY_IQEST_CMD, ~B43_NPHY_IQEST_CMD_MODE); + + b43_phy_set(dev, B43_NPHY_IQEST_CMD, B43_NPHY_IQEST_CMD_START); + + for (i = 1000; i; i--) { + tmp = b43_phy_read(dev, B43_NPHY_IQEST_CMD); + if (!(tmp & B43_NPHY_IQEST_CMD_START)) { + est->i0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO0); + est->q0_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO0); + est->iq0_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI0) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO0); + + est->i1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_IPACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IPACC_LO1); + est->q1_pwr = (b43_phy_read(dev, B43_NPHY_IQEST_QPACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_QPACC_LO1); + est->iq1_prod = (b43_phy_read(dev, B43_NPHY_IQEST_IQACC_HI1) << 16) | + b43_phy_read(dev, B43_NPHY_IQEST_IQACC_LO1); + return; + } + udelay(10); + } + memset(est, 0, sizeof(*est)); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RxIqCoeffs */ +static void b43_nphy_rx_iq_coeffs(struct b43_wldev *dev, bool write, + struct b43_phy_n_iq_comp *pcomp) +{ + if (write) { + b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPA0, pcomp->a0); + b43_phy_write(dev, B43_NPHY_C1_RXIQ_COMPB0, pcomp->b0); + b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPA1, pcomp->a1); + b43_phy_write(dev, B43_NPHY_C2_RXIQ_COMPB1, pcomp->b1); + } else { + pcomp->a0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPA0); + pcomp->b0 = b43_phy_read(dev, B43_NPHY_C1_RXIQ_COMPB0); + pcomp->a1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPA1); + pcomp->b1 = b43_phy_read(dev, B43_NPHY_C2_RXIQ_COMPB1); + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalcRxIqComp */ +static void b43_nphy_calc_rx_iq_comp(struct b43_wldev *dev, u8 mask) +{ + int i; + s32 iq; + u32 ii; + u32 qq; + int iq_nbits, qq_nbits; + int arsh, brsh; + u16 tmp, a, b; + + struct nphy_iq_est est; + struct b43_phy_n_iq_comp old; + struct b43_phy_n_iq_comp new = { }; + bool error = false; + + if (mask == 0) + return; + + b43_nphy_rx_iq_coeffs(dev, false, &old); + b43_nphy_rx_iq_coeffs(dev, true, &new); + b43_nphy_rx_iq_est(dev, &est, 0x4000, 32, false); + new = old; + + for (i = 0; i < 2; i++) { + if (i == 0 && (mask & 1)) { + iq = est.iq0_prod; + ii = est.i0_pwr; + qq = est.q0_pwr; + } else if (i == 1 && (mask & 2)) { + iq = est.iq1_prod; + ii = est.i1_pwr; + qq = est.q1_pwr; + } else { + B43_WARN_ON(1); + continue; + } + + if (ii + qq < 2) { + error = true; + break; + } + + iq_nbits = fls(abs(iq)); + qq_nbits = fls(qq); + + arsh = iq_nbits - 20; + if (arsh >= 0) { + a = -((iq << (30 - iq_nbits)) + (ii >> (1 + arsh))); + tmp = ii >> arsh; + } else { + a = -((iq << (30 - iq_nbits)) + (ii << (-1 - arsh))); + tmp = ii << -arsh; + } + if (tmp == 0) { + error = true; + break; + } + a /= tmp; + + brsh = qq_nbits - 11; + if (brsh >= 0) { + b = (qq << (31 - qq_nbits)); + tmp = ii >> brsh; + } else { + b = (qq << (31 - qq_nbits)); + tmp = ii << -brsh; + } + if (tmp == 0) { + error = true; + break; + } + b = int_sqrt(b / tmp - a * a) - (1 << 10); + + if (i == 0 && (mask & 0x1)) { + if (dev->phy.rev >= 3) { + new.a0 = a & 0x3FF; + new.b0 = b & 0x3FF; + } else { + new.a0 = b & 0x3FF; + new.b0 = a & 0x3FF; + } + } else if (i == 1 && (mask & 0x2)) { + if (dev->phy.rev >= 3) { + new.a1 = a & 0x3FF; + new.b1 = b & 0x3FF; + } else { + new.a1 = b & 0x3FF; + new.b1 = a & 0x3FF; + } + } + } + + if (error) + new = old; + + b43_nphy_rx_iq_coeffs(dev, true, &new); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxIqWar */ +static void b43_nphy_tx_iq_workaround(struct b43_wldev *dev) +{ + u16 array[4]; + int i; + + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, 0x3C50); + for (i = 0; i < 4; i++) + array[i] = b43_phy_read(dev, B43_NPHY_TABLE_DATALO); + + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW0, array[0]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW1, array[1]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW2, array[2]); + b43_shm_write16(dev, B43_SHM_SHARED, B43_SHM_SH_NPHY_TXIQW3, array[3]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ +static void b43_nphy_write_clip_detection(struct b43_wldev *dev, u16 *clip_st) +{ + b43_phy_write(dev, B43_NPHY_C1_CLIP1THRES, clip_st[0]); + b43_phy_write(dev, B43_NPHY_C2_CLIP1THRES, clip_st[1]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/clip-detection */ +static void b43_nphy_read_clip_detection(struct b43_wldev *dev, u16 *clip_st) +{ + clip_st[0] = b43_phy_read(dev, B43_NPHY_C1_CLIP1THRES); + clip_st[1] = b43_phy_read(dev, B43_NPHY_C2_CLIP1THRES); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/classifier */ +static u16 b43_nphy_classifier(struct b43_wldev *dev, u16 mask, u16 val) +{ + u16 tmp; + + if (dev->dev->id.revision == 16) + b43_mac_suspend(dev); + + tmp = b43_phy_read(dev, B43_NPHY_CLASSCTL); + tmp &= (B43_NPHY_CLASSCTL_CCKEN | B43_NPHY_CLASSCTL_OFDMEN | + B43_NPHY_CLASSCTL_WAITEDEN); + tmp &= ~mask; + tmp |= (val & mask); + b43_phy_maskset(dev, B43_NPHY_CLASSCTL, 0xFFF8, tmp); + + if (dev->dev->id.revision == 16) + b43_mac_enable(dev); + + return tmp; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/carriersearch */ +static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) +{ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + + if (enable) { + u16 clip[] = { 0xFFFF, 0xFFFF }; + if (nphy->deaf_count++ == 0) { + nphy->classifier_state = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 0x7, 0); + b43_nphy_read_clip_detection(dev, nphy->clip_state); + b43_nphy_write_clip_detection(dev, clip); + } + b43_nphy_reset_cca(dev); + } else { + if (--nphy->deaf_count == 0) { + b43_nphy_classifier(dev, 0x7, nphy->classifier_state); + b43_nphy_write_clip_detection(dev, nphy->clip_state); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlCoefSetup */ +static void b43_nphy_tx_pwr_ctrl_coef_setup(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i, j; + u32 tmp; + u32 cur_real, cur_imag, real_part, imag_part; + + u16 buffer[7]; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + + /* TODO: Read an N PHY Table with ID 15, length 7, offset 80, + width 16, and data pointer buffer */ + + for (i = 0; i < 2; i++) { + tmp = ((buffer[i * 2] & 0x3FF) << 10) | + (buffer[i * 2 + 1] & 0x3FF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (((i + 26) << 10) | 320)); + for (j = 0; j < 128; j++) { + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, + ((tmp >> 16) & 0xFFFF)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (tmp & 0xFFFF)); + } + } + + for (i = 0; i < 2; i++) { + tmp = buffer[5 + i]; + real_part = (tmp >> 8) & 0xFF; + imag_part = (tmp & 0xFF); + b43_phy_write(dev, B43_NPHY_TABLE_ADDR, + (((i + 26) << 10) | 448)); + + if (dev->phy.rev >= 3) { + cur_real = real_part; + cur_imag = imag_part; + tmp = ((cur_real & 0xFF) << 8) | (cur_imag & 0xFF); + } + + for (j = 0; j < 128; j++) { + if (dev->phy.rev < 3) { + cur_real = (real_part * loscale[j] + 128) >> 8; + cur_imag = (imag_part * loscale[j] + 128) >> 8; + tmp = ((cur_real & 0xFF) << 8) | + (cur_imag & 0xFF); + } + b43_phy_write(dev, B43_NPHY_TABLE_DATAHI, + ((tmp >> 16) & 0xFFFF)); + b43_phy_write(dev, B43_NPHY_TABLE_DATALO, + (tmp & 0xFFFF)); + } + } + + if (dev->phy.rev >= 3) { + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_NPHY_TXPWR_INDX0, 0xFFFF); + b43_shm_write16(dev, B43_SHM_SHARED, + B43_SHM_SH_NPHY_TXPWR_INDX1, 0xFFFF); + } + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); } enum b43_nphy_rf_sequence { @@ -411,81 +776,1339 @@ static void b43_nphy_bphy_init(struct b43_wldev *dev) b43_phy_write(dev, B43_PHY_N_BMODE(0x38), 0x668); } -/* RSSI Calibration */ -static void b43_nphy_rssi_cal(struct b43_wldev *dev, u8 type) +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/ScaleOffsetRssi */ +static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale, + s8 offset, u8 core, u8 rail, u8 type) { - //TODO + u16 tmp; + bool core1or5 = (core == 1) || (core == 5); + bool core2or5 = (core == 2) || (core == 5); + + offset = clamp_val(offset, -32, 31); + tmp = ((scale & 0x3F) << 8) | (offset & 0x3F); + + if (core1or5 && (rail == 0) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, tmp); + if (core1or5 && (rail == 1) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, tmp); + if (core2or5 && (rail == 0) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, tmp); + if (core2or5 && (rail == 1) && (type == 2)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, tmp); + if (core1or5 && (rail == 0) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, tmp); + if (core1or5 && (rail == 1) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, tmp); + if (core2or5 && (rail == 0) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, tmp); + if (core2or5 && (rail == 1) && (type == 0)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, tmp); + if (core1or5 && (rail == 0) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, tmp); + if (core1or5 && (rail == 1) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, tmp); + if (core2or5 && (rail == 0) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, tmp); + if (core2or5 && (rail == 1) && (type == 1)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, tmp); + if (core1or5 && (rail == 0) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TBD, tmp); + if (core1or5 && (rail == 1) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TBD, tmp); + if (core2or5 && (rail == 0) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TBD, tmp); + if (core2or5 && (rail == 1) && (type == 6)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TBD, tmp); + if (core1or5 && (rail == 0) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_PWRDET, tmp); + if (core1or5 && (rail == 1) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_PWRDET, tmp); + if (core2or5 && (rail == 0) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_PWRDET, tmp); + if (core2or5 && (rail == 1) && (type == 3)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_PWRDET, tmp); + if (core1or5 && (type == 4)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_TSSI, tmp); + if (core2or5 && (type == 4)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_TSSI, tmp); + if (core1or5 && (type == 5)) + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_TSSI, tmp); + if (core2or5 && (type == 5)) + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_TSSI, tmp); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSISel */ +static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, u8 type) +{ + u16 val; + + if (dev->phy.rev >= 3) { + /* TODO */ + } else { + if (type < 3) + val = 0; + else if (type == 6) + val = 1; + else if (type == 3) + val = 2; + else + val = 3; + + val = (val << 12) | (val << 14); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, val); + b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, val); + + if (type < 3) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO1, 0xFFCF, + (type + 1) << 4); + b43_phy_maskset(dev, B43_NPHY_RFCTL_RSSIO2, 0xFFCF, + (type + 1) << 4); + } + + /* TODO use some definitions */ + if (code == 0) { + b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, 0); + if (type < 3) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, + 0xFEC7, 0); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xEFDC, 0); + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, + 0xFFFE, 0); + udelay(20); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xFFFE, 0); + } + } else { + b43_phy_maskset(dev, B43_NPHY_AFECTL_OVER, 0xCFFF, + 0x3000); + if (type < 3) { + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, + 0xFEC7, 0x0180); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xEFDC, (code << 1 | 0x1021)); + b43_phy_maskset(dev, B43_NPHY_RFCTL_CMD, + 0xFFFE, 0x0001); + udelay(20); + b43_phy_maskset(dev, B43_NPHY_RFCTL_OVER, + 0xFFFE, 0); + } + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SetRssi2055Vcm */ +static void b43_nphy_set_rssi_2055_vcm(struct b43_wldev *dev, u8 type, u8 *buf) +{ + int i; + for (i = 0; i < 2; i++) { + if (type == 2) { + if (i == 0) { + b43_radio_maskset(dev, B2055_C1_B0NB_RSSIVCM, + 0xFC, buf[0]); + b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, + 0xFC, buf[1]); + } else { + b43_radio_maskset(dev, B2055_C2_B0NB_RSSIVCM, + 0xFC, buf[2 * i]); + b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, + 0xFC, buf[2 * i + 1]); + } + } else { + if (i == 0) + b43_radio_maskset(dev, B2055_C1_RX_BB_RSSICTL5, + 0xF3, buf[0] << 2); + else + b43_radio_maskset(dev, B2055_C2_RX_BB_RSSICTL5, + 0xF3, buf[2 * i + 1] << 2); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/PollRssi */ +static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf, + u8 nsamp) +{ + int i; + int out; + u16 save_regs_phy[9]; + u16 s[2]; + + if (dev->phy.rev >= 3) { + save_regs_phy[0] = b43_phy_read(dev, + B43_NPHY_RFCTL_LUT_TRSW_UP1); + save_regs_phy[1] = b43_phy_read(dev, + B43_NPHY_RFCTL_LUT_TRSW_UP2); + save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); + save_regs_phy[3] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); + save_regs_phy[4] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER1); + save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0); + save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1); + } + + b43_nphy_rssi_select(dev, 5, type); + + if (dev->phy.rev < 2) { + save_regs_phy[8] = b43_phy_read(dev, B43_NPHY_GPIO_SEL); + b43_phy_write(dev, B43_NPHY_GPIO_SEL, 5); + } + + for (i = 0; i < 4; i++) + buf[i] = 0; + + for (i = 0; i < nsamp; i++) { + if (dev->phy.rev < 2) { + s[0] = b43_phy_read(dev, B43_NPHY_GPIO_LOOUT); + s[1] = b43_phy_read(dev, B43_NPHY_GPIO_HIOUT); + } else { + s[0] = b43_phy_read(dev, B43_NPHY_RSSI1); + s[1] = b43_phy_read(dev, B43_NPHY_RSSI2); + } + + buf[0] += ((s8)((s[0] & 0x3F) << 2)) >> 2; + buf[1] += ((s8)(((s[0] >> 8) & 0x3F) << 2)) >> 2; + buf[2] += ((s8)((s[1] & 0x3F) << 2)) >> 2; + buf[3] += ((s8)(((s[1] >> 8) & 0x3F) << 2)) >> 2; + } + out = (buf[0] & 0xFF) << 24 | (buf[1] & 0xFF) << 16 | + (buf[2] & 0xFF) << 8 | (buf[3] & 0xFF); + + if (dev->phy.rev < 2) + b43_phy_write(dev, B43_NPHY_GPIO_SEL, save_regs_phy[8]); + + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP1, + save_regs_phy[0]); + b43_phy_write(dev, B43_NPHY_RFCTL_LUT_TRSW_UP2, + save_regs_phy[1]); + b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[2]); + b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[3]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, save_regs_phy[4]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]); + } + + return out; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal */ +static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, u8 type) +{ + int i, j; + u8 state[4]; + u8 code, val; + u16 class, override; + u8 regs_save_radio[2]; + u16 regs_save_phy[2]; + s8 offset[4]; + + u16 clip_state[2]; + u16 clip_off[2] = { 0xFFFF, 0xFFFF }; + s32 results_min[4] = { }; + u8 vcm_final[4] = { }; + s32 results[4][4] = { }; + s32 miniq[4][2] = { }; + + if (type == 2) { + code = 0; + val = 6; + } else if (type < 2) { + code = 25; + val = 4; + } else { + B43_WARN_ON(1); + return; + } + + class = b43_nphy_classifier(dev, 0, 0); + b43_nphy_classifier(dev, 7, 4); + b43_nphy_read_clip_detection(dev, clip_state); + b43_nphy_write_clip_detection(dev, clip_off); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + override = 0x140; + else + override = 0x110; + + regs_save_phy[0] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); + regs_save_radio[0] = b43_radio_read16(dev, B2055_C1_PD_RXTX); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, override); + b43_radio_write16(dev, B2055_C1_PD_RXTX, val); + + regs_save_phy[1] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); + regs_save_radio[1] = b43_radio_read16(dev, B2055_C2_PD_RXTX); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, override); + b43_radio_write16(dev, B2055_C2_PD_RXTX, val); + + state[0] = b43_radio_read16(dev, B2055_C1_PD_RSSIMISC) & 0x07; + state[1] = b43_radio_read16(dev, B2055_C2_PD_RSSIMISC) & 0x07; + b43_radio_mask(dev, B2055_C1_PD_RSSIMISC, 0xF8); + b43_radio_mask(dev, B2055_C2_PD_RSSIMISC, 0xF8); + state[2] = b43_radio_read16(dev, B2055_C1_SP_RSSI) & 0x07; + state[3] = b43_radio_read16(dev, B2055_C2_SP_RSSI) & 0x07; + + b43_nphy_rssi_select(dev, 5, type); + b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 0, type); + b43_nphy_scale_offset_rssi(dev, 0, 0, 5, 1, type); + + for (i = 0; i < 4; i++) { + u8 tmp[4]; + for (j = 0; j < 4; j++) + tmp[j] = i; + if (type != 1) + b43_nphy_set_rssi_2055_vcm(dev, type, tmp); + b43_nphy_poll_rssi(dev, type, results[i], 8); + if (type < 2) + for (j = 0; j < 2; j++) + miniq[i][j] = min(results[i][2 * j], + results[i][2 * j + 1]); + } + + for (i = 0; i < 4; i++) { + s32 mind = 40; + u8 minvcm = 0; + s32 minpoll = 249; + s32 curr; + for (j = 0; j < 4; j++) { + if (type == 2) + curr = abs(results[j][i]); + else + curr = abs(miniq[j][i / 2] - code * 8); + + if (curr < mind) { + mind = curr; + minvcm = j; + } + + if (results[j][i] < minpoll) + minpoll = results[j][i]; + } + results_min[i] = minpoll; + vcm_final[i] = minvcm; + } + + if (type != 1) + b43_nphy_set_rssi_2055_vcm(dev, type, vcm_final); + + for (i = 0; i < 4; i++) { + offset[i] = (code * 8) - results[vcm_final[i]][i]; + + if (offset[i] < 0) + offset[i] = -((abs(offset[i]) + 4) / 8); + else + offset[i] = (offset[i] + 4) / 8; + + if (results_min[i] == 248) + offset[i] = code - 32; + + if (i % 2 == 0) + b43_nphy_scale_offset_rssi(dev, 0, offset[i], 1, 0, + type); + else + b43_nphy_scale_offset_rssi(dev, 0, offset[i], 2, 1, + type); + } + + b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[0]); + b43_radio_maskset(dev, B2055_C1_PD_RSSIMISC, 0xF8, state[1]); + + switch (state[2]) { + case 1: + b43_nphy_rssi_select(dev, 1, 2); + break; + case 4: + b43_nphy_rssi_select(dev, 1, 0); + break; + case 2: + b43_nphy_rssi_select(dev, 1, 1); + break; + default: + b43_nphy_rssi_select(dev, 1, 1); + break; + } + + switch (state[3]) { + case 1: + b43_nphy_rssi_select(dev, 2, 2); + break; + case 4: + b43_nphy_rssi_select(dev, 2, 0); + break; + default: + b43_nphy_rssi_select(dev, 2, 1); + break; + } + + b43_nphy_rssi_select(dev, 0, type); + + b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, regs_save_phy[0]); + b43_radio_write16(dev, B2055_C1_PD_RXTX, regs_save_radio[0]); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, regs_save_phy[1]); + b43_radio_write16(dev, B2055_C2_PD_RXTX, regs_save_radio[1]); + + b43_nphy_classifier(dev, 7, class); + b43_nphy_write_clip_detection(dev, clip_state); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICalRev3 */ +static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) +{ + /* TODO */ +} + +/* + * RSSI Calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RSSICal + */ +static void b43_nphy_rssi_cal(struct b43_wldev *dev) +{ + if (dev->phy.rev >= 3) { + b43_nphy_rev3_rssi_cal(dev); + } else { + b43_nphy_rev2_rssi_cal(dev, 2); + b43_nphy_rev2_rssi_cal(dev, 0); + b43_nphy_rev2_rssi_cal(dev, 1); + } +} + +/* + * Restore RSSI Calibration + * http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreRssiCal + */ +static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 *rssical_radio_regs = NULL; + u16 *rssical_phy_regs = NULL; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (!nphy->rssical_chanspec_2G) + return; + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_2G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_2G; + } else { + if (!nphy->rssical_chanspec_5G) + return; + rssical_radio_regs = nphy->rssical_cache.rssical_radio_regs_5G; + rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; + } + + /* TODO use some definitions */ + b43_radio_maskset(dev, 0x602B, 0xE3, rssical_radio_regs[0]); + b43_radio_maskset(dev, 0x702B, 0xE3, rssical_radio_regs[1]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Z, rssical_phy_regs[0]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Z, rssical_phy_regs[1]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Z, rssical_phy_regs[2]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Z, rssical_phy_regs[3]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_X, rssical_phy_regs[4]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_X, rssical_phy_regs[5]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_X, rssical_phy_regs[6]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_X, rssical_phy_regs[7]); + + b43_phy_write(dev, B43_NPHY_RSSIMC_0I_RSSI_Y, rssical_phy_regs[8]); + b43_phy_write(dev, B43_NPHY_RSSIMC_0Q_RSSI_Y, rssical_phy_regs[9]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1I_RSSI_Y, rssical_phy_regs[10]); + b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ +static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) +{ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (dev->phy.rev >= 6) { + /* TODO If the chip is 47162 + return txpwrctrl_tx_gain_ipa_rev5 */ + return txpwrctrl_tx_gain_ipa_rev6; + } else if (dev->phy.rev >= 5) { + return txpwrctrl_tx_gain_ipa_rev5; + } else { + return txpwrctrl_tx_gain_ipa; + } + } else { + return txpwrctrl_tx_gain_ipa_5g; + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ +static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + u16 *save = nphy->tx_rx_cal_radio_saveregs; + + if (dev->phy.rev >= 3) { + /* TODO */ + } else { + save[0] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL1); + b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL1, 0x29); + + save[1] = b43_radio_read16(dev, B2055_C1_TX_RF_IQCAL2); + b43_radio_write16(dev, B2055_C1_TX_RF_IQCAL2, 0x54); + + save[2] = b43_radio_read16(dev, B2055_C2_TX_RF_IQCAL1); + b43_radio_write16(dev, B2055_C2_TX_RF_IQCAL1, 0x29); + + save[3] = b43_radio_read16(dev, B2055_C2_TX_RF_IQCAL2); + b43_radio_write16(dev, B2055_C2_TX_RF_IQCAL2, 0x54); + + save[3] = b43_radio_read16(dev, B2055_C1_PWRDET_RXTX); + save[4] = b43_radio_read16(dev, B2055_C2_PWRDET_RXTX); + + if (!(b43_phy_read(dev, B43_NPHY_BANDCTL) & + B43_NPHY_BANDCTL_5GHZ)) { + b43_radio_write16(dev, B2055_C1_PWRDET_RXTX, 0x04); + b43_radio_write16(dev, B2055_C2_PWRDET_RXTX, 0x04); + } else { + b43_radio_write16(dev, B2055_C1_PWRDET_RXTX, 0x20); + b43_radio_write16(dev, B2055_C2_PWRDET_RXTX, 0x20); + } + + if (dev->phy.rev < 2) { + b43_radio_set(dev, B2055_C1_TX_BB_MXGM, 0x20); + b43_radio_set(dev, B2055_C2_TX_BB_MXGM, 0x20); + } else { + b43_radio_mask(dev, B2055_C1_TX_BB_MXGM, ~0x20); + b43_radio_mask(dev, B2055_C2_TX_BB_MXGM, ~0x20); + } + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/IqCalGainParams */ +static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, + struct nphy_txgains target, + struct nphy_iqcal_params *params) +{ + int i, j, indx; + u16 gain; + + if (dev->phy.rev >= 3) { + params->txgm = target.txgm[core]; + params->pga = target.pga[core]; + params->pad = target.pad[core]; + params->ipa = target.ipa[core]; + params->cal_gain = (params->txgm << 12) | (params->pga << 8) | + (params->pad << 4) | (params->ipa); + for (j = 0; j < 5; j++) + params->ncorr[j] = 0x79; + } else { + gain = (target.pad[core]) | (target.pga[core] << 4) | + (target.txgm[core] << 8); + + indx = (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) ? + 1 : 0; + for (i = 0; i < 9; i++) + if (tbl_iqcal_gainparams[indx][i][0] == gain) + break; + i = min(i, 8); + + params->txgm = tbl_iqcal_gainparams[indx][i][1]; + params->pga = tbl_iqcal_gainparams[indx][i][2]; + params->pad = tbl_iqcal_gainparams[indx][i][3]; + params->cal_gain = (params->txgm << 7) | (params->pga << 4) | + (params->pad << 2); + for (j = 0; j < 4; j++) + params->ncorr[j] = tbl_iqcal_gainparams[indx][i][4 + j]; + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/UpdateTxCalLadder */ +static void b43_nphy_update_tx_cal_ladder(struct b43_wldev *dev, u16 core) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i; + u16 scale, entry; + + u16 tmp = nphy->txcal_bbmult; + if (core == 0) + tmp >>= 8; + tmp &= 0xff; + + for (i = 0; i < 18; i++) { + scale = (ladder_lo[i].percent * tmp) / 100; + entry = ((scale & 0xFF) << 8) | ladder_lo[i].g_env; + /* TODO: Write an N PHY Table with ID 15, length 1, + offset i, width 16, and data entry */ + + scale = (ladder_iq[i].percent * tmp) / 100; + entry = ((scale & 0xFF) << 8) | ladder_iq[i].g_env; + /* TODO: Write an N PHY Table with ID 15, length 1, + offset i + 32, width 16, and data entry */ + } +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetTxGain */ +static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 curr_gain[2]; + struct nphy_txgains target; + const u32 *table = NULL; + + if (nphy->txpwrctrl == 0) { + int i; + + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, true); + /* TODO: Read an N PHY Table with ID 7, length 2, + offset 0x110, width 16, and curr_gain */ + if (nphy->hang_avoid) + b43_nphy_stay_in_carrier_search(dev, false); + + for (i = 0; i < 2; ++i) { + if (dev->phy.rev >= 3) { + target.ipa[i] = curr_gain[i] & 0x000F; + target.pad[i] = (curr_gain[i] & 0x00F0) >> 4; + target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; + target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; + } else { + target.ipa[i] = curr_gain[i] & 0x0003; + target.pad[i] = (curr_gain[i] & 0x000C) >> 2; + target.pga[i] = (curr_gain[i] & 0x0070) >> 4; + target.txgm[i] = (curr_gain[i] & 0x0380) >> 7; + } + } + } else { + int i; + u16 index[2]; + index[0] = (b43_phy_read(dev, B43_NPHY_C1_TXPCTL_STAT) & + B43_NPHY_TXPCTL_STAT_BIDX) >> + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; + index[1] = (b43_phy_read(dev, B43_NPHY_C2_TXPCTL_STAT) & + B43_NPHY_TXPCTL_STAT_BIDX) >> + B43_NPHY_TXPCTL_STAT_BIDX_SHIFT; + + for (i = 0; i < 2; ++i) { + if (dev->phy.rev >= 3) { + enum ieee80211_band band = + b43_current_band(dev->wl); + + if ((nphy->ipa2g_on && + band == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && + band == IEEE80211_BAND_5GHZ)) { + table = b43_nphy_get_ipa_gain_table(dev); + } else { + if (band == IEEE80211_BAND_5GHZ) { + if (dev->phy.rev == 3) + table = b43_ntab_tx_gain_rev3_5ghz; + else if (dev->phy.rev == 4) + table = b43_ntab_tx_gain_rev4_5ghz; + else + table = b43_ntab_tx_gain_rev5plus_5ghz; + } else { + table = b43_ntab_tx_gain_rev3plus_2ghz; + } + } + + target.ipa[i] = (table[index[i]] >> 16) & 0xF; + target.pad[i] = (table[index[i]] >> 20) & 0xF; + target.pga[i] = (table[index[i]] >> 24) & 0xF; + target.txgm[i] = (table[index[i]] >> 28) & 0xF; + } else { + table = b43_ntab_tx_gain_rev0_1_2; + + target.ipa[i] = (table[index[i]] >> 16) & 0x3; + target.pad[i] = (table[index[i]] >> 18) & 0x3; + target.pga[i] = (table[index[i]] >> 20) & 0x7; + target.txgm[i] = (table[index[i]] >> 23) & 0x7; + } + } + } + + return target; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */ +static void b43_nphy_restore_cal(struct b43_wldev *dev) +{ + struct b43_phy_n *nphy = dev->phy.n; + + u16 coef[4]; + u16 *loft = NULL; + u16 *table = NULL; + + int i; + u16 *txcal_radio_regs = NULL; + struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + if (nphy->iqcal_chanspec_2G == 0) + return; + table = nphy->cal_cache.txcal_coeffs_2G; + loft = &nphy->cal_cache.txcal_coeffs_2G[5]; + } else { + if (nphy->iqcal_chanspec_5G == 0) + return; + table = nphy->cal_cache.txcal_coeffs_5G; + loft = &nphy->cal_cache.txcal_coeffs_5G[5]; + } + + /* TODO: Write an N PHY table with ID 15, length 4, offset 80, + width 16, and data from table */ + + for (i = 0; i < 4; i++) { + if (dev->phy.rev >= 3) + table[i] = coef[i]; + else + coef[i] = 0; + } + + /* TODO: Write an N PHY table with ID 15, length 4, offset 88, + width 16, and data from coef */ + /* TODO: Write an N PHY table with ID 15, length 2, offset 85, + width 16 and data from loft */ + /* TODO: Write an N PHY table with ID 15, length 2, offset 93, + width 16 and data from loft */ + + if (dev->phy.rev < 2) + b43_nphy_tx_iq_workaround(dev); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_2G; + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_2G; + } else { + txcal_radio_regs = nphy->cal_cache.txcal_radio_regs_5G; + rxcal_coeffs = &nphy->cal_cache.rxcal_coeffs_5G; + } + + /* TODO use some definitions */ + if (dev->phy.rev >= 3) { + b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); + b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); + b43_radio_write(dev, 0x3021, txcal_radio_regs[2]); + b43_radio_write(dev, 0x3022, txcal_radio_regs[3]); + b43_radio_write(dev, 0x2023, txcal_radio_regs[4]); + b43_radio_write(dev, 0x2024, txcal_radio_regs[5]); + b43_radio_write(dev, 0x3023, txcal_radio_regs[6]); + b43_radio_write(dev, 0x3024, txcal_radio_regs[7]); + } else { + b43_radio_write(dev, 0x8B, txcal_radio_regs[0]); + b43_radio_write(dev, 0xBA, txcal_radio_regs[1]); + b43_radio_write(dev, 0x8D, txcal_radio_regs[2]); + b43_radio_write(dev, 0xBC, txcal_radio_regs[3]); + } + b43_nphy_rx_iq_coeffs(dev, true, rxcal_coeffs); +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalTxIqlo */ +static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, + struct nphy_txgains target, + bool full, bool mphase) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i; + int error = 0; + int freq; + bool avoid = false; + u8 length; + u16 tmp, core, type, count, max, numb, last, cmd; + const u16 *table; + bool phy6or5x; + + u16 buffer[11]; + u16 diq_start = 0; + u16 save[2]; + u16 gain[2]; + struct nphy_iqcal_params params[2]; + bool updated[2] = { }; + + b43_nphy_stay_in_carrier_search(dev, true); + + if (dev->phy.rev >= 4) { + avoid = nphy->hang_avoid; + nphy->hang_avoid = 0; + } + + /* TODO: Read an N PHY Table with ID 7, length 2, offset 0x110, + width 16, and data pointer save */ + + for (i = 0; i < 2; i++) { + b43_nphy_iq_cal_gain_params(dev, i, target, ¶ms[i]); + gain[i] = params[i].cal_gain; + } + /* TODO: Write an N PHY Table with ID 7, length 2, offset 0x110, + width 16, and data pointer gain */ + + b43_nphy_tx_cal_radio_setup(dev); + /* TODO: Call N PHY TX Cal PHY Setup */ + + phy6or5x = dev->phy.rev >= 6 || + (dev->phy.rev == 5 && nphy->ipa2g_on && + b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ); + if (phy6or5x) { + /* TODO */ + } + + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); + + if (1 /* FIXME: the band width is 20 MHz */) + freq = 2500; + else + freq = 5000; + + if (nphy->mphase_cal_phase_id > 2) + ;/* TODO: Call N PHY Run Samples with (band width * 8), + 0xFFFF, 0, 1, 0 as arguments */ + else + ;/* TODO: Call N PHY TX Tone with freq, 250, 1, 0 as arguments + and save result as error */ + + if (error == 0) { + if (nphy->mphase_cal_phase_id > 2) { + table = nphy->mphase_txcal_bestcoeffs; + length = 11; + if (dev->phy.rev < 3) + length -= 2; + } else { + if (!full && nphy->txiqlocal_coeffsvalid) { + table = nphy->txiqlocal_bestc; + length = 11; + if (dev->phy.rev < 3) + length -= 2; + } else { + full = true; + if (dev->phy.rev >= 3) { + table = tbl_tx_iqlo_cal_startcoefs_nphyrev3; + length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3; + } else { + table = tbl_tx_iqlo_cal_startcoefs; + length = B43_NTAB_TX_IQLO_CAL_STARTCOEFS; + } + } + } + + /* TODO: Write an N PHY Table with ID 15, length from above, + offset 64, width 16, and the data pointer from above */ + + if (full) { + if (dev->phy.rev >= 3) + max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3; + else + max = B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL; + } else { + if (dev->phy.rev >= 3) + max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3; + else + max = B43_NTAB_TX_IQLO_CAL_CMDS_RECAL; + } + + if (mphase) { + count = nphy->mphase_txcal_cmdidx; + numb = min(max, + (u16)(count + nphy->mphase_txcal_numcmds)); + } else { + count = 0; + numb = max; + } + + for (; count < numb; count++) { + if (full) { + if (dev->phy.rev >= 3) + cmd = tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[count]; + else + cmd = tbl_tx_iqlo_cal_cmds_fullcal[count]; + } else { + if (dev->phy.rev >= 3) + cmd = tbl_tx_iqlo_cal_cmds_recal_nphyrev3[count]; + else + cmd = tbl_tx_iqlo_cal_cmds_recal[count]; + } + + core = (cmd & 0x3000) >> 12; + type = (cmd & 0x0F00) >> 8; + + if (phy6or5x && updated[core] == 0) { + b43_nphy_update_tx_cal_ladder(dev, core); + updated[core] = 1; + } + + tmp = (params[core].ncorr[type] << 8) | 0x66; + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDNNUM, tmp); + + if (type == 1 || type == 3 || type == 4) { + /* TODO: Read an N PHY Table with ID 15, + length 1, offset 69 + core, + width 16, and data pointer buffer */ + diq_start = buffer[0]; + buffer[0] = 0; + /* TODO: Write an N PHY Table with ID 15, + length 1, offset 69 + core, width 16, + and data of 0 */ + } + + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMD, cmd); + for (i = 0; i < 2000; i++) { + tmp = b43_phy_read(dev, B43_NPHY_IQLOCAL_CMD); + if (tmp & 0xC000) + break; + udelay(10); + } + + /* TODO: Read an N PHY Table with ID 15, + length table_length, offset 96, width 16, + and data pointer buffer */ + /* TODO: Write an N PHY Table with ID 15, + length table_length, offset 64, width 16, + and data pointer buffer */ + + if (type == 1 || type == 3 || type == 4) + buffer[0] = diq_start; + } + + if (mphase) + nphy->mphase_txcal_cmdidx = (numb >= max) ? 0 : numb; + + last = (dev->phy.rev < 3) ? 6 : 7; + + if (!mphase || nphy->mphase_cal_phase_id == last) { + /* TODO: Write an N PHY Table with ID 15, length 4, + offset 96, width 16, and data pointer buffer */ + /* TODO: Read an N PHY Table with ID 15, length 4, + offset 80, width 16, and data pointer buffer */ + if (dev->phy.rev < 3) { + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 0; + } + /* TODO: Write an N PHY Table with ID 15, length 4, + offset 88, width 16, and data pointer buffer */ + /* TODO: Read an N PHY Table with ID 15, length 2, + offset 101, width 16, and data pointer buffer*/ + /* TODO: Write an N PHY Table with ID 15, length 2, + offset 85, width 16, and data pointer buffer */ + /* TODO: Write an N PHY Table with ID 15, length 2, + offset 93, width 16, and data pointer buffer */ + length = 11; + if (dev->phy.rev < 3) + length -= 2; + /* TODO: Read an N PHY Table with ID 15, length length, + offset 96, width 16, and data pointer + nphy->txiqlocal_bestc */ + nphy->txiqlocal_coeffsvalid = true; + /* TODO: Set nphy->txiqlocal_chanspec to + the current channel */ + } else { + length = 11; + if (dev->phy.rev < 3) + length -= 2; + /* TODO: Read an N PHY Table with ID 5, length length, + offset 96, width 16, and data pointer + nphy->mphase_txcal_bestcoeffs */ + } + + /* TODO: Call N PHY Stop Playback */ + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0); + } + + /* TODO: Call N PHY TX Cal PHY Cleanup */ + /* TODO: Write an N PHY Table with ID 7, length 2, offset 0x110, + width 16, and data from save */ + + if (dev->phy.rev < 2 && (!mphase || nphy->mphase_cal_phase_id == last)) + b43_nphy_tx_iq_workaround(dev); + + if (dev->phy.rev >= 4) + nphy->hang_avoid = avoid; + + b43_nphy_stay_in_carrier_search(dev, false); + + return error; } +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIqRev2 */ +static int b43_nphy_rev2_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + struct b43_phy_n *nphy = dev->phy.n; + int i, j, index; + u8 rfctl[2]; + u8 afectl_core; + u16 tmp[6]; + u16 cur_hpf1, cur_hpf2, cur_lna; + u32 real, imag; + enum ieee80211_band band; + + u8 use; + u16 cur_hpf; + u16 lna[3] = { 3, 3, 1 }; + u16 hpf1[3] = { 7, 2, 0 }; + u16 hpf2[3] = { 2, 0, 0 }; + u32 power[3]; + u16 gain_save[2]; + u16 cal_gain[2]; + struct nphy_iqcal_params cal_params[2]; + struct nphy_iq_est est; + int ret = 0; + bool playtone = true; + int desired = 13; + + b43_nphy_stay_in_carrier_search(dev, 1); + + if (dev->phy.rev < 2) + ;/* TODO: Call N PHY Reapply TX Cal Coeffs */ + /* TODO: Read an N PHY Table with ID 7, length 2, offset 0x110, + width 16, and data gain_save */ + for (i = 0; i < 2; i++) { + b43_nphy_iq_cal_gain_params(dev, i, target, &cal_params[i]); + cal_gain[i] = cal_params[i].cal_gain; + } + /* TODO: Write an N PHY Table with ID 7, length 2, offset 0x110, + width 16, and data from cal_gain */ + + for (i = 0; i < 2; i++) { + if (i == 0) { + rfctl[0] = B43_NPHY_RFCTL_INTC1; + rfctl[1] = B43_NPHY_RFCTL_INTC2; + afectl_core = B43_NPHY_AFECTL_C1; + } else { + rfctl[0] = B43_NPHY_RFCTL_INTC2; + rfctl[1] = B43_NPHY_RFCTL_INTC1; + afectl_core = B43_NPHY_AFECTL_C2; + } + + tmp[1] = b43_phy_read(dev, B43_NPHY_RFSEQCA); + tmp[2] = b43_phy_read(dev, afectl_core); + tmp[3] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); + tmp[4] = b43_phy_read(dev, rfctl[0]); + tmp[5] = b43_phy_read(dev, rfctl[1]); + + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, + (u16)~B43_NPHY_RFSEQCA_RXDIS, + ((1 - i) << B43_NPHY_RFSEQCA_RXDIS_SHIFT)); + b43_phy_maskset(dev, B43_NPHY_RFSEQCA, ~B43_NPHY_RFSEQCA_TXEN, + (1 - i)); + b43_phy_set(dev, afectl_core, 0x0006); + b43_phy_set(dev, B43_NPHY_AFECTL_OVER, 0x0006); + + band = b43_current_band(dev->wl); + + if (nphy->rxcalparams & 0xFF000000) { + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[0], 0x140); + else + b43_phy_write(dev, rfctl[0], 0x110); + } else { + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[0], 0x180); + else + b43_phy_write(dev, rfctl[0], 0x120); + } + + if (band == IEEE80211_BAND_5GHZ) + b43_phy_write(dev, rfctl[1], 0x148); + else + b43_phy_write(dev, rfctl[1], 0x114); + + if (nphy->rxcalparams & 0x10000) { + b43_radio_maskset(dev, B2055_C1_GENSPARE2, 0xFC, + (i + 1)); + b43_radio_maskset(dev, B2055_C2_GENSPARE2, 0xFC, + (2 - i)); + } + + for (j = 0; i < 4; j++) { + if (j < 3) { + cur_lna = lna[j]; + cur_hpf1 = hpf1[j]; + cur_hpf2 = hpf2[j]; + } else { + if (power[1] > 10000) { + use = 1; + cur_hpf = cur_hpf1; + index = 2; + } else { + if (power[0] > 10000) { + use = 1; + cur_hpf = cur_hpf1; + index = 1; + } else { + index = 0; + use = 2; + cur_hpf = cur_hpf2; + } + } + cur_lna = lna[index]; + cur_hpf1 = hpf1[index]; + cur_hpf2 = hpf2[index]; + cur_hpf += desired - hweight32(power[index]); + cur_hpf = clamp_val(cur_hpf, 0, 10); + if (use == 1) + cur_hpf1 = cur_hpf; + else + cur_hpf2 = cur_hpf; + } + + tmp[0] = ((cur_hpf2 << 8) | (cur_hpf1 << 4) | + (cur_lna << 2)); + /* TODO:Call N PHY RF Ctrl Override with 0x400, tmp[0], + 3, 0 as arguments */ + /* TODO: Call N PHY Force RF Seq with 2 as argument */ + /* TODO: Call N PHT Stop Playback */ + + if (playtone) { + /* TODO: Call N PHY TX Tone with 4000, + (nphy_rxcalparams & 0xffff), 0, 0 + as arguments and save result as ret */ + playtone = false; + } else { + /* TODO: Call N PHY Run Samples with 160, + 0xFFFF, 0, 0, 0 as arguments */ + } + + if (ret == 0) { + if (j < 3) { + b43_nphy_rx_iq_est(dev, &est, 1024, 32, + false); + if (i == 0) { + real = est.i0_pwr; + imag = est.q0_pwr; + } else { + real = est.i1_pwr; + imag = est.q1_pwr; + } + power[i] = ((real + imag) / 1024) + 1; + } else { + b43_nphy_calc_rx_iq_comp(dev, 1 << i); + } + /* TODO: Call N PHY Stop Playback */ + } + + if (ret != 0) + break; + } + + b43_radio_mask(dev, B2055_C1_GENSPARE2, 0xFC); + b43_radio_mask(dev, B2055_C2_GENSPARE2, 0xFC); + b43_phy_write(dev, rfctl[1], tmp[5]); + b43_phy_write(dev, rfctl[0], tmp[4]); + b43_phy_write(dev, B43_NPHY_AFECTL_OVER, tmp[3]); + b43_phy_write(dev, afectl_core, tmp[2]); + b43_phy_write(dev, B43_NPHY_RFSEQCA, tmp[1]); + + if (ret != 0) + break; + } + + /* TODO: Call N PHY RF Ctrl Override with 0x400, 0, 3, 1 as arguments*/ + /* TODO: Call N PHY Force RF Seq with 2 as argument */ + /* TODO: Write an N PHY Table with ID 7, length 2, offset 0x110, + width 16, and data from gain_save */ + + b43_nphy_stay_in_carrier_search(dev, 0); + + return ret; +} + +static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + return -1; +} + +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/CalRxIq */ +static int b43_nphy_cal_rx_iq(struct b43_wldev *dev, + struct nphy_txgains target, u8 type, bool debug) +{ + if (dev->phy.rev >= 3) + return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug); + else + return b43_nphy_rev2_cal_rx_iq(dev, target, type, debug); +} + +/* + * Init N-PHY + * http://bcm-v4.sipsolutions.net/802.11/PHY/Init/N + */ int b43_phy_initn(struct b43_wldev *dev) { + struct ssb_bus *bus = dev->dev->bus; struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = phy->n; + u8 tx_pwr_state; + struct nphy_txgains target; u16 tmp; + enum ieee80211_band tmp2; + bool do_rssi_cal; + + u16 clip[2]; + bool do_cal = false; - //TODO: Spectral management + if ((dev->phy.rev >= 3) && + (bus->sprom.boardflags_lo & B43_BFL_EXTLNA) && + (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ)) { + chipco_set32(&dev->dev->bus->chipco, SSB_CHIPCO_CHIPCTL, 0x40); + } + nphy->deaf_count = 0; b43_nphy_tables_init(dev); + nphy->crsminpwr_adjusted = false; + nphy->noisevars_adjusted = false; /* Clear all overrides */ - b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + if (dev->phy.rev >= 3) { + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0); + b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0); + } else { + b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + } b43_phy_write(dev, B43_NPHY_RFCTL_INTC1, 0); b43_phy_write(dev, B43_NPHY_RFCTL_INTC2, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); - b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); + if (dev->phy.rev < 6) { + b43_phy_write(dev, B43_NPHY_RFCTL_INTC3, 0); + b43_phy_write(dev, B43_NPHY_RFCTL_INTC4, 0); + } b43_phy_mask(dev, B43_NPHY_RFSEQMODE, ~(B43_NPHY_RFSEQMODE_CAOVER | B43_NPHY_RFSEQMODE_TROVER)); + if (dev->phy.rev >= 3) + b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, 0); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, 0); - tmp = (phy->rev < 2) ? 64 : 59; - b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, - ~B43_NPHY_BPHY_CTL3_SCALE, - tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); - + if (dev->phy.rev <= 2) { + tmp = (dev->phy.rev == 2) ? 0x3B : 0x40; + b43_phy_maskset(dev, B43_NPHY_BPHY_CTL3, + ~B43_NPHY_BPHY_CTL3_SCALE, + tmp << B43_NPHY_BPHY_CTL3_SCALE_SHIFT); + } b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_20M, 0x20); b43_phy_write(dev, B43_NPHY_AFESEQ_TX2RX_PUD_40M, 0x20); - b43_phy_write(dev, B43_NPHY_TXREALFD, 184); - b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 200); - b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 80); - b43_phy_write(dev, B43_NPHY_C2_BCLIPBKOFF, 511); + if (bus->sprom.boardflags2_lo & 0x100 || + (bus->boardinfo.vendor == PCI_VENDOR_ID_APPLE && + bus->boardinfo.type == 0x8B)) + b43_phy_write(dev, B43_NPHY_TXREALFD, 0xA0); + else + b43_phy_write(dev, B43_NPHY_TXREALFD, 0xB8); + b43_phy_write(dev, B43_NPHY_MIMO_CRSTXEXT, 0xC8); + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50); + b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30); - //TODO MIMO-Config - //TODO Update TX/RX chain + /* TODO MIMO-Config */ + /* TODO Update TX/RX chain */ if (phy->rev < 2) { b43_phy_write(dev, B43_NPHY_DUP40_GFBL, 0xAA8); b43_phy_write(dev, B43_NPHY_DUP40_BL, 0x9A4); } + + tmp2 = b43_current_band(dev->wl); + if ((nphy->ipa2g_on && tmp2 == IEEE80211_BAND_2GHZ) || + (nphy->ipa5g_on && tmp2 == IEEE80211_BAND_5GHZ)) { + b43_phy_set(dev, B43_NPHY_PAPD_EN0, 0x1); + b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ0, 0x007F, + nphy->papd_epsilon_offset[0] << 7); + b43_phy_set(dev, B43_NPHY_PAPD_EN1, 0x1); + b43_phy_maskset(dev, B43_NPHY_EPS_TABLE_ADJ1, 0x007F, + nphy->papd_epsilon_offset[1] << 7); + /* TODO N PHY IPA Set TX Dig Filters */ + } else if (phy->rev >= 5) { + /* TODO N PHY Ext PA Set TX Dig Filters */ + } + b43_nphy_workarounds(dev); - b43_nphy_reset_cca(dev); - ssb_write32(dev->dev, SSB_TMSLOW, - ssb_read32(dev->dev, SSB_TMSLOW) | B43_TMSLOW_MACPHYCLKEN); + /* Reset CCA, in init code it differs a little from standard way */ + b43_nphy_bmac_clock_fgc(dev, 1); + tmp = b43_phy_read(dev, B43_NPHY_BBCFG); + b43_phy_write(dev, B43_NPHY_BBCFG, tmp | B43_NPHY_BBCFG_RSTCCA); + b43_phy_write(dev, B43_NPHY_BBCFG, tmp & ~B43_NPHY_BBCFG_RSTCCA); + b43_nphy_bmac_clock_fgc(dev, 0); + + /* TODO N PHY MAC PHY Clock Set with argument 1 */ + + b43_nphy_pa_override(dev, false); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_nphy_pa_override(dev, true); + + b43_nphy_classifier(dev, 0, 0); + b43_nphy_read_clip_detection(dev, clip); + tx_pwr_state = nphy->txpwrctrl; + /* TODO N PHY TX power control with argument 0 + (turning off power control) */ + /* TODO Fix the TX Power Settings */ + /* TODO N PHY TX Power Control Idle TSSI */ + /* TODO N PHY TX Power Control Setup */ + + if (phy->rev >= 3) { + /* TODO */ + } else { + /* TODO Write an N PHY table with ID 26, length 128, offset 192, width 32, and the data from Rev 2 TX Power Control Table */ + /* TODO Write an N PHY table with ID 27, length 128, offset 192, width 32, and the data from Rev 2 TX Power Control Table */ + } + + if (nphy->phyrxchain != 3) + ;/* TODO N PHY RX Core Set State with phyrxchain as argument */ + if (nphy->mphase_cal_phase_id > 0) + ;/* TODO PHY Periodic Calibration Multi-Phase Restart */ + + do_rssi_cal = false; + if (phy->rev >= 3) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + do_rssi_cal = (nphy->rssical_chanspec_2G == 0); + else + do_rssi_cal = (nphy->rssical_chanspec_5G == 0); + + if (do_rssi_cal) + b43_nphy_rssi_cal(dev); + else + b43_nphy_restore_rssi_cal(dev); + } else { + b43_nphy_rssi_cal(dev); + } + + if (!((nphy->measure_hold & 0x6) != 0)) { + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + do_cal = (nphy->iqcal_chanspec_2G == 0); + else + do_cal = (nphy->iqcal_chanspec_5G == 0); + + if (nphy->mute) + do_cal = false; + + if (do_cal) { + target = b43_nphy_get_tx_gains(dev); + + if (nphy->antsel_type == 2) + ;/*TODO NPHY Superswitch Init with argument 1*/ + if (nphy->perical != 2) { + b43_nphy_rssi_cal(dev); + if (phy->rev >= 3) { + nphy->cal_orig_pwr_idx[0] = + nphy->txpwrindex[0].index_internal; + nphy->cal_orig_pwr_idx[1] = + nphy->txpwrindex[1].index_internal; + /* TODO N PHY Pre Calibrate TX Gain */ + target = b43_nphy_get_tx_gains(dev); + } + } + } + } + + if (!b43_nphy_cal_tx_iq_lo(dev, target, true, false)) { + if (b43_nphy_cal_rx_iq(dev, target, 2, 0) == 0) + ;/* Call N PHY Save Cal */ + else if (nphy->mphase_cal_phase_id == 0) + ;/* N PHY Periodic Calibration with argument 3 */ + } else { + b43_nphy_restore_cal(dev); + } - b43_phy_read(dev, B43_NPHY_CLASSCTL); /* dummy read */ - //TODO read core1/2 clip1 thres regs - - if (1 /* FIXME Band is 2.4GHz */) - b43_nphy_bphy_init(dev); - //TODO disable TX power control - //TODO Fix the TX power settings - //TODO Init periodic calibration with reason 3 - b43_nphy_rssi_cal(dev, 2); - b43_nphy_rssi_cal(dev, 0); - b43_nphy_rssi_cal(dev, 1); - //TODO get TX gain - //TODO init superswitch - //TODO calibrate LO - //TODO idle TSSI TX pctl - //TODO TX power control power setup - //TODO table writes - //TODO TX power control coefficients - //TODO enable TX power control - //TODO control antenna selection - //TODO init radar detection - //TODO reset channel if changed + b43_nphy_tx_pwr_ctrl_coef_setup(dev); + /* TODO N PHY TX Power Control Enable with argument tx_pwr_state */ + b43_phy_write(dev, B43_NPHY_TXMACIF_HOLDOFF, 0x0015); + b43_phy_write(dev, B43_NPHY_TXMACDELAY, 0x0320); + if (phy->rev >= 3 && phy->rev <= 6) + b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x0014); + b43_nphy_tx_lp_fbw(dev); + /* TODO N PHY Spur Workaround */ b43err(dev->wl, "IEEE 802.11n devices are not supported, yet.\n"); return 0; diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 1749aef4147..4572866756f 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -231,6 +231,7 @@ #define B43_NPHY_C2_TXIQ_COMP_OFF B43_PHY_N(0x088) /* Core 2 TX I/Q comp offset */ #define B43_NPHY_C1_TXCTL B43_PHY_N(0x08B) /* Core 1 TX control */ #define B43_NPHY_C2_TXCTL B43_PHY_N(0x08C) /* Core 2 TX control */ +#define B43_NPHY_AFECTL_OVER1 B43_PHY_N(0x08F) /* AFE control override 1 */ #define B43_NPHY_SCRAM_SIGCTL B43_PHY_N(0x090) /* Scram signal control */ #define B43_NPHY_SCRAM_SIGCTL_INITST 0x007F /* Initial state value */ #define B43_NPHY_SCRAM_SIGCTL_INITST_SHIFT 0 @@ -705,6 +706,10 @@ #define B43_NPHY_TXPCTL_INIT B43_PHY_N(0x222) /* TX power controll init */ #define B43_NPHY_TXPCTL_INIT_PIDXI1 0x00FF /* Power index init 1 */ #define B43_NPHY_TXPCTL_INIT_PIDXI1_SHIFT 0 +#define B43_NPHY_PAPD_EN0 B43_PHY_N(0x297) /* PAPD Enable0 TBD */ +#define B43_NPHY_EPS_TABLE_ADJ0 B43_PHY_N(0x298) /* EPS Table Adj0 TBD */ +#define B43_NPHY_PAPD_EN1 B43_PHY_N(0x29B) /* PAPD Enable1 TBD */ +#define B43_NPHY_EPS_TABLE_ADJ1 B43_PHY_N(0x29C) /* EPS Table Adj1 TBD */ @@ -919,8 +924,88 @@ struct b43_wldev; +struct b43_phy_n_iq_comp { + s16 a0; + s16 b0; + s16 a1; + s16 b1; +}; + +struct b43_phy_n_rssical_cache { + u16 rssical_radio_regs_2G[2]; + u16 rssical_phy_regs_2G[12]; + + u16 rssical_radio_regs_5G[2]; + u16 rssical_phy_regs_5G[12]; +}; + +struct b43_phy_n_cal_cache { + u16 txcal_radio_regs_2G[8]; + u16 txcal_coeffs_2G[8]; + struct b43_phy_n_iq_comp rxcal_coeffs_2G; + + u16 txcal_radio_regs_5G[8]; + u16 txcal_coeffs_5G[8]; + struct b43_phy_n_iq_comp rxcal_coeffs_5G; +}; + +struct b43_phy_n_txpwrindex { + s8 index; + s8 index_internal; + s8 index_internal_save; + u16 AfectrlOverride; + u16 AfeCtrlDacGain; + u16 rad_gain; + u8 bbmult; + u16 iqcomp_a; + u16 iqcomp_b; + u16 locomp; +}; + struct b43_phy_n { - //TODO lots of missing stuff + u8 antsel_type; + u8 cal_orig_pwr_idx[2]; + u8 measure_hold; + u8 phyrxchain; + u8 perical; + u32 deaf_count; + u32 rxcalparams; + bool hang_avoid; + bool mute; + u16 papd_epsilon_offset[2]; + + u8 mphase_cal_phase_id; + u16 mphase_txcal_cmdidx; + u16 mphase_txcal_numcmds; + u16 mphase_txcal_bestcoeffs[11]; + + u8 txpwrctrl; + u16 txcal_bbmult; + u16 txiqlocal_bestc[11]; + bool txiqlocal_coeffsvalid; + struct b43_phy_n_txpwrindex txpwrindex[2]; + + u16 tx_rx_cal_phy_saveregs[11]; + u16 tx_rx_cal_radio_saveregs[22]; + + u16 rfctrl_intc1_save; + u16 rfctrl_intc2_save; + + u16 classifier_state; + u16 clip_state[2]; + + bool ipa2g_on; + u8 iqcal_chanspec_2G; + u8 rssical_chanspec_2G; + + bool ipa5g_on; + u8 iqcal_chanspec_5G; + u8 rssical_chanspec_5G; + + struct b43_phy_n_rssical_cache rssical_cache; + struct b43_phy_n_cal_cache cal_cache; + bool crsminpwr_adjusted; + bool noisevars_adjusted; }; diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 4e233631554..7dff853ab96 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -1336,7 +1336,7 @@ b43_nphy_get_chantabent(struct b43_wldev *dev, u8 channel) } -const u8 b43_ntab_adjustpower0[] = { +static const u8 b43_ntab_adjustpower0[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, @@ -1355,7 +1355,7 @@ const u8 b43_ntab_adjustpower0[] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, }; -const u8 b43_ntab_adjustpower1[] = { +static const u8 b43_ntab_adjustpower1[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, @@ -1374,11 +1374,11 @@ const u8 b43_ntab_adjustpower1[] = { 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, }; -const u16 b43_ntab_bdi[] = { +static const u16 b43_ntab_bdi[] = { 0x0070, 0x0126, 0x012C, 0x0246, 0x048D, 0x04D2, }; -const u32 b43_ntab_channelest[] = { +static const u32 b43_ntab_channelest[] = { 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x44444444, 0x10101010, 0x10101010, 0x10101010, 0x10101010, @@ -1405,7 +1405,7 @@ const u32 b43_ntab_channelest[] = { 0x10101010, 0x10101010, 0x10101010, 0x10101010, }; -const u8 b43_ntab_estimatepowerlt0[] = { +static const u8 b43_ntab_estimatepowerlt0[] = { 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, @@ -1416,7 +1416,7 @@ const u8 b43_ntab_estimatepowerlt0[] = { 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; -const u8 b43_ntab_estimatepowerlt1[] = { +static const u8 b43_ntab_estimatepowerlt1[] = { 0x50, 0x4F, 0x4E, 0x4D, 0x4C, 0x4B, 0x4A, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, @@ -1427,14 +1427,14 @@ const u8 b43_ntab_estimatepowerlt1[] = { 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, }; -const u8 b43_ntab_framelookup[] = { +static const u8 b43_ntab_framelookup[] = { 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, 0x0A, 0x0C, 0x1C, 0x1C, 0x0B, 0x0D, 0x1E, 0x1E, 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1A, 0x1A, 0x0E, 0x10, 0x20, 0x28, 0x0F, 0x11, 0x22, 0x2A, }; -const u32 b43_ntab_framestruct[] = { +static const u32 b43_ntab_framestruct[] = { 0x08004A04, 0x00100000, 0x01000A05, 0x00100020, 0x09804506, 0x00100030, 0x09804507, 0x00100030, 0x00000000, 0x00000000, 0x00000000, 0x00000000, @@ -1645,7 +1645,7 @@ const u32 b43_ntab_framestruct[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; -const u32 b43_ntab_gainctl0[] = { +static const u32 b43_ntab_gainctl0[] = { 0x007F003F, 0x007E013F, 0x007D023E, 0x007C033E, 0x007B043D, 0x007A053D, 0x0079063C, 0x0078073C, 0x0077083B, 0x0076093B, 0x00750A3A, 0x00740B3A, @@ -1680,7 +1680,7 @@ const u32 b43_ntab_gainctl0[] = { 0x00030C01, 0x00020D01, 0x00010E00, 0x00000F00, }; -const u32 b43_ntab_gainctl1[] = { +static const u32 b43_ntab_gainctl1[] = { 0x007F003F, 0x007E013F, 0x007D023E, 0x007C033E, 0x007B043D, 0x007A053D, 0x0079063C, 0x0078073C, 0x0077083B, 0x0076093B, 0x00750A3A, 0x00740B3A, @@ -1715,12 +1715,12 @@ const u32 b43_ntab_gainctl1[] = { 0x00030C01, 0x00020D01, 0x00010E00, 0x00000F00, }; -const u32 b43_ntab_intlevel[] = { +static const u32 b43_ntab_intlevel[] = { 0x00802070, 0x0671188D, 0x0A60192C, 0x0A300E46, 0x00C1188D, 0x080024D2, 0x00000070, }; -const u32 b43_ntab_iqlt0[] = { +static const u32 b43_ntab_iqlt0[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, @@ -1755,7 +1755,7 @@ const u32 b43_ntab_iqlt0[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, }; -const u32 b43_ntab_iqlt1[] = { +static const u32 b43_ntab_iqlt1[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, @@ -1790,7 +1790,7 @@ const u32 b43_ntab_iqlt1[] = { 0x0000007F, 0x0000007F, 0x0000007F, 0x0000007F, }; -const u16 b43_ntab_loftlt0[] = { +static const u16 b43_ntab_loftlt0[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, @@ -1815,7 +1815,7 @@ const u16 b43_ntab_loftlt0[] = { 0x0002, 0x0103, }; -const u16 b43_ntab_loftlt1[] = { +static const u16 b43_ntab_loftlt1[] = { 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, 0x0002, 0x0103, 0x0000, 0x0101, @@ -1840,7 +1840,7 @@ const u16 b43_ntab_loftlt1[] = { 0x0002, 0x0103, }; -const u8 b43_ntab_mcs[] = { +static const u8 b43_ntab_mcs[] = { 0x00, 0x08, 0x0A, 0x10, 0x12, 0x19, 0x1A, 0x1C, 0x40, 0x48, 0x4A, 0x50, 0x52, 0x59, 0x5A, 0x5C, 0x80, 0x88, 0x8A, 0x90, 0x92, 0x99, 0x9A, 0x9C, @@ -1859,7 +1859,7 @@ const u8 b43_ntab_mcs[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -const u32 b43_ntab_noisevar10[] = { +static const u32 b43_ntab_noisevar10[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, @@ -1926,7 +1926,7 @@ const u32 b43_ntab_noisevar10[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, }; -const u32 b43_ntab_noisevar11[] = { +static const u32 b43_ntab_noisevar11[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, @@ -1993,7 +1993,7 @@ const u32 b43_ntab_noisevar11[] = { 0x020C020C, 0x0000014D, 0x020C020C, 0x0000014D, }; -const u16 b43_ntab_pilot[] = { +static const u16 b43_ntab_pilot[] = { 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0xFF08, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0x80D5, 0xFF0A, 0xFF82, @@ -2011,12 +2011,12 @@ const u16 b43_ntab_pilot[] = { 0xF0A0, 0xF028, 0xFFFF, 0xFFFF, }; -const u32 b43_ntab_pilotlt[] = { +static const u32 b43_ntab_pilotlt[] = { 0x76540123, 0x62407351, 0x76543201, 0x76540213, 0x76540123, 0x76430521, }; -const u32 b43_ntab_tdi20a0[] = { +static const u32 b43_ntab_tdi20a0[] = { 0x00091226, 0x000A1429, 0x000B56AD, 0x000C58B0, 0x000D5AB3, 0x000E9CB6, 0x000F9EBA, 0x0000C13D, 0x00020301, 0x00030504, 0x00040708, 0x0005090B, @@ -2033,7 +2033,7 @@ const u32 b43_ntab_tdi20a0[] = { 0x00000000, 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdi20a1[] = { +static const u32 b43_ntab_tdi20a1[] = { 0x00014B26, 0x00028D29, 0x000393AD, 0x00049630, 0x0005D833, 0x0006DA36, 0x00099C3A, 0x000A9E3D, 0x000BC081, 0x000CC284, 0x000DC488, 0x000F068B, @@ -2050,7 +2050,7 @@ const u32 b43_ntab_tdi20a1[] = { 0x00000000, 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdi40a0[] = { +static const u32 b43_ntab_tdi40a0[] = { 0x0011A346, 0x00136CCF, 0x0014F5D9, 0x001641E2, 0x0017CB6B, 0x00195475, 0x001B2383, 0x001CAD0C, 0x001E7616, 0x0000821F, 0x00020BA8, 0x0003D4B2, @@ -2081,7 +2081,7 @@ const u32 b43_ntab_tdi40a0[] = { 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdi40a1[] = { +static const u32 b43_ntab_tdi40a1[] = { 0x001EDB36, 0x000129CA, 0x0002B353, 0x00047CDD, 0x0005C8E6, 0x000791EF, 0x00091BF9, 0x000AAA07, 0x000C3391, 0x000DFD1A, 0x00120923, 0x0013D22D, @@ -2112,7 +2112,7 @@ const u32 b43_ntab_tdi40a1[] = { 0x00000000, 0x00000000, }; -const u32 b43_ntab_tdtrn[] = { +static const u32 b43_ntab_tdtrn[] = { 0x061C061C, 0x0050EE68, 0xF592FE36, 0xFE5212F6, 0x00000C38, 0xFE5212F6, 0xF592FE36, 0x0050EE68, 0x061C061C, 0xEE680050, 0xFE36F592, 0x12F6FE52, @@ -2291,7 +2291,7 @@ const u32 b43_ntab_tdtrn[] = { 0xFA58FC00, 0x0B64FC7E, 0x0800F7B6, 0x00F006BE, }; -const u32 b43_ntab_tmap[] = { +static const u32 b43_ntab_tmap[] = { 0x8A88AA80, 0x8AAAAA8A, 0x8A8A8AA8, 0x00000888, 0x88000000, 0x8A8A88AA, 0x8AA88888, 0x8888A8A8, 0xF1111110, 0x11111111, 0x11F11111, 0x00000111, @@ -2406,6 +2406,483 @@ const u32 b43_ntab_tmap[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; +const u32 b43_ntab_tx_gain_rev0_1_2[] = { + 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, + 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, + 0x03c82a42, 0x03c82944, 0x03c82942, 0x03c82844, + 0x03c82842, 0x03c42b44, 0x03c42b42, 0x03c42a44, + 0x03c42a42, 0x03c42944, 0x03c42942, 0x03c42844, + 0x03c42842, 0x03c42744, 0x03c42742, 0x03c42644, + 0x03c42642, 0x03c42544, 0x03c42542, 0x03c42444, + 0x03c42442, 0x03c02b44, 0x03c02b42, 0x03c02a44, + 0x03c02a42, 0x03c02944, 0x03c02942, 0x03c02844, + 0x03c02842, 0x03c02744, 0x03c02742, 0x03b02b44, + 0x03b02b42, 0x03b02a44, 0x03b02a42, 0x03b02944, + 0x03b02942, 0x03b02844, 0x03b02842, 0x03b02744, + 0x03b02742, 0x03b02644, 0x03b02642, 0x03b02544, + 0x03b02542, 0x03a02b44, 0x03a02b42, 0x03a02a44, + 0x03a02a42, 0x03a02944, 0x03a02942, 0x03a02844, + 0x03a02842, 0x03a02744, 0x03a02742, 0x03902b44, + 0x03902b42, 0x03902a44, 0x03902a42, 0x03902944, + 0x03902942, 0x03902844, 0x03902842, 0x03902744, + 0x03902742, 0x03902644, 0x03902642, 0x03902544, + 0x03902542, 0x03802b44, 0x03802b42, 0x03802a44, + 0x03802a42, 0x03802944, 0x03802942, 0x03802844, + 0x03802842, 0x03802744, 0x03802742, 0x03802644, + 0x03802642, 0x03802544, 0x03802542, 0x03802444, + 0x03802442, 0x03802344, 0x03802342, 0x03802244, + 0x03802242, 0x03802144, 0x03802142, 0x03802044, + 0x03802042, 0x03801f44, 0x03801f42, 0x03801e44, + 0x03801e42, 0x03801d44, 0x03801d42, 0x03801c44, + 0x03801c42, 0x03801b44, 0x03801b42, 0x03801a44, + 0x03801a42, 0x03801944, 0x03801942, 0x03801844, + 0x03801842, 0x03801744, 0x03801742, 0x03801644, + 0x03801642, 0x03801544, 0x03801542, 0x03801444, + 0x03801442, 0x03801344, 0x03801342, 0x00002b00, +}; + +const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { + 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, + 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, + 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, + 0x1e41003c, 0x1e41003b, 0x1e410039, 0x1e410037, + 0x1d410044, 0x1d410042, 0x1d410040, 0x1d41003e, + 0x1d41003c, 0x1d41003b, 0x1d410039, 0x1d410037, + 0x1c410044, 0x1c410042, 0x1c410040, 0x1c41003e, + 0x1c41003c, 0x1c41003b, 0x1c410039, 0x1c410037, + 0x1b410044, 0x1b410042, 0x1b410040, 0x1b41003e, + 0x1b41003c, 0x1b41003b, 0x1b410039, 0x1b410037, + 0x1a410044, 0x1a410042, 0x1a410040, 0x1a41003e, + 0x1a41003c, 0x1a41003b, 0x1a410039, 0x1a410037, + 0x19410044, 0x19410042, 0x19410040, 0x1941003e, + 0x1941003c, 0x1941003b, 0x19410039, 0x19410037, + 0x18410044, 0x18410042, 0x18410040, 0x1841003e, + 0x1841003c, 0x1841003b, 0x18410039, 0x18410037, + 0x17410044, 0x17410042, 0x17410040, 0x1741003e, + 0x1741003c, 0x1741003b, 0x17410039, 0x17410037, + 0x16410044, 0x16410042, 0x16410040, 0x1641003e, + 0x1641003c, 0x1641003b, 0x16410039, 0x16410037, + 0x15410044, 0x15410042, 0x15410040, 0x1541003e, + 0x1541003c, 0x1541003b, 0x15410039, 0x15410037, + 0x14410044, 0x14410042, 0x14410040, 0x1441003e, + 0x1441003c, 0x1441003b, 0x14410039, 0x14410037, + 0x13410044, 0x13410042, 0x13410040, 0x1341003e, + 0x1341003c, 0x1341003b, 0x13410039, 0x13410037, + 0x12410044, 0x12410042, 0x12410040, 0x1241003e, + 0x1241003c, 0x1241003b, 0x12410039, 0x12410037, + 0x11410044, 0x11410042, 0x11410040, 0x1141003e, + 0x1141003c, 0x1141003b, 0x11410039, 0x11410037, + 0x10410044, 0x10410042, 0x10410040, 0x1041003e, + 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, +}; + +const u32 b43_ntab_tx_gain_rev3_5ghz[] = { + 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, + 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, + 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, + 0xcef7003c, 0xcef7003b, 0xcef70039, 0xcef70037, + 0xcdf70044, 0xcdf70042, 0xcdf70040, 0xcdf7003e, + 0xcdf7003c, 0xcdf7003b, 0xcdf70039, 0xcdf70037, + 0xccf70044, 0xccf70042, 0xccf70040, 0xccf7003e, + 0xccf7003c, 0xccf7003b, 0xccf70039, 0xccf70037, + 0xcbf70044, 0xcbf70042, 0xcbf70040, 0xcbf7003e, + 0xcbf7003c, 0xcbf7003b, 0xcbf70039, 0xcbf70037, + 0xcaf70044, 0xcaf70042, 0xcaf70040, 0xcaf7003e, + 0xcaf7003c, 0xcaf7003b, 0xcaf70039, 0xcaf70037, + 0xc9f70044, 0xc9f70042, 0xc9f70040, 0xc9f7003e, + 0xc9f7003c, 0xc9f7003b, 0xc9f70039, 0xc9f70037, + 0xc8f70044, 0xc8f70042, 0xc8f70040, 0xc8f7003e, + 0xc8f7003c, 0xc8f7003b, 0xc8f70039, 0xc8f70037, + 0xc7f70044, 0xc7f70042, 0xc7f70040, 0xc7f7003e, + 0xc7f7003c, 0xc7f7003b, 0xc7f70039, 0xc7f70037, + 0xc6f70044, 0xc6f70042, 0xc6f70040, 0xc6f7003e, + 0xc6f7003c, 0xc6f7003b, 0xc6f70039, 0xc6f70037, + 0xc5f70044, 0xc5f70042, 0xc5f70040, 0xc5f7003e, + 0xc5f7003c, 0xc5f7003b, 0xc5f70039, 0xc5f70037, + 0xc4f70044, 0xc4f70042, 0xc4f70040, 0xc4f7003e, + 0xc4f7003c, 0xc4f7003b, 0xc4f70039, 0xc4f70037, + 0xc3f70044, 0xc3f70042, 0xc3f70040, 0xc3f7003e, + 0xc3f7003c, 0xc3f7003b, 0xc3f70039, 0xc3f70037, + 0xc2f70044, 0xc2f70042, 0xc2f70040, 0xc2f7003e, + 0xc2f7003c, 0xc2f7003b, 0xc2f70039, 0xc2f70037, + 0xc1f70044, 0xc1f70042, 0xc1f70040, 0xc1f7003e, + 0xc1f7003c, 0xc1f7003b, 0xc1f70039, 0xc1f70037, + 0xc0f70044, 0xc0f70042, 0xc0f70040, 0xc0f7003e, + 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, +}; + +const u32 b43_ntab_tx_gain_rev4_5ghz[] = { + 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, + 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, + 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, + 0x2ef2003c, 0x2ef2003b, 0x2ef20039, 0x2ef20037, + 0x2df20044, 0x2df20042, 0x2df20040, 0x2df2003e, + 0x2df2003c, 0x2df2003b, 0x2df20039, 0x2df20037, + 0x2cf20044, 0x2cf20042, 0x2cf20040, 0x2cf2003e, + 0x2cf2003c, 0x2cf2003b, 0x2cf20039, 0x2cf20037, + 0x2bf20044, 0x2bf20042, 0x2bf20040, 0x2bf2003e, + 0x2bf2003c, 0x2bf2003b, 0x2bf20039, 0x2bf20037, + 0x2af20044, 0x2af20042, 0x2af20040, 0x2af2003e, + 0x2af2003c, 0x2af2003b, 0x2af20039, 0x2af20037, + 0x29f20044, 0x29f20042, 0x29f20040, 0x29f2003e, + 0x29f2003c, 0x29f2003b, 0x29f20039, 0x29f20037, + 0x28f20044, 0x28f20042, 0x28f20040, 0x28f2003e, + 0x28f2003c, 0x28f2003b, 0x28f20039, 0x28f20037, + 0x27f20044, 0x27f20042, 0x27f20040, 0x27f2003e, + 0x27f2003c, 0x27f2003b, 0x27f20039, 0x27f20037, + 0x26f20044, 0x26f20042, 0x26f20040, 0x26f2003e, + 0x26f2003c, 0x26f2003b, 0x26f20039, 0x26f20037, + 0x25f20044, 0x25f20042, 0x25f20040, 0x25f2003e, + 0x25f2003c, 0x25f2003b, 0x25f20039, 0x25f20037, + 0x24f20044, 0x24f20042, 0x24f20040, 0x24f2003e, + 0x24f2003c, 0x24f2003b, 0x24f20039, 0x24f20038, + 0x23f20041, 0x23f20040, 0x23f2003f, 0x23f2003e, + 0x23f2003c, 0x23f2003b, 0x23f20039, 0x23f20037, + 0x22f20044, 0x22f20042, 0x22f20040, 0x22f2003e, + 0x22f2003c, 0x22f2003b, 0x22f20039, 0x22f20037, + 0x21f20044, 0x21f20042, 0x21f20040, 0x21f2003e, + 0x21f2003c, 0x21f2003b, 0x21f20039, 0x21f20037, + 0x20d20043, 0x20d20041, 0x20d2003e, 0x20d2003c, + 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, +}; + +const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { + 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, + 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, + 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, + 0x0e62003c, 0x0e62003d, 0x0e62003b, 0x0e62003a, + 0x0d620043, 0x0d620041, 0x0d620040, 0x0d62003e, + 0x0d62003d, 0x0d62003c, 0x0d62003b, 0x0d62003a, + 0x0c620041, 0x0c620040, 0x0c62003f, 0x0c62003e, + 0x0c62003c, 0x0c62003b, 0x0c620039, 0x0c620037, + 0x0b620046, 0x0b620044, 0x0b620042, 0x0b620040, + 0x0b62003e, 0x0b62003c, 0x0b62003b, 0x0b62003a, + 0x0a620041, 0x0a620040, 0x0a62003e, 0x0a62003c, + 0x0a62003b, 0x0a62003a, 0x0a620039, 0x0a620038, + 0x0962003e, 0x0962003d, 0x0962003c, 0x0962003b, + 0x09620039, 0x09620037, 0x09620035, 0x09620033, + 0x08620044, 0x08620042, 0x08620040, 0x0862003e, + 0x0862003c, 0x0862003b, 0x0862003a, 0x08620039, + 0x07620043, 0x07620042, 0x07620040, 0x0762003f, + 0x0762003d, 0x0762003b, 0x0762003a, 0x07620039, + 0x0662003e, 0x0662003d, 0x0662003c, 0x0662003b, + 0x06620039, 0x06620037, 0x06620035, 0x06620033, + 0x05620046, 0x05620044, 0x05620042, 0x05620040, + 0x0562003e, 0x0562003c, 0x0562003b, 0x05620039, + 0x04620044, 0x04620042, 0x04620040, 0x0462003e, + 0x0462003c, 0x0462003b, 0x04620039, 0x04620038, + 0x0362003c, 0x0362003b, 0x0362003a, 0x03620039, + 0x03620038, 0x03620037, 0x03620035, 0x03620033, + 0x0262004c, 0x0262004a, 0x02620048, 0x02620047, + 0x02620046, 0x02620044, 0x02620043, 0x02620042, + 0x0162004a, 0x01620048, 0x01620046, 0x01620044, + 0x01620043, 0x01620042, 0x01620041, 0x01620040, + 0x00620042, 0x00620040, 0x0062003e, 0x0062003c, + 0x0062003b, 0x00620039, 0x00620037, 0x00620035, +}; + +const u32 txpwrctrl_tx_gain_ipa[] = { + 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, + 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, + 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, + 0x5ef70028, 0x5ef70027, 0x5ef70026, 0x5ef70025, + 0x5df7002d, 0x5df7002b, 0x5df7002a, 0x5df70029, + 0x5df70028, 0x5df70027, 0x5df70026, 0x5df70025, + 0x5cf7002d, 0x5cf7002b, 0x5cf7002a, 0x5cf70029, + 0x5cf70028, 0x5cf70027, 0x5cf70026, 0x5cf70025, + 0x5bf7002d, 0x5bf7002b, 0x5bf7002a, 0x5bf70029, + 0x5bf70028, 0x5bf70027, 0x5bf70026, 0x5bf70025, + 0x5af7002d, 0x5af7002b, 0x5af7002a, 0x5af70029, + 0x5af70028, 0x5af70027, 0x5af70026, 0x5af70025, + 0x59f7002d, 0x59f7002b, 0x59f7002a, 0x59f70029, + 0x59f70028, 0x59f70027, 0x59f70026, 0x59f70025, + 0x58f7002d, 0x58f7002b, 0x58f7002a, 0x58f70029, + 0x58f70028, 0x58f70027, 0x58f70026, 0x58f70025, + 0x57f7002d, 0x57f7002b, 0x57f7002a, 0x57f70029, + 0x57f70028, 0x57f70027, 0x57f70026, 0x57f70025, + 0x56f7002d, 0x56f7002b, 0x56f7002a, 0x56f70029, + 0x56f70028, 0x56f70027, 0x56f70026, 0x56f70025, + 0x55f7002d, 0x55f7002b, 0x55f7002a, 0x55f70029, + 0x55f70028, 0x55f70027, 0x55f70026, 0x55f70025, + 0x54f7002d, 0x54f7002b, 0x54f7002a, 0x54f70029, + 0x54f70028, 0x54f70027, 0x54f70026, 0x54f70025, + 0x53f7002d, 0x53f7002b, 0x53f7002a, 0x53f70029, + 0x53f70028, 0x53f70027, 0x53f70026, 0x53f70025, + 0x52f7002d, 0x52f7002b, 0x52f7002a, 0x52f70029, + 0x52f70028, 0x52f70027, 0x52f70026, 0x52f70025, + 0x51f7002d, 0x51f7002b, 0x51f7002a, 0x51f70029, + 0x51f70028, 0x51f70027, 0x51f70026, 0x51f70025, + 0x50f7002d, 0x50f7002b, 0x50f7002a, 0x50f70029, + 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, +}; + +const u32 txpwrctrl_tx_gain_ipa_rev5[] = { + 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, + 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, + 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, + 0x1ef70028, 0x1ef70027, 0x1ef70026, 0x1ef70025, + 0x1df7002d, 0x1df7002b, 0x1df7002a, 0x1df70029, + 0x1df70028, 0x1df70027, 0x1df70026, 0x1df70025, + 0x1cf7002d, 0x1cf7002b, 0x1cf7002a, 0x1cf70029, + 0x1cf70028, 0x1cf70027, 0x1cf70026, 0x1cf70025, + 0x1bf7002d, 0x1bf7002b, 0x1bf7002a, 0x1bf70029, + 0x1bf70028, 0x1bf70027, 0x1bf70026, 0x1bf70025, + 0x1af7002d, 0x1af7002b, 0x1af7002a, 0x1af70029, + 0x1af70028, 0x1af70027, 0x1af70026, 0x1af70025, + 0x19f7002d, 0x19f7002b, 0x19f7002a, 0x19f70029, + 0x19f70028, 0x19f70027, 0x19f70026, 0x19f70025, + 0x18f7002d, 0x18f7002b, 0x18f7002a, 0x18f70029, + 0x18f70028, 0x18f70027, 0x18f70026, 0x18f70025, + 0x17f7002d, 0x17f7002b, 0x17f7002a, 0x17f70029, + 0x17f70028, 0x17f70027, 0x17f70026, 0x17f70025, + 0x16f7002d, 0x16f7002b, 0x16f7002a, 0x16f70029, + 0x16f70028, 0x16f70027, 0x16f70026, 0x16f70025, + 0x15f7002d, 0x15f7002b, 0x15f7002a, 0x15f70029, + 0x15f70028, 0x15f70027, 0x15f70026, 0x15f70025, + 0x14f7002d, 0x14f7002b, 0x14f7002a, 0x14f70029, + 0x14f70028, 0x14f70027, 0x14f70026, 0x14f70025, + 0x13f7002d, 0x13f7002b, 0x13f7002a, 0x13f70029, + 0x13f70028, 0x13f70027, 0x13f70026, 0x13f70025, + 0x12f7002d, 0x12f7002b, 0x12f7002a, 0x12f70029, + 0x12f70028, 0x12f70027, 0x12f70026, 0x12f70025, + 0x11f7002d, 0x11f7002b, 0x11f7002a, 0x11f70029, + 0x11f70028, 0x11f70027, 0x11f70026, 0x11f70025, + 0x10f7002d, 0x10f7002b, 0x10f7002a, 0x10f70029, + 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, +}; + +const u32 txpwrctrl_tx_gain_ipa_rev6[] = { + 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, + 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, + 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, + 0x0ef70028, 0x0ef70027, 0x0ef70026, 0x0ef70025, + 0x0df7002d, 0x0df7002b, 0x0df7002a, 0x0df70029, + 0x0df70028, 0x0df70027, 0x0df70026, 0x0df70025, + 0x0cf7002d, 0x0cf7002b, 0x0cf7002a, 0x0cf70029, + 0x0cf70028, 0x0cf70027, 0x0cf70026, 0x0cf70025, + 0x0bf7002d, 0x0bf7002b, 0x0bf7002a, 0x0bf70029, + 0x0bf70028, 0x0bf70027, 0x0bf70026, 0x0bf70025, + 0x0af7002d, 0x0af7002b, 0x0af7002a, 0x0af70029, + 0x0af70028, 0x0af70027, 0x0af70026, 0x0af70025, + 0x09f7002d, 0x09f7002b, 0x09f7002a, 0x09f70029, + 0x09f70028, 0x09f70027, 0x09f70026, 0x09f70025, + 0x08f7002d, 0x08f7002b, 0x08f7002a, 0x08f70029, + 0x08f70028, 0x08f70027, 0x08f70026, 0x08f70025, + 0x07f7002d, 0x07f7002b, 0x07f7002a, 0x07f70029, + 0x07f70028, 0x07f70027, 0x07f70026, 0x07f70025, + 0x06f7002d, 0x06f7002b, 0x06f7002a, 0x06f70029, + 0x06f70028, 0x06f70027, 0x06f70026, 0x06f70025, + 0x05f7002d, 0x05f7002b, 0x05f7002a, 0x05f70029, + 0x05f70028, 0x05f70027, 0x05f70026, 0x05f70025, + 0x04f7002d, 0x04f7002b, 0x04f7002a, 0x04f70029, + 0x04f70028, 0x04f70027, 0x04f70026, 0x04f70025, + 0x03f7002d, 0x03f7002b, 0x03f7002a, 0x03f70029, + 0x03f70028, 0x03f70027, 0x03f70026, 0x03f70025, + 0x02f7002d, 0x02f7002b, 0x02f7002a, 0x02f70029, + 0x02f70028, 0x02f70027, 0x02f70026, 0x02f70025, + 0x01f7002d, 0x01f7002b, 0x01f7002a, 0x01f70029, + 0x01f70028, 0x01f70027, 0x01f70026, 0x01f70025, + 0x00f7002d, 0x00f7002b, 0x00f7002a, 0x00f70029, + 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, +}; + +const u32 txpwrctrl_tx_gain_ipa_5g[] = { + 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, + 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, + 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, + 0x7ff70026, 0x7ff70024, 0x7ff70023, 0x7ff70022, + 0x7ef70028, 0x7ef70027, 0x7ef70026, 0x7ef70025, + 0x7ef70024, 0x7ef70023, 0x7df70028, 0x7df70027, + 0x7df70026, 0x7df70025, 0x7df70024, 0x7df70023, + 0x7df70022, 0x7cf70029, 0x7cf70028, 0x7cf70027, + 0x7cf70026, 0x7cf70025, 0x7cf70023, 0x7cf70022, + 0x7bf70029, 0x7bf70028, 0x7bf70026, 0x7bf70025, + 0x7bf70024, 0x7bf70023, 0x7bf70022, 0x7bf70021, + 0x7af70029, 0x7af70028, 0x7af70027, 0x7af70026, + 0x7af70025, 0x7af70024, 0x7af70023, 0x7af70022, + 0x79f70029, 0x79f70028, 0x79f70027, 0x79f70026, + 0x79f70025, 0x79f70024, 0x79f70023, 0x79f70022, + 0x78f70029, 0x78f70028, 0x78f70027, 0x78f70026, + 0x78f70025, 0x78f70024, 0x78f70023, 0x78f70022, + 0x77f70029, 0x77f70028, 0x77f70027, 0x77f70026, + 0x77f70025, 0x77f70024, 0x77f70023, 0x77f70022, + 0x76f70029, 0x76f70028, 0x76f70027, 0x76f70026, + 0x76f70024, 0x76f70023, 0x76f70022, 0x76f70021, + 0x75f70029, 0x75f70028, 0x75f70027, 0x75f70026, + 0x75f70025, 0x75f70024, 0x75f70023, 0x74f70029, + 0x74f70028, 0x74f70026, 0x74f70025, 0x74f70024, + 0x74f70023, 0x74f70022, 0x73f70029, 0x73f70027, + 0x73f70026, 0x73f70025, 0x73f70024, 0x73f70023, + 0x73f70022, 0x72f70028, 0x72f70027, 0x72f70026, + 0x72f70025, 0x72f70024, 0x72f70023, 0x72f70022, + 0x71f70028, 0x71f70027, 0x71f70026, 0x71f70025, + 0x71f70024, 0x71f70023, 0x70f70028, 0x70f70027, + 0x70f70026, 0x70f70024, 0x70f70023, 0x70f70022, + 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, +}; + +const u16 tbl_iqcal_gainparams[2][9][8] = { + { + { 0x000, 0, 0, 2, 0x69, 0x69, 0x69, 0x69 }, + { 0x700, 7, 0, 0, 0x69, 0x69, 0x69, 0x69 }, + { 0x710, 7, 1, 0, 0x68, 0x68, 0x68, 0x68 }, + { 0x720, 7, 2, 0, 0x67, 0x67, 0x67, 0x67 }, + { 0x730, 7, 3, 0, 0x66, 0x66, 0x66, 0x66 }, + { 0x740, 7, 4, 0, 0x65, 0x65, 0x65, 0x65 }, + { 0x741, 7, 4, 1, 0x65, 0x65, 0x65, 0x65 }, + { 0x742, 7, 4, 2, 0x65, 0x65, 0x65, 0x65 }, + { 0x743, 7, 4, 3, 0x65, 0x65, 0x65, 0x65 } + }, + { + { 0x000, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x700, 7, 0, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x710, 7, 1, 0, 0x79, 0x79, 0x79, 0x79 }, + { 0x720, 7, 2, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x730, 7, 3, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x740, 7, 4, 0, 0x78, 0x78, 0x78, 0x78 }, + { 0x741, 7, 4, 1, 0x78, 0x78, 0x78, 0x78 }, + { 0x742, 7, 4, 2, 0x78, 0x78, 0x78, 0x78 }, + { 0x743, 7, 4, 3, 0x78, 0x78, 0x78, 0x78 } + } +}; + +const struct nphy_txiqcal_ladder ladder_lo[] = { + { 3, 0 }, + { 4, 0 }, + { 6, 0 }, + { 9, 0 }, + { 13, 0 }, + { 18, 0 }, + { 25, 0 }, + { 25, 1 }, + { 25, 2 }, + { 25, 3 }, + { 25, 4 }, + { 25, 5 }, + { 25, 6 }, + { 25, 7 }, + { 35, 7 }, + { 50, 7 }, + { 71, 7 }, + { 100, 7 } +}; + +const struct nphy_txiqcal_ladder ladder_iq[] = { + { 3, 0 }, + { 4, 0 }, + { 6, 0 }, + { 9, 0 }, + { 13, 0 }, + { 18, 0 }, + { 25, 0 }, + { 35, 0 }, + { 50, 0 }, + { 71, 0 }, + { 100, 0 }, + { 100, 1 }, + { 100, 2 }, + { 100, 3 }, + { 100, 4 }, + { 100, 5 }, + { 100, 6 }, + { 100, 7 } +}; + +const u16 loscale[] = { + 256, 256, 271, 271, + 287, 256, 256, 271, + 271, 287, 287, 304, + 304, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 341, + 341, 362, 362, 383, + 383, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 256, + 256, 271, 271, 287, + 287, 304, 304, 322, + 322, 341, 341, 362, + 362, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 256, + 256, 271, 271, 287, + 287, 304, 304, 322, + 322, 341, 341, 362, + 362, 256, 256, 271, + 271, 287, 287, 304, + 304, 322, 322, 341, + 341, 362, 362, 383, + 383, 406, 406, 430, + 430, 455, 455, 482, + 482, 511, 511, 541, + 541, 573, 573, 607, + 607, 643, 643, 681, + 681, 722, 722, 764, + 764, 810, 810, 858, + 858, 908, 908, 962, + 962, 1019, 1019, 256 +}; + +const u16 tbl_tx_iqlo_cal_loft_ladder_40[] = { + 0x0200, 0x0300, 0x0400, 0x0700, + 0x0900, 0x0c00, 0x1200, 0x1201, + 0x1202, 0x1203, 0x1204, 0x1205, + 0x1206, 0x1207, 0x1907, 0x2307, + 0x3207, 0x4707 +}; + +const u16 tbl_tx_iqlo_cal_loft_ladder_20[] = { + 0x0300, 0x0500, 0x0700, 0x0900, + 0x0d00, 0x1100, 0x1900, 0x1901, + 0x1902, 0x1903, 0x1904, 0x1905, + 0x1906, 0x1907, 0x2407, 0x3207, + 0x4607, 0x6407 +}; + +const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[] = { + 0x0100, 0x0200, 0x0400, 0x0700, + 0x0900, 0x0c00, 0x1200, 0x1900, + 0x2300, 0x3200, 0x4700, 0x4701, + 0x4702, 0x4703, 0x4704, 0x4705, + 0x4706, 0x4707 +}; + +const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[] = { + 0x0200, 0x0300, 0x0600, 0x0900, + 0x0d00, 0x1100, 0x1900, 0x2400, + 0x3200, 0x4600, 0x6400, 0x6401, + 0x6402, 0x6403, 0x6404, 0x6405, + 0x6406, 0x6407 +}; + +const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3] = { }; + +const u16 tbl_tx_iqlo_cal_startcoefs[B43_NTAB_TX_IQLO_CAL_STARTCOEFS] = { }; + +const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[] = { + 0x8423, 0x8323, 0x8073, 0x8256, + 0x8045, 0x8223, 0x9423, 0x9323, + 0x9073, 0x9256, 0x9045, 0x9223 +}; + +const u16 tbl_tx_iqlo_cal_cmds_recal[] = { + 0x8101, 0x8253, 0x8053, 0x8234, + 0x8034, 0x9101, 0x9253, 0x9053, + 0x9234, 0x9034 +}; + +const u16 tbl_tx_iqlo_cal_cmds_fullcal[] = { + 0x8123, 0x8264, 0x8086, 0x8245, + 0x8056, 0x9123, 0x9264, 0x9086, + 0x9245, 0x9056 +}; + +const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[] = { + 0x8434, 0x8334, 0x8084, 0x8267, + 0x8056, 0x8234, 0x9434, 0x9334, + 0x9084, 0x9267, 0x9056, 0x9234 +}; + static inline void assert_ntab_array_sizes(void) { #undef check @@ -2474,3 +2951,51 @@ void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value) /* Some compiletime assertions... */ assert_ntab_array_sizes(); } + +#define ntab_upload(dev, offset, data) do { \ + unsigned int i; \ + for (i = 0; i < (offset##_SIZE); i++) \ + b43_ntab_write(dev, (offset) + i, (data)[i]); \ + } while (0) + +void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev) +{ + /* Static tables */ + ntab_upload(dev, B43_NTAB_FRAMESTRUCT, b43_ntab_framestruct); + ntab_upload(dev, B43_NTAB_FRAMELT, b43_ntab_framelookup); + ntab_upload(dev, B43_NTAB_TMAP, b43_ntab_tmap); + ntab_upload(dev, B43_NTAB_TDTRN, b43_ntab_tdtrn); + ntab_upload(dev, B43_NTAB_INTLEVEL, b43_ntab_intlevel); + ntab_upload(dev, B43_NTAB_PILOT, b43_ntab_pilot); + ntab_upload(dev, B43_NTAB_PILOTLT, b43_ntab_pilotlt); + ntab_upload(dev, B43_NTAB_TDI20A0, b43_ntab_tdi20a0); + ntab_upload(dev, B43_NTAB_TDI20A1, b43_ntab_tdi20a1); + ntab_upload(dev, B43_NTAB_TDI40A0, b43_ntab_tdi40a0); + ntab_upload(dev, B43_NTAB_TDI40A1, b43_ntab_tdi40a1); + ntab_upload(dev, B43_NTAB_BDI, b43_ntab_bdi); + ntab_upload(dev, B43_NTAB_CHANEST, b43_ntab_channelest); + ntab_upload(dev, B43_NTAB_MCS, b43_ntab_mcs); + + /* Volatile tables */ + ntab_upload(dev, B43_NTAB_NOISEVAR10, b43_ntab_noisevar10); + ntab_upload(dev, B43_NTAB_NOISEVAR11, b43_ntab_noisevar11); + ntab_upload(dev, B43_NTAB_C0_ESTPLT, b43_ntab_estimatepowerlt0); + ntab_upload(dev, B43_NTAB_C1_ESTPLT, b43_ntab_estimatepowerlt1); + ntab_upload(dev, B43_NTAB_C0_ADJPLT, b43_ntab_adjustpower0); + ntab_upload(dev, B43_NTAB_C1_ADJPLT, b43_ntab_adjustpower1); + ntab_upload(dev, B43_NTAB_C0_GAINCTL, b43_ntab_gainctl0); + ntab_upload(dev, B43_NTAB_C1_GAINCTL, b43_ntab_gainctl1); + ntab_upload(dev, B43_NTAB_C0_IQLT, b43_ntab_iqlt0); + ntab_upload(dev, B43_NTAB_C1_IQLT, b43_ntab_iqlt1); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH, b43_ntab_loftlt0); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); +} + +void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev) +{ + /* Static tables */ + /* TODO */ + + /* Volatile tables */ + /* TODO */ +} diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 4d498b053ec..51636d02f8b 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -46,6 +46,11 @@ struct b43_nphy_channeltab_entry { struct b43_wldev; +struct nphy_txiqcal_ladder { + u8 percent; + u8 g_env; +}; + /* Upload the default register value table. * If "ghz5" is true, we upload the 5Ghz table. Otherwise the 2.4Ghz * table is uploaded. If "ignore_uploadflag" is true, we upload any value @@ -126,34 +131,46 @@ b43_nphy_get_chantabent(struct b43_wldev *dev, u8 channel); #define B43_NTAB_C1_LOFEEDTH B43_NTAB16(0x1B, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 1 */ #define B43_NTAB_C1_LOFEEDTH_SIZE 128 -void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value); +#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_20_SIZE 18 +#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS_REV3 11 +#define B43_NTAB_TX_IQLO_CAL_STARTCOEFS 9 +#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL_REV3 12 +#define B43_NTAB_TX_IQLO_CAL_CMDS_RECAL 10 +#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL 10 +#define B43_NTAB_TX_IQLO_CAL_CMDS_FULLCAL_REV3 12 -extern const u8 b43_ntab_adjustpower0[]; -extern const u8 b43_ntab_adjustpower1[]; -extern const u16 b43_ntab_bdi[]; -extern const u32 b43_ntab_channelest[]; -extern const u8 b43_ntab_estimatepowerlt0[]; -extern const u8 b43_ntab_estimatepowerlt1[]; -extern const u8 b43_ntab_framelookup[]; -extern const u32 b43_ntab_framestruct[]; -extern const u32 b43_ntab_gainctl0[]; -extern const u32 b43_ntab_gainctl1[]; -extern const u32 b43_ntab_intlevel[]; -extern const u32 b43_ntab_iqlt0[]; -extern const u32 b43_ntab_iqlt1[]; -extern const u16 b43_ntab_loftlt0[]; -extern const u16 b43_ntab_loftlt1[]; -extern const u8 b43_ntab_mcs[]; -extern const u32 b43_ntab_noisevar10[]; -extern const u32 b43_ntab_noisevar11[]; -extern const u16 b43_ntab_pilot[]; -extern const u32 b43_ntab_pilotlt[]; -extern const u32 b43_ntab_tdi20a0[]; -extern const u32 b43_ntab_tdi20a1[]; -extern const u32 b43_ntab_tdi40a0[]; -extern const u32 b43_ntab_tdi40a1[]; -extern const u32 b43_ntab_tdtrn[]; -extern const u32 b43_ntab_tmap[]; +void b43_ntab_write(struct b43_wldev *dev, u32 offset, u32 value); +void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev); +void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev); + +extern const u32 b43_ntab_tx_gain_rev0_1_2[]; +extern const u32 b43_ntab_tx_gain_rev3plus_2ghz[]; +extern const u32 b43_ntab_tx_gain_rev3_5ghz[]; +extern const u32 b43_ntab_tx_gain_rev4_5ghz[]; +extern const u32 b43_ntab_tx_gain_rev5plus_5ghz[]; + +extern const u32 txpwrctrl_tx_gain_ipa[]; +extern const u32 txpwrctrl_tx_gain_ipa_rev5[]; +extern const u32 txpwrctrl_tx_gain_ipa_rev6[]; +extern const u32 txpwrctrl_tx_gain_ipa_5g[]; +extern const u16 tbl_iqcal_gainparams[2][9][8]; +extern const struct nphy_txiqcal_ladder ladder_lo[]; +extern const struct nphy_txiqcal_ladder ladder_iq[]; +extern const u16 loscale[]; + +extern const u16 tbl_tx_iqlo_cal_loft_ladder_40[]; +extern const u16 tbl_tx_iqlo_cal_loft_ladder_20[]; +extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_40[]; +extern const u16 tbl_tx_iqlo_cal_iqimb_ladder_20[]; +extern const u16 tbl_tx_iqlo_cal_startcoefs_nphyrev3[]; +extern const u16 tbl_tx_iqlo_cal_startcoefs[]; +extern const u16 tbl_tx_iqlo_cal_cmds_recal_nphyrev3[]; +extern const u16 tbl_tx_iqlo_cal_cmds_recal[]; +extern const u16 tbl_tx_iqlo_cal_cmds_fullcal[]; +extern const u16 tbl_tx_iqlo_cal_cmds_fullcal_nphyrev3[]; #endif /* B43_TABLES_NPHY_H_ */ diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index fbae264095c..874a64a6c61 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -61,6 +61,8 @@ MODULE_AUTHOR("Michael Buesch"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(B43legacy_SUPPORTED_FIRMWARE_ID); +MODULE_FIRMWARE("b43legacy/ucode2.fw"); +MODULE_FIRMWARE("b43legacy/ucode4.fw"); #if defined(CONFIG_B43LEGACY_DMA) && defined(CONFIG_B43LEGACY_PIO) static int modparam_pio; @@ -3960,7 +3962,7 @@ static struct ssb_driver b43legacy_ssb_driver = { static void b43legacy_print_driverinfo(void) { - const char *feat_pci = "", *feat_leds = "", *feat_rfkill = "", + const char *feat_pci = "", *feat_leds = "", *feat_pio = "", *feat_dma = ""; #ifdef CONFIG_B43LEGACY_PCI_AUTOSELECT @@ -3969,9 +3971,6 @@ static void b43legacy_print_driverinfo(void) #ifdef CONFIG_B43LEGACY_LEDS feat_leds = "L"; #endif -#ifdef CONFIG_B43LEGACY_RFKILL - feat_rfkill = "R"; -#endif #ifdef CONFIG_B43LEGACY_PIO feat_pio = "I"; #endif @@ -3979,9 +3978,9 @@ static void b43legacy_print_driverinfo(void) feat_dma = "D"; #endif printk(KERN_INFO "Broadcom 43xx-legacy driver loaded " - "[ Features: %s%s%s%s%s, Firmware-ID: " + "[ Features: %s%s%s%s, Firmware-ID: " B43legacy_SUPPORTED_FIRMWARE_ID " ]\n", - feat_pci, feat_leds, feat_rfkill, feat_pio, feat_dma); + feat_pci, feat_leds, feat_pio, feat_dma); } static int __init b43legacy_init(void) diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlwifi/iwl-4965.c index 78706ce8b7a..cee368d4859 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlwifi/iwl-4965.c @@ -1961,7 +1961,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, struct ieee80211_tx_info *info; struct iwl4965_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; u32 status = le32_to_cpu(tx_resp->u.status); - int tid = MAX_TID_COUNT; + int tid = MAX_TID_COUNT - 1; int sta_id; int freed; u8 *qc = NULL; diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index ec6b27689fa..c3f8ec0a38b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -781,7 +781,7 @@ void iwl5000_txq_update_byte_cnt_tbl(struct iwl_priv *priv, scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; - if (txq->q.write_ptr < TFD_QUEUE_SIZE_BC_DUP) + if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id]. tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } @@ -800,12 +800,12 @@ void iwl5000_txq_inval_byte_cnt_tbl(struct iwl_priv *priv, if (txq_id != IWL_CMD_QUEUE_NUM) sta_id = txq->cmd[read_ptr]->cmd.tx.sta_id; - bc_ent = cpu_to_le16(1 | (sta_id << 12)); + bc_ent = cpu_to_le16(1 | (sta_id << 12)); scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; - if (txq->q.write_ptr < TFD_QUEUE_SIZE_BC_DUP) + if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; + tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; } static int iwl5000_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index c8fec626b71..344e99de4ca 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -2955,6 +2955,9 @@ static int iwl_mac_ampdu_action(struct ieee80211_hw *hw, return 0; else return ret; + case IEEE80211_AMPDU_TX_OPERATIONAL: + /* do nothing */ + return -EOPNOTSUPP; default: IWL_DEBUG_HT(priv, "unknown\n"); return -EINVAL; diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 28f3800c560..3320cce3d57 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -120,7 +120,6 @@ enum { CALIBRATION_COMPLETE_NOTIFICATION = 0x67, /* 802.11h related */ - RADAR_NOTIFICATION = 0x70, /* not used */ REPLY_QUIET_CMD = 0x71, /* not used */ REPLY_CHANNEL_SWITCH = 0x72, CHANNEL_SWITCH_NOTIFICATION = 0x73, @@ -2984,7 +2983,7 @@ struct statistics_rx_ht_phy { __le32 agg_crc32_good; __le32 agg_mpdu_cnt; __le32 agg_cnt; - __le32 reserved2; + __le32 unsupport_mcs; } __attribute__ ((packed)); #define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) @@ -3087,8 +3086,8 @@ struct statistics_div { } __attribute__ ((packed)); struct statistics_general { - __le32 temperature; - __le32 temperature_m; + __le32 temperature; /* radio temperature */ + __le32 temperature_m; /* for 5000 and up, this is radio voltage */ struct statistics_dbg dbg; __le32 sleep_time; __le32 slots_out; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 3ef86f6c775..8deb83bfe18 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -63,8 +63,6 @@ #ifndef __iwl_core_h__ #define __iwl_core_h__ -#include <generated/utsrelease.h> - /************************ * forward declarations * ************************/ @@ -72,7 +70,7 @@ struct iwl_host_cmd; struct iwl_cmd; -#define IWLWIFI_VERSION UTS_RELEASE "-k" +#define IWLWIFI_VERSION "in-tree:" #define DRV_COPYRIGHT "Copyright(c) 2003-2009 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index ee5aed12a4b..4a2ac9311ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -125,7 +125,7 @@ static ssize_t iwl_dbgfs_tx_statistics_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char *buf; int pos = 0; @@ -184,7 +184,7 @@ static ssize_t iwl_dbgfs_rx_statistics_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char *buf; int pos = 0; int cnt; @@ -232,7 +232,7 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, ssize_t ret; int i; int pos = 0; - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; size_t bufsz; /* default is to dump the entire data segment */ @@ -306,7 +306,7 @@ static ssize_t iwl_dbgfs_sram_write(struct file *file, static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_station_entry *station; int max_sta = priv->hw_params.max_stations; char *buf; @@ -376,7 +376,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, loff_t *ppos) { ssize_t ret; - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0, ofs = 0, buf_size = 0; const u8 *ptr; char *buf; @@ -464,7 +464,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, static ssize_t iwl_dbgfs_channels_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct ieee80211_channel *channels = NULL; const struct ieee80211_supported_band *supp_band = NULL; int pos = 0, i, bufsz = PAGE_SIZE; @@ -537,7 +537,7 @@ static ssize_t iwl_dbgfs_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[512]; int pos = 0; const size_t bufsz = sizeof(buf); @@ -585,7 +585,7 @@ static ssize_t iwl_dbgfs_interrupt_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; @@ -672,7 +672,7 @@ static ssize_t iwl_dbgfs_interrupt_write(struct file *file, static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0, i; char buf[256]; const size_t bufsz = sizeof(buf); @@ -695,7 +695,7 @@ static ssize_t iwl_dbgfs_qos_read(struct file *file, char __user *user_buf, static ssize_t iwl_dbgfs_led_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char buf[256]; const size_t bufsz = sizeof(buf); @@ -721,7 +721,7 @@ static ssize_t iwl_dbgfs_thermal_throttling_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_tt_mgmt *tt = &priv->thermal_throttle; struct iwl_tt_restriction *restriction; char buf[100]; @@ -781,7 +781,7 @@ static ssize_t iwl_dbgfs_disable_ht40_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[100]; int pos = 0; const size_t bufsz = sizeof(buf); @@ -838,7 +838,7 @@ static ssize_t iwl_dbgfs_sleep_level_override_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[10]; int pos, value; const size_t bufsz = sizeof(buf); @@ -856,7 +856,7 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[200]; int pos = 0, i; const size_t bufsz = sizeof(buf); @@ -994,7 +994,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_tx_queue *txq; struct iwl_queue *q; char *buf; @@ -1040,7 +1040,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; struct iwl_rx_queue *rxq = &priv->rxq; char buf[256]; int pos = 0; @@ -1086,7 +1086,7 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct statistics_rx_phy) * 20 + @@ -1387,6 +1387,9 @@ static ssize_t iwl_dbgfs_ucode_rx_stats_read(struct file *file, accum_ht->agg_mpdu_cnt); pos += scnprintf(buf + pos, bufsz - pos, "agg_cnt:\t\t%u\t\t\t%u\n", le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "unsupport_mcs:\t\t%u\t\t\t%u\n", + le32_to_cpu(ht->unsupport_mcs), + accum_ht->unsupport_mcs); ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -1397,7 +1400,7 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; int bufsz = (sizeof(struct statistics_tx) * 24) + 250; @@ -1539,7 +1542,7 @@ static ssize_t iwl_dbgfs_ucode_general_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; char *buf; int bufsz = sizeof(struct statistics_general) * 4 + 250; @@ -1630,7 +1633,7 @@ static ssize_t iwl_dbgfs_sensitivity_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; @@ -1711,7 +1714,7 @@ static ssize_t iwl_dbgfs_chain_noise_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; int pos = 0; int cnt = 0; char *buf; @@ -1769,7 +1772,7 @@ static ssize_t iwl_dbgfs_tx_power_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[128]; int pos = 0; ssize_t ret; @@ -1820,7 +1823,7 @@ static ssize_t iwl_dbgfs_power_save_status_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct iwl_priv *priv = (struct iwl_priv *)file->private_data; + struct iwl_priv *priv = file->private_data; char buf[60]; int pos = 0; const size_t bufsz = sizeof(buf); diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index 30e9ea6d54e..87d684efe11 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -58,7 +58,6 @@ const char *get_cmd_string(u8 cmd) IWL_CMD(COEX_PRIORITY_TABLE_CMD); IWL_CMD(COEX_MEDIUM_NOTIFICATION); IWL_CMD(COEX_EVENT_CMD); - IWL_CMD(RADAR_NOTIFICATION); IWL_CMD(REPLY_QUIET_CMD); IWL_CMD(REPLY_CHANNEL_SWITCH); IWL_CMD(CHANNEL_SWITCH_NOTIFICATION); diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 842811142be..79ffa3b98d7 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -268,7 +268,7 @@ struct iwm_priv { struct sk_buff_head rx_list; struct list_head rx_tickets; - struct list_head rx_packets[IWM_RX_ID_HASH + 1]; + struct list_head rx_packets[IWM_RX_ID_HASH]; struct workqueue_struct *rx_wq; struct work_struct rx_worker; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index c1c6ecd0c5b..68546ca0ba3 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -2,7 +2,7 @@ * drivers/net/wireless/mwl8k.c * Driver for Marvell TOPDOG 802.11 Wireless cards * - * Copyright (C) 2008-2009 Marvell Semiconductor Inc. + * Copyright (C) 2008, 2009, 2010 Marvell Semiconductor Inc. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any @@ -26,7 +26,7 @@ #define MWL8K_DESC "Marvell TOPDOG(R) 802.11 Wireless Network Driver" #define MWL8K_NAME KBUILD_MODNAME -#define MWL8K_VERSION "0.10" +#define MWL8K_VERSION "0.12" /* Register definitions */ #define MWL8K_HIU_GEN_PTR 0x00000c10 @@ -141,6 +141,14 @@ struct mwl8k_priv { /* hardware/firmware parameters */ bool ap_fw; struct rxd_ops *rxd_ops; + struct ieee80211_supported_band band_24; + struct ieee80211_channel channels_24[14]; + struct ieee80211_rate rates_24[14]; + struct ieee80211_supported_band band_50; + struct ieee80211_channel channels_50[4]; + struct ieee80211_rate rates_50[9]; + u32 ap_macids_supported; + u32 sta_macids_supported; /* firmware access */ struct mutex fw_mutex; @@ -154,9 +162,9 @@ struct mwl8k_priv { /* TX quiesce completion, protected by fw_mutex and tx_lock */ struct completion *tx_wait; - struct ieee80211_vif *vif; - - struct ieee80211_channel *current_channel; + /* List of interfaces. */ + u32 macids_used; + struct list_head vif_list; /* power management status cookie from firmware */ u32 *cookie; @@ -175,16 +183,15 @@ struct mwl8k_priv { struct mwl8k_rx_queue rxq[MWL8K_RX_QUEUES]; struct mwl8k_tx_queue txq[MWL8K_TX_QUEUES]; - /* PHY parameters */ - struct ieee80211_supported_band band; - struct ieee80211_channel channels[14]; - struct ieee80211_rate rates[14]; - bool radio_on; bool radio_short_preamble; bool sniffer_enabled; bool wmm_enabled; + struct work_struct sta_notify_worker; + spinlock_t sta_notify_list_lock; + struct list_head sta_notify_list; + /* XXX need to convert this to handle multiple interfaces */ bool capture_beacon; u8 capture_bssid[ETH_ALEN]; @@ -198,28 +205,33 @@ struct mwl8k_priv { */ struct work_struct finalize_join_worker; - /* Tasklet to reclaim TX descriptors and buffers after tx */ - struct tasklet_struct tx_reclaim_task; + /* Tasklet to perform TX reclaim. */ + struct tasklet_struct poll_tx_task; + + /* Tasklet to perform RX. */ + struct tasklet_struct poll_rx_task; }; /* Per interface specific private data */ struct mwl8k_vif { - /* Local MAC address. */ - u8 mac_addr[ETH_ALEN]; - - /* BSSID of AP. */ - u8 bssid[ETH_ALEN]; + struct list_head list; + struct ieee80211_vif *vif; - /* Index into station database. Returned by UPDATE_STADB. */ - u8 peer_id; + /* Firmware macid for this vif. */ + int macid; - /* Non AMPDU sequence number assigned by driver */ - u16 seqno; + /* Non AMPDU sequence number assigned by driver. */ + u16 seqno; }; - #define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) -static const struct ieee80211_channel mwl8k_channels[] = { +struct mwl8k_sta { + /* Index into station database. Returned by UPDATE_STADB. */ + u8 peer_id; +}; +#define MWL8K_STA(_sta) ((struct mwl8k_sta *)&((_sta)->drv_priv)) + +static const struct ieee80211_channel mwl8k_channels_24[] = { { .center_freq = 2412, .hw_value = 1, }, { .center_freq = 2417, .hw_value = 2, }, { .center_freq = 2422, .hw_value = 3, }, @@ -236,7 +248,7 @@ static const struct ieee80211_channel mwl8k_channels[] = { { .center_freq = 2484, .hw_value = 14, }, }; -static const struct ieee80211_rate mwl8k_rates[] = { +static const struct ieee80211_rate mwl8k_rates_24[] = { { .bitrate = 10, .hw_value = 2, }, { .bitrate = 20, .hw_value = 4, }, { .bitrate = 55, .hw_value = 11, }, @@ -253,8 +265,23 @@ static const struct ieee80211_rate mwl8k_rates[] = { { .bitrate = 720, .hw_value = 144, }, }; -static const u8 mwl8k_rateids[12] = { - 2, 4, 11, 22, 12, 18, 24, 36, 48, 72, 96, 108, +static const struct ieee80211_channel mwl8k_channels_50[] = { + { .center_freq = 5180, .hw_value = 36, }, + { .center_freq = 5200, .hw_value = 40, }, + { .center_freq = 5220, .hw_value = 44, }, + { .center_freq = 5240, .hw_value = 48, }, +}; + +static const struct ieee80211_rate mwl8k_rates_50[] = { + { .bitrate = 60, .hw_value = 12, }, + { .bitrate = 90, .hw_value = 18, }, + { .bitrate = 120, .hw_value = 24, }, + { .bitrate = 180, .hw_value = 36, }, + { .bitrate = 240, .hw_value = 48, }, + { .bitrate = 360, .hw_value = 72, }, + { .bitrate = 480, .hw_value = 96, }, + { .bitrate = 540, .hw_value = 108, }, + { .bitrate = 720, .hw_value = 144, }, }; /* Set or get info from Firmware */ @@ -270,6 +297,7 @@ static const u8 mwl8k_rateids[12] = { #define MWL8K_CMD_RADIO_CONTROL 0x001c #define MWL8K_CMD_RF_TX_POWER 0x001e #define MWL8K_CMD_RF_ANTENNA 0x0020 +#define MWL8K_CMD_SET_BEACON 0x0100 /* per-vif */ #define MWL8K_CMD_SET_PRE_SCAN 0x0107 #define MWL8K_CMD_SET_POST_SCAN 0x0108 #define MWL8K_CMD_SET_RF_CHANNEL 0x010a @@ -283,8 +311,10 @@ static const u8 mwl8k_rateids[12] = { #define MWL8K_CMD_MIMO_CONFIG 0x0125 #define MWL8K_CMD_USE_FIXED_RATE 0x0126 #define MWL8K_CMD_ENABLE_SNIFFER 0x0150 -#define MWL8K_CMD_SET_MAC_ADDR 0x0202 +#define MWL8K_CMD_SET_MAC_ADDR 0x0202 /* per-vif */ #define MWL8K_CMD_SET_RATEADAPT_MODE 0x0203 +#define MWL8K_CMD_BSS_START 0x1100 /* per-vif */ +#define MWL8K_CMD_SET_NEW_STN 0x1111 /* per-vif */ #define MWL8K_CMD_UPDATE_STADB 0x1123 static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) @@ -302,6 +332,7 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(RADIO_CONTROL); MWL8K_CMDNAME(RF_TX_POWER); MWL8K_CMDNAME(RF_ANTENNA); + MWL8K_CMDNAME(SET_BEACON); MWL8K_CMDNAME(SET_PRE_SCAN); MWL8K_CMDNAME(SET_POST_SCAN); MWL8K_CMDNAME(SET_RF_CHANNEL); @@ -317,6 +348,8 @@ static const char *mwl8k_cmd_name(u16 cmd, char *buf, int bufsize) MWL8K_CMDNAME(ENABLE_SNIFFER); MWL8K_CMDNAME(SET_MAC_ADDR); MWL8K_CMDNAME(SET_RATEADAPT_MODE); + MWL8K_CMDNAME(BSS_START); + MWL8K_CMDNAME(SET_NEW_STN); MWL8K_CMDNAME(UPDATE_STADB); default: snprintf(buf, bufsize, "0x%x", cmd); @@ -389,13 +422,11 @@ static int mwl8k_request_firmware(struct mwl8k_priv *priv) return 0; } -MODULE_FIRMWARE("mwl8k/helper_8687.fw"); -MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); - struct mwl8k_cmd_pkt { __le16 code; __le16 length; - __le16 seq_num; + __u8 seq_num; + __u8 macid; __le16 result; char payload[0]; } __attribute__((packed)); @@ -453,6 +484,7 @@ static int mwl8k_load_fw_image(struct mwl8k_priv *priv, cmd->code = cpu_to_le16(MWL8K_CMD_CODE_DNLD); cmd->seq_num = 0; + cmd->macid = 0; cmd->result = 0; done = 0; @@ -598,54 +630,6 @@ static int mwl8k_load_firmware(struct ieee80211_hw *hw) } -/* - * Defines shared between transmission and reception. - */ -/* HT control fields for firmware */ -struct ewc_ht_info { - __le16 control1; - __le16 control2; - __le16 control3; -} __attribute__((packed)); - -/* Firmware Station database operations */ -#define MWL8K_STA_DB_ADD_ENTRY 0 -#define MWL8K_STA_DB_MODIFY_ENTRY 1 -#define MWL8K_STA_DB_DEL_ENTRY 2 -#define MWL8K_STA_DB_FLUSH 3 - -/* Peer Entry flags - used to define the type of the peer node */ -#define MWL8K_PEER_TYPE_ACCESSPOINT 2 - -struct peer_capability_info { - /* Peer type - AP vs. STA. */ - __u8 peer_type; - - /* Basic 802.11 capabilities from assoc resp. */ - __le16 basic_caps; - - /* Set if peer supports 802.11n high throughput (HT). */ - __u8 ht_support; - - /* Valid if HT is supported. */ - __le16 ht_caps; - __u8 extended_ht_caps; - struct ewc_ht_info ewc_info; - - /* Legacy rate table. Intersection of our rates and peer rates. */ - __u8 legacy_rates[12]; - - /* HT rate table. Intersection of our rates and peer rates. */ - __u8 ht_rates[16]; - __u8 pad[16]; - - /* If set, interoperability mode, no proprietary extensions. */ - __u8 interop; - __u8 pad2; - __u8 station_id; - __le16 amsdu_enabled; -} __attribute__((packed)); - /* DMA header used by firmware and hardware. */ struct mwl8k_dma_data { __le16 fwlen; @@ -779,15 +763,21 @@ mwl8k_rxd_8366_ap_process(void *_rxd, struct ieee80211_rx_status *status, } else { int i; - for (i = 0; i < ARRAY_SIZE(mwl8k_rates); i++) { - if (mwl8k_rates[i].hw_value == rxd->rate) { + for (i = 0; i < ARRAY_SIZE(mwl8k_rates_24); i++) { + if (mwl8k_rates_24[i].hw_value == rxd->rate) { status->rate_idx = i; break; } } } - status->band = IEEE80211_BAND_2GHZ; + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } status->freq = ieee80211_channel_to_frequency(rxd->channel); *qos = rxd->qos_control; @@ -878,7 +868,13 @@ mwl8k_rxd_sta_process(void *_rxd, struct ieee80211_rx_status *status, if (rate_info & MWL8K_STA_RATE_INFO_MCS_FORMAT) status->flag |= RX_FLAG_HT; - status->band = IEEE80211_BAND_2GHZ; + if (rxd->channel > 14) { + status->band = IEEE80211_BAND_5GHZ; + if (!(status->flag & RX_FLAG_HT)) + status->rate_idx -= 5; + } else { + status->band = IEEE80211_BAND_2GHZ; + } status->freq = ieee80211_channel_to_frequency(rxd->channel); *qos = rxd->qos_control; @@ -1225,7 +1221,7 @@ static void mwl8k_dump_tx_rings(struct ieee80211_hw *hw) /* * Must be called with priv->fw_mutex held and tx queues stopped. */ -#define MWL8K_TX_WAIT_TIMEOUT_MS 1000 +#define MWL8K_TX_WAIT_TIMEOUT_MS 5000 static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) { @@ -1269,8 +1265,8 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) } if (priv->pending_tx_pkts < oldcount) { - printk(KERN_NOTICE "%s: timeout waiting for tx " - "rings to drain (%d -> %d pkts), retrying\n", + printk(KERN_NOTICE "%s: waiting for tx rings " + "to drain (%d -> %d pkts)\n", wiphy_name(hw->wiphy), oldcount, priv->pending_tx_pkts); retry = 1; @@ -1295,13 +1291,15 @@ static int mwl8k_tx_wait_empty(struct ieee80211_hw *hw) MWL8K_TXD_STATUS_OK_RETRY | \ MWL8K_TXD_STATUS_OK_MORE_RETRY)) -static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) +static int +mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - int wake = 0; + int processed; - while (txq->stats.len > 0) { + processed = 0; + while (txq->stats.len > 0 && limit--) { int tx; struct mwl8k_tx_desc *tx_desc; unsigned long addr; @@ -1348,11 +1346,13 @@ static void mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int force) ieee80211_tx_status_irqsafe(hw, skb); - wake = 1; + processed++; } - if (wake && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) + if (processed && priv->radio_on && !mutex_is_locked(&priv->fw_mutex)) ieee80211_wake_queue(hw, index); + + return processed; } /* must be called only when the card's transmit is completely halted */ @@ -1361,7 +1361,7 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) struct mwl8k_priv *priv = hw->priv; struct mwl8k_tx_queue *txq = priv->txq + index; - mwl8k_txq_reclaim(hw, index, 1); + mwl8k_txq_reclaim(hw, index, INT_MAX, 1); kfree(txq->skb); txq->skb = NULL; @@ -1399,11 +1399,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) mwl8k_vif = MWL8K_VIF(tx_info->control.vif); if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { - u16 seqno = mwl8k_vif->seqno; - wh->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - wh->seq_ctrl |= cpu_to_le16(seqno << 4); - mwl8k_vif->seqno = seqno++ % 4096; + wh->seq_ctrl |= cpu_to_le16(mwl8k_vif->seqno); + mwl8k_vif->seqno += 0x10; } /* Setup firmware control bit fields for each frame type. */ @@ -1449,7 +1447,10 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) tx->pkt_phys_addr = cpu_to_le32(dma); tx->pkt_len = cpu_to_le16(skb->len); tx->rate_info = 0; - tx->peer_id = mwl8k_vif->peer_id; + if (!priv->ap_fw && tx_info->control.sta != NULL) + tx->peer_id = MWL8K_STA(tx_info->control.sta)->peer_id; + else + tx->peer_id = 0; wmb(); tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); @@ -1602,6 +1603,56 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) return rc; } +static int mwl8k_post_pervif_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct mwl8k_cmd_pkt *cmd) +{ + if (vif != NULL) + cmd->macid = MWL8K_VIF(vif)->macid; + return mwl8k_post_cmd(hw, cmd); +} + +/* + * Setup code shared between STA and AP firmware images. + */ +static void mwl8k_setup_2ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_24) != sizeof(mwl8k_channels_24)); + memcpy(priv->channels_24, mwl8k_channels_24, sizeof(mwl8k_channels_24)); + + BUILD_BUG_ON(sizeof(priv->rates_24) != sizeof(mwl8k_rates_24)); + memcpy(priv->rates_24, mwl8k_rates_24, sizeof(mwl8k_rates_24)); + + priv->band_24.band = IEEE80211_BAND_2GHZ; + priv->band_24.channels = priv->channels_24; + priv->band_24.n_channels = ARRAY_SIZE(mwl8k_channels_24); + priv->band_24.bitrates = priv->rates_24; + priv->band_24.n_bitrates = ARRAY_SIZE(mwl8k_rates_24); + + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band_24; +} + +static void mwl8k_setup_5ghz_band(struct ieee80211_hw *hw) +{ + struct mwl8k_priv *priv = hw->priv; + + BUILD_BUG_ON(sizeof(priv->channels_50) != sizeof(mwl8k_channels_50)); + memcpy(priv->channels_50, mwl8k_channels_50, sizeof(mwl8k_channels_50)); + + BUILD_BUG_ON(sizeof(priv->rates_50) != sizeof(mwl8k_rates_50)); + memcpy(priv->rates_50, mwl8k_rates_50, sizeof(mwl8k_rates_50)); + + priv->band_50.band = IEEE80211_BAND_5GHZ; + priv->band_50.channels = priv->channels_50; + priv->band_50.n_channels = ARRAY_SIZE(mwl8k_channels_50); + priv->band_50.bitrates = priv->rates_50; + priv->band_50.n_bitrates = ARRAY_SIZE(mwl8k_rates_50); + + hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->band_50; +} + /* * CMD_GET_HW_SPEC (STA version). */ @@ -1624,6 +1675,89 @@ struct mwl8k_cmd_get_hw_spec_sta { __le32 total_rxd; } __attribute__((packed)); +#define MWL8K_CAP_MAX_AMSDU 0x20000000 +#define MWL8K_CAP_GREENFIELD 0x08000000 +#define MWL8K_CAP_AMPDU 0x04000000 +#define MWL8K_CAP_RX_STBC 0x01000000 +#define MWL8K_CAP_TX_STBC 0x00800000 +#define MWL8K_CAP_SHORTGI_40MHZ 0x00400000 +#define MWL8K_CAP_SHORTGI_20MHZ 0x00200000 +#define MWL8K_CAP_RX_ANTENNA_MASK 0x000e0000 +#define MWL8K_CAP_TX_ANTENNA_MASK 0x0001c000 +#define MWL8K_CAP_DELAY_BA 0x00003000 +#define MWL8K_CAP_MIMO 0x00000200 +#define MWL8K_CAP_40MHZ 0x00000100 +#define MWL8K_CAP_BAND_MASK 0x00000007 +#define MWL8K_CAP_5GHZ 0x00000004 +#define MWL8K_CAP_2GHZ4 0x00000001 + +static void +mwl8k_set_ht_caps(struct ieee80211_hw *hw, + struct ieee80211_supported_band *band, u32 cap) +{ + int rx_streams; + int tx_streams; + + band->ht_cap.ht_supported = 1; + + if (cap & MWL8K_CAP_MAX_AMSDU) + band->ht_cap.cap |= IEEE80211_HT_CAP_MAX_AMSDU; + if (cap & MWL8K_CAP_GREENFIELD) + band->ht_cap.cap |= IEEE80211_HT_CAP_GRN_FLD; + if (cap & MWL8K_CAP_AMPDU) { + hw->flags |= IEEE80211_HW_AMPDU_AGGREGATION; + band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + } + if (cap & MWL8K_CAP_RX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_RX_STBC; + if (cap & MWL8K_CAP_TX_STBC) + band->ht_cap.cap |= IEEE80211_HT_CAP_TX_STBC; + if (cap & MWL8K_CAP_SHORTGI_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; + if (cap & MWL8K_CAP_SHORTGI_20MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; + if (cap & MWL8K_CAP_DELAY_BA) + band->ht_cap.cap |= IEEE80211_HT_CAP_DELAY_BA; + if (cap & MWL8K_CAP_40MHZ) + band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + rx_streams = hweight32(cap & MWL8K_CAP_RX_ANTENNA_MASK); + tx_streams = hweight32(cap & MWL8K_CAP_TX_ANTENNA_MASK); + + band->ht_cap.mcs.rx_mask[0] = 0xff; + if (rx_streams >= 2) + band->ht_cap.mcs.rx_mask[1] = 0xff; + if (rx_streams >= 3) + band->ht_cap.mcs.rx_mask[2] = 0xff; + band->ht_cap.mcs.rx_mask[4] = 0x01; + band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + if (rx_streams != tx_streams) { + band->ht_cap.mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + band->ht_cap.mcs.tx_params |= (tx_streams - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + } +} + +static void +mwl8k_set_caps(struct ieee80211_hw *hw, u32 caps) +{ + struct mwl8k_priv *priv = hw->priv; + + if ((caps & MWL8K_CAP_2GHZ4) || !(caps & MWL8K_CAP_BAND_MASK)) { + mwl8k_setup_2ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_24, caps); + } + + if (caps & MWL8K_CAP_5GHZ) { + mwl8k_setup_5ghz_band(hw); + if (caps & MWL8K_CAP_MIMO) + mwl8k_set_ht_caps(hw, &priv->band_50, caps); + } +} + static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) { struct mwl8k_priv *priv = hw->priv; @@ -1654,6 +1788,9 @@ static int mwl8k_cmd_get_hw_spec_sta(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + mwl8k_set_caps(hw, le32_to_cpu(cmd->caps)); + priv->ap_macids_supported = 0x00000000; + priv->sta_macids_supported = 0x00000001; } kfree(cmd); @@ -1707,6 +1844,9 @@ static int mwl8k_cmd_get_hw_spec_ap(struct ieee80211_hw *hw) priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; + mwl8k_setup_2ghz_band(hw); + priv->ap_macids_supported = 0x000000ff; + priv->sta_macids_supported = 0x00000000; off = le32_to_cpu(cmd->wcbbase0) & 0xffff; iowrite32(cpu_to_le32(priv->txq[0].txd_dma), priv->sram + off); @@ -1752,7 +1892,9 @@ struct mwl8k_cmd_set_hw_spec { __le32 total_rxd; } __attribute__((packed)); -#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT 0x00000080 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP 0x00000020 +#define MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON 0x00000010 static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) { @@ -1773,7 +1915,9 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); for (i = 0; i < MWL8K_TX_QUEUES; i++) cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); - cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT); + cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | + MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON); cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); cmd->total_rxd = cpu_to_le32(MWL8K_RX_DESCS); @@ -2011,6 +2155,36 @@ mwl8k_cmd_rf_antenna(struct ieee80211_hw *hw, int antenna, int mask) } /* + * CMD_SET_BEACON. + */ +struct mwl8k_cmd_set_beacon { + struct mwl8k_cmd_pkt header; + __le16 beacon_len; + __u8 beacon[0]; +}; + +static int mwl8k_cmd_set_beacon(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *beacon, int len) +{ + struct mwl8k_cmd_set_beacon *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd) + len, GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_BEACON); + cmd->header.length = cpu_to_le16(sizeof(*cmd) + len); + cmd->beacon_len = cpu_to_le16(len); + memcpy(cmd->beacon, beacon, len); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* * CMD_SET_PRE_SCAN. */ struct mwl8k_cmd_set_pre_scan { @@ -2045,7 +2219,7 @@ struct mwl8k_cmd_set_post_scan { } __attribute__((packed)); static int -mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, __u8 *mac) +mwl8k_cmd_set_post_scan(struct ieee80211_hw *hw, const __u8 *mac) { struct mwl8k_cmd_set_post_scan *cmd; int rc; @@ -2076,8 +2250,9 @@ struct mwl8k_cmd_set_rf_channel { } __attribute__((packed)); static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, - struct ieee80211_channel *channel) + struct ieee80211_conf *conf) { + struct ieee80211_channel *channel = conf->channel; struct mwl8k_cmd_set_rf_channel *cmd; int rc; @@ -2089,10 +2264,19 @@ static int mwl8k_cmd_set_rf_channel(struct ieee80211_hw *hw, cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_CMD_SET); cmd->current_channel = channel->hw_value; + if (channel->band == IEEE80211_BAND_2GHZ) - cmd->channel_flags = cpu_to_le32(0x00000081); - else - cmd->channel_flags = cpu_to_le32(0x00000000); + cmd->channel_flags |= cpu_to_le32(0x00000001); + else if (channel->band == IEEE80211_BAND_5GHZ) + cmd->channel_flags |= cpu_to_le32(0x00000004); + + if (conf->channel_type == NL80211_CHAN_NO_HT || + conf->channel_type == NL80211_CHAN_HT20) + cmd->channel_flags |= cpu_to_le32(0x00000080); + else if (conf->channel_type == NL80211_CHAN_HT40MINUS) + cmd->channel_flags |= cpu_to_le32(0x000001900); + else if (conf->channel_type == NL80211_CHAN_HT40PLUS) + cmd->channel_flags |= cpu_to_le32(0x000000900); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2118,10 +2302,26 @@ struct mwl8k_cmd_update_set_aid { __u8 supp_rates[14]; } __attribute__((packed)); +static void legacy_rate_mask_to_array(u8 *rates, u32 mask) +{ + int i; + int j; + + /* + * Clear nonstandard rates 4 and 13. + */ + mask &= 0x1fef; + + for (i = 0, j = 0; i < 14; i++) { + if (mask & (1 << i)) + rates[j++] = mwl8k_rates_24[i].hw_value; + } +} + static int -mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +mwl8k_cmd_set_aid(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u32 legacy_rate_mask) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_set_aid *cmd; u16 prot_mode; int rc; @@ -2133,8 +2333,7 @@ mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_AID); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->aid = cpu_to_le16(vif->bss_conf.aid); - - memcpy(cmd->bssid, mv_vif->bssid, ETH_ALEN); + memcpy(cmd->bssid, vif->bss_conf.bssid, ETH_ALEN); if (vif->bss_conf.use_cts_prot) { prot_mode = MWL8K_FRAME_PROT_11G; @@ -2154,7 +2353,7 @@ mwl8k_cmd_set_aid(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } cmd->protection_mode = cpu_to_le16(prot_mode); - memcpy(cmd->supp_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + legacy_rate_mask_to_array(cmd->supp_rates, legacy_rate_mask); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2175,7 +2374,8 @@ struct mwl8k_cmd_set_rate { } __attribute__((packed)); static int -mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u32 legacy_rate_mask, u8 *mcs_rates) { struct mwl8k_cmd_set_rate *cmd; int rc; @@ -2186,7 +2386,8 @@ mwl8k_cmd_set_rate(struct ieee80211_hw *hw, struct ieee80211_vif *vif) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - memcpy(cmd->legacy_rates, mwl8k_rateids, sizeof(mwl8k_rateids)); + legacy_rate_mask_to_array(cmd->legacy_rates, legacy_rate_mask); + memcpy(cmd->mcs_set, mcs_rates, 16); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2244,8 +2445,8 @@ struct mwl8k_cmd_set_rts_threshold { __le16 threshold; } __attribute__((packed)); -static int mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, - u16 action, u16 threshold) +static int +mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, int rts_thresh) { struct mwl8k_cmd_set_rts_threshold *cmd; int rc; @@ -2256,8 +2457,8 @@ static int mwl8k_cmd_set_rts_threshold(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_RTS_THRESHOLD); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - cmd->action = cpu_to_le16(action); - cmd->threshold = cpu_to_le16(threshold); + cmd->action = cpu_to_le16(MWL8K_CMD_SET); + cmd->threshold = cpu_to_le16(rts_thresh); rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2357,12 +2558,6 @@ mwl8k_cmd_set_edca_params(struct ieee80211_hw *hw, __u8 qnum, if (cmd == NULL) return -ENOMEM; - /* - * Queues 0 (BE) and 1 (BK) are swapped in hardware for - * this call. - */ - qnum ^= !(qnum >> 1); - cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_EDCA_PARAMS); cmd->header.length = cpu_to_le16(sizeof(*cmd)); cmd->action = cpu_to_le16(MWL8K_SET_EDCA_ALL); @@ -2448,49 +2643,70 @@ static int mwl8k_cmd_mimo_config(struct ieee80211_hw *hw, __u8 rx, __u8 tx) } /* - * CMD_USE_FIXED_RATE. + * CMD_USE_FIXED_RATE (STA version). */ -#define MWL8K_RATE_TABLE_SIZE 8 -#define MWL8K_UCAST_RATE 0 -#define MWL8K_USE_AUTO_RATE 0x0002 +struct mwl8k_cmd_use_fixed_rate_sta { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[8]; + __le32 rate_type; + __le32 reserved1; + __le32 reserved2; +} __attribute__((packed)); -struct mwl8k_rate_entry { - /* Set to 1 if HT rate, 0 if legacy. */ - __le32 is_ht_rate; +#define MWL8K_USE_AUTO_RATE 0x0002 +#define MWL8K_UCAST_RATE 0 - /* Set to 1 to use retry_count field. */ - __le32 enable_retry; +static int mwl8k_cmd_use_fixed_rate_sta(struct ieee80211_hw *hw) +{ + struct mwl8k_cmd_use_fixed_rate_sta *cmd; + int rc; - /* Specified legacy rate or MCS. */ - __le32 rate; + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; - /* Number of allowed retries. */ - __le32 retry_count; -} __attribute__((packed)); + cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->rate_type = cpu_to_le32(MWL8K_UCAST_RATE); -struct mwl8k_rate_table { - /* 1 to allow specified rate and below */ - __le32 allow_rate_drop; - __le32 num_rates; - struct mwl8k_rate_entry rate_entry[MWL8K_RATE_TABLE_SIZE]; -} __attribute__((packed)); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); -struct mwl8k_cmd_use_fixed_rate { - struct mwl8k_cmd_pkt header; - __le32 action; - struct mwl8k_rate_table rate_table; + return rc; +} - /* Unicast, Broadcast or Multicast */ - __le32 rate_type; - __le32 reserved1; - __le32 reserved2; +/* + * CMD_USE_FIXED_RATE (AP version). + */ +struct mwl8k_cmd_use_fixed_rate_ap { + struct mwl8k_cmd_pkt header; + __le32 action; + __le32 allow_rate_drop; + __le32 num_rates; + struct mwl8k_rate_entry_ap { + __le32 is_ht_rate; + __le32 enable_retry; + __le32 rate; + __le32 retry_count; + } rate_entry[4]; + u8 multicast_rate; + u8 multicast_rate_type; + u8 management_rate; } __attribute__((packed)); -static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, - u32 action, u32 rate_type, struct mwl8k_rate_table *rate_table) +static int +mwl8k_cmd_use_fixed_rate_ap(struct ieee80211_hw *hw, int mcast, int mgmt) { - struct mwl8k_cmd_use_fixed_rate *cmd; - int count; + struct mwl8k_cmd_use_fixed_rate_ap *cmd; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2499,32 +2715,9 @@ static int mwl8k_cmd_use_fixed_rate(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_USE_FIXED_RATE); cmd->header.length = cpu_to_le16(sizeof(*cmd)); - - cmd->action = cpu_to_le32(action); - cmd->rate_type = cpu_to_le32(rate_type); - - if (rate_table != NULL) { - /* - * Copy over each field manually so that endian - * conversion can be done. - */ - cmd->rate_table.allow_rate_drop = - cpu_to_le32(rate_table->allow_rate_drop); - cmd->rate_table.num_rates = - cpu_to_le32(rate_table->num_rates); - - for (count = 0; count < rate_table->num_rates; count++) { - struct mwl8k_rate_entry *dst = - &cmd->rate_table.rate_entry[count]; - struct mwl8k_rate_entry *src = - &rate_table->rate_entry[count]; - - dst->is_ht_rate = cpu_to_le32(src->is_ht_rate); - dst->enable_retry = cpu_to_le32(src->enable_retry); - dst->rate = cpu_to_le32(src->rate); - dst->retry_count = cpu_to_le32(src->retry_count); - } - } + cmd->action = cpu_to_le32(MWL8K_USE_AUTO_RATE); + cmd->multicast_rate = mcast; + cmd->management_rate = mgmt; rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); @@ -2573,12 +2766,33 @@ struct mwl8k_cmd_set_mac_addr { }; } __attribute__((packed)); -static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) +#define MWL8K_MAC_TYPE_PRIMARY_CLIENT 0 +#define MWL8K_MAC_TYPE_SECONDARY_CLIENT 1 +#define MWL8K_MAC_TYPE_PRIMARY_AP 2 +#define MWL8K_MAC_TYPE_SECONDARY_AP 3 + +static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *mac) { struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); struct mwl8k_cmd_set_mac_addr *cmd; + int mac_type; int rc; + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + if (vif != NULL && vif->type == NL80211_IFTYPE_STATION) { + if (mwl8k_vif->macid + 1 == ffs(priv->sta_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_CLIENT; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_CLIENT; + } else if (vif != NULL && vif->type == NL80211_IFTYPE_AP) { + if (mwl8k_vif->macid + 1 == ffs(priv->ap_macids_supported)) + mac_type = MWL8K_MAC_TYPE_PRIMARY_AP; + else + mac_type = MWL8K_MAC_TYPE_SECONDARY_AP; + } + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (cmd == NULL) return -ENOMEM; @@ -2586,13 +2800,13 @@ static int mwl8k_cmd_set_mac_addr(struct ieee80211_hw *hw, u8 *mac) cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_MAC_ADDR); cmd->header.length = cpu_to_le16(sizeof(*cmd)); if (priv->ap_fw) { - cmd->mbss.mac_type = 0; + cmd->mbss.mac_type = cpu_to_le16(mac_type); memcpy(cmd->mbss.mac_addr, mac, ETH_ALEN); } else { memcpy(cmd->mac_addr, mac, ETH_ALEN); } - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); return rc; @@ -2628,8 +2842,180 @@ static int mwl8k_cmd_set_rateadapt_mode(struct ieee80211_hw *hw, __u16 mode) } /* + * CMD_BSS_START. + */ +struct mwl8k_cmd_bss_start { + struct mwl8k_cmd_pkt header; + __le32 enable; +} __attribute__((packed)); + +static int mwl8k_cmd_bss_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, int enable) +{ + struct mwl8k_cmd_bss_start *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_BSS_START); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->enable = cpu_to_le32(enable); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* + * CMD_SET_NEW_STN. + */ +struct mwl8k_cmd_set_new_stn { + struct mwl8k_cmd_pkt header; + __le16 aid; + __u8 mac_addr[6]; + __le16 stn_id; + __le16 action; + __le16 rsvd; + __le32 legacy_rates; + __u8 ht_rates[4]; + __le16 cap_info; + __le16 ht_capabilities_info; + __u8 mac_ht_param_info; + __u8 rev; + __u8 control_channel; + __u8 add_channel; + __le16 op_mode; + __le16 stbc; + __u8 add_qos_info; + __u8 is_qos_sta; + __le32 fw_sta_ptr; +} __attribute__((packed)); + +#define MWL8K_STA_ACTION_ADD 0 +#define MWL8K_STA_ACTION_REMOVE 2 + +static int mwl8k_cmd_set_new_stn_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mwl8k_cmd_set_new_stn *cmd; + u32 rates; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->aid = cpu_to_le16(sta->aid); + memcpy(cmd->mac_addr, sta->addr, ETH_ALEN); + cmd->stn_id = cpu_to_le16(sta->aid); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_ADD); + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + cmd->legacy_rates = cpu_to_le32(rates); + if (sta->ht_cap.ht_supported) { + cmd->ht_rates[0] = sta->ht_cap.mcs.rx_mask[0]; + cmd->ht_rates[1] = sta->ht_cap.mcs.rx_mask[1]; + cmd->ht_rates[2] = sta->ht_cap.mcs.rx_mask[2]; + cmd->ht_rates[3] = sta->ht_cap.mcs.rx_mask[3]; + cmd->ht_capabilities_info = cpu_to_le16(sta->ht_cap.cap); + cmd->mac_ht_param_info = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + cmd->is_qos_sta = 1; + } + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_set_new_stn_add_self(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, vif->addr, ETH_ALEN); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +static int mwl8k_cmd_set_new_stn_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_set_new_stn *cmd; + int rc; + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_SET_NEW_STN); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + memcpy(cmd->mac_addr, addr, ETH_ALEN); + cmd->action = cpu_to_le16(MWL8K_STA_ACTION_REMOVE); + + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); + kfree(cmd); + + return rc; +} + +/* * CMD_UPDATE_STADB. */ +struct ewc_ht_info { + __le16 control1; + __le16 control2; + __le16 control3; +} __attribute__((packed)); + +struct peer_capability_info { + /* Peer type - AP vs. STA. */ + __u8 peer_type; + + /* Basic 802.11 capabilities from assoc resp. */ + __le16 basic_caps; + + /* Set if peer supports 802.11n high throughput (HT). */ + __u8 ht_support; + + /* Valid if HT is supported. */ + __le16 ht_caps; + __u8 extended_ht_caps; + struct ewc_ht_info ewc_info; + + /* Legacy rate table. Intersection of our rates and peer rates. */ + __u8 legacy_rates[12]; + + /* HT rate table. Intersection of our rates and peer rates. */ + __u8 ht_rates[16]; + __u8 pad[16]; + + /* If set, interoperability mode, no proprietary extensions. */ + __u8 interop; + __u8 pad2; + __u8 station_id; + __le16 amsdu_enabled; +} __attribute__((packed)); + struct mwl8k_cmd_update_stadb { struct mwl8k_cmd_pkt header; @@ -2645,12 +3031,19 @@ struct mwl8k_cmd_update_stadb { struct peer_capability_info peer_info; } __attribute__((packed)); -static int mwl8k_cmd_update_stadb(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, __u32 action) +#define MWL8K_STA_DB_MODIFY_ENTRY 1 +#define MWL8K_STA_DB_DEL_ENTRY 2 + +/* Peer Entry flags - used to define the type of the peer node */ +#define MWL8K_PEER_TYPE_ACCESSPOINT 2 + +static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { - struct mwl8k_vif *mv_vif = MWL8K_VIF(vif); struct mwl8k_cmd_update_stadb *cmd; - struct peer_capability_info *peer_info; + struct peer_capability_info *p; + u32 rates; int rc; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); @@ -2659,37 +3052,47 @@ static int mwl8k_cmd_update_stadb(struct ieee80211_hw *hw, cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_MODIFY_ENTRY); + memcpy(cmd->peer_addr, sta->addr, ETH_ALEN); + + p = &cmd->peer_info; + p->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; + p->basic_caps = cpu_to_le16(vif->bss_conf.assoc_capability); + p->ht_support = sta->ht_cap.ht_supported; + p->ht_caps = sta->ht_cap.cap; + p->extended_ht_caps = (sta->ht_cap.ampdu_factor & 3) | + ((sta->ht_cap.ampdu_density & 7) << 2); + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rates = sta->supp_rates[IEEE80211_BAND_2GHZ]; + else + rates = sta->supp_rates[IEEE80211_BAND_5GHZ] << 5; + legacy_rate_mask_to_array(p->legacy_rates, rates); + memcpy(p->ht_rates, sta->ht_cap.mcs.rx_mask, 16); + p->interop = 1; + p->amsdu_enabled = 0; - cmd->action = cpu_to_le32(action); - peer_info = &cmd->peer_info; - memcpy(cmd->peer_addr, mv_vif->bssid, ETH_ALEN); + rc = mwl8k_post_cmd(hw, &cmd->header); + kfree(cmd); - switch (action) { - case MWL8K_STA_DB_ADD_ENTRY: - case MWL8K_STA_DB_MODIFY_ENTRY: - /* Build peer_info block */ - peer_info->peer_type = MWL8K_PEER_TYPE_ACCESSPOINT; - peer_info->basic_caps = - cpu_to_le16(vif->bss_conf.assoc_capability); - memcpy(peer_info->legacy_rates, mwl8k_rateids, - sizeof(mwl8k_rateids)); - peer_info->interop = 1; - peer_info->amsdu_enabled = 0; - - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = peer_info->station_id; + return rc ? rc : p->station_id; +} - break; +static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u8 *addr) +{ + struct mwl8k_cmd_update_stadb *cmd; + int rc; - case MWL8K_STA_DB_DEL_ENTRY: - case MWL8K_STA_DB_FLUSH: - default: - rc = mwl8k_post_cmd(hw, &cmd->header); - if (rc == 0) - mv_vif->peer_id = 0; - break; - } + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (cmd == NULL) + return -ENOMEM; + + cmd->header.code = cpu_to_le16(MWL8K_CMD_UPDATE_STADB); + cmd->header.length = cpu_to_le16(sizeof(*cmd)); + cmd->action = cpu_to_le32(MWL8K_STA_DB_DEL_ENTRY); + memcpy(cmd->peer_addr, addr, ETH_ALEN); + + rc = mwl8k_post_cmd(hw, &cmd->header); kfree(cmd); return rc; @@ -2706,19 +3109,22 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) u32 status; status = ioread32(priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); - if (!status) return IRQ_NONE; - if (status & MWL8K_A2H_INT_TX_DONE) - tasklet_schedule(&priv->tx_reclaim_task); + if (status & MWL8K_A2H_INT_TX_DONE) { + status &= ~MWL8K_A2H_INT_TX_DONE; + tasklet_schedule(&priv->poll_tx_task); + } if (status & MWL8K_A2H_INT_RX_READY) { - while (rxq_process(hw, 0, 1)) - rxq_refill(hw, 0, 1); + status &= ~MWL8K_A2H_INT_RX_READY; + tasklet_schedule(&priv->poll_rx_task); } + if (status) + iowrite32(~status, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + if (status & MWL8K_A2H_INT_OPC_DONE) { if (priv->hostcmd_wait != NULL) complete(priv->hostcmd_wait); @@ -2733,6 +3139,53 @@ static irqreturn_t mwl8k_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } +static void mwl8k_tx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + int i; + + limit = 32; + + spin_lock_bh(&priv->tx_lock); + + for (i = 0; i < MWL8K_TX_QUEUES; i++) + limit -= mwl8k_txq_reclaim(hw, i, limit, 0); + + if (!priv->pending_tx_pkts && priv->tx_wait != NULL) { + complete(priv->tx_wait); + priv->tx_wait = NULL; + } + + spin_unlock_bh(&priv->tx_lock); + + if (limit) { + writel(~MWL8K_A2H_INT_TX_DONE, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_tx_task); + } +} + +static void mwl8k_rx_poll(unsigned long data) +{ + struct ieee80211_hw *hw = (struct ieee80211_hw *)data; + struct mwl8k_priv *priv = hw->priv; + int limit; + + limit = 32; + limit -= rxq_process(hw, 0, limit); + limit -= rxq_refill(hw, 0, limit); + + if (limit) { + writel(~MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); + } else { + tasklet_schedule(&priv->poll_rx_task); + } +} + /* * Core driver operations. @@ -2743,7 +3196,7 @@ static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) int index = skb_get_queue_mapping(skb); int rc; - if (priv->current_channel == NULL) { + if (!priv->radio_on) { printk(KERN_DEBUG "%s: dropped TX frame since radio " "disabled\n", wiphy_name(hw->wiphy)); dev_kfree_skb(skb); @@ -2768,8 +3221,9 @@ static int mwl8k_start(struct ieee80211_hw *hw) return -EIO; } - /* Enable tx reclaim tasklet */ - tasklet_enable(&priv->tx_reclaim_task); + /* Enable TX reclaim and RX tasklets. */ + tasklet_enable(&priv->poll_tx_task); + tasklet_enable(&priv->poll_rx_task); /* Enable interrupts */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); @@ -2802,7 +3256,8 @@ static int mwl8k_start(struct ieee80211_hw *hw) if (rc) { iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); free_irq(priv->pdev->irq, hw); - tasklet_disable(&priv->tx_reclaim_task); + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); } return rc; @@ -2826,36 +3281,27 @@ static void mwl8k_stop(struct ieee80211_hw *hw) if (priv->beacon_skb != NULL) dev_kfree_skb(priv->beacon_skb); - /* Stop tx reclaim tasklet */ - tasklet_disable(&priv->tx_reclaim_task); + /* Stop TX reclaim and RX tasklets. */ + tasklet_disable(&priv->poll_tx_task); + tasklet_disable(&priv->poll_rx_task); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); } static int mwl8k_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) + struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; struct mwl8k_vif *mwl8k_vif; - - /* - * We only support one active interface at a time. - */ - if (priv->vif != NULL) - return -EBUSY; - - /* - * We only support managed interfaces for now. - */ - if (vif->type != NL80211_IFTYPE_STATION) - return -EINVAL; + u32 macids_supported; + int macid; /* * Reject interface creation if sniffer mode is active, as * STA operation is mutually exclusive with hardware sniffer - * mode. + * mode. (Sniffer mode is only used on STA firmware.) */ if (priv->sniffer_enabled) { printk(KERN_INFO "%s: unable to create STA " @@ -2864,19 +3310,37 @@ static int mwl8k_add_interface(struct ieee80211_hw *hw, return -EINVAL; } - /* Clean out driver private area */ + + switch (vif->type) { + case NL80211_IFTYPE_AP: + macids_supported = priv->ap_macids_supported; + break; + case NL80211_IFTYPE_STATION: + macids_supported = priv->sta_macids_supported; + break; + default: + return -EINVAL; + } + + macid = ffs(macids_supported & ~priv->macids_used); + if (!macid--) + return -EBUSY; + + /* Setup driver private area. */ mwl8k_vif = MWL8K_VIF(vif); memset(mwl8k_vif, 0, sizeof(*mwl8k_vif)); + mwl8k_vif->vif = vif; + mwl8k_vif->macid = macid; + mwl8k_vif->seqno = 0; - /* Set and save the mac address */ - mwl8k_cmd_set_mac_addr(hw, vif->addr); - memcpy(mwl8k_vif->mac_addr, vif->addr, ETH_ALEN); + /* Set the mac address. */ + mwl8k_cmd_set_mac_addr(hw, vif, vif->addr); - /* Set Initial sequence number to zero */ - mwl8k_vif->seqno = 0; + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_add_self(hw, vif); - priv->vif = vif; - priv->current_channel = NULL; + priv->macids_used |= 1 << mwl8k_vif->macid; + list_add_tail(&mwl8k_vif->list, &priv->vif_list); return 0; } @@ -2885,13 +3349,15 @@ static void mwl8k_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { struct mwl8k_priv *priv = hw->priv; + struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); - if (priv->vif == NULL) - return; + if (priv->ap_fw) + mwl8k_cmd_set_new_stn_del(hw, vif, vif->addr); - mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + mwl8k_cmd_set_mac_addr(hw, vif, "\x00\x00\x00\x00\x00\x00"); - priv->vif = NULL; + priv->macids_used &= ~(1 << mwl8k_vif->macid); + list_del(&mwl8k_vif->list); } static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) @@ -2902,7 +3368,6 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (conf->flags & IEEE80211_CONF_IDLE) { mwl8k_cmd_radio_disable(hw); - priv->current_channel = NULL; return 0; } @@ -2914,12 +3379,10 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) goto out; - rc = mwl8k_cmd_set_rf_channel(hw, conf->channel); + rc = mwl8k_cmd_set_rf_channel(hw, conf); if (rc) goto out; - priv->current_channel = conf->channel; - if (conf->power_level > 18) conf->power_level = 18; rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); @@ -2940,75 +3403,160 @@ out: return rc; } -static void mwl8k_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u32 changed) +static void +mwl8k_bss_info_changed_sta(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) { struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mwl8k_vif = MWL8K_VIF(vif); + u32 ap_legacy_rates; + u8 ap_mcs_rates[16]; int rc; - if ((changed & BSS_CHANGED_ASSOC) == 0) + if (mwl8k_fw_lock(hw)) return; - priv->capture_beacon = false; - - rc = mwl8k_fw_lock(hw); - if (rc) - return; + /* + * No need to capture a beacon if we're no longer associated. + */ + if ((changed & BSS_CHANGED_ASSOC) && !vif->bss_conf.assoc) + priv->capture_beacon = false; + /* + * Get the AP's legacy and MCS rates. + */ if (vif->bss_conf.assoc) { - memcpy(mwl8k_vif->bssid, vif->bss_conf.bssid, ETH_ALEN); + struct ieee80211_sta *ap; + + rcu_read_lock(); + + ap = ieee80211_find_sta(vif, vif->bss_conf.bssid); + if (ap == NULL) { + rcu_read_unlock(); + goto out; + } + + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) { + ap_legacy_rates = ap->supp_rates[IEEE80211_BAND_2GHZ]; + } else { + ap_legacy_rates = + ap->supp_rates[IEEE80211_BAND_5GHZ] << 5; + } + memcpy(ap_mcs_rates, ap->ht_cap.mcs.rx_mask, 16); + + rcu_read_unlock(); + } - /* Install rates */ - rc = mwl8k_cmd_set_rate(hw, vif); + if ((changed & BSS_CHANGED_ASSOC) && vif->bss_conf.assoc) { + rc = mwl8k_cmd_set_rate(hw, vif, ap_legacy_rates, ap_mcs_rates); if (rc) goto out; - /* Turn on rate adaptation */ - rc = mwl8k_cmd_use_fixed_rate(hw, MWL8K_USE_AUTO_RATE, - MWL8K_UCAST_RATE, NULL); + rc = mwl8k_cmd_use_fixed_rate_sta(hw); if (rc) goto out; + } - /* Set radio preamble */ + if (changed & BSS_CHANGED_ERP_PREAMBLE) { rc = mwl8k_set_radio_preamble(hw, vif->bss_conf.use_short_preamble); if (rc) goto out; + } - /* Set slot time */ + if (changed & BSS_CHANGED_ERP_SLOT) { rc = mwl8k_cmd_set_slot(hw, vif->bss_conf.use_short_slot); if (rc) goto out; + } - /* Update peer rate info */ - rc = mwl8k_cmd_update_stadb(hw, vif, - MWL8K_STA_DB_MODIFY_ENTRY); - if (rc) - goto out; - - /* Set AID */ - rc = mwl8k_cmd_set_aid(hw, vif); + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_ERP_CTS_PROT | + BSS_CHANGED_HT))) { + rc = mwl8k_cmd_set_aid(hw, vif, ap_legacy_rates); if (rc) goto out; + } + if (vif->bss_conf.assoc && + (changed & (BSS_CHANGED_ASSOC | BSS_CHANGED_BEACON_INT))) { /* * Finalize the join. Tell rx handler to process * next beacon from our BSSID. */ - memcpy(priv->capture_bssid, mwl8k_vif->bssid, ETH_ALEN); + memcpy(priv->capture_bssid, vif->bss_conf.bssid, ETH_ALEN); priv->capture_beacon = true; - } else { - rc = mwl8k_cmd_update_stadb(hw, vif, MWL8K_STA_DB_DEL_ENTRY); - memset(mwl8k_vif->bssid, 0, ETH_ALEN); } out: mwl8k_fw_unlock(hw); } +static void +mwl8k_bss_info_changed_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + int rc; + + if (mwl8k_fw_lock(hw)) + return; + + if (changed & BSS_CHANGED_ERP_PREAMBLE) { + rc = mwl8k_set_radio_preamble(hw, + vif->bss_conf.use_short_preamble); + if (rc) + goto out; + } + + if (changed & BSS_CHANGED_BASIC_RATES) { + int idx; + int rate; + + /* + * Use lowest supported basic rate for multicasts + * and management frames (such as probe responses -- + * beacons will always go out at 1 Mb/s). + */ + idx = ffs(vif->bss_conf.basic_rates); + if (idx) + idx--; + + if (hw->conf.channel->band == IEEE80211_BAND_2GHZ) + rate = mwl8k_rates_24[idx].hw_value; + else + rate = mwl8k_rates_50[idx].hw_value; + + mwl8k_cmd_use_fixed_rate_ap(hw, rate, rate); + } + + if (changed & (BSS_CHANGED_BEACON_INT | BSS_CHANGED_BEACON)) { + struct sk_buff *skb; + + skb = ieee80211_beacon_get(hw, vif); + if (skb != NULL) { + mwl8k_cmd_set_beacon(hw, vif, skb->data, skb->len); + kfree_skb(skb); + } + } + + if (changed & BSS_CHANGED_BEACON_ENABLED) + mwl8k_cmd_bss_start(hw, vif, info->enable_beacon); + +out: + mwl8k_fw_unlock(hw); +} + +static void +mwl8k_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u32 changed) +{ + struct mwl8k_priv *priv = hw->priv; + + if (!priv->ap_fw) + mwl8k_bss_info_changed_sta(hw, vif, info, changed); + else + mwl8k_bss_info_changed_ap(hw, vif, info, changed); +} + static u64 mwl8k_prepare_multicast(struct ieee80211_hw *hw, int mc_count, struct dev_addr_list *mclist) { @@ -3038,7 +3586,7 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, * operation, so refuse to enable sniffer mode if a STA * interface is active. */ - if (priv->vif != NULL) { + if (!list_empty(&priv->vif_list)) { if (net_ratelimit()) printk(KERN_INFO "%s: not enabling sniffer " "mode because STA interface is active\n", @@ -3059,6 +3607,14 @@ mwl8k_configure_filter_sniffer(struct ieee80211_hw *hw, return 1; } +static struct mwl8k_vif *mwl8k_first_vif(struct mwl8k_priv *priv) +{ + if (!list_empty(&priv->vif_list)) + return list_entry(priv->vif_list.next, struct mwl8k_vif, list); + + return NULL; +} + static void mwl8k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -3090,8 +3646,10 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, /* Clear unsupported feature flags */ *total_flags &= FIF_ALLMULTI | FIF_BCN_PRBRESP_PROMISC; - if (mwl8k_fw_lock(hw)) + if (mwl8k_fw_lock(hw)) { + kfree(cmd); return; + } if (priv->sniffer_enabled) { mwl8k_cmd_enable_sniffer(hw, 0); @@ -3105,7 +3663,8 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, */ mwl8k_cmd_set_pre_scan(hw); } else { - u8 *bssid; + struct mwl8k_vif *mwl8k_vif; + const u8 *bssid; /* * Enable the BSS filter. @@ -3115,9 +3674,11 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, * (where the OUI part needs to be nonzero for * the BSSID to be accepted by POST_SCAN). */ - bssid = "\x01\x00\x00\x00\x00\x00"; - if (priv->vif != NULL) - bssid = MWL8K_VIF(priv->vif)->bssid; + mwl8k_vif = mwl8k_first_vif(priv); + if (mwl8k_vif != NULL) + bssid = mwl8k_vif->vif->bss_conf.bssid; + else + bssid = "\x01\x00\x00\x00\x00\x00"; mwl8k_cmd_set_post_scan(hw, bssid); } @@ -3144,7 +3705,93 @@ static void mwl8k_configure_filter(struct ieee80211_hw *hw, static int mwl8k_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { - return mwl8k_cmd_set_rts_threshold(hw, MWL8K_CMD_SET, value); + return mwl8k_cmd_set_rts_threshold(hw, value); +} + +struct mwl8k_sta_notify_item +{ + struct list_head list; + struct ieee80211_vif *vif; + enum sta_notify_cmd cmd; + struct ieee80211_sta sta; +}; + +static void +mwl8k_do_sta_notify(struct ieee80211_hw *hw, struct mwl8k_sta_notify_item *s) +{ + struct mwl8k_priv *priv = hw->priv; + + /* + * STA firmware uses UPDATE_STADB, AP firmware uses SET_NEW_STN. + */ + if (!priv->ap_fw && s->cmd == STA_NOTIFY_ADD) { + int rc; + + rc = mwl8k_cmd_update_stadb_add(hw, s->vif, &s->sta); + if (rc >= 0) { + struct ieee80211_sta *sta; + + rcu_read_lock(); + sta = ieee80211_find_sta(s->vif, s->sta.addr); + if (sta != NULL) + MWL8K_STA(sta)->peer_id = rc; + rcu_read_unlock(); + } + } else if (!priv->ap_fw && s->cmd == STA_NOTIFY_REMOVE) { + mwl8k_cmd_update_stadb_del(hw, s->vif, s->sta.addr); + } else if (priv->ap_fw && s->cmd == STA_NOTIFY_ADD) { + mwl8k_cmd_set_new_stn_add(hw, s->vif, &s->sta); + } else if (priv->ap_fw && s->cmd == STA_NOTIFY_REMOVE) { + mwl8k_cmd_set_new_stn_del(hw, s->vif, s->sta.addr); + } +} + +static void mwl8k_sta_notify_worker(struct work_struct *work) +{ + struct mwl8k_priv *priv = + container_of(work, struct mwl8k_priv, sta_notify_worker); + struct ieee80211_hw *hw = priv->hw; + + spin_lock_bh(&priv->sta_notify_list_lock); + while (!list_empty(&priv->sta_notify_list)) { + struct mwl8k_sta_notify_item *s; + + s = list_entry(priv->sta_notify_list.next, + struct mwl8k_sta_notify_item, list); + list_del(&s->list); + + spin_unlock_bh(&priv->sta_notify_list_lock); + + mwl8k_do_sta_notify(hw, s); + kfree(s); + + spin_lock_bh(&priv->sta_notify_list_lock); + } + spin_unlock_bh(&priv->sta_notify_list_lock); +} + +static void +mwl8k_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, struct ieee80211_sta *sta) +{ + struct mwl8k_priv *priv = hw->priv; + struct mwl8k_sta_notify_item *s; + + if (cmd != STA_NOTIFY_ADD && cmd != STA_NOTIFY_REMOVE) + return; + + s = kmalloc(sizeof(*s), GFP_ATOMIC); + if (s != NULL) { + s->vif = vif; + s->cmd = cmd; + s->sta = *sta; + + spin_lock(&priv->sta_notify_list_lock); + list_add_tail(&s->list, &priv->sta_notify_list); + spin_unlock(&priv->sta_notify_list_lock); + + ieee80211_queue_work(hw, &priv->sta_notify_worker); + } } static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, @@ -3195,6 +3842,22 @@ static int mwl8k_get_stats(struct ieee80211_hw *hw, return mwl8k_cmd_get_stat(hw, stats); } +static int +mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + switch (action) { + case IEEE80211_AMPDU_RX_START: + case IEEE80211_AMPDU_RX_STOP: + if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) + return -ENOTSUPP; + return 0; + default: + return -ENOTSUPP; + } +} + static const struct ieee80211_ops mwl8k_ops = { .tx = mwl8k_tx, .start = mwl8k_start, @@ -3206,47 +3869,41 @@ static const struct ieee80211_ops mwl8k_ops = { .prepare_multicast = mwl8k_prepare_multicast, .configure_filter = mwl8k_configure_filter, .set_rts_threshold = mwl8k_set_rts_threshold, + .sta_notify = mwl8k_sta_notify, .conf_tx = mwl8k_conf_tx, .get_tx_stats = mwl8k_get_tx_stats, .get_stats = mwl8k_get_stats, + .ampdu_action = mwl8k_ampdu_action, }; -static void mwl8k_tx_reclaim_handler(unsigned long data) -{ - int i; - struct ieee80211_hw *hw = (struct ieee80211_hw *) data; - struct mwl8k_priv *priv = hw->priv; - - spin_lock_bh(&priv->tx_lock); - for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 0); - - if (priv->tx_wait != NULL && !priv->pending_tx_pkts) { - complete(priv->tx_wait); - priv->tx_wait = NULL; - } - spin_unlock_bh(&priv->tx_lock); -} - static void mwl8k_finalize_join_worker(struct work_struct *work) { struct mwl8k_priv *priv = container_of(work, struct mwl8k_priv, finalize_join_worker); struct sk_buff *skb = priv->beacon_skb; + struct mwl8k_vif *mwl8k_vif; - mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, - priv->vif->bss_conf.dtim_period); - dev_kfree_skb(skb); + mwl8k_vif = mwl8k_first_vif(priv); + if (mwl8k_vif != NULL) + mwl8k_cmd_finalize_join(priv->hw, skb->data, skb->len, + mwl8k_vif->vif->bss_conf.dtim_period); + dev_kfree_skb(skb); priv->beacon_skb = NULL; } enum { - MWL8687 = 0, + MWL8363 = 0, + MWL8687, MWL8366, }; static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { + [MWL8363] = { + .part_name = "88w8363", + .helper_image = "mwl8k/helper_8363.fw", + .fw_image = "mwl8k/fmimage_8363.fw", + }, [MWL8687] = { .part_name = "88w8687", .helper_image = "mwl8k/helper_8687.fw", @@ -3260,10 +3917,20 @@ static struct mwl8k_device_info mwl8k_info_tbl[] __devinitdata = { }, }; +MODULE_FIRMWARE("mwl8k/helper_8363.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8363.fw"); +MODULE_FIRMWARE("mwl8k/helper_8687.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8687.fw"); +MODULE_FIRMWARE("mwl8k/helper_8366.fw"); +MODULE_FIRMWARE("mwl8k/fmimage_8366.fw"); + static DEFINE_PCI_DEVICE_TABLE(mwl8k_pci_id_table) = { + { PCI_VDEVICE(MARVELL, 0x2a0c), .driver_data = MWL8363, }, + { PCI_VDEVICE(MARVELL, 0x2a24), .driver_data = MWL8363, }, { PCI_VDEVICE(MARVELL, 0x2a2b), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a30), .driver_data = MWL8687, }, { PCI_VDEVICE(MARVELL, 0x2a40), .driver_data = MWL8366, }, + { PCI_VDEVICE(MARVELL, 0x2a43), .driver_data = MWL8366, }, { }, }; MODULE_DEVICE_TABLE(pci, mwl8k_pci_id_table); @@ -3361,27 +4028,23 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, mwl8k_release_firmware(priv); - if (priv->ap_fw) + if (priv->ap_fw) { priv->rxd_ops = priv->device_info->ap_rxd_ops; - else + if (priv->rxd_ops == NULL) { + printk(KERN_ERR "%s: Driver does not have AP " + "firmware image support for this hardware\n", + wiphy_name(hw->wiphy)); + goto err_stop_firmware; + } + } else { priv->rxd_ops = &rxd_sta_ops; + } priv->sniffer_enabled = false; priv->wmm_enabled = false; priv->pending_tx_pkts = 0; - memcpy(priv->channels, mwl8k_channels, sizeof(mwl8k_channels)); - priv->band.band = IEEE80211_BAND_2GHZ; - priv->band.channels = priv->channels; - priv->band.n_channels = ARRAY_SIZE(mwl8k_channels); - priv->band.bitrates = priv->rates; - priv->band.n_bitrates = ARRAY_SIZE(mwl8k_rates); - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &priv->band; - - BUILD_BUG_ON(sizeof(priv->rates) != sizeof(mwl8k_rates)); - memcpy(priv->rates, mwl8k_rates, sizeof(mwl8k_rates)); - /* * Extra headroom is the size of the required DMA header * minus the size of the smallest 802.11 frame (CTS frame). @@ -3396,19 +4059,28 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* Set rssi and noise values to dBm */ hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM; hw->vif_data_size = sizeof(struct mwl8k_vif); - priv->vif = NULL; + hw->sta_data_size = sizeof(struct mwl8k_sta); + + priv->macids_used = 0; + INIT_LIST_HEAD(&priv->vif_list); /* Set default radio state and preamble */ priv->radio_on = 0; priv->radio_short_preamble = 0; + /* Station database handling */ + INIT_WORK(&priv->sta_notify_worker, mwl8k_sta_notify_worker); + spin_lock_init(&priv->sta_notify_list_lock); + INIT_LIST_HEAD(&priv->sta_notify_list); + /* Finalize join worker */ INIT_WORK(&priv->finalize_join_worker, mwl8k_finalize_join_worker); - /* TX reclaim tasklet */ - tasklet_init(&priv->tx_reclaim_task, - mwl8k_tx_reclaim_handler, (unsigned long)hw); - tasklet_disable(&priv->tx_reclaim_task); + /* TX reclaim and RX tasklets. */ + tasklet_init(&priv->poll_tx_task, mwl8k_tx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_tx_task); + tasklet_init(&priv->poll_rx_task, mwl8k_rx_poll, (unsigned long)hw); + tasklet_disable(&priv->poll_rx_task); /* Power management cookie */ priv->cookie = pci_alloc_consistent(priv->pdev, 4, &priv->cookie_dma); @@ -3437,7 +4109,8 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS); iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); - iowrite32(0, priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); + iowrite32(MWL8K_A2H_INT_TX_DONE | MWL8K_A2H_INT_RX_READY, + priv->regs + MWL8K_HIU_A2H_INTERRUPT_CLEAR_SEL); iowrite32(0xffffffff, priv->regs + MWL8K_HIU_A2H_INTERRUPT_STATUS_MASK); rc = request_irq(priv->pdev->irq, mwl8k_interrupt, @@ -3450,7 +4123,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, /* * Temporarily enable interrupts. Initial firmware host - * commands use interrupts and avoids polling. Disable + * commands use interrupts and avoid polling. Disable * interrupts when done. */ iowrite32(MWL8K_A2H_EVENTS, priv->regs + MWL8K_HIU_A2H_INTERRUPT_MASK); @@ -3462,8 +4135,6 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, rc = mwl8k_cmd_set_hw_spec(hw); } else { rc = mwl8k_cmd_get_hw_spec_sta(hw); - - hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); } if (rc) { printk(KERN_ERR "%s: Cannot initialise firmware\n", @@ -3471,6 +4142,13 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, goto err_free_irq; } + hw->wiphy->interface_modes = 0; + if (priv->ap_macids_supported) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); + if (priv->sta_macids_supported) + hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); + + /* Turn radio off */ rc = mwl8k_cmd_radio_disable(hw); if (rc) { @@ -3479,7 +4157,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, } /* Clear MAC address */ - rc = mwl8k_cmd_set_mac_addr(hw, "\x00\x00\x00\x00\x00\x00"); + rc = mwl8k_cmd_set_mac_addr(hw, NULL, "\x00\x00\x00\x00\x00\x00"); if (rc) { printk(KERN_ERR "%s: Cannot clear MAC address\n", wiphy_name(hw->wiphy)); @@ -3494,7 +4172,7 @@ static int __devinit mwl8k_probe(struct pci_dev *pdev, if (rc) { printk(KERN_ERR "%s: Cannot register device\n", wiphy_name(hw->wiphy)); - goto err_free_irq; + goto err_free_queues; } printk(KERN_INFO "%s: %s v%d, %pM, %s firmware %u.%u.%u.%u\n", @@ -3562,15 +4240,16 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) ieee80211_unregister_hw(hw); - /* Remove tx reclaim tasklet */ - tasklet_kill(&priv->tx_reclaim_task); + /* Remove TX reclaim and RX tasklets. */ + tasklet_kill(&priv->poll_tx_task); + tasklet_kill(&priv->poll_rx_task); /* Stop hardware */ mwl8k_hw_reset(priv); /* Return all skbs to mac80211 */ for (i = 0; i < MWL8K_TX_QUEUES; i++) - mwl8k_txq_reclaim(hw, i, 1); + mwl8k_txq_reclaim(hw, i, INT_MAX, 1); for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_deinit(hw, i); diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c index 8ff7db85328..529a37364eb 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/rt2x00/rt2800lib.c @@ -245,6 +245,25 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, } EXPORT_SYMBOL_GPL(rt2800_mcu_request); +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) +{ + unsigned int i; + u32 reg; + + for (i = 0; i < REGISTER_BUSY_COUNT; i++) { + rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); + if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && + !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) + return 0; + + msleep(1); + } + + ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); + return -EACCES; +} +EXPORT_SYMBOL_GPL(rt2800_wait_wpdma_ready); + #ifdef CONFIG_RT2X00_LIB_DEBUGFS const struct rt2x00debug rt2800_rt2x00debug = { .owner = THIS_MODULE, @@ -339,7 +358,7 @@ static int rt2800_blink_set(struct led_classdev *led_cdev, rt2x00_set_field32(®, LED_CFG_OFF_PERIOD, *delay_off); rt2x00_set_field32(®, LED_CFG_SLOW_BLINK_PERIOD, 3); rt2x00_set_field32(®, LED_CFG_R_LED_MODE, 3); - rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 12); + rt2x00_set_field32(®, LED_CFG_G_LED_MODE, 3); rt2x00_set_field32(®, LED_CFG_Y_LED_MODE, 3); rt2x00_set_field32(®, LED_CFG_LED_POLAR, 1); rt2800_register_write(led->rt2x00dev, LED_CFG, reg); @@ -347,7 +366,7 @@ static int rt2800_blink_set(struct led_classdev *led_cdev, return 0; } -void rt2800_init_led(struct rt2x00_dev *rt2x00dev, +static void rt2800_init_led(struct rt2x00_dev *rt2x00dev, struct rt2x00_led *led, enum led_type type) { led->rt2x00dev = rt2x00dev; @@ -356,7 +375,6 @@ void rt2800_init_led(struct rt2x00_dev *rt2x00dev, led->led_dev.blink_set = rt2800_blink_set; led->flags = LED_INITIALIZED; } -EXPORT_SYMBOL_GPL(rt2800_init_led); #endif /* CONFIG_RT2X00_LIB_LEDS */ /* @@ -1862,7 +1880,8 @@ int rt2800_init_eeprom(struct rt2x00_dev *rt2x00dev) !rt2x00_rf(rt2x00dev, RF3020) && !rt2x00_rf(rt2x00dev, RF2020) && !rt2x00_rf(rt2x00dev, RF3021) && - !rt2x00_rf(rt2x00dev, RF3022)) { + !rt2x00_rf(rt2x00dev, RF3022) && + !rt2x00_rf(rt2x00dev, RF3052)) { ERROR(rt2x00dev, "Invalid RF chipset detected.\n"); return -ENODEV; } @@ -2047,7 +2066,7 @@ int rt2800_probe_hw_mode(struct rt2x00_dev *rt2x00dev) if (rt2x00_rf(rt2x00dev, RF2820) || rt2x00_rf(rt2x00dev, RF2720) || - (rt2x00_intf_is_pci(rt2x00dev) && rt2x00_rf(rt2x00dev, RF3052))) { + rt2x00_rf(rt2x00dev, RF3052)) { spec->num_channels = 14; spec->channels = rf_vals; } else if (rt2x00_rf(rt2x00dev, RF2850) || rt2x00_rf(rt2x00dev, RF2750)) { diff --git a/drivers/net/wireless/rt2x00/rt2800lib.h b/drivers/net/wireless/rt2x00/rt2800lib.h index 535ce22f2ac..ebabeae62d1 100644 --- a/drivers/net/wireless/rt2x00/rt2800lib.h +++ b/drivers/net/wireless/rt2x00/rt2800lib.h @@ -114,8 +114,6 @@ void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev, extern const struct rt2x00debug rt2800_rt2x00debug; int rt2800_rfkill_poll(struct rt2x00_dev *rt2x00dev); -void rt2800_init_led(struct rt2x00_dev *rt2x00dev, - struct rt2x00_led *led, enum led_type type); int rt2800_config_shared_key(struct rt2x00_dev *rt2x00dev, struct rt2x00lib_crypto *crypto, struct ieee80211_key_conf *key); @@ -139,6 +137,7 @@ void rt2800_link_tuner(struct rt2x00_dev *rt2x00dev, struct link_qual *qual, int rt2800_init_registers(struct rt2x00_dev *rt2x00dev); int rt2800_init_bbp(struct rt2x00_dev *rt2x00dev); int rt2800_init_rfcsr(struct rt2x00_dev *rt2x00dev); +int rt2800_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev); int rt2800_efuse_detect(struct rt2x00_dev *rt2x00dev); void rt2800_read_eeprom_efuse(struct rt2x00_dev *rt2x00dev); diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c index 99095e1ee13..d64181cbc9c 100644 --- a/drivers/net/wireless/rt2x00/rt2800pci.c +++ b/drivers/net/wireless/rt2x00/rt2800pci.c @@ -453,24 +453,6 @@ static void rt2800pci_toggle_irq(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, INT_MASK_CSR, reg); } -static int rt2800pci_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && - !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) - return 0; - - msleep(1); - } - - ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); - return -EACCES; -} - static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -479,10 +461,10 @@ static int rt2800pci_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (unlikely(rt2800pci_wait_wpdma_ready(rt2x00dev) || + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800pci_init_queues(rt2x00dev) || rt2800_init_registers(rt2x00dev) || - rt2800pci_wait_wpdma_ready(rt2x00dev) || + rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_bbp(rt2x00dev) || rt2800_init_rfcsr(rt2x00dev))) return -EIO; @@ -562,7 +544,7 @@ static void rt2800pci_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, PBF_SYS_CTRL, 0x00000e00); /* Wait for DMA, ignore error */ - rt2800pci_wait_wpdma_ready(rt2x00dev); + rt2800_wait_wpdma_ready(rt2x00dev); } static int rt2800pci_set_state(struct rt2x00_dev *rt2x00dev, diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 0510f020dcf..82755cf8b73 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -248,24 +248,6 @@ static void rt2800usb_toggle_rx(struct rt2x00_dev *rt2x00dev, rt2800_register_write(rt2x00dev, MAC_SYS_CTRL, reg); } -static int rt2800usb_wait_wpdma_ready(struct rt2x00_dev *rt2x00dev) -{ - unsigned int i; - u32 reg; - - for (i = 0; i < REGISTER_BUSY_COUNT; i++) { - rt2800_register_read(rt2x00dev, WPDMA_GLO_CFG, ®); - if (!rt2x00_get_field32(reg, WPDMA_GLO_CFG_TX_DMA_BUSY) && - !rt2x00_get_field32(reg, WPDMA_GLO_CFG_RX_DMA_BUSY)) - return 0; - - msleep(1); - } - - ERROR(rt2x00dev, "WPDMA TX/RX busy, aborting.\n"); - return -EACCES; -} - static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) { u32 reg; @@ -274,7 +256,7 @@ static int rt2800usb_enable_radio(struct rt2x00_dev *rt2x00dev) /* * Initialize all registers. */ - if (unlikely(rt2800usb_wait_wpdma_ready(rt2x00dev) || + if (unlikely(rt2800_wait_wpdma_ready(rt2x00dev) || rt2800_init_registers(rt2x00dev) || rt2800_init_bbp(rt2x00dev) || rt2800_init_rfcsr(rt2x00dev))) @@ -344,7 +326,7 @@ static void rt2800usb_disable_radio(struct rt2x00_dev *rt2x00dev) rt2800_register_write(rt2x00dev, TX_PIN_CFG, 0); /* Wait for DMA, ignore error */ - rt2800usb_wait_wpdma_ready(rt2x00dev); + rt2800_wait_wpdma_ready(rt2x00dev); rt2x00usb_disable_radio(rt2x00dev); } diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index b4c6e0a6d7e..096da85a66f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -119,6 +119,12 @@ ( ((unsigned long)((__skb)->data + (__header))) & 3 ) /* + * Constants for extra TX headroom for alignment purposes. + */ +#define RT2X00_ALIGN_SIZE 4 /* Only whole frame needs alignment */ +#define RT2X00_L2PAD_SIZE 8 /* Both header & payload need alignment */ + +/* * Standard timing and size defines. * These values should follow the ieee80211 specifications. */ diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index d7711e4d475..b93731b7990 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -688,7 +688,17 @@ static int rt2x00lib_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Initialize extra TX headroom required. */ - rt2x00dev->hw->extra_tx_headroom = rt2x00dev->ops->extra_tx_headroom; + rt2x00dev->hw->extra_tx_headroom = + max_t(unsigned int, IEEE80211_TX_STATUS_HEADROOM, + rt2x00dev->ops->extra_tx_headroom); + + /* + * Take TX headroom required for alignment into account. + */ + if (test_bit(DRIVER_REQUIRE_L2PAD, &rt2x00dev->flags)) + rt2x00dev->hw->extra_tx_headroom += RT2X00_L2PAD_SIZE; + else if (test_bit(DRIVER_REQUIRE_DMA, &rt2x00dev->flags)) + rt2x00dev->hw->extra_tx_headroom += RT2X00_ALIGN_SIZE; /* * Register HW. diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c index 3d8fb684b4e..0b4801a1460 100644 --- a/drivers/net/wireless/rt2x00/rt2x00queue.c +++ b/drivers/net/wireless/rt2x00/rt2x00queue.c @@ -104,7 +104,7 @@ void rt2x00queue_map_txskb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) * is also mapped to the DMA so it can be used for transfering * additional descriptor information to the hardware. */ - skb_push(skb, rt2x00dev->hw->extra_tx_headroom); + skb_push(skb, rt2x00dev->ops->extra_tx_headroom); skbdesc->skb_dma = dma_map_single(rt2x00dev->dev, skb->data, skb->len, DMA_TO_DEVICE); @@ -112,7 +112,7 @@ void rt2x00queue_map_txskb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) /* * Restore data pointer to original location again. */ - skb_pull(skb, rt2x00dev->hw->extra_tx_headroom); + skb_pull(skb, rt2x00dev->ops->extra_tx_headroom); skbdesc->flags |= SKBDESC_DMA_MAPPED_TX; } @@ -134,7 +134,7 @@ void rt2x00queue_unmap_skb(struct rt2x00_dev *rt2x00dev, struct sk_buff *skb) * by the driver, but it was actually mapped to DMA. */ dma_unmap_single(rt2x00dev->dev, skbdesc->skb_dma, - skb->len + rt2x00dev->hw->extra_tx_headroom, + skb->len + rt2x00dev->ops->extra_tx_headroom, DMA_TO_DEVICE); skbdesc->flags &= ~SKBDESC_DMA_MAPPED_TX; } diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.c b/drivers/net/wireless/wl12xx/wl1251_cmd.c index 770f260726b..0320b478bb3 100644 --- a/drivers/net/wireless/wl12xx/wl1251_cmd.c +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.c @@ -410,3 +410,86 @@ out: kfree(cmd); return ret; } + +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, + struct ieee80211_channel *channels[], + unsigned int n_channels, unsigned int n_probes) +{ + struct wl1251_cmd_scan *cmd; + int i, ret = 0; + + wl1251_debug(DEBUG_CMD, "cmd scan"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); + cmd->params.rx_filter_options = cpu_to_le32(CFG_RX_PRSP_EN | + CFG_RX_MGMT_EN | + CFG_RX_BCN_EN); + cmd->params.scan_options = 0; + cmd->params.num_channels = n_channels; + cmd->params.num_probe_requests = n_probes; + cmd->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ + cmd->params.tid_trigger = 0; + + for (i = 0; i < n_channels; i++) { + cmd->channels[i].min_duration = + cpu_to_le32(WL1251_SCAN_MIN_DURATION); + cmd->channels[i].max_duration = + cpu_to_le32(WL1251_SCAN_MAX_DURATION); + memset(&cmd->channels[i].bssid_lsb, 0xff, 4); + memset(&cmd->channels[i].bssid_msb, 0xff, 2); + cmd->channels[i].early_termination = 0; + cmd->channels[i].tx_power_att = 0; + cmd->channels[i].channel = channels[i]->hw_value; + } + + cmd->params.ssid_len = ssid_len; + if (ssid) + memcpy(cmd->params.ssid, ssid, ssid_len); + + ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("cmd scan failed: %d", ret); + goto out; + } + + wl1251_mem_read(wl, wl->cmd_box_addr, cmd, sizeof(*cmd)); + + if (cmd->header.status != CMD_STATUS_SUCCESS) { + wl1251_error("cmd scan status wasn't success: %d", + cmd->header.status); + ret = -EIO; + goto out; + } + +out: + kfree(cmd); + return ret; +} + +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout) +{ + struct wl1251_cmd_trigger_scan_to *cmd; + int ret; + + wl1251_debug(DEBUG_CMD, "cmd trigger scan to"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + cmd->timeout = timeout; + + ret = wl1251_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd)); + if (ret < 0) { + wl1251_error("cmd trigger scan to failed: %d", ret); + goto out; + } + +out: + kfree(cmd); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/wl1251_cmd.h b/drivers/net/wireless/wl12xx/wl1251_cmd.h index dff798ad0ef..4ad67cae94d 100644 --- a/drivers/net/wireless/wl12xx/wl1251_cmd.h +++ b/drivers/net/wireless/wl12xx/wl1251_cmd.h @@ -27,6 +27,8 @@ #include "wl1251.h" +#include <net/cfg80211.h> + struct acx_header; int wl1251_cmd_send(struct wl1251 *wl, u16 type, void *buf, size_t buf_len); @@ -43,6 +45,10 @@ int wl1251_cmd_read_memory(struct wl1251 *wl, u32 addr, void *answer, size_t len); int wl1251_cmd_template_set(struct wl1251 *wl, u16 cmd_id, void *buf, size_t buf_len); +int wl1251_cmd_scan(struct wl1251 *wl, u8 *ssid, size_t ssid_len, + struct ieee80211_channel *channels[], + unsigned int n_channels, unsigned int n_probes); +int wl1251_cmd_trigger_scan_to(struct wl1251 *wl, u32 timeout); /* unit ms */ #define WL1251_COMMAND_TIMEOUT 2000 @@ -163,8 +169,12 @@ struct cmd_read_write_memory { #define CMDMBOX_HEADER_LEN 4 #define CMDMBOX_INFO_ELEM_HEADER_LEN 4 +#define WL1251_SCAN_MIN_DURATION 30000 +#define WL1251_SCAN_MAX_DURATION 60000 + +#define WL1251_SCAN_NUM_PROBES 3 -struct basic_scan_parameters { +struct wl1251_scan_parameters { u32 rx_config_options; u32 rx_filter_options; @@ -189,11 +199,11 @@ struct basic_scan_parameters { u8 tid_trigger; u8 ssid_len; - u32 ssid[8]; + u8 ssid[32]; } __attribute__ ((packed)); -struct basic_scan_channel_parameters { +struct wl1251_scan_ch_parameters { u32 min_duration; /* in TU */ u32 max_duration; /* in TU */ u32 bssid_lsb; @@ -213,11 +223,11 @@ struct basic_scan_channel_parameters { /* SCAN parameters */ #define SCAN_MAX_NUM_OF_CHANNELS 16 -struct cmd_scan { +struct wl1251_cmd_scan { struct wl1251_cmd_header header; - struct basic_scan_parameters params; - struct basic_scan_channel_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; + struct wl1251_scan_parameters params; + struct wl1251_scan_ch_parameters channels[SCAN_MAX_NUM_OF_CHANNELS]; } __attribute__ ((packed)); enum { diff --git a/drivers/net/wireless/wl12xx/wl1251_main.c b/drivers/net/wireless/wl12xx/wl1251_main.c index 4e373f3dbc4..595f0f94d16 100644 --- a/drivers/net/wireless/wl12xx/wl1251_main.c +++ b/drivers/net/wireless/wl12xx/wl1251_main.c @@ -563,43 +563,25 @@ static void wl1251_op_remove_interface(struct ieee80211_hw *hw, mutex_unlock(&wl->mutex); } -static int wl1251_build_null_data(struct wl1251 *wl) +static int wl1251_build_qos_null_data(struct wl1251 *wl) { - struct wl12xx_null_data_template template; + struct ieee80211_qos_hdr template; - if (!is_zero_ether_addr(wl->bssid)) { - memcpy(template.header.da, wl->bssid, ETH_ALEN); - memcpy(template.header.bssid, wl->bssid, ETH_ALEN); - } else { - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - } - - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | - IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS); - - return wl1251_cmd_template_set(wl, CMD_NULL_DATA, &template, - sizeof(template)); - -} - -static int wl1251_build_ps_poll(struct wl1251 *wl, u16 aid) -{ - struct wl12xx_ps_poll_template template; + memset(&template, 0, sizeof(template)); - memcpy(template.bssid, wl->bssid, ETH_ALEN); - memcpy(template.ta, wl->mac_addr, ETH_ALEN); + memcpy(template.addr1, wl->bssid, ETH_ALEN); + memcpy(template.addr2, wl->mac_addr, ETH_ALEN); + memcpy(template.addr3, wl->bssid, ETH_ALEN); - /* aid in PS-Poll has its two MSBs each set to 1 */ - template.aid = cpu_to_le16(1 << 15 | 1 << 14 | aid); + template.frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC | + IEEE80211_FCTL_TODS); - template.fc = cpu_to_le16(IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL); + /* FIXME: not sure what priority to use here */ + template.qos_ctrl = cpu_to_le16(0); - return wl1251_cmd_template_set(wl, CMD_PS_POLL, &template, + return wl1251_cmd_template_set(wl, CMD_QOS_NULL_DATA, &template, sizeof(template)); - } static int wl1251_op_config(struct ieee80211_hw *hw, u32 changed) @@ -870,199 +852,61 @@ out: return ret; } -static int wl1251_build_basic_rates(char *rates) -{ - u8 index = 0; - - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_1MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_2MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_5MB; - rates[index++] = IEEE80211_BASIC_RATE_MASK | IEEE80211_CCK_RATE_11MB; - - return index; -} - -static int wl1251_build_extended_rates(char *rates) +static int wl1251_op_hw_scan(struct ieee80211_hw *hw, + struct cfg80211_scan_request *req) { - u8 index = 0; - - rates[index++] = IEEE80211_OFDM_RATE_6MB; - rates[index++] = IEEE80211_OFDM_RATE_9MB; - rates[index++] = IEEE80211_OFDM_RATE_12MB; - rates[index++] = IEEE80211_OFDM_RATE_18MB; - rates[index++] = IEEE80211_OFDM_RATE_24MB; - rates[index++] = IEEE80211_OFDM_RATE_36MB; - rates[index++] = IEEE80211_OFDM_RATE_48MB; - rates[index++] = IEEE80211_OFDM_RATE_54MB; - - return index; -} - + struct wl1251 *wl = hw->priv; + struct sk_buff *skb; + size_t ssid_len = 0; + u8 *ssid = NULL; + int ret; -static int wl1251_build_probe_req(struct wl1251 *wl, u8 *ssid, size_t ssid_len) -{ - struct wl12xx_probe_req_template template; - struct wl12xx_ie_rates *rates; - char *ptr; - u16 size; - - ptr = (char *)&template; - size = sizeof(struct ieee80211_header); - - memset(template.header.da, 0xff, ETH_ALEN); - memset(template.header.bssid, 0xff, ETH_ALEN); - memcpy(template.header.sa, wl->mac_addr, ETH_ALEN); - template.header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); - - /* IEs */ - /* SSID */ - template.ssid.header.id = WLAN_EID_SSID; - template.ssid.header.len = ssid_len; - if (ssid_len && ssid) - memcpy(template.ssid.ssid, ssid, ssid_len); - size += sizeof(struct wl12xx_ie_header) + ssid_len; - ptr += size; - - /* Basic Rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_SUPP_RATES; - rates->header.len = wl1251_build_basic_rates(rates->rates); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - ptr += sizeof(struct wl12xx_ie_header) + rates->header.len; - - /* Extended rates */ - rates = (struct wl12xx_ie_rates *)ptr; - rates->header.id = WLAN_EID_EXT_SUPP_RATES; - rates->header.len = wl1251_build_extended_rates(rates->rates); - size += sizeof(struct wl12xx_ie_header) + rates->header.len; - - wl1251_dump(DEBUG_SCAN, "PROBE REQ: ", &template, size); - - return wl1251_cmd_template_set(wl, CMD_PROBE_REQ, &template, - size); -} + wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); -static int wl1251_hw_scan(struct wl1251 *wl, u8 *ssid, size_t len, - u8 active_scan, u8 high_prio, u8 num_channels, - u8 probe_requests) -{ - struct wl1251_cmd_trigger_scan_to *trigger = NULL; - struct cmd_scan *params = NULL; - int i, ret; - u16 scan_options = 0; - - if (wl->scanning) - return -EINVAL; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) - return -ENOMEM; - - params->params.rx_config_options = cpu_to_le32(CFG_RX_ALL_GOOD); - params->params.rx_filter_options = - cpu_to_le32(CFG_RX_PRSP_EN | CFG_RX_MGMT_EN | CFG_RX_BCN_EN); - - /* High priority scan */ - if (!active_scan) - scan_options |= SCAN_PASSIVE; - if (high_prio) - scan_options |= SCAN_PRIORITY_HIGH; - params->params.scan_options = scan_options; - - params->params.num_channels = num_channels; - params->params.num_probe_requests = probe_requests; - params->params.tx_rate = cpu_to_le16(1 << 1); /* 2 Mbps */ - params->params.tid_trigger = 0; - - for (i = 0; i < num_channels; i++) { - params->channels[i].min_duration = cpu_to_le32(30000); - params->channels[i].max_duration = cpu_to_le32(60000); - memset(¶ms->channels[i].bssid_lsb, 0xff, 4); - memset(¶ms->channels[i].bssid_msb, 0xff, 2); - params->channels[i].early_termination = 0; - params->channels[i].tx_power_att = 0; - params->channels[i].channel = i + 1; - memset(params->channels[i].pad, 0, 3); + if (req->n_ssids) { + ssid = req->ssids[0].ssid; + ssid_len = req->ssids[0].ssid_len; } - for (i = num_channels; i < SCAN_MAX_NUM_OF_CHANNELS; i++) - memset(¶ms->channels[i], 0, - sizeof(struct basic_scan_channel_parameters)); - - if (len && ssid) { - params->params.ssid_len = len; - memcpy(params->params.ssid, ssid, len); - } else { - params->params.ssid_len = 0; - memset(params->params.ssid, 0, 32); - } + mutex_lock(&wl->mutex); - ret = wl1251_build_probe_req(wl, ssid, len); - if (ret < 0) { - wl1251_error("PROBE request template failed"); + if (wl->scanning) { + wl1251_debug(DEBUG_SCAN, "scan already in progress"); + ret = -EINVAL; goto out; } - trigger = kzalloc(sizeof(*trigger), GFP_KERNEL); - if (!trigger) + ret = wl1251_ps_elp_wakeup(wl); + if (ret < 0) goto out; - trigger->timeout = 0; - - ret = wl1251_cmd_send(wl, CMD_TRIGGER_SCAN_TO, trigger, - sizeof(*trigger)); - if (ret < 0) { - wl1251_error("trigger scan to failed for hw scan"); + skb = ieee80211_probereq_get(wl->hw, wl->vif, ssid, ssid_len, + req->ie, req->ie_len); + if (!skb) { + ret = -ENOMEM; goto out; } - wl1251_dump(DEBUG_SCAN, "SCAN: ", params, sizeof(*params)); - - wl->scanning = true; + ret = wl1251_cmd_template_set(wl, CMD_PROBE_REQ, skb->data, + skb->len); + dev_kfree_skb(skb); + if (ret < 0) + goto out_sleep; - ret = wl1251_cmd_send(wl, CMD_SCAN, params, sizeof(*params)); + ret = wl1251_cmd_trigger_scan_to(wl, 0); if (ret < 0) - wl1251_error("SCAN failed"); + goto out_sleep; - wl1251_mem_read(wl, wl->cmd_box_addr, params, sizeof(*params)); + wl->scanning = true; - if (params->header.status != CMD_STATUS_SUCCESS) { - wl1251_error("TEST command answer error: %d", - params->header.status); + ret = wl1251_cmd_scan(wl, ssid, ssid_len, req->channels, + req->n_channels, WL1251_SCAN_NUM_PROBES); + if (ret < 0) { wl->scanning = false; - ret = -EIO; - goto out; - } - -out: - kfree(params); - return ret; - -} - -static int wl1251_op_hw_scan(struct ieee80211_hw *hw, - struct cfg80211_scan_request *req) -{ - struct wl1251 *wl = hw->priv; - int ret; - u8 *ssid = NULL; - size_t ssid_len = 0; - - wl1251_debug(DEBUG_MAC80211, "mac80211 hw scan"); - - if (req->n_ssids) { - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; + goto out_sleep; } - mutex_lock(&wl->mutex); - - ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) - goto out; - - ret = wl1251_hw_scan(hw->priv, ssid, ssid_len, 1, 0, 13, 3); - +out_sleep: wl1251_ps_elp_sleep(wl); out: @@ -1101,7 +945,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, { enum wl1251_cmd_ps_mode mode; struct wl1251 *wl = hw->priv; - struct sk_buff *beacon; + struct sk_buff *beacon, *skb; int ret; wl1251_debug(DEBUG_MAC80211, "mac80211 bss info changed"); @@ -1115,7 +959,17 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_BSSID) { memcpy(wl->bssid, bss_conf->bssid, ETH_ALEN); - ret = wl1251_build_null_data(wl); + skb = ieee80211_nullfunc_get(wl->hw, wl->vif); + if (!skb) + goto out_sleep; + + ret = wl1251_cmd_template_set(wl, CMD_NULL_DATA, + skb->data, skb->len); + dev_kfree_skb(skb); + if (ret < 0) + goto out_sleep; + + ret = wl1251_build_qos_null_data(wl); if (ret < 0) goto out; @@ -1136,7 +990,14 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, wl->dtim_period); wl->aid = bss_conf->aid; - ret = wl1251_build_ps_poll(wl, wl->aid); + skb = ieee80211_pspoll_get(wl->hw, wl->vif); + if (!skb) + goto out_sleep; + + ret = wl1251_cmd_template_set(wl, CMD_PS_POLL, + skb->data, + skb->len); + dev_kfree_skb(skb); if (ret < 0) goto out_sleep; @@ -1182,7 +1043,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, ret = wl1251_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { wl1251_warning("Set ctsprotect failed %d", ret); - goto out; + goto out_sleep; } } @@ -1193,7 +1054,7 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, if (ret < 0) { dev_kfree_skb(beacon); - goto out; + goto out_sleep; } ret = wl1251_cmd_template_set(wl, CMD_PROBE_RESP, beacon->data, @@ -1202,13 +1063,13 @@ static void wl1251_op_bss_info_changed(struct ieee80211_hw *hw, dev_kfree_skb(beacon); if (ret < 0) - goto out; + goto out_sleep; ret = wl1251_join(wl, wl->bss_type, wl->beacon_int, wl->channel, wl->dtim_period); if (ret < 0) - goto out; + goto out_sleep; } out_sleep: @@ -1282,6 +1143,7 @@ static struct ieee80211_channel wl1251_channels[] = { static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, const struct ieee80211_tx_queue_params *params) { + enum wl1251_acx_ps_scheme ps_scheme; struct wl1251 *wl = hw->priv; int ret; @@ -1299,10 +1161,14 @@ static int wl1251_op_conf_tx(struct ieee80211_hw *hw, u16 queue, if (ret < 0) goto out_sleep; + if (params->uapsd) + ps_scheme = WL1251_ACX_PS_SCHEME_UPSD_TRIGGER; + else + ps_scheme = WL1251_ACX_PS_SCHEME_LEGACY; + ret = wl1251_acx_tid_cfg(wl, wl1251_tx_get_queue(queue), CHANNEL_TYPE_EDCF, - wl1251_tx_get_queue(queue), - WL1251_ACX_PS_SCHEME_LEGACY, + wl1251_tx_get_queue(queue), ps_scheme, WL1251_ACX_ACK_POLICY_LEGACY); if (ret < 0) goto out_sleep; @@ -1376,7 +1242,8 @@ int wl1251_init_ieee80211(struct wl1251 *wl) wl->hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_NOISE_DBM | IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_BEACON_FILTER; + IEEE80211_HW_BEACON_FILTER | + IEEE80211_HW_SUPPORTS_UAPSD; wl->hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION); wl->hw->wiphy->max_scan_ssids = 1; diff --git a/drivers/net/wireless/wl12xx/wl1271_reg.h b/drivers/net/wireless/wl12xx/wl1271_reg.h index 1f237389d1c..99096077152 100644 --- a/drivers/net/wireless/wl12xx/wl1271_reg.h +++ b/drivers/net/wireless/wl12xx/wl1271_reg.h @@ -62,73 +62,10 @@ #define WL1271_SLV_REG_DATA (REGISTERS_BASE + 0x0008) #define WL1271_SLV_REG_ADATA (REGISTERS_BASE + 0x000c) #define WL1271_SLV_MEM_DATA (REGISTERS_BASE + 0x0018) -/* - * Interrupt registers. - * 64 bit interrupt sources registers ws ced. - * sme interupts were removed and new ones were added. - * Order was changed. - */ -#define FIQ_MASK (REGISTERS_BASE + 0x0400) -#define FIQ_MASK_L (REGISTERS_BASE + 0x0400) -#define FIQ_MASK_H (REGISTERS_BASE + 0x0404) -#define FIQ_MASK_SET (REGISTERS_BASE + 0x0408) -#define FIQ_MASK_SET_L (REGISTERS_BASE + 0x0408) -#define FIQ_MASK_SET_H (REGISTERS_BASE + 0x040C) -#define FIQ_MASK_CLR (REGISTERS_BASE + 0x0410) -#define FIQ_MASK_CLR_L (REGISTERS_BASE + 0x0410) -#define FIQ_MASK_CLR_H (REGISTERS_BASE + 0x0414) -#define IRQ_MASK (REGISTERS_BASE + 0x0418) -#define IRQ_MASK_L (REGISTERS_BASE + 0x0418) -#define IRQ_MASK_H (REGISTERS_BASE + 0x041C) -#define IRQ_MASK_SET (REGISTERS_BASE + 0x0420) -#define IRQ_MASK_SET_L (REGISTERS_BASE + 0x0420) -#define IRQ_MASK_SET_H (REGISTERS_BASE + 0x0424) -#define IRQ_MASK_CLR (REGISTERS_BASE + 0x0428) -#define IRQ_MASK_CLR_L (REGISTERS_BASE + 0x0428) -#define IRQ_MASK_CLR_H (REGISTERS_BASE + 0x042C) -#define ECPU_MASK (REGISTERS_BASE + 0x0448) -#define FIQ_STS_L (REGISTERS_BASE + 0x044C) -#define FIQ_STS_H (REGISTERS_BASE + 0x0450) -#define IRQ_STS_L (REGISTERS_BASE + 0x0454) -#define IRQ_STS_H (REGISTERS_BASE + 0x0458) -#define INT_STS_ND (REGISTERS_BASE + 0x0464) -#define INT_STS_RAW_L (REGISTERS_BASE + 0x0464) -#define INT_STS_RAW_H (REGISTERS_BASE + 0x0468) -#define INT_STS_CLR (REGISTERS_BASE + 0x04B4) -#define INT_STS_CLR_L (REGISTERS_BASE + 0x04B4) -#define INT_STS_CLR_H (REGISTERS_BASE + 0x04B8) -#define INT_ACK (REGISTERS_BASE + 0x046C) -#define INT_ACK_L (REGISTERS_BASE + 0x046C) -#define INT_ACK_H (REGISTERS_BASE + 0x0470) -#define INT_TRIG (REGISTERS_BASE + 0x0474) -#define INT_TRIG_L (REGISTERS_BASE + 0x0474) -#define INT_TRIG_H (REGISTERS_BASE + 0x0478) -#define HOST_STS_L (REGISTERS_BASE + 0x045C) -#define HOST_STS_H (REGISTERS_BASE + 0x0460) -#define HOST_MASK (REGISTERS_BASE + 0x0430) -#define HOST_MASK_L (REGISTERS_BASE + 0x0430) -#define HOST_MASK_H (REGISTERS_BASE + 0x0434) -#define HOST_MASK_SET (REGISTERS_BASE + 0x0438) -#define HOST_MASK_SET_L (REGISTERS_BASE + 0x0438) -#define HOST_MASK_SET_H (REGISTERS_BASE + 0x043C) -#define HOST_MASK_CLR (REGISTERS_BASE + 0x0440) -#define HOST_MASK_CLR_L (REGISTERS_BASE + 0x0440) -#define HOST_MASK_CLR_H (REGISTERS_BASE + 0x0444) #define ACX_REG_INTERRUPT_TRIG (REGISTERS_BASE + 0x0474) #define ACX_REG_INTERRUPT_TRIG_H (REGISTERS_BASE + 0x0478) -/* Host Interrupts*/ -#define HINT_MASK (REGISTERS_BASE + 0x0494) -#define HINT_MASK_SET (REGISTERS_BASE + 0x0498) -#define HINT_MASK_CLR (REGISTERS_BASE + 0x049C) -#define HINT_STS_ND_MASKED (REGISTERS_BASE + 0x04A0) -/*1150 spec calls this HINT_STS_RAW*/ -#define HINT_STS_ND (REGISTERS_BASE + 0x04B0) -#define HINT_STS_CLR (REGISTERS_BASE + 0x04A4) -#define HINT_ACK (REGISTERS_BASE + 0x04A8) -#define HINT_TRIG (REGISTERS_BASE + 0x04AC) - /*============================================= Host Interrupt Mask Register - 32bit (RW) ------------------------------------------ @@ -433,16 +370,6 @@ /*=============================================== - Phy regs - ===============================================*/ -#define ACX_PHY_ADDR_REG SBB_ADDR -#define ACX_PHY_DATA_REG SBB_DATA -#define ACX_PHY_CTRL_REG SBB_CTL -#define ACX_PHY_REG_WR_MASK 0x00000001ul -#define ACX_PHY_REG_RD_MASK 0x00000002ul - - -/*=============================================== EEPROM Read/Write Request 32bit RW ------------------------------------------ 1 EE_READ - EEPROM Read Request 1 - Setting this bit @@ -511,28 +438,6 @@ #define ACX_CONT_WIND_MIN_MASK 0x0000007f #define ACX_CONT_WIND_MAX 0x03ff0000 -/* - * Indirect slave register/memory registers - * ---------------------------------------- - */ -#define HW_SLAVE_REG_ADDR_REG 0x00000004 -#define HW_SLAVE_REG_DATA_REG 0x00000008 -#define HW_SLAVE_REG_CTRL_REG 0x0000000c - -#define SLAVE_AUTO_INC 0x00010000 -#define SLAVE_NO_AUTO_INC 0x00000000 -#define SLAVE_HOST_LITTLE_ENDIAN 0x00000000 - -#define HW_SLAVE_MEM_ADDR_REG SLV_MEM_ADDR -#define HW_SLAVE_MEM_DATA_REG SLV_MEM_DATA -#define HW_SLAVE_MEM_CTRL_REG SLV_MEM_CTL -#define HW_SLAVE_MEM_ENDIAN_REG SLV_END_CTL - -#define HW_FUNC_EVENT_INT_EN 0x8000 -#define HW_FUNC_EVENT_MASK_REG 0x00000034 - -#define ACX_MAC_TIMESTAMP_REG (MAC_TIMESTAMP) - /*=============================================== HI_CFG Interface Configuration Register Values ------------------------------------------ @@ -647,10 +552,6 @@ b12-b0 - Supported Rate indicator bits as defined below. ******************************************************************************/ -#define TNETW1251_CHIP_ID_PG1_0 0x07010101 -#define TNETW1251_CHIP_ID_PG1_1 0x07020101 -#define TNETW1251_CHIP_ID_PG1_2 0x07030101 - /************************************************************************* Interrupt Trigger Register (Host -> WiLink) diff --git a/drivers/net/wireless/wl12xx/wl1271_spi.c b/drivers/net/wireless/wl12xx/wl1271_spi.c index 02978a16e73..ee9564aa6ec 100644 --- a/drivers/net/wireless/wl12xx/wl1271_spi.c +++ b/drivers/net/wireless/wl12xx/wl1271_spi.c @@ -397,8 +397,7 @@ u16 wl1271_top_reg_read(struct wl1271 *wl, int addr) /* poll for data ready */ do { val = wl1271_spi_read32(wl, OCP_DATA_READ); - timeout--; - } while (!(val & OCP_READY_MASK) && timeout); + } while (!(val & OCP_READY_MASK) && --timeout); if (!timeout) { wl1271_warning("Top register access timed out."); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 709fe5e06f7..2d555cc3050 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -987,12 +987,13 @@ static void zd_op_configure_filter(struct ieee80211_hw *hw, changed_flags &= SUPPORTED_FIF_FLAGS; *new_flags &= SUPPORTED_FIF_FLAGS; - /* changed_flags is always populated but this driver - * doesn't support all FIF flags so its possible we don't - * need to do anything */ - if (!changed_flags) - return; - + /* + * If multicast parameter (as returned by zd_op_prepare_multicast) + * has changed, no bit in changed_flags is set. To handle this + * situation, we do not return if changed_flags is 0. If we do so, + * we will have some issue with IPv6 which uses multicast for link + * layer address resolution. + */ if (*new_flags & (FIF_PROMISC_IN_BSS | FIF_ALLMULTI)) zd_mc_add_all(&hash); diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211.h b/drivers/staging/rtl8187se/ieee80211/ieee80211.h index 0d490c164db..9086047c32d 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211.h +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211.h @@ -482,15 +482,6 @@ struct ieee80211_header_data { u16 seq_ctrl; }; -struct ieee80211_hdr_3addr { - u16 frame_ctl; - u16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - u16 seq_ctl; -} __attribute__ ((packed)); - struct ieee80211_hdr_4addr { u16 frame_ctl; u16 duration_id; diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c index c7c645af0eb..a2150670ef5 100644 --- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c +++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c @@ -203,7 +203,7 @@ inline void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee enqueue_mgmt(ieee,skb); }else{ - header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0]<<4); + header->seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0]<<4); if (ieee->seq_ctrl[0] == 0xFFF) ieee->seq_ctrl[0] = 0; @@ -220,7 +220,7 @@ inline void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee spin_unlock_irqrestore(&ieee->lock, flags); spin_lock_irqsave(&ieee->mgmt_tx_lock, flags); - header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + header->seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0] << 4); if (ieee->seq_ctrl[0] == 0xFFF) ieee->seq_ctrl[0] = 0; @@ -246,7 +246,7 @@ inline void softmac_ps_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *i if(single){ - header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + header->seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0] << 4); if (ieee->seq_ctrl[0] == 0xFFF) ieee->seq_ctrl[0] = 0; @@ -259,7 +259,7 @@ inline void softmac_ps_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *i }else{ - header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + header->seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0] << 4); if (ieee->seq_ctrl[0] == 0xFFF) ieee->seq_ctrl[0] = 0; @@ -287,7 +287,7 @@ inline struct sk_buff *ieee80211_disassociate_skb( return NULL; disass = (struct ieee80211_disassoc_frame *) skb_put(skb,sizeof(struct ieee80211_disassoc_frame)); - disass->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_DISASSOC); + disass->header.frame_control = cpu_to_le16(IEEE80211_STYPE_DISASSOC); disass->header.duration_id = 0; memcpy(disass->header.addr1, beacon->bssid, ETH_ALEN); @@ -905,7 +905,7 @@ struct sk_buff* ieee80211_assoc_resp(struct ieee80211_device *ieee, u8 *dest) assoc = (struct ieee80211_assoc_response_frame *) skb_put(skb,sizeof(struct ieee80211_assoc_response_frame)); - assoc->header.frame_ctl = cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP); + assoc->header.frame_control = cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP); memcpy(assoc->header.addr1, dest,ETH_ALEN); memcpy(assoc->header.addr3, ieee->dev->dev_addr, ETH_ALEN); memcpy(assoc->header.addr2, ieee->dev->dev_addr, ETH_ALEN); @@ -981,7 +981,7 @@ struct sk_buff* ieee80211_null_func(struct ieee80211_device *ieee,short pwr) memcpy(hdr->addr2, ieee->dev->dev_addr, ETH_ALEN); memcpy(hdr->addr3, ieee->current_network.bssid, ETH_ALEN); - hdr->frame_ctl = cpu_to_le16(IEEE80211_FTYPE_DATA | + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | IEEE80211_FCTL_TODS | (pwr ? IEEE80211_FCTL_PM:0)); @@ -1084,7 +1084,7 @@ inline struct sk_buff *ieee80211_association_req(struct ieee80211_network *beaco skb_put(skb, sizeof(struct ieee80211_assoc_request_frame)); - hdr->header.frame_ctl = IEEE80211_STYPE_ASSOC_REQ; + hdr->header.frame_control = IEEE80211_STYPE_ASSOC_REQ; hdr->header.duration_id= 37; //FIXME memcpy(hdr->header.addr1, beacon->bssid, ETH_ALEN); memcpy(hdr->header.addr2, ieee->dev->dev_addr, ETH_ALEN); @@ -1786,11 +1786,11 @@ ieee80211_rx_frame_softmac(struct ieee80211_device *ieee, struct sk_buff *skb, tasklet_schedule(&ieee->ps_task); - if(WLAN_FC_GET_STYPE(header->frame_ctl) != IEEE80211_STYPE_PROBE_RESP && - WLAN_FC_GET_STYPE(header->frame_ctl) != IEEE80211_STYPE_BEACON) + if (WLAN_FC_GET_STYPE(header->frame_control) != IEEE80211_STYPE_PROBE_RESP && + WLAN_FC_GET_STYPE(header->frame_control) != IEEE80211_STYPE_BEACON) ieee->last_rx_ps_time = jiffies; - switch (WLAN_FC_GET_STYPE(header->frame_ctl)) { + switch (WLAN_FC_GET_STYPE(header->frame_control)) { case IEEE80211_STYPE_ASSOC_RESP: case IEEE80211_STYPE_REASSOC_RESP: @@ -2064,7 +2064,7 @@ void ieee80211_rtl_wake_queue(struct ieee80211_device *ieee) header = (struct ieee80211_hdr_3addr *) skb->data; - header->seq_ctl = cpu_to_le16(ieee->seq_ctrl[0] << 4); + header->seq_ctrl = cpu_to_le16(ieee->seq_ctrl[0] << 4); if (ieee->seq_ctrl[0] == 0xFFF) ieee->seq_ctrl[0] = 0; diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c index e0f13efdb15..1847f38b9f2 100644 --- a/drivers/staging/rtl8187se/r8180_core.c +++ b/drivers/staging/rtl8187se/r8180_core.c @@ -1890,7 +1890,7 @@ rate) struct r8180_priv *priv = (struct r8180_priv *)ieee80211_priv(dev); int mode; struct ieee80211_hdr_3addr *h = (struct ieee80211_hdr_3addr *) skb->data; - short morefrag = (h->frame_ctl) & IEEE80211_FCTL_MOREFRAGS; + short morefrag = (h->frame_control) & IEEE80211_FCTL_MOREFRAGS; unsigned long flags; int priority; @@ -2158,7 +2158,7 @@ short rtl8180_tx(struct net_device *dev, u8* txbuf, int len, int priority, TxDescDuration = ThisFrameTime + aSifsTime + AckTime; } - if(!(frag_hdr->frame_ctl & IEEE80211_FCTL_MOREFRAGS)) { //no more fragment + if (!(frag_hdr->frame_control & IEEE80211_FCTL_MOREFRAGS)) { // ThisFrame-ACK. Duration = aSifsTime + AckTime; } else { // One or more fragments remained. diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index aeea282bd2f..842701906ae 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -120,6 +120,24 @@ #define IEEE80211_QOS_CTL_TID_MASK 0x000F #define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 +/* U-APSD queue for WMM IEs sent by AP */ +#define IEEE80211_WMM_IE_AP_QOSINFO_UAPSD (1<<7) + +/* U-APSD queues for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VO (1<<0) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_VI (1<<1) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BK (1<<2) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_BE (1<<3) +#define IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK 0x0f + +/* U-APSD max SP length for WMM IEs sent by STA */ +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL 0x00 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_2 0x01 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_4 0x02 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_6 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK 0x03 +#define IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT 5 + struct ieee80211_hdr { __le16 frame_control; __le16 duration_id; @@ -130,6 +148,25 @@ struct ieee80211_hdr { u8 addr4[6]; } __attribute__ ((packed)); +struct ieee80211_hdr_3addr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; +} __attribute__ ((packed)); + +struct ieee80211_qos_hdr { + __le16 frame_control; + __le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + __le16 seq_ctrl; + __le16 qos_ctrl; +} __attribute__ ((packed)); + /** * ieee80211_has_tods - check if IEEE80211_FCTL_TODS is set * @fc: frame control bytes in little-endian byteorder diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 2bfbe88837e..127a7301576 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -295,6 +295,10 @@ * This command is also used as an event to notify when a requested * remain-on-channel duration has expired. * + * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX + * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface + * and @NL80211_ATTR_TX_RATES the set of allowed rates. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -381,6 +385,8 @@ enum nl80211_commands { NL80211_CMD_REMAIN_ON_CHANNEL, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + NL80211_CMD_SET_TX_BITRATE_MASK, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -430,6 +436,8 @@ enum nl80211_commands { * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length * larger than or equal to this use RTS/CTS handshake); allowed range: * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 + * section 7.3.2.9; dot11CoverageClass; u8 * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name @@ -638,6 +646,13 @@ enum nl80211_commands { * * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. * + * @NL80211_ATTR_TX_RATES: Nested set of attributes + * (enum nl80211_tx_rate_attributes) describing TX rates per band. The + * enum nl80211_band value is used as the index (nla_type() of the nested + * data. If a band is not included, it will be configured to allow all + * rates based on negotiated supported rates information. This attribute + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -779,6 +794,10 @@ enum nl80211_attrs { NL80211_ATTR_COOKIE, + NL80211_ATTR_WIPHY_COVERAGE_CLASS, + + NL80211_ATTR_TX_RATES, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1359,13 +1378,20 @@ enum nl80211_channel_type { * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the - * raw information elements from the probe response/beacon (bin) + * raw information elements from the probe response/beacon (bin); + * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are + * from a Probe Response frame; otherwise they are from a Beacon frame. + * However, if the driver does not indicate the source of the IEs, these + * IEs may be from either frame subtype. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon * in unspecified units, scaled to 0..100 (u8) * @NL80211_BSS_STATUS: status, if this BSS is "used" * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information + * elements from a Beacon frame (bin); not present if no Beacon frame has + * yet been received * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -1381,6 +1407,7 @@ enum nl80211_bss { NL80211_BSS_SIGNAL_UNSPEC, NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, + NL80211_BSS_BEACON_IES, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -1478,4 +1505,33 @@ enum nl80211_key_attributes { NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 }; +/** + * enum nl80211_tx_rate_attributes - TX rate set attributes + * @__NL80211_TXRATE_INVALID: invalid + * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection + * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with + * 1 = 500 kbps) but without the IE length restriction (at most + * %NL80211_MAX_SUPP_RATES in a single array). + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ - 2.4 GHz ISM band + * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz) + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index add79930f47..2af52704e67 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -39,8 +39,8 @@ * @IEEE80211_BAND_5GHZ: around 5GHz band (4.9-5.7) */ enum ieee80211_band { - IEEE80211_BAND_2GHZ, - IEEE80211_BAND_5GHZ, + IEEE80211_BAND_2GHZ = NL80211_BAND_2GHZ, + IEEE80211_BAND_5GHZ = NL80211_BAND_5GHZ, /* keep last */ IEEE80211_NUM_BANDS @@ -626,8 +626,14 @@ enum cfg80211_signal_type { * @beacon_interval: the beacon interval as from the frame * @capability: the capability field in host byte order * @information_elements: the information elements (Note that there - * is no guarantee that these are well-formed!) + * is no guarantee that these are well-formed!); this is a pointer to + * either the beacon_ies or proberesp_ies depending on whether Probe + * Response frame has been received * @len_information_elements: total length of the information elements + * @beacon_ies: the information elements from the last Beacon frame + * @len_beacon_ies: total length of the beacon_ies + * @proberesp_ies: the information elements from the last Probe Response frame + * @len_proberesp_ies: total length of the proberesp_ies * @signal: signal strength value (type depends on the wiphy's signal_type) * @free_priv: function pointer to free private data * @priv: private area for driver use, has at least wiphy->bss_priv_size bytes @@ -641,6 +647,10 @@ struct cfg80211_bss { u16 capability; u8 *information_elements; size_t len_information_elements; + u8 *beacon_ies; + size_t len_beacon_ies; + u8 *proberesp_ies; + size_t len_proberesp_ies; s32 signal; @@ -837,6 +847,7 @@ enum wiphy_params_flags { WIPHY_PARAM_RETRY_LONG = 1 << 1, WIPHY_PARAM_FRAG_THRESHOLD = 1 << 2, WIPHY_PARAM_RTS_THRESHOLD = 1 << 3, + WIPHY_PARAM_COVERAGE_CLASS = 1 << 4, }; /** @@ -856,20 +867,11 @@ enum tx_power_setting { * cfg80211_bitrate_mask - masks for bitrate control */ struct cfg80211_bitrate_mask { -/* - * As discussed in Berlin, this struct really - * should look like this: - struct { u32 legacy; - u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; + /* TODO: add support for masking MCS rates; e.g.: */ + /* u8 mcs[IEEE80211_HT_MCS_MASK_LEN]; */ } control[IEEE80211_NUM_BANDS]; - - * Since we can always fix in-kernel users, let's keep - * it simpler for now: - */ - u32 fixed; /* fixed bitrate, 0 == not fixed */ - u32 maxrate; /* in kbps, 0 == no limit */ }; /** * struct cfg80211_pmksa - PMK Security Association @@ -1236,6 +1238,7 @@ struct wiphy { u8 retry_long; u32 frag_threshold; u32 rts_threshold; + u8 coverage_class; char fw_version[ETHTOOL_BUSINFO_LEN]; u32 hw_version; diff --git a/include/net/mac80211.h b/include/net/mac80211.h index f073a2a5057..c90047de442 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -107,12 +107,14 @@ enum ieee80211_max_queues { * 2^n-1 in the range 1..32767] * @cw_max: maximum contention window [like @cw_min] * @txop: maximum burst time in units of 32 usecs, 0 meaning disabled + * @uapsd: is U-APSD mode enabled for the queue */ struct ieee80211_tx_queue_params { u16 txop; u16 cw_min; u16 cw_max; u8 aifs; + bool uapsd; }; /** @@ -255,9 +257,6 @@ struct ieee80211_bss_conf { * @IEEE80211_TX_CTL_RATE_CTRL_PROBE: internal to mac80211, can be * set by rate control algorithms to indicate probe rate, will * be cleared for fragmented frames (except on the last fragment) - * @IEEE80211_TX_INTFL_RCALGO: mac80211 internal flag, do not test or - * set this flag in the driver; indicates that the rate control - * algorithm was used and should be notified of TX status * @IEEE80211_TX_INTFL_NEED_TXPROCESSING: completely internal to mac80211, * used to indicate that a pending frame requires TX processing before * it can be sent out. @@ -287,7 +286,6 @@ enum mac80211_tx_control_flags { IEEE80211_TX_STAT_AMPDU = BIT(10), IEEE80211_TX_STAT_AMPDU_NO_BACK = BIT(11), IEEE80211_TX_CTL_RATE_CTRL_PROBE = BIT(12), - IEEE80211_TX_INTFL_RCALGO = BIT(13), IEEE80211_TX_INTFL_NEED_TXPROCESSING = BIT(14), IEEE80211_TX_INTFL_RETRIED = BIT(15), IEEE80211_TX_INTFL_DONT_ENCRYPT = BIT(16), @@ -571,7 +569,13 @@ struct ieee80211_rx_status { * @IEEE80211_CONF_MONITOR: there's a monitor interface present -- use this * to determine for example whether to calculate timestamps for packets * or not, do not use instead of filter flags! - * @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only) + * @IEEE80211_CONF_PS: Enable 802.11 power save mode (managed mode only). + * This is the power save mode defined by IEEE 802.11-2007 section 11.2, + * meaning that the hardware still wakes up for beacons, is able to + * transmit frames and receive the possible acknowledgment frames. + * Not to be confused with hardware specific wakeup/sleep states, + * driver is responsible for that. See the section "Powersave support" + * for more. * @IEEE80211_CONF_IDLE: The device is running, but idle; if the flag is set * the driver should be prepared to handle configuration requests but * may turn the device off as much as possible. Typically, this flag will @@ -611,7 +615,11 @@ enum ieee80211_conf_changed { /** * enum ieee80211_smps_mode - spatial multiplexing power save mode * - * @ + * @IEEE80211_SMPS_AUTOMATIC: automatic + * @IEEE80211_SMPS_OFF: off + * @IEEE80211_SMPS_STATIC: static + * @IEEE80211_SMPS_DYNAMIC: dynamic + * @IEEE80211_SMPS_NUM_MODES: internal, don't use */ enum ieee80211_smps_mode { IEEE80211_SMPS_AUTOMATIC, @@ -933,6 +941,11 @@ enum ieee80211_tkip_key_type { * Hardware supports dynamic spatial multiplexing powersave, * ie. can turn off all but one chain and then wake the rest * up as required after, for example, rts/cts handshake. + * + * @IEEE80211_HW_SUPPORTS_UAPSD: + * Hardware supports Unscheduled Automatic Power Save Delivery + * (U-APSD) in managed mode. The mode is configured with + * conf_tx() operation. */ enum ieee80211_hw_flags { IEEE80211_HW_HAS_RATE_CONTROL = 1<<0, @@ -952,6 +965,7 @@ enum ieee80211_hw_flags { IEEE80211_HW_BEACON_FILTER = 1<<14, IEEE80211_HW_SUPPORTS_STATIC_SMPS = 1<<15, IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16, + IEEE80211_HW_SUPPORTS_UAPSD = 1<<17, }; /** @@ -1130,18 +1144,24 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * * mac80211 has support for various powersave implementations. * - * First, it can support hardware that handles all powersaving by - * itself, such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS - * hardware flag. In that case, it will be told about the desired - * powersave mode depending on the association status, and the driver - * must take care of sending nullfunc frames when necessary, i.e. when - * entering and leaving powersave mode. The driver is required to look at - * the AID in beacons and signal to the AP that it woke up when it finds - * traffic directed to it. This mode supports dynamic PS by simply - * enabling/disabling PS. - * - * Additionally, such hardware may set the %IEEE80211_HW_SUPPORTS_DYNAMIC_PS - * flag to indicate that it can support dynamic PS mode itself (see below). + * First, it can support hardware that handles all powersaving by itself, + * such hardware should simply set the %IEEE80211_HW_SUPPORTS_PS hardware + * flag. In that case, it will be told about the desired powersave mode + * with the %IEEE80211_CONF_PS flag depending on the association status. + * The hardware must take care of sending nullfunc frames when necessary, + * i.e. when entering and leaving powersave mode. The hardware is required + * to look at the AID in beacons and signal to the AP that it woke up when + * it finds traffic directed to it. + * + * %IEEE80211_CONF_PS flag enabled means that the powersave mode defined in + * IEEE 802.11-2007 section 11.2 is enabled. This is not to be confused + * with hardware wakeup and sleep states. Driver is responsible for waking + * up the hardware before issueing commands to the hardware and putting it + * back to sleep at approriate times. + * + * When PS is enabled, hardware needs to wakeup for beacons and receive the + * buffered multicast/broadcast frames after the beacon. Also it must be + * possible to send frames and receive the acknowledment frame. * * Other hardware designs cannot send nullfunc frames by themselves and also * need software support for parsing the TIM bitmap. This is also supported @@ -1149,14 +1169,35 @@ ieee80211_get_alt_retry_rate(const struct ieee80211_hw *hw, * %IEEE80211_HW_PS_NULLFUNC_STACK flags. The hardware is of course still * required to pass up beacons. The hardware is still required to handle * waking up for multicast traffic; if it cannot the driver must handle that - * as best as it can, mac80211 is too slow. - * - * Dynamic powersave mode is an extension to normal powersave mode in which - * the hardware stays awake for a user-specified period of time after sending - * a frame so that reply frames need not be buffered and therefore delayed - * to the next wakeup. This can either be supported by hardware, in which case - * the driver needs to look at the @dynamic_ps_timeout hardware configuration - * value, or by the stack if all nullfunc handling is in the stack. + * as best as it can, mac80211 is too slow to do that. + * + * Dynamic powersave is an extension to normal powersave in which the + * hardware stays awake for a user-specified period of time after sending a + * frame so that reply frames need not be buffered and therefore delayed to + * the next wakeup. It's compromise of getting good enough latency when + * there's data traffic and still saving significantly power in idle + * periods. + * + * Dynamic powersave is supported by simply mac80211 enabling and disabling + * PS based on traffic. Driver needs to only set %IEEE80211_HW_SUPPORTS_PS + * flag and mac80211 will handle everything automatically. Additionally, + * hardware having support for the dynamic PS feature may set the + * %IEEE80211_HW_SUPPORTS_DYNAMIC_PS flag to indicate that it can support + * dynamic PS mode itself. The driver needs to look at the + * @dynamic_ps_timeout hardware configuration value and use it that value + * whenever %IEEE80211_CONF_PS is set. In this case mac80211 will disable + * dynamic PS feature in stack and will just keep %IEEE80211_CONF_PS + * enabled whenever user has enabled powersave. + * + * Driver informs U-APSD client support by enabling + * %IEEE80211_HW_SUPPORTS_UAPSD flag. The mode is configured through the + * uapsd paramater in conf_tx() operation. Hardware needs to send the QoS + * Nullfunc frames and stay awake until the service period has ended. To + * utilize U-APSD, dynamic powersave is disabled for voip AC and all frames + * from that AC are transmitted with powersave enabled. + * + * Note: U-APSD client mode is not yet supported with + * %IEEE80211_HW_PS_NULLFUNC_STACK. */ /** @@ -1533,6 +1574,10 @@ enum ieee80211_ampdu_mlme_action { * and need to call wiphy_rfkill_set_hw_state() in the callback. * The callback can sleep. * + * @set_coverage_class: Set slot time for given coverage class as specified + * in IEEE 802.11-2007 section 17.3.8.6 and modify ACK timeout + * accordingly. This callback is not required and may sleep. + * * @testmode_cmd: Implement a cfg80211 test mode command. * The callback can sleep. * @@ -1592,6 +1637,7 @@ struct ieee80211_ops { struct ieee80211_sta *sta, u16 tid, u16 *ssn); void (*rfkill_poll)(struct ieee80211_hw *hw); + void (*set_coverage_class)(struct ieee80211_hw *hw, u8 coverage_class); #ifdef CONFIG_NL80211_TESTMODE int (*testmode_cmd)(struct ieee80211_hw *hw, void *data, int len); #endif @@ -1874,6 +1920,53 @@ static inline struct sk_buff *ieee80211_beacon_get(struct ieee80211_hw *hw, } /** + * ieee80211_pspoll_get - retrieve a PS Poll template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a PS Poll a template which can, for example, uploaded to + * hardware. The template must be updated after association so that correct + * AID, BSSID and MAC address is used. + * + * Note: Caller (or hardware) is responsible for setting the + * &IEEE80211_FCTL_PM bit. + */ +struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** + * ieee80211_nullfunc_get - retrieve a nullfunc template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * + * Creates a Nullfunc template which can, for example, uploaded to + * hardware. The template must be updated after association so that correct + * BSSID and address is used. + * + * Note: Caller (or hardware) is responsible for setting the + * &IEEE80211_FCTL_PM bit as well as Duration and Sequence Control fields. + */ +struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +/** + * ieee80211_probereq_get - retrieve a Probe Request template + * @hw: pointer obtained from ieee80211_alloc_hw(). + * @vif: &struct ieee80211_vif pointer from the add_interface callback. + * @ssid: SSID buffer + * @ssid_len: length of SSID + * @ie: buffer containing all IEs except SSID for the template + * @ie_len: length of the IE buffer + * + * Creates a Probe Request template which can, for example, be uploaded to + * hardware. + */ +struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len); + +/** * ieee80211_rts_get - RTS frame generation function * @hw: pointer obtained from ieee80211_alloc_hw(). * @vif: &struct ieee80211_vif pointer from the add_interface callback. @@ -2292,8 +2385,12 @@ enum rate_control_changed { * @short_preamble: whether mac80211 will request short-preamble transmission * if the selected rate supports it * @max_rate_idx: user-requested maximum rate (not MCS for now) + * (deprecated; this will be removed once drivers get updated to use + * rate_idx_mask) + * @rate_idx_mask: user-requested rate mask (not MCS for now) * @skb: the skb that will be transmitted, the control information in it needs * to be filled in + * @ap: whether this frame is sent out in AP mode */ struct ieee80211_tx_rate_control { struct ieee80211_hw *hw; @@ -2303,6 +2400,8 @@ struct ieee80211_tx_rate_control { struct ieee80211_tx_rate reported_rate; bool rts, short_preamble; u8 max_rate_idx; + u32 rate_idx_mask; + bool ap; }; struct rate_control_ops { diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index ceda36618d3..718fbcff84d 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -179,7 +179,8 @@ static void sta_addba_resp_timer_expired(unsigned long data) /* check if the TID waits for addBA response */ spin_lock_bh(&sta->lock); - if ((*state & (HT_ADDBA_REQUESTED_MSK | HT_ADDBA_RECEIVED_MSK)) != + if ((*state & (HT_ADDBA_REQUESTED_MSK | HT_ADDBA_RECEIVED_MSK | + HT_AGG_STATE_REQ_STOP_BA_MSK)) != HT_ADDBA_REQUESTED_MSK) { spin_unlock_bh(&sta->lock); *state = HT_AGG_STATE_IDLE; @@ -301,7 +302,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) * call back right away, it must see that the flow has begun */ *state |= HT_ADDBA_REQUESTED_MSK; - start_seq_num = sta->tid_seq[tid]; + start_seq_num = sta->tid_seq[tid] >> 4; ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START, pubsta, tid, &start_seq_num); diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 2e5e841e9b7..b0102c538b3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -148,7 +148,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, rcu_read_lock(); if (mac_addr) { - sta = sta_info_get(sdata, mac_addr); + sta = sta_info_get_bss(sdata, mac_addr); if (!sta) { ieee80211_key_free(key); err = -ENOENT; @@ -179,7 +179,7 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, if (mac_addr) { ret = -ENOENT; - sta = sta_info_get(sdata, mac_addr); + sta = sta_info_get_bss(sdata, mac_addr); if (!sta) goto out_unlock; @@ -226,7 +226,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, rcu_read_lock(); if (mac_addr) { - sta = sta_info_get(sdata, mac_addr); + sta = sta_info_get_bss(sdata, mac_addr); if (!sta) goto out; @@ -419,7 +419,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, rcu_read_lock(); - sta = sta_info_get(sdata, mac); + sta = sta_info_get_bss(sdata, mac); if (sta) { ret = 0; sta_set_sinfo(sta, sinfo); @@ -775,7 +775,7 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, if (mac) { rcu_read_lock(); - sta = sta_info_get(sdata, mac); + sta = sta_info_get_bss(sdata, mac); if (!sta) { rcu_read_unlock(); return -ENOENT; @@ -803,7 +803,7 @@ static int ieee80211_change_station(struct wiphy *wiphy, rcu_read_lock(); - sta = sta_info_get(sdata, mac); + sta = sta_info_get_bss(sdata, mac); if (!sta) { rcu_read_unlock(); return -ENOENT; @@ -1085,6 +1085,13 @@ static int ieee80211_change_bss(struct wiphy *wiphy, params->use_short_preamble; changed |= BSS_CHANGED_ERP_PREAMBLE; } + + if (!sdata->vif.bss_conf.use_short_slot && + sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) { + sdata->vif.bss_conf.use_short_slot = true; + changed |= BSS_CHANGED_ERP_SLOT; + } + if (params->use_short_slot_time >= 0) { sdata->vif.bss_conf.use_short_slot = params->use_short_slot_time; @@ -1128,6 +1135,13 @@ static int ieee80211_set_txq_params(struct wiphy *wiphy, p.cw_max = params->cwmax; p.cw_min = params->cwmin; p.txop = params->txop; + + /* + * Setting tx queue params disables u-apsd because it's only + * called in master mode. + */ + p.uapsd = false; + if (drv_conf_tx(local, params->queue, &p)) { printk(KERN_DEBUG "%s: failed to set TX queue " "parameters for queue %d\n", @@ -1230,6 +1244,13 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) struct ieee80211_local *local = wiphy_priv(wiphy); int err; + if (changed & WIPHY_PARAM_COVERAGE_CLASS) { + err = drv_set_coverage_class(local, wiphy->coverage_class); + + if (err) + return err; + } + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { err = drv_set_rts_threshold(local, wiphy->rts_threshold); @@ -1399,8 +1420,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); int i; - u32 target_rate; - struct ieee80211_supported_band *sband; /* * This _could_ be supported by providing a hook for @@ -1410,35 +1429,11 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, if (local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL) return -EOPNOTSUPP; - sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; - - /* - * target_rate = -1, rate->fixed = 0 means auto only, so use all rates - * target_rate = X, rate->fixed = 1 means only rate X - * target_rate = X, rate->fixed = 0 means all rates <= X - */ - sdata->max_ratectrl_rateidx = -1; - sdata->force_unicast_rateidx = -1; - - if (mask->fixed) - target_rate = mask->fixed / 100; - else if (mask->maxrate) - target_rate = mask->maxrate / 100; - else - return 0; - - for (i = 0; i< sband->n_bitrates; i++) { - if (target_rate != sband->bitrates[i].bitrate) - continue; - /* requested bitrate found */ - sdata->max_ratectrl_rateidx = i; - if (mask->fixed) - sdata->force_unicast_rateidx = i; - return 0; - } + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + sdata->rc_rateidx_mask[i] = mask->control[i].legacy; - return -EINVAL; + return 0; } static int ieee80211_remain_on_channel(struct wiphy *wiphy, diff --git a/net/mac80211/debugfs.c b/net/mac80211/debugfs.c index e4b54093d41..b3bc32b62a5 100644 --- a/net/mac80211/debugfs.c +++ b/net/mac80211/debugfs.c @@ -158,6 +158,98 @@ static const struct file_operations noack_ops = { .open = mac80211_open_file_generic }; +static ssize_t uapsd_queues_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + int res; + char buf[10]; + + res = scnprintf(buf, sizeof(buf), "0x%x\n", local->uapsd_queues); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static ssize_t uapsd_queues_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + unsigned long val; + char buf[10]; + size_t len; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = strict_strtoul(buf, 0, &val); + + if (ret) + return -EINVAL; + + if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK) + return -ERANGE; + + local->uapsd_queues = val; + + return count; +} + +static const struct file_operations uapsd_queues_ops = { + .read = uapsd_queues_read, + .write = uapsd_queues_write, + .open = mac80211_open_file_generic +}; + +static ssize_t uapsd_max_sp_len_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + int res; + char buf[10]; + + res = scnprintf(buf, sizeof(buf), "0x%x\n", local->uapsd_max_sp_len); + + return simple_read_from_buffer(user_buf, count, ppos, buf, res); +} + +static ssize_t uapsd_max_sp_len_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_local *local = file->private_data; + unsigned long val; + char buf[10]; + size_t len; + int ret; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + buf[len] = '\0'; + + ret = strict_strtoul(buf, 0, &val); + + if (ret) + return -EINVAL; + + if (val & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK) + return -ERANGE; + + local->uapsd_max_sp_len = val; + + return count; +} + +static const struct file_operations uapsd_max_sp_len_ops = { + .read = uapsd_max_sp_len_read, + .write = uapsd_max_sp_len_write, + .open = mac80211_open_file_generic +}; + static ssize_t queues_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -314,6 +406,8 @@ void debugfs_hw_add(struct ieee80211_local *local) DEBUGFS_ADD(queues); DEBUGFS_ADD_MODE(reset, 0200); DEBUGFS_ADD(noack); + DEBUGFS_ADD(uapsd_queues); + DEBUGFS_ADD(uapsd_max_sp_len); statsd = debugfs_create_dir("statistics", phyd); diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 59f6e3bcbd0..9affe2cd185 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -127,8 +127,10 @@ __IEEE80211_IF_FILE(name, ieee80211_if_write_##name) /* common attributes */ IEEE80211_IF_FILE(drop_unencrypted, drop_unencrypted, DEC); -IEEE80211_IF_FILE(force_unicast_rateidx, force_unicast_rateidx, DEC); -IEEE80211_IF_FILE(max_ratectrl_rateidx, max_ratectrl_rateidx, DEC); +IEEE80211_IF_FILE(rc_rateidx_mask_2ghz, rc_rateidx_mask[IEEE80211_BAND_2GHZ], + HEX); +IEEE80211_IF_FILE(rc_rateidx_mask_5ghz, rc_rateidx_mask[IEEE80211_BAND_5GHZ], + HEX); /* STA attributes */ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); @@ -253,7 +255,7 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode, #endif -#define DEBUGFS_ADD(name, type) \ +#define DEBUGFS_ADD(name) \ debugfs_create_file(#name, 0400, sdata->debugfs.dir, \ sdata, &name##_ops); @@ -263,40 +265,40 @@ IEEE80211_IF_FILE(dot11MeshHWMPRootMode, static void add_sta_files(struct ieee80211_sub_if_data *sdata) { - DEBUGFS_ADD(drop_unencrypted, sta); - DEBUGFS_ADD(force_unicast_rateidx, sta); - DEBUGFS_ADD(max_ratectrl_rateidx, sta); + DEBUGFS_ADD(drop_unencrypted); + DEBUGFS_ADD(rc_rateidx_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mask_5ghz); - DEBUGFS_ADD(bssid, sta); - DEBUGFS_ADD(aid, sta); + DEBUGFS_ADD(bssid); + DEBUGFS_ADD(aid); DEBUGFS_ADD_MODE(smps, 0600); } static void add_ap_files(struct ieee80211_sub_if_data *sdata) { - DEBUGFS_ADD(drop_unencrypted, ap); - DEBUGFS_ADD(force_unicast_rateidx, ap); - DEBUGFS_ADD(max_ratectrl_rateidx, ap); + DEBUGFS_ADD(drop_unencrypted); + DEBUGFS_ADD(rc_rateidx_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mask_5ghz); - DEBUGFS_ADD(num_sta_ps, ap); - DEBUGFS_ADD(dtim_count, ap); - DEBUGFS_ADD(num_buffered_multicast, ap); + DEBUGFS_ADD(num_sta_ps); + DEBUGFS_ADD(dtim_count); + DEBUGFS_ADD(num_buffered_multicast); } static void add_wds_files(struct ieee80211_sub_if_data *sdata) { - DEBUGFS_ADD(drop_unencrypted, wds); - DEBUGFS_ADD(force_unicast_rateidx, wds); - DEBUGFS_ADD(max_ratectrl_rateidx, wds); + DEBUGFS_ADD(drop_unencrypted); + DEBUGFS_ADD(rc_rateidx_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mask_5ghz); - DEBUGFS_ADD(peer, wds); + DEBUGFS_ADD(peer); } static void add_vlan_files(struct ieee80211_sub_if_data *sdata) { - DEBUGFS_ADD(drop_unencrypted, vlan); - DEBUGFS_ADD(force_unicast_rateidx, vlan); - DEBUGFS_ADD(max_ratectrl_rateidx, vlan); + DEBUGFS_ADD(drop_unencrypted); + DEBUGFS_ADD(rc_rateidx_mask_2ghz); + DEBUGFS_ADD(rc_rateidx_mask_5ghz); } static void add_monitor_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 8757ea73d54..de91d39e027 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -214,6 +214,21 @@ static inline int drv_set_rts_threshold(struct ieee80211_local *local, return ret; } +static inline int drv_set_coverage_class(struct ieee80211_local *local, + u8 value) +{ + int ret = 0; + might_sleep(); + + if (local->ops->set_coverage_class) + local->ops->set_coverage_class(&local->hw, value); + else + ret = -EOPNOTSUPP; + + trace_drv_set_coverage_class(local, value, ret); + return ret; +} + static inline void drv_sta_notify(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, enum sta_notify_cmd cmd, diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index 977cc7528bc..0ea258123b8 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -491,6 +491,29 @@ TRACE_EVENT(drv_set_rts_threshold, ) ); +TRACE_EVENT(drv_set_coverage_class, + TP_PROTO(struct ieee80211_local *local, u8 value, int ret), + + TP_ARGS(local, value, ret), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(u8, value) + __field(int, ret) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->ret = ret; + __entry->value = value; + ), + + TP_printk( + LOCAL_PR_FMT " value:%d ret:%d", + LOCAL_PR_ARG, __entry->value, __entry->ret + ) +); + TRACE_EVENT(drv_sta_notify, TP_PROTO(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index a27921ee6e6..c18f576f184 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -58,6 +58,15 @@ struct ieee80211_local; #define TU_TO_EXP_TIME(x) (jiffies + usecs_to_jiffies((x) * 1024)) +#define IEEE80211_DEFAULT_UAPSD_QUEUES \ + (IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | \ + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE | \ + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | \ + IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + +#define IEEE80211_DEFAULT_MAX_SP_LEN \ + IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL + struct ieee80211_fragment_entry { unsigned long first_frag_time; unsigned int seq; @@ -78,6 +87,7 @@ struct ieee80211_bss { u8 dtim_period; bool wmm_used; + bool uapsd_supported; unsigned long last_probe_resp; @@ -285,7 +295,7 @@ struct ieee80211_work { u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid_len; u8 supp_rates_len; - bool wmm_used, use_11n; + bool wmm_used, use_11n, uapsd_used; } assoc; struct { u32 duration; @@ -306,6 +316,7 @@ enum ieee80211_sta_flags { IEEE80211_STA_DISABLE_11N = BIT(4), IEEE80211_STA_CSA_RECEIVED = BIT(5), IEEE80211_STA_MFP_ENABLED = BIT(6), + IEEE80211_STA_UAPSD_ENABLED = BIT(7), }; struct ieee80211_if_managed { @@ -494,8 +505,8 @@ struct ieee80211_sub_if_data { */ struct ieee80211_if_ap *bss; - int force_unicast_rateidx; /* forced TX rateidx for unicast frames */ - int max_ratectrl_rateidx; /* max TX rateidx for rate control */ + /* bitmap of allowed (non-MCS) rate indexes for rate control */ + u32 rc_rateidx_mask[IEEE80211_NUM_BANDS]; union { struct ieee80211_if_ap ap; @@ -797,6 +808,20 @@ struct ieee80211_local { int wifi_wme_noack_test; unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ + /* + * Bitmask of enabled u-apsd queues, + * IEEE80211_WMM_IE_STA_QOSINFO_AC_BE & co. Needs a new association + * to take effect. + */ + unsigned int uapsd_queues; + + /* + * Maximum number of buffered frames AP can deliver during a + * service period, IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL or similar. + * Needs a new association to take effect. + */ + unsigned int uapsd_max_sp_len; + bool pspolling; bool offchannel_ps_enabled; /* diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 00a1f4ccdaf..fe140bf033f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -15,12 +15,14 @@ #include <linux/netdevice.h> #include <linux/rtnetlink.h> #include <net/mac80211.h> +#include <net/ieee80211_radiotap.h> #include "ieee80211_i.h" #include "sta_info.h" #include "debugfs_netdev.h" #include "mesh.h" #include "led.h" #include "driver-ops.h" +#include "wme.h" /** * DOC: Interface list locking @@ -63,15 +65,16 @@ static int ieee80211_change_mtu(struct net_device *dev, int new_mtu) static int ieee80211_change_mac(struct net_device *dev, void *addr) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct sockaddr *sa = addr; int ret; if (ieee80211_sdata_running(sdata)) return -EBUSY; - ret = eth_mac_addr(dev, addr); + ret = eth_mac_addr(dev, sa); if (ret == 0) - memcpy(sdata->vif.addr, addr, ETH_ALEN); + memcpy(sdata->vif.addr, sa->sa_data, ETH_ALEN); return ret; } @@ -326,7 +329,7 @@ static int ieee80211_open(struct net_device *dev) if (sdata->vif.type == NL80211_IFTYPE_STATION) ieee80211_queue_work(&local->hw, &sdata->u.mgd.work); - netif_start_queue(dev); + netif_tx_start_all_queues(dev); return 0; err_del_interface: @@ -354,7 +357,7 @@ static int ieee80211_stop(struct net_device *dev) /* * Stop TX on this interface first. */ - netif_stop_queue(dev); + netif_tx_stop_all_queues(dev); /* * Purge work for this interface. @@ -657,6 +660,12 @@ static void ieee80211_teardown_sdata(struct net_device *dev) WARN_ON(flushed); } +static u16 ieee80211_netdev_select_queue(struct net_device *dev, + struct sk_buff *skb) +{ + return ieee80211_select_queue(IEEE80211_DEV_TO_SUB_IF(dev), skb); +} + static const struct net_device_ops ieee80211_dataif_ops = { .ndo_open = ieee80211_open, .ndo_stop = ieee80211_stop, @@ -665,8 +674,34 @@ static const struct net_device_ops ieee80211_dataif_ops = { .ndo_set_multicast_list = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = ieee80211_change_mac, + .ndo_select_queue = ieee80211_netdev_select_queue, }; +static u16 ieee80211_monitor_select_queue(struct net_device *dev, + struct sk_buff *skb) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_local *local = sdata->local; + struct ieee80211_hdr *hdr; + struct ieee80211_radiotap_header *rtap = (void *)skb->data; + + if (local->hw.queues < 4) + return 0; + + if (skb->len < 4 || + skb->len < rtap->it_len + 2 /* frame control */) + return 0; /* doesn't matter, frame will be dropped */ + + hdr = (void *)((u8 *)skb->data + rtap->it_len); + + if (!ieee80211_is_data(hdr->frame_control)) { + skb->priority = 7; + return ieee802_1d_to_ac[skb->priority]; + } + + return ieee80211_downgrade_queue(local, skb); +} + static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_open = ieee80211_open, .ndo_stop = ieee80211_stop, @@ -675,6 +710,7 @@ static const struct net_device_ops ieee80211_monitorif_ops = { .ndo_set_multicast_list = ieee80211_set_multicast_list, .ndo_change_mtu = ieee80211_change_mtu, .ndo_set_mac_address = eth_mac_addr, + .ndo_select_queue = ieee80211_monitor_select_queue, }; static void ieee80211_if_setup(struct net_device *dev) @@ -781,8 +817,8 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ASSERT_RTNL(); - ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, - name, ieee80211_if_setup); + ndev = alloc_netdev_mq(sizeof(*sdata) + local->hw.vif_data_size, + name, ieee80211_if_setup, local->hw.queues); if (!ndev) return -ENOMEM; dev_net_set(ndev, wiphy_net(local->hw.wiphy)); @@ -820,8 +856,12 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, INIT_LIST_HEAD(&sdata->key_list); - sdata->force_unicast_rateidx = -1; - sdata->max_ratectrl_rateidx = -1; + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + struct ieee80211_supported_band *sband; + sband = local->hw.wiphy->bands[i]; + sdata->rc_rateidx_mask[i] = + sband ? (1 << sband->n_bitrates) - 1 : 0; + } /* setup type-dependent data */ ieee80211_setup_sdata(sdata, type); diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d0a14d953f0..ec8f767ba95 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -17,7 +17,6 @@ #include <linux/skbuff.h> #include <linux/etherdevice.h> #include <linux/if_arp.h> -#include <linux/wireless.h> #include <linux/rtnetlink.h> #include <linux/bitmap.h> #include <linux/pm_qos_params.h> @@ -385,6 +384,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; local->user_power_level = -1; + local->uapsd_queues = IEEE80211_DEFAULT_UAPSD_QUEUES; + local->uapsd_max_sp_len = IEEE80211_DEFAULT_MAX_SP_LEN; INIT_LIST_HEAD(&local->interfaces); mutex_init(&local->iflist_mtx); @@ -492,6 +493,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) else if (local->hw.flags & IEEE80211_HW_SIGNAL_UNSPEC) local->hw.wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; + WARN((local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD) + && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK), + "U-APSD not supported with HW_PS_NULLFUNC_STACK\n"); + /* * Calculate scan IE length -- we need this to alloc * memory and to subtract from the driver limit. It diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 72920ee0788..a82564e73d9 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -249,30 +249,15 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, void ieee80211_send_pspoll(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata) { - struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; struct ieee80211_pspoll *pspoll; struct sk_buff *skb; - u16 fc; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll)); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for " - "pspoll frame\n", sdata->name); + skb = ieee80211_pspoll_get(&local->hw, &sdata->vif); + if (!skb) return; - } - skb_reserve(skb, local->hw.extra_tx_headroom); - - pspoll = (struct ieee80211_pspoll *) skb_put(skb, sizeof(*pspoll)); - memset(pspoll, 0, sizeof(*pspoll)); - fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL | IEEE80211_FCTL_PM; - pspoll->frame_control = cpu_to_le16(fc); - pspoll->aid = cpu_to_le16(ifmgd->aid); - - /* aid in PS-Poll has its two MSBs each set to 1 */ - pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14); - memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN); - memcpy(pspoll->ta, sdata->vif.addr, ETH_ALEN); + pspoll = (struct ieee80211_pspoll *) skb->data; + pspoll->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); @@ -283,30 +268,47 @@ void ieee80211_send_nullfunc(struct ieee80211_local *local, int powersave) { struct sk_buff *skb; + struct ieee80211_hdr_3addr *nullfunc; + + skb = ieee80211_nullfunc_get(&local->hw, &sdata->vif); + if (!skb) + return; + + nullfunc = (struct ieee80211_hdr_3addr *) skb->data; + if (powersave) + nullfunc->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); + + IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + ieee80211_tx_skb(sdata, skb); +} + +static void ieee80211_send_4addr_nullfunc(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + struct sk_buff *skb; struct ieee80211_hdr *nullfunc; __le16 fc; if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_STATION)) return; - skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + 30); if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc " - "frame\n", sdata->name); + printk(KERN_DEBUG "%s: failed to allocate buffer for 4addr " + "nullfunc frame\n", sdata->name); return; } skb_reserve(skb, local->hw.extra_tx_headroom); - nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24); - memset(nullfunc, 0, 24); + nullfunc = (struct ieee80211_hdr *) skb_put(skb, 30); + memset(nullfunc, 0, 30); fc = cpu_to_le16(IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC | - IEEE80211_FCTL_TODS); - if (powersave) - fc |= cpu_to_le16(IEEE80211_FCTL_PM); + IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); nullfunc->frame_control = fc; memcpy(nullfunc->addr1, sdata->u.mgd.bssid, ETH_ALEN); memcpy(nullfunc->addr2, sdata->vif.addr, ETH_ALEN); memcpy(nullfunc->addr3, sdata->u.mgd.bssid, ETH_ALEN); + memcpy(nullfunc->addr4, sdata->vif.addr, ETH_ALEN); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); @@ -567,7 +569,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, struct ieee80211_tx_queue_params params; size_t left; int count; - u8 *pos; + u8 *pos, uapsd_queues = 0; if (local->hw.queues < 4) return; @@ -577,6 +579,10 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1) return; + + if (ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) + uapsd_queues = local->uapsd_queues; + count = wmm_param[6] & 0x0f; if (count == ifmgd->wmm_last_param_set) return; @@ -591,6 +597,7 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, for (; left >= 4; left -= 4, pos += 4) { int aci = (pos[0] >> 5) & 0x03; int acm = (pos[0] >> 4) & 0x01; + bool uapsd = false; int queue; switch (aci) { @@ -598,22 +605,30 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, queue = 3; if (acm) local->wmm_acm |= BIT(1) | BIT(2); /* BK/- */ + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd = true; break; case 2: /* AC_VI */ queue = 1; if (acm) local->wmm_acm |= BIT(4) | BIT(5); /* CL/VI */ + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd = true; break; case 3: /* AC_VO */ queue = 0; if (acm) local->wmm_acm |= BIT(6) | BIT(7); /* VO/NC */ + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd = true; break; case 0: /* AC_BE */ default: queue = 2; if (acm) local->wmm_acm |= BIT(0) | BIT(3); /* BE/EE */ + if (uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd = true; break; } @@ -621,11 +636,14 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local, params.cw_max = ecw2cw((pos[1] & 0xf0) >> 4); params.cw_min = ecw2cw(pos[1] & 0x0f); params.txop = get_unaligned_le16(pos + 2); + params.uapsd = uapsd; + #ifdef CONFIG_MAC80211_VERBOSE_DEBUG printk(KERN_DEBUG "%s: WMM queue=%d aci=%d acm=%d aifs=%d " - "cWmin=%d cWmax=%d txop=%d\n", + "cWmin=%d cWmax=%d txop=%d uapsd=%d\n", wiphy_name(local->hw.wiphy), queue, aci, acm, - params.aifs, params.cw_min, params.cw_max, params.txop); + params.aifs, params.cw_min, params.cw_max, params.txop, + params.uapsd); #endif if (drv_conf_tx(local, queue, ¶ms) && local->ops->conf_tx) printk(KERN_DEBUG "%s: failed to set TX queue " @@ -652,6 +670,8 @@ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata, } use_short_slot = !!(capab & WLAN_CAPABILITY_SHORT_SLOT_TIME); + if (sdata->local->hw.conf.channel->band == IEEE80211_BAND_5GHZ) + use_short_slot = true; if (use_protection != bss_conf->use_cts_prot) { bss_conf->use_cts_prot = use_protection; @@ -723,7 +743,7 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, ieee80211_recalc_smps(local, sdata); mutex_unlock(&local->iflist_mtx); - netif_start_queue(sdata->dev); + netif_tx_start_all_queues(sdata->dev); netif_carrier_on(sdata->dev); } @@ -759,7 +779,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata) * time -- we don't want the scan code to enable queues. */ - netif_stop_queue(sdata->dev); + netif_tx_stop_all_queues(sdata->dev); netif_carrier_off(sdata->dev); rcu_read_lock(); @@ -1096,7 +1116,7 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, if (err) { printk(KERN_DEBUG "%s: failed to insert STA entry for" " the AP (error %d)\n", sdata->name, err); - return RX_MGMT_CFG80211_ASSOC_ERROR; + return false; } if (elems.wmm_param) @@ -1120,6 +1140,13 @@ static bool ieee80211_assoc_success(struct ieee80211_work *wk, ieee80211_set_associated(sdata, cbss, changed); /* + * If we're using 4-addr mode, let the AP know that we're + * doing so, so that it can create the STA VLAN on its side + */ + if (ifmgd->use_4addr) + ieee80211_send_4addr_nullfunc(local, sdata); + + /* * Start timer to probe the connection to the AP now. * Also start the timer that will detect beacon loss. */ @@ -1774,7 +1801,7 @@ int ieee80211_mgd_auth(struct ieee80211_sub_if_data *sdata, if (!wk) return -ENOMEM; - memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN);; + memcpy(wk->filter_ta, req->bss->bssid, ETH_ALEN); if (req->ie && req->ie_len) { memcpy(wk->ie, req->ie, req->ie_len); @@ -1897,6 +1924,15 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, wk->assoc.ht_information_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_HT_INFORMATION); + if (bss->wmm_used && bss->uapsd_supported && + (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { + wk->assoc.uapsd_used = true; + ifmgd->flags |= IEEE80211_STA_UAPSD_ENABLED; + } else { + wk->assoc.uapsd_used = false; + ifmgd->flags &= ~IEEE80211_STA_UAPSD_ENABLED; + } + ssid = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); memcpy(wk->assoc.ssid, ssid + 2, ssid[1]); wk->assoc.ssid_len = ssid[1]; diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index a7bbfc40a64..c36b1911987 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -113,7 +113,7 @@ void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) */ if (sdata->vif.type != NL80211_IFTYPE_STATION && sdata->vif.type != NL80211_IFTYPE_MONITOR) - netif_stop_queue(sdata->dev); + netif_tx_stop_all_queues(sdata->dev); } mutex_unlock(&local->iflist_mtx); } @@ -131,7 +131,7 @@ void ieee80211_offchannel_stop_station(struct ieee80211_local *local) continue; if (sdata->vif.type == NL80211_IFTYPE_STATION) { - netif_stop_queue(sdata->dev); + netif_tx_stop_all_queues(sdata->dev); if (sdata->u.mgd.associated) ieee80211_offchannel_ps_enable(sdata); } @@ -153,9 +153,11 @@ void ieee80211_offchannel_return(struct ieee80211_local *local, if (sdata->vif.type == NL80211_IFTYPE_STATION) { if (sdata->u.mgd.associated) ieee80211_offchannel_ps_disable(sdata); - netif_wake_queue(sdata->dev); } + if (sdata->vif.type != NL80211_IFTYPE_MONITOR) + netif_tx_wake_all_queues(sdata->dev); + /* re-enable beaconing */ if (enable_beaconing && (sdata->vif.type == NL80211_IFTYPE_AP || diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index b9007f80cb9..c74b7c85403 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -207,6 +207,27 @@ static bool rc_no_data_or_no_ack(struct ieee80211_tx_rate_control *txrc) return ((info->flags & IEEE80211_TX_CTL_NO_ACK) || !ieee80211_is_data(fc)); } +static void rc_send_low_broadcast(s8 *idx, u32 basic_rates, u8 max_rate_idx) +{ + u8 i; + + if (basic_rates == 0) + return; /* assume basic rates unknown and accept rate */ + if (*idx < 0) + return; + if (basic_rates & (1 << *idx)) + return; /* selected rate is a basic rate */ + + for (i = *idx + 1; i <= max_rate_idx; i++) { + if (basic_rates & (1 << i)) { + *idx = i; + return; + } + } + + /* could not find a basic rate; use original selection */ +} + bool rate_control_send_low(struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) @@ -218,12 +239,48 @@ bool rate_control_send_low(struct ieee80211_sta *sta, info->control.rates[0].count = (info->flags & IEEE80211_TX_CTL_NO_ACK) ? 1 : txrc->hw->max_rate_tries; + if (!sta && txrc->ap) + rc_send_low_broadcast(&info->control.rates[0].idx, + txrc->bss_conf->basic_rates, + txrc->sband->n_bitrates); return true; } return false; } EXPORT_SYMBOL(rate_control_send_low); +static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, + int n_bitrates, u32 mask) +{ + int j; + + /* See whether the selected rate or anything below it is allowed. */ + for (j = rate->idx; j >= 0; j--) { + if (mask & (1 << j)) { + /* Okay, found a suitable rate. Use it. */ + rate->idx = j; + return; + } + } + + /* Try to find a higher rate that would be allowed */ + for (j = rate->idx + 1; j < n_bitrates; j++) { + if (mask & (1 << j)) { + /* Okay, found a suitable rate. Use it. */ + rate->idx = j; + return; + } + } + + /* + * Uh.. No suitable rate exists. This should not really happen with + * sane TX rate mask configurations. However, should someone manage to + * configure supported rates and TX rate mask in incompatible way, + * allow the frame to be transmitted with whatever the rate control + * selected. + */ +} + void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct sta_info *sta, struct ieee80211_tx_rate_control *txrc) @@ -233,6 +290,7 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, struct ieee80211_sta *ista = NULL; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); int i; + u32 mask; if (sta) { ista = &sta->sta; @@ -245,23 +303,31 @@ void rate_control_get_rate(struct ieee80211_sub_if_data *sdata, info->control.rates[i].count = 1; } - if (sta && sdata->force_unicast_rateidx > -1) { - info->control.rates[0].idx = sdata->force_unicast_rateidx; - } else { - ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); - info->flags |= IEEE80211_TX_INTFL_RCALGO; - } + ref->ops->get_rate(ref->priv, ista, priv_sta, txrc); /* - * try to enforce the maximum rate the user wanted + * Try to enforce the rateidx mask the user wanted. skip this if the + * default mask (allow all rates) is used to save some processing for + * the common case. */ - if (sdata->max_ratectrl_rateidx > -1) + mask = sdata->rc_rateidx_mask[info->band]; + if (mask != (1 << txrc->sband->n_bitrates) - 1) { + if (sta) { + /* Filter out rates that the STA does not support */ + mask &= sta->sta.supp_rates[info->band]; + } + /* + * Make sure the rate index selected for each TX rate is + * included in the configured mask and change the rate indexes + * if needed. + */ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { + /* Rate masking supports only legacy rates for now */ if (info->control.rates[i].flags & IEEE80211_TX_RC_MCS) continue; - info->control.rates[i].idx = - min_t(s8, info->control.rates[i].idx, - sdata->max_ratectrl_rateidx); + rate_idx_match_mask(&info->control.rates[i], + txrc->sband->n_bitrates, mask); + } } BUG_ON(info->control.rates[0].idx < 0); diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index cb9bd1f65e2..669dddd4052 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -44,10 +44,7 @@ static inline void rate_control_tx_status(struct ieee80211_local *local, struct rate_control_ref *ref = local->rate_ctrl; struct ieee80211_sta *ista = &sta->sta; void *priv_sta = sta->rate_ctrl_priv; - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (likely(info->flags & IEEE80211_TX_INTFL_RCALGO)) - ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); + ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index bfcf09eb64b..efa6d3689c5 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -1111,6 +1111,18 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (ieee80211_is_nullfunc(hdr->frame_control) || ieee80211_is_qos_nullfunc(hdr->frame_control)) { I802_DEBUG_INC(rx->local->rx_handlers_drop_nullfunc); + + /* + * If we receive a 4-addr nullfunc frame from a STA + * that was not moved to a 4-addr STA vlan yet, drop + * the frame to the monitor interface, to make sure + * that hostapd sees it + */ + if (ieee80211_has_a4(hdr->frame_control) && + (rx->sdata->vif.type == NL80211_IFTYPE_AP || + (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + !rx->sdata->u.vlan.sta))) + return RX_DROP_MONITOR; /* * Update counter and free packet here to avoid * counting this as a dropped packed. @@ -1665,7 +1677,9 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) memset(info, 0, sizeof(*info)); info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING; info->control.vif = &rx->sdata->vif; - ieee80211_select_queue(local, fwd_skb); + skb_set_queue_mapping(skb, + ieee80211_select_queue(rx->sdata, fwd_skb)); + ieee80211_set_qos_hdr(local, skb); if (is_multicast_ether_addr(fwd_hdr->addr1)) IEEE80211_IFSTA_MESH_CTR_INC(&sdata->u.mesh, fwded_mcast); diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 365f4097551..9afe2f9885d 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -12,7 +12,6 @@ * published by the Free Software Foundation. */ -#include <linux/wireless.h> #include <linux/if_arp.h> #include <linux/rtnetlink.h> #include <net/mac80211.h> @@ -55,6 +54,23 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local, cfg80211_put_bss(container_of((void *)bss, struct cfg80211_bss, priv)); } +static bool is_uapsd_supported(struct ieee802_11_elems *elems) +{ + u8 qos_info; + + if (elems->wmm_info && elems->wmm_info_len == 7 + && elems->wmm_info[5] == 1) + qos_info = elems->wmm_info[6]; + else if (elems->wmm_param && elems->wmm_param_len == 24 + && elems->wmm_param[5] == 1) + qos_info = elems->wmm_param[6]; + else + /* no valid wmm information or parameter element found */ + return false; + + return qos_info & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD; +} + struct ieee80211_bss * ieee80211_bss_info_update(struct ieee80211_local *local, struct ieee80211_rx_status *rx_status, @@ -118,6 +134,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local, } bss->wmm_used = elems->wmm_param || elems->wmm_info; + bss->uapsd_supported = is_uapsd_supported(elems); if (!beacon) bss->last_probe_resp = jiffies; @@ -285,6 +302,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) ieee80211_mlme_notify_scan_completed(local); ieee80211_ibss_notify_scan_completed(local); ieee80211_mesh_notify_scan_completed(local); + ieee80211_queue_work(&local->hw, &local->work_work); } EXPORT_SYMBOL(ieee80211_scan_completed); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 47da552ce8a..f735826f055 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -119,6 +119,27 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, return sta; } +/* + * Get sta info either from the specified interface + * or from one of its vlans + */ +struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, + const u8 *addr) +{ + struct ieee80211_local *local = sdata->local; + struct sta_info *sta; + + sta = rcu_dereference(local->sta_hash[STA_HASH(addr)]); + while (sta) { + if ((sta->sdata == sdata || + sta->sdata->bss == sdata->bss) && + memcmp(sta->sta.addr, addr, ETH_ALEN) == 0) + break; + sta = rcu_dereference(sta->hnext); + } + return sta; +} + struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, int idx) { diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c8208236e89..6f79bba5706 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -408,6 +408,9 @@ static inline u32 get_sta_flags(struct sta_info *sta) struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, const u8 *addr); +struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, + const u8 *addr); + static inline void for_each_sta_info_type_check(struct ieee80211_local *local, const u8 *addr, diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 7bba49d2b6c..daf81048c1f 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -180,6 +180,71 @@ static int inline is_ieee80211_device(struct ieee80211_local *local, } /* tx handlers */ +static ieee80211_tx_result debug_noinline +ieee80211_tx_h_dynamic_ps(struct ieee80211_tx_data *tx) +{ + struct ieee80211_local *local = tx->local; + struct ieee80211_if_managed *ifmgd; + + /* driver doesn't support power save */ + if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) + return TX_CONTINUE; + + /* hardware does dynamic power save */ + if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) + return TX_CONTINUE; + + /* dynamic power save disabled */ + if (local->hw.conf.dynamic_ps_timeout <= 0) + return TX_CONTINUE; + + /* we are scanning, don't enable power save */ + if (local->scanning) + return TX_CONTINUE; + + if (!local->ps_sdata) + return TX_CONTINUE; + + /* No point if we're going to suspend */ + if (local->quiescing) + return TX_CONTINUE; + + /* dynamic ps is supported only in managed mode */ + if (tx->sdata->vif.type != NL80211_IFTYPE_STATION) + return TX_CONTINUE; + + ifmgd = &tx->sdata->u.mgd; + + /* + * Don't wakeup from power save if u-apsd is enabled, voip ac has + * u-apsd enabled and the frame is in voip class. This effectively + * means that even if all access categories have u-apsd enabled, in + * practise u-apsd is only used with the voip ac. This is a + * workaround for the case when received voip class packets do not + * have correct qos tag for some reason, due the network or the + * peer application. + * + * Note: local->uapsd_queues access is racy here. If the value is + * changed via debugfs, user needs to reassociate manually to have + * everything in sync. + */ + if ((ifmgd->flags & IEEE80211_STA_UAPSD_ENABLED) + && (local->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + && skb_get_queue_mapping(tx->skb) == 0) + return TX_CONTINUE; + + if (local->hw.conf.flags & IEEE80211_CONF_PS) { + ieee80211_stop_queues_by_reason(&local->hw, + IEEE80211_QUEUE_STOP_REASON_PS); + ieee80211_queue_work(&local->hw, + &local->dynamic_ps_disable_work); + } + + mod_timer(&local->dynamic_ps_timer, jiffies + + msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); + + return TX_CONTINUE; +} static ieee80211_tx_result debug_noinline ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx) @@ -519,7 +584,12 @@ ieee80211_tx_h_rate_ctrl(struct ieee80211_tx_data *tx) txrc.bss_conf = &tx->sdata->vif.bss_conf; txrc.skb = tx->skb; txrc.reported_rate.idx = -1; - txrc.max_rate_idx = tx->sdata->max_ratectrl_rateidx; + txrc.rate_idx_mask = tx->sdata->rc_rateidx_mask[tx->channel->band]; + if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) + txrc.max_rate_idx = -1; + else + txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + txrc.ap = tx->sdata->vif.type == NL80211_IFTYPE_AP; /* set up RTS protection if desired */ if (len > tx->local->hw.wiphy->rts_threshold) { @@ -1051,8 +1121,11 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, hdr = (struct ieee80211_hdr *) skb->data; - if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) { tx->sta = rcu_dereference(sdata->u.vlan.sta); + if (!tx->sta && sdata->dev->ieee80211_ptr->use_4addr) + return TX_DROP; + } if (!tx->sta) tx->sta = sta_info_get(sdata, hdr->addr1); @@ -1215,6 +1288,7 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) goto txh_done; \ } while (0) + CALL_TXH(ieee80211_tx_h_dynamic_ps); CALL_TXH(ieee80211_tx_h_check_assoc); CALL_TXH(ieee80211_tx_h_ps_buf); CALL_TXH(ieee80211_tx_h_select_key); @@ -1397,34 +1471,6 @@ static int ieee80211_skb_resize(struct ieee80211_local *local, return 0; } -static bool need_dynamic_ps(struct ieee80211_local *local) -{ - /* driver doesn't support power save */ - if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_PS)) - return false; - - /* hardware does dynamic power save */ - if (local->hw.flags & IEEE80211_HW_SUPPORTS_DYNAMIC_PS) - return false; - - /* dynamic power save disabled */ - if (local->hw.conf.dynamic_ps_timeout <= 0) - return false; - - /* we are scanning, don't enable power save */ - if (local->scanning) - return false; - - if (!local->ps_sdata) - return false; - - /* No point if we're going to suspend */ - if (local->quiescing) - return false; - - return true; -} - static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { @@ -1435,18 +1481,6 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, int headroom; bool may_encrypt; - if (need_dynamic_ps(local)) { - if (local->hw.conf.flags & IEEE80211_CONF_PS) { - ieee80211_stop_queues_by_reason(&local->hw, - IEEE80211_QUEUE_STOP_REASON_PS); - ieee80211_queue_work(&local->hw, - &local->dynamic_ps_disable_work); - } - - mod_timer(&local->dynamic_ps_timer, jiffies + - msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); - } - rcu_read_lock(); if (unlikely(sdata->vif.type == NL80211_IFTYPE_MONITOR)) { @@ -1511,7 +1545,7 @@ static void ieee80211_xmit(struct ieee80211_sub_if_data *sdata, return; } - ieee80211_select_queue(local, skb); + ieee80211_set_qos_hdr(local, skb); ieee80211_tx(sdata, skb, false); rcu_read_unlock(); } @@ -2060,6 +2094,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, struct beacon_data *beacon; struct ieee80211_supported_band *sband; enum ieee80211_band band = local->hw.conf.channel->band; + struct ieee80211_tx_rate_control txrc; sband = local->hw.wiphy->bands[band]; @@ -2167,21 +2202,25 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); info->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; + info->flags |= IEEE80211_TX_CTL_NO_ACK; info->band = band; - /* - * XXX: For now, always use the lowest rate - */ - info->control.rates[0].idx = 0; - info->control.rates[0].count = 1; - info->control.rates[1].idx = -1; - info->control.rates[2].idx = -1; - info->control.rates[3].idx = -1; - info->control.rates[4].idx = -1; - BUILD_BUG_ON(IEEE80211_TX_MAX_RATES != 5); + + memset(&txrc, 0, sizeof(txrc)); + txrc.hw = hw; + txrc.sband = sband; + txrc.bss_conf = &sdata->vif.bss_conf; + txrc.skb = skb; + txrc.reported_rate.idx = -1; + txrc.rate_idx_mask = sdata->rc_rateidx_mask[band]; + if (txrc.rate_idx_mask == (1 << sband->n_bitrates) - 1) + txrc.max_rate_idx = -1; + else + txrc.max_rate_idx = fls(txrc.rate_idx_mask) - 1; + txrc.ap = true; + rate_control_get_rate(sdata, NULL, &txrc); info->control.vif = vif; - info->flags |= IEEE80211_TX_CTL_NO_ACK; info->flags |= IEEE80211_TX_CTL_CLEAR_PS_FILT; info->flags |= IEEE80211_TX_CTL_ASSIGN_SEQ; out: @@ -2190,6 +2229,134 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, } EXPORT_SYMBOL(ieee80211_beacon_get_tim); +struct sk_buff *ieee80211_pspoll_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_managed *ifmgd; + struct ieee80211_pspoll *pspoll; + struct ieee80211_local *local; + struct sk_buff *skb; + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) + return NULL; + + sdata = vif_to_sdata(vif); + ifmgd = &sdata->u.mgd; + local = sdata->local; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*pspoll)); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for " + "pspoll template\n", sdata->name); + return NULL; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + pspoll = (struct ieee80211_pspoll *) skb_put(skb, sizeof(*pspoll)); + memset(pspoll, 0, sizeof(*pspoll)); + pspoll->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | + IEEE80211_STYPE_PSPOLL); + pspoll->aid = cpu_to_le16(ifmgd->aid); + + /* aid in PS-Poll has its two MSBs each set to 1 */ + pspoll->aid |= cpu_to_le16(1 << 15 | 1 << 14); + + memcpy(pspoll->bssid, ifmgd->bssid, ETH_ALEN); + memcpy(pspoll->ta, vif->addr, ETH_ALEN); + + return skb; +} +EXPORT_SYMBOL(ieee80211_pspoll_get); + +struct sk_buff *ieee80211_nullfunc_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ieee80211_hdr_3addr *nullfunc; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_if_managed *ifmgd; + struct ieee80211_local *local; + struct sk_buff *skb; + + if (WARN_ON(vif->type != NL80211_IFTYPE_STATION)) + return NULL; + + sdata = vif_to_sdata(vif); + ifmgd = &sdata->u.mgd; + local = sdata->local; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*nullfunc)); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for nullfunc " + "template\n", sdata->name); + return NULL; + } + skb_reserve(skb, local->hw.extra_tx_headroom); + + nullfunc = (struct ieee80211_hdr_3addr *) skb_put(skb, + sizeof(*nullfunc)); + memset(nullfunc, 0, sizeof(*nullfunc)); + nullfunc->frame_control = cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC | + IEEE80211_FCTL_TODS); + memcpy(nullfunc->addr1, ifmgd->bssid, ETH_ALEN); + memcpy(nullfunc->addr2, vif->addr, ETH_ALEN); + memcpy(nullfunc->addr3, ifmgd->bssid, ETH_ALEN); + + return skb; +} +EXPORT_SYMBOL(ieee80211_nullfunc_get); + +struct sk_buff *ieee80211_probereq_get(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *ssid, size_t ssid_len, + const u8 *ie, size_t ie_len) +{ + struct ieee80211_sub_if_data *sdata; + struct ieee80211_local *local; + struct ieee80211_hdr_3addr *hdr; + struct sk_buff *skb; + size_t ie_ssid_len; + u8 *pos; + + sdata = vif_to_sdata(vif); + local = sdata->local; + ie_ssid_len = 2 + ssid_len; + + skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*hdr) + + ie_ssid_len + ie_len); + if (!skb) { + printk(KERN_DEBUG "%s: failed to allocate buffer for probe " + "request template\n", sdata->name); + return NULL; + } + + skb_reserve(skb, local->hw.extra_tx_headroom); + + hdr = (struct ieee80211_hdr_3addr *) skb_put(skb, sizeof(*hdr)); + memset(hdr, 0, sizeof(*hdr)); + hdr->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_PROBE_REQ); + memset(hdr->addr1, 0xff, ETH_ALEN); + memcpy(hdr->addr2, vif->addr, ETH_ALEN); + memset(hdr->addr3, 0xff, ETH_ALEN); + + pos = skb_put(skb, ie_ssid_len); + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + if (ssid) + memcpy(pos, ssid, ssid_len); + pos += ssid_len; + + if (ie) { + pos = skb_put(skb, ie_len); + memcpy(pos, ie, ie_len); + } + + return skb; +} +EXPORT_SYMBOL(ieee80211_probereq_get); + void ieee80211_rts_get(struct ieee80211_hw *hw, struct ieee80211_vif *vif, const void *frame, size_t frame_len, const struct ieee80211_tx_info *frame_txctl, @@ -2289,6 +2456,9 @@ void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) skb_set_network_header(skb, 0); skb_set_transport_header(skb, 0); + /* send all internal mgmt frames on VO */ + skb_set_queue_mapping(skb, 0); + /* * The other path calling ieee80211_xmit is from the tasklet, * and while we can handle concurrent transmissions locking diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 7e38858a928..ca170b417da 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -18,7 +18,6 @@ #include <linux/skbuff.h> #include <linux/etherdevice.h> #include <linux/if_arp.h> -#include <linux/wireless.h> #include <linux/bitmap.h> #include <linux/crc32.h> #include <net/net_namespace.h> @@ -269,6 +268,7 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata; if (WARN_ON(queue >= hw->queues)) return; @@ -281,6 +281,11 @@ static void __ieee80211_wake_queue(struct ieee80211_hw *hw, int queue, if (!skb_queue_empty(&local->pending[queue])) tasklet_schedule(&local->tx_pending_tasklet); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) + netif_tx_wake_queue(netdev_get_tx_queue(sdata->dev, queue)); + rcu_read_unlock(); } void ieee80211_wake_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -305,11 +310,17 @@ static void __ieee80211_stop_queue(struct ieee80211_hw *hw, int queue, enum queue_stop_reason reason) { struct ieee80211_local *local = hw_to_local(hw); + struct ieee80211_sub_if_data *sdata; if (WARN_ON(queue >= hw->queues)) return; __set_bit(reason, &local->queue_stop_reasons[queue]); + + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) + netif_tx_stop_queue(netdev_get_tx_queue(sdata->dev, queue)); + rcu_read_unlock(); } void ieee80211_stop_queue_by_reason(struct ieee80211_hw *hw, int queue, @@ -781,6 +792,8 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata) break; } + qparam.uapsd = false; + drv_conf_tx(local, queue, &qparam); } } @@ -989,40 +1002,33 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos; - - skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + - ie_len); - if (!skb) { - printk(KERN_DEBUG "%s: failed to allocate buffer for probe " - "request\n", sdata->name); + size_t buf_len; + u8 *buf; + + /* FIXME: come up with a proper value */ + buf = kmalloc(200 + ie_len, GFP_KERNEL); + if (!buf) { + printk(KERN_DEBUG "%s: failed to allocate temporary IE " + "buffer\n", sdata->name); return; } - skb_reserve(skb, local->hw.extra_tx_headroom); - mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24); - memset(mgmt, 0, 24); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_PROBE_REQ); - memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); + buf_len = ieee80211_build_preq_ies(local, buf, ie, ie_len, + local->hw.conf.channel->band); + + skb = ieee80211_probereq_get(&local->hw, &sdata->vif, + ssid, ssid_len, + buf, buf_len); + if (dst) { + mgmt = (struct ieee80211_mgmt *) skb->data; memcpy(mgmt->da, dst, ETH_ALEN); memcpy(mgmt->bssid, dst, ETH_ALEN); - } else { - memset(mgmt->da, 0xff, ETH_ALEN); - memset(mgmt->bssid, 0xff, ETH_ALEN); } - pos = skb_put(skb, 2 + ssid_len); - *pos++ = WLAN_EID_SSID; - *pos++ = ssid_len; - memcpy(pos, ssid, ssid_len); - pos += ssid_len; - - skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len, - local->hw.conf.channel->band)); IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT; ieee80211_tx_skb(sdata, skb); + kfree(buf); } u32 ieee80211_sta_get_rates(struct ieee80211_local *local, @@ -1066,9 +1072,9 @@ void ieee80211_stop_device(struct ieee80211_local *local) ieee80211_led_radio(local, false); cancel_work_sync(&local->reconfig_filter); - drv_stop(local); flush_workqueue(local->workqueue); + drv_stop(local); } int ieee80211_reconfig(struct ieee80211_local *local) @@ -1094,7 +1100,7 @@ int ieee80211_reconfig(struct ieee80211_local *local) if (res) { WARN(local->suspended, "Harware became unavailable " "upon resume. This is could be a software issue" - "prior to suspend or a harware issue\n"); + "prior to suspend or a hardware issue\n"); return res; } diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index b19b7696f3a..34e6d02da77 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -44,22 +44,69 @@ static int wme_downgrade_ac(struct sk_buff *skb) } -/* Indicate which queue to use. */ -static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) +/* Indicate which queue to use. */ +u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_local *local = sdata->local; + struct sta_info *sta = NULL; + u32 sta_flags = 0; + const u8 *ra = NULL; + bool qos = false; - if (!ieee80211_is_data(hdr->frame_control)) { - /* management frames go on AC_VO queue, but are sent - * without QoS control fields */ - return 0; + if (local->hw.queues < 4 || skb->len < 6) { + skb->priority = 0; /* required for correct WPA/11i MIC */ + return min_t(u16, local->hw.queues - 1, + ieee802_1d_to_ac[skb->priority]); + } + + rcu_read_lock(); + switch (sdata->vif.type) { + case NL80211_IFTYPE_AP_VLAN: + rcu_read_lock(); + sta = rcu_dereference(sdata->u.vlan.sta); + if (sta) + sta_flags = get_sta_flags(sta); + rcu_read_unlock(); + if (sta) + break; + case NL80211_IFTYPE_AP: + ra = skb->data; + break; + case NL80211_IFTYPE_WDS: + ra = sdata->u.wds.remote_addr; + break; +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + /* + * XXX: This is clearly broken ... but already was before, + * because ieee80211_fill_mesh_addresses() would clear A1 + * except for multicast addresses. + */ + break; +#endif + case NL80211_IFTYPE_STATION: + ra = sdata->u.mgd.bssid; + break; + case NL80211_IFTYPE_ADHOC: + ra = skb->data; + break; + default: + break; } - if (0 /* injected */) { - /* use AC from radiotap */ + if (!sta && ra && !is_multicast_ether_addr(ra)) { + sta = sta_info_get(sdata, ra); + if (sta) + sta_flags = get_sta_flags(sta); } - if (!ieee80211_is_data_qos(hdr->frame_control)) { + if (sta_flags & WLAN_STA_WME) + qos = true; + + rcu_read_unlock(); + + if (!qos) { skb->priority = 0; /* required for correct WPA/11i MIC */ return ieee802_1d_to_ac[skb->priority]; } @@ -68,6 +115,12 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) * data frame has */ skb->priority = cfg80211_classify8021d(skb); + return ieee80211_downgrade_queue(local, skb); +} + +u16 ieee80211_downgrade_queue(struct ieee80211_local *local, + struct sk_buff *skb) +{ /* in case we are a client verify acm is not set for this ac */ while (unlikely(local->wmm_acm & BIT(skb->priority))) { if (wme_downgrade_ac(skb)) { @@ -85,24 +138,17 @@ static u16 classify80211(struct ieee80211_local *local, struct sk_buff *skb) return ieee802_1d_to_ac[skb->priority]; } -void ieee80211_select_queue(struct ieee80211_local *local, struct sk_buff *skb) +void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - u16 queue; - u8 tid; - - queue = classify80211(local, skb); - if (unlikely(queue >= local->hw.queues)) - queue = local->hw.queues - 1; - - /* - * Now we know the 1d priority, fill in the QoS header if - * there is one (and we haven't done this before). - */ + struct ieee80211_hdr *hdr = (void *)skb->data; + + /* Fill in the QoS header if there is one. */ if (ieee80211_is_data_qos(hdr->frame_control)) { u8 *p = ieee80211_get_qos_ctl(hdr); - u8 ack_policy = 0; + u8 ack_policy = 0, tid; + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + if (unlikely(local->wifi_wme_noack_test)) ack_policy |= QOS_CONTROL_ACK_POLICY_NOACK << QOS_CONTROL_ACK_POLICY_SHIFT; @@ -110,6 +156,4 @@ void ieee80211_select_queue(struct ieee80211_local *local, struct sk_buff *skb) *p++ = ack_policy | tid; *p = 0; } - - skb_set_queue_mapping(skb, queue); } diff --git a/net/mac80211/wme.h b/net/mac80211/wme.h index d4fd87ca511..6053b1c9fee 100644 --- a/net/mac80211/wme.h +++ b/net/mac80211/wme.h @@ -20,7 +20,11 @@ extern const int ieee802_1d_to_ac[8]; -void ieee80211_select_queue(struct ieee80211_local *local, - struct sk_buff *skb); +u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb); +void ieee80211_set_qos_hdr(struct ieee80211_local *local, struct sk_buff *skb); +u16 ieee80211_downgrade_queue(struct ieee80211_local *local, + struct sk_buff *skb); + #endif /* _WME_H */ diff --git a/net/mac80211/work.c b/net/mac80211/work.c index ea89ed70734..81bd5d592bb 100644 --- a/net/mac80211/work.c +++ b/net/mac80211/work.c @@ -202,7 +202,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, struct ieee80211_local *local = sdata->local; struct sk_buff *skb; struct ieee80211_mgmt *mgmt; - u8 *pos; + u8 *pos, qos_info; const u8 *ies; size_t offset = 0, noffset; int i, len, count, rates_len, supp_rates_len; @@ -375,6 +375,14 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, } if (wk->assoc.wmm_used && local->hw.queues >= 4) { + if (wk->assoc.uapsd_used) { + qos_info = local->uapsd_queues; + qos_info |= (local->uapsd_max_sp_len << + IEEE80211_WMM_IE_STA_QOSINFO_SP_SHIFT); + } else { + qos_info = 0; + } + pos = skb_put(skb, 9); *pos++ = WLAN_EID_VENDOR_SPECIFIC; *pos++ = 7; /* len */ @@ -384,7 +392,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, *pos++ = 2; /* WME */ *pos++ = 0; /* WME info */ *pos++ = 1; /* WME ver */ - *pos++ = 0; + *pos++ = qos_info; } /* add any remaining custom (i.e. vendor specific here) IEs */ @@ -531,9 +539,9 @@ ieee80211_remain_on_channel_timeout(struct ieee80211_work *wk) wk->remain.started = true; wk->timeout = jiffies + msecs_to_jiffies(wk->remain.duration); - cfg80211_ready_on_channel(wk->sdata->dev, (u64)wk, wk->chan, - wk->chan_type, wk->remain.duration, - GFP_KERNEL); + cfg80211_ready_on_channel(wk->sdata->dev, (unsigned long) wk, + wk->chan, wk->chan_type, + wk->remain.duration, GFP_KERNEL); return WORK_ACT_NONE; } @@ -818,6 +826,7 @@ static void ieee80211_work_work(struct work_struct *work) wk->chan == local->tmp_channel && wk->chan_type == local->tmp_channel_type) { wk->started = true; + wk->timeout = jiffies; } if (!wk->started && !local->tmp_channel) { @@ -935,6 +944,9 @@ void ieee80211_add_work(struct ieee80211_work *wk) if (WARN_ON(!wk->done)) return; + if (WARN_ON(!ieee80211_sdata_running(wk->sdata))) + return; + wk->started = false; local = wk->sdata->local; @@ -1027,7 +1039,7 @@ static enum work_done_result ieee80211_remain_done(struct ieee80211_work *wk, /* * We are done serving the remain-on-channel command. */ - cfg80211_remain_on_channel_expired(wk->sdata->dev, (u64)wk, + cfg80211_remain_on_channel_expired(wk->sdata->dev, (unsigned long) wk, wk->chan, wk->chan_type, GFP_KERNEL); @@ -1053,7 +1065,7 @@ int ieee80211_wk_remain_on_channel(struct ieee80211_sub_if_data *sdata, wk->remain.duration = duration; - *cookie = (u64)wk; + *cookie = (unsigned long) wk; ieee80211_add_work(wk); @@ -1069,7 +1081,7 @@ int ieee80211_wk_cancel_remain_on_channel(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->work_mtx); list_for_each_entry_safe(wk, tmp, &local->work_list, list) { - if ((u64)wk == cookie) { + if ((unsigned long) wk == cookie) { wk->timeout = jiffies; found = true; break; diff --git a/net/wireless/core.c b/net/wireless/core.c index c2a2c563d21..0a545bb6ed0 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -402,6 +402,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.retry_long = 4; rdev->wiphy.frag_threshold = (u32) -1; rdev->wiphy.rts_threshold = (u32) -1; + rdev->wiphy.coverage_class = 0; return &rdev->wiphy; } diff --git a/net/wireless/core.h b/net/wireless/core.h index 30ec95f05b5..2d6a6b9c0c4 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -111,7 +111,8 @@ struct cfg80211_internal_bss { unsigned long ts; struct kref ref; atomic_t hold; - bool ies_allocated; + bool beacon_ies_allocated; + bool proberesp_ies_allocated; /* must be last because of priv member */ struct cfg80211_bss pub; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index e3bee3cecdf..4af7991a9ec 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -69,6 +69,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 }, [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, @@ -143,6 +144,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { .len = WLAN_PMKID_LEN }, [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, + [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED }, }; /* policy for the attributes */ @@ -444,6 +446,8 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.frag_threshold); NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, dev->wiphy.rts_threshold); + NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, + dev->wiphy.coverage_class); NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, dev->wiphy.max_scan_ssids); @@ -572,6 +576,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(del_pmksa, DEL_PMKSA); CMD(flush_pmksa, FLUSH_PMKSA); CMD(remain_on_channel, REMAIN_ON_CHANNEL); + CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); @@ -684,6 +689,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) u32 changed; u8 retry_short = 0, retry_long = 0; u32 frag_threshold = 0, rts_threshold = 0; + u8 coverage_class = 0; rtnl_lock(); @@ -806,9 +812,16 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) changed |= WIPHY_PARAM_RTS_THRESHOLD; } + if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) { + coverage_class = nla_get_u8( + info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]); + changed |= WIPHY_PARAM_COVERAGE_CLASS; + } + if (changed) { u8 old_retry_short, old_retry_long; u32 old_frag_threshold, old_rts_threshold; + u8 old_coverage_class; if (!rdev->ops->set_wiphy_params) { result = -EOPNOTSUPP; @@ -819,6 +832,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) old_retry_long = rdev->wiphy.retry_long; old_frag_threshold = rdev->wiphy.frag_threshold; old_rts_threshold = rdev->wiphy.rts_threshold; + old_coverage_class = rdev->wiphy.coverage_class; if (changed & WIPHY_PARAM_RETRY_SHORT) rdev->wiphy.retry_short = retry_short; @@ -828,6 +842,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rdev->wiphy.frag_threshold = frag_threshold; if (changed & WIPHY_PARAM_RTS_THRESHOLD) rdev->wiphy.rts_threshold = rts_threshold; + if (changed & WIPHY_PARAM_COVERAGE_CLASS) + rdev->wiphy.coverage_class = coverage_class; result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed); if (result) { @@ -835,6 +851,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) rdev->wiphy.retry_long = old_retry_long; rdev->wiphy.frag_threshold = old_frag_threshold; rdev->wiphy.rts_threshold = old_rts_threshold; + rdev->wiphy.coverage_class = old_coverage_class; } } @@ -3146,6 +3163,10 @@ static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags, NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS, res->len_information_elements, res->information_elements); + if (res->beacon_ies && res->len_beacon_ies && + res->beacon_ies != res->information_elements) + NLA_PUT(msg, NL80211_BSS_BEACON_IES, + res->len_beacon_ies, res->beacon_ies); if (res->tsf) NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf); if (res->beacon_interval) @@ -4423,6 +4444,109 @@ static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, return err; } +static u32 rateset_to_mask(struct ieee80211_supported_band *sband, + u8 *rates, u8 rates_len) +{ + u8 i; + u32 mask = 0; + + for (i = 0; i < rates_len; i++) { + int rate = (rates[i] & 0x7f) * 5; + int ridx; + for (ridx = 0; ridx < sband->n_bitrates; ridx++) { + struct ieee80211_rate *srate = + &sband->bitrates[ridx]; + if (rate == srate->bitrate) { + mask |= 1 << ridx; + break; + } + } + if (ridx == sband->n_bitrates) + return 0; /* rate not found */ + } + + return mask; +} + +static struct nla_policy +nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] __read_mostly = { + [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY, + .len = NL80211_MAX_SUPP_RATES }, +}; + +static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb, + struct genl_info *info) +{ + struct nlattr *tb[NL80211_TXRATE_MAX + 1]; + struct cfg80211_registered_device *rdev; + struct cfg80211_bitrate_mask mask; + int err, rem, i; + struct net_device *dev; + struct nlattr *tx_rates; + struct ieee80211_supported_band *sband; + + if (info->attrs[NL80211_ATTR_TX_RATES] == NULL) + return -EINVAL; + + rtnl_lock(); + + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); + if (err) + goto unlock_rtnl; + + if (!rdev->ops->set_bitrate_mask) { + err = -EOPNOTSUPP; + goto unlock; + } + + memset(&mask, 0, sizeof(mask)); + /* Default to all rates enabled */ + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + sband = rdev->wiphy.bands[i]; + mask.control[i].legacy = + sband ? (1 << sband->n_bitrates) - 1 : 0; + } + + /* + * The nested attribute uses enum nl80211_band as the index. This maps + * directly to the enum ieee80211_band values used in cfg80211. + */ + nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) + { + enum ieee80211_band band = nla_type(tx_rates); + if (band < 0 || band >= IEEE80211_NUM_BANDS) { + err = -EINVAL; + goto unlock; + } + sband = rdev->wiphy.bands[band]; + if (sband == NULL) { + err = -EINVAL; + goto unlock; + } + nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates), + nla_len(tx_rates), nl80211_txattr_policy); + if (tb[NL80211_TXRATE_LEGACY]) { + mask.control[band].legacy = rateset_to_mask( + sband, + nla_data(tb[NL80211_TXRATE_LEGACY]), + nla_len(tb[NL80211_TXRATE_LEGACY])); + if (mask.control[band].legacy == 0) { + err = -EINVAL; + goto unlock; + } + } + } + + err = rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask); + + unlock: + dev_put(dev); + cfg80211_unlock_rdev(rdev); + unlock_rtnl: + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -4697,6 +4821,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_TX_BITRATE_MASK, + .doit = nl80211_set_tx_bitrate_mask, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 87ea60d84c3..5f8071de795 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -43,6 +43,15 @@ #include "regdb.h" #include "nl80211.h" +#ifdef CONFIG_CFG80211_REG_DEBUG +#define REG_DBG_PRINT(format, args...) \ + do { \ + printk(KERN_DEBUG format , ## args); \ + } while (0) +#else +#define REG_DBG_PRINT(args...) +#endif + /* Receipt of information from last regulatory request */ static struct regulatory_request *last_request; @@ -476,12 +485,212 @@ static bool freq_in_rule_band(const struct ieee80211_freq_range *freq_range, } /* + * This is a work around for sanity checking ieee80211_channel_to_frequency()'s + * work. ieee80211_channel_to_frequency() can for example currently provide a + * 2 GHz channel when in fact a 5 GHz channel was desired. An example would be + * an AP providing channel 8 on a country IE triplet when it sent this on the + * 5 GHz band, that channel is designed to be channel 8 on 5 GHz, not a 2 GHz + * channel. + * + * This can be removed once ieee80211_channel_to_frequency() takes in a band. + */ +static bool chan_in_band(int chan, enum ieee80211_band band) +{ + int center_freq = ieee80211_channel_to_frequency(chan); + + switch (band) { + case IEEE80211_BAND_2GHZ: + if (center_freq <= 2484) + return true; + return false; + case IEEE80211_BAND_5GHZ: + if (center_freq >= 5005) + return true; + return false; + default: + return false; + } +} + +/* + * Some APs may send a country IE triplet for each channel they + * support and while this is completely overkill and silly we still + * need to support it. We avoid making a single rule for each channel + * though and to help us with this we use this helper to find the + * actual subband end channel. These type of country IE triplet + * scenerios are handled then, all yielding two regulaotry rules from + * parsing a country IE: + * + * [1] + * [2] + * [36] + * [40] + * + * [1] + * [2-4] + * [5-12] + * [36] + * [40-44] + * + * [1-4] + * [5-7] + * [36-44] + * [48-64] + * + * [36-36] + * [40-40] + * [44-44] + * [48-48] + * [52-52] + * [56-56] + * [60-60] + * [64-64] + * [100-100] + * [104-104] + * [108-108] + * [112-112] + * [116-116] + * [120-120] + * [124-124] + * [128-128] + * [132-132] + * [136-136] + * [140-140] + * + * Returns 0 if the IE has been found to be invalid in the middle + * somewhere. + */ +static int max_subband_chan(enum ieee80211_band band, + int orig_cur_chan, + int orig_end_channel, + s8 orig_max_power, + u8 **country_ie, + u8 *country_ie_len) +{ + u8 *triplets_start = *country_ie; + u8 len_at_triplet = *country_ie_len; + int end_subband_chan = orig_end_channel; + + /* + * We'll deal with padding for the caller unless + * its not immediate and we don't process any channels + */ + if (*country_ie_len == 1) { + *country_ie += 1; + *country_ie_len -= 1; + return orig_end_channel; + } + + /* Move to the next triplet and then start search */ + *country_ie += 3; + *country_ie_len -= 3; + + if (!chan_in_band(orig_cur_chan, band)) + return 0; + + while (*country_ie_len >= 3) { + int end_channel = 0; + struct ieee80211_country_ie_triplet *triplet = + (struct ieee80211_country_ie_triplet *) *country_ie; + int cur_channel = 0, next_expected_chan; + + /* means last triplet is completely unrelated to this one */ + if (triplet->ext.reg_extension_id >= + IEEE80211_COUNTRY_EXTENSION_ID) { + *country_ie -= 3; + *country_ie_len += 3; + break; + } + + if (triplet->chans.first_channel == 0) { + *country_ie += 1; + *country_ie_len -= 1; + if (*country_ie_len != 0) + return 0; + break; + } + + if (triplet->chans.num_channels == 0) + return 0; + + /* Monitonically increasing channel order */ + if (triplet->chans.first_channel <= end_subband_chan) + return 0; + + if (!chan_in_band(triplet->chans.first_channel, band)) + return 0; + + /* 2 GHz */ + if (triplet->chans.first_channel <= 14) { + end_channel = triplet->chans.first_channel + + triplet->chans.num_channels - 1; + } + else { + end_channel = triplet->chans.first_channel + + (4 * (triplet->chans.num_channels - 1)); + } + + if (!chan_in_band(end_channel, band)) + return 0; + + if (orig_max_power != triplet->chans.max_power) { + *country_ie -= 3; + *country_ie_len += 3; + break; + } + + cur_channel = triplet->chans.first_channel; + + /* The key is finding the right next expected channel */ + if (band == IEEE80211_BAND_2GHZ) + next_expected_chan = end_subband_chan + 1; + else + next_expected_chan = end_subband_chan + 4; + + if (cur_channel != next_expected_chan) { + *country_ie -= 3; + *country_ie_len += 3; + break; + } + + end_subband_chan = end_channel; + + /* Move to the next one */ + *country_ie += 3; + *country_ie_len -= 3; + + /* + * Padding needs to be dealt with if we processed + * some channels. + */ + if (*country_ie_len == 1) { + *country_ie += 1; + *country_ie_len -= 1; + break; + } + + /* If seen, the IE is invalid */ + if (*country_ie_len == 2) + return 0; + } + + if (end_subband_chan == orig_end_channel) { + *country_ie = triplets_start; + *country_ie_len = len_at_triplet; + return orig_end_channel; + } + + return end_subband_chan; +} + +/* * Converts a country IE to a regulatory domain. A regulatory domain * structure has a lot of information which the IE doesn't yet have, * so for the other values we use upper max values as we will intersect * with our userspace regulatory agent to get lower bounds. */ static struct ieee80211_regdomain *country_ie_2_rd( + enum ieee80211_band band, u8 *country_ie, u8 country_ie_len, u32 *checksum) @@ -543,10 +752,29 @@ static struct ieee80211_regdomain *country_ie_2_rd( continue; } + /* + * APs can add padding to make length divisible + * by two, required by the spec. + */ + if (triplet->chans.first_channel == 0) { + country_ie++; + country_ie_len--; + /* This is expected to be at the very end only */ + if (country_ie_len != 0) + return NULL; + break; + } + + if (triplet->chans.num_channels == 0) + return NULL; + + if (!chan_in_band(triplet->chans.first_channel, band)) + return NULL; + /* 2 GHz */ - if (triplet->chans.first_channel <= 14) + if (band == IEEE80211_BAND_2GHZ) end_channel = triplet->chans.first_channel + - triplet->chans.num_channels; + triplet->chans.num_channels - 1; else /* * 5 GHz -- For example in country IEs if the first @@ -561,6 +789,24 @@ static struct ieee80211_regdomain *country_ie_2_rd( (4 * (triplet->chans.num_channels - 1)); cur_channel = triplet->chans.first_channel; + + /* + * Enhancement for APs that send a triplet for every channel + * or for whatever reason sends triplets with multiple channels + * separated when in fact they should be together. + */ + end_channel = max_subband_chan(band, + cur_channel, + end_channel, + triplet->chans.max_power, + &country_ie, + &country_ie_len); + if (!end_channel) + return NULL; + + if (!chan_in_band(end_channel, band)) + return NULL; + cur_sub_max_channel = end_channel; /* Basic sanity check */ @@ -591,10 +837,13 @@ static struct ieee80211_regdomain *country_ie_2_rd( last_sub_max_channel = cur_sub_max_channel; - country_ie += 3; - country_ie_len -= 3; num_rules++; + if (country_ie_len >= 3) { + country_ie += 3; + country_ie_len -= 3; + } + /* * Note: this is not a IEEE requirement but * simply a memory requirement @@ -637,6 +886,12 @@ static struct ieee80211_regdomain *country_ie_2_rd( continue; } + if (triplet->chans.first_channel == 0) { + country_ie++; + country_ie_len--; + break; + } + reg_rule = &rd->reg_rules[i]; freq_range = ®_rule->freq_range; power_rule = ®_rule->power_rule; @@ -644,13 +899,20 @@ static struct ieee80211_regdomain *country_ie_2_rd( reg_rule->flags = flags; /* 2 GHz */ - if (triplet->chans.first_channel <= 14) + if (band == IEEE80211_BAND_2GHZ) end_channel = triplet->chans.first_channel + - triplet->chans.num_channels; + triplet->chans.num_channels -1; else end_channel = triplet->chans.first_channel + (4 * (triplet->chans.num_channels - 1)); + end_channel = max_subband_chan(band, + triplet->chans.first_channel, + end_channel, + triplet->chans.max_power, + &country_ie, + &country_ie_len); + /* * The +10 is since the regulatory domain expects * the actual band edge, not the center of freq for @@ -671,12 +933,15 @@ static struct ieee80211_regdomain *country_ie_2_rd( */ freq_range->max_bandwidth_khz = MHZ_TO_KHZ(40); power_rule->max_antenna_gain = DBI_TO_MBI(100); - power_rule->max_eirp = DBM_TO_MBM(100); + power_rule->max_eirp = DBM_TO_MBM(triplet->chans.max_power); - country_ie += 3; - country_ie_len -= 3; i++; + if (country_ie_len >= 3) { + country_ie += 3; + country_ie_len -= 3; + } + BUG_ON(i > NL80211_MAX_SUPP_REG_RULES); } @@ -972,25 +1237,21 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, if (r == -ERANGE && last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) { -#ifdef CONFIG_CFG80211_REG_DEBUG - printk(KERN_DEBUG "cfg80211: Leaving channel %d MHz " + REG_DBG_PRINT("cfg80211: Leaving channel %d MHz " "intact on %s - no rule found in band on " "Country IE\n", - chan->center_freq, wiphy_name(wiphy)); -#endif + chan->center_freq, wiphy_name(wiphy)); } else { /* * In this case we know the country IE has at least one reg rule * for the band so we respect its band definitions */ -#ifdef CONFIG_CFG80211_REG_DEBUG if (last_request->initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE) - printk(KERN_DEBUG "cfg80211: Disabling " + REG_DBG_PRINT("cfg80211: Disabling " "channel %d MHz on %s due to " "Country IE\n", chan->center_freq, wiphy_name(wiphy)); -#endif flags |= IEEE80211_CHAN_DISABLED; chan->flags = flags; } @@ -1685,7 +1946,7 @@ int regulatory_hint_user(const char *alpha2) request->wiphy_idx = WIPHY_IDX_STALE; request->alpha2[0] = alpha2[0]; request->alpha2[1] = alpha2[1]; - request->initiator = NL80211_REGDOM_SET_BY_USER, + request->initiator = NL80211_REGDOM_SET_BY_USER; queue_regulatory_request(request); @@ -1753,8 +2014,9 @@ static bool reg_same_country_ie_hint(struct wiphy *wiphy, * therefore cannot iterate over the rdev list here. */ void regulatory_hint_11d(struct wiphy *wiphy, - u8 *country_ie, - u8 country_ie_len) + enum ieee80211_band band, + u8 *country_ie, + u8 country_ie_len) { struct ieee80211_regdomain *rd = NULL; char alpha2[2]; @@ -1800,9 +2062,11 @@ void regulatory_hint_11d(struct wiphy *wiphy, wiphy_idx_valid(last_request->wiphy_idx))) goto out; - rd = country_ie_2_rd(country_ie, country_ie_len, &checksum); - if (!rd) + rd = country_ie_2_rd(band, country_ie, country_ie_len, &checksum); + if (!rd) { + REG_DBG_PRINT("cfg80211: Ignoring bogus country IE\n"); goto out; + } /* * This will not happen right now but we leave it here for the @@ -1870,13 +2134,12 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, if (!reg_beacon) return -ENOMEM; -#ifdef CONFIG_CFG80211_REG_DEBUG - printk(KERN_DEBUG "cfg80211: Found new beacon on " - "frequency: %d MHz (Ch %d) on %s\n", - beacon_chan->center_freq, - ieee80211_frequency_to_channel(beacon_chan->center_freq), - wiphy_name(wiphy)); -#endif + REG_DBG_PRINT("cfg80211: Found new beacon on " + "frequency: %d MHz (Ch %d) on %s\n", + beacon_chan->center_freq, + ieee80211_frequency_to_channel(beacon_chan->center_freq), + wiphy_name(wiphy)); + memcpy(®_beacon->chan, beacon_chan, sizeof(struct ieee80211_channel)); diff --git a/net/wireless/reg.h b/net/wireless/reg.h index 3362c7c069b..3018508226a 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h @@ -41,14 +41,25 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, * regulatory_hint_11d - hints a country IE as a regulatory domain * @wiphy: the wireless device giving the hint (used only for reporting * conflicts) + * @band: the band on which the country IE was received on. This determines + * the band we'll process the country IE channel triplets for. * @country_ie: pointer to the country IE * @country_ie_len: length of the country IE * * We will intersect the rd with the what CRDA tells us should apply * for the alpha2 this country IE belongs to, this prevents APs from * sending us incorrect or outdated information against a country. + * + * The AP is expected to provide Country IE channel triplets for the + * band it is on. It is technically possible for APs to send channel + * country IE triplets even for channels outside of the band they are + * in but for that they would have to use the regulatory extension + * in combination with a triplet but this behaviour is currently + * not observed. For this reason if a triplet is seen with channel + * information for a band the BSS is not present in it will be ignored. */ void regulatory_hint_11d(struct wiphy *wiphy, + enum ieee80211_band band, u8 *country_ie, u8 country_ie_len); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 0c2cbbebca9..06b0231ee5e 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -100,8 +100,10 @@ static void bss_release(struct kref *ref) if (bss->pub.free_priv) bss->pub.free_priv(&bss->pub); - if (bss->ies_allocated) - kfree(bss->pub.information_elements); + if (bss->beacon_ies_allocated) + kfree(bss->pub.beacon_ies); + if (bss->proberesp_ies_allocated) + kfree(bss->pub.proberesp_ies); BUG_ON(atomic_read(&bss->hold)); @@ -375,8 +377,7 @@ rb_find_bss(struct cfg80211_registered_device *dev, static struct cfg80211_internal_bss * cfg80211_bss_update(struct cfg80211_registered_device *dev, - struct cfg80211_internal_bss *res, - bool overwrite) + struct cfg80211_internal_bss *res) { struct cfg80211_internal_bss *found = NULL; const u8 *meshid, *meshcfg; @@ -418,28 +419,64 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, found->pub.capability = res->pub.capability; found->ts = res->ts; - /* overwrite IEs */ - if (overwrite) { + /* Update IEs */ + if (res->pub.proberesp_ies) { size_t used = dev->wiphy.bss_priv_size + sizeof(*res); - size_t ielen = res->pub.len_information_elements; + size_t ielen = res->pub.len_proberesp_ies; + + if (found->pub.proberesp_ies && + !found->proberesp_ies_allocated && + ksize(found) >= used + ielen) { + memcpy(found->pub.proberesp_ies, + res->pub.proberesp_ies, ielen); + found->pub.len_proberesp_ies = ielen; + } else { + u8 *ies = found->pub.proberesp_ies; + + if (found->proberesp_ies_allocated) + ies = krealloc(ies, ielen, GFP_ATOMIC); + else + ies = kmalloc(ielen, GFP_ATOMIC); + + if (ies) { + memcpy(ies, res->pub.proberesp_ies, + ielen); + found->proberesp_ies_allocated = true; + found->pub.proberesp_ies = ies; + found->pub.len_proberesp_ies = ielen; + } + } - if (!found->ies_allocated && ksize(found) >= used + ielen) { - memcpy(found->pub.information_elements, - res->pub.information_elements, ielen); - found->pub.len_information_elements = ielen; + /* Override possible earlier Beacon frame IEs */ + found->pub.information_elements = + found->pub.proberesp_ies; + found->pub.len_information_elements = + found->pub.len_proberesp_ies; + } + if (res->pub.beacon_ies) { + size_t used = dev->wiphy.bss_priv_size + sizeof(*res); + size_t ielen = res->pub.len_beacon_ies; + + if (found->pub.beacon_ies && + !found->beacon_ies_allocated && + ksize(found) >= used + ielen) { + memcpy(found->pub.beacon_ies, + res->pub.beacon_ies, ielen); + found->pub.len_beacon_ies = ielen; } else { - u8 *ies = found->pub.information_elements; + u8 *ies = found->pub.beacon_ies; - if (found->ies_allocated) + if (found->beacon_ies_allocated) ies = krealloc(ies, ielen, GFP_ATOMIC); else ies = kmalloc(ielen, GFP_ATOMIC); if (ies) { - memcpy(ies, res->pub.information_elements, ielen); - found->ies_allocated = true; - found->pub.information_elements = ies; - found->pub.len_information_elements = ielen; + memcpy(ies, res->pub.beacon_ies, + ielen); + found->beacon_ies_allocated = true; + found->pub.beacon_ies = ies; + found->pub.len_beacon_ies = ielen; } } } @@ -489,14 +526,26 @@ cfg80211_inform_bss(struct wiphy *wiphy, res->pub.tsf = timestamp; res->pub.beacon_interval = beacon_interval; res->pub.capability = capability; - /* point to after the private area */ - res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; - memcpy(res->pub.information_elements, ie, ielen); - res->pub.len_information_elements = ielen; + /* + * Since we do not know here whether the IEs are from a Beacon or Probe + * Response frame, we need to pick one of the options and only use it + * with the driver that does not provide the full Beacon/Probe Response + * frame. Use Beacon frame pointer to avoid indicating that this should + * override the information_elements pointer should we have received an + * earlier indication of Probe Response data. + * + * The initial buffer for the IEs is allocated with the BSS entry and + * is located after the private area. + */ + res->pub.beacon_ies = (u8 *)res + sizeof(*res) + privsz; + memcpy(res->pub.beacon_ies, ie, ielen); + res->pub.len_beacon_ies = ielen; + res->pub.information_elements = res->pub.beacon_ies; + res->pub.len_information_elements = res->pub.len_beacon_ies; kref_init(&res->ref); - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, 0); + res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); if (!res) return NULL; @@ -517,7 +566,6 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, struct cfg80211_internal_bss *res; size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); - bool overwrite; size_t privsz = wiphy->bss_priv_size; if (WARN_ON(wiphy->signal_type == NL80211_BSS_SIGNAL_UNSPEC && @@ -538,16 +586,28 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, res->pub.tsf = le64_to_cpu(mgmt->u.probe_resp.timestamp); res->pub.beacon_interval = le16_to_cpu(mgmt->u.probe_resp.beacon_int); res->pub.capability = le16_to_cpu(mgmt->u.probe_resp.capab_info); - /* point to after the private area */ - res->pub.information_elements = (u8 *)res + sizeof(*res) + privsz; - memcpy(res->pub.information_elements, mgmt->u.probe_resp.variable, ielen); - res->pub.len_information_elements = ielen; + /* + * The initial buffer for the IEs is allocated with the BSS entry and + * is located after the private area. + */ + if (ieee80211_is_probe_resp(mgmt->frame_control)) { + res->pub.proberesp_ies = (u8 *) res + sizeof(*res) + privsz; + memcpy(res->pub.proberesp_ies, mgmt->u.probe_resp.variable, + ielen); + res->pub.len_proberesp_ies = ielen; + res->pub.information_elements = res->pub.proberesp_ies; + res->pub.len_information_elements = res->pub.len_proberesp_ies; + } else { + res->pub.beacon_ies = (u8 *) res + sizeof(*res) + privsz; + memcpy(res->pub.beacon_ies, mgmt->u.beacon.variable, ielen); + res->pub.len_beacon_ies = ielen; + res->pub.information_elements = res->pub.beacon_ies; + res->pub.len_information_elements = res->pub.len_beacon_ies; + } kref_init(&res->ref); - overwrite = ieee80211_is_probe_resp(mgmt->frame_control); - - res = cfg80211_bss_update(wiphy_to_dev(wiphy), res, overwrite); + res = cfg80211_bss_update(wiphy_to_dev(wiphy), res); if (!res) return NULL; diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 2333d78187e..2ce5e1609a3 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -454,6 +454,7 @@ void __cfg80211_connect_result(struct net_device *dev, const u8 *bssid, * - and country_ie[1] which is the IE length */ regulatory_hint_11d(wdev->wiphy, + bss->channel->band, country_ie + 2, country_ie[1]); } diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 4198243a3df..966d2f01bea 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -1204,21 +1204,47 @@ int cfg80211_wext_siwrate(struct net_device *dev, struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); struct cfg80211_bitrate_mask mask; + u32 fixed, maxrate; + struct ieee80211_supported_band *sband; + int band, ridx; + bool match = false; if (!rdev->ops->set_bitrate_mask) return -EOPNOTSUPP; - mask.fixed = 0; - mask.maxrate = 0; + memset(&mask, 0, sizeof(mask)); + fixed = 0; + maxrate = 0; if (rate->value < 0) { /* nothing */ } else if (rate->fixed) { - mask.fixed = rate->value / 1000; /* kbps */ + fixed = rate->value / 100000; } else { - mask.maxrate = rate->value / 1000; /* kbps */ + maxrate = rate->value / 100000; + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wdev->wiphy->bands[band]; + if (sband == NULL) + continue; + for (ridx = 0; ridx < sband->n_bitrates; ridx++) { + struct ieee80211_rate *srate = &sband->bitrates[ridx]; + if (fixed == srate->bitrate) { + mask.control[band].legacy = 1 << ridx; + match = true; + break; + } + if (srate->bitrate <= maxrate) { + mask.control[band].legacy |= 1 << ridx; + match = true; + } + } } + if (!match) + return -EINVAL; + return rdev->ops->set_bitrate_mask(wdev->wiphy, dev, NULL, &mask); } EXPORT_SYMBOL_GPL(cfg80211_wext_siwrate); |