diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
49 files changed, 4295 insertions, 3623 deletions
diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 57703d5209d..ae08498dfca 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -102,12 +102,28 @@ config IWLWIFI_DEVICE_TRACING occur. endmenu -config IWLWIFI_DEVICE_SVTOOL - bool "iwlwifi device svtool support" +config IWLWIFI_DEVICE_TESTMODE + def_bool y depends on IWLWIFI - select NL80211_TESTMODE + depends on NL80211_TESTMODE help - This option enables the svtool support for iwlwifi device through - NL80211_TESTMODE. svtool is a software validation tool that runs in - the user space and interacts with the device in the kernel space - through the generic netlink message via NL80211_TESTMODE channel. + This option enables the testmode support for iwlwifi device through + NL80211_TESTMODE. This provide the capabilities of enable user space + validation applications to interacts with the device through the + generic netlink message via NL80211_TESTMODE channel. + +config IWLWIFI_P2P + bool "iwlwifi experimental P2P support" + depends on IWLWIFI + help + This option enables experimental P2P support for some devices + based on microcode support. Since P2P support is still under + development, this option may even enable it for some devices + now that turn out to not support it in the future due to + microcode restrictions. + + To determine if your microcode supports the experimental P2P + offered by this option, check if the driver advertises AP + support when it is loaded. + + Say Y only if you want to experiment with P2P. diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index c73e5ed8db5..9dc84a7354d 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,7 +1,7 @@ # WIFI obj-$(CONFIG_IWLWIFI) += iwlwifi.o -iwlwifi-objs := iwl-agn.o iwl-agn-rs.o -iwlwifi-objs += iwl-agn-ucode.o iwl-agn-tx.o +iwlwifi-objs := iwl-agn.o iwl-agn-rs.o iwl-mac80211.o +iwlwifi-objs += iwl-ucode.o iwl-agn-tx.o iwlwifi-objs += iwl-agn-lib.o iwl-agn-calib.o iwl-io.o iwlwifi-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-rx.o @@ -18,7 +18,7 @@ iwlwifi-objs += iwl-trans-pcie.o iwl-trans-pcie-rx.o iwl-trans-pcie-tx.o iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o -iwlwifi-$(CONFIG_IWLWIFI_DEVICE_SVTOOL) += iwl-sv-open.o +iwlwifi-$(CONFIG_IWLWIFI_DEVICE_TESTMODE) += iwl-testmode.o CFLAGS_iwl-devtrace.o := -I$(src) diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index e12b48c2cff..1ef7bfc2ab2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -124,10 +124,10 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv) { if (iwlagn_mod_params.num_of_queues >= IWL_MIN_NUM_QUEUES && iwlagn_mod_params.num_of_queues <= IWLAGN_NUM_QUEUES) - priv->cfg->base_params->num_of_queues = + cfg(priv)->base_params->num_of_queues = iwlagn_mod_params.num_of_queues; - hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues; + hw_params(priv).max_txq_num = cfg(priv)->base_params->num_of_queues; priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; hw_params(priv).max_data_size = IWLAGN_RTC_DATA_SIZE; @@ -135,28 +135,19 @@ static int iwl1000_hw_set_hw_params(struct iwl_priv *priv) hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ); - hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) + hw_params(priv).tx_chains_num = num_of_ant(cfg(priv)->valid_tx_ant); + if (cfg(priv)->rx_with_siso_diversity) hw_params(priv).rx_chains_num = 1; else hw_params(priv).rx_chains_num = - num_of_ant(priv->cfg->valid_rx_ant); - hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant; - hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant; + num_of_ant(cfg(priv)->valid_rx_ant); + hw_params(priv).valid_tx_ant = cfg(priv)->valid_tx_ant; + hw_params(priv).valid_rx_ant = cfg(priv)->valid_rx_ant; iwl1000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ - /* Set initial calibration set */ hw_params(priv).sens = &iwl1000_sensitivity; - hw_params(priv).calib_init_cfg = - BIT(IWL_CALIB_XTAL) | - BIT(IWL_CALIB_LO) | - BIT(IWL_CALIB_TX_IQ) | - BIT(IWL_CALIB_TX_IQ_PERD) | - BIT(IWL_CALIB_BASE_BAND); - if (priv->cfg->need_dc_calib) - hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_DC); return 0; } @@ -191,6 +182,7 @@ static struct iwl_base_params iwl1000_base_params = { .chain_noise_scale = 1000, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 128, + .wd_disable = true, }; static struct iwl_ht_params iwl1000_ht_params = { .ht_greenfield_support = true, diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index 79431977a96..094693328db 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -86,7 +86,7 @@ static void iwl2000_nic_config(struct iwl_priv *priv) { iwl_rf_config(priv); - if (priv->cfg->iq_invert) + if (cfg(priv)->iq_invert) iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER); } @@ -120,10 +120,10 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv) { if (iwlagn_mod_params.num_of_queues >= IWL_MIN_NUM_QUEUES && iwlagn_mod_params.num_of_queues <= IWLAGN_NUM_QUEUES) - priv->cfg->base_params->num_of_queues = + cfg(priv)->base_params->num_of_queues = iwlagn_mod_params.num_of_queues; - hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues; + hw_params(priv).max_txq_num = cfg(priv)->base_params->num_of_queues; priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; hw_params(priv).max_data_size = IWL60_RTC_DATA_SIZE; @@ -131,29 +131,19 @@ static int iwl2000_hw_set_hw_params(struct iwl_priv *priv) hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ); - hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) + hw_params(priv).tx_chains_num = num_of_ant(cfg(priv)->valid_tx_ant); + if (cfg(priv)->rx_with_siso_diversity) hw_params(priv).rx_chains_num = 1; else hw_params(priv).rx_chains_num = - num_of_ant(priv->cfg->valid_rx_ant); - hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant; - hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant; + num_of_ant(cfg(priv)->valid_rx_ant); + hw_params(priv).valid_tx_ant = cfg(priv)->valid_tx_ant; + hw_params(priv).valid_rx_ant = cfg(priv)->valid_rx_ant; iwl2000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ - /* Set initial calibration set */ hw_params(priv).sens = &iwl2000_sensitivity; - hw_params(priv).calib_init_cfg = - BIT(IWL_CALIB_XTAL) | - BIT(IWL_CALIB_LO) | - BIT(IWL_CALIB_TX_IQ) | - BIT(IWL_CALIB_BASE_BAND); - if (priv->cfg->need_dc_calib) - hw_params(priv).calib_rt_cfg |= IWL_CALIB_CFG_DC_IDX; - if (priv->cfg->need_temp_offset_calib) - hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET); return 0; } @@ -258,25 +248,19 @@ static struct iwl_bt_params iwl2030_bt_params = { .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .lib = &iwl2000_lib, \ .base_params = &iwl2000_base_params, \ - .need_dc_calib = true, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ .iq_invert = true \ struct iwl_cfg iwl2000_2bgn_cfg = { - .name = "2000 Series 2x2 BGN", + .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", IWL_DEVICE_2000, .ht_params = &iwl2000_ht_params, }; -struct iwl_cfg iwl2000_2bg_cfg = { - .name = "2000 Series 2x2 BG", - IWL_DEVICE_2000, -}; - struct iwl_cfg iwl2000_2bgn_d_cfg = { - .name = "2000D Series 2x2 BGN", + .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", IWL_DEVICE_2000, .ht_params = &iwl2000_ht_params, }; @@ -291,7 +275,6 @@ struct iwl_cfg iwl2000_2bgn_d_cfg = { .lib = &iwl2030_lib, \ .base_params = &iwl2030_base_params, \ .bt_params = &iwl2030_bt_params, \ - .need_dc_calib = true, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ @@ -299,16 +282,11 @@ struct iwl_cfg iwl2000_2bgn_d_cfg = { .iq_invert = true \ struct iwl_cfg iwl2030_2bgn_cfg = { - .name = "2000 Series 2x2 BGN/BT", + .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", IWL_DEVICE_2030, .ht_params = &iwl2000_ht_params, }; -struct iwl_cfg iwl2030_2bg_cfg = { - .name = "2000 Series 2x2 BG/BT", - IWL_DEVICE_2030, -}; - #define IWL_DEVICE_105 \ .fw_name_pre = IWL105_FW_PRE, \ .ucode_api_max = IWL105_UCODE_API_MAX, \ @@ -318,7 +296,6 @@ struct iwl_cfg iwl2030_2bg_cfg = { .eeprom_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ .lib = &iwl2000_lib, \ .base_params = &iwl2000_base_params, \ - .need_dc_calib = true, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ @@ -326,19 +303,14 @@ struct iwl_cfg iwl2030_2bg_cfg = { .rx_with_siso_diversity = true, \ .iq_invert = true \ -struct iwl_cfg iwl105_bg_cfg = { - .name = "105 Series 1x1 BG", - IWL_DEVICE_105, -}; - struct iwl_cfg iwl105_bgn_cfg = { - .name = "105 Series 1x1 BGN", + .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", IWL_DEVICE_105, .ht_params = &iwl2000_ht_params, }; struct iwl_cfg iwl105_bgn_d_cfg = { - .name = "105D Series 1x1 BGN", + .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", IWL_DEVICE_105, .ht_params = &iwl2000_ht_params, }; @@ -353,7 +325,6 @@ struct iwl_cfg iwl105_bgn_d_cfg = { .lib = &iwl2030_lib, \ .base_params = &iwl2030_base_params, \ .bt_params = &iwl2030_bt_params, \ - .need_dc_calib = true, \ .need_temp_offset_calib = true, \ .temp_offset_v2 = true, \ .led_mode = IWL_LED_RF_STATE, \ @@ -361,13 +332,8 @@ struct iwl_cfg iwl105_bgn_d_cfg = { .rx_with_siso_diversity = true, \ .iq_invert = true \ -struct iwl_cfg iwl135_bg_cfg = { - .name = "135 Series 1x1 BG/BT", - IWL_DEVICE_135, -}; - struct iwl_cfg iwl135_bgn_cfg = { - .name = "135 Series 1x1 BGN/BT", + .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", IWL_DEVICE_135, .ht_params = &iwl2000_ht_params, }; diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index c511c98a89a..b3a365fea7b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -134,10 +134,10 @@ static struct iwl_sensitivity_ranges iwl5150_sensitivity = { #define IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF (-5) -static s32 iwl_temp_calib_to_offset(struct iwl_priv *priv) +static s32 iwl_temp_calib_to_offset(struct iwl_shared *shrd) { u16 temperature, voltage; - __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(priv, + __le16 *temp_calib = (__le16 *)iwl_eeprom_query_addr(shrd, EEPROM_KELVIN_TEMPERATURE); temperature = le16_to_cpu(temp_calib[0]); @@ -151,7 +151,7 @@ static void iwl5150_set_ct_threshold(struct iwl_priv *priv) { const s32 volt2temp_coef = IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF; s32 threshold = (s32)CELSIUS_TO_KELVIN(CT_KILL_THRESHOLD_LEGACY) - - iwl_temp_calib_to_offset(priv); + iwl_temp_calib_to_offset(priv->shrd); hw_params(priv).ct_kill_threshold = threshold * volt2temp_coef; } @@ -166,10 +166,10 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) { if (iwlagn_mod_params.num_of_queues >= IWL_MIN_NUM_QUEUES && iwlagn_mod_params.num_of_queues <= IWLAGN_NUM_QUEUES) - priv->cfg->base_params->num_of_queues = + cfg(priv)->base_params->num_of_queues = iwlagn_mod_params.num_of_queues; - hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues; + hw_params(priv).max_txq_num = cfg(priv)->base_params->num_of_queues; priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; hw_params(priv).max_data_size = IWLAGN_RTC_DATA_SIZE; @@ -178,22 +178,15 @@ static int iwl5000_hw_set_hw_params(struct iwl_priv *priv) hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); - hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); - hw_params(priv).rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); - hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant; - hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant; + hw_params(priv).tx_chains_num = num_of_ant(cfg(priv)->valid_tx_ant); + hw_params(priv).rx_chains_num = num_of_ant(cfg(priv)->valid_rx_ant); + hw_params(priv).valid_tx_ant = cfg(priv)->valid_tx_ant; + hw_params(priv).valid_rx_ant = cfg(priv)->valid_rx_ant; iwl5000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ - /* Set initial calibration set */ hw_params(priv).sens = &iwl5000_sensitivity; - hw_params(priv).calib_init_cfg = - BIT(IWL_CALIB_XTAL) | - BIT(IWL_CALIB_LO) | - BIT(IWL_CALIB_TX_IQ) | - BIT(IWL_CALIB_TX_IQ_PERD) | - BIT(IWL_CALIB_BASE_BAND); return 0; } @@ -202,10 +195,10 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv) { if (iwlagn_mod_params.num_of_queues >= IWL_MIN_NUM_QUEUES && iwlagn_mod_params.num_of_queues <= IWLAGN_NUM_QUEUES) - priv->cfg->base_params->num_of_queues = + cfg(priv)->base_params->num_of_queues = iwlagn_mod_params.num_of_queues; - hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues; + hw_params(priv).max_txq_num = cfg(priv)->base_params->num_of_queues; priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; hw_params(priv).max_data_size = IWLAGN_RTC_DATA_SIZE; @@ -214,22 +207,15 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv) hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); - hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); - hw_params(priv).rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); - hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant; - hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant; + hw_params(priv).tx_chains_num = num_of_ant(cfg(priv)->valid_tx_ant); + hw_params(priv).rx_chains_num = num_of_ant(cfg(priv)->valid_rx_ant); + hw_params(priv).valid_tx_ant = cfg(priv)->valid_tx_ant; + hw_params(priv).valid_rx_ant = cfg(priv)->valid_rx_ant; iwl5150_set_ct_threshold(priv); /* Set initial sensitivity parameters */ - /* Set initial calibration set */ hw_params(priv).sens = &iwl5150_sensitivity; - hw_params(priv).calib_init_cfg = - BIT(IWL_CALIB_LO) | - BIT(IWL_CALIB_TX_IQ) | - BIT(IWL_CALIB_BASE_BAND); - if (priv->cfg->need_dc_calib) - hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_DC); return 0; } @@ -237,7 +223,7 @@ static int iwl5150_hw_set_hw_params(struct iwl_priv *priv) static void iwl5150_temperature(struct iwl_priv *priv) { u32 vt = 0; - s32 offset = iwl_temp_calib_to_offset(priv); + s32 offset = iwl_temp_calib_to_offset(priv->shrd); vt = le32_to_cpu(priv->statistics.common.temperature); vt = vt / IWL_5150_VOLTAGE_TO_TEMPERATURE_COEFF + offset; @@ -364,6 +350,7 @@ static struct iwl_base_params iwl5000_base_params = { .wd_timeout = IWL_LONG_WD_TIMEOUT, .max_event_log_size = 512, .no_idle_support = true, + .wd_disable = true, }; static struct iwl_ht_params iwl5000_ht_params = { .ht_greenfield_support = true, @@ -433,7 +420,7 @@ struct iwl_cfg iwl5350_agn_cfg = { .eeprom_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ .lib = &iwl5150_lib, \ .base_params = &iwl5000_base_params, \ - .need_dc_calib = true, \ + .no_xtal_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index c840c78278d..54b753399e6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -46,11 +46,12 @@ #include "iwl-cfg.h" /* Highest firmware API version supported */ -#define IWL6000_UCODE_API_MAX 4 +#define IWL6000_UCODE_API_MAX 6 #define IWL6050_UCODE_API_MAX 5 #define IWL6000G2_UCODE_API_MAX 6 /* Oldest version we won't warn about */ +#define IWL6000_UCODE_API_OK 4 #define IWL6000G2_UCODE_API_OK 5 /* Lowest firmware API version supported */ @@ -80,7 +81,7 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv) static void iwl6050_additional_nic_config(struct iwl_priv *priv) { /* Indicate calibration version to uCode. */ - if (iwlagn_eeprom_calib_version(priv) >= 6) + if (iwl_eeprom_calib_version(priv->shrd) >= 6) iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); } @@ -88,7 +89,7 @@ static void iwl6050_additional_nic_config(struct iwl_priv *priv) static void iwl6150_additional_nic_config(struct iwl_priv *priv) { /* Indicate calibration version to uCode. */ - if (iwlagn_eeprom_calib_version(priv) >= 6) + if (iwl_eeprom_calib_version(priv->shrd) >= 6) iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6); iwl_set_bit(bus(priv), CSR_GP_DRIVER_REG, @@ -101,14 +102,14 @@ static void iwl6000_nic_config(struct iwl_priv *priv) iwl_rf_config(priv); /* no locking required for register write */ - if (priv->cfg->pa_type == IWL_PA_INTERNAL) { + if (cfg(priv)->pa_type == IWL_PA_INTERNAL) { /* 2x2 IPA phy type */ iwl_write32(bus(priv), CSR_GP_DRIVER_REG, CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA); } /* do additional nic configuration if needed */ - if (priv->cfg->additional_nic_config) - priv->cfg->additional_nic_config(priv); + if (cfg(priv)->additional_nic_config) + cfg(priv)->additional_nic_config(priv); } static struct iwl_sensitivity_ranges iwl6000_sensitivity = { @@ -140,10 +141,10 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv) { if (iwlagn_mod_params.num_of_queues >= IWL_MIN_NUM_QUEUES && iwlagn_mod_params.num_of_queues <= IWLAGN_NUM_QUEUES) - priv->cfg->base_params->num_of_queues = + cfg(priv)->base_params->num_of_queues = iwlagn_mod_params.num_of_queues; - hw_params(priv).max_txq_num = priv->cfg->base_params->num_of_queues; + hw_params(priv).max_txq_num = cfg(priv)->base_params->num_of_queues; priv->contexts[IWL_RXON_CTX_BSS].bcast_sta_id = IWLAGN_BROADCAST_ID; hw_params(priv).max_data_size = IWL60_RTC_DATA_SIZE; @@ -152,29 +153,19 @@ static int iwl6000_hw_set_hw_params(struct iwl_priv *priv) hw_params(priv).ht40_channel = BIT(IEEE80211_BAND_2GHZ) | BIT(IEEE80211_BAND_5GHZ); - hw_params(priv).tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); - if (priv->cfg->rx_with_siso_diversity) + hw_params(priv).tx_chains_num = num_of_ant(cfg(priv)->valid_tx_ant); + if (cfg(priv)->rx_with_siso_diversity) hw_params(priv).rx_chains_num = 1; else hw_params(priv).rx_chains_num = - num_of_ant(priv->cfg->valid_rx_ant); - hw_params(priv).valid_tx_ant = priv->cfg->valid_tx_ant; - hw_params(priv).valid_rx_ant = priv->cfg->valid_rx_ant; + num_of_ant(cfg(priv)->valid_rx_ant); + hw_params(priv).valid_tx_ant = cfg(priv)->valid_tx_ant; + hw_params(priv).valid_rx_ant = cfg(priv)->valid_rx_ant; iwl6000_set_ct_threshold(priv); /* Set initial sensitivity parameters */ - /* Set initial calibration set */ hw_params(priv).sens = &iwl6000_sensitivity; - hw_params(priv).calib_init_cfg = - BIT(IWL_CALIB_XTAL) | - BIT(IWL_CALIB_LO) | - BIT(IWL_CALIB_TX_IQ) | - BIT(IWL_CALIB_BASE_BAND); - if (priv->cfg->need_dc_calib) - hw_params(priv).calib_rt_cfg |= IWL_CALIB_CFG_DC_IDX; - if (priv->cfg->need_temp_offset_calib) - hw_params(priv).calib_init_cfg |= BIT(IWL_CALIB_TEMP_OFFSET); return 0; } @@ -364,7 +355,6 @@ static struct iwl_bt_params iwl6000_bt_params = { .eeprom_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ .lib = &iwl6000_lib, \ .base_params = &iwl6000_g2_base_params, \ - .need_dc_calib = true, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE @@ -406,7 +396,6 @@ struct iwl_cfg iwl6005_2agn_d_cfg = { .lib = &iwl6030_lib, \ .base_params = &iwl6000_g2_base_params, \ .bt_params = &iwl6000_bt_params, \ - .need_dc_calib = true, \ .need_temp_offset_calib = true, \ .led_mode = IWL_LED_RF_STATE, \ .adv_pm = true \ @@ -434,21 +423,11 @@ struct iwl_cfg iwl6030_2bg_cfg = { }; struct iwl_cfg iwl6035_2agn_cfg = { - .name = "6035 Series 2x2 AGN/BT", + .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", IWL_DEVICE_6030, .ht_params = &iwl6000_ht_params, }; -struct iwl_cfg iwl6035_2abg_cfg = { - .name = "6035 Series 2x2 ABG/BT", - IWL_DEVICE_6030, -}; - -struct iwl_cfg iwl6035_2bg_cfg = { - .name = "6035 Series 2x2 BG/BT", - IWL_DEVICE_6030, -}; - struct iwl_cfg iwl1030_bgn_cfg = { .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", IWL_DEVICE_6030, @@ -479,6 +458,7 @@ struct iwl_cfg iwl130_bg_cfg = { #define IWL_DEVICE_6000i \ .fw_name_pre = IWL6000_FW_PRE, \ .ucode_api_max = IWL6000_UCODE_API_MAX, \ + .ucode_api_ok = IWL6000_UCODE_API_OK, \ .ucode_api_min = IWL6000_UCODE_API_MIN, \ .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ @@ -516,7 +496,6 @@ struct iwl_cfg iwl6000i_2bg_cfg = { .eeprom_ver = EEPROM_6050_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ .base_params = &iwl6050_base_params, \ - .need_dc_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true @@ -540,7 +519,6 @@ struct iwl_cfg iwl6050_2abg_cfg = { .eeprom_ver = EEPROM_6150_EEPROM_VERSION, \ .eeprom_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ .base_params = &iwl6050_base_params, \ - .need_dc_calib = true, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true @@ -559,17 +537,17 @@ struct iwl_cfg iwl6000_3agn_cfg = { .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, + .ucode_api_ok = IWL6000_UCODE_API_OK, .ucode_api_min = IWL6000_UCODE_API_MIN, .eeprom_ver = EEPROM_6000_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_6000_TX_POWER_VERSION, .lib = &iwl6000_lib, .base_params = &iwl6000_base_params, .ht_params = &iwl6000_ht_params, - .need_dc_calib = true, .led_mode = IWL_LED_BLINK, }; -MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_OK)); MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_MAX)); MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX)); MODULE_FIRMWARE(IWL6030_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX)); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c index 03bac48558b..50ff849c9f6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-calib.c @@ -82,56 +82,64 @@ struct statistics_general_data { u32 beacon_energy_c; }; -int iwl_send_calib_results(struct iwl_priv *priv) +int iwl_send_calib_results(struct iwl_trans *trans) { - int ret = 0; - int i = 0; - struct iwl_host_cmd hcmd = { .id = REPLY_PHY_CALIBRATION_CMD, .flags = CMD_SYNC, }; - - for (i = 0; i < IWL_CALIB_MAX; i++) { - if ((BIT(i) & hw_params(priv).calib_init_cfg) && - priv->calib_results[i].buf) { - hcmd.len[0] = priv->calib_results[i].buf_len; - hcmd.data[0] = priv->calib_results[i].buf; - hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; - ret = iwl_trans_send_cmd(trans(priv), &hcmd); - if (ret) { - IWL_ERR(priv, "Error %d iteration %d\n", - ret, i); - break; - } + struct iwl_calib_result *res; + + list_for_each_entry(res, &trans->calib_results, list) { + int ret; + + hcmd.len[0] = res->cmd_len; + hcmd.data[0] = &res->hdr; + hcmd.dataflags[0] = IWL_HCMD_DFL_NOCOPY; + ret = iwl_trans_send_cmd(trans, &hcmd); + if (ret) { + IWL_ERR(trans, "Error %d on calib cmd %d\n", + ret, res->hdr.op_code); + return ret; } } - return ret; + return 0; } -int iwl_calib_set(struct iwl_calib_result *res, const u8 *buf, int len) +int iwl_calib_set(struct iwl_trans *trans, + const struct iwl_calib_hdr *cmd, int len) { - if (res->buf_len != len) { - kfree(res->buf); - res->buf = kzalloc(len, GFP_ATOMIC); - } - if (unlikely(res->buf == NULL)) + struct iwl_calib_result *res, *tmp; + + res = kmalloc(sizeof(*res) + len - sizeof(struct iwl_calib_hdr), + GFP_ATOMIC); + if (!res) return -ENOMEM; + memcpy(&res->hdr, cmd, len); + res->cmd_len = len; + + list_for_each_entry(tmp, &trans->calib_results, list) { + if (tmp->hdr.op_code == res->hdr.op_code) { + list_replace(&tmp->list, &res->list); + kfree(tmp); + return 0; + } + } + + /* wasn't in list already */ + list_add_tail(&res->list, &trans->calib_results); - res->buf_len = len; - memcpy(res->buf, buf, len); return 0; } -void iwl_calib_free_results(struct iwl_priv *priv) +void iwl_calib_free_results(struct iwl_trans *trans) { - int i; + struct iwl_calib_result *res, *tmp; - for (i = 0; i < IWL_CALIB_MAX; i++) { - kfree(priv->calib_results[i].buf); - priv->calib_results[i].buf = NULL; - priv->calib_results[i].buf_len = 0; + list_for_each_entry_safe(res, tmp, &trans->calib_results, list) { + list_del(&res->list); + kfree(res); } } @@ -505,7 +513,7 @@ static int iwl_enhance_sensitivity_write(struct iwl_priv *priv) iwl_prepare_legacy_sensitivity_tbl(priv, data, &cmd.enhance_table[0]); - if (priv->cfg->base_params->hd_v2) { + if (cfg(priv)->base_params->hd_v2) { cmd.enhance_table[HD_INA_NON_SQUARE_DET_OFDM_INDEX] = HD_INA_NON_SQUARE_DET_OFDM_DATA_V2; cmd.enhance_table[HD_INA_NON_SQUARE_DET_CCK_INDEX] = @@ -839,7 +847,7 @@ static void iwl_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, * connect the first valid tx chain */ first_chain = - find_first_chain(priv->cfg->valid_tx_ant); + find_first_chain(cfg(priv)->valid_tx_ant); data->disconn_array[first_chain] = 0; active_chains |= BIT(first_chain); IWL_DEBUG_CALIB(priv, @@ -882,7 +890,7 @@ static void iwlagn_gain_computation(struct iwl_priv *priv, continue; } - delta_g = (priv->cfg->base_params->chain_noise_scale * + delta_g = (cfg(priv)->base_params->chain_noise_scale * ((s32)average_noise[default_chain] - (s32)average_noise[i])) / 1500; @@ -1039,8 +1047,8 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) return; /* Analyze signal for disconnected antenna */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) { /* Disable disconnected antenna algorithm for advanced bt coex, assuming valid antennas are connected */ data->active_chains = hw_params(priv).valid_rx_ant; @@ -1074,7 +1082,7 @@ void iwl_chain_noise_calibration(struct iwl_priv *priv) iwlagn_gain_computation(priv, average_noise, min_average_noise_antenna_i, min_average_noise, - find_first_chain(priv->cfg->valid_rx_ant)); + find_first_chain(cfg(priv)->valid_rx_ant)); /* Some power changes may have been made during the calibration. * Update and commit the RXON diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-calib.h b/drivers/net/wireless/iwlwifi/iwl-agn-calib.h index a869fc9205d..10275ce92bd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-calib.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-calib.h @@ -72,8 +72,4 @@ void iwl_sensitivity_calibration(struct iwl_priv *priv); void iwl_init_sensitivity(struct iwl_priv *priv); void iwl_reset_run_time_calib(struct iwl_priv *priv); -int iwl_send_calib_results(struct iwl_priv *priv); -int iwl_calib_set(struct iwl_calib_result *res, const u8 *buf, int len); -void iwl_calib_free_results(struct iwl_priv *priv); - #endif /* __iwl_calib_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 1a52ed29f2d..64cf439035c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -32,6 +32,7 @@ #include <linux/init.h> #include <linux/sched.h> +#include "iwl-wifi.h" #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-io.h" @@ -92,11 +93,11 @@ void iwlagn_temperature(struct iwl_priv *priv) iwl_tt_handler(priv); } -u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv) +u16 iwl_eeprom_calib_version(struct iwl_shared *shrd) { struct iwl_eeprom_calib_hdr *hdr; - hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, + hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(shrd, EEPROM_CALIB_ALL); return hdr->version; @@ -105,7 +106,7 @@ u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv) /* * EEPROM */ -static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address) +static u32 eeprom_indirect_address(const struct iwl_shared *shrd, u32 address) { u16 offset = 0; @@ -114,31 +115,31 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address) switch (address & INDIRECT_TYPE_MSK) { case INDIRECT_HOST: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_HOST); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_HOST); break; case INDIRECT_GENERAL: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_GENERAL); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_GENERAL); break; case INDIRECT_REGULATORY: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_REGULATORY); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_REGULATORY); break; case INDIRECT_TXP_LIMIT: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_TXP_LIMIT); break; case INDIRECT_TXP_LIMIT_SIZE: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_TXP_LIMIT_SIZE); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_TXP_LIMIT_SIZE); break; case INDIRECT_CALIBRATION: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_CALIBRATION); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_CALIBRATION); break; case INDIRECT_PROCESS_ADJST: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_PROCESS_ADJST); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_PROCESS_ADJST); break; case INDIRECT_OTHERS: - offset = iwl_eeprom_query16(priv, EEPROM_LINK_OTHERS); + offset = iwl_eeprom_query16(shrd, EEPROM_LINK_OTHERS); break; default: - IWL_ERR(priv, "illegal indirect type: 0x%X\n", + IWL_ERR(shrd->trans, "illegal indirect type: 0x%X\n", address & INDIRECT_TYPE_MSK); break; } @@ -147,11 +148,11 @@ static u32 eeprom_indirect_address(const struct iwl_priv *priv, u32 address) return (address & ADDRESS_MSK) + (offset << 1); } -const u8 *iwl_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) +const u8 *iwl_eeprom_query_addr(const struct iwl_shared *shrd, size_t offset) { - u32 address = eeprom_indirect_address(priv, offset); - BUG_ON(address >= priv->cfg->base_params->eeprom_size); - return &priv->eeprom[address]; + u32 address = eeprom_indirect_address(shrd, offset); + BUG_ON(address >= shrd->cfg->base_params->eeprom_size); + return &shrd->eeprom[address]; } struct iwl_mod_params iwlagn_mod_params = { @@ -232,7 +233,7 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | IWL_PAN_SCD_MULTICAST_MSK; - if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE) + if (cfg(priv)->sku & EEPROM_SKU_CAP_11N_ENABLE) flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", @@ -374,15 +375,15 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != sizeof(basic.bt3_lookup_table)); - if (priv->cfg->bt_params) { - if (priv->cfg->bt_params->bt_session_2) { + if (cfg(priv)->bt_params) { + if (cfg(priv)->bt_params->bt_session_2) { bt_cmd_2000.prio_boost = cpu_to_le32( - priv->cfg->bt_params->bt_prio_boost); + cfg(priv)->bt_params->bt_prio_boost); bt_cmd_2000.tx_prio_boost = 0; bt_cmd_2000.rx_prio_boost = 0; } else { bt_cmd_6000.prio_boost = - priv->cfg->bt_params->bt_prio_boost; + cfg(priv)->bt_params->bt_prio_boost; bt_cmd_6000.tx_prio_boost = 0; bt_cmd_6000.rx_prio_boost = 0; } @@ -430,7 +431,7 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - if (priv->cfg->bt_params->bt_session_2) { + if (cfg(priv)->bt_params->bt_session_2) { memcpy(&bt_cmd_2000.basic, &basic, sizeof(basic)); ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_BT_CONFIG, @@ -799,8 +800,8 @@ static bool is_single_rx_stream(struct iwl_priv *priv) */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* @@ -827,6 +828,7 @@ static int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) case IEEE80211_SMPS_STATIC: case IEEE80211_SMPS_DYNAMIC: return IWL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_AUTOMATIC: case IEEE80211_SMPS_OFF: return active_cnt; default: @@ -870,8 +872,8 @@ void iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) else active_chains = hw_params(priv).valid_rx_ant; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist && (priv->bt_full_concurrent || priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { /* @@ -933,53 +935,359 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) return ant; } -/* notification wait support */ -void iwlagn_init_notification_wait(struct iwl_priv *priv, - struct iwl_notification_wait *wait_entry, - u8 cmd, - void (*fn)(struct iwl_priv *priv, - struct iwl_rx_packet *pkt, - void *data), - void *fn_data) +#ifdef CONFIG_PM_SLEEP +static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) { - wait_entry->fn = fn; - wait_entry->fn_data = fn_data; - wait_entry->cmd = cmd; - wait_entry->triggered = false; - wait_entry->aborted = false; - - spin_lock_bh(&priv->notif_wait_lock); - list_add(&wait_entry->list, &priv->notif_waits); - spin_unlock_bh(&priv->notif_wait_lock); + int i; + + for (i = 0; i < IWLAGN_P1K_SIZE; i++) + out[i] = cpu_to_le16(p1k[i]); } -int iwlagn_wait_notification(struct iwl_priv *priv, - struct iwl_notification_wait *wait_entry, - unsigned long timeout) +struct wowlan_key_data { + struct iwl_rxon_context *ctx; + struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; + struct iwlagn_wowlan_tkip_params_cmd *tkip; + const u8 *bssid; + bool error, use_rsc_tsc, use_tkip; +}; + + +static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) { - int ret; + struct iwl_priv *priv = hw->priv; + struct wowlan_key_data *data = _data; + struct iwl_rxon_context *ctx = data->ctx; + struct aes_sc *aes_sc, *aes_tx_sc = NULL; + struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; + struct iwlagn_p1k_cache *rx_p1ks; + u8 *rx_mic_key; + struct ieee80211_key_seq seq; + u32 cur_rx_iv32 = 0; + u16 p1k[IWLAGN_P1K_SIZE]; + int ret, i; + + mutex_lock(&priv->shrd->mutex); - ret = wait_event_timeout(priv->notif_waitq, - wait_entry->triggered || wait_entry->aborted, - timeout); + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + !sta && !ctx->key_mapping_keys) + ret = iwl_set_default_wep_key(priv, ctx, key); + else + ret = iwl_set_dynamic_key(priv, ctx, key, sta); - spin_lock_bh(&priv->notif_wait_lock); - list_del(&wait_entry->list); - spin_unlock_bh(&priv->notif_wait_lock); + if (ret) { + IWL_ERR(priv, "Error setting key during suspend!\n"); + data->error = true; + } - if (wait_entry->aborted) - return -EIO; + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + if (sta) { + tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; + tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; - /* return value is always >= 0 */ - if (ret <= 0) - return -ETIMEDOUT; - return 0; + rx_p1ks = data->tkip->rx_uni; + + ieee80211_get_key_tx_seq(key, &seq); + tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); + + ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); + iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); + + memcpy(data->tkip->mic_keys.tx, + &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], + IWLAGN_MIC_KEY_SIZE); + + rx_mic_key = data->tkip->mic_keys.rx_unicast; + } else { + tkip_sc = + data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; + rx_p1ks = data->tkip->rx_multi; + rx_mic_key = data->tkip->mic_keys.rx_mcast; + } + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 (as they need to to avoid replay attacks) + * for checking the IV in the frames. + */ + for (i = 0; i < IWLAGN_NUM_RSC; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); + tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); + /* wrapping isn't allowed, AP must rekey */ + if (seq.tkip.iv32 > cur_rx_iv32) + cur_rx_iv32 = seq.tkip.iv32; + } + + ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); + iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); + ieee80211_get_tkip_rx_p1k(key, data->bssid, + cur_rx_iv32 + 1, p1k); + iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); + + memcpy(rx_mic_key, + &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], + IWLAGN_MIC_KEY_SIZE); + + data->use_tkip = true; + data->use_rsc_tsc = true; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (sta) { + u8 *pn = seq.ccmp.pn; + + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; + aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; + + ieee80211_get_key_tx_seq(key, &seq); + aes_tx_sc->pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } else + aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; + + /* + * For non-QoS this relies on the fact that both the uCode and + * mac80211 use TID 0 for checking the IV in the frames. + */ + for (i = 0; i < IWLAGN_NUM_RSC; i++) { + u8 *pn = seq.ccmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + aes_sc->pn = cpu_to_le64( + (u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } + data->use_rsc_tsc = true; + break; + } + + mutex_unlock(&priv->shrd->mutex); +} + +int iwlagn_send_patterns(struct iwl_priv *priv, + struct cfg80211_wowlan *wowlan) +{ + struct iwlagn_wowlan_patterns_cmd *pattern_cmd; + struct iwl_host_cmd cmd = { + .id = REPLY_WOWLAN_PATTERNS, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .flags = CMD_SYNC, + }; + int i, err; + + if (!wowlan->n_patterns) + return 0; + + cmd.len[0] = sizeof(*pattern_cmd) + + wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); + + pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); + if (!pattern_cmd) + return -ENOMEM; + + pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); + + for (i = 0; i < wowlan->n_patterns; i++) { + int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + + memcpy(&pattern_cmd->patterns[i].mask, + wowlan->patterns[i].mask, mask_len); + memcpy(&pattern_cmd->patterns[i].pattern, + wowlan->patterns[i].pattern, + wowlan->patterns[i].pattern_len); + pattern_cmd->patterns[i].mask_size = mask_len; + pattern_cmd->patterns[i].pattern_size = + wowlan->patterns[i].pattern_len; + } + + cmd.data[0] = pattern_cmd; + err = iwl_trans_send_cmd(trans(priv), &cmd); + kfree(pattern_cmd); + return err; } -void iwlagn_remove_notification(struct iwl_priv *priv, - struct iwl_notification_wait *wait_entry) +int iwlagn_suspend(struct iwl_priv *priv, + struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { - spin_lock_bh(&priv->notif_wait_lock); - list_del(&wait_entry->list); - spin_unlock_bh(&priv->notif_wait_lock); + struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; + struct iwl_rxon_cmd rxon; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; + struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; + struct iwlagn_d3_config_cmd d3_cfg_cmd = {}; + struct wowlan_key_data key_data = { + .ctx = ctx, + .bssid = ctx->active.bssid_addr, + .use_rsc_tsc = false, + .tkip = &tkip_cmd, + .use_tkip = false, + }; + int ret, i; + u16 seq; + + key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); + if (!key_data.rsc_tsc) + return -ENOMEM; + + memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); + + /* + * We know the last used seqno, and the uCode expects to know that + * one, it will increment before TX. + */ + seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; + wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); + + /* + * For QoS counters, we store the one to use next, so subtract 0x10 + * since the uCode will add 0x10 before using the value. + */ + for (i = 0; i < IWL_MAX_TID_COUNT; i++) { + seq = priv->tid_data[IWL_AP_ID][i].seq_number; + seq -= 0x10; + wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); + } + + if (wowlan->disconnect) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | + IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); + if (wowlan->magic_pkt) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); + if (wowlan->gtk_rekey_failure) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); + if (wowlan->eap_identity_req) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); + if (wowlan->four_way_handshake) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); + if (wowlan->n_patterns) + wakeup_filter_cmd.enabled |= + cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); + + if (wowlan->rfkill_release) + d3_cfg_cmd.wakeup_flags |= + cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); + + iwl_scan_cancel_timeout(priv, 200); + + memcpy(&rxon, &ctx->active, sizeof(rxon)); + + iwl_trans_stop_device(trans(priv)); + + priv->shrd->wowlan = true; + + ret = iwl_load_ucode_wait_alive(trans(priv), IWL_UCODE_WOWLAN); + if (ret) + goto out; + + /* now configure WoWLAN ucode */ + ret = iwl_alive_start(priv); + if (ret) + goto out; + + memcpy(&ctx->staging, &rxon, sizeof(rxon)); + ret = iwlagn_commit_rxon(priv, ctx); + if (ret) + goto out; + + ret = iwl_power_update_mode(priv, true); + if (ret) + goto out; + + if (!iwlagn_mod_params.sw_crypto) { + /* mark all keys clear */ + priv->ucode_key_table = 0; + ctx->key_mapping_keys = 0; + + /* + * This needs to be unlocked due to lock ordering + * constraints. Since we're in the suspend path + * that isn't really a problem though. + */ + mutex_unlock(&priv->shrd->mutex); + ieee80211_iter_keys(priv->hw, ctx->vif, + iwlagn_wowlan_program_keys, + &key_data); + mutex_lock(&priv->shrd->mutex); + if (key_data.error) { + ret = -EIO; + goto out; + } + + if (key_data.use_rsc_tsc) { + struct iwl_host_cmd rsc_tsc_cmd = { + .id = REPLY_WOWLAN_TSC_RSC_PARAMS, + .flags = CMD_SYNC, + .data[0] = key_data.rsc_tsc, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .len[0] = sizeof(key_data.rsc_tsc), + }; + + ret = iwl_trans_send_cmd(trans(priv), &rsc_tsc_cmd); + if (ret) + goto out; + } + + if (key_data.use_tkip) { + ret = iwl_trans_send_cmd_pdu(trans(priv), + REPLY_WOWLAN_TKIP_PARAMS, + CMD_SYNC, sizeof(tkip_cmd), + &tkip_cmd); + if (ret) + goto out; + } + + if (priv->have_rekey_data) { + memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); + memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); + kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); + memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); + kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); + kek_kck_cmd.replay_ctr = priv->replay_ctr; + + ret = iwl_trans_send_cmd_pdu(trans(priv), + REPLY_WOWLAN_KEK_KCK_MATERIAL, + CMD_SYNC, sizeof(kek_kck_cmd), + &kek_kck_cmd); + if (ret) + goto out; + } + } + + ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_D3_CONFIG, CMD_SYNC, + sizeof(d3_cfg_cmd), &d3_cfg_cmd); + if (ret) + goto out; + + ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WOWLAN_WAKEUP_FILTER, + CMD_SYNC, sizeof(wakeup_filter_cmd), + &wakeup_filter_cmd); + if (ret) + goto out; + + ret = iwlagn_send_patterns(priv, wowlan); + out: + kfree(key_data.rsc_tsc); + return ret; } +#endif diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c index 66118cea2af..334b5ae8fdd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.c @@ -298,7 +298,7 @@ static u8 rs_tl_add_packet(struct iwl_lq_sta *lq_data, } else return IWL_MAX_TID_COUNT; - if (unlikely(tid >= TID_MAX_LOAD_COUNT)) + if (unlikely(tid >= IWL_MAX_TID_COUNT)) return IWL_MAX_TID_COUNT; tl = &lq_data->load[tid]; @@ -352,7 +352,7 @@ static void rs_program_fix_rate(struct iwl_priv *priv, lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ lq_sta->active_mimo3_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ -#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE /* testmode has higher priority to overwirte the fixed rate */ if (priv->tm_fixed_rate) lq_sta->dbg_fixed_rate = priv->tm_fixed_rate; @@ -379,7 +379,7 @@ static u32 rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) s32 index; struct iwl_traffic_load *tl = NULL; - if (tid >= TID_MAX_LOAD_COUNT) + if (tid >= IWL_MAX_TID_COUNT) return 0; tl = &(lq_data->load[tid]); @@ -444,11 +444,11 @@ static void rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, struct iwl_lq_sta *lq_data, struct ieee80211_sta *sta) { - if (tid < TID_MAX_LOAD_COUNT) + if (tid < IWL_MAX_TID_COUNT) rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); else - IWL_ERR(priv, "tid exceeds max load count: %d/%d\n", - tid, TID_MAX_LOAD_COUNT); + IWL_ERR(priv, "tid exceeds max TID count: %d/%d\n", + tid, IWL_MAX_TID_COUNT); } static inline int get_num_of_ant_from_rate(u32 rate_n_flags) @@ -1081,12 +1081,12 @@ done: if (sta && sta->supp_rates[sband->band]) rs_rate_scale_perform(priv, skb, sta, lq_sta); -#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_SVTOOL) +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_IWLWIFI_DEVICE_TESTMODE) if ((priv->tm_fixed_rate) && (priv->tm_fixed_rate != lq_sta->dbg_fixed_rate)) rs_program_fix_rate(priv, lq_sta); #endif - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist) + if (cfg(priv)->bt_params && cfg(priv)->bt_params->advanced_bt_coexist) rs_bt_update_lq(priv, ctx, lq_sta); } @@ -1458,10 +1458,8 @@ static int rs_move_legacy_other(struct iwl_priv *priv, break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ - valid_tx_ant = - first_antenna(hw_params(priv).valid_tx_ant); if (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2) - tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + tbl->action = IWL_LEGACY_SWITCH_SISO; break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: @@ -1636,10 +1634,8 @@ static int rs_move_siso_to_other(struct iwl_priv *priv, break; case IWL_BT_COEX_TRAFFIC_LOAD_LOW: /* avoid antenna B unless MIMO */ - valid_tx_ant = - first_antenna(hw_params(priv).valid_tx_ant); if (tbl->action == IWL_SISO_SWITCH_ANTENNA2) - tbl->action = IWL_SISO_SWITCH_ANTENNA1; + tbl->action = IWL_SISO_SWITCH_MIMO2_AB; break; case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: @@ -2277,7 +2273,7 @@ static void rs_rate_scale_perform(struct iwl_priv *priv, tid = rs_tl_add_packet(lq_sta, hdr); if ((tid != IWL_MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { - tid_data = &priv->shrd->tid_data[lq_sta->lq.sta_id][tid]; + tid_data = &priv->tid_data[lq_sta->lq.sta_id][tid]; if (tid_data->agg.state == IWL_AGG_OFF) lq_sta->is_agg = 0; else @@ -2649,8 +2645,7 @@ lq_update: (lq_sta->tx_agg_tid_en & (1 << tid)) && (tid != IWL_MAX_TID_COUNT)) { u8 sta_id = lq_sta->lq.sta_id; - tid_data = - &priv->shrd->tid_data[sta_id][tid]; + tid_data = &priv->tid_data[sta_id][tid]; if (tid_data->agg.state == IWL_AGG_OFF) { IWL_DEBUG_RATE(priv, "try to aggregate tid %d\n", @@ -2908,7 +2903,7 @@ void iwl_rs_rate_init(struct iwl_priv *priv, struct ieee80211_sta *sta, u8 sta_i if (sband->band == IEEE80211_BAND_5GHZ) lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; lq_sta->is_agg = 0; -#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE priv->tm_fixed_rate = 0; #endif #ifdef CONFIG_MAC80211_DEBUGFS @@ -3059,11 +3054,11 @@ static void rs_fill_link_cmd(struct iwl_priv *priv, * overwrite if needed, pass aggregation time limit * to uCode in uSec */ - if (priv && priv->cfg->bt_params && - priv->cfg->bt_params->agg_time_limit && + if (priv && cfg(priv)->bt_params && + cfg(priv)->bt_params->agg_time_limit && priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) lq_cmd->agg_params.agg_time_limit = - cpu_to_le16(priv->cfg->bt_params->agg_time_limit); + cpu_to_le16(cfg(priv)->bt_params->agg_time_limit); } static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h index f4f6deb829a..6675b3c816d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rs.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rs.h @@ -281,7 +281,6 @@ enum { #define TID_QUEUE_CELL_SPACING 50 /*mS */ #define TID_QUEUE_MAX_SIZE 20 #define TID_ROUND_VALUE 5 /* mS */ -#define TID_MAX_LOAD_COUNT 8 #define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) #define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) @@ -402,7 +401,7 @@ struct iwl_lq_sta { struct iwl_link_quality_cmd lq; struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ - struct iwl_traffic_load load[TID_MAX_LOAD_COUNT]; + struct iwl_traffic_load load[IWL_MAX_TID_COUNT]; u8 tx_agg_tid_en; #ifdef CONFIG_MAC80211_DEBUGFS struct dentry *rs_sta_dbgfs_scale_table_file; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c index 5af9e6258a1..b22b2976f89 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c @@ -117,6 +117,7 @@ const char *get_cmd_string(u8 cmd) IWL_CMD(REPLY_WOWLAN_TKIP_PARAMS); IWL_CMD(REPLY_WOWLAN_KEK_KCK_MATERIAL); IWL_CMD(REPLY_WOWLAN_GET_STATUS); + IWL_CMD(REPLY_D3_CONFIG); default: return "UNKNOWN"; @@ -317,7 +318,7 @@ static bool iwlagn_good_plcp_health(struct iwl_priv *priv, unsigned int msecs) { int delta; - int threshold = priv->cfg->base_params->plcp_delta_threshold; + int threshold = cfg(priv)->base_params->plcp_delta_threshold; if (threshold == IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); @@ -582,8 +583,8 @@ static int iwlagn_rx_statistics(struct iwl_priv *priv, iwlagn_rx_calc_noise(priv); queue_work(priv->shrd->workqueue, &priv->run_time_calib_work); } - if (priv->cfg->lib->temperature && change) - priv->cfg->lib->temperature(priv); + if (cfg(priv)->lib->temperature && change) + cfg(priv)->lib->temperature(priv); return 0; } @@ -800,7 +801,8 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, ctx->active.bssid_addr)) continue; ctx->last_tx_rejected = false; - iwl_trans_wake_any_queue(trans(priv), ctx->ctxid); + iwl_trans_wake_any_queue(trans(priv), ctx->ctxid, + "channel got active"); } } @@ -1032,6 +1034,50 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, return 0; } +static int iwlagn_rx_noa_notification(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb, + struct iwl_device_cmd *cmd) +{ + struct iwl_wipan_noa_data *new_data, *old_data; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_wipan_noa_notification *noa_notif = (void *)pkt->u.raw; + + /* no condition -- we're in softirq */ + old_data = rcu_dereference_protected(priv->noa_data, true); + + if (noa_notif->noa_active) { + u32 len = le16_to_cpu(noa_notif->noa_attribute.length); + u32 copylen = len; + + /* EID, len, OUI, subtype */ + len += 1 + 1 + 3 + 1; + /* P2P id, P2P length */ + len += 1 + 2; + copylen += 1 + 2; + + new_data = kmalloc(sizeof(*new_data) + len, GFP_ATOMIC); + if (new_data) { + new_data->length = len; + new_data->data[0] = WLAN_EID_VENDOR_SPECIFIC; + new_data->data[1] = len - 2; /* not counting EID, len */ + new_data->data[2] = (WLAN_OUI_WFA >> 16) & 0xff; + new_data->data[3] = (WLAN_OUI_WFA >> 8) & 0xff; + new_data->data[4] = (WLAN_OUI_WFA >> 0) & 0xff; + new_data->data[5] = WLAN_OUI_TYPE_WFA_P2P; + memcpy(&new_data->data[6], &noa_notif->noa_attribute, + copylen); + } + } else + new_data = NULL; + + rcu_assign_pointer(priv->noa_data, new_data); + + if (old_data) + kfree_rcu(old_data, rcu_head); + + return 0; +} + /** * iwl_setup_rx_handlers - Initialize Rx handler callbacks * @@ -1055,6 +1101,8 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) handlers[BEACON_NOTIFICATION] = iwlagn_rx_beacon_notif; handlers[REPLY_ADD_STA] = iwl_add_sta_callback; + handlers[REPLY_WIPAN_NOA_NOTIFICATION] = iwlagn_rx_noa_notification; + /* * The same handler is used for both the REPLY to a discrete * statistics request from the host as well as for the periodic @@ -1083,13 +1131,13 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv) priv->rx_handlers[REPLY_TX] = iwlagn_rx_reply_tx; /* set up notification wait support */ - spin_lock_init(&priv->notif_wait_lock); - INIT_LIST_HEAD(&priv->notif_waits); - init_waitqueue_head(&priv->notif_waitq); + spin_lock_init(&priv->shrd->notif_wait_lock); + INIT_LIST_HEAD(&priv->shrd->notif_waits); + init_waitqueue_head(&priv->shrd->notif_waitq); /* Set up BT Rx handlers */ - if (priv->cfg->lib->bt_rx_handler_setup) - priv->cfg->lib->bt_rx_handler_setup(priv); + if (cfg(priv)->lib->bt_rx_handler_setup) + cfg(priv)->lib->bt_rx_handler_setup(priv); } @@ -1104,11 +1152,11 @@ int iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, * even if the RX handler consumes the RXB we have * access to it in the notification wait entry. */ - if (!list_empty(&priv->notif_waits)) { + if (!list_empty(&priv->shrd->notif_waits)) { struct iwl_notification_wait *w; - spin_lock(&priv->notif_wait_lock); - list_for_each_entry(w, &priv->notif_waits, list) { + spin_lock(&priv->shrd->notif_wait_lock); + list_for_each_entry(w, &priv->shrd->notif_waits, list) { if (w->cmd != pkt->hdr.cmd) continue; IWL_DEBUG_RX(priv, @@ -1117,11 +1165,11 @@ int iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, pkt->hdr.cmd); w->triggered = true; if (w->fn) - w->fn(priv, pkt, w->fn_data); + w->fn(trans(priv), pkt, w->fn_data); } - spin_unlock(&priv->notif_wait_lock); + spin_unlock(&priv->shrd->notif_wait_lock); - wake_up_all(&priv->notif_waitq); + wake_up_all(&priv->shrd->notif_waitq); } if (priv->pre_rx_handler) diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 58a381c01c8..1c665941662 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -45,7 +45,8 @@ static int iwlagn_disable_bss(struct iwl_priv *priv, send->filter_flags = old_filter; if (ret) - IWL_ERR(priv, "Error clearing ASSOC_MSK on BSS (%d)\n", ret); + IWL_DEBUG_QUIET_RFKILL(priv, + "Error clearing ASSOC_MSK on BSS (%d)\n", ret); return ret; } @@ -59,7 +60,7 @@ static int iwlagn_disable_pan(struct iwl_priv *priv, u8 old_dev_type = send->dev_type; int ret; - iwlagn_init_notification_wait(priv, &disable_wait, + iwl_init_notification_wait(priv->shrd, &disable_wait, REPLY_WIPAN_DEACTIVATION_COMPLETE, NULL, NULL); @@ -73,9 +74,9 @@ static int iwlagn_disable_pan(struct iwl_priv *priv, if (ret) { IWL_ERR(priv, "Error disabling PAN (%d)\n", ret); - iwlagn_remove_notification(priv, &disable_wait); + iwl_remove_notification(priv->shrd, &disable_wait); } else { - ret = iwlagn_wait_notification(priv, &disable_wait, HZ); + ret = iwl_wait_notification(priv->shrd, &disable_wait, HZ); if (ret) IWL_ERR(priv, "Timed out waiting for PAN disable\n"); } @@ -116,7 +117,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv, if (ctx->ht.enabled) ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; - IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", + IWL_DEBUG_INFO(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", ctx->qos_data.qos_active, ctx->qos_data.def_qos_parm.qos_flags); @@ -124,7 +125,7 @@ static void iwlagn_update_qos(struct iwl_priv *priv, sizeof(struct iwl_qosparam_cmd), &ctx->qos_data.def_qos_parm); if (ret) - IWL_ERR(priv, "Failed to update QoS\n"); + IWL_DEBUG_QUIET_RFKILL(priv, "Failed to update QoS\n"); } static int iwlagn_update_beacon(struct iwl_priv *priv, @@ -295,9 +296,9 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv, } if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && - priv->cfg->ht_params && priv->cfg->ht_params->smps_mode) + cfg(priv)->ht_params && cfg(priv)->ht_params->smps_mode) ieee80211_request_smps(ctx->vif, - priv->cfg->ht_params->smps_mode); + cfg(priv)->ht_params->smps_mode); return 0; } @@ -444,8 +445,8 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) * force CTS-to-self frames protection if RTS-CTS is not preferred * one aggregation protection method */ - if (!(priv->cfg->ht_params && - priv->cfg->ht_params->use_rts_for_aggregation)) + if (!(cfg(priv)->ht_params && + cfg(priv)->ht_params->use_rts_for_aggregation)) ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; if ((ctx->vif && ctx->vif->bss_conf.use_short_slot) || @@ -528,6 +529,24 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) return 0; } +void iwlagn_config_ht40(struct ieee80211_conf *conf, + struct iwl_rxon_context *ctx) +{ + if (conf_is_ht40_minus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ctx->ht.is_40mhz = true; + } else if (conf_is_ht40_plus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ctx->ht.is_40mhz = true; + } else { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ctx->ht.is_40mhz = false; + } +} + int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) { struct iwl_priv *priv = hw->priv; @@ -541,6 +560,9 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) mutex_lock(&priv->shrd->mutex); + if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) + goto out; + if (unlikely(test_bit(STATUS_SCANNING, &priv->shrd->status))) { IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); goto out; @@ -586,19 +608,11 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed) ctx->ht.enabled = conf_is_ht(conf); if (ctx->ht.enabled) { - if (conf_is_ht40_minus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ctx->ht.is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ctx->ht.is_40mhz = true; - } else { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - ctx->ht.is_40mhz = false; - } + /* if HT40 is used, it should not change + * after associated except channel switch */ + if (!ctx->ht.is_40mhz || + !iwl_is_associated_ctx(ctx)) + iwlagn_config_ht40(conf, ctx); } else ctx->ht.is_40mhz = false; @@ -840,7 +854,8 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, if (ctx->last_tx_rejected) { ctx->last_tx_rejected = false; iwl_trans_wake_any_queue(trans(priv), - ctx->ctxid); + ctx->ctxid, + "Disassoc: flush queue"); } ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c index ed628362393..7353826095f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-sta.c @@ -130,25 +130,15 @@ int iwl_add_sta_callback(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, return iwl_process_add_sta_resp(priv, addsta, pkt); } -static u16 iwlagn_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) -{ - u16 size = (u16)sizeof(struct iwl_addsta_cmd); - struct iwl_addsta_cmd *addsta = (struct iwl_addsta_cmd *)data; - memcpy(addsta, cmd, size); - /* resrved in 5000 */ - addsta->rate_n_flags = cpu_to_le16(0); - return size; -} - int iwl_send_add_sta(struct iwl_priv *priv, struct iwl_addsta_cmd *sta, u8 flags) { int ret = 0; - u8 data[sizeof(*sta)]; struct iwl_host_cmd cmd = { .id = REPLY_ADD_STA, .flags = flags, - .data = { data, }, + .data = { sta, }, + .len = { sizeof(*sta), }, }; u8 sta_id __maybe_unused = sta->sta.sta_id; @@ -160,7 +150,6 @@ int iwl_send_add_sta(struct iwl_priv *priv, might_sleep(); } - cmd.len[0] = iwlagn_build_addsta_hcmd(sta, data); ret = iwl_trans_send_cmd(trans(priv), &cmd); if (ret || (flags & CMD_ASYNC)) @@ -463,6 +452,7 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr) { unsigned long flags; + u8 tid; if (!iwl_is_ready(priv->shrd)) { IWL_DEBUG_INFO(priv, @@ -501,6 +491,10 @@ int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, priv->stations[sta_id].lq = NULL; } + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + memset(&priv->tid_data[sta_id][tid], 0, + sizeof(priv->tid_data[sta_id][tid])); + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; priv->num_stations--; @@ -647,7 +641,7 @@ void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx) int ret; struct iwl_addsta_cmd sta_cmd; struct iwl_link_quality_cmd lq; - bool active; + bool active, have_lq = false; spin_lock_irqsave(&priv->shrd->sta_lock, flags); if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { @@ -657,7 +651,10 @@ void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx) memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(sta_cmd)); sta_cmd.mode = 0; - memcpy(&lq, priv->stations[sta_id].lq, sizeof(lq)); + if (priv->stations[sta_id].lq) { + memcpy(&lq, priv->stations[sta_id].lq, sizeof(lq)); + have_lq = true; + } active = priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE; priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; @@ -679,7 +676,8 @@ void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx) if (ret) IWL_ERR(priv, "failed to re-add STA %pM (%d)\n", priv->stations[sta_id].sta.sta.addr, ret); - iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true); + if (have_lq) + iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true); } int iwl_get_free_ucode_key_offset(struct iwl_priv *priv) @@ -825,28 +823,6 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, return ret; } -int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - int ret; - - IWL_DEBUG_MAC80211(priv, "enter: received request to remove " - "station %pM\n", sta->addr); - mutex_lock(&priv->shrd->mutex); - IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", - sta->addr); - ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); - if (ret) - IWL_ERR(priv, "Error removing station %pM\n", - sta->addr); - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} void iwl_sta_fill_lq(struct iwl_priv *priv, struct iwl_rxon_context *ctx, u8 sta_id, struct iwl_link_quality_cmd *link_cmd) @@ -1268,9 +1244,6 @@ int iwl_set_dynamic_key(struct iwl_priv *priv, switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_TKIP: - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - if (sta) addr = sta->addr; else /* station mode case only */ @@ -1283,8 +1256,6 @@ int iwl_set_dynamic_key(struct iwl_priv *priv, seq.tkip.iv32, p1k, CMD_SYNC); break; case WLAN_CIPHER_SUITE_CCMP: - keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; - /* fall through */ case WLAN_CIPHER_SUITE_WEP40: case WLAN_CIPHER_SUITE_WEP104: ret = iwlagn_send_sta_key(priv, keyconf, sta_id, @@ -1464,20 +1435,7 @@ int iwl_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); } -static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) -{ - unsigned long flags; - spin_lock_irqsave(&priv->shrd->sta_lock, flags); - priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK; - priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; - priv->stations[sta_id].sta.sta.modify_mask = 0; - priv->stations[sta_id].sta.sleep_tx_count = 0; - priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); - spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); - -} void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) { @@ -1494,36 +1452,3 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); } - -void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - int sta_id; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - switch (cmd) { - case STA_NOTIFY_SLEEP: - WARN_ON(!sta_priv->client); - sta_priv->asleep = true; - if (atomic_read(&sta_priv->pending_frames) > 0) - ieee80211_sta_block_awake(hw, sta, true); - break; - case STA_NOTIFY_AWAKE: - WARN_ON(!sta_priv->client); - if (!sta_priv->asleep) - break; - sta_priv->asleep = false; - sta_id = iwl_sta_id(sta); - if (sta_id != IWL_INVALID_STATION) - iwl_sta_modify_ps_wake(priv, sta_id); - break; - default: - break; - } - IWL_DEBUG_MAC80211(priv, "leave\n"); -} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c index c27180a7335..b0dff7a753a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tt.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tt.c @@ -633,7 +633,7 @@ void iwl_tt_initialize(struct iwl_priv *priv) INIT_WORK(&priv->ct_enter, iwl_bg_ct_enter); INIT_WORK(&priv->ct_exit, iwl_bg_ct_exit); - if (priv->cfg->base_params->adv_thermal_throttle) { + if (cfg(priv)->base_params->adv_thermal_throttle) { IWL_DEBUG_TEMP(priv, "Advanced Thermal Throttling\n"); tt->restriction = kcalloc(IWL_TI_STATE_MAX, sizeof(struct iwl_tt_restriction), diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 35a6b71f358..c664c272655 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -74,8 +74,8 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, else if (ieee80211_is_back_req(fc)) tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; else if (info->band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist && (ieee80211_is_auth(fc) || ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc) || skb->protocol == cpu_to_be16(ETH_P_PAE))) @@ -91,7 +91,10 @@ static void iwlagn_tx_cmd_build_basic(struct iwl_priv *priv, tx_cmd->tid_tspec = qc[0] & 0xf; tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } else { - tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + else + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; } iwlagn_tx_cmd_protection(priv, info, fc, &tx_flags); @@ -148,7 +151,7 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, if (ieee80211_is_data(fc)) { tx_cmd->initial_rate_index = 0; tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; -#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE if (priv->tm_fixed_rate) { /* * rate overwrite by testmode @@ -161,7 +164,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, } #endif return; - } + } else if (ieee80211_is_back_req(fc)) + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; /** * If the current TX rate stored in mac80211 has the MCS bit set, it's @@ -187,8 +191,8 @@ static void iwlagn_tx_cmd_build_rate(struct iwl_priv *priv, rate_flags |= RATE_MCS_CCK_MSK; /* Set up antennas */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ priv->mgmt_tx_ant = iwl_toggle_tx_ant(priv, priv->mgmt_tx_ant, @@ -258,8 +262,8 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) __le16 fc; u8 hdr_len; - u16 len; - u8 sta_id; + u16 len, seq_number = 0; + u8 sta_id, tid = IWL_MAX_TID_COUNT; unsigned long flags; bool is_agg = false; @@ -283,6 +287,19 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); #endif + if (unlikely(ieee80211_is_probe_resp(fc))) { + struct iwl_wipan_noa_data *noa_data = + rcu_dereference(priv->noa_data); + + if (noa_data && + pskb_expand_head(skb, 0, noa_data->length, + GFP_ATOMIC) == 0) { + memcpy(skb_put(skb, noa_data->length), + noa_data->data, noa_data->length); + hdr = (struct ieee80211_hdr *)skb->data; + } + } + hdr_len = ieee80211_hdrlen(fc); /* For management frames use broadcast id to do not break aggregation */ @@ -351,9 +368,51 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) info->driver_data[0] = ctx; info->driver_data[1] = dev_cmd; - if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id)) + if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { + u8 *qc = NULL; + struct iwl_tid_data *tid_data; + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + goto drop_unlock_sta; + tid_data = &priv->tid_data[sta_id][tid]; + + /* aggregation is on for this <sta,tid> */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + tid_data->agg.state != IWL_AGG_ON) { + IWL_ERR(priv, "TX_CTL_AMPDU while not in AGG:" + " Tx flags = 0x%08x, agg.state = %d", + info->flags, tid_data->agg.state); + IWL_ERR(priv, "sta_id = %d, tid = %d seq_num = %d", + sta_id, tid, SEQ_TO_SN(tid_data->seq_number)); + goto drop_unlock_sta; + } + + /* We can receive packets from the stack in IWL_AGG_{ON,OFF} + * only. Check this here. + */ + if (WARN_ONCE(tid_data->agg.state != IWL_AGG_ON && + tid_data->agg.state != IWL_AGG_OFF, + "Tx while agg.state = %d", tid_data->agg.state)) + goto drop_unlock_sta; + + seq_number = tid_data->seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + seq_number += 0x10; + } + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + if (iwl_trans_tx(trans(priv), skb, dev_cmd, ctx->ctxid, sta_id, tid)) goto drop_unlock_sta; + if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) && + !ieee80211_has_morefrags(fc)) + priv->tid_data[sta_id][tid].seq_number = seq_number; + spin_unlock(&priv->shrd->sta_lock); spin_unlock_irqrestore(&priv->shrd->lock, flags); @@ -378,10 +437,81 @@ drop_unlock_priv: return -1; } +int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + struct iwl_tid_data *tid_data; + unsigned long flags; + int sta_id; + + sta_id = iwl_sta_id(sta); + + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_irqsave(&priv->shrd->sta_lock, flags); + + tid_data = &priv->tid_data[sta_id][tid]; + + switch (priv->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* + * This can happen if the peer stops aggregation + * again before we've had a chance to drain the + * queue we selected previously, i.e. before the + * session was really started completely. + */ + IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); + goto turn_off; + case IWL_AGG_ON: + break; + default: + IWL_WARN(priv, "Stopping AGG while state not ON " + "or starting for %d on %d (%d)\n", sta_id, tid, + priv->tid_data[sta_id][tid].agg.state); + spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + return 0; + } + + tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); + + /* There are still packets for this RA / TID in the HW */ + if (tid_data->agg.ssn != tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " + "next_recl = %d", + tid_data->agg.ssn, + tid_data->next_reclaimed); + priv->tid_data[sta_id][tid].agg.state = + IWL_EMPTYING_HW_QUEUE_DELBA; + spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + return 0; + } + + IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d", + tid_data->agg.ssn); +turn_off: + priv->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF; + + /* do not restore/save irqs */ + spin_unlock(&priv->shrd->sta_lock); + spin_lock(&priv->shrd->lock); + + iwl_trans_tx_agg_disable(trans(priv), sta_id, tid); + + spin_unlock_irqrestore(&priv->shrd->lock, flags); + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + + return 0; +} + int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn) { - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_tid_data *tid_data; + unsigned long flags; int sta_id; int ret; @@ -396,7 +526,7 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, if (unlikely(tid >= IWL_MAX_TID_COUNT)) return -EINVAL; - if (priv->shrd->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { + if (priv->tid_data[sta_id][tid].agg.state != IWL_AGG_OFF) { IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); return -ENXIO; } @@ -405,27 +535,136 @@ int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, if (ret) return ret; - ret = iwl_trans_tx_agg_alloc(trans(priv), vif_priv->ctx->ctxid, sta_id, - tid, ssn); + spin_lock_irqsave(&priv->shrd->sta_lock, flags); + + tid_data = &priv->tid_data[sta_id][tid]; + tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); + + *ssn = tid_data->agg.ssn; + + ret = iwl_trans_tx_agg_alloc(trans(priv), sta_id, tid); + if (ret) { + spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + return ret; + } + + if (*ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, "Can proceed: ssn = next_recl = %d", + tid_data->agg.ssn); + tid_data->agg.state = IWL_AGG_ON; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + } else { + IWL_DEBUG_TX_QUEUES(priv, "Can't proceed: ssn %d, " + "next_reclaimed = %d", + tid_data->agg.ssn, + tid_data->next_reclaimed); + tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; + } + + spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return ret; } -int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, u16 tid) +int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size) { - int sta_id; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + unsigned long flags; + u16 ssn; - sta_id = iwl_sta_id(sta); + buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); - if (sta_id == IWL_INVALID_STATION) { - IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); - return -ENXIO; + spin_lock_irqsave(&priv->shrd->sta_lock, flags); + ssn = priv->tid_data[sta_priv->sta_id][tid].agg.ssn; + spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + + iwl_trans_tx_agg_setup(trans(priv), ctx->ctxid, sta_priv->sta_id, tid, + buf_size, ssn); + + /* + * If the limit is 0, then it wasn't initialised yet, + * use the default. We can do that since we take the + * minimum below, and we don't want to go above our + * default due to hardware restrictions. + */ + if (sta_priv->max_agg_bufsize == 0) + sta_priv->max_agg_bufsize = + LINK_QUAL_AGG_FRAME_LIMIT_DEF; + + /* + * Even though in theory the peer could have different + * aggregation reorder buffer sizes for different sessions, + * our ucode doesn't allow for that and has a global limit + * for each station. Therefore, use the minimum of all the + * aggregation sessions and our default value. + */ + sta_priv->max_agg_bufsize = + min(sta_priv->max_agg_bufsize, buf_size); + + if (cfg(priv)->ht_params && + cfg(priv)->ht_params->use_rts_for_aggregation) { + /* + * switch to RTS/CTS if it is the prefer protection + * method for HT traffic + */ + + sta_priv->lq_sta.lq.general_params.flags |= + LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; } + priv->agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + + sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = + sta_priv->max_agg_bufsize; + + IWL_INFO(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", + sta->addr, tid); - return iwl_trans_tx_agg_disable(trans(priv), vif_priv->ctx->ctxid, - sta_id, tid); + return iwl_send_lq_cmd(priv, ctx, + &sta_priv->lq_sta.lq, CMD_ASYNC, false); +} + +static void iwlagn_check_ratid_empty(struct iwl_priv *priv, int sta_id, u8 tid) +{ + struct iwl_tid_data *tid_data = &priv->tid_data[sta_id][tid]; + enum iwl_rxon_context_id ctx; + struct ieee80211_vif *vif; + u8 *addr; + + lockdep_assert_held(&priv->shrd->sta_lock); + + addr = priv->stations[sta_id].sta.sta.addr; + ctx = priv->stations[sta_id].ctxid; + vif = priv->contexts[ctx].vif; + + switch (priv->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_DELBA: + /* There are no packets for this RA / TID in the HW any more */ + if (tid_data->agg.ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can continue DELBA flow ssn = next_recl =" + " %d", tid_data->next_reclaimed); + iwl_trans_tx_agg_disable(trans(priv), sta_id, tid); + tid_data->agg.state = IWL_AGG_OFF; + ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); + } + break; + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* There are no packets for this RA / TID in the HW any more */ + if (tid_data->agg.ssn == tid_data->next_reclaimed) { + IWL_DEBUG_TX_QUEUES(priv, + "Can continue ADDBA flow ssn = next_recl =" + " %d", tid_data->next_reclaimed); + tid_data->agg.state = IWL_AGG_ON; + ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); + } + break; + default: + break; + } } static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, @@ -565,7 +804,7 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, IWLAGN_TX_RES_TID_POS; int sta_id = (tx_resp->ra_tid & IWLAGN_TX_RES_RA_MSK) >> IWLAGN_TX_RES_RA_POS; - struct iwl_ht_agg *agg = &priv->shrd->tid_data[sta_id][tid].agg; + struct iwl_ht_agg *agg = &priv->tid_data[sta_id][tid].agg; u32 status = le16_to_cpu(tx_resp->status.status); int i; @@ -581,8 +820,8 @@ static void iwl_rx_reply_tx_agg(struct iwl_priv *priv, * notification again. */ if (tx_resp->bt_kill_count && tx_resp->frame_count == 1 && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) { IWL_DEBUG_COEX(priv, "receive reply tx w/ bt_kill\n"); } @@ -755,7 +994,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; struct ieee80211_hdr *hdr; u32 status = le16_to_cpu(tx_resp->status.status); - u32 ssn = iwlagn_get_scd_ssn(tx_resp); + u16 ssn = iwlagn_get_scd_ssn(tx_resp); int tid; int sta_id; int freed; @@ -777,10 +1016,34 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, iwl_rx_reply_tx_agg(priv, tx_resp); if (tx_resp->frame_count == 1) { + u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); + next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); + + if (is_agg) { + /* If this is an aggregation queue, we can rely on the + * ssn since the wifi sequence number corresponds to + * the index in the TFD ring (%256). + * The seq_ctl is the sequence control of the packet + * to which this Tx response relates. But if there is a + * hole in the bitmap of the BA we received, this Tx + * response may allow to reclaim the hole and all the + * subsequent packets that were already acked. + * In that case, seq_ctl != ssn, and the next packet + * to be reclaimed will be ssn and not seq_ctl. + */ + next_reclaimed = ssn; + } + __skb_queue_head_init(&skbs); + priv->tid_data[sta_id][tid].next_reclaimed = next_reclaimed; + + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", + next_reclaimed); + /*we can free until ssn % q.n_bd not inclusive */ - iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, - ssn, status, &skbs); + WARN_ON(iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, + ssn, status, &skbs)); + iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); @@ -800,7 +1063,8 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, iwl_is_associated_ctx(ctx) && ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) { ctx->last_tx_rejected = true; - iwl_trans_stop_queue(trans(priv), txq_id); + iwl_trans_stop_queue(trans(priv), txq_id, + "Tx on passive channel"); IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) " @@ -874,27 +1138,24 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, sta_id = ba_resp->sta_id; tid = ba_resp->tid; - agg = &priv->shrd->tid_data[sta_id][tid].agg; + agg = &priv->tid_data[sta_id][tid].agg; spin_lock_irqsave(&priv->shrd->sta_lock, flags); - if (unlikely(agg->txq_id != scd_flow)) { - /* - * FIXME: this is a uCode bug which need to be addressed, - * log the information and return for now! - * since it is possible happen very often and in order - * not to fill the syslog, don't enable the logging by default - */ - IWL_DEBUG_TX_REPLY(priv, - "BA scd_flow %d does not match txq_id %d\n", - scd_flow, agg->txq_id); + if (unlikely(!agg->wait_for_ba)) { + if (unlikely(ba_resp->bitmap)) + IWL_ERR(priv, "Received BA when not expected\n"); spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return 0; } - if (unlikely(!agg->wait_for_ba)) { - if (unlikely(ba_resp->bitmap)) - IWL_ERR(priv, "Received BA when not expected\n"); + __skb_queue_head_init(&reclaimed_skbs); + + /* Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack window (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). */ + if (iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, + ba_resp_scd_ssn, 0, &reclaimed_skbs)) { spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); return 0; } @@ -906,11 +1167,9 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, ba_resp->sta_id); IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx, " "scd_flow = %d, scd_ssn = %d\n", - ba_resp->tid, - ba_resp->seq_ctl, + ba_resp->tid, le16_to_cpu(ba_resp->seq_ctl), (unsigned long long)le64_to_cpu(ba_resp->bitmap), - ba_resp->scd_flow, - ba_resp->scd_ssn); + scd_flow, ba_resp_scd_ssn); /* Mark that the expected block-ack response arrived */ agg->wait_for_ba = false; @@ -929,13 +1188,9 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, IWL_DEBUG_HT(priv, "agg frames sent:%d, acked:%d\n", ba_resp->txed, ba_resp->txed_2_done); - __skb_queue_head_init(&reclaimed_skbs); + priv->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; - /* Release all TFDs before the SSN, i.e. all TFDs in front of - * block-ack window (we assume that they've been successfully - * transmitted ... if not, it's too late anyway). */ - iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, ba_resp_scd_ssn, - 0, &reclaimed_skbs); + iwlagn_check_ratid_empty(priv, sta_id, tid); freed = 0; while (!skb_queue_empty(&reclaimed_skbs)) { diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index ccba69b7f8a..b5c7c5f0a75 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -30,7 +30,6 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/dma-mapping.h> #include <linux/delay.h> #include <linux/sched.h> #include <linux/skbuff.h> @@ -44,6 +43,7 @@ #include <asm/div64.h> #include "iwl-eeprom.h" +#include "iwl-wifi.h" #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-io.h" @@ -367,7 +367,7 @@ static void iwl_continuous_event_trace(struct iwl_priv *priv) u32 num_wraps; /* # times uCode wrapped to top of log */ u32 next_entry; /* index of next entry to be written by uCode */ - base = priv->device_pointers.error_event_table; + base = priv->shrd->device_pointers.error_event_table; if (iwlagn_hw_valid_rtc_data_addr(base)) { capacity = iwl_read_targ_mem(bus(priv), base); num_wraps = iwl_read_targ_mem(bus(priv), @@ -452,52 +452,6 @@ static void iwl_bg_tx_flush(struct work_struct *work) iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); } -/****************************************************************************** - * - * uCode download functions - * - ******************************************************************************/ - -static void iwl_free_fw_desc(struct iwl_priv *priv, struct fw_desc *desc) -{ - if (desc->v_addr) - dma_free_coherent(bus(priv)->dev, desc->len, - desc->v_addr, desc->p_addr); - desc->v_addr = NULL; - desc->len = 0; -} - -static void iwl_free_fw_img(struct iwl_priv *priv, struct fw_img *img) -{ - iwl_free_fw_desc(priv, &img->code); - iwl_free_fw_desc(priv, &img->data); -} - -static void iwl_dealloc_ucode(struct iwl_priv *priv) -{ - iwl_free_fw_img(priv, &priv->ucode_rt); - iwl_free_fw_img(priv, &priv->ucode_init); - iwl_free_fw_img(priv, &priv->ucode_wowlan); -} - -static int iwl_alloc_fw_desc(struct iwl_priv *priv, struct fw_desc *desc, - const void *data, size_t len) -{ - if (!len) { - desc->v_addr = NULL; - return -EINVAL; - } - - desc->v_addr = dma_alloc_coherent(bus(priv)->dev, len, - &desc->p_addr, GFP_KERNEL); - if (!desc->v_addr) - return -ENOMEM; - - desc->len = len; - memcpy(desc->v_addr, data, len); - return 0; -} - static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) { int i; @@ -555,23 +509,14 @@ static void iwl_init_context(struct iwl_priv *priv, u32 ucode_flags) BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); } - -struct iwlagn_ucode_capabilities { - u32 max_probe_length; - u32 standard_phy_calibration_size; - u32 flags; -}; - static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context); -static int iwlagn_mac_setup_register(struct iwl_priv *priv, - struct iwlagn_ucode_capabilities *capa); #define UCODE_EXPERIMENTAL_INDEX 100 #define UCODE_EXPERIMENTAL_TAG "exp" static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first) { - const char *name_pre = priv->cfg->fw_name_pre; + const char *name_pre = cfg(priv)->fw_name_pre; char tag[8]; if (first) { @@ -580,14 +525,14 @@ static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first) strcpy(tag, UCODE_EXPERIMENTAL_TAG); } else if (priv->fw_index == UCODE_EXPERIMENTAL_INDEX) { #endif - priv->fw_index = priv->cfg->ucode_api_max; + priv->fw_index = cfg(priv)->ucode_api_max; sprintf(tag, "%d", priv->fw_index); } else { priv->fw_index--; sprintf(tag, "%d", priv->fw_index); } - if (priv->fw_index < priv->cfg->ucode_api_min) { + if (priv->fw_index < cfg(priv)->ucode_api_min) { IWL_ERR(priv, "no suitable firmware found!\n"); return -ENOENT; } @@ -892,9 +837,9 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) struct iwl_ucode_header *ucode; int err; struct iwlagn_firmware_pieces pieces; - const unsigned int api_max = priv->cfg->ucode_api_max; - unsigned int api_ok = priv->cfg->ucode_api_ok; - const unsigned int api_min = priv->cfg->ucode_api_min; + const unsigned int api_max = cfg(priv)->ucode_api_max; + unsigned int api_ok = cfg(priv)->ucode_api_ok; + const unsigned int api_min = cfg(priv)->ucode_api_min; u32 api_ver; char buildstr[25]; u32 build; @@ -1040,30 +985,32 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) /* Runtime instructions and 2 copies of data: * 1) unmodified from disk * 2) backup cache for save/restore during power-downs */ - if (iwl_alloc_fw_desc(priv, &priv->ucode_rt.code, + if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_rt.code, pieces.inst, pieces.inst_size)) goto err_pci_alloc; - if (iwl_alloc_fw_desc(priv, &priv->ucode_rt.data, + if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_rt.data, pieces.data, pieces.data_size)) goto err_pci_alloc; /* Initialization instructions and data */ if (pieces.init_size && pieces.init_data_size) { - if (iwl_alloc_fw_desc(priv, &priv->ucode_init.code, + if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_init.code, pieces.init, pieces.init_size)) goto err_pci_alloc; - if (iwl_alloc_fw_desc(priv, &priv->ucode_init.data, + if (iwl_alloc_fw_desc(bus(priv), &trans(priv)->ucode_init.data, pieces.init_data, pieces.init_data_size)) goto err_pci_alloc; } /* WoWLAN instructions and data */ if (pieces.wowlan_inst_size && pieces.wowlan_data_size) { - if (iwl_alloc_fw_desc(priv, &priv->ucode_wowlan.code, + if (iwl_alloc_fw_desc(bus(priv), + &trans(priv)->ucode_wowlan.code, pieces.wowlan_inst, pieces.wowlan_inst_size)) goto err_pci_alloc; - if (iwl_alloc_fw_desc(priv, &priv->ucode_wowlan.data, + if (iwl_alloc_fw_desc(bus(priv), + &trans(priv)->ucode_wowlan.data, pieces.wowlan_data, pieces.wowlan_data_size)) goto err_pci_alloc; @@ -1081,20 +1028,23 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) priv->init_evtlog_size = (pieces.init_evtlog_size - 16)/12; else priv->init_evtlog_size = - priv->cfg->base_params->max_event_log_size; + cfg(priv)->base_params->max_event_log_size; priv->init_errlog_ptr = pieces.init_errlog_ptr; priv->inst_evtlog_ptr = pieces.inst_evtlog_ptr; if (pieces.inst_evtlog_size) priv->inst_evtlog_size = (pieces.inst_evtlog_size - 16)/12; else priv->inst_evtlog_size = - priv->cfg->base_params->max_event_log_size; + cfg(priv)->base_params->max_event_log_size; priv->inst_errlog_ptr = pieces.inst_errlog_ptr; +#ifndef CONFIG_IWLWIFI_P2P + ucode_capa.flags &= ~IWL_UCODE_TLV_FLAGS_PAN; +#endif priv->new_scan_threshold_behaviour = !!(ucode_capa.flags & IWL_UCODE_TLV_FLAGS_NEWSCAN); - if (!(priv->cfg->sku & EEPROM_SKU_CAP_IPAN_ENABLE)) + if (!(cfg(priv)->sku & EEPROM_SKU_CAP_IPAN_ENABLE)) ucode_capa.flags &= ~IWL_UCODE_TLV_FLAGS_PAN; /* @@ -1111,7 +1061,6 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) priv->sta_key_max_num = STA_KEY_MAX_NUM; priv->shrd->cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; } - /* * figure out the offset of chain noise reset and gain commands * base on the size of standard phy calibration commands table size @@ -1156,7 +1105,7 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context) err_pci_alloc: IWL_ERR(priv, "failed to allocate pci memory\n"); - iwl_dealloc_ucode(priv); + iwl_dealloc_ucode(trans(priv)); out_unbind: complete(&priv->firmware_loading_complete); device_release_driver(bus(priv)->dev); @@ -1176,7 +1125,7 @@ static void iwl_rf_kill_ct_config(struct iwl_priv *priv) spin_unlock_irqrestore(&priv->shrd->lock, flags); priv->thermal_throttle.ct_kill_toggle = false; - if (priv->cfg->base_params->support_ct_kill_exit) { + if (cfg(priv)->base_params->support_ct_kill_exit) { adv_cmd.critical_temperature_enter = cpu_to_le32(hw_params(priv).ct_kill_threshold); adv_cmd.critical_temperature_exit = @@ -1271,10 +1220,10 @@ int iwl_alive_start(struct iwl_priv *priv) return -ERFKILL; /* download priority table before any calibration request */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) { /* Configure Bluetooth device coexistence support */ - if (priv->cfg->bt_params->bt_sco_disable) + if (cfg(priv)->bt_params->bt_sco_disable) priv->bt_enable_pspoll = false; else priv->bt_enable_pspoll = true; @@ -1286,14 +1235,14 @@ int iwl_alive_start(struct iwl_priv *priv) priv->bt_valid = IWLAGN_BT_VALID_ENABLE_FLAGS; priv->cur_rssi_ctx = NULL; - iwlagn_send_prio_tbl(priv); + iwl_send_prio_tbl(trans(priv)); /* FIXME: w/a to force change uCode BT state machine */ - ret = iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, + ret = iwl_send_bt_env(trans(priv), IWL_BT_COEX_ENV_OPEN, BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret; - ret = iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_CLOSE, + ret = iwl_send_bt_env(trans(priv), IWL_BT_COEX_ENV_CLOSE, BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret; @@ -1304,16 +1253,17 @@ int iwl_alive_start(struct iwl_priv *priv) iwl_send_bt_config(priv); } - if (hw_params(priv).calib_rt_cfg) - iwlagn_send_calib_cfg_rt(priv, - hw_params(priv).calib_rt_cfg); + /* + * Perform runtime calibrations, including DC calibration. + */ + iwlagn_send_calib_cfg_rt(priv, IWL_CALIB_CFG_DC_IDX); ieee80211_wake_queues(priv->hw); priv->active_rate = IWL_RATES_MASK; /* Configure Tx antenna selection based on H/W config */ - iwlagn_send_tx_ant_config(priv, priv->cfg->valid_tx_ant); + iwlagn_send_tx_ant_config(priv, cfg(priv)->valid_tx_ant); if (iwl_is_associated_ctx(ctx) && !priv->shrd->wowlan) { struct iwl_rxon_cmd *active_rxon = @@ -1352,7 +1302,7 @@ int iwl_alive_start(struct iwl_priv *priv) static void iwl_cancel_deferred_work(struct iwl_priv *priv); -static void __iwl_down(struct iwl_priv *priv) +void __iwl_down(struct iwl_priv *priv) { int exit_pending; @@ -1382,9 +1332,9 @@ static void __iwl_down(struct iwl_priv *priv) priv->bt_status = 0; priv->cur_rssi_ctx = NULL; priv->bt_is_sco = 0; - if (priv->cfg->bt_params) + if (cfg(priv)->bt_params) priv->bt_traffic_load = - priv->cfg->bt_params->bt_init_traffic_load; + cfg(priv)->bt_params->bt_init_traffic_load; else priv->bt_traffic_load = 0; priv->bt_full_concurrent = false; @@ -1415,7 +1365,7 @@ static void __iwl_down(struct iwl_priv *priv) priv->beacon_skb = NULL; } -static void iwl_down(struct iwl_priv *priv) +void iwl_down(struct iwl_priv *priv) { mutex_lock(&priv->shrd->mutex); __iwl_down(priv); @@ -1424,57 +1374,6 @@ static void iwl_down(struct iwl_priv *priv) iwl_cancel_deferred_work(priv); } -#define MAX_HW_RESTARTS 5 - -static int __iwl_up(struct iwl_priv *priv) -{ - struct iwl_rxon_context *ctx; - int ret; - - lockdep_assert_held(&priv->shrd->mutex); - - if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) { - IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); - return -EIO; - } - - for_each_context(priv, ctx) { - ret = iwlagn_alloc_bcast_station(priv, ctx); - if (ret) { - iwl_dealloc_bcast_stations(priv); - return ret; - } - } - - ret = iwlagn_run_init_ucode(priv); - if (ret) { - IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); - goto error; - } - - ret = iwlagn_load_ucode_wait_alive(priv, - &priv->ucode_rt, - IWL_UCODE_REGULAR); - if (ret) { - IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); - goto error; - } - - ret = iwl_alive_start(priv); - if (ret) - goto error; - return 0; - - error: - set_bit(STATUS_EXIT_PENDING, &priv->shrd->status); - __iwl_down(priv); - clear_bit(STATUS_EXIT_PENDING, &priv->shrd->status); - - IWL_ERR(priv, "Unable to initialize device.\n"); - return ret; -} - - /***************************************************************************** * * Workqueue callbacks @@ -1502,7 +1401,7 @@ static void iwl_bg_run_time_calib_work(struct work_struct *work) mutex_unlock(&priv->shrd->mutex); } -static void iwlagn_prepare_restart(struct iwl_priv *priv) +void iwlagn_prepare_restart(struct iwl_priv *priv) { struct iwl_rxon_context *ctx; bool bt_full_concurrent; @@ -1559,1173 +1458,8 @@ static void iwl_bg_restart(struct work_struct *data) } } -/***************************************************************************** - * - * mac80211 entry point functions - * - *****************************************************************************/ - -static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = { - { - .max = 1, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION), - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT), - }, -}; - -static const struct ieee80211_iface_combination -iwlagn_iface_combinations_dualmode[] = { - { .num_different_channels = 1, - .max_interfaces = 2, - .beacon_int_infra_match = true, - .limits = iwlagn_sta_ap_limits, - .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), - }, - { .num_different_channels = 1, - .max_interfaces = 2, - .limits = iwlagn_2sta_limits, - .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), - }, -}; - -static const struct ieee80211_iface_combination -iwlagn_iface_combinations_p2p[] = { - { .num_different_channels = 1, - .max_interfaces = 2, - .beacon_int_infra_match = true, - .limits = iwlagn_p2p_sta_go_limits, - .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits), - }, - { .num_different_channels = 1, - .max_interfaces = 2, - .limits = iwlagn_p2p_2sta_limits, - .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits), - }, -}; - -/* - * Not a mac80211 entry point function, but it fits in with all the - * other mac80211 functions grouped here. - */ -static int iwlagn_mac_setup_register(struct iwl_priv *priv, - struct iwlagn_ucode_capabilities *capa) -{ - int ret; - struct ieee80211_hw *hw = priv->hw; - struct iwl_rxon_context *ctx; - - hw->rate_control_algorithm = "iwl-agn-rs"; - - /* Tell mac80211 our characteristics */ - hw->flags = IEEE80211_HW_SIGNAL_DBM | - IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_NEED_DTIM_PERIOD | - IEEE80211_HW_SPECTRUM_MGMT | - IEEE80211_HW_REPORTS_TX_ACK_STATUS; - - /* - * Including the following line will crash some AP's. This - * workaround removes the stimulus which causes the crash until - * the AP software can be fixed. - hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; - */ - - hw->flags |= IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_DYNAMIC_PS; - - if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE) - hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | - IEEE80211_HW_SUPPORTS_STATIC_SMPS; - - if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) - hw->flags |= IEEE80211_HW_MFP_CAPABLE; - - hw->sta_data_size = sizeof(struct iwl_station_priv); - hw->vif_data_size = sizeof(struct iwl_vif_priv); - - for_each_context(priv, ctx) { - hw->wiphy->interface_modes |= ctx->interface_modes; - hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; - } - - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - - if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) { - hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwlagn_iface_combinations_p2p); - } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { - hw->wiphy->iface_combinations = iwlagn_iface_combinations_dualmode; - hw->wiphy->n_iface_combinations = - ARRAY_SIZE(iwlagn_iface_combinations_dualmode); - } - - hw->wiphy->max_remain_on_channel_duration = 1000; - - hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | - WIPHY_FLAG_DISABLE_BEACON_HINTS | - WIPHY_FLAG_IBSS_RSN; - - if (priv->ucode_wowlan.code.len && device_can_wakeup(bus(priv)->dev)) { - hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | - WIPHY_WOWLAN_DISCONNECT | - WIPHY_WOWLAN_EAP_IDENTITY_REQ | - WIPHY_WOWLAN_RFKILL_RELEASE; - if (!iwlagn_mod_params.sw_crypto) - hw->wiphy->wowlan.flags |= - WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | - WIPHY_WOWLAN_GTK_REKEY_FAILURE; - - hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; - hw->wiphy->wowlan.pattern_min_len = - IWLAGN_WOWLAN_MIN_PATTERN_LEN; - hw->wiphy->wowlan.pattern_max_len = - IWLAGN_WOWLAN_MAX_PATTERN_LEN; - } - - if (iwlagn_mod_params.power_save) - hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; - else - hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; - - hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; - /* we create the 802.11 header and a zero-length SSID element */ - hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 2; - - /* Default value; 4 EDCA QOS priorities */ - hw->queues = 4; - - hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; - - if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = - &priv->bands[IEEE80211_BAND_2GHZ]; - if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) - priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = - &priv->bands[IEEE80211_BAND_5GHZ]; - - iwl_leds_init(priv); - - ret = ieee80211_register_hw(priv->hw); - if (ret) { - IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); - return ret; - } - priv->mac80211_registered = 1; - - return 0; -} - - -static int iwlagn_mac_start(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - int ret; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - /* we should be verifying the device is ready to be opened */ - mutex_lock(&priv->shrd->mutex); - ret = __iwl_up(priv); - mutex_unlock(&priv->shrd->mutex); - if (ret) - return ret; - - IWL_DEBUG_INFO(priv, "Start UP work done.\n"); - - /* Now we should be done, and the READY bit should be set. */ - if (WARN_ON(!test_bit(STATUS_READY, &priv->shrd->status))) - ret = -EIO; - - iwlagn_led_enable(priv); - - priv->is_open = 1; - IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; -} - -static void iwlagn_mac_stop(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!priv->is_open) - return; - - priv->is_open = 0; - - iwl_down(priv); - - flush_workqueue(priv->shrd->workqueue); - - /* User space software may expect getting rfkill changes - * even if interface is down */ - iwl_write32(bus(priv), CSR_INT, 0xFFFFFFFF); - iwl_enable_rfkill_int(priv); - - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -#ifdef CONFIG_PM_SLEEP -static int iwlagn_send_patterns(struct iwl_priv *priv, - struct cfg80211_wowlan *wowlan) -{ - struct iwlagn_wowlan_patterns_cmd *pattern_cmd; - struct iwl_host_cmd cmd = { - .id = REPLY_WOWLAN_PATTERNS, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .flags = CMD_SYNC, - }; - int i, err; - - if (!wowlan->n_patterns) - return 0; - - cmd.len[0] = sizeof(*pattern_cmd) + - wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern); - - pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); - if (!pattern_cmd) - return -ENOMEM; - - pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); - - for (i = 0; i < wowlan->n_patterns; i++) { - int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); - - memcpy(&pattern_cmd->patterns[i].mask, - wowlan->patterns[i].mask, mask_len); - memcpy(&pattern_cmd->patterns[i].pattern, - wowlan->patterns[i].pattern, - wowlan->patterns[i].pattern_len); - pattern_cmd->patterns[i].mask_size = mask_len; - pattern_cmd->patterns[i].pattern_size = - wowlan->patterns[i].pattern_len; - } - - cmd.data[0] = pattern_cmd; - err = iwl_trans_send_cmd(trans(priv), &cmd); - kfree(pattern_cmd); - return err; -} -#endif - -static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_gtk_rekey_data *data) -{ - struct iwl_priv *priv = hw->priv; - - if (iwlagn_mod_params.sw_crypto) - return; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) - goto out; - - memcpy(priv->kek, data->kek, NL80211_KEK_LEN); - memcpy(priv->kck, data->kck, NL80211_KCK_LEN); - priv->replay_ctr = cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); - priv->have_rekey_data = true; - - out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -struct wowlan_key_data { - struct iwl_rxon_context *ctx; - struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; - struct iwlagn_wowlan_tkip_params_cmd *tkip; - const u8 *bssid; - bool error, use_rsc_tsc, use_tkip; -}; - -#ifdef CONFIG_PM_SLEEP -static void iwlagn_convert_p1k(u16 *p1k, __le16 *out) -{ - int i; - - for (i = 0; i < IWLAGN_P1K_SIZE; i++) - out[i] = cpu_to_le16(p1k[i]); -} - -static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key, - void *_data) -{ - struct iwl_priv *priv = hw->priv; - struct wowlan_key_data *data = _data; - struct iwl_rxon_context *ctx = data->ctx; - struct aes_sc *aes_sc, *aes_tx_sc = NULL; - struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; - struct iwlagn_p1k_cache *rx_p1ks; - u8 *rx_mic_key; - struct ieee80211_key_seq seq; - u32 cur_rx_iv32 = 0; - u16 p1k[IWLAGN_P1K_SIZE]; - int ret, i; - - mutex_lock(&priv->shrd->mutex); - - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && - !sta && !ctx->key_mapping_keys) - ret = iwl_set_default_wep_key(priv, ctx, key); - else - ret = iwl_set_dynamic_key(priv, ctx, key, sta); - - if (ret) { - IWL_ERR(priv, "Error setting key during suspend!\n"); - data->error = true; - } - - switch (key->cipher) { - case WLAN_CIPHER_SUITE_TKIP: - if (sta) { - tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; - tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; - - rx_p1ks = data->tkip->rx_uni; - - ieee80211_get_key_tx_seq(key, &seq); - tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32); - - ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); - iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); - - memcpy(data->tkip->mic_keys.tx, - &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], - IWLAGN_MIC_KEY_SIZE); - - rx_mic_key = data->tkip->mic_keys.rx_unicast; - } else { - tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; - rx_p1ks = data->tkip->rx_multi; - rx_mic_key = data->tkip->mic_keys.rx_mcast; - } - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 (as they need to to avoid replay attacks) - * for checking the IV in the frames. - */ - for (i = 0; i < IWLAGN_NUM_RSC; i++) { - ieee80211_get_key_rx_seq(key, i, &seq); - tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); - tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); - /* wrapping isn't allowed, AP must rekey */ - if (seq.tkip.iv32 > cur_rx_iv32) - cur_rx_iv32 = seq.tkip.iv32; - } - - ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); - iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); - ieee80211_get_tkip_rx_p1k(key, data->bssid, - cur_rx_iv32 + 1, p1k); - iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); - - memcpy(rx_mic_key, - &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], - IWLAGN_MIC_KEY_SIZE); - - data->use_tkip = true; - data->use_rsc_tsc = true; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (sta) { - u8 *pn = seq.ccmp.pn; - - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; - aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; - - ieee80211_get_key_tx_seq(key, &seq); - aes_tx_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } else - aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; - - /* - * For non-QoS this relies on the fact that both the uCode and - * mac80211 use TID 0 for checking the IV in the frames. - */ - for (i = 0; i < IWLAGN_NUM_RSC; i++) { - u8 *pn = seq.ccmp.pn; - - ieee80211_get_key_rx_seq(key, i, &seq); - aes_sc->pn = cpu_to_le64( - (u64)pn[5] | - ((u64)pn[4] << 8) | - ((u64)pn[3] << 16) | - ((u64)pn[2] << 24) | - ((u64)pn[1] << 32) | - ((u64)pn[0] << 40)); - } - data->use_rsc_tsc = true; - break; - } - - mutex_unlock(&priv->shrd->mutex); -} - -static int iwlagn_mac_suspend(struct ieee80211_hw *hw, - struct cfg80211_wowlan *wowlan) -{ - struct iwl_priv *priv = hw->priv; - struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; - struct iwl_rxon_cmd rxon; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; - struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; - struct wowlan_key_data key_data = { - .ctx = ctx, - .bssid = ctx->active.bssid_addr, - .use_rsc_tsc = false, - .tkip = &tkip_cmd, - .use_tkip = false, - }; - int ret, i; - u16 seq; - - if (WARN_ON(!wowlan)) - return -EINVAL; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - /* Don't attempt WoWLAN when not associated, tear down instead. */ - if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || - !iwl_is_associated_ctx(ctx)) { - ret = 1; - goto out; - } - - key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); - if (!key_data.rsc_tsc) { - ret = -ENOMEM; - goto out; - } - - memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); - - /* - * We know the last used seqno, and the uCode expects to know that - * one, it will increment before TX. - */ - seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; - wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); - - /* - * For QoS counters, we store the one to use next, so subtract 0x10 - * since the uCode will add 0x10 before using the value. - */ - for (i = 0; i < 8; i++) { - seq = priv->shrd->tid_data[IWL_AP_ID][i].seq_number; - seq -= 0x10; - wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); - } - - if (wowlan->disconnect) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | - IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); - if (wowlan->magic_pkt) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); - if (wowlan->gtk_rekey_failure) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); - if (wowlan->eap_identity_req) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); - if (wowlan->four_way_handshake) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); - if (wowlan->rfkill_release) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_RFKILL); - if (wowlan->n_patterns) - wakeup_filter_cmd.enabled |= - cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); - - iwl_scan_cancel_timeout(priv, 200); - - memcpy(&rxon, &ctx->active, sizeof(rxon)); - - iwl_trans_stop_device(trans(priv)); - - priv->shrd->wowlan = true; - - ret = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_wowlan, - IWL_UCODE_WOWLAN); - if (ret) - goto error; - - /* now configure WoWLAN ucode */ - ret = iwl_alive_start(priv); - if (ret) - goto error; - - memcpy(&ctx->staging, &rxon, sizeof(rxon)); - ret = iwlagn_commit_rxon(priv, ctx); - if (ret) - goto error; - - ret = iwl_power_update_mode(priv, true); - if (ret) - goto error; - - if (!iwlagn_mod_params.sw_crypto) { - /* mark all keys clear */ - priv->ucode_key_table = 0; - ctx->key_mapping_keys = 0; - - /* - * This needs to be unlocked due to lock ordering - * constraints. Since we're in the suspend path - * that isn't really a problem though. - */ - mutex_unlock(&priv->shrd->mutex); - ieee80211_iter_keys(priv->hw, ctx->vif, - iwlagn_wowlan_program_keys, - &key_data); - mutex_lock(&priv->shrd->mutex); - if (key_data.error) { - ret = -EIO; - goto error; - } - - if (key_data.use_rsc_tsc) { - struct iwl_host_cmd rsc_tsc_cmd = { - .id = REPLY_WOWLAN_TSC_RSC_PARAMS, - .flags = CMD_SYNC, - .data[0] = key_data.rsc_tsc, - .dataflags[0] = IWL_HCMD_DFL_NOCOPY, - .len[0] = sizeof(*key_data.rsc_tsc), - }; - - ret = iwl_trans_send_cmd(trans(priv), &rsc_tsc_cmd); - if (ret) - goto error; - } - - if (key_data.use_tkip) { - ret = iwl_trans_send_cmd_pdu(trans(priv), - REPLY_WOWLAN_TKIP_PARAMS, - CMD_SYNC, sizeof(tkip_cmd), - &tkip_cmd); - if (ret) - goto error; - } - - if (priv->have_rekey_data) { - memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); - memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); - kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); - memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); - kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); - kek_kck_cmd.replay_ctr = priv->replay_ctr; - - ret = iwl_trans_send_cmd_pdu(trans(priv), - REPLY_WOWLAN_KEK_KCK_MATERIAL, - CMD_SYNC, sizeof(kek_kck_cmd), - &kek_kck_cmd); - if (ret) - goto error; - } - } - - ret = iwl_trans_send_cmd_pdu(trans(priv), REPLY_WOWLAN_WAKEUP_FILTER, - CMD_SYNC, sizeof(wakeup_filter_cmd), - &wakeup_filter_cmd); - if (ret) - goto error; - - ret = iwlagn_send_patterns(priv, wowlan); - if (ret) - goto error; - - device_set_wakeup_enable(bus(priv)->dev, true); - - /* Now let the ucode operate on its own */ - iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_SET, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - - goto out; - - error: - priv->shrd->wowlan = false; - iwlagn_prepare_restart(priv); - ieee80211_restart_hw(priv->hw); - out: - mutex_unlock(&priv->shrd->mutex); - kfree(key_data.rsc_tsc); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static int iwlagn_mac_resume(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct ieee80211_vif *vif; - unsigned long flags; - u32 base, status = 0xffffffff; - int ret = -EIO; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR, - CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); - - base = priv->device_pointers.error_event_table; - if (iwlagn_hw_valid_rtc_data_addr(base)) { - spin_lock_irqsave(&bus(priv)->reg_lock, flags); - ret = iwl_grab_nic_access_silent(bus(priv)); - if (ret == 0) { - iwl_write32(bus(priv), HBUS_TARG_MEM_RADDR, base); - status = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); - iwl_release_nic_access(bus(priv)); - } - spin_unlock_irqrestore(&bus(priv)->reg_lock, flags); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (ret == 0) { - if (!priv->wowlan_sram) - priv->wowlan_sram = - kzalloc(priv->ucode_wowlan.data.len, - GFP_KERNEL); - - if (priv->wowlan_sram) - _iwl_read_targ_mem_words( - bus(priv), 0x800000, priv->wowlan_sram, - priv->ucode_wowlan.data.len / 4); - } -#endif - } - - /* we'll clear ctx->vif during iwlagn_prepare_restart() */ - vif = ctx->vif; - - priv->shrd->wowlan = false; - - device_set_wakeup_enable(bus(priv)->dev, false); - - iwlagn_prepare_restart(priv); - - memset((void *)&ctx->active, 0, sizeof(ctx->active)); - iwl_connection_init_rx_config(priv, ctx); - iwlagn_set_rxon_chain(priv, ctx); - - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - ieee80211_resume_disconnect(vif); - - return 1; -} -#endif - -static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) -{ - struct iwl_priv *priv = hw->priv; - - IWL_DEBUG_MACDUMP(priv, "enter\n"); - - IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, - ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); - - if (iwlagn_tx_skb(priv, skb)) - dev_kfree_skb_any(skb); - - IWL_DEBUG_MACDUMP(priv, "leave\n"); -} - -static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_key_conf *keyconf, - struct ieee80211_sta *sta, - u32 iv32, u16 *phase1key) -{ - struct iwl_priv *priv = hw->priv; - - iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); -} - -static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta, - struct ieee80211_key_conf *key) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - int ret; - bool is_default_wep_key = false; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (iwlagn_mod_params.sw_crypto) { - IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); - return -EOPNOTSUPP; - } - - /* - * We could program these keys into the hardware as well, but we - * don't expect much multicast traffic in IBSS and having keys - * for more stations is probably more useful. - * - * Mark key TX-only and return 0. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && - !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { - key->hw_key_idx = WEP_INVALID_OFFSET; - return 0; - } - - /* If they key was TX-only, accept deletion */ - if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) - return 0; - - mutex_lock(&priv->shrd->mutex); - iwl_scan_cancel_timeout(priv, 100); - - BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); - - /* - * If we are getting WEP group key and we didn't receive any key mapping - * so far, we are in legacy wep mode (group key only), otherwise we are - * in 1X mode. - * In legacy wep mode, we use another host command to the uCode. - */ - if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || - key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { - if (cmd == SET_KEY) - is_default_wep_key = !ctx->key_mapping_keys; - else - is_default_wep_key = - key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; - } - - - switch (cmd) { - case SET_KEY: - if (is_default_wep_key) { - ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); - break; - } - ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); - if (ret) { - /* - * can't add key for RX, but we don't need it - * in the device for TX so still return 0 - */ - ret = 0; - key->hw_key_idx = WEP_INVALID_OFFSET; - } - - IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); - break; - case DISABLE_KEY: - if (is_default_wep_key) - ret = iwl_remove_default_wep_key(priv, ctx, key); - else - ret = iwl_remove_dynamic_key(priv, ctx, key, sta); - - IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); - break; - default: - ret = -EINVAL; - } - - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum ieee80211_ampdu_mlme_action action, - struct ieee80211_sta *sta, u16 tid, u16 *ssn, - u8 buf_size) -{ - struct iwl_priv *priv = hw->priv; - int ret = -EINVAL; - struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", - sta->addr, tid); - - if (!(priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE)) - return -EACCES; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - switch (action) { - case IEEE80211_AMPDU_RX_START: - IWL_DEBUG_HT(priv, "start Rx\n"); - ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); - break; - case IEEE80211_AMPDU_RX_STOP: - IWL_DEBUG_HT(priv, "stop Rx\n"); - ret = iwl_sta_rx_agg_stop(priv, sta, tid); - if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) - ret = 0; - break; - case IEEE80211_AMPDU_TX_START: - IWL_DEBUG_HT(priv, "start Tx\n"); - ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); - break; - case IEEE80211_AMPDU_TX_STOP: - IWL_DEBUG_HT(priv, "stop Tx\n"); - ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); - if ((ret == 0) && (priv->agg_tids_count > 0)) { - priv->agg_tids_count--; - IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", - priv->agg_tids_count); - } - if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) - ret = 0; - if (!priv->agg_tids_count && priv->cfg->ht_params && - priv->cfg->ht_params->use_rts_for_aggregation) { - /* - * switch off RTS/CTS if it was previously enabled - */ - sta_priv->lq_sta.lq.general_params.flags &= - ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; - iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), - &sta_priv->lq_sta.lq, CMD_ASYNC, false); - } - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - buf_size = min_t(int, buf_size, LINK_QUAL_AGG_FRAME_LIMIT_DEF); - - iwl_trans_tx_agg_setup(trans(priv), ctx->ctxid, iwl_sta_id(sta), - tid, buf_size); - - /* - * If the limit is 0, then it wasn't initialised yet, - * use the default. We can do that since we take the - * minimum below, and we don't want to go above our - * default due to hardware restrictions. - */ - if (sta_priv->max_agg_bufsize == 0) - sta_priv->max_agg_bufsize = - LINK_QUAL_AGG_FRAME_LIMIT_DEF; - - /* - * Even though in theory the peer could have different - * aggregation reorder buffer sizes for different sessions, - * our ucode doesn't allow for that and has a global limit - * for each station. Therefore, use the minimum of all the - * aggregation sessions and our default value. - */ - sta_priv->max_agg_bufsize = - min(sta_priv->max_agg_bufsize, buf_size); - - if (priv->cfg->ht_params && - priv->cfg->ht_params->use_rts_for_aggregation) { - /* - * switch to RTS/CTS if it is the prefer protection - * method for HT traffic - */ - - sta_priv->lq_sta.lq.general_params.flags |= - LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; - } - priv->agg_tids_count++; - IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", - priv->agg_tids_count); - - sta_priv->lq_sta.lq.agg_params.agg_frame_cnt_limit = - sta_priv->max_agg_bufsize; - - iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), - &sta_priv->lq_sta.lq, CMD_ASYNC, false); - - IWL_INFO(priv, "Tx aggregation enabled on ra = %pM tid = %d\n", - sta->addr, tid); - ret = 0; - break; - } - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - return ret; -} - -static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_sta *sta) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - bool is_ap = vif->type == NL80211_IFTYPE_STATION; - int ret = 0; - u8 sta_id; - - IWL_DEBUG_MAC80211(priv, "received request to add station %pM\n", - sta->addr); - mutex_lock(&priv->shrd->mutex); - IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", - sta->addr); - sta_priv->sta_id = IWL_INVALID_STATION; - - atomic_set(&sta_priv->pending_frames, 0); - if (vif->type == NL80211_IFTYPE_AP) - sta_priv->client = true; - - ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, - is_ap, sta, &sta_id); - if (ret) { - IWL_ERR(priv, "Unable to add station %pM (%d)\n", - sta->addr, ret); - /* Should we return success if return code is EEXIST ? */ - goto out; - } - - sta_priv->sta_id = sta_id; - - /* Initialize rate scaling */ - IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n", - sta->addr); - iwl_rs_rate_init(priv, sta, sta_id); - out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, - struct ieee80211_channel_switch *ch_switch) -{ - struct iwl_priv *priv = hw->priv; - const struct iwl_channel_info *ch_info; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = ch_switch->channel; - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - /* - * MULTI-FIXME - * When we add support for multiple interfaces, we need to - * revisit this. The channel switch command in the device - * only affects the BSS context, but what does that really - * mean? And what if we get a CSA on the second interface? - * This needs a lot of work. - */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - u16 ch; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - mutex_lock(&priv->shrd->mutex); - - if (iwl_is_rfkill(priv->shrd)) - goto out; - - if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status) || - test_bit(STATUS_SCANNING, &priv->shrd->status) || - test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status)) - goto out; - - if (!iwl_is_associated_ctx(ctx)) - goto out; - - if (!priv->cfg->lib->set_channel_switch) - goto out; - - ch = channel->hw_value; - if (le16_to_cpu(ctx->active.channel) == ch) - goto out; - - ch_info = iwl_get_channel_info(priv, channel->band, ch); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_MAC80211(priv, "invalid channel\n"); - goto out; - } - - spin_lock_irq(&priv->shrd->lock); - - priv->current_ht_config.smps = conf->smps_mode; - - /* Configure HT40 channels */ - ctx->ht.enabled = conf_is_ht(conf); - if (ctx->ht.enabled) { - if (conf_is_ht40_minus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ctx->ht.is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ctx->ht.is_40mhz = true; - } else { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - ctx->ht.is_40mhz = false; - } - } else - ctx->ht.is_40mhz = false; - - if ((le16_to_cpu(ctx->staging.channel) != ch)) - ctx->staging.flags = 0; - - iwl_set_rxon_channel(priv, channel, ctx); - iwl_set_rxon_ht(priv, ht_conf); - iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); - - spin_unlock_irq(&priv->shrd->lock); - - iwl_set_rate(priv); - /* - * at this point, staging_rxon has the - * configuration for channel switch - */ - set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status); - priv->switch_channel = cpu_to_le16(ch); - if (priv->cfg->lib->set_channel_switch(priv, ch_switch)) { - clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status); - priv->switch_channel = 0; - ieee80211_chswitch_done(ctx->vif, false); - } - -out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static void iwlagn_configure_filter(struct ieee80211_hw *hw, - unsigned int changed_flags, - unsigned int *total_flags, - u64 multicast) -{ - struct iwl_priv *priv = hw->priv; - __le32 filter_or = 0, filter_nand = 0; - struct iwl_rxon_context *ctx; - -#define CHK(test, flag) do { \ - if (*total_flags & (test)) \ - filter_or |= (flag); \ - else \ - filter_nand |= (flag); \ - } while (0) - - IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", - changed_flags, *total_flags); - - CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); - /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ - CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); - CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); - -#undef CHK - - mutex_lock(&priv->shrd->mutex); - - for_each_context(priv, ctx) { - ctx->staging.filter_flags &= ~filter_nand; - ctx->staging.filter_flags |= filter_or; - - /* - * Not committing directly because hardware can perform a scan, - * but we'll eventually commit the filter flags change anyway. - */ - } - - mutex_unlock(&priv->shrd->mutex); - - /* - * Receiving all multicast frames is always enabled by the - * default flags setup in iwl_connection_init_rx_config() - * since we currently do not support programming multicast - * filters into the device. - */ - *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | - FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; -} - -static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) -{ - struct iwl_priv *priv = hw->priv; - mutex_lock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "enter\n"); - if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) { - IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); - goto done; - } - if (iwl_is_rfkill(priv->shrd)) { - IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); - goto done; - } - - /* - * mac80211 will not push any more frames for transmit - * until the flush is completed - */ - if (drop) { - IWL_DEBUG_MAC80211(priv, "send flush command\n"); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { - IWL_ERR(priv, "flush request fail\n"); - goto done; - } - } - IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n"); - iwl_trans_wait_tx_queue_empty(trans(priv)); -done: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} void iwlagn_disable_roc(struct iwl_priv *priv) { @@ -2759,160 +1493,6 @@ static void iwlagn_disable_roc_work(struct work_struct *work) mutex_unlock(&priv->shrd->mutex); } -static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, - int duration) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; - int err = 0; - - if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN))) - return -EOPNOTSUPP; - - if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT))) - return -EOPNOTSUPP; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - if (test_bit(STATUS_SCAN_HW, &priv->shrd->status)) { - err = -EBUSY; - goto out; - } - - priv->hw_roc_channel = channel; - priv->hw_roc_chantype = channel_type; - priv->hw_roc_duration = duration; - priv->hw_roc_start_notified = false; - cancel_delayed_work(&priv->hw_roc_disable_work); - - if (!ctx->is_active) { - ctx->is_active = true; - ctx->staging.dev_type = RXON_DEV_TYPE_P2P; - memcpy(ctx->staging.node_addr, - priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, - ETH_ALEN); - memcpy(ctx->staging.bssid_addr, - priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, - ETH_ALEN); - err = iwlagn_commit_rxon(priv, ctx); - if (err) - goto out; - ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK | - RXON_FILTER_PROMISC_MSK | - RXON_FILTER_CTL2HOST_MSK; - - err = iwlagn_commit_rxon(priv, ctx); - if (err) { - iwlagn_disable_roc(priv); - goto out; - } - priv->hw_roc_setup = true; - } - - err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band); - if (err) - iwlagn_disable_roc(priv); - - out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return err; -} - -static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - - if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN))) - return -EOPNOTSUPP; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - iwl_scan_cancel_timeout(priv, priv->hw_roc_duration); - iwlagn_disable_roc(priv); - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return 0; -} - -static int iwlagn_mac_tx_sync(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *bssid, - enum ieee80211_tx_sync_type type) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - int ret; - u8 sta_id; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - if (iwl_is_associated_ctx(ctx)) { - ret = 0; - goto out; - } - - if (ctx->preauth_bssid || test_bit(STATUS_SCAN_HW, &priv->shrd->status)) { - ret = -EBUSY; - goto out; - } - - ret = iwl_add_station_common(priv, ctx, bssid, true, NULL, &sta_id); - if (ret) - goto out; - - if (WARN_ON(sta_id != ctx->ap_sta_id)) { - ret = -EIO; - goto out_remove_sta; - } - - memcpy(ctx->bssid, bssid, ETH_ALEN); - ctx->preauth_bssid = true; - - ret = iwlagn_commit_rxon(priv, ctx); - - if (ret == 0) - goto out; - - out_remove_sta: - iwl_remove_station(priv, sta_id, bssid); - out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return ret; -} - -static void iwlagn_mac_finish_tx_sync(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - const u8 *bssid, - enum ieee80211_tx_sync_type type) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *ctx = vif_priv->ctx; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - if (iwl_is_associated_ctx(ctx)) - goto out; - - iwl_remove_station(priv, ctx->ap_sta_id, bssid); - ctx->preauth_bssid = false; - /* no need to commit */ - out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - /***************************************************************************** * * driver setup and teardown @@ -2936,8 +1516,8 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) iwl_setup_scan_deferred_work(priv); - if (priv->cfg->lib->bt_setup_deferred_work) - priv->cfg->lib->bt_setup_deferred_work(priv); + if (cfg(priv)->lib->bt_setup_deferred_work) + cfg(priv)->lib->bt_setup_deferred_work(priv); init_timer(&priv->statistics_periodic); priv->statistics_periodic.data = (unsigned long)priv; @@ -2954,8 +1534,8 @@ static void iwl_setup_deferred_work(struct iwl_priv *priv) static void iwl_cancel_deferred_work(struct iwl_priv *priv) { - if (priv->cfg->lib->cancel_deferred_work) - priv->cfg->lib->cancel_deferred_work(priv); + if (cfg(priv)->lib->cancel_deferred_work) + cfg(priv)->lib->cancel_deferred_work(priv); cancel_work_sync(&priv->run_time_calib_work); cancel_work_sync(&priv->beacon_update); @@ -2999,6 +1579,8 @@ static int iwl_init_drv(struct iwl_priv *priv) mutex_init(&priv->shrd->mutex); + INIT_LIST_HEAD(&trans(priv)->calib_results); + priv->ieee_channels = NULL; priv->ieee_rates = NULL; priv->band = IEEE80211_BAND_2GHZ; @@ -3022,8 +1604,8 @@ static int iwl_init_drv(struct iwl_priv *priv) iwl_init_scan_params(priv); /* init bt coex */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) { priv->kill_ack_mask = IWLAGN_BT_KILL_ACK_MASK_DEFAULT; priv->kill_cts_mask = IWLAGN_BT_KILL_CTS_MASK_DEFAULT; priv->bt_valid = IWLAGN_BT_ALL_VALID_MSK; @@ -3055,88 +1637,19 @@ err: static void iwl_uninit_drv(struct iwl_priv *priv) { - iwl_calib_free_results(priv); iwl_free_geos(priv); iwl_free_channel_map(priv); if (priv->tx_cmd_pool) kmem_cache_destroy(priv->tx_cmd_pool); kfree(priv->scan_cmd); kfree(priv->beacon_cmd); + kfree(rcu_dereference_raw(priv->noa_data)); #ifdef CONFIG_IWLWIFI_DEBUGFS kfree(priv->wowlan_sram); #endif } -static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, - enum ieee80211_rssi_event rssi_event) -{ - struct iwl_priv *priv = hw->priv; - IWL_DEBUG_MAC80211(priv, "enter\n"); - mutex_lock(&priv->shrd->mutex); - - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { - if (rssi_event == RSSI_EVENT_LOW) - priv->bt_enable_pspoll = true; - else if (rssi_event == RSSI_EVENT_HIGH) - priv->bt_enable_pspoll = false; - - iwlagn_send_advance_bt_config(priv); - } else { - IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," - "ignoring RSSI callback\n"); - } - - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); -} - -static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, - struct ieee80211_sta *sta, bool set) -{ - struct iwl_priv *priv = hw->priv; - - queue_work(priv->shrd->workqueue, &priv->beacon_update); - - return 0; -} - -struct ieee80211_ops iwlagn_hw_ops = { - .tx = iwlagn_mac_tx, - .start = iwlagn_mac_start, - .stop = iwlagn_mac_stop, -#ifdef CONFIG_PM_SLEEP - .suspend = iwlagn_mac_suspend, - .resume = iwlagn_mac_resume, -#endif - .add_interface = iwlagn_mac_add_interface, - .remove_interface = iwlagn_mac_remove_interface, - .change_interface = iwlagn_mac_change_interface, - .config = iwlagn_mac_config, - .configure_filter = iwlagn_configure_filter, - .set_key = iwlagn_mac_set_key, - .update_tkip_key = iwlagn_mac_update_tkip_key, - .set_rekey_data = iwlagn_mac_set_rekey_data, - .conf_tx = iwlagn_mac_conf_tx, - .bss_info_changed = iwlagn_bss_info_changed, - .ampdu_action = iwlagn_mac_ampdu_action, - .hw_scan = iwlagn_mac_hw_scan, - .sta_notify = iwlagn_mac_sta_notify, - .sta_add = iwlagn_mac_sta_add, - .sta_remove = iwlagn_mac_sta_remove, - .channel_switch = iwlagn_mac_channel_switch, - .flush = iwlagn_mac_flush, - .tx_last_beacon = iwlagn_mac_tx_last_beacon, - .remain_on_channel = iwlagn_mac_remain_on_channel, - .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, - .rssi_callback = iwlagn_mac_rssi_callback, - CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd) - CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump) - .tx_sync = iwlagn_mac_tx_sync, - .finish_tx_sync = iwlagn_mac_finish_tx_sync, - .set_tim = iwlagn_mac_set_tim, -}; static u32 iwl_hw_detect(struct iwl_priv *priv) { @@ -3156,40 +1669,55 @@ static int iwl_set_hw_params(struct iwl_priv *priv) hw_params(priv).rx_page_order = get_order(IWL_RX_BUF_SIZE_4K); - if (iwlagn_mod_params.disable_11n) - priv->cfg->sku &= ~EEPROM_SKU_CAP_11N_ENABLE; + if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_ALL) + cfg(priv)->sku &= ~EEPROM_SKU_CAP_11N_ENABLE; hw_params(priv).num_ampdu_queues = - priv->cfg->base_params->num_of_ampdu_queues; + cfg(priv)->base_params->num_of_ampdu_queues; hw_params(priv).shadow_reg_enable = - priv->cfg->base_params->shadow_reg_enable; - hw_params(priv).sku = priv->cfg->sku; - hw_params(priv).wd_timeout = priv->cfg->base_params->wd_timeout; + cfg(priv)->base_params->shadow_reg_enable; + hw_params(priv).sku = cfg(priv)->sku; + hw_params(priv).wd_timeout = cfg(priv)->base_params->wd_timeout; /* Device-specific setup */ - return priv->cfg->lib->set_hw_params(priv); + return cfg(priv)->lib->set_hw_params(priv); } -/* This function both allocates and initializes hw and priv. */ -static struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg) -{ - struct iwl_priv *priv; - /* mac80211 allocates memory for this device instance, including - * space for this driver's private structure */ - struct ieee80211_hw *hw; - hw = ieee80211_alloc_hw(sizeof(struct iwl_priv), &iwlagn_hw_ops); - if (hw == NULL) { - pr_err("%s: Can not allocate network device\n", - cfg->name); - goto out; - } - priv = hw->priv; - priv->hw = hw; +static void iwl_debug_config(struct iwl_priv *priv) +{ + dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEBUG " +#ifdef CONFIG_IWLWIFI_DEBUG + "enabled\n"); +#else + "disabled\n"); +#endif + dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEBUGFS " +#ifdef CONFIG_IWLWIFI_DEBUGFS + "enabled\n"); +#else + "disabled\n"); +#endif + dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEVICE_TRACING " +#ifdef CONFIG_IWLWIFI_DEVICE_TRACING + "enabled\n"); +#else + "disabled\n"); +#endif -out: - return hw; + dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_DEVICE_TESTMODE " +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE + "enabled\n"); +#else + "disabled\n"); +#endif + dev_printk(KERN_INFO, bus(priv)->dev, "CONFIG_IWLWIFI_P2P " +#ifdef CONFIG_IWLWIFI_P2P + "enabled\n"); +#else + "disabled\n"); +#endif } int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, @@ -3204,8 +1732,9 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, /************************ * 1. Allocating HW data ************************/ - hw = iwl_alloc_all(cfg); + hw = iwl_alloc_all(); if (!hw) { + pr_err("%s: Cannot allocate network device\n", cfg->name); err = -ENOMEM; goto out; } @@ -3226,8 +1755,11 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, SET_IEEE80211_DEV(hw, bus(priv)->dev); + /* what debugging capabilities we have */ + iwl_debug_config(priv); + IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); - priv->cfg = cfg; + cfg(priv) = cfg; /* is antenna coupling more than 35dB ? */ priv->bt_ant_couple_ok = @@ -3261,7 +1793,7 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, ***********************/ hw_rev = iwl_hw_detect(priv); IWL_INFO(priv, "Detected %s, REV=0x%X\n", - priv->cfg->name, hw_rev); + cfg(priv)->name, hw_rev); err = iwl_trans_request_irq(trans(priv)); if (err) @@ -3291,11 +1823,11 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, goto out_free_eeprom; /* extract MAC Address */ - iwl_eeprom_get_mac(priv, priv->addresses[0].addr); + iwl_eeprom_get_mac(priv->shrd, priv->addresses[0].addr); IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); priv->hw->wiphy->addresses = priv->addresses; priv->hw->wiphy->n_addresses = 1; - num_mac = iwl_eeprom_query16(priv, EEPROM_NUM_MAC_ADDRESS); + num_mac = iwl_eeprom_query16(priv->shrd, EEPROM_NUM_MAC_ADDRESS); if (num_mac > 1) { memcpy(priv->addresses[1].addr, priv->addresses[0].addr, ETH_ALEN); @@ -3360,7 +1892,7 @@ out_destroy_workqueue: priv->shrd->workqueue = NULL; iwl_uninit_drv(priv); out_free_eeprom: - iwl_eeprom_free(priv); + iwl_eeprom_free(priv->shrd); out_free_trans: iwl_trans_free(trans(priv)); out_free_traffic_mem: @@ -3385,21 +1917,16 @@ void __devexit iwl_remove(struct iwl_priv * priv) set_bit(STATUS_EXIT_PENDING, &priv->shrd->status); iwl_testmode_cleanup(priv); - iwl_leds_exit(priv); - - if (priv->mac80211_registered) { - ieee80211_unregister_hw(priv->hw); - priv->mac80211_registered = 0; - } + iwlagn_mac_unregister(priv); iwl_tt_exit(priv); /*This will stop the queues, move the device to low power state */ iwl_trans_stop_device(trans(priv)); - iwl_dealloc_ucode(priv); + iwl_dealloc_ucode(trans(priv)); - iwl_eeprom_free(priv); + iwl_eeprom_free(priv->shrd); /*netif_stop_queue(dev); */ flush_workqueue(priv->shrd->workqueue); @@ -3469,8 +1996,9 @@ module_param_named(swcrypto, iwlagn_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); module_param_named(queues_num, iwlagn_mod_params.num_of_queues, int, S_IRUGO); MODULE_PARM_DESC(queues_num, "number of hw queues."); -module_param_named(11n_disable, iwlagn_mod_params.disable_11n, int, S_IRUGO); -MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); +module_param_named(11n_disable, iwlagn_mod_params.disable_11n, uint, S_IRUGO); +MODULE_PARM_DESC(11n_disable, + "disable 11n functionality, bitmap: 1: full, 2: agg TX, 4: agg RX"); module_param_named(amsdu_size_8K, iwlagn_mod_params.amsdu_size_8K, int, S_IRUGO); MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); @@ -3499,9 +2027,10 @@ MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])"); module_param_named(ack_check, iwlagn_mod_params.ack_check, bool, S_IRUGO); MODULE_PARM_DESC(ack_check, "Check ack health (default: 0 [disabled])"); -module_param_named(wd_disable, iwlagn_mod_params.wd_disable, bool, S_IRUGO); +module_param_named(wd_disable, iwlagn_mod_params.wd_disable, int, S_IRUGO); MODULE_PARM_DESC(wd_disable, - "Disable stuck queue watchdog timer (default: 0 [enabled])"); + "Disable stuck queue watchdog timer 0=system default, " + "1=disable, 2=enable (default: 0)"); /* * set bt_coex_active to true, uCode will do kill/defer diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index 5b936ec1a54..f84fb3c5356 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -65,6 +65,12 @@ #include "iwl-dev.h" +struct iwlagn_ucode_capabilities { + u32 max_probe_length; + u32 standard_phy_calibration_size; + u32 flags; +}; + extern struct ieee80211_ops iwlagn_hw_ops; int iwl_reset_ict(struct iwl_trans *trans); @@ -77,6 +83,16 @@ static inline void iwl_set_calib_hdr(struct iwl_calib_hdr *hdr, u8 cmd) hdr->data_valid = 1; } +void __iwl_down(struct iwl_priv *priv); +void iwl_down(struct iwl_priv *priv); +void iwlagn_prepare_restart(struct iwl_priv *priv); + +/* MAC80211 */ +struct ieee80211_hw *iwl_alloc_all(void); +int iwlagn_mac_setup_register(struct iwl_priv *priv, + struct iwlagn_ucode_capabilities *capa); +void iwlagn_mac_unregister(struct iwl_priv *priv); + /* RXON */ int iwlagn_set_pan_params(struct iwl_priv *priv); int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx); @@ -86,25 +102,27 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changes); +void iwlagn_config_ht40(struct ieee80211_conf *conf, + struct iwl_rxon_context *ctx); /* uCode */ int iwlagn_rx_calib_result(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, struct iwl_device_cmd *cmd); -int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type); -void iwlagn_send_prio_tbl(struct iwl_priv *priv); -int iwlagn_run_init_ucode(struct iwl_priv *priv); -int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, - struct fw_img *image, - enum iwlagn_ucode_type ucode_type); /* lib */ int iwlagn_send_tx_power(struct iwl_priv *priv); void iwlagn_temperature(struct iwl_priv *priv); -u16 iwlagn_eeprom_calib_version(struct iwl_priv *priv); +u16 iwl_eeprom_calib_version(struct iwl_shared *shrd); int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); int iwlagn_send_beacon_cmd(struct iwl_priv *priv); +#ifdef CONFIG_PM_SLEEP +int iwlagn_send_patterns(struct iwl_priv *priv, + struct cfg80211_wowlan *wowlan); +int iwlagn_suspend(struct iwl_priv *priv, + struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); +#endif /* rx */ int iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); @@ -115,6 +133,8 @@ void iwl_setup_rx_handlers(struct iwl_priv *priv); int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); int iwlagn_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid, u16 *ssn); +int iwlagn_tx_agg_oper(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u8 buf_size); int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, struct ieee80211_sta *sta, u16 tid); int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, @@ -196,9 +216,6 @@ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, struct ieee80211_sta *sta, u8 *sta_id_r); int iwl_remove_station(struct iwl_priv *priv, const u8 sta_id, const u8 *addr); -int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); - u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr, bool is_ap, struct ieee80211_sta *sta); @@ -316,10 +333,6 @@ void iwl_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt); int iwl_update_bcast_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx); int iwl_update_bcast_stations(struct iwl_priv *priv); -void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum sta_notify_cmd cmd, - struct ieee80211_sta *sta); /* rate */ static inline u32 iwl_ant_idx_to_flags(u8 ant_idx) @@ -339,28 +352,11 @@ static inline __le32 iwl_hw_set_rate_n_flags(u8 rate, u32 flags) /* eeprom */ void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv); -void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac); - -/* notification wait support */ -void __acquires(wait_entry) -iwlagn_init_notification_wait(struct iwl_priv *priv, - struct iwl_notification_wait *wait_entry, - u8 cmd, - void (*fn)(struct iwl_priv *priv, - struct iwl_rx_packet *pkt, - void *data), - void *fn_data); -int __must_check __releases(wait_entry) -iwlagn_wait_notification(struct iwl_priv *priv, - struct iwl_notification_wait *wait_entry, - unsigned long timeout); -void __releases(wait_entry) -iwlagn_remove_notification(struct iwl_priv *priv, - struct iwl_notification_wait *wait_entry); -extern int iwlagn_init_alive_start(struct iwl_priv *priv); +void iwl_eeprom_get_mac(const struct iwl_shared *shrd, u8 *mac); + extern int iwl_alive_start(struct iwl_priv *priv); /* svtool */ -#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE extern int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len); extern int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/iwl-bus.h b/drivers/net/wireless/iwlwifi/iwl-bus.h index 08b97594e30..940d5038b39 100644 --- a/drivers/net/wireless/iwlwifi/iwl-bus.h +++ b/drivers/net/wireless/iwlwifi/iwl-bus.h @@ -122,7 +122,8 @@ struct iwl_bus; * struct iwl_bus_ops - bus specific operations * @get_pm_support: must returns true if the bus can go to sleep * @apm_config: will be called during the config of the APM - * @get_hw_id: prints the hw_id in the provided buffer + * @get_hw_id_string: prints the hw_id in the provided buffer + * @get_hw_id: get hw_id in u32 * @write8: write a byte to register at offset ofs * @write32: write a dword to register at offset ofs * @wread32: read a dword at register at offset ofs @@ -130,7 +131,8 @@ struct iwl_bus; struct iwl_bus_ops { bool (*get_pm_support)(struct iwl_bus *bus); void (*apm_config)(struct iwl_bus *bus); - void (*get_hw_id)(struct iwl_bus *bus, char buf[], int buf_len); + void (*get_hw_id_string)(struct iwl_bus *bus, char buf[], int buf_len); + u32 (*get_hw_id)(struct iwl_bus *bus); void (*write8)(struct iwl_bus *bus, u32 ofs, u8 val); void (*write32)(struct iwl_bus *bus, u32 ofs, u32 val); u32 (*read32)(struct iwl_bus *bus, u32 ofs); @@ -172,9 +174,15 @@ static inline void bus_apm_config(struct iwl_bus *bus) bus->ops->apm_config(bus); } -static inline void bus_get_hw_id(struct iwl_bus *bus, char buf[], int buf_len) +static inline void bus_get_hw_id_string(struct iwl_bus *bus, char buf[], + int buf_len) { - bus->ops->get_hw_id(bus, buf, buf_len); + bus->ops->get_hw_id_string(bus, buf, buf_len); +} + +static inline u32 bus_get_hw_id(struct iwl_bus *bus) +{ + return bus->ops->get_hw_id(bus); } static inline void bus_write8(struct iwl_bus *bus, u32 ofs, u8 val) diff --git a/drivers/net/wireless/iwlwifi/iwl-cfg.h b/drivers/net/wireless/iwlwifi/iwl-cfg.h index 2a2dc4597ba..e1d78257e4a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-cfg.h +++ b/drivers/net/wireless/iwlwifi/iwl-cfg.h @@ -101,17 +101,11 @@ extern struct iwl_cfg iwl100_bg_cfg; extern struct iwl_cfg iwl130_bgn_cfg; extern struct iwl_cfg iwl130_bg_cfg; extern struct iwl_cfg iwl2000_2bgn_cfg; -extern struct iwl_cfg iwl2000_2bg_cfg; extern struct iwl_cfg iwl2000_2bgn_d_cfg; extern struct iwl_cfg iwl2030_2bgn_cfg; -extern struct iwl_cfg iwl2030_2bg_cfg; extern struct iwl_cfg iwl6035_2agn_cfg; -extern struct iwl_cfg iwl6035_2abg_cfg; -extern struct iwl_cfg iwl6035_2bg_cfg; -extern struct iwl_cfg iwl105_bg_cfg; extern struct iwl_cfg iwl105_bgn_cfg; extern struct iwl_cfg iwl105_bgn_d_cfg; -extern struct iwl_cfg iwl135_bg_cfg; extern struct iwl_cfg iwl135_bgn_cfg; #endif /* __iwl_pci_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 69d5f85d11e..265de39d394 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -109,10 +109,10 @@ enum { /* RX, TX, LEDs */ REPLY_TX = 0x1c, REPLY_LEDS_CMD = 0x48, - REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* for 4965 and up */ + REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* WiMAX coexistence */ - COEX_PRIORITY_TABLE_CMD = 0x5a, /* for 5000 series and up */ + COEX_PRIORITY_TABLE_CMD = 0x5a, COEX_MEDIUM_NOTIFICATION = 0x5b, COEX_EVENT_CMD = 0x5c, @@ -198,6 +198,7 @@ enum { REPLY_WOWLAN_TKIP_PARAMS = 0xe3, REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4, REPLY_WOWLAN_GET_STATUS = 0xe5, + REPLY_D3_CONFIG = 0xd3, REPLY_MAX = 0xff }; @@ -465,23 +466,27 @@ struct iwl_error_event_table { u32 frame_ptr; /* frame pointer */ u32 stack_ptr; /* stack pointer */ u32 hcmd; /* last host command header */ -#if 0 - /* no need to read the remainder, we don't use the values */ - u32 isr0; /* isr status register LMPM_NIC_ISR0: rxtx_flag */ - u32 isr1; /* isr status register LMPM_NIC_ISR1: host_flag */ - u32 isr2; /* isr status register LMPM_NIC_ISR2: enc_flag */ - u32 isr3; /* isr status register LMPM_NIC_ISR3: time_flag */ - u32 isr4; /* isr status register LMPM_NIC_ISR4: wico interrupt */ + u32 isr0; /* isr status register LMPM_NIC_ISR0: + * rxtx_flag */ + u32 isr1; /* isr status register LMPM_NIC_ISR1: + * host_flag */ + u32 isr2; /* isr status register LMPM_NIC_ISR2: + * enc_flag */ + u32 isr3; /* isr status register LMPM_NIC_ISR3: + * time_flag */ + u32 isr4; /* isr status register LMPM_NIC_ISR4: + * wico interrupt */ u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ u32 wait_event; /* wait event() caller address */ u32 l2p_control; /* L2pControlField */ u32 l2p_duration; /* L2pDurationField */ u32 l2p_mhvalid; /* L2pMhValidBits */ u32 l2p_addr_match; /* L2pAddrMatchStat */ - u32 lmpm_pmg_sel; /* indicate which clocks are turned on (LMPM_PMG_SEL) */ - u32 u_timestamp; /* indicate when the date and time of the compilation */ + u32 lmpm_pmg_sel; /* indicate which clocks are turned on + * (LMPM_PMG_SEL) */ + u32 u_timestamp; /* indicate when the date and time of the + * compilation */ u32 flow_handler; /* FH read/write pointers, RX credit */ -#endif } __packed; struct iwl_alive_resp { @@ -809,7 +814,7 @@ struct iwl_qosparam_cmd { #define IWLAGN_STATION_COUNT 16 #define IWL_INVALID_STATION 255 -#define IWL_MAX_TID_COUNT 9 +#define IWL_MAX_TID_COUNT 8 #define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) #define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) @@ -930,8 +935,7 @@ struct iwl_addsta_cmd { * corresponding to bit (e.g. bit 5 controls TID 5). * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ __le16 tid_disable_tx; - - __le16 rate_n_flags; /* 3945 only */ + __le16 legacy_reserved; /* TID for which to add block-ack support. * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ @@ -1161,8 +1165,7 @@ struct iwl_rx_mpdu_res_start { * * uCode handles retrying Tx when an ACK is expected but not received. * This includes trying lower data rates than the one requested in the Tx - * command, as set up by the REPLY_RATE_SCALE (for 3945) or - * REPLY_TX_LINK_QUALITY_CMD (agn). + * command, as set up by the REPLY_TX_LINK_QUALITY_CMD (agn). * * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. * This command must be executed after every RXON command, before Tx can occur. @@ -1174,25 +1177,9 @@ struct iwl_rx_mpdu_res_start { * 1: Use RTS/CTS protocol or CTS-to-self if spec allows it * before this frame. if CTS-to-self required check * RXON_FLG_SELF_CTS_EN status. - * unused in 3945/4965, used in 5000 series and after */ #define TX_CMD_FLG_PROT_REQUIRE_MSK cpu_to_le32(1 << 0) -/* - * 1: Use Request-To-Send protocol before this frame. - * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. - * used in 3945/4965, unused in 5000 series and after - */ -#define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1) - -/* - * 1: Transmit Clear-To-Send to self before this frame. - * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. - * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. - * used in 3945/4965, unused in 5000 series and after - */ -#define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2) - /* 1: Expect ACK from receiving station * 0: Don't expect ACK (MAC header's duration field s/b 0) * Set this for unicast frames, but not broadcast/multicast. */ @@ -1210,18 +1197,8 @@ struct iwl_rx_mpdu_res_start { * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ #define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) -/* - * 1: Frame requires full Tx-Op protection. - * Set this if either RTS or CTS Tx Flag gets set. - * used in 3945/4965, unused in 5000 series and after - */ -#define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7) - -/* Tx antenna selection field; used only for 3945, reserved (0) for agn devices. - * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ +/* Tx antenna selection field; reserved (0) for agn devices. */ #define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) -#define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8) -#define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9) /* 1: Ignore Bluetooth priority for this frame. * 0: Delay Tx until Bluetooth device is done (normal usage). */ @@ -1567,7 +1544,6 @@ struct iwl_compressed_ba_resp { __le64 bitmap; __le16 scd_flow; __le16 scd_ssn; - /* following only for 5000 series and up */ u8 txed; /* number of frames sent */ u8 txed_2_done; /* number of frames acked */ } __packed; @@ -1669,7 +1645,7 @@ struct iwl_link_qual_agg_params { /* * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) * - * For agn devices only; 3945 uses REPLY_RATE_SCALE. + * For agn devices * * Each station in the agn device's internal station table has its own table * of 16 @@ -1918,7 +1894,7 @@ struct iwl_link_quality_cmd { /* * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) * - * 3945 and agn devices support hardware handshake with Bluetooth device on + * agn devices support hardware handshake with Bluetooth device on * same platform. Bluetooth device alerts wireless device when it will Tx; * wireless device can delay or kill its own Tx to accommodate. */ @@ -2202,8 +2178,8 @@ struct iwl_spectrum_notification { struct iwl_powertable_cmd { __le16 flags; - u8 keep_alive_seconds; /* 3945 reserved */ - u8 debug_flags; /* 3945 reserved */ + u8 keep_alive_seconds; + u8 debug_flags; __le32 rx_data_timeout; __le32 tx_data_timeout; __le32 sleep_interval[IWL_POWER_VEC_SIZE]; @@ -2324,9 +2300,9 @@ struct iwl_scan_channel { /** * struct iwl_ssid_ie - directed scan network information element * - * Up to 20 of these may appear in REPLY_SCAN_CMD (Note: Only 4 are in - * 3945 SCAN api), selected by "type" bit field in struct iwl_scan_channel; - * each channel may select different ssids from among the 20 (4) entries. + * Up to 20 of these may appear in REPLY_SCAN_CMD, + * selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 entries. * SSID IEs get transmitted in reverse order of entry. */ struct iwl_ssid_ie { @@ -2335,7 +2311,6 @@ struct iwl_ssid_ie { u8 ssid[32]; } __packed; -#define PROBE_OPTION_MAX_3945 4 #define PROBE_OPTION_MAX 20 #define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) #define IWL_GOOD_CRC_TH_DISABLED 0 @@ -2416,8 +2391,6 @@ struct iwl_scan_cmd { * channel */ __le32 suspend_time; /* pause scan this long (in "extended beacon * format") when returning to service chnl: - * 3945; 31:24 # beacons, 19:0 additional usec, - * 4965; 31:22 # beacons, 21:0 additional usec. */ __le32 flags; /* RXON_FLG_* */ __le32 filter_flags; /* RXON_FILTER_* */ @@ -2733,7 +2706,7 @@ struct statistics_div { struct statistics_general_common { __le32 temperature; /* radio temperature */ - __le32 temperature_m; /* for 5000 and up, this is radio voltage */ + __le32 temperature_m; /* radio voltage */ struct statistics_dbg dbg; __le32 sleep_time; __le32 slots_out; @@ -3801,6 +3774,19 @@ struct iwl_bt_coex_prot_env_cmd { } __attribute__((packed)); /* + * REPLY_D3_CONFIG + */ +enum iwlagn_d3_wakeup_filters { + IWLAGN_D3_WAKEUP_RFKILL = BIT(0), + IWLAGN_D3_WAKEUP_SYSASSERT = BIT(1), +}; + +struct iwlagn_d3_config_cmd { + __le32 min_sleep_time; + __le32 wakeup_flags; +} __packed; + +/* * REPLY_WOWLAN_PATTERNS */ #define IWLAGN_WOWLAN_MIN_PATTERN_LEN 16 @@ -3830,19 +3816,16 @@ enum iwlagn_wowlan_wakeup_filters { IWLAGN_WOWLAN_WAKEUP_BEACON_MISS = BIT(2), IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3), IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4), - IWLAGN_WOWLAN_WAKEUP_RFKILL = BIT(5), - IWLAGN_WOWLAN_WAKEUP_UCODE_ERROR = BIT(6), - IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(7), - IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(8), - IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(9), - IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(10), + IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(5), + IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(6), + IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(7), + IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(8), }; struct iwlagn_wowlan_wakeup_filter_cmd { __le32 enabled; __le16 non_qos_seq; - u8 min_sleep_seconds; - u8 reserved; + __le16 reserved; __le16 qos_seq[8]; }; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 001fdf140ab..7bcfa781e0b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -60,8 +60,8 @@ static void iwl_init_ht_hw_capab(const struct iwl_priv *priv, ht_info->ht_supported = true; - if (priv->cfg->ht_params && - priv->cfg->ht_params->ht_greenfield_support) + if (cfg(priv)->ht_params && + cfg(priv)->ht_params->ht_greenfield_support) ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; max_bit_rate = MAX_BIT_RATE_20_MHZ; @@ -76,11 +76,7 @@ static void iwl_init_ht_hw_capab(const struct iwl_priv *priv, ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; - if (priv->cfg->bt_params && priv->cfg->bt_params->ampdu_factor) - ht_info->ampdu_factor = priv->cfg->bt_params->ampdu_factor; ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; - if (priv->cfg->bt_params && priv->cfg->bt_params->ampdu_density) - ht_info->ampdu_density = priv->cfg->bt_params->ampdu_density; ht_info->mcs.rx_mask[0] = 0xFF; if (rx_chains_num >= 2) @@ -141,7 +137,7 @@ int iwl_init_geos(struct iwl_priv *priv) sband->bitrates = &rates[IWL_FIRST_OFDM_RATE]; sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE; - if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE) + if (cfg(priv)->sku & EEPROM_SKU_CAP_11N_ENABLE) iwl_init_ht_hw_capab(priv, &sband->ht_cap, IEEE80211_BAND_5GHZ); @@ -151,7 +147,7 @@ int iwl_init_geos(struct iwl_priv *priv) sband->bitrates = rates; sband->n_bitrates = IWL_RATE_COUNT_LEGACY; - if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE) + if (cfg(priv)->sku & EEPROM_SKU_CAP_11N_ENABLE) iwl_init_ht_hw_capab(priv, &sband->ht_cap, IEEE80211_BAND_2GHZ); @@ -206,12 +202,12 @@ int iwl_init_geos(struct iwl_priv *priv) priv->tx_power_next = max_tx_power; if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && - priv->cfg->sku & EEPROM_SKU_CAP_BAND_52GHZ) { + cfg(priv)->sku & EEPROM_SKU_CAP_BAND_52GHZ) { char buf[32]; - bus_get_hw_id(bus(priv), buf, sizeof(buf)); + bus_get_hw_id_string(bus(priv), buf, sizeof(buf)); IWL_INFO(priv, "Incorrectly detected BG card as ABG. " "Please send your %s to maintainer.\n", buf); - priv->cfg->sku &= ~EEPROM_SKU_CAP_BAND_52GHZ; + cfg(priv)->sku &= ~EEPROM_SKU_CAP_BAND_52GHZ; } IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n", @@ -836,19 +832,6 @@ void iwl_print_rx_config_cmd(struct iwl_priv *priv, } #endif -static void iwlagn_abort_notification_waits(struct iwl_priv *priv) -{ - unsigned long flags; - struct iwl_notification_wait *wait_entry; - - spin_lock_irqsave(&priv->notif_wait_lock, flags); - list_for_each_entry(wait_entry, &priv->notif_waits, list) - wait_entry->aborted = true; - spin_unlock_irqrestore(&priv->notif_wait_lock, flags); - - wake_up_all(&priv->notif_waitq); -} - void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) { unsigned int reload_msec; @@ -860,7 +843,7 @@ void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) /* Cancel currently queued command. */ clear_bit(STATUS_HCMD_ACTIVE, &priv->shrd->status); - iwlagn_abort_notification_waits(priv); + iwl_abort_notification_waits(priv->shrd); /* Keep the restart process from trying to send host * commands by clearing the ready bit */ @@ -979,9 +962,9 @@ int iwl_apm_init(struct iwl_priv *priv) bus_apm_config(bus(priv)); /* Configure analog phase-lock-loop before activating to D0A */ - if (priv->cfg->base_params->pll_cfg_val) + if (cfg(priv)->base_params->pll_cfg_val) iwl_set_bit(bus(priv), CSR_ANA_PLL_CFG, - priv->cfg->base_params->pll_cfg_val); + cfg(priv)->base_params->pll_cfg_val); /* * Set "initialization complete" bit to move adapter from @@ -1120,229 +1103,8 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) &statistics_cmd); } -int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_rxon_context *ctx; - unsigned long flags; - int q; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (!iwl_is_ready_rf(priv->shrd)) { - IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); - return -EIO; - } - - if (queue >= AC_NUM) { - IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); - return 0; - } - - q = AC_NUM - 1 - queue; - - spin_lock_irqsave(&priv->shrd->lock, flags); - - /* - * MULTI-FIXME - * This may need to be done per interface in nl80211/cfg80211/mac80211. - */ - for_each_context(priv, ctx) { - ctx->qos_data.def_qos_parm.ac[q].cw_min = - cpu_to_le16(params->cw_min); - ctx->qos_data.def_qos_parm.ac[q].cw_max = - cpu_to_le16(params->cw_max); - ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; - ctx->qos_data.def_qos_parm.ac[q].edca_txop = - cpu_to_le16((params->txop * 32)); - - ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; - } - - spin_unlock_irqrestore(&priv->shrd->lock, flags); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - return 0; -} - -int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - - return priv->ibss_manager == IWL_IBSS_MANAGER; -} - -static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - iwl_connection_init_rx_config(priv, ctx); - - iwlagn_set_rxon_chain(priv, ctx); - - return iwlagn_commit_rxon(priv, ctx); -} - -static int iwl_setup_interface(struct iwl_priv *priv, - struct iwl_rxon_context *ctx) -{ - struct ieee80211_vif *vif = ctx->vif; - int err; - - lockdep_assert_held(&priv->shrd->mutex); - - /* - * This variable will be correct only when there's just - * a single context, but all code using it is for hardware - * that supports only one context. - */ - priv->iw_mode = vif->type; - - ctx->is_active = true; - - err = iwl_set_mode(priv, ctx); - if (err) { - if (!ctx->always_active) - ctx->is_active = false; - return err; - } - - if (priv->cfg->bt_params && priv->cfg->bt_params->advanced_bt_coexist && - vif->type == NL80211_IFTYPE_ADHOC) { - /* - * pretend to have high BT traffic as long as we - * are operating in IBSS mode, as this will cause - * the rate scaling etc. to behave as intended. - */ - priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; - } - - return 0; -} - -int iwlagn_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; - struct iwl_rxon_context *tmp, *ctx = NULL; - int err; - enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); - - IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", - viftype, vif->addr); - cancel_delayed_work_sync(&priv->hw_roc_disable_work); - mutex_lock(&priv->shrd->mutex); - - iwlagn_disable_roc(priv); - - if (!iwl_is_ready_rf(priv->shrd)) { - IWL_WARN(priv, "Try to add interface when device not ready\n"); - err = -EINVAL; - goto out; - } - - for_each_context(priv, tmp) { - u32 possible_modes = - tmp->interface_modes | tmp->exclusive_interface_modes; - - if (tmp->vif) { - /* check if this busy context is exclusive */ - if (tmp->exclusive_interface_modes & - BIT(tmp->vif->type)) { - err = -EINVAL; - goto out; - } - continue; - } - - if (!(possible_modes & BIT(viftype))) - continue; - - /* have maybe usable context w/o interface */ - ctx = tmp; - break; - } - - if (!ctx) { - err = -EOPNOTSUPP; - goto out; - } - - vif_priv->ctx = ctx; - ctx->vif = vif; - - err = iwl_setup_interface(priv, ctx); - if (!err) - goto out; - - ctx->vif = NULL; - priv->iw_mode = NL80211_IFTYPE_STATION; - out: - mutex_unlock(&priv->shrd->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - return err; -} - -static void iwl_teardown_interface(struct iwl_priv *priv, - struct ieee80211_vif *vif, - bool mode_change) -{ - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - lockdep_assert_held(&priv->shrd->mutex); - - if (priv->scan_vif == vif) { - iwl_scan_cancel_timeout(priv, 200); - iwl_force_scan_end(priv); - } - - if (!mode_change) { - iwl_set_mode(priv, ctx); - if (!ctx->always_active) - ctx->is_active = false; - } - - /* - * When removing the IBSS interface, overwrite the - * BT traffic load with the stored one from the last - * notification, if any. If this is a device that - * doesn't implement this, this has no effect since - * both values are the same and zero. - */ - if (vif->type == NL80211_IFTYPE_ADHOC) - priv->bt_traffic_load = priv->last_bt_traffic_load; -} - -void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - mutex_lock(&priv->shrd->mutex); - - if (WARN_ON(ctx->vif != vif)) { - struct iwl_rxon_context *tmp; - IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); - for_each_context(priv, tmp) - IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", - tmp->ctxid, tmp, tmp->vif); - } - ctx->vif = NULL; - - iwl_teardown_interface(priv, vif, false); - - mutex_unlock(&priv->shrd->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - -} #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -1649,97 +1411,13 @@ int iwl_force_reset(struct iwl_priv *priv, int mode, bool external) return 0; } -int iwlagn_mac_change_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - struct iwl_rxon_context *bss_ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_rxon_context *tmp; - enum nl80211_iftype newviftype = newtype; - u32 interface_modes; - int err; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - newtype = ieee80211_iftype_p2p(newtype, newp2p); - - mutex_lock(&priv->shrd->mutex); - - if (!ctx->vif || !iwl_is_ready_rf(priv->shrd)) { - /* - * Huh? But wait ... this can maybe happen when - * we're in the middle of a firmware restart! - */ - err = -EBUSY; - goto out; - } - - interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; - - if (!(interface_modes & BIT(newtype))) { - err = -EBUSY; - goto out; - } - - /* - * Refuse a change that should be done by moving from the PAN - * context to the BSS context instead, if the BSS context is - * available and can support the new interface type. - */ - if (ctx->ctxid == IWL_RXON_CTX_PAN && !bss_ctx->vif && - (bss_ctx->interface_modes & BIT(newtype) || - bss_ctx->exclusive_interface_modes & BIT(newtype))) { - BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); - err = -EBUSY; - goto out; - } - - if (ctx->exclusive_interface_modes & BIT(newtype)) { - for_each_context(priv, tmp) { - if (ctx == tmp) - continue; - - if (!tmp->vif) - continue; - - /* - * The current mode switch would be exclusive, but - * another context is active ... refuse the switch. - */ - err = -EBUSY; - goto out; - } - } - - /* success */ - iwl_teardown_interface(priv, vif, true); - vif->type = newviftype; - vif->p2p = newp2p; - err = iwl_setup_interface(priv, ctx); - WARN_ON(err); - /* - * We've switched internally, but submitting to the - * device may have failed for some reason. Mask this - * error, because otherwise mac80211 will not switch - * (and set the interface type back) and we'll be - * out of sync with it. - */ - err = 0; - - out: - mutex_unlock(&priv->shrd->mutex); - IWL_DEBUG_MAC80211(priv, "leave\n"); - - return err; -} int iwl_cmd_echo_test(struct iwl_priv *priv) { int ret; struct iwl_host_cmd cmd = { .id = REPLY_ECHO, + .len = { 0 }, .flags = CMD_SYNC, }; @@ -1783,7 +1461,7 @@ void iwl_bg_watchdog(unsigned long data) if (iwl_is_rfkill(priv->shrd)) return; - timeout = priv->cfg->base_params->wd_timeout; + timeout = cfg(priv)->base_params->wd_timeout; if (timeout == 0) return; @@ -1808,13 +1486,25 @@ void iwl_bg_watchdog(unsigned long data) void iwl_setup_watchdog(struct iwl_priv *priv) { - unsigned int timeout = priv->cfg->base_params->wd_timeout; - - if (timeout && !iwlagn_mod_params.wd_disable) - mod_timer(&priv->watchdog, - jiffies + msecs_to_jiffies(IWL_WD_TICK(timeout))); - else - del_timer(&priv->watchdog); + unsigned int timeout = cfg(priv)->base_params->wd_timeout; + + if (!iwlagn_mod_params.wd_disable) { + /* use system default */ + if (timeout && !cfg(priv)->base_params->wd_disable) + mod_timer(&priv->watchdog, + jiffies + + msecs_to_jiffies(IWL_WD_TICK(timeout))); + else + del_timer(&priv->watchdog); + } else { + /* module parameter overwrite default configuration */ + if (timeout && iwlagn_mod_params.wd_disable == 2) + mod_timer(&priv->watchdog, + jiffies + + msecs_to_jiffies(IWL_WD_TICK(timeout))); + else + del_timer(&priv->watchdog); + } } /** @@ -1890,34 +1580,6 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, return cpu_to_le32(res); } -void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, - enum iwl_rxon_context_id ctx, - u8 sta_id, u8 tid) -{ - struct ieee80211_vif *vif; - u8 *addr = priv->stations[sta_id].sta.sta.addr; - - if (ctx == NUM_IWL_RXON_CTX) - ctx = priv->stations[sta_id].ctxid; - vif = priv->contexts[ctx].vif; - - ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); -} - -void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv, - enum iwl_rxon_context_id ctx, - u8 sta_id, u8 tid) -{ - struct ieee80211_vif *vif; - u8 *addr = priv->stations[sta_id].sta.sta.addr; - - if (ctx == NUM_IWL_RXON_CTX) - ctx = priv->stations[sta_id].ctxid; - vif = priv->contexts[ctx].vif; - - ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); -} - void iwl_set_hw_rfkill_state(struct iwl_priv *priv, bool state) { wiphy_rfkill_set_hw_state(priv->hw->wiphy, state); @@ -1925,8 +1587,7 @@ void iwl_set_hw_rfkill_state(struct iwl_priv *priv, bool state) void iwl_nic_config(struct iwl_priv *priv) { - priv->cfg->lib->nic_config(priv); - + cfg(priv)->lib->nic_config(priv); } void iwl_free_skb(struct iwl_priv *priv, struct sk_buff *skb) diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index 137da338070..7bf76ab94dd 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -113,6 +113,7 @@ struct iwl_lib_ops { * @shadow_reg_enable: HW shadhow register bit * @no_idle_support: do not support idle mode * @hd_v2: v2 of enhanced sensitivity value, used for 2000 series and up + * wd_disable: disable watchdog timer */ struct iwl_base_params { int eeprom_size; @@ -134,14 +135,13 @@ struct iwl_base_params { const bool shadow_reg_enable; const bool no_idle_support; const bool hd_v2; + const bool wd_disable; }; /* * @advanced_bt_coexist: support advanced bt coexist * @bt_init_traffic_load: specify initial bt traffic load * @bt_prio_boost: default bt priority boost value * @agg_time_limit: maximum number of uSec in aggregation - * @ampdu_factor: Maximum A-MPDU length factor - * @ampdu_density: Minimum A-MPDU spacing * @bt_sco_disable: uCode should not response to BT in SCO/ESCO mode */ struct iwl_bt_params { @@ -149,8 +149,6 @@ struct iwl_bt_params { u8 bt_init_traffic_load; u8 bt_prio_boost; u16 agg_time_limit; - u8 ampdu_factor; - u8 ampdu_density; bool bt_sco_disable; bool bt_session_2; }; @@ -163,84 +161,10 @@ struct iwl_ht_params { enum ieee80211_smps_mode smps_mode; }; -/** - * struct iwl_cfg - * @name: Offical name of the device - * @fw_name_pre: Firmware filename prefix. The api version and extension - * (.ucode) will be added to filename before loading from disk. The - * filename is constructed as fw_name_pre<api>.ucode. - * @ucode_api_max: Highest version of uCode API supported by driver. - * @ucode_api_ok: oldest version of the uCode API that is OK to load - * without a warning, for use in transitions - * @ucode_api_min: Lowest version of uCode API supported by driver. - * @valid_tx_ant: valid transmit antenna - * @valid_rx_ant: valid receive antenna - * @sku: sku information from EEPROM - * @eeprom_ver: EEPROM version - * @eeprom_calib_ver: EEPROM calibration version - * @lib: pointer to the lib ops - * @additional_nic_config: additional nic configuration - * @base_params: pointer to basic parameters - * @ht_params: point to ht patameters - * @bt_params: pointer to bt parameters - * @pa_type: used by 6000 series only to identify the type of Power Amplifier - * @need_dc_calib: need to perform init dc calibration - * @need_temp_offset_calib: need to perform temperature offset calibration - * @scan_antennas: available antenna for scan operation - * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) - * @adv_pm: advance power management - * @rx_with_siso_diversity: 1x1 device with rx antenna diversity - * @internal_wimax_coex: internal wifi/wimax combo device - * @iq_invert: I/Q inversion - * @temp_offset_v2: support v2 of temperature offset calibration - * - * We enable the driver to be backward compatible wrt API version. The - * driver specifies which APIs it supports (with @ucode_api_max being the - * highest and @ucode_api_min the lowest). Firmware will only be loaded if - * it has a supported API version. - * - * The ideal usage of this infrastructure is to treat a new ucode API - * release as a new hardware revision. - */ -struct iwl_cfg { - /* params specific to an individual device within a device family */ - const char *name; - const char *fw_name_pre; - const unsigned int ucode_api_max; - const unsigned int ucode_api_ok; - const unsigned int ucode_api_min; - u8 valid_tx_ant; - u8 valid_rx_ant; - u16 sku; - u16 eeprom_ver; - u16 eeprom_calib_ver; - const struct iwl_lib_ops *lib; - void (*additional_nic_config)(struct iwl_priv *priv); - /* params not likely to change within a device family */ - struct iwl_base_params *base_params; - /* params likely to change within a device family */ - struct iwl_ht_params *ht_params; - struct iwl_bt_params *bt_params; - enum iwl_pa_type pa_type; /* if used set to IWL_PA_SYSTEM */ - const bool need_dc_calib; /* if used set to true */ - const bool need_temp_offset_calib; /* if used set to true */ - u8 scan_rx_antennas[IEEE80211_NUM_BANDS]; - enum iwl_led_mode led_mode; - const bool adv_pm; - const bool rx_with_siso_diversity; - const bool internal_wimax_coex; - const bool iq_invert; - const bool temp_offset_v2; -}; - /*************************** * L i b * ***************************/ -int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, u16 queue, - const struct ieee80211_tx_queue_params *params); -int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, int hw_decrypt); int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx); @@ -260,13 +184,6 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, void iwl_connection_init_rx_config(struct iwl_priv *priv, struct iwl_rxon_context *ctx); void iwl_set_rate(struct iwl_priv *priv); -int iwlagn_mac_add_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif); -int iwlagn_mac_change_interface(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - enum nl80211_iftype newtype, bool newp2p); int iwl_cmd_echo_test(struct iwl_priv *priv); #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_alloc_traffic_mem(struct iwl_priv *priv); @@ -323,9 +240,6 @@ void iwl_init_scan_params(struct iwl_priv *priv); int iwl_scan_cancel(struct iwl_priv *priv); void iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); void iwl_force_scan_end(struct iwl_priv *priv); -int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_scan_request *req); void iwl_internal_short_hw_scan(struct iwl_priv *priv); int iwl_force_reset(struct iwl_priv *priv, int mode, bool external); u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, @@ -379,8 +293,8 @@ static inline const struct ieee80211_supported_band *iwl_get_hw_mode( static inline bool iwl_advanced_bt_coexist(struct iwl_priv *priv) { - return priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist; + return cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist; } static inline void iwl_enable_rfkill_int(struct iwl_priv *priv) diff --git a/drivers/net/wireless/iwlwifi/iwl-csr.h b/drivers/net/wireless/iwlwifi/iwl-csr.h index b9f3267e720..fbc3095c7b4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/iwlwifi/iwl-csr.h @@ -284,8 +284,8 @@ #define CSR_HW_REV_TYPE_6x35 CSR_HW_REV_TYPE_6x05 #define CSR_HW_REV_TYPE_2x30 (0x00000C0) #define CSR_HW_REV_TYPE_2x00 (0x0000100) -#define CSR_HW_REV_TYPE_200 (0x0000110) -#define CSR_HW_REV_TYPE_230 (0x0000120) +#define CSR_HW_REV_TYPE_105 (0x0000110) +#define CSR_HW_REV_TYPE_135 (0x0000120) #define CSR_HW_REV_TYPE_NONE (0x00001F0) /* EEPROM REG */ diff --git a/drivers/net/wireless/iwlwifi/iwl-debug.h b/drivers/net/wireless/iwlwifi/iwl-debug.h index 69a77e24d22..f8fc2393dd4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/iwlwifi/iwl-debug.h @@ -47,20 +47,21 @@ do { \ } while (0) #ifdef CONFIG_IWLWIFI_DEBUG -#define IWL_DEBUG(m, level, fmt, args...) \ +#define IWL_DEBUG(m, level, fmt, ...) \ do { \ if (iwl_get_debug_level((m)->shrd) & (level)) \ - dev_printk(KERN_ERR, bus(m)->dev, \ - "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ - __func__ , ## args); \ + dev_err(bus(m)->dev, "%c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__, \ + ##__VA_ARGS__); \ } while (0) -#define IWL_DEBUG_LIMIT(m, level, fmt, args...) \ +#define IWL_DEBUG_LIMIT(m, level, fmt, ...) \ do { \ - if (iwl_get_debug_level((m)->shrd) & (level) && net_ratelimit())\ - dev_printk(KERN_ERR, bus(m)->dev, \ - "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ - __func__ , ## args); \ + if (iwl_get_debug_level((m)->shrd) & (level) && \ + net_ratelimit()) \ + dev_err(bus(m)->dev, "%c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__, \ + ##__VA_ARGS__); \ } while (0) #define iwl_print_hex_dump(m, level, p, len) \ @@ -70,10 +71,29 @@ do { \ DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ } while (0) +#define IWL_DEBUG_QUIET_RFKILL(p, fmt, ...) \ +do { \ + if (!iwl_is_rfkill(p->shrd)) \ + dev_err(bus(p)->dev, "%s%c %s " fmt, \ + "", \ + in_interrupt() ? 'I' : 'U', __func__, \ + ##__VA_ARGS__); \ + else if (iwl_get_debug_level(p->shrd) & IWL_DL_RADIO) \ + dev_err(bus(p)->dev, "%s%c %s " fmt, \ + "(RFKILL) ", \ + in_interrupt() ? 'I' : 'U', __func__, \ + ##__VA_ARGS__); \ +} while (0) + #else #define IWL_DEBUG(m, level, fmt, args...) #define IWL_DEBUG_LIMIT(m, level, fmt, args...) #define iwl_print_hex_dump(m, level, p, len) +#define IWL_DEBUG_QUIET_RFKILL(p, fmt, args...) \ +do { \ + if (!iwl_is_rfkill(p->shrd)) \ + IWL_ERR(p, fmt, ##args); \ +} while (0) #endif /* CONFIG_IWLWIFI_DEBUG */ #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -114,48 +134,43 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) */ /* 0x0000000F - 0x00000001 */ -#define IWL_DL_INFO (1 << 0) -#define IWL_DL_MAC80211 (1 << 1) -#define IWL_DL_HCMD (1 << 2) -#define IWL_DL_STATE (1 << 3) +#define IWL_DL_INFO 0x00000001 +#define IWL_DL_MAC80211 0x00000002 +#define IWL_DL_HCMD 0x00000004 +#define IWL_DL_STATE 0x00000008 /* 0x000000F0 - 0x00000010 */ -#define IWL_DL_MACDUMP (1 << 4) -#define IWL_DL_HCMD_DUMP (1 << 5) -#define IWL_DL_EEPROM (1 << 6) -#define IWL_DL_RADIO (1 << 7) +#define IWL_DL_EEPROM 0x00000040 +#define IWL_DL_RADIO 0x00000080 /* 0x00000F00 - 0x00000100 */ -#define IWL_DL_POWER (1 << 8) -#define IWL_DL_TEMP (1 << 9) -/* reserved (1 << 10) */ -#define IWL_DL_SCAN (1 << 11) +#define IWL_DL_POWER 0x00000100 +#define IWL_DL_TEMP 0x00000200 +#define IWL_DL_SCAN 0x00000800 /* 0x0000F000 - 0x00001000 */ -#define IWL_DL_ASSOC (1 << 12) -#define IWL_DL_DROP (1 << 13) -/* reserved (1 << 14) */ -#define IWL_DL_COEX (1 << 15) +#define IWL_DL_ASSOC 0x00001000 +#define IWL_DL_DROP 0x00002000 +#define IWL_DL_COEX 0x00008000 /* 0x000F0000 - 0x00010000 */ -#define IWL_DL_FW (1 << 16) -#define IWL_DL_RF_KILL (1 << 17) -#define IWL_DL_FW_ERRORS (1 << 18) -#define IWL_DL_LED (1 << 19) +#define IWL_DL_FW 0x00010000 +#define IWL_DL_RF_KILL 0x00020000 +#define IWL_DL_FW_ERRORS 0x00040000 +#define IWL_DL_LED 0x00080000 /* 0x00F00000 - 0x00100000 */ -#define IWL_DL_RATE (1 << 20) -#define IWL_DL_CALIB (1 << 21) -#define IWL_DL_WEP (1 << 22) -#define IWL_DL_TX (1 << 23) +#define IWL_DL_RATE 0x00100000 +#define IWL_DL_CALIB 0x00200000 +#define IWL_DL_WEP 0x00400000 +#define IWL_DL_TX 0x00800000 /* 0x0F000000 - 0x01000000 */ -#define IWL_DL_RX (1 << 24) -#define IWL_DL_ISR (1 << 25) -#define IWL_DL_HT (1 << 26) +#define IWL_DL_RX 0x01000000 +#define IWL_DL_ISR 0x02000000 +#define IWL_DL_HT 0x04000000 /* 0xF0000000 - 0x10000000 */ -#define IWL_DL_11H (1 << 28) -#define IWL_DL_STATS (1 << 29) -#define IWL_DL_TX_REPLY (1 << 30) -#define IWL_DL_QOS (1 << 31) +#define IWL_DL_11H 0x10000000 +#define IWL_DL_STATS 0x20000000 +#define IWL_DL_TX_REPLY 0x40000000 +#define IWL_DL_TX_QUEUES 0x80000000 #define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) -#define IWL_DEBUG_MACDUMP(p, f, a...) IWL_DEBUG(p, IWL_DL_MACDUMP, f, ## a) #define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) #define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) #define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) @@ -164,7 +179,6 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) #define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) #define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) #define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) -#define IWL_DEBUG_HC_DUMP(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD_DUMP, f, ## a) #define IWL_DEBUG_EEPROM(p, f, a...) IWL_DEBUG(p, IWL_DL_EEPROM, f, ## a) #define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) #define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a) @@ -186,9 +200,7 @@ static inline void iwl_dbgfs_unregister(struct iwl_priv *priv) #define IWL_DEBUG_STATS_LIMIT(p, f, a...) \ IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a) #define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a) -#define IWL_DEBUG_TX_REPLY_LIMIT(p, f, a...) \ - IWL_DEBUG_LIMIT(p, IWL_DL_TX_REPLY, f, ## a) -#define IWL_DEBUG_QOS(p, f, a...) IWL_DEBUG(p, IWL_DL_QOS, f, ## a) +#define IWL_DEBUG_TX_QUEUES(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_QUEUES, f, ## a) #define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a) #define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) #define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index a1670e3f8bf..04a3343f461 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -234,11 +234,12 @@ static ssize_t iwl_dbgfs_sram_read(struct file *file, /* default is to dump the entire data segment */ if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { + struct iwl_trans *trans = trans(priv); priv->dbgfs_sram_offset = 0x800000; - if (priv->ucode_type == IWL_UCODE_INIT) - priv->dbgfs_sram_len = priv->ucode_init.data.len; + if (trans->shrd->ucode_type == IWL_UCODE_INIT) + priv->dbgfs_sram_len = trans->ucode_init.data.len; else - priv->dbgfs_sram_len = priv->ucode_rt.data.len; + priv->dbgfs_sram_len = trans->ucode_rt.data.len; } len = priv->dbgfs_sram_len; @@ -341,7 +342,7 @@ static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file, return simple_read_from_buffer(user_buf, count, ppos, priv->wowlan_sram, - priv->ucode_wowlan.data.len); + trans(priv)->ucode_wowlan.data.len); } static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -371,15 +372,13 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, i, station->sta.sta.addr, station->sta.station_flags_msk); pos += scnprintf(buf + pos, bufsz - pos, - "TID\tseq_num\ttxq_id\ttfds\trate_n_flags\n"); + "TID\tseq_num\trate_n_flags\n"); for (j = 0; j < IWL_MAX_TID_COUNT; j++) { - tid_data = &priv->shrd->tid_data[i][j]; + tid_data = &priv->tid_data[i][j]; pos += scnprintf(buf + pos, bufsz - pos, - "%d:\t%#x\t%#x\t%u\t%#x", + "%d:\t%#x\t%#x", j, tid_data->seq_number, - tid_data->agg.txq_id, - tid_data->tfds_in_queue, tid_data->agg.rate_n_flags); if (tid_data->agg.wait_for_ba) @@ -407,7 +406,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, const u8 *ptr; char *buf; u16 eeprom_ver; - size_t eeprom_len = priv->cfg->base_params->eeprom_size; + size_t eeprom_len = cfg(priv)->base_params->eeprom_size; buf_size = 4 * eeprom_len + 256; if (eeprom_len % 16) { @@ -415,7 +414,7 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, return -ENODATA; } - ptr = priv->eeprom; + ptr = priv->shrd->eeprom; if (!ptr) { IWL_ERR(priv, "Invalid EEPROM/OTP memory\n"); return -ENOMEM; @@ -427,10 +426,10 @@ static ssize_t iwl_dbgfs_nvm_read(struct file *file, IWL_ERR(priv, "Can not allocate Buffer\n"); return -ENOMEM; } - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); + eeprom_ver = iwl_eeprom_query16(priv->shrd, EEPROM_VERSION); pos += scnprintf(buf + pos, buf_size - pos, "NVM Type: %s, " "version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + (trans(priv)->nvm_device_type == NVM_DEVICE_TYPE_OTP) ? "OTP" : "EEPROM", eeprom_ver); for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); @@ -1541,15 +1540,15 @@ static ssize_t iwl_dbgfs_ucode_tx_stats_read(struct file *file, if (tx->tx_power.ant_a || tx->tx_power.ant_b || tx->tx_power.ant_c) { pos += scnprintf(buf + pos, bufsz - pos, "tx power: (1/2 dB step)\n"); - if ((priv->cfg->valid_tx_ant & ANT_A) && tx->tx_power.ant_a) + if ((cfg(priv)->valid_tx_ant & ANT_A) && tx->tx_power.ant_a) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna A:", tx->tx_power.ant_a); - if ((priv->cfg->valid_tx_ant & ANT_B) && tx->tx_power.ant_b) + if ((cfg(priv)->valid_tx_ant & ANT_B) && tx->tx_power.ant_b) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna B:", tx->tx_power.ant_b); - if ((priv->cfg->valid_tx_ant & ANT_C) && tx->tx_power.ant_c) + if ((cfg(priv)->valid_tx_ant & ANT_C) && tx->tx_power.ant_c) pos += scnprintf(buf + pos, bufsz - pos, fmt_hex, "antenna C:", tx->tx_power.ant_c); @@ -2220,7 +2219,7 @@ static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file, const size_t bufsz = sizeof(buf); pos += scnprintf(buf + pos, bufsz - pos, "%u\n", - priv->cfg->base_params->plcp_delta_threshold); + cfg(priv)->base_params->plcp_delta_threshold); return simple_read_from_buffer(user_buf, count, ppos, buf, pos); } @@ -2242,10 +2241,10 @@ static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file, return -EINVAL; if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) - priv->cfg->base_params->plcp_delta_threshold = + cfg(priv)->base_params->plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; else - priv->cfg->base_params->plcp_delta_threshold = plcp; + cfg(priv)->base_params->plcp_delta_threshold = plcp; return count; } @@ -2347,7 +2346,7 @@ static ssize_t iwl_dbgfs_wd_timeout_write(struct file *file, if (timeout < 0 || timeout > IWL_MAX_WD_TIMEOUT) timeout = IWL_DEF_WD_TIMEOUT; - priv->cfg->base_params->wd_timeout = timeout; + cfg(priv)->base_params->wd_timeout = timeout; iwl_setup_watchdog(priv); return count; } @@ -2407,10 +2406,10 @@ static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, char buf[40]; const size_t bufsz = sizeof(buf); - if (priv->cfg->ht_params) + if (cfg(priv)->ht_params) pos += scnprintf(buf + pos, bufsz - pos, "use %s for aggregation\n", - (priv->cfg->ht_params->use_rts_for_aggregation) ? + (cfg(priv)->ht_params->use_rts_for_aggregation) ? "rts/cts" : "cts-to-self"); else pos += scnprintf(buf + pos, bufsz - pos, "N/A"); @@ -2427,7 +2426,7 @@ static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, int buf_size; int rts; - if (!priv->cfg->ht_params) + if (!cfg(priv)->ht_params) return -EINVAL; memset(buf, 0, sizeof(buf)); @@ -2437,9 +2436,9 @@ static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, if (sscanf(buf, "%d", &rts) != 1) return -EINVAL; if (rts) - priv->cfg->ht_params->use_rts_for_aggregation = true; + cfg(priv)->ht_params->use_rts_for_aggregation = true; else - priv->cfg->ht_params->use_rts_for_aggregation = false; + cfg(priv)->ht_params->use_rts_for_aggregation = false; return count; } diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index 6c00a447963..e54a4d11e58 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -60,11 +60,10 @@ struct iwl_tx_queue; /* Default noise level to report when noise measurement is not available. * This may be because we're: - * 1) Not associated (4965, no beacon statistics being sent to driver) + * 1) Not associated no beacon statistics being sent to driver) * 2) Scanning (noise measurement does not apply to associated channel) - * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) * Use default noise value of -127 ... this is below the range of measurable - * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. + * Rx dBm for all agn devices, so it can indicate "unmeasurable" to user. * Also, -127 works better than 0 when averaging frames with/without * noise info (e.g. averaging might be done in app); measured dBm values are * always negative ... using a negative value as the default keeps all @@ -190,6 +189,69 @@ struct iwl_qos_info { struct iwl_qosparam_cmd def_qos_parm; }; +/** + * enum iwl_agg_state + * + * The state machine of the BA agreement establishment / tear down. + * These states relate to a specific RA / TID. + * + * @IWL_AGG_OFF: aggregation is not used + * @IWL_AGG_ON: aggregation session is up + * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the + * HW queue to be empty from packets for this RA /TID. + */ +enum iwl_agg_state { + IWL_AGG_OFF = 0, + IWL_AGG_ON, + IWL_EMPTYING_HW_QUEUE_ADDBA, + IWL_EMPTYING_HW_QUEUE_DELBA, +}; + +/** + * struct iwl_ht_agg - aggregation state machine + + * This structs holds the states for the BA agreement establishment and tear + * down. It also holds the state during the BA session itself. This struct is + * duplicated for each RA / TID. + + * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the + * Tx response (REPLY_TX), and the block ack notification + * (REPLY_COMPRESSED_BA). + * @state: state of the BA agreement establishment / tear down. + * @txq_id: Tx queue used by the BA session - used by the transport layer. + * Needed by the upper layer for debugfs only. + * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or + * the first packet to be sent in legacy HW queue in Tx AGG stop flow. + * Basically when next_reclaimed reaches ssn, we can tell mac80211 that + * we are ready to finish the Tx AGG stop / start flow. + * @wait_for_ba: Expect block-ack before next Tx reply + */ +struct iwl_ht_agg { + u32 rate_n_flags; + enum iwl_agg_state state; + u16 txq_id; + u16 ssn; + bool wait_for_ba; +}; + +/** + * struct iwl_tid_data - one for each RA / TID + + * This structs holds the states for each RA / TID. + + * @seq_number: the next WiFi sequence number to use + * @next_reclaimed: the WiFi sequence number of the next packet to be acked. + * This is basically (last acked packet++). + * @agg: aggregation state machine + */ +struct iwl_tid_data { + u16 seq_number; + u16 next_reclaimed; + struct iwl_ht_agg agg; +}; + /* * Structure should be accessed with sta_lock held. When station addition * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only @@ -230,17 +292,6 @@ struct iwl_vif_priv { u8 ibss_bssid_sta_id; }; -/* one for each uCode image (inst/data, boot/init/runtime) */ -struct fw_desc { - void *v_addr; /* access by driver */ - dma_addr_t p_addr; /* access by card's busmaster DMA */ - u32 len; /* bytes */ -}; - -struct fw_img { - struct fw_desc code, data; -}; - /* v1/v2 uCode file layout */ struct iwl_ucode_header { __le32 ver; /* major/minor/API/serial */ @@ -452,29 +503,6 @@ enum iwlagn_chain_noise_state { IWL_CHAIN_NOISE_DONE, }; - -/* - * enum iwl_calib - * defines the order in which results of initial calibrations - * should be sent to the runtime uCode - */ -enum iwl_calib { - IWL_CALIB_XTAL, - IWL_CALIB_DC, - IWL_CALIB_LO, - IWL_CALIB_TX_IQ, - IWL_CALIB_TX_IQ_PERD, - IWL_CALIB_BASE_BAND, - IWL_CALIB_TEMP_OFFSET, - IWL_CALIB_MAX -}; - -/* Opaque calibration results */ -struct iwl_calib_result { - void *buf; - size_t buf_len; -}; - /* Sensitivity calib data */ struct iwl_sensitivity_data { u32 auto_corr_ofdm; @@ -547,16 +575,6 @@ enum iwl_access_mode { IWL_OTP_ACCESS_RELATIVE, }; -/** - * enum iwl_pa_type - Power Amplifier type - * @IWL_PA_SYSTEM: based on uCode configuration - * @IWL_PA_INTERNAL: use Internal only - */ -enum iwl_pa_type { - IWL_PA_SYSTEM = 0, - IWL_PA_INTERNAL = 1, -}; - /* reply_tx_statistics (for _agn devices) */ struct reply_tx_error_statistics { u32 pp_delay; @@ -714,35 +732,6 @@ struct iwl_force_reset { */ #define IWLAGN_EXT_BEACON_TIME_POS 22 -/** - * struct iwl_notification_wait - notification wait entry - * @list: list head for global list - * @fn: function called with the notification - * @cmd: command ID - * - * This structure is not used directly, to wait for a - * notification declare it on the stack, and call - * iwlagn_init_notification_wait() with appropriate - * parameters. Then do whatever will cause the ucode - * to notify the driver, and to wait for that then - * call iwlagn_wait_notification(). - * - * Each notification is one-shot. If at some point we - * need to support multi-shot notifications (which - * can't be allocated on the stack) we need to modify - * the code for them. - */ -struct iwl_notification_wait { - struct list_head list; - - void (*fn)(struct iwl_priv *priv, struct iwl_rx_packet *pkt, - void *data); - void *fn_data; - - u8 cmd; - bool triggered, aborted; -}; - struct iwl_rxon_context { struct ieee80211_vif *vif; @@ -805,14 +794,7 @@ enum iwl_scan_type { IWL_SCAN_ROC, }; -enum iwlagn_ucode_type { - IWL_UCODE_NONE, - IWL_UCODE_REGULAR, - IWL_UCODE_INIT, - IWL_UCODE_WOWLAN, -}; - -#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE struct iwl_testmode_trace { u32 buff_size; u32 total_size; @@ -822,8 +804,20 @@ struct iwl_testmode_trace { dma_addr_t dma_addr; bool trace_enabled; }; +struct iwl_testmode_sram { + u32 buff_size; + u32 num_chunks; + u8 *buff_addr; + bool sram_readed; +}; #endif +struct iwl_wipan_noa_data { + struct rcu_head rcu_head; + u32 length; + u8 data[]; +}; + struct iwl_priv { /*data shared among all the driver's layers */ @@ -835,7 +829,6 @@ struct iwl_priv { struct ieee80211_channel *ieee_channels; struct ieee80211_rate *ieee_rates; struct kmem_cache *tx_cmd_pool; - struct iwl_cfg *cfg; enum ieee80211_band band; @@ -880,8 +873,7 @@ struct iwl_priv { s32 temperature; /* Celsius */ s32 last_temperature; - /* init calibration results */ - struct iwl_calib_result calib_results[IWL_CALIB_MAX]; + struct iwl_wipan_noa_data __rcu *noa_data; /* Scan related variables */ unsigned long scan_start; @@ -907,23 +899,12 @@ struct iwl_priv { u32 ucode_ver; /* version of ucode, copy of iwl_ucode.ver */ - struct fw_img ucode_rt; - struct fw_img ucode_init; - struct fw_img ucode_wowlan; - - enum iwlagn_ucode_type ucode_type; - u8 ucode_write_complete; /* the image write is complete */ char firmware_name[25]; struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; __le16 switch_channel; - struct { - u32 error_event_table; - u32 log_event_table; - } device_pointers; - u16 active_rate; u8 start_calib; @@ -951,17 +932,13 @@ struct iwl_priv { int num_stations; struct iwl_station_entry stations[IWLAGN_STATION_COUNT]; unsigned long ucode_key_table; + struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; u8 mac80211_registered; /* Indication if ieee80211_ops->open has been called */ u8 is_open; - /* eeprom -- this is in the card's little endian byte order */ - u8 *eeprom; - int nvm_device_type; - struct iwl_eeprom_calib_info *calib_info; - enum nl80211_iftype iw_mode; /* Last Rx'd beacon timestamp */ @@ -1017,10 +994,6 @@ struct iwl_priv { /* counts reply_tx error */ struct reply_tx_error_statistics reply_tx_stats; struct reply_agg_tx_error_statistics reply_agg_tx_stats; - /* notification wait support */ - struct list_head notif_waits; - spinlock_t notif_wait_lock; - wait_queue_head_t notif_waitq; /* remain-on-channel offload support */ struct ieee80211_channel *hw_roc_channel; @@ -1098,8 +1071,9 @@ struct iwl_priv { struct led_classdev led; unsigned long blink_on, blink_off; bool led_registered; -#ifdef CONFIG_IWLWIFI_DEVICE_SVTOOL +#ifdef CONFIG_IWLWIFI_DEVICE_TESTMODE struct iwl_testmode_trace testmode_trace; + struct iwl_testmode_sram testmode_sram; u32 tm_fixed_rate; #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.c b/drivers/net/wireless/iwlwifi/iwl-devtrace.c index a635a7e7544..2a2c8de64a0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.c +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.c @@ -28,7 +28,7 @@ /* sparse doesn't like tracepoint macros */ #ifndef __CHECKER__ -#include "iwl-dev.h" +#include "iwl-trans.h" #define CREATE_TRACE_POINTS #include "iwl-devtrace.h" diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 8a51c5ccda1..9b212a8f30b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -29,7 +29,6 @@ #include <linux/tracepoint.h> -struct iwl_priv; #if !defined(CONFIG_IWLWIFI_DEVICE_TRACING) || defined(__CHECKER__) #undef TRACE_EVENT @@ -37,14 +36,14 @@ struct iwl_priv; static inline void trace_ ## name(proto) {} #endif -#define PRIV_ENTRY __field(struct iwl_priv *, priv) +#define PRIV_ENTRY __field(void *, priv) #define PRIV_ASSIGN __entry->priv = priv #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_io TRACE_EVENT(iwlwifi_dev_ioread32, - TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_PROTO(void *priv, u32 offs, u32 val), TP_ARGS(priv, offs, val), TP_STRUCT__entry( PRIV_ENTRY @@ -60,7 +59,7 @@ TRACE_EVENT(iwlwifi_dev_ioread32, ); TRACE_EVENT(iwlwifi_dev_iowrite8, - TP_PROTO(struct iwl_priv *priv, u32 offs, u8 val), + TP_PROTO(void *priv, u32 offs, u8 val), TP_ARGS(priv, offs, val), TP_STRUCT__entry( PRIV_ENTRY @@ -76,7 +75,7 @@ TRACE_EVENT(iwlwifi_dev_iowrite8, ); TRACE_EVENT(iwlwifi_dev_iowrite32, - TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_PROTO(void *priv, u32 offs, u32 val), TP_ARGS(priv, offs, val), TP_STRUCT__entry( PRIV_ENTRY @@ -91,11 +90,40 @@ TRACE_EVENT(iwlwifi_dev_iowrite32, TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, __entry->offs, __entry->val) ); +TRACE_EVENT(iwlwifi_dev_irq, + TP_PROTO(void *priv), + TP_ARGS(priv), + TP_STRUCT__entry( + PRIV_ENTRY + ), + TP_fast_assign( + PRIV_ASSIGN; + ), + /* TP_printk("") doesn't compile */ + TP_printk("%d", 0) +); + +TRACE_EVENT(iwlwifi_dev_ict_read, + TP_PROTO(void *priv, u32 index, u32 value), + TP_ARGS(priv, index, value), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, index) + __field(u32, value) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->index = index; + __entry->value = value; + ), + TP_printk("read ict[%d] = %#.8x", __entry->index, __entry->value) +); + #undef TRACE_SYSTEM #define TRACE_SYSTEM iwlwifi_ucode TRACE_EVENT(iwlwifi_dev_ucode_cont_event, - TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_PROTO(void *priv, u32 time, u32 data, u32 ev), TP_ARGS(priv, time, data, ev), TP_STRUCT__entry( PRIV_ENTRY @@ -115,7 +143,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_cont_event, ); TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, - TP_PROTO(struct iwl_priv *priv, u32 wraps, u32 n_entry, u32 p_entry), + TP_PROTO(void *priv, u32 wraps, u32 n_entry, u32 p_entry), TP_ARGS(priv, wraps, n_entry, p_entry), TP_STRUCT__entry( PRIV_ENTRY @@ -139,7 +167,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_wrap_event, #define TRACE_SYSTEM iwlwifi TRACE_EVENT(iwlwifi_dev_hcmd, - TP_PROTO(struct iwl_priv *priv, u32 flags, + TP_PROTO(void *priv, u32 flags, const void *hcmd0, size_t len0, const void *hcmd1, size_t len1, const void *hcmd2, size_t len2), @@ -164,7 +192,7 @@ TRACE_EVENT(iwlwifi_dev_hcmd, ); TRACE_EVENT(iwlwifi_dev_rx, - TP_PROTO(struct iwl_priv *priv, void *rxbuf, size_t len), + TP_PROTO(void *priv, void *rxbuf, size_t len), TP_ARGS(priv, rxbuf, len), TP_STRUCT__entry( PRIV_ENTRY @@ -179,7 +207,7 @@ TRACE_EVENT(iwlwifi_dev_rx, ); TRACE_EVENT(iwlwifi_dev_tx, - TP_PROTO(struct iwl_priv *priv, void *tfd, size_t tfdlen, + TP_PROTO(void *priv, void *tfd, size_t tfdlen, void *buf0, size_t buf0_len, void *buf1, size_t buf1_len), TP_ARGS(priv, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), @@ -211,7 +239,7 @@ TRACE_EVENT(iwlwifi_dev_tx, ); TRACE_EVENT(iwlwifi_dev_ucode_error, - TP_PROTO(struct iwl_priv *priv, u32 desc, u32 tsf_low, + TP_PROTO(void *priv, u32 desc, u32 tsf_low, u32 data1, u32 data2, u32 line, u32 blink1, u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver, @@ -271,7 +299,7 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, ); TRACE_EVENT(iwlwifi_dev_ucode_event, - TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_PROTO(void *priv, u32 time, u32 data, u32 ev), TP_ARGS(priv, time, data, ev), TP_STRUCT__entry( PRIV_ENTRY diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index a4e43bd4a54..c1eda9724f4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -149,23 +149,23 @@ static const u8 iwl_eeprom_band_7[] = { /* 5.2 ht40 channel */ * EEPROM chip, not a single event, so even reads could conflict if they * weren't arbitrated by the semaphore. */ -static int iwl_eeprom_acquire_semaphore(struct iwl_priv *priv) +static int iwl_eeprom_acquire_semaphore(struct iwl_bus *bus) { u16 count; int ret; for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { /* Request semaphore */ - iwl_set_bit(bus(priv), CSR_HW_IF_CONFIG_REG, + iwl_set_bit(bus, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); /* See if we got it */ - ret = iwl_poll_bit(bus(priv), CSR_HW_IF_CONFIG_REG, + ret = iwl_poll_bit(bus, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, EEPROM_SEM_TIMEOUT); if (ret >= 0) { - IWL_DEBUG_EEPROM(priv, + IWL_DEBUG_EEPROM(bus, "Acquired semaphore after %d tries.\n", count+1); return ret; @@ -175,39 +175,39 @@ static int iwl_eeprom_acquire_semaphore(struct iwl_priv *priv) return ret; } -static void iwl_eeprom_release_semaphore(struct iwl_priv *priv) +static void iwl_eeprom_release_semaphore(struct iwl_bus *bus) { - iwl_clear_bit(bus(priv), CSR_HW_IF_CONFIG_REG, + iwl_clear_bit(bus, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); } -static int iwl_eeprom_verify_signature(struct iwl_priv *priv) +static int iwl_eeprom_verify_signature(struct iwl_trans *trans) { - u32 gp = iwl_read32(bus(priv), CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; + u32 gp = iwl_read32(bus(trans), CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; int ret = 0; - IWL_DEBUG_EEPROM(priv, "EEPROM signature=0x%08x\n", gp); + IWL_DEBUG_EEPROM(trans, "EEPROM signature=0x%08x\n", gp); switch (gp) { case CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP: - if (priv->nvm_device_type != NVM_DEVICE_TYPE_OTP) { - IWL_ERR(priv, "EEPROM with bad signature: 0x%08x\n", + if (trans->nvm_device_type != NVM_DEVICE_TYPE_OTP) { + IWL_ERR(trans, "EEPROM with bad signature: 0x%08x\n", gp); ret = -ENOENT; } break; case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: - if (priv->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) { - IWL_ERR(priv, "OTP with bad signature: 0x%08x\n", gp); + if (trans->nvm_device_type != NVM_DEVICE_TYPE_EEPROM) { + IWL_ERR(trans, "OTP with bad signature: 0x%08x\n", gp); ret = -ENOENT; } break; case CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP: default: - IWL_ERR(priv, "bad EEPROM/OTP signature, type=%s, " + IWL_ERR(trans, "bad EEPROM/OTP signature, type=%s, " "EEPROM_GP=0x%08x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + (trans->nvm_device_type == NVM_DEVICE_TYPE_OTP) ? "OTP" : "EEPROM", gp); ret = -ENOENT; break; @@ -215,11 +215,11 @@ static int iwl_eeprom_verify_signature(struct iwl_priv *priv) return ret; } -u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) +u16 iwl_eeprom_query16(const struct iwl_shared *shrd, size_t offset) { - if (!priv->eeprom) + if (!shrd->eeprom) return 0; - return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); + return (u16)shrd->eeprom[offset] | ((u16)shrd->eeprom[offset + 1] << 8); } int iwl_eeprom_check_version(struct iwl_priv *priv) @@ -227,11 +227,11 @@ int iwl_eeprom_check_version(struct iwl_priv *priv) u16 eeprom_ver; u16 calib_ver; - eeprom_ver = iwl_eeprom_query16(priv, EEPROM_VERSION); - calib_ver = iwlagn_eeprom_calib_version(priv); + eeprom_ver = iwl_eeprom_query16(priv->shrd, EEPROM_VERSION); + calib_ver = iwl_eeprom_calib_version(priv->shrd); - if (eeprom_ver < priv->cfg->eeprom_ver || - calib_ver < priv->cfg->eeprom_calib_ver) + if (eeprom_ver < cfg(priv)->eeprom_ver || + calib_ver < cfg(priv)->eeprom_calib_ver) goto err; IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", @@ -241,45 +241,46 @@ int iwl_eeprom_check_version(struct iwl_priv *priv) err: IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " "CALIB=0x%x < 0x%x\n", - eeprom_ver, priv->cfg->eeprom_ver, - calib_ver, priv->cfg->eeprom_calib_ver); + eeprom_ver, cfg(priv)->eeprom_ver, + calib_ver, cfg(priv)->eeprom_calib_ver); return -EINVAL; } int iwl_eeprom_check_sku(struct iwl_priv *priv) { + struct iwl_shared *shrd = priv->shrd; u16 radio_cfg; - if (!priv->cfg->sku) { + if (!cfg(priv)->sku) { /* not using sku overwrite */ - priv->cfg->sku = iwl_eeprom_query16(priv, EEPROM_SKU_CAP); - if (priv->cfg->sku & EEPROM_SKU_CAP_11N_ENABLE && - !priv->cfg->ht_params) { + cfg(priv)->sku = iwl_eeprom_query16(shrd, EEPROM_SKU_CAP); + if (cfg(priv)->sku & EEPROM_SKU_CAP_11N_ENABLE && + !cfg(priv)->ht_params) { IWL_ERR(priv, "Invalid 11n configuration\n"); return -EINVAL; } } - if (!priv->cfg->sku) { + if (!cfg(priv)->sku) { IWL_ERR(priv, "Invalid device sku\n"); return -EINVAL; } - IWL_INFO(priv, "Device SKU: 0X%x\n", priv->cfg->sku); + IWL_INFO(priv, "Device SKU: 0x%X\n", cfg(priv)->sku); - if (!priv->cfg->valid_tx_ant && !priv->cfg->valid_rx_ant) { + if (!cfg(priv)->valid_tx_ant && !cfg(priv)->valid_rx_ant) { /* not using .cfg overwrite */ - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); - priv->cfg->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); - priv->cfg->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); - if (!priv->cfg->valid_tx_ant || !priv->cfg->valid_rx_ant) { - IWL_ERR(priv, "Invalid chain (0X%x, 0X%x)\n", - priv->cfg->valid_tx_ant, - priv->cfg->valid_rx_ant); + radio_cfg = iwl_eeprom_query16(shrd, EEPROM_RADIO_CONFIG); + cfg(priv)->valid_tx_ant = EEPROM_RF_CFG_TX_ANT_MSK(radio_cfg); + cfg(priv)->valid_rx_ant = EEPROM_RF_CFG_RX_ANT_MSK(radio_cfg); + if (!cfg(priv)->valid_tx_ant || !cfg(priv)->valid_rx_ant) { + IWL_ERR(priv, "Invalid chain (0x%X, 0x%X)\n", + cfg(priv)->valid_tx_ant, + cfg(priv)->valid_rx_ant); return -EINVAL; } - IWL_INFO(priv, "Valid Tx ant: 0X%x, Valid Rx ant: 0X%x\n", - priv->cfg->valid_tx_ant, priv->cfg->valid_rx_ant); + IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", + cfg(priv)->valid_tx_ant, cfg(priv)->valid_rx_ant); } /* * for some special cases, @@ -289,9 +290,9 @@ int iwl_eeprom_check_sku(struct iwl_priv *priv) return 0; } -void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac) +void iwl_eeprom_get_mac(const struct iwl_shared *shrd, u8 *mac) { - const u8 *addr = iwl_eeprom_query_addr(priv, + const u8 *addr = iwl_eeprom_query_addr(shrd, EEPROM_MAC_ADDRESS); memcpy(mac, addr, ETH_ALEN); } @@ -302,19 +303,19 @@ void iwl_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac) * ******************************************************************************/ -static void iwl_set_otp_access(struct iwl_priv *priv, enum iwl_access_mode mode) +static void iwl_set_otp_access(struct iwl_bus *bus, enum iwl_access_mode mode) { - iwl_read32(bus(priv), CSR_OTP_GP_REG); + iwl_read32(bus, CSR_OTP_GP_REG); if (mode == IWL_OTP_ACCESS_ABSOLUTE) - iwl_clear_bit(bus(priv), CSR_OTP_GP_REG, + iwl_clear_bit(bus, CSR_OTP_GP_REG, CSR_OTP_GP_REG_OTP_ACCESS_MODE); else - iwl_set_bit(bus(priv), CSR_OTP_GP_REG, + iwl_set_bit(bus, CSR_OTP_GP_REG, CSR_OTP_GP_REG_OTP_ACCESS_MODE); } -static int iwl_get_nvm_type(struct iwl_priv *priv, u32 hw_rev) +static int iwl_get_nvm_type(struct iwl_bus *bus, u32 hw_rev) { u32 otpgp; int nvm_type; @@ -322,7 +323,7 @@ static int iwl_get_nvm_type(struct iwl_priv *priv, u32 hw_rev) /* OTP only valid for CP/PP and after */ switch (hw_rev & CSR_HW_REV_TYPE_MSK) { case CSR_HW_REV_TYPE_NONE: - IWL_ERR(priv, "Unknown hardware type\n"); + IWL_ERR(bus, "Unknown hardware type\n"); return -ENOENT; case CSR_HW_REV_TYPE_5300: case CSR_HW_REV_TYPE_5350: @@ -331,7 +332,7 @@ static int iwl_get_nvm_type(struct iwl_priv *priv, u32 hw_rev) nvm_type = NVM_DEVICE_TYPE_EEPROM; break; default: - otpgp = iwl_read32(bus(priv), CSR_OTP_GP_REG); + otpgp = iwl_read32(bus, CSR_OTP_GP_REG); if (otpgp & CSR_OTP_GP_REG_DEVICE_SELECT) nvm_type = NVM_DEVICE_TYPE_OTP; else @@ -341,73 +342,73 @@ static int iwl_get_nvm_type(struct iwl_priv *priv, u32 hw_rev) return nvm_type; } -static int iwl_init_otp_access(struct iwl_priv *priv) +static int iwl_init_otp_access(struct iwl_bus *bus) { int ret; /* Enable 40MHz radio clock */ - iwl_write32(bus(priv), CSR_GP_CNTRL, - iwl_read32(bus(priv), CSR_GP_CNTRL) | + iwl_write32(bus, CSR_GP_CNTRL, + iwl_read32(bus, CSR_GP_CNTRL) | CSR_GP_CNTRL_REG_FLAG_INIT_DONE); /* wait for clock to be ready */ - ret = iwl_poll_bit(bus(priv), CSR_GP_CNTRL, + ret = iwl_poll_bit(bus, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); if (ret < 0) - IWL_ERR(priv, "Time out access OTP\n"); + IWL_ERR(bus, "Time out access OTP\n"); else { - iwl_set_bits_prph(bus(priv), APMG_PS_CTRL_REG, + iwl_set_bits_prph(bus, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); udelay(5); - iwl_clear_bits_prph(bus(priv), APMG_PS_CTRL_REG, + iwl_clear_bits_prph(bus, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); /* * CSR auto clock gate disable bit - * this is only applicable for HW with OTP shadow RAM */ - if (priv->cfg->base_params->shadow_ram_support) - iwl_set_bit(bus(priv), CSR_DBG_LINK_PWR_MGMT_REG, + if (cfg(bus)->base_params->shadow_ram_support) + iwl_set_bit(bus, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); } return ret; } -static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, __le16 *eeprom_data) +static int iwl_read_otp_word(struct iwl_bus *bus, u16 addr, __le16 *eeprom_data) { int ret = 0; u32 r; u32 otpgp; - iwl_write32(bus(priv), CSR_EEPROM_REG, + iwl_write32(bus, CSR_EEPROM_REG, CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); - ret = iwl_poll_bit(bus(priv), CSR_EEPROM_REG, + ret = iwl_poll_bit(bus, CSR_EEPROM_REG, CSR_EEPROM_REG_READ_VALID_MSK, CSR_EEPROM_REG_READ_VALID_MSK, IWL_EEPROM_ACCESS_TIMEOUT); if (ret < 0) { - IWL_ERR(priv, "Time out reading OTP[%d]\n", addr); + IWL_ERR(bus, "Time out reading OTP[%d]\n", addr); return ret; } - r = iwl_read32(bus(priv), CSR_EEPROM_REG); + r = iwl_read32(bus, CSR_EEPROM_REG); /* check for ECC errors: */ - otpgp = iwl_read32(bus(priv), CSR_OTP_GP_REG); + otpgp = iwl_read32(bus, CSR_OTP_GP_REG); if (otpgp & CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK) { /* stop in this case */ /* set the uncorrectable OTP ECC bit for acknowledgement */ - iwl_set_bit(bus(priv), CSR_OTP_GP_REG, + iwl_set_bit(bus, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); - IWL_ERR(priv, "Uncorrectable OTP ECC error, abort OTP read\n"); + IWL_ERR(bus, "Uncorrectable OTP ECC error, abort OTP read\n"); return -EINVAL; } if (otpgp & CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK) { /* continue in this case */ /* set the correctable OTP ECC bit for acknowledgement */ - iwl_set_bit(bus(priv), CSR_OTP_GP_REG, + iwl_set_bit(bus, CSR_OTP_GP_REG, CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK); - IWL_ERR(priv, "Correctable OTP ECC error, continue read\n"); + IWL_ERR(bus, "Correctable OTP ECC error, continue read\n"); } *eeprom_data = cpu_to_le16(r >> 16); return 0; @@ -416,20 +417,20 @@ static int iwl_read_otp_word(struct iwl_priv *priv, u16 addr, __le16 *eeprom_dat /* * iwl_is_otp_empty: check for empty OTP */ -static bool iwl_is_otp_empty(struct iwl_priv *priv) +static bool iwl_is_otp_empty(struct iwl_bus *bus) { u16 next_link_addr = 0; __le16 link_value; bool is_empty = false; /* locate the beginning of OTP link list */ - if (!iwl_read_otp_word(priv, next_link_addr, &link_value)) { + if (!iwl_read_otp_word(bus, next_link_addr, &link_value)) { if (!link_value) { - IWL_ERR(priv, "OTP is empty\n"); + IWL_ERR(bus, "OTP is empty\n"); is_empty = true; } } else { - IWL_ERR(priv, "Unable to read first block of OTP list.\n"); + IWL_ERR(bus, "Unable to read first block of OTP list.\n"); is_empty = true; } @@ -446,7 +447,7 @@ static bool iwl_is_otp_empty(struct iwl_priv *priv) * we should read and used to configure the device. * only perform this operation if shadow RAM is disabled */ -static int iwl_find_otp_image(struct iwl_priv *priv, +static int iwl_find_otp_image(struct iwl_bus *bus, u16 *validblockaddr) { u16 next_link_addr = 0, valid_addr; @@ -454,10 +455,10 @@ static int iwl_find_otp_image(struct iwl_priv *priv, int usedblocks = 0; /* set addressing mode to absolute to traverse the link list */ - iwl_set_otp_access(priv, IWL_OTP_ACCESS_ABSOLUTE); + iwl_set_otp_access(bus, IWL_OTP_ACCESS_ABSOLUTE); /* checking for empty OTP or error */ - if (iwl_is_otp_empty(priv)) + if (iwl_is_otp_empty(bus)) return -EINVAL; /* @@ -471,9 +472,9 @@ static int iwl_find_otp_image(struct iwl_priv *priv, */ valid_addr = next_link_addr; next_link_addr = le16_to_cpu(link_value) * sizeof(u16); - IWL_DEBUG_EEPROM(priv, "OTP blocks %d addr 0x%x\n", + IWL_DEBUG_EEPROM(bus, "OTP blocks %d addr 0x%x\n", usedblocks, next_link_addr); - if (iwl_read_otp_word(priv, next_link_addr, &link_value)) + if (iwl_read_otp_word(bus, next_link_addr, &link_value)) return -EINVAL; if (!link_value) { /* @@ -488,10 +489,10 @@ static int iwl_find_otp_image(struct iwl_priv *priv, } /* more in the link list, continue */ usedblocks++; - } while (usedblocks <= priv->cfg->base_params->max_ll_items); + } while (usedblocks <= cfg(bus)->base_params->max_ll_items); /* OTP has no valid blocks */ - IWL_DEBUG_EEPROM(priv, "OTP has no valid blocks\n"); + IWL_DEBUG_EEPROM(bus, "OTP has no valid blocks\n"); return -EINVAL; } @@ -504,28 +505,28 @@ static int iwl_find_otp_image(struct iwl_priv *priv, * iwl_get_max_txpower_avg - get the highest tx power from all chains. * find the highest tx power from all chains for the channel */ -static s8 iwl_get_max_txpower_avg(struct iwl_priv *priv, +static s8 iwl_get_max_txpower_avg(struct iwl_cfg *cfg, struct iwl_eeprom_enhanced_txpwr *enhanced_txpower, int element, s8 *max_txpower_in_half_dbm) { s8 max_txpower_avg = 0; /* (dBm) */ /* Take the highest tx power from any valid chains */ - if ((priv->cfg->valid_tx_ant & ANT_A) && + if ((cfg->valid_tx_ant & ANT_A) && (enhanced_txpower[element].chain_a_max > max_txpower_avg)) max_txpower_avg = enhanced_txpower[element].chain_a_max; - if ((priv->cfg->valid_tx_ant & ANT_B) && + if ((cfg->valid_tx_ant & ANT_B) && (enhanced_txpower[element].chain_b_max > max_txpower_avg)) max_txpower_avg = enhanced_txpower[element].chain_b_max; - if ((priv->cfg->valid_tx_ant & ANT_C) && + if ((cfg->valid_tx_ant & ANT_C) && (enhanced_txpower[element].chain_c_max > max_txpower_avg)) max_txpower_avg = enhanced_txpower[element].chain_c_max; - if (((priv->cfg->valid_tx_ant == ANT_AB) | - (priv->cfg->valid_tx_ant == ANT_BC) | - (priv->cfg->valid_tx_ant == ANT_AC)) && + if (((cfg->valid_tx_ant == ANT_AB) | + (cfg->valid_tx_ant == ANT_BC) | + (cfg->valid_tx_ant == ANT_AC)) && (enhanced_txpower[element].mimo2_max > max_txpower_avg)) max_txpower_avg = enhanced_txpower[element].mimo2_max; - if ((priv->cfg->valid_tx_ant == ANT_ABC) && + if ((cfg->valid_tx_ant == ANT_ABC) && (enhanced_txpower[element].mimo3_max > max_txpower_avg)) max_txpower_avg = enhanced_txpower[element].mimo3_max; @@ -582,6 +583,7 @@ iwl_eeprom_enh_txp_read_element(struct iwl_priv *priv, void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) { + struct iwl_shared *shrd = priv->shrd; struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; int idx, entries; __le16 *txp_len; @@ -590,10 +592,10 @@ void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); /* the length is in 16-bit words, but we want entries */ - txp_len = (__le16 *) iwl_eeprom_query_addr(priv, EEPROM_TXP_SZ_OFFS); + txp_len = (__le16 *) iwl_eeprom_query_addr(shrd, EEPROM_TXP_SZ_OFFS); entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; - txp_array = (void *) iwl_eeprom_query_addr(priv, EEPROM_TXP_OFFS); + txp_array = (void *) iwl_eeprom_query_addr(shrd, EEPROM_TXP_OFFS); for (idx = 0; idx < entries; idx++) { txp = &txp_array[idx]; @@ -627,7 +629,7 @@ void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) ((txp->delta_20_in_40 & 0xf0) >> 4), (txp->delta_20_in_40 & 0x0f)); - max_txp_avg = iwl_get_max_txpower_avg(priv, txp_array, idx, + max_txp_avg = iwl_get_max_txpower_avg(cfg(priv), txp_array, idx, &max_txp_avg_halfdbm); /* @@ -646,12 +648,13 @@ void iwl_eeprom_enhanced_txpower(struct iwl_priv *priv) /** * iwl_eeprom_init - read EEPROM contents * - * Load the EEPROM contents from adapter into priv->eeprom + * Load the EEPROM contents from adapter into shrd->eeprom * * NOTE: This routine uses the non-debug IO access functions. */ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) { + struct iwl_shared *shrd = priv->shrd; __le16 *e; u32 gp = iwl_read32(bus(priv), CSR_EEPROM_GP); int sz; @@ -660,22 +663,22 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) u16 validblockaddr = 0; u16 cache_addr = 0; - priv->nvm_device_type = iwl_get_nvm_type(priv, hw_rev); - if (priv->nvm_device_type == -ENOENT) + trans(priv)->nvm_device_type = iwl_get_nvm_type(bus(priv), hw_rev); + if (trans(priv)->nvm_device_type == -ENOENT) return -ENOENT; /* allocate eeprom */ - sz = priv->cfg->base_params->eeprom_size; + sz = cfg(priv)->base_params->eeprom_size; IWL_DEBUG_EEPROM(priv, "NVM size = %d\n", sz); - priv->eeprom = kzalloc(sz, GFP_KERNEL); - if (!priv->eeprom) { + shrd->eeprom = kzalloc(sz, GFP_KERNEL); + if (!shrd->eeprom) { ret = -ENOMEM; goto alloc_err; } - e = (__le16 *)priv->eeprom; + e = (__le16 *)shrd->eeprom; iwl_apm_init(priv); - ret = iwl_eeprom_verify_signature(priv); + ret = iwl_eeprom_verify_signature(trans(priv)); if (ret < 0) { IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); ret = -ENOENT; @@ -683,16 +686,16 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) } /* Make sure driver (instead of uCode) is allowed to read EEPROM */ - ret = iwl_eeprom_acquire_semaphore(priv); + ret = iwl_eeprom_acquire_semaphore(bus(priv)); if (ret < 0) { IWL_ERR(priv, "Failed to acquire EEPROM semaphore.\n"); ret = -ENOENT; goto err; } - if (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) { + if (trans(priv)->nvm_device_type == NVM_DEVICE_TYPE_OTP) { - ret = iwl_init_otp_access(priv); + ret = iwl_init_otp_access(bus(priv)); if (ret) { IWL_ERR(priv, "Failed to initialize OTP access.\n"); ret = -ENOENT; @@ -706,8 +709,8 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); /* traversing the linked list if no shadow ram supported */ - if (!priv->cfg->base_params->shadow_ram_support) { - if (iwl_find_otp_image(priv, &validblockaddr)) { + if (!cfg(priv)->base_params->shadow_ram_support) { + if (iwl_find_otp_image(bus(priv), &validblockaddr)) { ret = -ENOENT; goto done; } @@ -716,7 +719,7 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) addr += sizeof(u16)) { __le16 eeprom_data; - ret = iwl_read_otp_word(priv, addr, &eeprom_data); + ret = iwl_read_otp_word(bus(priv), addr, &eeprom_data); if (ret) goto done; e[cache_addr / 2] = eeprom_data; @@ -744,27 +747,27 @@ int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev) } IWL_DEBUG_EEPROM(priv, "NVM Type: %s, version: 0x%x\n", - (priv->nvm_device_type == NVM_DEVICE_TYPE_OTP) + (trans(priv)->nvm_device_type == NVM_DEVICE_TYPE_OTP) ? "OTP" : "EEPROM", - iwl_eeprom_query16(priv, EEPROM_VERSION)); + iwl_eeprom_query16(shrd, EEPROM_VERSION)); ret = 0; done: - iwl_eeprom_release_semaphore(priv); + iwl_eeprom_release_semaphore(bus(priv)); err: if (ret) - iwl_eeprom_free(priv); + iwl_eeprom_free(priv->shrd); /* Reset chip to save power until we load uCode during "up". */ iwl_apm_stop(priv); alloc_err: return ret; } -void iwl_eeprom_free(struct iwl_priv *priv) +void iwl_eeprom_free(struct iwl_shared *shrd) { - kfree(priv->eeprom); - priv->eeprom = NULL; + kfree(shrd->eeprom); + shrd->eeprom = NULL; } static void iwl_init_band_reference(const struct iwl_priv *priv, @@ -772,49 +775,50 @@ static void iwl_init_band_reference(const struct iwl_priv *priv, const struct iwl_eeprom_channel **eeprom_ch_info, const u8 **eeprom_ch_index) { - u32 offset = priv->cfg->lib-> + struct iwl_shared *shrd = priv->shrd; + u32 offset = cfg(priv)->lib-> eeprom_ops.regulatory_bands[eep_band - 1]; switch (eep_band) { case 1: /* 2.4GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_1); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_1; break; case 2: /* 4.9GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_2); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_2; break; case 3: /* 5.2GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_3); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_3; break; case 4: /* 5.5GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_4); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_4; break; case 5: /* 5.7GHz band */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_5); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_5; break; case 6: /* 2.4GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_6); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_6; break; case 7: /* 5 GHz ht40 channels */ *eeprom_ch_count = ARRAY_SIZE(iwl_eeprom_band_7); *eeprom_ch_info = (struct iwl_eeprom_channel *) - iwl_eeprom_query_addr(priv, offset); + iwl_eeprom_query_addr(shrd, offset); *eeprom_ch_index = iwl_eeprom_band_7; break; default: @@ -979,9 +983,9 @@ int iwl_init_channel_map(struct iwl_priv *priv) } /* Check if we do have HT40 channels */ - if (priv->cfg->lib->eeprom_ops.regulatory_bands[5] == + if (cfg(priv)->lib->eeprom_ops.regulatory_bands[5] == EEPROM_REGULATORY_BAND_NO_HT40 && - priv->cfg->lib->eeprom_ops.regulatory_bands[6] == + cfg(priv)->lib->eeprom_ops.regulatory_bands[6] == EEPROM_REGULATORY_BAND_NO_HT40) return 0; @@ -1017,8 +1021,8 @@ int iwl_init_channel_map(struct iwl_priv *priv) * driver need to process addition information * to determine the max channel tx power limits */ - if (priv->cfg->lib->eeprom_ops.update_enhanced_txpower) - priv->cfg->lib->eeprom_ops.update_enhanced_txpower(priv); + if (cfg(priv)->lib->eeprom_ops.update_enhanced_txpower) + cfg(priv)->lib->eeprom_ops.update_enhanced_txpower(priv); return 0; } @@ -1064,7 +1068,7 @@ void iwl_rf_config(struct iwl_priv *priv) { u16 radio_cfg; - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); + radio_cfg = iwl_eeprom_query16(priv->shrd, EEPROM_RADIO_CONFIG); /* write radio config values to register */ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) <= EEPROM_RF_CONFIG_TYPE_MAX) { diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.h b/drivers/net/wireless/iwlwifi/iwl-eeprom.h index c94747e7299..9fa937ec35e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.h +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.h @@ -66,6 +66,7 @@ #include <net/mac80211.h> struct iwl_priv; +struct iwl_shared; /* * EEPROM access time values: @@ -305,11 +306,11 @@ struct iwl_eeprom_ops { int iwl_eeprom_init(struct iwl_priv *priv, u32 hw_rev); -void iwl_eeprom_free(struct iwl_priv *priv); +void iwl_eeprom_free(struct iwl_shared *shrd); int iwl_eeprom_check_version(struct iwl_priv *priv); int iwl_eeprom_check_sku(struct iwl_priv *priv); -const u8 *iwl_eeprom_query_addr(const struct iwl_priv *priv, size_t offset); -u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset); +const u8 *iwl_eeprom_query_addr(const struct iwl_shared *shrd, size_t offset); +u16 iwl_eeprom_query16(const struct iwl_shared *shrd, size_t offset); int iwl_init_channel_map(struct iwl_priv *priv); void iwl_free_channel_map(struct iwl_priv *priv); const struct iwl_channel_info *iwl_get_channel_info( diff --git a/drivers/net/wireless/iwlwifi/iwl-io.c b/drivers/net/wireless/iwlwifi/iwl-io.c index 3ffa8e62b85..d57ea6484bb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/iwlwifi/iwl-io.c @@ -143,7 +143,7 @@ u32 iwl_read_direct32(struct iwl_bus *bus, u32 reg) spin_lock_irqsave(&bus->reg_lock, flags); iwl_grab_nic_access(bus); - value = iwl_read32(bus(bus), reg); + value = iwl_read32(bus, reg); iwl_release_nic_access(bus); spin_unlock_irqrestore(&bus->reg_lock, flags); @@ -283,16 +283,29 @@ u32 iwl_read_targ_mem(struct iwl_bus *bus, u32 addr) return value; } -void iwl_write_targ_mem(struct iwl_bus *bus, u32 addr, u32 val) +int _iwl_write_targ_mem_words(struct iwl_bus *bus, u32 addr, + void *buf, int words) { unsigned long flags; + int offs, result = 0; + u32 *vals = buf; spin_lock_irqsave(&bus->reg_lock, flags); if (!iwl_grab_nic_access(bus)) { iwl_write32(bus, HBUS_TARG_MEM_WADDR, addr); wmb(); - iwl_write32(bus, HBUS_TARG_MEM_WDAT, val); + + for (offs = 0; offs < words; offs++) + iwl_write32(bus, HBUS_TARG_MEM_WDAT, vals[offs]); iwl_release_nic_access(bus); - } + } else + result = -EBUSY; spin_unlock_irqrestore(&bus->reg_lock, flags); + + return result; +} + +int iwl_write_targ_mem(struct iwl_bus *bus, u32 addr, u32 val) +{ + return _iwl_write_targ_mem_words(bus, addr, &val, 1); } diff --git a/drivers/net/wireless/iwlwifi/iwl-io.h b/drivers/net/wireless/iwlwifi/iwl-io.h index ced2cbeb6ea..aae2eeb331a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/iwlwifi/iwl-io.h @@ -85,6 +85,9 @@ void _iwl_read_targ_mem_words(struct iwl_bus *bus, u32 addr, (bufsize) / sizeof(u32));\ } while (0) +int _iwl_write_targ_mem_words(struct iwl_bus *bus, u32 addr, + void *buf, int words); + u32 iwl_read_targ_mem(struct iwl_bus *bus, u32 addr); -void iwl_write_targ_mem(struct iwl_bus *bus, u32 addr, u32 val); +int iwl_write_targ_mem(struct iwl_bus *bus, u32 addr, u32 val); #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index eb541735296..14dcbfcdc0f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -137,11 +137,11 @@ static int iwl_led_cmd(struct iwl_priv *priv, } IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", - priv->cfg->base_params->led_compensation); + cfg(priv)->base_params->led_compensation); led_cmd.on = iwl_blink_compensation(priv, on, - priv->cfg->base_params->led_compensation); + cfg(priv)->base_params->led_compensation); led_cmd.off = iwl_blink_compensation(priv, off, - priv->cfg->base_params->led_compensation); + cfg(priv)->base_params->led_compensation); ret = iwl_send_led_cmd(priv, &led_cmd); if (!ret) { @@ -178,7 +178,7 @@ void iwl_leds_init(struct iwl_priv *priv) int ret; if (mode == IWL_LED_DEFAULT) - mode = priv->cfg->led_mode; + mode = cfg(priv)->led_mode; priv->led.name = kasprintf(GFP_KERNEL, "%s-led", wiphy_name(priv->hw->wiphy)); diff --git a/drivers/net/wireless/iwlwifi/iwl-led.h b/drivers/net/wireless/iwlwifi/iwl-led.h index 1c93dfef693..2550b3c7dcb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.h +++ b/drivers/net/wireless/iwlwifi/iwl-led.h @@ -36,20 +36,6 @@ struct iwl_priv; #define IWL_LED_ACTIVITY (0<<1) #define IWL_LED_LINK (1<<1) -/* - * LED mode - * IWL_LED_DEFAULT: use device default - * IWL_LED_RF_STATE: turn LED on/off based on RF state - * LED ON = RF ON - * LED OFF = RF OFF - * IWL_LED_BLINK: adjust led blink rate based on blink table - */ -enum iwl_led_mode { - IWL_LED_DEFAULT, - IWL_LED_RF_STATE, - IWL_LED_BLINK, -}; - void iwlagn_led_enable(struct iwl_priv *priv); void iwl_leds_init(struct iwl_priv *priv); void iwl_leds_exit(struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlwifi/iwl-mac80211.c b/drivers/net/wireless/iwlwifi/iwl-mac80211.c new file mode 100644 index 00000000000..f980e574e1f --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-mac80211.c @@ -0,0 +1,1601 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> + +#include <net/mac80211.h> + +#include <asm/div64.h> + +#include "iwl-eeprom.h" +#include "iwl-wifi.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-agn-calib.h" +#include "iwl-agn.h" +#include "iwl-shared.h" +#include "iwl-bus.h" +#include "iwl-trans.h" + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +static const struct ieee80211_iface_limit iwlagn_sta_ap_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_2sta_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_p2p_sta_go_limits[] = { + { + .max = 1, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_limit iwlagn_p2p_2sta_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION), + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT), + }, +}; + +static const struct ieee80211_iface_combination +iwlagn_iface_combinations_dualmode[] = { + { .num_different_channels = 1, + .max_interfaces = 2, + .beacon_int_infra_match = true, + .limits = iwlagn_sta_ap_limits, + .n_limits = ARRAY_SIZE(iwlagn_sta_ap_limits), + }, + { .num_different_channels = 1, + .max_interfaces = 2, + .limits = iwlagn_2sta_limits, + .n_limits = ARRAY_SIZE(iwlagn_2sta_limits), + }, +}; + +static const struct ieee80211_iface_combination +iwlagn_iface_combinations_p2p[] = { + { .num_different_channels = 1, + .max_interfaces = 2, + .beacon_int_infra_match = true, + .limits = iwlagn_p2p_sta_go_limits, + .n_limits = ARRAY_SIZE(iwlagn_p2p_sta_go_limits), + }, + { .num_different_channels = 1, + .max_interfaces = 2, + .limits = iwlagn_p2p_2sta_limits, + .n_limits = ARRAY_SIZE(iwlagn_p2p_2sta_limits), + }, +}; + +/* + * Not a mac80211 entry point function, but it fits in with all the + * other mac80211 functions grouped here. + */ +int iwlagn_mac_setup_register(struct iwl_priv *priv, + struct iwlagn_ucode_capabilities *capa) +{ + int ret; + struct ieee80211_hw *hw = priv->hw; + struct iwl_rxon_context *ctx; + + hw->rate_control_algorithm = "iwl-agn-rs"; + + /* Tell mac80211 our characteristics */ + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_NEED_DTIM_PERIOD | + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_REPORTS_TX_ACK_STATUS; + + /* + * Including the following line will crash some AP's. This + * workaround removes the stimulus which causes the crash until + * the AP software can be fixed. + hw->max_tx_aggregation_subframes = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + */ + + hw->flags |= IEEE80211_HW_SUPPORTS_PS | + IEEE80211_HW_SUPPORTS_DYNAMIC_PS; + + if (cfg(priv)->sku & EEPROM_SKU_CAP_11N_ENABLE) + hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_SUPPORTS_STATIC_SMPS; + + if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) + hw->flags |= IEEE80211_HW_MFP_CAPABLE; + + hw->sta_data_size = sizeof(struct iwl_station_priv); + hw->vif_data_size = sizeof(struct iwl_vif_priv); + + for_each_context(priv, ctx) { + hw->wiphy->interface_modes |= ctx->interface_modes; + hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; + } + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + + if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT)) { + hw->wiphy->iface_combinations = iwlagn_iface_combinations_p2p; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(iwlagn_iface_combinations_p2p); + } else if (hw->wiphy->interface_modes & BIT(NL80211_IFTYPE_AP)) { + hw->wiphy->iface_combinations = + iwlagn_iface_combinations_dualmode; + hw->wiphy->n_iface_combinations = + ARRAY_SIZE(iwlagn_iface_combinations_dualmode); + } + + hw->wiphy->max_remain_on_channel_duration = 1000; + + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | + WIPHY_FLAG_DISABLE_BEACON_HINTS | + WIPHY_FLAG_IBSS_RSN; + + if (trans(priv)->ucode_wowlan.code.len && + device_can_wakeup(bus(priv)->dev)) { + hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE; + if (!iwlagn_mod_params.sw_crypto) + hw->wiphy->wowlan.flags |= + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE; + + hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS; + hw->wiphy->wowlan.pattern_min_len = + IWLAGN_WOWLAN_MIN_PATTERN_LEN; + hw->wiphy->wowlan.pattern_max_len = + IWLAGN_WOWLAN_MAX_PATTERN_LEN; + } + + if (iwlagn_mod_params.power_save) + hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + else + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + /* we create the 802.11 header and a zero-length SSID element */ + hw->wiphy->max_scan_ie_len = capa->max_probe_length - 24 - 2; + + /* Default value; 4 EDCA QOS priorities */ + hw->queues = 4; + + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + + if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->bands[IEEE80211_BAND_2GHZ]; + if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &priv->bands[IEEE80211_BAND_5GHZ]; + + hw->wiphy->hw_version = bus_get_hw_id(bus(priv)); + + iwl_leds_init(priv); + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); + return ret; + } + priv->mac80211_registered = 1; + + return 0; +} + +void iwlagn_mac_unregister(struct iwl_priv *priv) +{ + if (!priv->mac80211_registered) + return; + iwl_leds_exit(priv); + ieee80211_unregister_hw(priv->hw); + priv->mac80211_registered = 0; +} + +static int __iwl_up(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int ret; + + lockdep_assert_held(&priv->shrd->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) { + IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + for_each_context(priv, ctx) { + ret = iwlagn_alloc_bcast_station(priv, ctx); + if (ret) { + iwl_dealloc_bcast_stations(priv); + return ret; + } + } + + ret = iwl_run_init_ucode(trans(priv)); + if (ret) { + IWL_ERR(priv, "Failed to run INIT ucode: %d\n", ret); + goto error; + } + + ret = iwl_load_ucode_wait_alive(trans(priv), IWL_UCODE_REGULAR); + if (ret) { + IWL_ERR(priv, "Failed to start RT ucode: %d\n", ret); + goto error; + } + + ret = iwl_alive_start(priv); + if (ret) + goto error; + return 0; + + error: + set_bit(STATUS_EXIT_PENDING, &priv->shrd->status); + __iwl_down(priv); + clear_bit(STATUS_EXIT_PENDING, &priv->shrd->status); + + IWL_ERR(priv, "Unable to initialize device.\n"); + return ret; +} + +static int iwlagn_mac_start(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&priv->shrd->mutex); + ret = __iwl_up(priv); + mutex_unlock(&priv->shrd->mutex); + if (ret) + return ret; + + IWL_DEBUG_INFO(priv, "Start UP work done.\n"); + + /* Now we should be done, and the READY bit should be set. */ + if (WARN_ON(!test_bit(STATUS_READY, &priv->shrd->status))) + ret = -EIO; + + iwlagn_led_enable(priv); + + priv->is_open = 1; + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +static void iwlagn_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!priv->is_open) + return; + + priv->is_open = 0; + + iwl_down(priv); + + flush_workqueue(priv->shrd->workqueue); + + /* User space software may expect getting rfkill changes + * even if interface is down */ + iwl_write32(bus(priv), CSR_INT, 0xFFFFFFFF); + iwl_enable_rfkill_int(priv); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct iwl_priv *priv = hw->priv; + + if (iwlagn_mod_params.sw_crypto) + return; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif) + goto out; + + memcpy(priv->kek, data->kek, NL80211_KEK_LEN); + memcpy(priv->kck, data->kck, NL80211_KCK_LEN); + priv->replay_ctr = + cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr)); + priv->have_rekey_data = true; + + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +#ifdef CONFIG_PM_SLEEP + +static int iwlagn_mac_suspend(struct ieee80211_hw *hw, + struct cfg80211_wowlan *wowlan) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + int ret; + + if (WARN_ON(!wowlan)) + return -EINVAL; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + /* Don't attempt WoWLAN when not associated, tear down instead. */ + if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION || + !iwl_is_associated_ctx(ctx)) { + ret = 1; + goto out; + } + + ret = iwlagn_suspend(priv, hw, wowlan); + if (ret) + goto error; + + device_set_wakeup_enable(bus(priv)->dev, true); + + /* Now let the ucode operate on its own */ + iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + goto out; + + error: + priv->shrd->wowlan = false; + iwlagn_prepare_restart(priv); + ieee80211_restart_hw(priv->hw); + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static int iwlagn_mac_resume(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct ieee80211_vif *vif; + unsigned long flags; + u32 base, status = 0xffffffff; + int ret = -EIO; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + iwl_write32(bus(priv), CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE); + + base = priv->shrd->device_pointers.error_event_table; + if (iwlagn_hw_valid_rtc_data_addr(base)) { + spin_lock_irqsave(&bus(priv)->reg_lock, flags); + ret = iwl_grab_nic_access_silent(bus(priv)); + if (ret == 0) { + iwl_write32(bus(priv), HBUS_TARG_MEM_RADDR, base); + status = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); + iwl_release_nic_access(bus(priv)); + } + spin_unlock_irqrestore(&bus(priv)->reg_lock, flags); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (ret == 0) { + struct iwl_trans *trans = trans(priv); + if (!priv->wowlan_sram) + priv->wowlan_sram = + kzalloc(trans->ucode_wowlan.data.len, + GFP_KERNEL); + + if (priv->wowlan_sram) + _iwl_read_targ_mem_words( + bus(priv), 0x800000, priv->wowlan_sram, + trans->ucode_wowlan.data.len / 4); + } +#endif + } + + /* we'll clear ctx->vif during iwlagn_prepare_restart() */ + vif = ctx->vif; + + priv->shrd->wowlan = false; + + device_set_wakeup_enable(bus(priv)->dev, false); + + iwlagn_prepare_restart(priv); + + memset((void *)&ctx->active, 0, sizeof(ctx->active)); + iwl_connection_init_rx_config(priv, ctx); + iwlagn_set_rxon_chain(priv, ctx); + + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + ieee80211_resume_disconnect(vif); + + return 1; +} + +#endif + +static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, + ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); + + if (iwlagn_tx_skb(priv, skb)) + dev_kfree_skb_any(skb); +} + +static void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) +{ + struct iwl_priv *priv = hw->priv; + + iwl_update_tkip_key(priv, vif, keyconf, sta, iv32, phase1key); +} + +static int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int ret; + bool is_default_wep_key = false; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (iwlagn_mod_params.sw_crypto) { + IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + /* fall through */ + case WLAN_CIPHER_SUITE_CCMP: + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + break; + default: + break; + } + + /* + * We could program these keys into the hardware as well, but we + * don't expect much multicast traffic in IBSS and having keys + * for more stations is probably more useful. + * + * Mark key TX-only and return 0. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + key->hw_key_idx = WEP_INVALID_OFFSET; + return 0; + } + + /* If they key was TX-only, accept deletion */ + if (cmd == DISABLE_KEY && key->hw_key_idx == WEP_INVALID_OFFSET) + return 0; + + mutex_lock(&priv->shrd->mutex); + iwl_scan_cancel_timeout(priv, 100); + + BUILD_BUG_ON(WEP_INVALID_OFFSET == IWLAGN_HW_KEY_DEFAULT); + + /* + * If we are getting WEP group key and we didn't receive any key mapping + * so far, we are in legacy wep mode (group key only), otherwise we are + * in 1X mode. + * In legacy wep mode, we use another host command to the uCode. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && !sta) { + if (cmd == SET_KEY) + is_default_wep_key = !ctx->key_mapping_keys; + else + is_default_wep_key = + key->hw_key_idx == IWLAGN_HW_KEY_DEFAULT; + } + + + switch (cmd) { + case SET_KEY: + if (is_default_wep_key) { + ret = iwl_set_default_wep_key(priv, vif_priv->ctx, key); + break; + } + ret = iwl_set_dynamic_key(priv, vif_priv->ctx, key, sta); + if (ret) { + /* + * can't add key for RX, but we don't need it + * in the device for TX so still return 0 + */ + ret = 0; + key->hw_key_idx = WEP_INVALID_OFFSET; + } + + IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (is_default_wep_key) + ret = iwl_remove_default_wep_key(priv, ctx, key); + else + ret = iwl_remove_dynamic_key(priv, ctx, key, sta); + + IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct iwl_priv *priv = hw->priv; + int ret = -EINVAL; + struct iwl_station_priv *sta_priv = (void *) sta->drv_priv; + + IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", + sta->addr, tid); + + if (!(cfg(priv)->sku & EEPROM_SKU_CAP_11N_ENABLE)) + return -EACCES; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_RXAGG) + break; + IWL_DEBUG_HT(priv, "start Rx\n"); + ret = iwl_sta_rx_agg_start(priv, sta, tid, *ssn); + break; + case IEEE80211_AMPDU_RX_STOP: + IWL_DEBUG_HT(priv, "stop Rx\n"); + ret = iwl_sta_rx_agg_stop(priv, sta, tid); + if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) + ret = 0; + break; + case IEEE80211_AMPDU_TX_START: + if (iwlagn_mod_params.disable_11n & IWL_DISABLE_HT_TXAGG) + break; + IWL_DEBUG_HT(priv, "start Tx\n"); + ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); + break; + case IEEE80211_AMPDU_TX_STOP: + IWL_DEBUG_HT(priv, "stop Tx\n"); + ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); + if ((ret == 0) && (priv->agg_tids_count > 0)) { + priv->agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->agg_tids_count = %u\n", + priv->agg_tids_count); + } + if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) + ret = 0; + if (!priv->agg_tids_count && cfg(priv)->ht_params && + cfg(priv)->ht_params->use_rts_for_aggregation) { + /* + * switch off RTS/CTS if it was previously enabled + */ + sta_priv->lq_sta.lq.general_params.flags &= + ~LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK; + iwl_send_lq_cmd(priv, iwl_rxon_ctx_from_vif(vif), + &sta_priv->lq_sta.lq, CMD_ASYNC, false); + } + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = iwlagn_tx_agg_oper(priv, vif, sta, tid, buf_size); + break; + } + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + return ret; +} + +static int iwlagn_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + bool is_ap = vif->type == NL80211_IFTYPE_STATION; + int ret = 0; + u8 sta_id; + + IWL_DEBUG_MAC80211(priv, "received request to add station %pM\n", + sta->addr); + mutex_lock(&priv->shrd->mutex); + IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", + sta->addr); + sta_priv->sta_id = IWL_INVALID_STATION; + + atomic_set(&sta_priv->pending_frames, 0); + if (vif->type == NL80211_IFTYPE_AP) + sta_priv->client = true; + + ret = iwl_add_station_common(priv, vif_priv->ctx, sta->addr, + is_ap, sta, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + goto out; + } + + sta_priv->sta_id = sta_id; + + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n", + sta->addr); + iwl_rs_rate_init(priv, sta, sta_id); + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_channel_switch *ch_switch) +{ + struct iwl_priv *priv = hw->priv; + const struct iwl_channel_info *ch_info; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = ch_switch->channel; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + /* + * MULTI-FIXME + * When we add support for multiple interfaces, we need to + * revisit this. The channel switch command in the device + * only affects the BSS context, but what does that really + * mean? And what if we get a CSA on the second interface? + * This needs a lot of work. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u16 ch; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->shrd->mutex); + + if (iwl_is_rfkill(priv->shrd)) + goto out; + + if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status) || + test_bit(STATUS_SCANNING, &priv->shrd->status) || + test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status)) + goto out; + + if (!iwl_is_associated_ctx(ctx)) + goto out; + + if (!cfg(priv)->lib->set_channel_switch) + goto out; + + ch = channel->hw_value; + if (le16_to_cpu(ctx->active.channel) == ch) + goto out; + + ch_info = iwl_get_channel_info(priv, channel->band, ch); + if (!is_channel_valid(ch_info)) { + IWL_DEBUG_MAC80211(priv, "invalid channel\n"); + goto out; + } + + spin_lock_irq(&priv->shrd->lock); + + priv->current_ht_config.smps = conf->smps_mode; + + /* Configure HT40 channels */ + ctx->ht.enabled = conf_is_ht(conf); + if (ctx->ht.enabled) + iwlagn_config_ht40(conf, ctx); + else + ctx->ht.is_40mhz = false; + + if ((le16_to_cpu(ctx->staging.channel) != ch)) + ctx->staging.flags = 0; + + iwl_set_rxon_channel(priv, channel, ctx); + iwl_set_rxon_ht(priv, ht_conf); + iwl_set_flags_for_band(priv, ctx, channel->band, ctx->vif); + + spin_unlock_irq(&priv->shrd->lock); + + iwl_set_rate(priv); + /* + * at this point, staging_rxon has the + * configuration for channel switch + */ + set_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status); + priv->switch_channel = cpu_to_le16(ch); + if (cfg(priv)->lib->set_channel_switch(priv, ch_switch)) { + clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->shrd->status); + priv->switch_channel = 0; + ieee80211_chswitch_done(ctx->vif, false); + } + +out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static void iwlagn_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_priv *priv = hw->priv; + __le32 filter_or = 0, filter_nand = 0; + struct iwl_rxon_context *ctx; + +#define CHK(test, flag) do { \ + if (*total_flags & (test)) \ + filter_or |= (flag); \ + else \ + filter_nand |= (flag); \ + } while (0) + + IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", + changed_flags, *total_flags); + + CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); + /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ + CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); + CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); + +#undef CHK + + mutex_lock(&priv->shrd->mutex); + + for_each_context(priv, ctx) { + ctx->staging.filter_flags &= ~filter_nand; + ctx->staging.filter_flags |= filter_or; + + /* + * Not committing directly because hardware can perform a scan, + * but we'll eventually commit the filter flags change anyway. + */ + } + + mutex_unlock(&priv->shrd->mutex); + + /* + * Receiving all multicast frames is always enabled by the + * default flags setup in iwl_connection_init_rx_config() + * since we currently do not support programming multicast + * filters into the device. + */ + *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) +{ + struct iwl_priv *priv = hw->priv; + + mutex_lock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (test_bit(STATUS_EXIT_PENDING, &priv->shrd->status)) { + IWL_DEBUG_TX(priv, "Aborting flush due to device shutdown\n"); + goto done; + } + if (iwl_is_rfkill(priv->shrd)) { + IWL_DEBUG_TX(priv, "Aborting flush due to RF Kill\n"); + goto done; + } + + /* + * mac80211 will not push any more frames for transmit + * until the flush is completed + */ + if (drop) { + IWL_DEBUG_MAC80211(priv, "send flush command\n"); + if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + IWL_ERR(priv, "flush request fail\n"); + goto done; + } + } + IWL_DEBUG_MAC80211(priv, "wait transmit/flush all frames\n"); + iwl_trans_wait_tx_queue_empty(trans(priv)); +done: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_channel *channel, + enum nl80211_channel_type channel_type, + int duration) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_PAN]; + int err = 0; + + if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN))) + return -EOPNOTSUPP; + + if (!(ctx->interface_modes & BIT(NL80211_IFTYPE_P2P_CLIENT))) + return -EOPNOTSUPP; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + if (test_bit(STATUS_SCAN_HW, &priv->shrd->status)) { + err = -EBUSY; + goto out; + } + + priv->hw_roc_channel = channel; + priv->hw_roc_chantype = channel_type; + /* convert from ms to TU */ + priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); + priv->hw_roc_start_notified = false; + cancel_delayed_work(&priv->hw_roc_disable_work); + + if (!ctx->is_active) { + static const struct iwl_qos_info default_qos_data = { + .def_qos_parm = { + .ac[0] = { + .cw_min = cpu_to_le16(3), + .cw_max = cpu_to_le16(7), + .aifsn = 2, + .edca_txop = cpu_to_le16(1504), + }, + .ac[1] = { + .cw_min = cpu_to_le16(7), + .cw_max = cpu_to_le16(15), + .aifsn = 2, + .edca_txop = cpu_to_le16(3008), + }, + .ac[2] = { + .cw_min = cpu_to_le16(15), + .cw_max = cpu_to_le16(1023), + .aifsn = 3, + }, + .ac[3] = { + .cw_min = cpu_to_le16(15), + .cw_max = cpu_to_le16(1023), + .aifsn = 7, + }, + }, + }; + + ctx->is_active = true; + ctx->qos_data = default_qos_data; + ctx->staging.dev_type = RXON_DEV_TYPE_P2P; + memcpy(ctx->staging.node_addr, + priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, + ETH_ALEN); + memcpy(ctx->staging.bssid_addr, + priv->contexts[IWL_RXON_CTX_BSS].staging.node_addr, + ETH_ALEN); + err = iwlagn_commit_rxon(priv, ctx); + if (err) + goto out; + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK | + RXON_FILTER_PROMISC_MSK | + RXON_FILTER_CTL2HOST_MSK; + + err = iwlagn_commit_rxon(priv, ctx); + if (err) { + iwlagn_disable_roc(priv); + goto out; + } + priv->hw_roc_setup = true; + } + + err = iwl_scan_initiate(priv, ctx->vif, IWL_SCAN_ROC, channel->band); + if (err) + iwlagn_disable_roc(priv); + + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return err; +} + +static int iwlagn_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + + if (!(priv->shrd->valid_contexts & BIT(IWL_RXON_CTX_PAN))) + return -EOPNOTSUPP; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + iwl_scan_cancel_timeout(priv, priv->hw_roc_duration); + iwlagn_disable_roc(priv); + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return 0; +} + +static int iwlagn_mac_tx_sync(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *bssid, + enum ieee80211_tx_sync_type type) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int ret; + u8 sta_id; + + if (ctx->ctxid != IWL_RXON_CTX_PAN) + return 0; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + if (iwl_is_associated_ctx(ctx)) { + ret = 0; + goto out; + } + + if (ctx->preauth_bssid || test_bit(STATUS_SCAN_HW, + &priv->shrd->status)) { + ret = -EBUSY; + goto out; + } + + ret = iwl_add_station_common(priv, ctx, bssid, true, NULL, &sta_id); + if (ret) + goto out; + + if (WARN_ON(sta_id != ctx->ap_sta_id)) { + ret = -EIO; + goto out_remove_sta; + } + + memcpy(ctx->bssid, bssid, ETH_ALEN); + ctx->preauth_bssid = true; + + ret = iwlagn_commit_rxon(priv, ctx); + + if (ret == 0) + goto out; + + out_remove_sta: + iwl_remove_station(priv, sta_id, bssid); + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwlagn_mac_finish_tx_sync(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + const u8 *bssid, + enum ieee80211_tx_sync_type type) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + + if (ctx->ctxid != IWL_RXON_CTX_PAN) + return; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + if (iwl_is_associated_ctx(ctx)) + goto out; + + iwl_remove_station(priv, ctx->ap_sta_id, bssid); + ctx->preauth_bssid = false; + /* no need to commit */ + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static void iwlagn_mac_rssi_callback(struct ieee80211_hw *hw, + enum ieee80211_rssi_event rssi_event) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + mutex_lock(&priv->shrd->mutex); + + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) { + if (rssi_event == RSSI_EVENT_LOW) + priv->bt_enable_pspoll = true; + else if (rssi_event == RSSI_EVENT_HIGH) + priv->bt_enable_pspoll = false; + + iwlagn_send_advance_bt_config(priv); + } else { + IWL_DEBUG_MAC80211(priv, "Advanced BT coex disabled," + "ignoring RSSI callback\n"); + } + + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +static int iwlagn_mac_set_tim(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, bool set) +{ + struct iwl_priv *priv = hw->priv; + + queue_work(priv->shrd->workqueue, &priv->beacon_update); + + return 0; +} + +static int iwlagn_mac_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + unsigned long flags; + int q; + + if (WARN_ON(!ctx)) + return -EINVAL; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!iwl_is_ready_rf(priv->shrd)) { + IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); + return -EIO; + } + + if (queue >= AC_NUM) { + IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); + return 0; + } + + q = AC_NUM - 1 - queue; + + spin_lock_irqsave(&priv->shrd->lock, flags); + + ctx->qos_data.def_qos_parm.ac[q].cw_min = + cpu_to_le16(params->cw_min); + ctx->qos_data.def_qos_parm.ac[q].cw_max = + cpu_to_le16(params->cw_max); + ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + ctx->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->txop * 32)); + + ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; + + spin_unlock_irqrestore(&priv->shrd->lock, flags); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +static int iwlagn_mac_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + + return priv->ibss_manager == IWL_IBSS_MANAGER; +} + +static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + iwl_connection_init_rx_config(priv, ctx); + + iwlagn_set_rxon_chain(priv, ctx); + + return iwlagn_commit_rxon(priv, ctx); +} + +static int iwl_setup_interface(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct ieee80211_vif *vif = ctx->vif; + int err; + + lockdep_assert_held(&priv->shrd->mutex); + + /* + * This variable will be correct only when there's just + * a single context, but all code using it is for hardware + * that supports only one context. + */ + priv->iw_mode = vif->type; + + ctx->is_active = true; + + err = iwl_set_mode(priv, ctx); + if (err) { + if (!ctx->always_active) + ctx->is_active = false; + return err; + } + + if (cfg(priv)->bt_params && cfg(priv)->bt_params->advanced_bt_coexist && + vif->type == NL80211_IFTYPE_ADHOC) { + /* + * pretend to have high BT traffic as long as we + * are operating in IBSS mode, as this will cause + * the rate scaling etc. to behave as intended. + */ + priv->bt_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_HIGH; + } + + return 0; +} + +static int iwlagn_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *tmp, *ctx = NULL; + int err; + enum nl80211_iftype viftype = ieee80211_vif_type_p2p(vif); + + IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", + viftype, vif->addr); + + cancel_delayed_work_sync(&priv->hw_roc_disable_work); + + mutex_lock(&priv->shrd->mutex); + + iwlagn_disable_roc(priv); + + if (!iwl_is_ready_rf(priv->shrd)) { + IWL_WARN(priv, "Try to add interface when device not ready\n"); + err = -EINVAL; + goto out; + } + + for_each_context(priv, tmp) { + u32 possible_modes = + tmp->interface_modes | tmp->exclusive_interface_modes; + + if (tmp->vif) { + /* check if this busy context is exclusive */ + if (tmp->exclusive_interface_modes & + BIT(tmp->vif->type)) { + err = -EINVAL; + goto out; + } + continue; + } + + if (!(possible_modes & BIT(viftype))) + continue; + + /* have maybe usable context w/o interface */ + ctx = tmp; + break; + } + + if (!ctx) { + err = -EOPNOTSUPP; + goto out; + } + + vif_priv->ctx = ctx; + ctx->vif = vif; + + err = iwl_setup_interface(priv, ctx); + if (!err) + goto out; + + ctx->vif = NULL; + priv->iw_mode = NL80211_IFTYPE_STATION; + out: + mutex_unlock(&priv->shrd->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return err; +} + +static void iwl_teardown_interface(struct iwl_priv *priv, + struct ieee80211_vif *vif, + bool mode_change) +{ + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + lockdep_assert_held(&priv->shrd->mutex); + + if (priv->scan_vif == vif) { + iwl_scan_cancel_timeout(priv, 200); + iwl_force_scan_end(priv); + } + + if (!mode_change) { + iwl_set_mode(priv, ctx); + if (!ctx->always_active) + ctx->is_active = false; + } + + /* + * When removing the IBSS interface, overwrite the + * BT traffic load with the stored one from the last + * notification, if any. If this is a device that + * doesn't implement this, this has no effect since + * both values are the same and zero. + */ + if (vif->type == NL80211_IFTYPE_ADHOC) + priv->bt_traffic_load = priv->last_bt_traffic_load; +} + +static void iwlagn_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->shrd->mutex); + + if (WARN_ON(ctx->vif != vif)) { + struct iwl_rxon_context *tmp; + IWL_ERR(priv, "ctx->vif = %p, vif = %p\n", ctx->vif, vif); + for_each_context(priv, tmp) + IWL_ERR(priv, "\tID = %d:\tctx = %p\tctx->vif = %p\n", + tmp->ctxid, tmp, tmp->vif); + } + ctx->vif = NULL; + + iwl_teardown_interface(priv, vif, false); + + mutex_unlock(&priv->shrd->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + +} + +static int iwlagn_mac_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + struct iwl_rxon_context *bss_ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_rxon_context *tmp; + enum nl80211_iftype newviftype = newtype; + u32 interface_modes; + int err; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + newtype = ieee80211_iftype_p2p(newtype, newp2p); + + mutex_lock(&priv->shrd->mutex); + + if (!ctx->vif || !iwl_is_ready_rf(priv->shrd)) { + /* + * Huh? But wait ... this can maybe happen when + * we're in the middle of a firmware restart! + */ + err = -EBUSY; + goto out; + } + + interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; + + if (!(interface_modes & BIT(newtype))) { + err = -EBUSY; + goto out; + } + + /* + * Refuse a change that should be done by moving from the PAN + * context to the BSS context instead, if the BSS context is + * available and can support the new interface type. + */ + if (ctx->ctxid == IWL_RXON_CTX_PAN && !bss_ctx->vif && + (bss_ctx->interface_modes & BIT(newtype) || + bss_ctx->exclusive_interface_modes & BIT(newtype))) { + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 2); + err = -EBUSY; + goto out; + } + + if (ctx->exclusive_interface_modes & BIT(newtype)) { + for_each_context(priv, tmp) { + if (ctx == tmp) + continue; + + if (!tmp->vif) + continue; + + /* + * The current mode switch would be exclusive, but + * another context is active ... refuse the switch. + */ + err = -EBUSY; + goto out; + } + } + + /* success */ + iwl_teardown_interface(priv, vif, true); + vif->type = newviftype; + vif->p2p = newp2p; + err = iwl_setup_interface(priv, ctx); + WARN_ON(err); + /* + * We've switched internally, but submitting to the + * device may have failed for some reason. Mask this + * error, because otherwise mac80211 will not switch + * (and set the interface type back) and we'll be + * out of sync with it. + */ + err = 0; + + out: + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return err; +} + +static int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct iwl_priv *priv = hw->priv; + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (req->n_channels == 0) + return -EINVAL; + + mutex_lock(&priv->shrd->mutex); + + /* + * If an internal scan is in progress, just set + * up the scan_request as per above. + */ + if (priv->scan_type != IWL_SCAN_NORMAL) { + IWL_DEBUG_SCAN(priv, + "SCAN request during internal scan - defer\n"); + priv->scan_request = req; + priv->scan_vif = vif; + ret = 0; + } else { + priv->scan_request = req; + priv->scan_vif = vif; + /* + * mac80211 will only ask for one band at a time + * so using channels[0] here is ok + */ + ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, + req->channels[0]->band); + if (ret) { + priv->scan_request = NULL; + priv->scan_vif = NULL; + } + } + + IWL_DEBUG_MAC80211(priv, "leave\n"); + + mutex_unlock(&priv->shrd->mutex); + + return ret; +} + +static int iwlagn_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int ret; + + IWL_DEBUG_MAC80211(priv, "enter: received request to remove " + "station %pM\n", sta->addr); + mutex_lock(&priv->shrd->mutex); + IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", + sta->addr); + ret = iwl_remove_station(priv, sta_priv->sta_id, sta->addr); + if (ret) + IWL_DEBUG_QUIET_RFKILL(priv, "Error removing station %pM\n", + sta->addr); + mutex_unlock(&priv->shrd->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +static void iwl_sta_modify_ps_wake(struct iwl_priv *priv, int sta_id) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->shrd->sta_lock, flags); + priv->stations[sta_id].sta.station_flags &= ~STA_FLG_PWR_SAVE_MSK; + priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; + priv->stations[sta_id].sta.sta.modify_mask = 0; + priv->stations[sta_id].sta.sleep_tx_count = 0; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); + spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); + +} + +static void iwlagn_mac_sta_notify(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum sta_notify_cmd cmd, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + int sta_id; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + switch (cmd) { + case STA_NOTIFY_SLEEP: + WARN_ON(!sta_priv->client); + sta_priv->asleep = true; + if (atomic_read(&sta_priv->pending_frames) > 0) + ieee80211_sta_block_awake(hw, sta, true); + break; + case STA_NOTIFY_AWAKE: + WARN_ON(!sta_priv->client); + if (!sta_priv->asleep) + break; + sta_priv->asleep = false; + sta_id = iwl_sta_id(sta); + if (sta_id != IWL_INVALID_STATION) + iwl_sta_modify_ps_wake(priv, sta_id); + break; + default: + break; + } + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +struct ieee80211_ops iwlagn_hw_ops = { + .tx = iwlagn_mac_tx, + .start = iwlagn_mac_start, + .stop = iwlagn_mac_stop, +#ifdef CONFIG_PM_SLEEP + .suspend = iwlagn_mac_suspend, + .resume = iwlagn_mac_resume, +#endif + .add_interface = iwlagn_mac_add_interface, + .remove_interface = iwlagn_mac_remove_interface, + .change_interface = iwlagn_mac_change_interface, + .config = iwlagn_mac_config, + .configure_filter = iwlagn_configure_filter, + .set_key = iwlagn_mac_set_key, + .update_tkip_key = iwlagn_mac_update_tkip_key, + .set_rekey_data = iwlagn_mac_set_rekey_data, + .conf_tx = iwlagn_mac_conf_tx, + .bss_info_changed = iwlagn_bss_info_changed, + .ampdu_action = iwlagn_mac_ampdu_action, + .hw_scan = iwlagn_mac_hw_scan, + .sta_notify = iwlagn_mac_sta_notify, + .sta_add = iwlagn_mac_sta_add, + .sta_remove = iwlagn_mac_sta_remove, + .channel_switch = iwlagn_mac_channel_switch, + .flush = iwlagn_mac_flush, + .tx_last_beacon = iwlagn_mac_tx_last_beacon, + .remain_on_channel = iwlagn_mac_remain_on_channel, + .cancel_remain_on_channel = iwlagn_mac_cancel_remain_on_channel, + .rssi_callback = iwlagn_mac_rssi_callback, + CFG80211_TESTMODE_CMD(iwlagn_mac_testmode_cmd) + CFG80211_TESTMODE_DUMP(iwlagn_mac_testmode_dump) + .tx_sync = iwlagn_mac_tx_sync, + .finish_tx_sync = iwlagn_mac_finish_tx_sync, + .set_tim = iwlagn_mac_set_tim, +}; + +/* This function both allocates and initializes hw and priv. */ +struct ieee80211_hw *iwl_alloc_all(void) +{ + struct iwl_priv *priv; + /* mac80211 allocates memory for this device instance, including + * space for this driver's private structure */ + struct ieee80211_hw *hw; + + hw = ieee80211_alloc_hw(sizeof(struct iwl_priv), &iwlagn_hw_ops); + if (!hw) + goto out; + + priv = hw->priv; + priv->hw = hw; + +out: + return hw; +} diff --git a/drivers/net/wireless/iwlwifi/iwl-pci.c b/drivers/net/wireless/iwlwifi/iwl-pci.c index 1800029911a..fb30ea7ca96 100644 --- a/drivers/net/wireless/iwlwifi/iwl-pci.c +++ b/drivers/net/wireless/iwlwifi/iwl-pci.c @@ -135,7 +135,7 @@ static void iwl_pci_apm_config(struct iwl_bus *bus) } } -static void iwl_pci_get_hw_id(struct iwl_bus *bus, char buf[], +static void iwl_pci_get_hw_id_string(struct iwl_bus *bus, char buf[], int buf_len) { struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus); @@ -144,6 +144,13 @@ static void iwl_pci_get_hw_id(struct iwl_bus *bus, char buf[], pci_dev->subsystem_device); } +static u32 iwl_pci_get_hw_id(struct iwl_bus *bus) +{ + struct pci_dev *pci_dev = IWL_BUS_GET_PCI_DEV(bus); + + return (pci_dev->device << 16) + pci_dev->subsystem_device; +} + static void iwl_pci_write8(struct iwl_bus *bus, u32 ofs, u8 val) { iowrite8(val, IWL_BUS_GET_PCI_BUS(bus)->hw_base + ofs); @@ -163,6 +170,7 @@ static u32 iwl_pci_read32(struct iwl_bus *bus, u32 ofs) static const struct iwl_bus_ops bus_ops_pci = { .get_pm_support = iwl_pci_is_pm_supported, .apm_config = iwl_pci_apm_config, + .get_hw_id_string = iwl_pci_get_hw_id_string, .get_hw_id = iwl_pci_get_hw_id, .write8 = iwl_pci_write8, .write32 = iwl_pci_write32, @@ -256,6 +264,8 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, {IWL_PCI_DEVICE(0x0082, 0x1341, iwl6005_2agn_d_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_cfg)},/* low 5GHz active */ + {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_cfg)},/* high 5GHz active */ /* 6x30 Series */ {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, @@ -325,46 +335,28 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4026, iwl2000_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0891, 0x4226, iwl2000_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4426, iwl2000_2bg_cfg)}, {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, /* 2x30 Series */ {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0887, 0x4066, iwl2030_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0888, 0x4266, iwl2030_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0887, 0x4466, iwl2030_2bg_cfg)}, /* 6x35 Series */ {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4064, iwl6035_2abg_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x4264, iwl6035_2abg_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4464, iwl6035_2abg_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4066, iwl6035_2bg_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x4266, iwl6035_2bg_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4466, iwl6035_2bg_cfg)}, /* 105 Series */ {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0026, iwl105_bg_cfg)}, - {IWL_PCI_DEVICE(0x0895, 0x0226, iwl105_bg_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0426, iwl105_bg_cfg)}, {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, /* 135 Series */ {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0892, 0x0066, iwl135_bg_cfg)}, - {IWL_PCI_DEVICE(0x0893, 0x0266, iwl135_bg_cfg)}, - {IWL_PCI_DEVICE(0x0892, 0x0466, iwl135_bg_cfg)}, {0} }; diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 4eaab204322..2b188a6025b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -167,7 +167,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, u8 skip; u32 slp_itrvl; - if (priv->cfg->adv_pm) { + if (cfg(priv)->adv_pm) { table = apm_range_2; if (period <= IWL_DTIM_RANGE_1_MAX) table = apm_range_1; @@ -221,7 +221,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { - if (!priv->cfg->bt_params->bt_sco_disable) + if (!cfg(priv)->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; @@ -307,7 +307,7 @@ static void iwl_power_fill_sleep_cmd(struct iwl_priv *priv, cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; if (iwl_advanced_bt_coexist(priv)) { - if (!priv->cfg->bt_params->bt_sco_disable) + if (!cfg(priv)->bt_params->bt_sco_disable) cmd->flags |= IWL_POWER_BT_SCO_ENA; else cmd->flags &= ~IWL_POWER_BT_SCO_ENA; @@ -350,7 +350,7 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, if (priv->shrd->wowlan) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper); - else if (!priv->cfg->base_params->no_idle_support && + else if (!cfg(priv)->base_params->no_idle_support && priv->hw->conf.flags & IEEE80211_CONF_IDLE) iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); else if (iwl_tt_is_low_power_state(priv)) { diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index e5d727f537d..084aa2c4ccf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -416,6 +416,8 @@ static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) if (!iwl_is_associated_ctx(ctx)) continue; + if (ctx->staging.dev_type == RXON_DEV_TYPE_P2P) + continue; value = ctx->beacon_int; if (!value) value = IWL_PASSIVE_DWELL_BASE; @@ -678,7 +680,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) priv->contexts[IWL_RXON_CTX_BSS].active.flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; - if (chan_mod == CHANNEL_MODE_PURE_40) { + if ((priv->scan_request && priv->scan_request->no_cck) || + chan_mod == CHANNEL_MODE_PURE_40) { rate = IWL_RATE_6M_PLCP; } else { rate = IWL_RATE_1M_PLCP; @@ -688,8 +691,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) * Internal scans are passive, so we can indiscriminately set * the BT ignore flag on 2.4 GHz since it applies to TX only. */ - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) scan->tx_cmd.tx_flags |= TX_CMD_FLG_IGNORE_BT; break; case IEEE80211_BAND_5GHZ: @@ -730,12 +733,12 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) band = priv->scan_band; - if (priv->cfg->scan_rx_antennas[band]) - rx_ant = priv->cfg->scan_rx_antennas[band]; + if (cfg(priv)->scan_rx_antennas[band]) + rx_ant = cfg(priv)->scan_rx_antennas[band]; if (band == IEEE80211_BAND_2GHZ && - priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist) { /* transmit 2.4 GHz probes only on first antenna */ scan_tx_antennas = first_antenna(scan_tx_antennas); } @@ -759,8 +762,8 @@ static int iwlagn_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) rx_ant = first_antenna(active_chains); } - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist && + if (cfg(priv)->bt_params && + cfg(priv)->bt_params->advanced_bt_coexist && priv->bt_full_concurrent) { /* operated as 1x1 in full concurrency mode */ rx_ant = first_antenna(rx_ant); @@ -938,51 +941,6 @@ int __must_check iwl_scan_initiate(struct iwl_priv *priv, return 0; } -int iwlagn_mac_hw_scan(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct cfg80211_scan_request *req) -{ - struct iwl_priv *priv = hw->priv; - int ret; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - if (req->n_channels == 0) - return -EINVAL; - - mutex_lock(&priv->shrd->mutex); - - /* - * If an internal scan is in progress, just set - * up the scan_request as per above. - */ - if (priv->scan_type != IWL_SCAN_NORMAL) { - IWL_DEBUG_SCAN(priv, - "SCAN request during internal scan - defer\n"); - priv->scan_request = req; - priv->scan_vif = vif; - ret = 0; - } else { - priv->scan_request = req; - priv->scan_vif = vif; - /* - * mac80211 will only ask for one band at a time - * so using channels[0] here is ok - */ - ret = iwl_scan_initiate(priv, vif, IWL_SCAN_NORMAL, - req->channels[0]->band); - if (ret) { - priv->scan_request = NULL; - priv->scan_vif = NULL; - } - } - - IWL_DEBUG_MAC80211(priv, "leave\n"); - - mutex_unlock(&priv->shrd->mutex); - - return ret; -} /* * internal short scan, this function should only been called while associated. diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h index 1f7a93c67c4..dc55cc4a810 100644 --- a/drivers/net/wireless/iwlwifi/iwl-shared.h +++ b/drivers/net/wireless/iwlwifi/iwl-shared.h @@ -94,9 +94,9 @@ * This implementation is iwl-pci.c */ -struct iwl_cfg; struct iwl_bus; struct iwl_priv; +struct iwl_trans; struct iwl_sensitivity_ranges; struct iwl_trans_ops; @@ -107,6 +107,10 @@ struct iwl_trans_ops; extern struct iwl_mod_params iwlagn_mod_params; +#define IWL_DISABLE_HT_ALL BIT(0) +#define IWL_DISABLE_HT_TXAGG BIT(1) +#define IWL_DISABLE_HT_RXAGG BIT(2) + /** * struct iwl_mod_params * @@ -114,13 +118,14 @@ extern struct iwl_mod_params iwlagn_mod_params; * * @sw_crypto: using hardware encryption, default = 0 * @num_of_queues: number of tx queue, HW dependent - * @disable_11n: 11n capabilities enabled, default = 0 + * @disable_11n: disable 11n capabilities, default = 0, + * use IWL_DISABLE_HT_* constants * @amsdu_size_8K: enable 8K amsdu size, default = 1 * @antenna: both antennas (use diversity), default = 0 * @restart_fw: restart firmware, default = 1 * @plcp_check: enable plcp health check, default = true * @ack_check: disable ack health check, default = false - * @wd_disable: enable stuck queue check, default = false + * @wd_disable: enable stuck queue check, default = 0 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 * @no_sleep_autoadjust: disable autoadjust, default = true @@ -135,13 +140,13 @@ extern struct iwl_mod_params iwlagn_mod_params; struct iwl_mod_params { int sw_crypto; int num_of_queues; - int disable_11n; + unsigned int disable_11n; int amsdu_size_8K; int antenna; int restart_fw; bool plcp_check; bool ack_check; - bool wd_disable; + int wd_disable; bool bt_coex_active; int led_mode; bool no_sleep_autoadjust; @@ -174,8 +179,6 @@ struct iwl_mod_params { * @ct_kill_exit_threshold: when to reeable the device - in hw dependent unit * relevant for 1000, 6000 and up * @wd_timeout: TX queues watchdog timeout - * @calib_init_cfg: setup initial calibrations for the hw - * @calib_rt_cfg: setup runtime calibrations for the hw * @struct iwl_sensitivity_ranges: range of sensitivity values */ struct iwl_hw_params { @@ -195,67 +198,148 @@ struct iwl_hw_params { u32 ct_kill_exit_threshold; unsigned int wd_timeout; - u32 calib_init_cfg; - u32 calib_rt_cfg; const struct iwl_sensitivity_ranges *sens; }; /** - * enum iwl_agg_state + * enum iwl_ucode_type * - * The state machine of the BA agreement establishment / tear down. - * These states relate to a specific RA / TID. + * The type of ucode currently loaded on the hardware. * - * @IWL_AGG_OFF: aggregation is not used - * @IWL_AGG_ON: aggregation session is up - * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. - * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. + * @IWL_UCODE_NONE: No ucode loaded + * @IWL_UCODE_REGULAR: Normal runtime ucode + * @IWL_UCODE_INIT: Initial ucode + * @IWL_UCODE_WOWLAN: Wake on Wireless enabled ucode */ -enum iwl_agg_state { - IWL_AGG_OFF = 0, - IWL_AGG_ON, - IWL_EMPTYING_HW_QUEUE_ADDBA, - IWL_EMPTYING_HW_QUEUE_DELBA, +enum iwl_ucode_type { + IWL_UCODE_NONE, + IWL_UCODE_REGULAR, + IWL_UCODE_INIT, + IWL_UCODE_WOWLAN, }; /** - * struct iwl_ht_agg - aggregation state machine - - * This structs holds the states for the BA agreement establishment and tear - * down. It also holds the state during the BA session itself. This struct is - * duplicated for each RA / TID. - - * @rate_n_flags: Rate at which Tx was attempted. Holds the data between the - * Tx response (REPLY_TX), and the block ack notification - * (REPLY_COMPRESSED_BA). - * @state: state of the BA agreement establishment / tear down. - * @txq_id: Tx queue used by the BA session - used by the transport layer. - * Needed by the upper layer for debugfs only. - * @wait_for_ba: Expect block-ack before next Tx reply + * struct iwl_notification_wait - notification wait entry + * @list: list head for global list + * @fn: function called with the notification + * @cmd: command ID + * + * This structure is not used directly, to wait for a + * notification declare it on the stack, and call + * iwlagn_init_notification_wait() with appropriate + * parameters. Then do whatever will cause the ucode + * to notify the driver, and to wait for that then + * call iwlagn_wait_notification(). + * + * Each notification is one-shot. If at some point we + * need to support multi-shot notifications (which + * can't be allocated on the stack) we need to modify + * the code for them. */ -struct iwl_ht_agg { - u32 rate_n_flags; - enum iwl_agg_state state; - u16 txq_id; - bool wait_for_ba; +struct iwl_notification_wait { + struct list_head list; + + void (*fn)(struct iwl_trans *trans, struct iwl_rx_packet *pkt, + void *data); + void *fn_data; + + u8 cmd; + bool triggered, aborted; }; /** - * struct iwl_tid_data - one for each RA / TID + * enum iwl_pa_type - Power Amplifier type + * @IWL_PA_SYSTEM: based on uCode configuration + * @IWL_PA_INTERNAL: use Internal only + */ +enum iwl_pa_type { + IWL_PA_SYSTEM = 0, + IWL_PA_INTERNAL = 1, +}; - * This structs holds the states for each RA / TID. +/* + * LED mode + * IWL_LED_DEFAULT: use device default + * IWL_LED_RF_STATE: turn LED on/off based on RF state + * LED ON = RF ON + * LED OFF = RF OFF + * IWL_LED_BLINK: adjust led blink rate based on blink table + */ +enum iwl_led_mode { + IWL_LED_DEFAULT, + IWL_LED_RF_STATE, + IWL_LED_BLINK, +}; - * @seq_number: the next WiFi sequence number to use - * @tfds_in_queue: number of packets sent to the HW queues. - * Exported for debugfs only - * @agg: aggregation state machine +/** + * struct iwl_cfg + * @name: Offical name of the device + * @fw_name_pre: Firmware filename prefix. The api version and extension + * (.ucode) will be added to filename before loading from disk. The + * filename is constructed as fw_name_pre<api>.ucode. + * @ucode_api_max: Highest version of uCode API supported by driver. + * @ucode_api_ok: oldest version of the uCode API that is OK to load + * without a warning, for use in transitions + * @ucode_api_min: Lowest version of uCode API supported by driver. + * @valid_tx_ant: valid transmit antenna + * @valid_rx_ant: valid receive antenna + * @sku: sku information from EEPROM + * @eeprom_ver: EEPROM version + * @eeprom_calib_ver: EEPROM calibration version + * @lib: pointer to the lib ops + * @additional_nic_config: additional nic configuration + * @base_params: pointer to basic parameters + * @ht_params: point to ht patameters + * @bt_params: pointer to bt parameters + * @pa_type: used by 6000 series only to identify the type of Power Amplifier + * @need_temp_offset_calib: need to perform temperature offset calibration + * @no_xtal_calib: some devices do not need crystal calibration data, + * don't send it to those + * @scan_rx_antennas: available antenna for scan operation + * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) + * @adv_pm: advance power management + * @rx_with_siso_diversity: 1x1 device with rx antenna diversity + * @internal_wimax_coex: internal wifi/wimax combo device + * @iq_invert: I/Q inversion + * @temp_offset_v2: support v2 of temperature offset calibration + * + * We enable the driver to be backward compatible wrt API version. The + * driver specifies which APIs it supports (with @ucode_api_max being the + * highest and @ucode_api_min the lowest). Firmware will only be loaded if + * it has a supported API version. + * + * The ideal usage of this infrastructure is to treat a new ucode API + * release as a new hardware revision. */ -struct iwl_tid_data { - u16 seq_number; - u16 tfds_in_queue; - struct iwl_ht_agg agg; +struct iwl_cfg { + /* params specific to an individual device within a device family */ + const char *name; + const char *fw_name_pre; + const unsigned int ucode_api_max; + const unsigned int ucode_api_ok; + const unsigned int ucode_api_min; + u8 valid_tx_ant; + u8 valid_rx_ant; + u16 sku; + u16 eeprom_ver; + u16 eeprom_calib_ver; + const struct iwl_lib_ops *lib; + void (*additional_nic_config)(struct iwl_priv *priv); + /* params not likely to change within a device family */ + struct iwl_base_params *base_params; + /* params likely to change within a device family */ + struct iwl_ht_params *ht_params; + struct iwl_bt_params *bt_params; + enum iwl_pa_type pa_type; /* if used set to IWL_PA_SYSTEM */ + const bool need_temp_offset_calib; /* if used set to true */ + const bool no_xtal_calib; + u8 scan_rx_antennas[IEEE80211_NUM_BANDS]; + enum iwl_led_mode led_mode; + const bool adv_pm; + const bool rx_with_siso_diversity; + const bool internal_wimax_coex; + const bool iq_invert; + const bool temp_offset_v2; }; /** @@ -266,15 +350,25 @@ struct iwl_tid_data { * @ucode_owner: IWL_OWNERSHIP_* * @cmd_queue: command queue number * @status: STATUS_* + * @wowlan: are we running wowlan uCode * @valid_contexts: microcode/device supports multiple contexts * @bus: pointer to the bus layer data + * @cfg: see struct iwl_cfg * @priv: pointer to the upper layer data + * @trans: pointer to the transport layer data * @hw_params: see struct iwl_hw_params * @workqueue: the workqueue used by all the layers of the driver * @lock: protect general shared data * @sta_lock: protects the station table. * If lock and sta_lock are needed, lock must be acquired first. * @mutex: + * @wait_command_queue: the wait_queue for SYNC host command nad uCode load + * @eeprom: pointer to the eeprom/OTP image + * @ucode_type: indicator of loaded ucode image + * @notif_waits: things waiting for notification + * @notif_wait_lock: lock protecting notification + * @notif_waitq: head of notification wait queue + * @device_pointers: pointers to ucode event tables */ struct iwl_shared { #ifdef CONFIG_IWLWIFI_DEBUG @@ -290,6 +384,7 @@ struct iwl_shared { u8 valid_contexts; struct iwl_bus *bus; + struct iwl_cfg *cfg; struct iwl_priv *priv; struct iwl_trans *trans; struct iwl_hw_params hw_params; @@ -299,13 +394,29 @@ struct iwl_shared { spinlock_t sta_lock; struct mutex mutex; - struct iwl_tid_data tid_data[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; - wait_queue_head_t wait_command_queue; + + /* eeprom -- this is in the card's little endian byte order */ + u8 *eeprom; + + /* ucode related variables */ + enum iwl_ucode_type ucode_type; + + /* notification wait support */ + struct list_head notif_waits; + spinlock_t notif_wait_lock; + wait_queue_head_t notif_waitq; + + struct { + u32 error_event_table; + u32 log_event_table; + } device_pointers; + }; /*Whatever _m is (iwl_trans, iwl_priv, iwl_bus, these macros will work */ #define priv(_m) ((_m)->shrd->priv) +#define cfg(_m) ((_m)->shrd->cfg) #define bus(_m) ((_m)->shrd->bus) #define trans(_m) ((_m)->shrd->trans) #define hw_params(_m) ((_m)->shrd->hw_params) @@ -427,12 +538,6 @@ int __must_check iwl_rx_dispatch(struct iwl_priv *priv, struct iwl_device_cmd *cmd); int iwlagn_hw_valid_rtc_data_addr(u32 addr); -void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, - enum iwl_rxon_context_id ctx, - u8 sta_id, u8 tid); -void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv, - enum iwl_rxon_context_id ctx, - u8 sta_id, u8 tid); void iwl_set_hw_rfkill_state(struct iwl_priv *priv, bool state); void iwl_nic_config(struct iwl_priv *priv); void iwl_free_skb(struct iwl_priv *priv, struct sk_buff *skb); @@ -445,6 +550,24 @@ bool iwl_check_for_ct_kill(struct iwl_priv *priv); void iwl_stop_sw_queue(struct iwl_priv *priv, u8 ac); void iwl_wake_sw_queue(struct iwl_priv *priv, u8 ac); +/* notification wait support */ +void iwl_abort_notification_waits(struct iwl_shared *shrd); +void __acquires(wait_entry) +iwl_init_notification_wait(struct iwl_shared *shrd, + struct iwl_notification_wait *wait_entry, + u8 cmd, + void (*fn)(struct iwl_trans *trans, + struct iwl_rx_packet *pkt, + void *data), + void *fn_data); +int __must_check __releases(wait_entry) +iwl_wait_notification(struct iwl_shared *shrd, + struct iwl_notification_wait *wait_entry, + unsigned long timeout); +void __releases(wait_entry) +iwl_remove_notification(struct iwl_shared *shrd, + struct iwl_notification_wait *wait_entry); + #ifdef CONFIG_IWLWIFI_DEBUGFS void iwl_reset_traffic_log(struct iwl_priv *priv); #endif /* CONFIG_IWLWIFI_DEBUGFS */ diff --git a/drivers/net/wireless/iwlwifi/iwl-sv-open.c b/drivers/net/wireless/iwlwifi/iwl-testmode.c index 5e50d88f302..4a5cddd2d56 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sv-open.c +++ b/drivers/net/wireless/iwlwifi/iwl-testmode.c @@ -70,6 +70,7 @@ #include <net/mac80211.h> #include <net/netlink.h> +#include "iwl-wifi.h" #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-debug.h" @@ -77,6 +78,7 @@ #include "iwl-agn.h" #include "iwl-testmode.h" #include "iwl-trans.h" +#include "iwl-bus.h" /* The TLVs used in the gnl message policy between the kernel module and * user space application. iwl_testmode_gnl_msg_policy is to be carried @@ -106,6 +108,13 @@ struct nla_policy iwl_testmode_gnl_msg_policy[IWL_TM_ATTR_MAX] = { [IWL_TM_ATTR_FIXRATE] = { .type = NLA_U32, }, [IWL_TM_ATTR_UCODE_OWNER] = { .type = NLA_U8, }, + + [IWL_TM_ATTR_SRAM_ADDR] = { .type = NLA_U32, }, + [IWL_TM_ATTR_SRAM_SIZE] = { .type = NLA_U32, }, + [IWL_TM_ATTR_SRAM_DUMP] = { .type = NLA_UNSPEC, }, + + [IWL_TM_ATTR_FW_VERSION] = { .type = NLA_U32, }, + [IWL_TM_ATTR_DEVICE_ID] = { .type = NLA_U32, }, }; /* @@ -177,6 +186,18 @@ void iwl_testmode_init(struct iwl_priv *priv) { priv->pre_rx_handler = iwl_testmode_ucode_rx_pkt; priv->testmode_trace.trace_enabled = false; + priv->testmode_sram.sram_readed = false; +} + +static void iwl_sram_cleanup(struct iwl_priv *priv) +{ + if (priv->testmode_sram.sram_readed) { + kfree(priv->testmode_sram.buff_addr); + priv->testmode_sram.buff_addr = NULL; + priv->testmode_sram.buff_size = 0; + priv->testmode_sram.num_chunks = 0; + priv->testmode_sram.sram_readed = false; + } } static void iwl_trace_cleanup(struct iwl_priv *priv) @@ -201,6 +222,7 @@ static void iwl_trace_cleanup(struct iwl_priv *priv) void iwl_testmode_cleanup(struct iwl_priv *priv) { iwl_trace_cleanup(priv); + iwl_sram_cleanup(priv); } /* @@ -276,7 +298,7 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) IWL_INFO(priv, "testmode register access command offset 0x%x\n", ofs); switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { - case IWL_TM_CMD_APP2DEV_REG_READ32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: val32 = iwl_read32(bus(priv), ofs); IWL_INFO(priv, "32bit value to read 0x%x\n", val32); @@ -291,7 +313,7 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) IWL_DEBUG_INFO(priv, "Error sending msg : %d\n", status); break; - case IWL_TM_CMD_APP2DEV_REG_WRITE32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: if (!tb[IWL_TM_ATTR_REG_VALUE32]) { IWL_DEBUG_INFO(priv, "Error finding value to write\n"); @@ -302,7 +324,7 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) iwl_write32(bus(priv), ofs, val32); } break; - case IWL_TM_CMD_APP2DEV_REG_WRITE8: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: if (!tb[IWL_TM_ATTR_REG_VALUE8]) { IWL_DEBUG_INFO(priv, "Error finding value to write\n"); return -ENOMSG; @@ -312,6 +334,32 @@ static int iwl_testmode_reg(struct ieee80211_hw *hw, struct nlattr **tb) iwl_write8(bus(priv), ofs, val8); } break; + case IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32: + val32 = iwl_read_prph(bus(priv), ofs); + IWL_INFO(priv, "32bit value to read 0x%x\n", val32); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_DEBUG_INFO(priv, "Error allocating memory\n"); + return -ENOMEM; + } + NLA_PUT_U32(skb, IWL_TM_ATTR_REG_VALUE32, val32); + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_DEBUG_INFO(priv, + "Error sending msg : %d\n", status); + break; + case IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32: + if (!tb[IWL_TM_ATTR_REG_VALUE32]) { + IWL_DEBUG_INFO(priv, + "Error finding value to write\n"); + return -ENOMSG; + } else { + val32 = nla_get_u32(tb[IWL_TM_ATTR_REG_VALUE32]); + IWL_INFO(priv, "32bit value to write 0x%x\n", val32); + iwl_write_prph(bus(priv), ofs, val32); + } + break; default: IWL_DEBUG_INFO(priv, "Unknown testmode register command ID\n"); return -ENOSYS; @@ -330,24 +378,24 @@ static int iwl_testmode_cfg_init_calib(struct iwl_priv *priv) struct iwl_notification_wait calib_wait; int ret; - iwlagn_init_notification_wait(priv, &calib_wait, + iwl_init_notification_wait(priv->shrd, &calib_wait, CALIBRATION_COMPLETE_NOTIFICATION, NULL, NULL); - ret = iwlagn_init_alive_start(priv); + ret = iwl_init_alive_start(trans(priv)); if (ret) { IWL_DEBUG_INFO(priv, "Error configuring init calibration: %d\n", ret); goto cfg_init_calib_error; } - ret = iwlagn_wait_notification(priv, &calib_wait, 2 * HZ); + ret = iwl_wait_notification(priv->shrd, &calib_wait, 2 * HZ); if (ret) IWL_DEBUG_INFO(priv, "Error detecting" " CALIBRATION_COMPLETE_NOTIFICATION: %d\n", ret); return ret; cfg_init_calib_error: - iwlagn_remove_notification(priv, &calib_wait); + iwl_remove_notification(priv->shrd, &calib_wait); return ret; } @@ -370,14 +418,16 @@ cfg_init_calib_error: static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) { struct iwl_priv *priv = hw->priv; + struct iwl_trans *trans = trans(priv); struct sk_buff *skb; unsigned char *rsp_data_ptr = NULL; int status = 0, rsp_data_len = 0; + u32 devid; switch (nla_get_u32(tb[IWL_TM_ATTR_COMMAND])) { case IWL_TM_CMD_APP2DEV_GET_DEVICENAME: - rsp_data_ptr = (unsigned char *)priv->cfg->name; - rsp_data_len = strlen(priv->cfg->name); + rsp_data_ptr = (unsigned char *)cfg(priv)->name; + rsp_data_len = strlen(cfg(priv)->name); skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, rsp_data_len + 20); if (!skb) { @@ -396,8 +446,7 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) break; case IWL_TM_CMD_APP2DEV_LOAD_INIT_FW: - status = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_init, - IWL_UCODE_INIT); + status = iwl_load_ucode_wait_alive(trans, IWL_UCODE_INIT); if (status) IWL_DEBUG_INFO(priv, "Error loading init ucode: %d\n", status); @@ -405,13 +454,11 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) case IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB: iwl_testmode_cfg_init_calib(priv); - iwl_trans_stop_device(trans(priv)); + iwl_trans_stop_device(trans); break; case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: - status = iwlagn_load_ucode_wait_alive(priv, - &priv->ucode_rt, - IWL_UCODE_REGULAR); + status = iwl_load_ucode_wait_alive(trans, IWL_UCODE_REGULAR); if (status) { IWL_DEBUG_INFO(priv, "Error loading runtime ucode: %d\n", status); @@ -423,10 +470,25 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) "Error starting the device: %d\n", status); break; + case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: + iwl_scan_cancel_timeout(priv, 200); + iwl_trans_stop_device(trans); + status = iwl_load_ucode_wait_alive(trans, IWL_UCODE_WOWLAN); + if (status) { + IWL_DEBUG_INFO(priv, + "Error loading WOWLAN ucode: %d\n", status); + break; + } + status = iwl_alive_start(priv); + if (status) + IWL_DEBUG_INFO(priv, + "Error starting the device: %d\n", status); + break; + case IWL_TM_CMD_APP2DEV_GET_EEPROM: - if (priv->eeprom) { + if (priv->shrd->eeprom) { skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, - priv->cfg->base_params->eeprom_size + 20); + cfg(priv)->base_params->eeprom_size + 20); if (!skb) { IWL_DEBUG_INFO(priv, "Error allocating memory\n"); @@ -435,8 +497,8 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) NLA_PUT_U32(skb, IWL_TM_ATTR_COMMAND, IWL_TM_CMD_DEV2APP_EEPROM_RSP); NLA_PUT(skb, IWL_TM_ATTR_EEPROM, - priv->cfg->base_params->eeprom_size, - priv->eeprom); + cfg(priv)->base_params->eeprom_size, + priv->shrd->eeprom); status = cfg80211_testmode_reply(skb); if (status < 0) IWL_DEBUG_INFO(priv, @@ -455,6 +517,37 @@ static int iwl_testmode_driver(struct ieee80211_hw *hw, struct nlattr **tb) priv->tm_fixed_rate = nla_get_u32(tb[IWL_TM_ATTR_FIXRATE]); break; + case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: + IWL_INFO(priv, "uCode version raw: 0x%x\n", priv->ucode_ver); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_DEBUG_INFO(priv, "Error allocating memory\n"); + return -ENOMEM; + } + NLA_PUT_U32(skb, IWL_TM_ATTR_FW_VERSION, priv->ucode_ver); + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_DEBUG_INFO(priv, + "Error sending msg : %d\n", status); + break; + + case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: + devid = bus_get_hw_id(bus(priv)); + IWL_INFO(priv, "hw version: 0x%x\n", devid); + + skb = cfg80211_testmode_alloc_reply_skb(hw->wiphy, 20); + if (!skb) { + IWL_DEBUG_INFO(priv, "Error allocating memory\n"); + return -ENOMEM; + } + NLA_PUT_U32(skb, IWL_TM_ATTR_DEVICE_ID, devid); + status = cfg80211_testmode_reply(skb); + if (status < 0) + IWL_DEBUG_INFO(priv, + "Error sending msg : %d\n", status); + break; + default: IWL_DEBUG_INFO(priv, "Unknown testmode driver command ID\n"); return -ENOSYS; @@ -535,7 +628,7 @@ static int iwl_testmode_trace(struct ieee80211_hw *hw, struct nlattr **tb) } priv->testmode_trace.num_chunks = DIV_ROUND_UP(priv->testmode_trace.buff_size, - TRACE_CHUNK_SIZE); + DUMP_CHUNK_SIZE); break; case IWL_TM_CMD_APP2DEV_END_TRACE: @@ -567,15 +660,15 @@ static int iwl_testmode_trace_dump(struct ieee80211_hw *hw, struct nlattr **tb, idx = cb->args[4]; if (idx >= priv->testmode_trace.num_chunks) return -ENOENT; - length = TRACE_CHUNK_SIZE; + length = DUMP_CHUNK_SIZE; if (((idx + 1) == priv->testmode_trace.num_chunks) && - (priv->testmode_trace.buff_size % TRACE_CHUNK_SIZE)) + (priv->testmode_trace.buff_size % DUMP_CHUNK_SIZE)) length = priv->testmode_trace.buff_size % - TRACE_CHUNK_SIZE; + DUMP_CHUNK_SIZE; NLA_PUT(skb, IWL_TM_ATTR_TRACE_DUMP, length, priv->testmode_trace.trace_addr + - (TRACE_CHUNK_SIZE * idx)); + (DUMP_CHUNK_SIZE * idx)); idx++; cb->args[4] = idx; return 0; @@ -621,6 +714,110 @@ static int iwl_testmode_ownership(struct ieee80211_hw *hw, struct nlattr **tb) return 0; } +/* + * This function handles the user application commands for SRAM data dump + * + * It retrieves the mandatory fields IWL_TM_ATTR_SRAM_ADDR and + * IWL_TM_ATTR_SRAM_SIZE to decide the memory area for SRAM data reading + * + * Several error will be retured, -EBUSY if the SRAM data retrieved by + * previous command has not been delivered to userspace, or -ENOMSG if + * the mandatory fields (IWL_TM_ATTR_SRAM_ADDR,IWL_TM_ATTR_SRAM_SIZE) + * are missing, or -ENOMEM if the buffer allocation fails. + * + * Otherwise 0 is replied indicating the success of the SRAM reading. + * + * @hw: ieee80211_hw object that represents the device + * @tb: gnl message fields from the user space + */ +static int iwl_testmode_sram(struct ieee80211_hw *hw, struct nlattr **tb) +{ + struct iwl_priv *priv = hw->priv; + u32 base, ofs, size, maxsize; + + if (priv->testmode_sram.sram_readed) + return -EBUSY; + + if (!tb[IWL_TM_ATTR_SRAM_ADDR]) { + IWL_DEBUG_INFO(priv, "Error finding SRAM offset address\n"); + return -ENOMSG; + } + ofs = nla_get_u32(tb[IWL_TM_ATTR_SRAM_ADDR]); + if (!tb[IWL_TM_ATTR_SRAM_SIZE]) { + IWL_DEBUG_INFO(priv, "Error finding size for SRAM reading\n"); + return -ENOMSG; + } + size = nla_get_u32(tb[IWL_TM_ATTR_SRAM_SIZE]); + switch (priv->shrd->ucode_type) { + case IWL_UCODE_REGULAR: + maxsize = trans(priv)->ucode_rt.data.len; + break; + case IWL_UCODE_INIT: + maxsize = trans(priv)->ucode_init.data.len; + break; + case IWL_UCODE_WOWLAN: + maxsize = trans(priv)->ucode_wowlan.data.len; + break; + case IWL_UCODE_NONE: + IWL_DEBUG_INFO(priv, "Error, uCode does not been loaded\n"); + return -ENOSYS; + default: + IWL_DEBUG_INFO(priv, "Error, unsupported uCode type\n"); + return -ENOSYS; + } + if ((ofs + size) > maxsize) { + IWL_DEBUG_INFO(priv, "Invalid offset/size: out of range\n"); + return -EINVAL; + } + priv->testmode_sram.buff_size = (size / 4) * 4; + priv->testmode_sram.buff_addr = + kmalloc(priv->testmode_sram.buff_size, GFP_KERNEL); + if (priv->testmode_sram.buff_addr == NULL) { + IWL_DEBUG_INFO(priv, "Error allocating memory\n"); + return -ENOMEM; + } + base = 0x800000; + _iwl_read_targ_mem_words(bus(priv), base + ofs, + priv->testmode_sram.buff_addr, + priv->testmode_sram.buff_size / 4); + priv->testmode_sram.num_chunks = + DIV_ROUND_UP(priv->testmode_sram.buff_size, DUMP_CHUNK_SIZE); + priv->testmode_sram.sram_readed = true; + return 0; +} + +static int iwl_testmode_sram_dump(struct ieee80211_hw *hw, struct nlattr **tb, + struct sk_buff *skb, + struct netlink_callback *cb) +{ + struct iwl_priv *priv = hw->priv; + int idx, length; + + if (priv->testmode_sram.sram_readed) { + idx = cb->args[4]; + if (idx >= priv->testmode_sram.num_chunks) { + iwl_sram_cleanup(priv); + return -ENOENT; + } + length = DUMP_CHUNK_SIZE; + if (((idx + 1) == priv->testmode_sram.num_chunks) && + (priv->testmode_sram.buff_size % DUMP_CHUNK_SIZE)) + length = priv->testmode_sram.buff_size % + DUMP_CHUNK_SIZE; + + NLA_PUT(skb, IWL_TM_ATTR_SRAM_DUMP, length, + priv->testmode_sram.buff_addr + + (DUMP_CHUNK_SIZE * idx)); + idx++; + cb->args[4] = idx; + return 0; + } else + return -EFAULT; + + nla_put_failure: + return -ENOBUFS; +} + /* The testmode gnl message handler that takes the gnl message from the * user space and parses it per the policy iwl_testmode_gnl_msg_policy, then @@ -668,9 +865,11 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) IWL_DEBUG_INFO(priv, "testmode cmd to uCode\n"); result = iwl_testmode_ucode(hw, tb); break; - case IWL_TM_CMD_APP2DEV_REG_READ32: - case IWL_TM_CMD_APP2DEV_REG_WRITE32: - case IWL_TM_CMD_APP2DEV_REG_WRITE8: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + case IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: + case IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32: + case IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32: IWL_DEBUG_INFO(priv, "testmode cmd to register\n"); result = iwl_testmode_reg(hw, tb); break; @@ -680,6 +879,9 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) case IWL_TM_CMD_APP2DEV_LOAD_RUNTIME_FW: case IWL_TM_CMD_APP2DEV_GET_EEPROM: case IWL_TM_CMD_APP2DEV_FIXRATE_REQ: + case IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: + case IWL_TM_CMD_APP2DEV_GET_FW_VERSION: + case IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: IWL_DEBUG_INFO(priv, "testmode cmd to driver\n"); result = iwl_testmode_driver(hw, tb); break; @@ -696,6 +898,11 @@ int iwlagn_mac_testmode_cmd(struct ieee80211_hw *hw, void *data, int len) result = iwl_testmode_ownership(hw, tb); break; + case IWL_TM_CMD_APP2DEV_READ_SRAM: + IWL_DEBUG_INFO(priv, "testmode sram read cmd to driver\n"); + result = iwl_testmode_sram(hw, tb); + break; + default: IWL_DEBUG_INFO(priv, "Unknown testmode command\n"); result = -ENOSYS; @@ -744,6 +951,10 @@ int iwlagn_mac_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *skb, IWL_DEBUG_INFO(priv, "uCode trace cmd to driver\n"); result = iwl_testmode_trace_dump(hw, tb, skb, cb); break; + case IWL_TM_CMD_APP2DEV_DUMP_SRAM: + IWL_DEBUG_INFO(priv, "testmode sram dump cmd to driver\n"); + result = iwl_testmode_sram_dump(hw, tb, skb, cb); + break; default: result = -EINVAL; break; diff --git a/drivers/net/wireless/iwlwifi/iwl-testmode.h b/drivers/net/wireless/iwlwifi/iwl-testmode.h index b980bda4b0f..26138f11034 100644 --- a/drivers/net/wireless/iwlwifi/iwl-testmode.h +++ b/drivers/net/wireless/iwlwifi/iwl-testmode.h @@ -76,9 +76,9 @@ * the actual uCode host command ID is carried with * IWL_TM_ATTR_UCODE_CMD_ID * - * @IWL_TM_CMD_APP2DEV_REG_READ32: - * @IWL_TM_CMD_APP2DEV_REG_WRITE32: - * @IWL_TM_CMD_APP2DEV_REG_WRITE8: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32: + * @IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8: * commands from user applicaiton to access register * * @IWL_TM_CMD_APP2DEV_GET_DEVICENAME: retrieve device name @@ -103,16 +103,30 @@ * @IWL_TM_CMD_DEV2APP_EEPROM_RSP: * commands from kernel space to carry the eeprom response * to user application + * * @IWL_TM_CMD_APP2DEV_OWNERSHIP: * commands from user application to own change the ownership of the uCode * if application has the ownership, the only host command from * testmode will deliver to uCode. Default owner is driver + * + * @IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32: + * @IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32: + * commands from user applicaiton to indirectly access peripheral register + * + * @IWL_TM_CMD_APP2DEV_READ_SRAM: + * @IWL_TM_CMD_APP2DEV_DUMP_SRAM: + * commands from user applicaiton to read data in sram + * + * @IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW: load Weak On Wireless LAN uCode image + * @IWL_TM_CMD_APP2DEV_GET_FW_VERSION: retrieve uCode version + * @IWL_TM_CMD_APP2DEV_GET_DEVICE_ID: retrieve ID information in device + * */ enum iwl_tm_cmd_t { IWL_TM_CMD_APP2DEV_UCODE = 1, - IWL_TM_CMD_APP2DEV_REG_READ32 = 2, - IWL_TM_CMD_APP2DEV_REG_WRITE32 = 3, - IWL_TM_CMD_APP2DEV_REG_WRITE8 = 4, + IWL_TM_CMD_APP2DEV_DIRECT_REG_READ32 = 2, + IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE32 = 3, + IWL_TM_CMD_APP2DEV_DIRECT_REG_WRITE8 = 4, IWL_TM_CMD_APP2DEV_GET_DEVICENAME = 5, IWL_TM_CMD_APP2DEV_LOAD_INIT_FW = 6, IWL_TM_CMD_APP2DEV_CFG_INIT_CALIB = 7, @@ -126,7 +140,14 @@ enum iwl_tm_cmd_t { IWL_TM_CMD_DEV2APP_UCODE_RX_PKT = 15, IWL_TM_CMD_DEV2APP_EEPROM_RSP = 16, IWL_TM_CMD_APP2DEV_OWNERSHIP = 17, - IWL_TM_CMD_MAX = 18, + IWL_TM_CMD_APP2DEV_INDIRECT_REG_READ32 = 18, + IWL_TM_CMD_APP2DEV_INDIRECT_REG_WRITE32 = 19, + IWL_TM_CMD_APP2DEV_READ_SRAM = 20, + IWL_TM_CMD_APP2DEV_DUMP_SRAM = 21, + IWL_TM_CMD_APP2DEV_LOAD_WOWLAN_FW = 22, + IWL_TM_CMD_APP2DEV_GET_FW_VERSION = 23, + IWL_TM_CMD_APP2DEV_GET_DEVICE_ID = 24, + IWL_TM_CMD_MAX = 25, }; /* @@ -196,6 +217,26 @@ enum iwl_tm_cmd_t { * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_OWNERSHIP, * The mandatory fields are: * IWL_TM_ATTR_UCODE_OWNER for the new owner + * + * @IWL_TM_ATTR_SRAM_ADDR: + * @IWL_TM_ATTR_SRAM_SIZE: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_READ_SRAM, + * The mandatory fields are: + * IWL_TM_ATTR_SRAM_ADDR for the address in sram + * IWL_TM_ATTR_SRAM_SIZE for the buffer size of data reading + * + * @IWL_TM_ATTR_SRAM_DUMP: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_DUMP_SRAM, + * IWL_TM_ATTR_SRAM_DUMP for the data in sram + * + * @IWL_TM_ATTR_FW_VERSION: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_FW_VERSION, + * IWL_TM_ATTR_FW_VERSION for the uCode version + * + * @IWL_TM_ATTR_DEVICE_ID: + * When IWL_TM_ATTR_COMMAND is IWL_TM_CMD_APP2DEV_GET_DEVICE_ID, + * IWL_TM_ATTR_DEVICE_ID for the device ID information + * */ enum iwl_tm_attr_t { IWL_TM_ATTR_NOT_APPLICABLE = 0, @@ -213,7 +254,12 @@ enum iwl_tm_attr_t { IWL_TM_ATTR_TRACE_DUMP = 12, IWL_TM_ATTR_FIXRATE = 13, IWL_TM_ATTR_UCODE_OWNER = 14, - IWL_TM_ATTR_MAX = 15, + IWL_TM_ATTR_SRAM_ADDR = 15, + IWL_TM_ATTR_SRAM_SIZE = 16, + IWL_TM_ATTR_SRAM_DUMP = 17, + IWL_TM_ATTR_FW_VERSION = 18, + IWL_TM_ATTR_DEVICE_ID = 19, + IWL_TM_ATTR_MAX = 20, }; /* uCode trace buffer */ @@ -221,6 +267,8 @@ enum iwl_tm_attr_t { #define TRACE_BUFF_SIZE_MIN 0x20000 #define TRACE_BUFF_SIZE_DEF TRACE_BUFF_SIZE_MIN #define TRACE_BUFF_PADD 0x2000 -#define TRACE_CHUNK_SIZE (PAGE_SIZE - 1024) + +/* Maximum data size of each dump it packet */ +#define DUMP_CHUNK_SIZE (PAGE_SIZE - 1024) #endif diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h index 2b6756e8b8f..f6debf91d7b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h @@ -219,9 +219,7 @@ struct iwl_trans_pcie { /* INT ICT Table */ __le32 *ict_tbl; - void *ict_tbl_vir; dma_addr_t ict_tbl_dma; - dma_addr_t aligned_ict_tbl_dma; int ict_index; u32 inta; bool use_ict; @@ -236,6 +234,7 @@ struct iwl_trans_pcie { const u8 *ac_to_fifo[NUM_IWL_RXON_CTX]; const u8 *ac_to_queue[NUM_IWL_RXON_CTX]; u8 mcast_queue[NUM_IWL_RXON_CTX]; + u8 agg_txq[IWLAGN_STATION_COUNT][IWL_MAX_TID_COUNT]; struct iwl_tx_queue *txq; unsigned long txq_ctx_active_msk; @@ -280,20 +279,16 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, struct iwl_tx_queue *txq, u16 byte_cnt); -void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id); int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, - int tid); + int sta_id, int tid); void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index); void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, struct iwl_tx_queue *txq, int tx_fifo_id, int scd_retry); -int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, - int tid, u16 *ssn); +int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans, int sta_id, int tid); void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, enum iwl_rxon_context_id ctx, - int sta_id, int tid, int frame_limit); + int sta_id, int tid, int frame_limit, u16 ssn); void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, int index, enum dma_data_direction dma_dir); int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, @@ -354,8 +349,13 @@ static inline void iwl_set_swq_id(struct iwl_tx_queue *txq, u8 ac, u8 hwq) txq->swq_id = (hwq << 2) | ac; } +static inline u8 iwl_get_queue_ac(struct iwl_tx_queue *txq) +{ + return txq->swq_id & 0x3; +} + static inline void iwl_wake_queue(struct iwl_trans *trans, - struct iwl_tx_queue *txq) + struct iwl_tx_queue *txq, const char *msg) { u8 queue = txq->swq_id; u8 ac = queue & 3; @@ -363,13 +363,22 @@ static inline void iwl_wake_queue(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (test_and_clear_bit(hwq, trans_pcie->queue_stopped)) - if (atomic_dec_return(&trans_pcie->queue_stop_count[ac]) <= 0) + if (test_and_clear_bit(hwq, trans_pcie->queue_stopped)) { + if (atomic_dec_return(&trans_pcie->queue_stop_count[ac]) <= 0) { iwl_wake_sw_queue(priv(trans), ac); + IWL_DEBUG_TX_QUEUES(trans, "Wake hwq %d ac %d. %s", + hwq, ac, msg); + } else { + IWL_DEBUG_TX_QUEUES(trans, "Don't wake hwq %d ac %d" + " stop count %d. %s", + hwq, ac, atomic_read(&trans_pcie-> + queue_stop_count[ac]), msg); + } + } } static inline void iwl_stop_queue(struct iwl_trans *trans, - struct iwl_tx_queue *txq) + struct iwl_tx_queue *txq, const char *msg) { u8 queue = txq->swq_id; u8 ac = queue & 3; @@ -377,9 +386,23 @@ static inline void iwl_stop_queue(struct iwl_trans *trans, struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (!test_and_set_bit(hwq, trans_pcie->queue_stopped)) - if (atomic_inc_return(&trans_pcie->queue_stop_count[ac]) > 0) + if (!test_and_set_bit(hwq, trans_pcie->queue_stopped)) { + if (atomic_inc_return(&trans_pcie->queue_stop_count[ac]) > 0) { iwl_stop_sw_queue(priv(trans), ac); + IWL_DEBUG_TX_QUEUES(trans, "Stop hwq %d ac %d" + " stop count %d. %s", + hwq, ac, atomic_read(&trans_pcie-> + queue_stop_count[ac]), msg); + } else { + IWL_DEBUG_TX_QUEUES(trans, "Don't stop hwq %d ac %d" + " stop count %d. %s", + hwq, ac, atomic_read(&trans_pcie-> + queue_stop_count[ac]), msg); + } + } else { + IWL_DEBUG_TX_QUEUES(trans, "stop hwq %d, but it is stopped/ %s", + hwq, msg); + } } #ifdef ieee80211_stop_queue diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c index 374c68cc1d7..752493f0040 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c @@ -594,8 +594,8 @@ static void iwl_dump_nic_error_log(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - base = priv->device_pointers.error_event_table; - if (priv->ucode_type == IWL_UCODE_INIT) { + base = trans->shrd->device_pointers.error_event_table; + if (trans->shrd->ucode_type == IWL_UCODE_INIT) { if (!base) base = priv->init_errlog_ptr; } else { @@ -607,7 +607,7 @@ static void iwl_dump_nic_error_log(struct iwl_trans *trans) IWL_ERR(trans, "Not valid error log pointer 0x%08X for %s uCode\n", base, - (priv->ucode_type == IWL_UCODE_INIT) + (trans->shrd->ucode_type == IWL_UCODE_INIT) ? "Init" : "RT"); return; } @@ -648,6 +648,21 @@ static void iwl_dump_nic_error_log(struct iwl_trans *trans) IWL_ERR(trans, "0x%08X | hw version\n", table.hw_ver); IWL_ERR(trans, "0x%08X | board version\n", table.brd_ver); IWL_ERR(trans, "0x%08X | hcmd\n", table.hcmd); + + IWL_ERR(trans, "0x%08X | isr0\n", table.isr0); + IWL_ERR(trans, "0x%08X | isr1\n", table.isr1); + IWL_ERR(trans, "0x%08X | isr2\n", table.isr2); + IWL_ERR(trans, "0x%08X | isr3\n", table.isr3); + IWL_ERR(trans, "0x%08X | isr4\n", table.isr4); + IWL_ERR(trans, "0x%08X | isr_pref\n", table.isr_pref); + IWL_ERR(trans, "0x%08X | wait_event\n", table.wait_event); + IWL_ERR(trans, "0x%08X | l2p_control\n", table.l2p_control); + IWL_ERR(trans, "0x%08X | l2p_duration\n", table.l2p_duration); + IWL_ERR(trans, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid); + IWL_ERR(trans, "0x%08X | l2p_addr_match\n", table.l2p_addr_match); + IWL_ERR(trans, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); + IWL_ERR(trans, "0x%08X | timestamp\n", table.u_timestamp); + IWL_ERR(trans, "0x%08X | flow_handler\n", table.flow_handler); } /** @@ -657,7 +672,7 @@ static void iwl_irq_handle_error(struct iwl_trans *trans) { struct iwl_priv *priv = priv(trans); /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ - if (priv->cfg->internal_wimax_coex && + if (cfg(priv)->internal_wimax_coex && (!(iwl_read_prph(bus(trans), APMG_CLK_CTRL_REG) & APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(bus(trans), APMG_PS_CTRL_REG) & @@ -709,8 +724,8 @@ static int iwl_print_event_log(struct iwl_trans *trans, u32 start_idx, if (num_events == 0) return pos; - base = priv->device_pointers.log_event_table; - if (priv->ucode_type == IWL_UCODE_INIT) { + base = trans->shrd->device_pointers.log_event_table; + if (trans->shrd->ucode_type == IWL_UCODE_INIT) { if (!base) base = priv->init_evtlog_ptr; } else { @@ -823,8 +838,8 @@ int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log, size_t bufsz = 0; struct iwl_priv *priv = priv(trans); - base = priv->device_pointers.log_event_table; - if (priv->ucode_type == IWL_UCODE_INIT) { + base = trans->shrd->device_pointers.log_event_table; + if (trans->shrd->ucode_type == IWL_UCODE_INIT) { logsize = priv->init_evtlog_size; if (!base) base = priv->init_evtlog_ptr; @@ -838,7 +853,7 @@ int iwl_dump_nic_event_log(struct iwl_trans *trans, bool full_log, IWL_ERR(trans, "Invalid event log pointer 0x%08X for %s uCode\n", base, - (priv->ucode_type == IWL_UCODE_INIT) + (trans->shrd->ucode_type == IWL_UCODE_INIT) ? "Init" : "RT"); return -EINVAL; } @@ -1108,7 +1123,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) isr_stats->tx++; handled |= CSR_INT_BIT_FH_TX; /* Wake up uCode load routine, now that load is complete */ - priv(trans)->ucode_write_complete = 1; + trans->ucode_write_complete = 1; wake_up(&trans->shrd->wait_command_queue); } @@ -1136,7 +1151,11 @@ void iwl_irq_tasklet(struct iwl_trans *trans) * ICT functions * ******************************************************************************/ -#define ICT_COUNT (PAGE_SIZE/sizeof(u32)) + +/* a device (PCI-E) page is 4096 bytes long */ +#define ICT_SHIFT 12 +#define ICT_SIZE (1 << ICT_SHIFT) +#define ICT_COUNT (ICT_SIZE / sizeof(u32)) /* Free dram table */ void iwl_free_isr_ict(struct iwl_trans *trans) @@ -1144,21 +1163,19 @@ void iwl_free_isr_ict(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (trans_pcie->ict_tbl_vir) { - dma_free_coherent(bus(trans)->dev, - (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, - trans_pcie->ict_tbl_vir, + if (trans_pcie->ict_tbl) { + dma_free_coherent(bus(trans)->dev, ICT_SIZE, + trans_pcie->ict_tbl, trans_pcie->ict_tbl_dma); - trans_pcie->ict_tbl_vir = NULL; - memset(&trans_pcie->ict_tbl_dma, 0, - sizeof(trans_pcie->ict_tbl_dma)); - memset(&trans_pcie->aligned_ict_tbl_dma, 0, - sizeof(trans_pcie->aligned_ict_tbl_dma)); + trans_pcie->ict_tbl = NULL; + trans_pcie->ict_tbl_dma = 0; } } -/* allocate dram shared table it is a PAGE_SIZE aligned +/* + * allocate dram shared table, it is an aligned memory + * block of ICT_SIZE. * also reset all data related to ICT table interrupt. */ int iwl_alloc_isr_ict(struct iwl_trans *trans) @@ -1166,36 +1183,26 @@ int iwl_alloc_isr_ict(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - /* allocate shrared data table */ - trans_pcie->ict_tbl_vir = - dma_alloc_coherent(bus(trans)->dev, - (sizeof(u32) * ICT_COUNT) + PAGE_SIZE, - &trans_pcie->ict_tbl_dma, GFP_KERNEL); - if (!trans_pcie->ict_tbl_vir) + trans_pcie->ict_tbl = + dma_alloc_coherent(bus(trans)->dev, ICT_SIZE, + &trans_pcie->ict_tbl_dma, + GFP_KERNEL); + if (!trans_pcie->ict_tbl) return -ENOMEM; - /* align table to PAGE_SIZE boundary */ - trans_pcie->aligned_ict_tbl_dma = - ALIGN(trans_pcie->ict_tbl_dma, PAGE_SIZE); - - IWL_DEBUG_ISR(trans, "ict dma addr %Lx dma aligned %Lx diff %d\n", - (unsigned long long)trans_pcie->ict_tbl_dma, - (unsigned long long)trans_pcie->aligned_ict_tbl_dma, - (int)(trans_pcie->aligned_ict_tbl_dma - - trans_pcie->ict_tbl_dma)); + /* just an API sanity check ... it is guaranteed to be aligned */ + if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { + iwl_free_isr_ict(trans); + return -EINVAL; + } - trans_pcie->ict_tbl = trans_pcie->ict_tbl_vir + - (trans_pcie->aligned_ict_tbl_dma - - trans_pcie->ict_tbl_dma); + IWL_DEBUG_ISR(trans, "ict dma addr %Lx\n", + (unsigned long long)trans_pcie->ict_tbl_dma); - IWL_DEBUG_ISR(trans, "ict vir addr %p vir aligned %p diff %d\n", - trans_pcie->ict_tbl, trans_pcie->ict_tbl_vir, - (int)(trans_pcie->aligned_ict_tbl_dma - - trans_pcie->ict_tbl_dma)); + IWL_DEBUG_ISR(trans, "ict vir addr %p\n", trans_pcie->ict_tbl); /* reset table and index to all 0 */ - memset(trans_pcie->ict_tbl_vir, 0, - (sizeof(u32) * ICT_COUNT) + PAGE_SIZE); + memset(trans_pcie->ict_tbl, 0, ICT_SIZE); trans_pcie->ict_index = 0; /* add periodic RX interrupt */ @@ -1213,23 +1220,20 @@ int iwl_reset_ict(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (!trans_pcie->ict_tbl_vir) + if (!trans_pcie->ict_tbl) return 0; spin_lock_irqsave(&trans->shrd->lock, flags); iwl_disable_interrupts(trans); - memset(&trans_pcie->ict_tbl[0], 0, sizeof(u32) * ICT_COUNT); + memset(trans_pcie->ict_tbl, 0, ICT_SIZE); - val = trans_pcie->aligned_ict_tbl_dma >> PAGE_SHIFT; + val = trans_pcie->ict_tbl_dma >> ICT_SHIFT; val |= CSR_DRAM_INT_TBL_ENABLE; val |= CSR_DRAM_INIT_TBL_WRAP_CHECK; - IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%X " - "aligned dma address %Lx\n", - val, - (unsigned long long)trans_pcie->aligned_ict_tbl_dma); + IWL_DEBUG_ISR(trans, "CSR_DRAM_INT_TBL_REG =0x%x\n", val); iwl_write32(bus(trans), CSR_DRAM_INT_TBL_REG, val); trans_pcie->use_ict = true; @@ -1266,6 +1270,8 @@ static irqreturn_t iwl_isr(int irq, void *data) if (!trans) return IRQ_NONE; + trace_iwlwifi_dev_irq(priv(trans)); + trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); spin_lock_irqsave(&trans->shrd->lock, flags); @@ -1340,6 +1346,7 @@ irqreturn_t iwl_isr_ict(int irq, void *data) struct iwl_trans_pcie *trans_pcie; u32 inta, inta_mask; u32 val = 0; + u32 read; unsigned long flags; if (!trans) @@ -1353,6 +1360,8 @@ irqreturn_t iwl_isr_ict(int irq, void *data) if (!trans_pcie->use_ict) return iwl_isr(irq, data); + trace_iwlwifi_dev_irq(priv(trans)); + spin_lock_irqsave(&trans->shrd->lock, flags); /* Disable (but don't clear!) interrupts here to avoid @@ -1367,24 +1376,29 @@ irqreturn_t iwl_isr_ict(int irq, void *data) /* Ignore interrupt if there's nothing in NIC to service. * This may be due to IRQ shared with another device, * or due to sporadic interrupts thrown from our NIC. */ - if (!trans_pcie->ict_tbl[trans_pcie->ict_index]) { + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(priv(trans), trans_pcie->ict_index, read); + if (!read) { IWL_DEBUG_ISR(trans, "Ignore interrupt, inta == 0\n"); goto none; } - /* read all entries that not 0 start with ict_index */ - while (trans_pcie->ict_tbl[trans_pcie->ict_index]) { - - val |= le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + /* + * Collect all entries up to the first 0, starting from ict_index; + * note we already read at ict_index. + */ + do { + val |= read; IWL_DEBUG_ISR(trans, "ICT index %d value 0x%08X\n", - trans_pcie->ict_index, - le32_to_cpu( - trans_pcie->ict_tbl[trans_pcie->ict_index])); + trans_pcie->ict_index, read); trans_pcie->ict_tbl[trans_pcie->ict_index] = 0; trans_pcie->ict_index = iwl_queue_inc_wrap(trans_pcie->ict_index, ICT_COUNT); - } + read = le32_to_cpu(trans_pcie->ict_tbl[trans_pcie->ict_index]); + trace_iwlwifi_dev_ict_read(priv(trans), trans_pcie->ict_index, + read); + } while (read); /* We should not get this value, just ignore it. */ if (val == 0xffffffff) @@ -1411,7 +1425,7 @@ irqreturn_t iwl_isr_ict(int irq, void *data) if (likely(inta)) tasklet_schedule(&trans_pcie->irq_tasklet); else if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status) && - !trans_pcie->inta) { + !trans_pcie->inta) { /* Allow interrupt if was disabled by this handler and * no tasklet was schedules, We should not enable interrupt, * tasklet will enable it. @@ -1427,7 +1441,7 @@ irqreturn_t iwl_isr_ict(int irq, void *data) * only Re-enable if disabled by irq. */ if (test_bit(STATUS_INT_ENABLED, &trans->shrd->status) && - !trans_pcie->inta) + !trans_pcie->inta) iwl_enable_interrupts(trans); spin_unlock_irqrestore(&trans->shrd->lock, flags); diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index 4a0c95302a7..bd29568177e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c @@ -408,6 +408,7 @@ static void iwlagn_tx_queue_stop_scheduler(struct iwl_trans *trans, u16 txq_id) void iwl_trans_set_wr_ptrs(struct iwl_trans *trans, int txq_id, u32 index) { + IWL_DEBUG_TX_QUEUES(trans, "Q %d WrPtr: %d", txq_id, index & 0xff); iwl_write_direct32(bus(trans), HBUS_TARG_WRPTR, (index & 0xff) | (txq_id << 8)); iwl_write_prph(bus(trans), SCD_QUEUE_RDPTR(txq_id), index); @@ -430,7 +431,7 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, txq->sched_retry = scd_retry; - IWL_DEBUG_INFO(trans, "%s %s Queue %d on FIFO %d\n", + IWL_DEBUG_TX_QUEUES(trans, "%s %s Queue %d on FIFO %d\n", active ? "Activate" : "Deactivate", scd_retry ? "BA" : "AC/CMD", txq_id, tx_fifo_id); } @@ -446,14 +447,21 @@ static inline int get_fifo_from_tid(struct iwl_trans_pcie *trans_pcie, return -EINVAL; } +static inline bool is_agg_txqid_valid(struct iwl_trans *trans, int txq_id) +{ + if (txq_id < IWLAGN_FIRST_AMPDU_QUEUE) + return false; + return txq_id < (IWLAGN_FIRST_AMPDU_QUEUE + + hw_params(trans).num_ampdu_queues); +} + void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, enum iwl_rxon_context_id ctx, int sta_id, - int tid, int frame_limit) + int tid, int frame_limit, u16 ssn) { - int tx_fifo, txq_id, ssn_idx; + int tx_fifo, txq_id; u16 ra_tid; unsigned long flags; - struct iwl_tid_data *tid_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -469,11 +477,15 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, return; } - spin_lock_irqsave(&trans->shrd->sta_lock, flags); - tid_data = &trans->shrd->tid_data[sta_id][tid]; - ssn_idx = SEQ_TO_SN(tid_data->seq_number); - txq_id = tid_data->agg.txq_id; - spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); + txq_id = trans_pcie->agg_txq[sta_id][tid]; + if (WARN_ON_ONCE(is_agg_txqid_valid(trans, txq_id) == false)) { + IWL_ERR(trans, + "queue number out of range: %d, must be %d to %d\n", + txq_id, IWLAGN_FIRST_AMPDU_QUEUE, + IWLAGN_FIRST_AMPDU_QUEUE + + hw_params(trans).num_ampdu_queues - 1); + return; + } ra_tid = BUILD_RAxTID(sta_id, tid); @@ -493,9 +505,9 @@ void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, /* Place first TFD at index corresponding to start sequence number. * Assumes that ssn_idx is valid (!= 0xFFF) */ - trans_pcie->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); - trans_pcie->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); - iwl_trans_set_wr_ptrs(trans, txq_id, ssn_idx); + trans_pcie->txq[txq_id].q.read_ptr = (ssn & 0xff); + trans_pcie->txq[txq_id].q.write_ptr = (ssn & 0xff); + iwl_trans_set_wr_ptrs(trans, txq_id, ssn); /* Set up Tx window size and frame limit for this queue */ iwl_write_targ_mem(bus(trans), trans_pcie->scd_base_addr + @@ -539,12 +551,9 @@ static int iwlagn_txq_ctx_activate_free(struct iwl_trans *trans) } int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, - int tid, u16 *ssn) + int sta_id, int tid) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tid_data *tid_data; - unsigned long flags; int txq_id; txq_id = iwlagn_txq_ctx_activate_free(trans); @@ -553,117 +562,39 @@ int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans, return -ENXIO; } - spin_lock_irqsave(&trans->shrd->sta_lock, flags); - tid_data = &trans->shrd->tid_data[sta_id][tid]; - *ssn = SEQ_TO_SN(tid_data->seq_number); - tid_data->agg.txq_id = txq_id; + trans_pcie->agg_txq[sta_id][tid] = txq_id; iwl_set_swq_id(&trans_pcie->txq[txq_id], get_ac_from_tid(tid), txq_id); - tid_data = &trans->shrd->tid_data[sta_id][tid]; - if (tid_data->tfds_in_queue == 0) { - IWL_DEBUG_HT(trans, "HW queue is empty\n"); - tid_data->agg.state = IWL_AGG_ON; - iwl_start_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid); - } else { - IWL_DEBUG_HT(trans, "HW queue is NOT empty: %d packets in HW" - "queue\n", tid_data->tfds_in_queue); - tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; - } - spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); - return 0; } -void iwl_trans_pcie_txq_agg_disable(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - iwlagn_tx_queue_stop_scheduler(trans, txq_id); - - iwl_clear_bits_prph(bus(trans), SCD_AGGR_SEL, (1 << txq_id)); - - trans_pcie->txq[txq_id].q.read_ptr = 0; - trans_pcie->txq[txq_id].q.write_ptr = 0; - /* supposes that ssn_idx is valid (!= 0xFFF) */ - iwl_trans_set_wr_ptrs(trans, txq_id, 0); - - iwl_clear_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id)); - iwl_txq_ctx_deactivate(trans_pcie, txq_id); - iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, 0); -} - -int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, - int tid) +int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, int sta_id, int tid) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned long flags; - int read_ptr, write_ptr; - struct iwl_tid_data *tid_data; - int txq_id; - - spin_lock_irqsave(&trans->shrd->sta_lock, flags); + u8 txq_id = trans_pcie->agg_txq[sta_id][tid]; - tid_data = &trans->shrd->tid_data[sta_id][tid]; - txq_id = tid_data->agg.txq_id; - - if ((IWLAGN_FIRST_AMPDU_QUEUE > txq_id) || - (IWLAGN_FIRST_AMPDU_QUEUE + - hw_params(trans).num_ampdu_queues <= txq_id)) { + if (WARN_ON_ONCE(is_agg_txqid_valid(trans, txq_id) == false)) { IWL_ERR(trans, "queue number out of range: %d, must be %d to %d\n", txq_id, IWLAGN_FIRST_AMPDU_QUEUE, IWLAGN_FIRST_AMPDU_QUEUE + hw_params(trans).num_ampdu_queues - 1); - spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); return -EINVAL; } - switch (trans->shrd->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* - * This can happen if the peer stops aggregation - * again before we've had a chance to drain the - * queue we selected previously, i.e. before the - * session was really started completely. - */ - IWL_DEBUG_HT(trans, "AGG stop before setup done\n"); - goto turn_off; - case IWL_AGG_ON: - break; - default: - IWL_WARN(trans, "Stopping AGG while state not ON " - "or starting for %d on %d (%d)\n", sta_id, tid, - trans->shrd->tid_data[sta_id][tid].agg.state); - spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); - return 0; - } - - write_ptr = trans_pcie->txq[txq_id].q.write_ptr; - read_ptr = trans_pcie->txq[txq_id].q.read_ptr; - - /* The queue is not empty */ - if (write_ptr != read_ptr) { - IWL_DEBUG_HT(trans, "Stopping a non empty AGG HW QUEUE\n"); - trans->shrd->tid_data[sta_id][tid].agg.state = - IWL_EMPTYING_HW_QUEUE_DELBA; - spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); - return 0; - } - - IWL_DEBUG_HT(trans, "HW queue is empty\n"); -turn_off: - trans->shrd->tid_data[sta_id][tid].agg.state = IWL_AGG_OFF; - - /* do not restore/save irqs */ - spin_unlock(&trans->shrd->sta_lock); - spin_lock(&trans->shrd->lock); - - iwl_trans_pcie_txq_agg_disable(trans, txq_id); + iwlagn_tx_queue_stop_scheduler(trans, txq_id); - spin_unlock_irqrestore(&trans->shrd->lock, flags); + iwl_clear_bits_prph(bus(trans), SCD_AGGR_SEL, (1 << txq_id)); - iwl_stop_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid); + trans_pcie->agg_txq[sta_id][tid] = 0; + trans_pcie->txq[txq_id].q.read_ptr = 0; + trans_pcie->txq[txq_id].q.write_ptr = 0; + /* supposes that ssn_idx is valid (!= 0xFFF) */ + iwl_trans_set_wr_ptrs(trans, txq_id, 0); + iwl_clear_bits_prph(bus(trans), SCD_INTERRUPT_MASK, (1 << txq_id)); + iwl_txq_ctx_deactivate(trans_pcie, txq_id); + iwl_trans_tx_queue_set_status(trans, &trans_pcie->txq[txq_id], 0, 0); return 0; } @@ -982,7 +913,8 @@ static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) ret = iwl_enqueue_hcmd(trans, cmd); if (ret < 0) { - IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", + IWL_DEBUG_QUIET_RFKILL(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", get_cmd_string(cmd->id), ret); return ret; } @@ -1000,6 +932,20 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", get_cmd_string(cmd->id)); + if (test_bit(STATUS_EXIT_PENDING, &trans->shrd->status)) + return -EBUSY; + + + if (test_bit(STATUS_RF_KILL_HW, &trans->shrd->status)) { + IWL_ERR(trans, "Command %s aborted: RF KILL Switch\n", + get_cmd_string(cmd->id)); + return -ECANCELED; + } + if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) { + IWL_ERR(trans, "Command %s failed: FW Error\n", + get_cmd_string(cmd->id)); + return -EIO; + } set_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", get_cmd_string(cmd->id)); @@ -1008,7 +954,8 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (cmd_idx < 0) { ret = cmd_idx; clear_bit(STATUS_HCMD_ACTIVE, &trans->shrd->status); - IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", + IWL_DEBUG_QUIET_RFKILL(trans, + "Error sending %s: enqueue_hcmd failed: %d\n", get_cmd_string(cmd->id), ret); return ret; } @@ -1022,12 +969,12 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) &trans_pcie->txq[trans->shrd->cmd_queue]; struct iwl_queue *q = &txq->q; - IWL_ERR(trans, + IWL_DEBUG_QUIET_RFKILL(trans, "Error sending %s: time out after %dms.\n", get_cmd_string(cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); - IWL_ERR(trans, + IWL_DEBUG_QUIET_RFKILL(trans, "Current CMD queue read_ptr %d write_ptr %d\n", q->read_ptr, q->write_ptr); @@ -1039,18 +986,6 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) } } - if (test_bit(STATUS_RF_KILL_HW, &trans->shrd->status)) { - IWL_ERR(trans, "Command %s aborted: RF KILL Switch\n", - get_cmd_string(cmd->id)); - ret = -ECANCELED; - goto fail; - } - if (test_bit(STATUS_FW_ERROR, &trans->shrd->status)) { - IWL_ERR(trans, "Command %s failed: FW Error\n", - get_cmd_string(cmd->id)); - ret = -EIO; - goto fail; - } if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { IWL_ERR(trans, "Error: Response NULL in '%s'\n", get_cmd_string(cmd->id)); @@ -1071,7 +1006,7 @@ cancel: trans_pcie->txq[trans->shrd->cmd_queue].meta[cmd_idx].flags &= ~CMD_WANT_SKB; } -fail: + if (cmd->reply_page) { iwl_free_pages(trans->shrd, cmd->reply_page); cmd->reply_page = 0; @@ -1115,9 +1050,6 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, return 0; } - IWL_DEBUG_TX_REPLY(trans, "reclaim: [%d, %d, %d]\n", txq_id, - q->read_ptr, index); - if (WARN_ON(!skb_queue_empty(skbs))) return 0; diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index ce918980e97..67d6e324e26 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c @@ -88,18 +88,16 @@ static int iwl_trans_rx_alloc(struct iwl_trans *trans) return -EINVAL; /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_alloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - &rxq->bd_dma, GFP_KERNEL); + rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + &rxq->bd_dma, GFP_KERNEL); if (!rxq->bd) goto err_bd; - memset(rxq->bd, 0, sizeof(__le32) * RX_QUEUE_SIZE); /*Allocate the driver's pointer to receive buffer status */ - rxq->rb_stts = dma_alloc_coherent(dev, sizeof(*rxq->rb_stts), - &rxq->rb_stts_dma, GFP_KERNEL); + rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), + &rxq->rb_stts_dma, GFP_KERNEL); if (!rxq->rb_stts) goto err_rb_stts; - memset(rxq->rb_stts, 0, sizeof(*rxq->rb_stts)); return 0; @@ -1044,7 +1042,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx, - u8 sta_id) + u8 sta_id, u8 tid) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -1058,13 +1056,12 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, dma_addr_t txcmd_phys; dma_addr_t scratch_phys; u16 len, firstlen, secondlen; - u16 seq_number = 0; u8 wait_write_ptr = 0; u8 txq_id; - u8 tid = 0; bool is_agg = false; __le16 fc = hdr->frame_control; u8 hdr_len = ieee80211_hdrlen(fc); + u16 __maybe_unused wifi_seq; /* * Send this frame after DTIM -- there's a special queue @@ -1085,36 +1082,28 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, txq_id = trans_pcie->ac_to_queue[ctx][skb_get_queue_mapping(skb)]; - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - u8 *qc = NULL; - struct iwl_tid_data *tid_data; - qc = ieee80211_get_qos_ctl(hdr); - tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; - tid_data = &trans->shrd->tid_data[sta_id][tid]; - - if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) - return -1; - - seq_number = tid_data->seq_number; - seq_number &= IEEE80211_SCTL_SEQ; - hdr->seq_ctrl = hdr->seq_ctrl & - cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(seq_number); - seq_number += 0x10; - /* aggregation is on for this <sta,tid> */ - if (info->flags & IEEE80211_TX_CTL_AMPDU) { - WARN_ON_ONCE(tid_data->agg.state != IWL_AGG_ON); - txq_id = tid_data->agg.txq_id; - is_agg = true; - } + /* aggregation is on for this <sta,tid> */ + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + WARN_ON(tid >= IWL_MAX_TID_COUNT); + txq_id = trans_pcie->agg_txq[sta_id][tid]; + is_agg = true; } - /* Copy MAC header from skb into command buffer */ - memcpy(tx_cmd->hdr, hdr, hdr_len); - txq = &trans_pcie->txq[txq_id]; q = &txq->q; + /* In AGG mode, the index in the ring must correspond to the WiFi + * sequence number. This is a HW requirements to help the SCD to parse + * the BA. + * Check here that the packets are in the right place on the ring. + */ +#ifdef CONFIG_IWLWIFI_DEBUG + wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + WARN_ONCE(is_agg && ((wifi_seq & 0xff) != q->write_ptr), + "Q: %d WiFi Seq %d tfdNum %d", + txq_id, wifi_seq, q->write_ptr); +#endif + /* Set up driver data for this TFD */ txq->skbs[q->write_ptr] = skb; txq->cmd[q->write_ptr] = dev_cmd; @@ -1197,9 +1186,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, iwl_print_hex_dump(trans, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); /* Set up entry for this TFD in Tx byte-count array */ - if (is_agg) - iwl_trans_txq_update_byte_cnt_tbl(trans, txq, - le16_to_cpu(tx_cmd->len)); + iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); dma_sync_single_for_device(bus(trans)->dev, txcmd_phys, firstlen, DMA_BIDIRECTIONAL); @@ -1214,13 +1201,6 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); iwl_txq_update_write_ptr(trans, txq); - if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { - trans->shrd->tid_data[sta_id][tid].tfds_in_queue++; - if (!ieee80211_has_morefrags(fc)) - trans->shrd->tid_data[sta_id][tid].seq_number = - seq_number; - } - /* * At this point the frame is "transmitted" successfully * and we will get a TX status notification eventually, @@ -1232,7 +1212,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, txq->need_update = 1; iwl_txq_update_write_ptr(trans, txq); } else { - iwl_stop_queue(trans, txq); + iwl_stop_queue(trans, txq, "Queue is full"); } } return 0; @@ -1269,101 +1249,48 @@ static int iwl_trans_pcie_request_irq(struct iwl_trans *trans) return 0; } -static int iwlagn_txq_check_empty(struct iwl_trans *trans, - int sta_id, u8 tid, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_queue *q = &trans_pcie->txq[txq_id].q; - struct iwl_tid_data *tid_data = &trans->shrd->tid_data[sta_id][tid]; - - lockdep_assert_held(&trans->shrd->sta_lock); - - switch (trans->shrd->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_DELBA: - /* We are reclaiming the last packet of the */ - /* aggregated HW queue */ - if ((txq_id == tid_data->agg.txq_id) && - (q->read_ptr == q->write_ptr)) { - IWL_DEBUG_HT(trans, - "HW queue empty: continue DELBA flow\n"); - iwl_trans_pcie_txq_agg_disable(trans, txq_id); - tid_data->agg.state = IWL_AGG_OFF; - iwl_stop_tx_ba_trans_ready(priv(trans), - NUM_IWL_RXON_CTX, - sta_id, tid); - iwl_wake_queue(trans, &trans_pcie->txq[txq_id]); - } - break; - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* We are reclaiming the last packet of the queue */ - if (tid_data->tfds_in_queue == 0) { - IWL_DEBUG_HT(trans, - "HW queue empty: continue ADDBA flow\n"); - tid_data->agg.state = IWL_AGG_ON; - iwl_start_tx_ba_trans_ready(priv(trans), - NUM_IWL_RXON_CTX, - sta_id, tid); - } - break; - default: - break; - } - - return 0; -} - -static void iwl_free_tfds_in_queue(struct iwl_trans *trans, - int sta_id, int tid, int freed) -{ - lockdep_assert_held(&trans->shrd->sta_lock); - - if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed) - trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed; - else { - IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n", - trans->shrd->tid_data[sta_id][tid].tfds_in_queue, - freed); - trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0; - } -} - -static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, +static int iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, int txq_id, int ssn, u32 status, struct sk_buff_head *skbs) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - enum iwl_agg_state agg_state; /* n_bd is usually 256 => n_bd - 1 = 0xff */ int tfd_num = ssn & (txq->q.n_bd - 1); int freed = 0; - bool cond; txq->time_stamp = jiffies; - if (txq->sched_retry) { - agg_state = - trans->shrd->tid_data[txq->sta_id][txq->tid].agg.state; - cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA); - } else { - cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX); + if (unlikely(txq_id >= IWLAGN_FIRST_AMPDU_QUEUE && + txq_id != trans_pcie->agg_txq[sta_id][tid])) { + /* + * FIXME: this is a uCode bug which need to be addressed, + * log the information and return for now. + * Since it is can possibly happen very often and in order + * not to fill the syslog, don't use IWL_ERR or IWL_WARN + */ + IWL_DEBUG_TX_QUEUES(trans, "Bad queue mapping txq_id %d, " + "agg_txq[sta_id[tid] %d", txq_id, + trans_pcie->agg_txq[sta_id][tid]); + return 1; } if (txq->q.read_ptr != tfd_num) { - IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim " - "scd_ssn=%d idx=%d txq=%d swq=%d\n", - ssn , tfd_num, txq_id, txq->swq_id); + IWL_DEBUG_TX_REPLY(trans, "[Q %d | AC %d] %d -> %d (%d)\n", + txq_id, iwl_get_queue_ac(txq), txq->q.read_ptr, + tfd_num, ssn); freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); - if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond) - iwl_wake_queue(trans, txq); + if (iwl_queue_space(&txq->q) > txq->q.low_mark && + (!txq->sched_retry || + status != TX_STATUS_FAIL_PASSIVE_NO_RX)) + iwl_wake_queue(trans, txq, "Packets reclaimed"); } - - iwl_free_tfds_in_queue(trans, sta_id, tid, freed); - iwlagn_txq_check_empty(trans, sta_id, tid, txq_id); + return 0; } static void iwl_trans_pcie_free(struct iwl_trans *trans) { + iwl_calib_free_results(trans); iwl_trans_pcie_tx_free(trans); iwl_trans_pcie_rx_free(trans); free_irq(bus(trans)->irq, trans); @@ -1419,7 +1346,8 @@ static int iwl_trans_pcie_resume(struct iwl_trans *trans) #endif /* CONFIG_PM_SLEEP */ static void iwl_trans_pcie_wake_any_queue(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx) + enum iwl_rxon_context_id ctx, + const char *msg) { u8 ac, txq_id; struct iwl_trans_pcie *trans_pcie = @@ -1427,11 +1355,11 @@ static void iwl_trans_pcie_wake_any_queue(struct iwl_trans *trans, for (ac = 0; ac < AC_NUM; ac++) { txq_id = trans_pcie->ac_to_queue[ctx][ac]; - IWL_DEBUG_INFO(trans, "Queue Status: Q[%d] %s\n", + IWL_DEBUG_TX_QUEUES(trans, "Queue Status: Q[%d] %s\n", ac, (atomic_read(&trans_pcie->queue_stop_count[ac]) > 0) ? "stopped" : "awake"); - iwl_wake_queue(trans, &trans_pcie->txq[txq_id]); + iwl_wake_queue(trans, &trans_pcie->txq[txq_id], msg); } } @@ -1454,11 +1382,12 @@ static struct iwl_trans *iwl_trans_pcie_alloc(struct iwl_shared *shrd) return iwl_trans; } -static void iwl_trans_pcie_stop_queue(struct iwl_trans *trans, int txq_id) +static void iwl_trans_pcie_stop_queue(struct iwl_trans *trans, int txq_id, + const char *msg) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - iwl_stop_queue(trans, &trans_pcie->txq[txq_id]); + iwl_stop_queue(trans, &trans_pcie->txq[txq_id], msg); } #define IWL_FLUSH_WAIT_MS 2000 @@ -1513,8 +1442,12 @@ static int iwl_trans_pcie_check_stuck_queue(struct iwl_trans *trans, int cnt) if (time_after(jiffies, timeout)) { IWL_ERR(trans, "Queue %d stuck for %u ms.\n", q->id, hw_params(trans).wd_timeout); - IWL_ERR(trans, "Current read_ptr %d write_ptr %d\n", + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", q->read_ptr, q->write_ptr); + IWL_ERR(trans, "Current HW read_ptr %d write_ptr %d\n", + iwl_read_prph(bus(trans), SCD_QUEUE_RDPTR(cnt)) + & (TFD_QUEUE_SIZE_MAX - 1), + iwl_read_prph(bus(trans), SCD_QUEUE_WRPTR(cnt))); return 1; } diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index c5923125c3f..e6bf3f55477 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -171,32 +171,31 @@ struct iwl_trans_ops { void (*tx_start)(struct iwl_trans *trans); void (*wake_any_queue)(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx); + enum iwl_rxon_context_id ctx, + const char *msg); int (*send_cmd)(struct iwl_trans *trans, struct iwl_host_cmd *cmd); int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx, - u8 sta_id); - void (*reclaim)(struct iwl_trans *trans, int sta_id, int tid, + u8 sta_id, u8 tid); + int (*reclaim)(struct iwl_trans *trans, int sta_id, int tid, int txq_id, int ssn, u32 status, struct sk_buff_head *skbs); int (*tx_agg_disable)(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, - int tid); + int sta_id, int tid); int (*tx_agg_alloc)(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, int tid, - u16 *ssn); + int sta_id, int tid); void (*tx_agg_setup)(struct iwl_trans *trans, enum iwl_rxon_context_id ctx, int sta_id, int tid, - int frame_limit); + int frame_limit, u16 ssn); void (*kick_nic)(struct iwl_trans *trans); void (*free)(struct iwl_trans *trans); - void (*stop_queue)(struct iwl_trans *trans, int q); + void (*stop_queue)(struct iwl_trans *trans, int q, const char *msg); int (*dbgfs_register)(struct iwl_trans *trans, struct dentry* dir); int (*check_stuck_queue)(struct iwl_trans *trans, int q); @@ -207,17 +206,54 @@ struct iwl_trans_ops { #endif }; +/* one for each uCode image (inst/data, boot/init/runtime) */ +struct fw_desc { + dma_addr_t p_addr; /* hardware address */ + void *v_addr; /* software address */ + u32 len; /* size in bytes */ +}; + +struct fw_img { + struct fw_desc code; /* firmware code image */ + struct fw_desc data; /* firmware data image */ +}; + +/* Opaque calibration results */ +struct iwl_calib_result { + struct list_head list; + size_t cmd_len; + struct iwl_calib_hdr hdr; + /* data follows */ +}; + /** * struct iwl_trans - transport common data * @ops - pointer to iwl_trans_ops * @shrd - pointer to iwl_shared which holds shared data from the upper layer * @hcmd_lock: protects HCMD + * @ucode_write_complete: indicates that the ucode has been copied. + * @ucode_rt: run time ucode image + * @ucode_init: init ucode image + * @ucode_wowlan: wake on wireless ucode image (optional) + * @nvm_device_type: indicates OTP or eeprom + * @calib_results: list head for init calibration results */ struct iwl_trans { const struct iwl_trans_ops *ops; struct iwl_shared *shrd; spinlock_t hcmd_lock; + u8 ucode_write_complete; /* the image write is complete */ + struct fw_img ucode_rt; + struct fw_img ucode_init; + struct fw_img ucode_wowlan; + + /* eeprom related variables */ + int nvm_device_type; + + /* init calibration results */ + struct list_head calib_results; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __attribute__((__aligned__(sizeof(void *)))); @@ -249,9 +285,10 @@ static inline void iwl_trans_tx_start(struct iwl_trans *trans) } static inline void iwl_trans_wake_any_queue(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx) + enum iwl_rxon_context_id ctx, + const char *msg) { - trans->ops->wake_any_queue(trans, ctx); + trans->ops->wake_any_queue(trans, ctx, msg); } @@ -266,39 +303,38 @@ int iwl_trans_send_cmd_pdu(struct iwl_trans *trans, u8 id, static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, enum iwl_rxon_context_id ctx, - u8 sta_id) + u8 sta_id, u8 tid) { - return trans->ops->tx(trans, skb, dev_cmd, ctx, sta_id); + return trans->ops->tx(trans, skb, dev_cmd, ctx, sta_id, tid); } -static inline void iwl_trans_reclaim(struct iwl_trans *trans, int sta_id, +static inline int iwl_trans_reclaim(struct iwl_trans *trans, int sta_id, int tid, int txq_id, int ssn, u32 status, struct sk_buff_head *skbs) { - trans->ops->reclaim(trans, sta_id, tid, txq_id, ssn, status, skbs); + return trans->ops->reclaim(trans, sta_id, tid, txq_id, ssn, + status, skbs); } static inline int iwl_trans_tx_agg_disable(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, int sta_id, int tid) { - return trans->ops->tx_agg_disable(trans, ctx, sta_id, tid); + return trans->ops->tx_agg_disable(trans, sta_id, tid); } static inline int iwl_trans_tx_agg_alloc(struct iwl_trans *trans, - enum iwl_rxon_context_id ctx, - int sta_id, int tid, u16 *ssn) + int sta_id, int tid) { - return trans->ops->tx_agg_alloc(trans, ctx, sta_id, tid, ssn); + return trans->ops->tx_agg_alloc(trans, sta_id, tid); } static inline void iwl_trans_tx_agg_setup(struct iwl_trans *trans, enum iwl_rxon_context_id ctx, int sta_id, int tid, - int frame_limit) + int frame_limit, u16 ssn) { - trans->ops->tx_agg_setup(trans, ctx, sta_id, tid, frame_limit); + trans->ops->tx_agg_setup(trans, ctx, sta_id, tid, frame_limit, ssn); } static inline void iwl_trans_kick_nic(struct iwl_trans *trans) @@ -311,9 +347,10 @@ static inline void iwl_trans_free(struct iwl_trans *trans) trans->ops->free(trans); } -static inline void iwl_trans_stop_queue(struct iwl_trans *trans, int q) +static inline void iwl_trans_stop_queue(struct iwl_trans *trans, int q, + const char *msg) { - trans->ops->stop_queue(trans, q); + trans->ops->stop_queue(trans, q, msg); } static inline int iwl_trans_wait_tx_queue_empty(struct iwl_trans *trans) @@ -348,4 +385,13 @@ static inline int iwl_trans_resume(struct iwl_trans *trans) ******************************************************/ extern const struct iwl_trans_ops trans_ops_pcie; +int iwl_alloc_fw_desc(struct iwl_bus *bus, struct fw_desc *desc, + const void *data, size_t len); +void iwl_dealloc_ucode(struct iwl_trans *trans); + +int iwl_send_calib_results(struct iwl_trans *trans); +int iwl_calib_set(struct iwl_trans *trans, + const struct iwl_calib_hdr *cmd, int len); +void iwl_calib_free_results(struct iwl_trans *trans); + #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c b/drivers/net/wireless/iwlwifi/iwl-ucode.c index 8ba0dd54e37..36a1b5b2585 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-ucode.c +++ b/drivers/net/wireless/iwlwifi/iwl-ucode.c @@ -31,7 +31,9 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/sched.h> +#include <linux/dma-mapping.h> +#include "iwl-wifi.h" #include "iwl-dev.h" #include "iwl-core.h" #include "iwl-io.h" @@ -72,51 +74,98 @@ static struct iwl_wimax_coex_event_entry cu_priorities[COEX_NUM_OF_EVENTS] = { {COEX_CU_RSRVD2_RP, COEX_CU_RSRVD2_WP, 0, COEX_RSRVD2_FLAGS} }; +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static void iwl_free_fw_desc(struct iwl_bus *bus, struct fw_desc *desc) +{ + if (desc->v_addr) + dma_free_coherent(bus->dev, desc->len, + desc->v_addr, desc->p_addr); + desc->v_addr = NULL; + desc->len = 0; +} + +static void iwl_free_fw_img(struct iwl_bus *bus, struct fw_img *img) +{ + iwl_free_fw_desc(bus, &img->code); + iwl_free_fw_desc(bus, &img->data); +} + +void iwl_dealloc_ucode(struct iwl_trans *trans) +{ + iwl_free_fw_img(bus(trans), &trans->ucode_rt); + iwl_free_fw_img(bus(trans), &trans->ucode_init); + iwl_free_fw_img(bus(trans), &trans->ucode_wowlan); +} + +int iwl_alloc_fw_desc(struct iwl_bus *bus, struct fw_desc *desc, + const void *data, size_t len) +{ + if (!len) { + desc->v_addr = NULL; + return -EINVAL; + } + + desc->v_addr = dma_alloc_coherent(bus->dev, len, + &desc->p_addr, GFP_KERNEL); + if (!desc->v_addr) + return -ENOMEM; + + desc->len = len; + memcpy(desc->v_addr, data, len); + return 0; +} + /* * ucode */ -static int iwlagn_load_section(struct iwl_priv *priv, const char *name, +static int iwl_load_section(struct iwl_trans *trans, const char *name, struct fw_desc *image, u32 dst_addr) { + struct iwl_bus *bus = bus(trans); dma_addr_t phy_addr = image->p_addr; u32 byte_cnt = image->len; int ret; - priv->ucode_write_complete = 0; + trans->ucode_write_complete = 0; - iwl_write_direct32(bus(priv), + iwl_write_direct32(bus, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); - iwl_write_direct32(bus(priv), + iwl_write_direct32(bus, FH_SRVC_CHNL_SRAM_ADDR_REG(FH_SRVC_CHNL), dst_addr); - iwl_write_direct32(bus(priv), + iwl_write_direct32(bus, FH_TFDIB_CTRL0_REG(FH_SRVC_CHNL), phy_addr & FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); - iwl_write_direct32(bus(priv), + iwl_write_direct32(bus, FH_TFDIB_CTRL1_REG(FH_SRVC_CHNL), (iwl_get_dma_hi_addr(phy_addr) << FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); - iwl_write_direct32(bus(priv), + iwl_write_direct32(bus, FH_TCSR_CHNL_TX_BUF_STS_REG(FH_SRVC_CHNL), 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 1 << FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); - iwl_write_direct32(bus(priv), + iwl_write_direct32(bus, FH_TCSR_CHNL_TX_CONFIG_REG(FH_SRVC_CHNL), FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); - IWL_DEBUG_FW(priv, "%s uCode section being loaded...\n", name); - ret = wait_event_timeout(priv->shrd->wait_command_queue, - priv->ucode_write_complete, 5 * HZ); + IWL_DEBUG_FW(bus, "%s uCode section being loaded...\n", name); + ret = wait_event_timeout(trans->shrd->wait_command_queue, + trans->ucode_write_complete, 5 * HZ); if (!ret) { - IWL_ERR(priv, "Could not load the %s uCode section\n", + IWL_ERR(trans, "Could not load the %s uCode section\n", name); return -ETIMEDOUT; } @@ -124,41 +173,65 @@ static int iwlagn_load_section(struct iwl_priv *priv, const char *name, return 0; } -static int iwlagn_load_given_ucode(struct iwl_priv *priv, - struct fw_img *image) +static inline struct fw_img *iwl_get_ucode_image(struct iwl_trans *trans, + enum iwl_ucode_type ucode_type) +{ + switch (ucode_type) { + case IWL_UCODE_INIT: + return &trans->ucode_init; + case IWL_UCODE_WOWLAN: + return &trans->ucode_wowlan; + case IWL_UCODE_REGULAR: + return &trans->ucode_rt; + case IWL_UCODE_NONE: + break; + } + return NULL; +} + +static int iwl_load_given_ucode(struct iwl_trans *trans, + enum iwl_ucode_type ucode_type) { int ret = 0; + struct fw_img *image = iwl_get_ucode_image(trans, ucode_type); - ret = iwlagn_load_section(priv, "INST", &image->code, + + if (!image) { + IWL_ERR(trans, "Invalid ucode requested (%d)\n", + ucode_type); + return -EINVAL; + } + + ret = iwl_load_section(trans, "INST", &image->code, IWLAGN_RTC_INST_LOWER_BOUND); if (ret) return ret; - return iwlagn_load_section(priv, "DATA", &image->data, + return iwl_load_section(trans, "DATA", &image->data, IWLAGN_RTC_DATA_LOWER_BOUND); } /* * Calibration */ -static int iwlagn_set_Xtal_calib(struct iwl_priv *priv) +static int iwl_set_Xtal_calib(struct iwl_trans *trans) { struct iwl_calib_xtal_freq_cmd cmd; __le16 *xtal_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_XTAL); + (__le16 *)iwl_eeprom_query_addr(trans->shrd, EEPROM_XTAL); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_CRYSTAL_FRQ_CMD); cmd.cap_pin1 = le16_to_cpu(xtal_calib[0]); cmd.cap_pin2 = le16_to_cpu(xtal_calib[1]); - return iwl_calib_set(&priv->calib_results[IWL_CALIB_XTAL], - (u8 *)&cmd, sizeof(cmd)); + return iwl_calib_set(trans, (void *)&cmd, sizeof(cmd)); } -static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv) +static int iwl_set_temperature_offset_calib(struct iwl_trans *trans) { struct iwl_calib_temperature_offset_cmd cmd; __le16 *offset_calib = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); + (__le16 *)iwl_eeprom_query_addr(trans->shrd, + EEPROM_RAW_TEMPERATURE); memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); @@ -166,49 +239,48 @@ static int iwlagn_set_temperature_offset_calib(struct iwl_priv *priv) if (!(cmd.radio_sensor_offset)) cmd.radio_sensor_offset = DEFAULT_RADIO_SENSOR_OFFSET; - IWL_DEBUG_CALIB(priv, "Radio sensor offset: %d\n", + IWL_DEBUG_CALIB(trans, "Radio sensor offset: %d\n", le16_to_cpu(cmd.radio_sensor_offset)); - return iwl_calib_set(&priv->calib_results[IWL_CALIB_TEMP_OFFSET], - (u8 *)&cmd, sizeof(cmd)); + return iwl_calib_set(trans, (void *)&cmd, sizeof(cmd)); } -static int iwlagn_set_temperature_offset_calib_v2(struct iwl_priv *priv) +static int iwl_set_temperature_offset_calib_v2(struct iwl_trans *trans) { struct iwl_calib_temperature_offset_v2_cmd cmd; - __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(priv, + __le16 *offset_calib_high = (__le16 *)iwl_eeprom_query_addr(trans->shrd, EEPROM_KELVIN_TEMPERATURE); __le16 *offset_calib_low = - (__le16 *)iwl_eeprom_query_addr(priv, EEPROM_RAW_TEMPERATURE); + (__le16 *)iwl_eeprom_query_addr(trans->shrd, + EEPROM_RAW_TEMPERATURE); struct iwl_eeprom_calib_hdr *hdr; memset(&cmd, 0, sizeof(cmd)); iwl_set_calib_hdr(&cmd.hdr, IWL_PHY_CALIBRATE_TEMP_OFFSET_CMD); - hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(priv, + hdr = (struct iwl_eeprom_calib_hdr *)iwl_eeprom_query_addr(trans->shrd, EEPROM_CALIB_ALL); memcpy(&cmd.radio_sensor_offset_high, offset_calib_high, sizeof(*offset_calib_high)); memcpy(&cmd.radio_sensor_offset_low, offset_calib_low, sizeof(*offset_calib_low)); if (!(cmd.radio_sensor_offset_low)) { - IWL_DEBUG_CALIB(priv, "no info in EEPROM, use default\n"); + IWL_DEBUG_CALIB(trans, "no info in EEPROM, use default\n"); cmd.radio_sensor_offset_low = DEFAULT_RADIO_SENSOR_OFFSET; cmd.radio_sensor_offset_high = DEFAULT_RADIO_SENSOR_OFFSET; } memcpy(&cmd.burntVoltageRef, &hdr->voltage, sizeof(hdr->voltage)); - IWL_DEBUG_CALIB(priv, "Radio sensor offset high: %d\n", + IWL_DEBUG_CALIB(trans, "Radio sensor offset high: %d\n", le16_to_cpu(cmd.radio_sensor_offset_high)); - IWL_DEBUG_CALIB(priv, "Radio sensor offset low: %d\n", + IWL_DEBUG_CALIB(trans, "Radio sensor offset low: %d\n", le16_to_cpu(cmd.radio_sensor_offset_low)); - IWL_DEBUG_CALIB(priv, "Voltage Ref: %d\n", + IWL_DEBUG_CALIB(trans, "Voltage Ref: %d\n", le16_to_cpu(cmd.burntVoltageRef)); - return iwl_calib_set(&priv->calib_results[IWL_CALIB_TEMP_OFFSET], - (u8 *)&cmd, sizeof(cmd)); + return iwl_calib_set(trans, (void *)&cmd, sizeof(cmd)); } -static int iwlagn_send_calib_cfg(struct iwl_priv *priv) +static int iwl_send_calib_cfg(struct iwl_trans *trans) { struct iwl_calib_cfg_cmd calib_cfg_cmd; struct iwl_host_cmd cmd = { @@ -224,7 +296,7 @@ static int iwlagn_send_calib_cfg(struct iwl_priv *priv) calib_cfg_cmd.ucd_calib_cfg.flags = IWL_CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_MSK; - return iwl_trans_send_cmd(trans(priv), &cmd); + return iwl_trans_send_cmd(trans, &cmd); } int iwlagn_rx_calib_result(struct iwl_priv *priv, @@ -234,60 +306,37 @@ int iwlagn_rx_calib_result(struct iwl_priv *priv, struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_calib_hdr *hdr = (struct iwl_calib_hdr *)pkt->u.raw; int len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; - int index; /* reduce the size of the length field itself */ len -= 4; - /* Define the order in which the results will be sent to the runtime - * uCode. iwl_send_calib_results sends them in a row according to - * their index. We sort them here - */ - switch (hdr->op_code) { - case IWL_PHY_CALIBRATE_DC_CMD: - index = IWL_CALIB_DC; - break; - case IWL_PHY_CALIBRATE_LO_CMD: - index = IWL_CALIB_LO; - break; - case IWL_PHY_CALIBRATE_TX_IQ_CMD: - index = IWL_CALIB_TX_IQ; - break; - case IWL_PHY_CALIBRATE_TX_IQ_PERD_CMD: - index = IWL_CALIB_TX_IQ_PERD; - break; - case IWL_PHY_CALIBRATE_BASE_BAND_CMD: - index = IWL_CALIB_BASE_BAND; - break; - default: - IWL_ERR(priv, "Unknown calibration notification %d\n", - hdr->op_code); - return -1; - } - iwl_calib_set(&priv->calib_results[index], pkt->u.raw, len); + if (iwl_calib_set(trans(priv), hdr, len)) + IWL_ERR(priv, "Failed to record calibration data %d\n", + hdr->op_code); + return 0; } -int iwlagn_init_alive_start(struct iwl_priv *priv) +int iwl_init_alive_start(struct iwl_trans *trans) { int ret; - if (priv->cfg->bt_params && - priv->cfg->bt_params->advanced_bt_coexist) { + if (cfg(trans)->bt_params && + cfg(trans)->bt_params->advanced_bt_coexist) { /* * Tell uCode we are ready to perform calibration * need to perform this before any calibration * no need to close the envlope since we are going * to load the runtime uCode later. */ - ret = iwlagn_send_bt_env(priv, IWL_BT_COEX_ENV_OPEN, + ret = iwl_send_bt_env(trans, IWL_BT_COEX_ENV_OPEN, BT_COEX_PRIO_TBL_EVT_INIT_CALIB2); if (ret) return ret; } - ret = iwlagn_send_calib_cfg(priv); + ret = iwl_send_calib_cfg(trans); if (ret) return ret; @@ -295,21 +344,21 @@ int iwlagn_init_alive_start(struct iwl_priv *priv) * temperature offset calibration is only needed for runtime ucode, * so prepare the value now. */ - if (priv->cfg->need_temp_offset_calib) { - if (priv->cfg->temp_offset_v2) - return iwlagn_set_temperature_offset_calib_v2(priv); + if (cfg(trans)->need_temp_offset_calib) { + if (cfg(trans)->temp_offset_v2) + return iwl_set_temperature_offset_calib_v2(trans); else - return iwlagn_set_temperature_offset_calib(priv); + return iwl_set_temperature_offset_calib(trans); } return 0; } -static int iwlagn_send_wimax_coex(struct iwl_priv *priv) +static int iwl_send_wimax_coex(struct iwl_trans *trans) { struct iwl_wimax_coex_cmd coex_cmd; - if (priv->cfg->base_params->support_wimax_coexist) { + if (cfg(trans)->base_params->support_wimax_coexist) { /* UnMask wake up src at associated sleep */ coex_cmd.flags = COEX_FLAGS_ASSOC_WA_UNMASK_MSK; @@ -328,12 +377,12 @@ static int iwlagn_send_wimax_coex(struct iwl_priv *priv) /* coexistence is disabled */ memset(&coex_cmd, 0, sizeof(coex_cmd)); } - return iwl_trans_send_cmd_pdu(trans(priv), + return iwl_trans_send_cmd_pdu(trans, COEX_PRIORITY_TABLE_CMD, CMD_SYNC, sizeof(coex_cmd), &coex_cmd); } -static const u8 iwlagn_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { +static const u8 iwl_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | (0 << IWL_BT_COEX_PRIO_TBL_SHARED_ANTENNA_POS)), ((BT_COEX_PRIO_TBL_PRIO_BYPASS << IWL_BT_COEX_PRIO_TBL_PRIO_POS) | @@ -355,61 +404,64 @@ static const u8 iwlagn_bt_prio_tbl[BT_COEX_PRIO_TBL_EVT_MAX] = { 0, 0, 0, 0, 0, 0, 0 }; -void iwlagn_send_prio_tbl(struct iwl_priv *priv) +void iwl_send_prio_tbl(struct iwl_trans *trans) { struct iwl_bt_coex_prio_table_cmd prio_tbl_cmd; - memcpy(prio_tbl_cmd.prio_tbl, iwlagn_bt_prio_tbl, - sizeof(iwlagn_bt_prio_tbl)); - if (iwl_trans_send_cmd_pdu(trans(priv), + memcpy(prio_tbl_cmd.prio_tbl, iwl_bt_prio_tbl, + sizeof(iwl_bt_prio_tbl)); + if (iwl_trans_send_cmd_pdu(trans, REPLY_BT_COEX_PRIO_TABLE, CMD_SYNC, sizeof(prio_tbl_cmd), &prio_tbl_cmd)) - IWL_ERR(priv, "failed to send BT prio tbl command\n"); + IWL_ERR(trans, "failed to send BT prio tbl command\n"); } -int iwlagn_send_bt_env(struct iwl_priv *priv, u8 action, u8 type) +int iwl_send_bt_env(struct iwl_trans *trans, u8 action, u8 type) { struct iwl_bt_coex_prot_env_cmd env_cmd; int ret; env_cmd.action = action; env_cmd.type = type; - ret = iwl_trans_send_cmd_pdu(trans(priv), + ret = iwl_trans_send_cmd_pdu(trans, REPLY_BT_COEX_PROT_ENV, CMD_SYNC, sizeof(env_cmd), &env_cmd); if (ret) - IWL_ERR(priv, "failed to send BT env command\n"); + IWL_ERR(trans, "failed to send BT env command\n"); return ret; } -static int iwlagn_alive_notify(struct iwl_priv *priv) +static int iwl_alive_notify(struct iwl_trans *trans) { + struct iwl_priv *priv = priv(trans); struct iwl_rxon_context *ctx; int ret; if (!priv->tx_cmd_pool) priv->tx_cmd_pool = - kmem_cache_create("iwlagn_dev_cmd", + kmem_cache_create("iwl_dev_cmd", sizeof(struct iwl_device_cmd), sizeof(void *), 0, NULL); if (!priv->tx_cmd_pool) return -ENOMEM; - iwl_trans_tx_start(trans(priv)); + iwl_trans_tx_start(trans); for_each_context(priv, ctx) ctx->last_tx_rejected = false; - ret = iwlagn_send_wimax_coex(priv); + ret = iwl_send_wimax_coex(trans); if (ret) return ret; - ret = iwlagn_set_Xtal_calib(priv); - if (ret) - return ret; + if (!cfg(priv)->no_xtal_calib) { + ret = iwl_set_Xtal_calib(trans); + if (ret) + return ret; + } - return iwl_send_calib_results(priv); + return iwl_send_calib_results(trans); } @@ -418,7 +470,7 @@ static int iwlagn_alive_notify(struct iwl_priv *priv) * using sample data 100 bytes apart. If these sample points are good, * it's a pretty good bet that everything between them is good, too. */ -static int iwl_verify_inst_sparse(struct iwl_priv *priv, +static int iwl_verify_inst_sparse(struct iwl_bus *bus, struct fw_desc *fw_desc) { __le32 *image = (__le32 *)fw_desc->v_addr; @@ -426,15 +478,15 @@ static int iwl_verify_inst_sparse(struct iwl_priv *priv, u32 val; u32 i; - IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); + IWL_DEBUG_FW(bus, "ucode inst image size is %u\n", len); for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) { /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IWL_DL_IO is set */ - iwl_write_direct32(bus(priv), HBUS_TARG_MEM_RADDR, + iwl_write_direct32(bus, HBUS_TARG_MEM_RADDR, i + IWLAGN_RTC_INST_LOWER_BOUND); - val = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); + val = iwl_read32(bus, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) return -EIO; } @@ -442,7 +494,7 @@ static int iwl_verify_inst_sparse(struct iwl_priv *priv, return 0; } -static void iwl_print_mismatch_inst(struct iwl_priv *priv, +static void iwl_print_mismatch_inst(struct iwl_bus *bus, struct fw_desc *fw_desc) { __le32 *image = (__le32 *)fw_desc->v_addr; @@ -451,18 +503,18 @@ static void iwl_print_mismatch_inst(struct iwl_priv *priv, u32 offs; int errors = 0; - IWL_DEBUG_FW(priv, "ucode inst image size is %u\n", len); + IWL_DEBUG_FW(bus, "ucode inst image size is %u\n", len); - iwl_write_direct32(bus(priv), HBUS_TARG_MEM_RADDR, + iwl_write_direct32(bus, HBUS_TARG_MEM_RADDR, IWLAGN_RTC_INST_LOWER_BOUND); for (offs = 0; offs < len && errors < 20; offs += sizeof(u32), image++) { /* read data comes through single port, auto-incr addr */ - val = iwl_read32(bus(priv), HBUS_TARG_MEM_RDAT); + val = iwl_read32(bus, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { - IWL_ERR(priv, "uCode INST section at " + IWL_ERR(bus, "uCode INST section at " "offset 0x%x, is 0x%x, s/b 0x%x\n", offs, val, le32_to_cpu(*image)); errors++; @@ -474,91 +526,163 @@ static void iwl_print_mismatch_inst(struct iwl_priv *priv, * iwl_verify_ucode - determine which instruction image is in SRAM, * and verify its contents */ -static int iwl_verify_ucode(struct iwl_priv *priv, struct fw_img *img) +static int iwl_verify_ucode(struct iwl_trans *trans, + enum iwl_ucode_type ucode_type) { - if (!iwl_verify_inst_sparse(priv, &img->code)) { - IWL_DEBUG_FW(priv, "uCode is good in inst SRAM\n"); + struct fw_img *img = iwl_get_ucode_image(trans, ucode_type); + + if (!img) { + IWL_ERR(trans, "Invalid ucode requested (%d)\n", ucode_type); + return -EINVAL; + } + + if (!iwl_verify_inst_sparse(bus(trans), &img->code)) { + IWL_DEBUG_FW(trans, "uCode is good in inst SRAM\n"); return 0; } - IWL_ERR(priv, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n"); + IWL_ERR(trans, "UCODE IMAGE IN INSTRUCTION SRAM NOT VALID!!\n"); - iwl_print_mismatch_inst(priv, &img->code); + iwl_print_mismatch_inst(bus(trans), &img->code); return -EIO; } -struct iwlagn_alive_data { +struct iwl_alive_data { bool valid; u8 subtype; }; -static void iwlagn_alive_fn(struct iwl_priv *priv, +static void iwl_alive_fn(struct iwl_trans *trans, struct iwl_rx_packet *pkt, void *data) { - struct iwlagn_alive_data *alive_data = data; + struct iwl_alive_data *alive_data = data; struct iwl_alive_resp *palive; palive = &pkt->u.alive_frame; - IWL_DEBUG_FW(priv, "Alive ucode status 0x%08X revision " + IWL_DEBUG_FW(trans, "Alive ucode status 0x%08X revision " "0x%01X 0x%01X\n", palive->is_valid, palive->ver_type, palive->ver_subtype); - priv->device_pointers.error_event_table = + trans->shrd->device_pointers.error_event_table = le32_to_cpu(palive->error_event_table_ptr); - priv->device_pointers.log_event_table = + trans->shrd->device_pointers.log_event_table = le32_to_cpu(palive->log_event_table_ptr); alive_data->subtype = palive->ver_subtype; alive_data->valid = palive->is_valid == UCODE_VALID_OK; } +/* notification wait support */ +void iwl_init_notification_wait(struct iwl_shared *shrd, + struct iwl_notification_wait *wait_entry, + u8 cmd, + void (*fn)(struct iwl_trans *trans, + struct iwl_rx_packet *pkt, + void *data), + void *fn_data) +{ + wait_entry->fn = fn; + wait_entry->fn_data = fn_data; + wait_entry->cmd = cmd; + wait_entry->triggered = false; + wait_entry->aborted = false; + + spin_lock_bh(&shrd->notif_wait_lock); + list_add(&wait_entry->list, &shrd->notif_waits); + spin_unlock_bh(&shrd->notif_wait_lock); +} + +int iwl_wait_notification(struct iwl_shared *shrd, + struct iwl_notification_wait *wait_entry, + unsigned long timeout) +{ + int ret; + + ret = wait_event_timeout(shrd->notif_waitq, + wait_entry->triggered || wait_entry->aborted, + timeout); + + spin_lock_bh(&shrd->notif_wait_lock); + list_del(&wait_entry->list); + spin_unlock_bh(&shrd->notif_wait_lock); + + if (wait_entry->aborted) + return -EIO; + + /* return value is always >= 0 */ + if (ret <= 0) + return -ETIMEDOUT; + return 0; +} + +void iwl_remove_notification(struct iwl_shared *shrd, + struct iwl_notification_wait *wait_entry) +{ + spin_lock_bh(&shrd->notif_wait_lock); + list_del(&wait_entry->list); + spin_unlock_bh(&shrd->notif_wait_lock); +} + +void iwl_abort_notification_waits(struct iwl_shared *shrd) +{ + unsigned long flags; + struct iwl_notification_wait *wait_entry; + + spin_lock_irqsave(&shrd->notif_wait_lock, flags); + list_for_each_entry(wait_entry, &shrd->notif_waits, list) + wait_entry->aborted = true; + spin_unlock_irqrestore(&shrd->notif_wait_lock, flags); + + wake_up_all(&shrd->notif_waitq); +} + #define UCODE_ALIVE_TIMEOUT HZ #define UCODE_CALIB_TIMEOUT (2*HZ) -int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, - struct fw_img *image, - enum iwlagn_ucode_type ucode_type) +int iwl_load_ucode_wait_alive(struct iwl_trans *trans, + enum iwl_ucode_type ucode_type) { struct iwl_notification_wait alive_wait; - struct iwlagn_alive_data alive_data; + struct iwl_alive_data alive_data; int ret; - enum iwlagn_ucode_type old_type; + enum iwl_ucode_type old_type; - ret = iwl_trans_start_device(trans(priv)); + ret = iwl_trans_start_device(trans); if (ret) return ret; - iwlagn_init_notification_wait(priv, &alive_wait, REPLY_ALIVE, - iwlagn_alive_fn, &alive_data); + iwl_init_notification_wait(trans->shrd, &alive_wait, REPLY_ALIVE, + iwl_alive_fn, &alive_data); - old_type = priv->ucode_type; - priv->ucode_type = ucode_type; + old_type = trans->shrd->ucode_type; + trans->shrd->ucode_type = ucode_type; - ret = iwlagn_load_given_ucode(priv, image); + ret = iwl_load_given_ucode(trans, ucode_type); if (ret) { - priv->ucode_type = old_type; - iwlagn_remove_notification(priv, &alive_wait); + trans->shrd->ucode_type = old_type; + iwl_remove_notification(trans->shrd, &alive_wait); return ret; } - iwl_trans_kick_nic(trans(priv)); + iwl_trans_kick_nic(trans); /* * Some things may run in the background now, but we * just wait for the ALIVE notification here. */ - ret = iwlagn_wait_notification(priv, &alive_wait, UCODE_ALIVE_TIMEOUT); + ret = iwl_wait_notification(trans->shrd, &alive_wait, + UCODE_ALIVE_TIMEOUT); if (ret) { - priv->ucode_type = old_type; + trans->shrd->ucode_type = old_type; return ret; } if (!alive_data.valid) { - IWL_ERR(priv, "Loaded ucode is not valid!\n"); - priv->ucode_type = old_type; + IWL_ERR(trans, "Loaded ucode is not valid!\n"); + trans->shrd->ucode_type = old_type; return -EIO; } @@ -568,9 +692,9 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, * skip it for WoWLAN. */ if (ucode_type != IWL_UCODE_WOWLAN) { - ret = iwl_verify_ucode(priv, image); + ret = iwl_verify_ucode(trans, ucode_type); if (ret) { - priv->ucode_type = old_type; + trans->shrd->ucode_type = old_type; return ret; } @@ -578,42 +702,41 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, msleep(5); } - ret = iwlagn_alive_notify(priv); + ret = iwl_alive_notify(trans); if (ret) { - IWL_WARN(priv, + IWL_WARN(trans, "Could not complete ALIVE transition: %d\n", ret); - priv->ucode_type = old_type; + trans->shrd->ucode_type = old_type; return ret; } return 0; } -int iwlagn_run_init_ucode(struct iwl_priv *priv) +int iwl_run_init_ucode(struct iwl_trans *trans) { struct iwl_notification_wait calib_wait; int ret; - lockdep_assert_held(&priv->shrd->mutex); + lockdep_assert_held(&trans->shrd->mutex); /* No init ucode required? Curious, but maybe ok */ - if (!priv->ucode_init.code.len) + if (!trans->ucode_init.code.len) return 0; - if (priv->ucode_type != IWL_UCODE_NONE) + if (trans->shrd->ucode_type != IWL_UCODE_NONE) return 0; - iwlagn_init_notification_wait(priv, &calib_wait, + iwl_init_notification_wait(trans->shrd, &calib_wait, CALIBRATION_COMPLETE_NOTIFICATION, NULL, NULL); /* Will also start the device */ - ret = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_init, - IWL_UCODE_INIT); + ret = iwl_load_ucode_wait_alive(trans, IWL_UCODE_INIT); if (ret) goto error; - ret = iwlagn_init_alive_start(priv); + ret = iwl_init_alive_start(trans); if (ret) goto error; @@ -621,14 +744,15 @@ int iwlagn_run_init_ucode(struct iwl_priv *priv) * Some things may run in the background now, but we * just wait for the calibration complete notification. */ - ret = iwlagn_wait_notification(priv, &calib_wait, UCODE_CALIB_TIMEOUT); + ret = iwl_wait_notification(trans->shrd, &calib_wait, + UCODE_CALIB_TIMEOUT); goto out; error: - iwlagn_remove_notification(priv, &calib_wait); + iwl_remove_notification(trans->shrd, &calib_wait); out: /* Whatever happened, stop the device */ - iwl_trans_stop_device(trans(priv)); + iwl_trans_stop_device(trans); return ret; } diff --git a/drivers/net/wireless/iwlwifi/iwl-wifi.h b/drivers/net/wireless/iwlwifi/iwl-wifi.h new file mode 100644 index 00000000000..18501101a53 --- /dev/null +++ b/drivers/net/wireless/iwlwifi/iwl-wifi.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_wifi_h__ +#define __iwl_wifi_h__ + +#include "iwl-shared.h" + +int iwl_send_bt_env(struct iwl_trans *trans, u8 action, u8 type); +void iwl_send_prio_tbl(struct iwl_trans *trans); +int iwl_init_alive_start(struct iwl_trans *trans); +int iwl_run_init_ucode(struct iwl_trans *trans); +int iwl_load_ucode_wait_alive(struct iwl_trans *trans, + enum iwl_ucode_type ucode_type); +#endif /* __iwl_wifi_h__ */ |