diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-core.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-core.c | 747 |
1 files changed, 465 insertions, 282 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 3d9443b9bec1..87a2e40972ba 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -64,7 +64,8 @@ MODULE_LICENSE("GPL"); * * default: bt_coex_active = true (BT_COEX_ENABLE) */ -static bool bt_coex_active = true; +bool bt_coex_active = true; +EXPORT_SYMBOL_GPL(bt_coex_active); module_param(bt_coex_active, bool, S_IRUGO); MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); @@ -146,6 +147,10 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) int i; u8 ind = ant; + if (priv->band == IEEE80211_BAND_2GHZ && + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) + return 0; + for (i = 0; i < RATE_ANT_NUM - 1; i++) { ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; if (valid & BIT(ind)) @@ -186,27 +191,27 @@ EXPORT_SYMBOL(iwl_alloc_all); /* * QoS support */ -static void iwl_update_qos(struct iwl_priv *priv) +static void iwl_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - priv->qos_data.def_qos_parm.qos_flags = 0; + ctx->qos_data.def_qos_parm.qos_flags = 0; - if (priv->qos_data.qos_active) - priv->qos_data.def_qos_parm.qos_flags |= + if (ctx->qos_data.qos_active) + ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_UPDATE_EDCA_MSK; - if (priv->current_ht_config.is_ht) - priv->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; + 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", - priv->qos_data.qos_active, - priv->qos_data.def_qos_parm.qos_flags); + ctx->qos_data.qos_active, + ctx->qos_data.def_qos_parm.qos_flags); - iwl_send_cmd_pdu_async(priv, REPLY_QOS_PARAM, + iwl_send_cmd_pdu_async(priv, ctx->qos_cmd, sizeof(struct iwl_qosparam_cmd), - &priv->qos_data.def_qos_parm, NULL); + &ctx->qos_data.def_qos_parm, NULL); } #define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ @@ -436,15 +441,15 @@ static bool is_single_rx_stream(struct iwl_priv *priv) priv->current_ht_config.single_chain_sufficient; } -static u8 iwl_is_channel_extension(struct iwl_priv *priv, - enum ieee80211_band band, - u16 channel, u8 extension_chan_offset) +static bool iwl_is_channel_extension(struct iwl_priv *priv, + enum ieee80211_band band, + u16 channel, u8 extension_chan_offset) { const struct iwl_channel_info *ch_info; ch_info = iwl_get_channel_info(priv, band, channel); if (!is_channel_valid(ch_info)) - return 0; + return false; if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) return !(ch_info->ht40_extension_channel & @@ -453,31 +458,31 @@ static u8 iwl_is_channel_extension(struct iwl_priv *priv, return !(ch_info->ht40_extension_channel & IEEE80211_CHAN_NO_HT40MINUS); - return 0; + return false; } -u8 iwl_is_ht40_tx_allowed(struct iwl_priv *priv, - struct ieee80211_sta_ht_cap *sta_ht_inf) +bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta_ht_cap *ht_cap) { - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - - if (!ht_conf->is_ht || !ht_conf->is_40mhz) - return 0; + if (!ctx->ht.enabled || !ctx->ht.is_40mhz) + return false; - /* We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 + /* + * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 * the bit will not set if it is pure 40MHz case */ - if (sta_ht_inf) { - if (!sta_ht_inf->ht_supported) - return 0; - } + if (ht_cap && !ht_cap->ht_supported) + return false; + #ifdef CONFIG_IWLWIFI_DEBUGFS if (priv->disable_ht40) - return 0; + return false; #endif + return iwl_is_channel_extension(priv, priv->band, - le16_to_cpu(priv->staging_rxon.channel), - ht_conf->extension_chan_offset); + le16_to_cpu(ctx->staging.channel), + ctx->ht.extension_chan_offset); } EXPORT_SYMBOL(iwl_is_ht40_tx_allowed); @@ -495,55 +500,64 @@ static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) return new_val; } -int iwl_send_rxon_timing(struct iwl_priv *priv, struct ieee80211_vif *vif) +int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { u64 tsf; s32 interval_tm, rem; struct ieee80211_conf *conf = NULL; u16 beacon_int; + struct ieee80211_vif *vif = ctx->vif; conf = ieee80211_get_hw_conf(priv->hw); lockdep_assert_held(&priv->mutex); - memset(&priv->rxon_timing, 0, sizeof(struct iwl_rxon_time_cmd)); + memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); - priv->rxon_timing.timestamp = cpu_to_le64(priv->timestamp); - priv->rxon_timing.listen_interval = cpu_to_le16(conf->listen_interval); + ctx->timing.timestamp = cpu_to_le64(priv->timestamp); + ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); - beacon_int = vif->bss_conf.beacon_int; + beacon_int = vif ? vif->bss_conf.beacon_int : 0; - if (vif->type == NL80211_IFTYPE_ADHOC) { - /* TODO: we need to get atim_window from upper stack - * for now we set to 0 */ - priv->rxon_timing.atim_window = 0; - } else { - priv->rxon_timing.atim_window = 0; - } + /* + * TODO: For IBSS we need to get atim_window from mac80211, + * for now just always use 0 + */ + ctx->timing.atim_window = 0; - beacon_int = iwl_adjust_beacon_interval(beacon_int, + if (ctx->ctxid == IWL_RXON_CTX_PAN && + (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION)) { + ctx->timing.beacon_interval = + priv->contexts[IWL_RXON_CTX_BSS].timing.beacon_interval; + beacon_int = le16_to_cpu(ctx->timing.beacon_interval); + } else { + beacon_int = iwl_adjust_beacon_interval(beacon_int, priv->hw_params.max_beacon_itrvl * TIME_UNIT); - priv->rxon_timing.beacon_interval = cpu_to_le16(beacon_int); + ctx->timing.beacon_interval = cpu_to_le16(beacon_int); + } tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ interval_tm = beacon_int * TIME_UNIT; rem = do_div(tsf, interval_tm); - priv->rxon_timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; IWL_DEBUG_ASSOC(priv, "beacon interval %d beacon timer %d beacon tim %d\n", - le16_to_cpu(priv->rxon_timing.beacon_interval), - le32_to_cpu(priv->rxon_timing.beacon_init_val), - le16_to_cpu(priv->rxon_timing.atim_window)); + le16_to_cpu(ctx->timing.beacon_interval), + le32_to_cpu(ctx->timing.beacon_init_val), + le16_to_cpu(ctx->timing.atim_window)); - return iwl_send_cmd_pdu(priv, REPLY_RXON_TIMING, - sizeof(priv->rxon_timing), &priv->rxon_timing); + return iwl_send_cmd_pdu(priv, ctx->rxon_timing_cmd, + sizeof(ctx->timing), &ctx->timing); } EXPORT_SYMBOL(iwl_send_rxon_timing); -void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt) +void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + int hw_decrypt) { - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + struct iwl_rxon_cmd *rxon = &ctx->staging; if (hw_decrypt) rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; @@ -560,11 +574,11 @@ EXPORT_SYMBOL(iwl_set_rxon_hwcrypto); * be #ifdef'd out once the driver is stable and folks aren't actively * making changes */ -int iwl_check_rxon_cmd(struct iwl_priv *priv) +int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int error = 0; int counter = 1; - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + struct iwl_rxon_cmd *rxon = &ctx->staging; if (rxon->flags & RXON_FLG_BAND_24G_MSK) { error |= le32_to_cpu(rxon->flags & @@ -636,66 +650,83 @@ EXPORT_SYMBOL(iwl_check_rxon_cmd); * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. */ -int iwl_full_rxon_required(struct iwl_priv *priv) +int iwl_full_rxon_required(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) { + const struct iwl_rxon_cmd *staging = &ctx->staging; + const struct iwl_rxon_cmd *active = &ctx->active; + +#define CHK(cond) \ + if ((cond)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ + return 1; \ + } + +#define CHK_NEQ(c1, c2) \ + if ((c1) != (c2)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " \ + #c1 " != " #c2 " - %d != %d\n", \ + (c1), (c2)); \ + return 1; \ + } /* These items are only settable from the full RXON command */ - if (!(iwl_is_associated(priv)) || - compare_ether_addr(priv->staging_rxon.bssid_addr, - priv->active_rxon.bssid_addr) || - compare_ether_addr(priv->staging_rxon.node_addr, - priv->active_rxon.node_addr) || - compare_ether_addr(priv->staging_rxon.wlap_bssid_addr, - priv->active_rxon.wlap_bssid_addr) || - (priv->staging_rxon.dev_type != priv->active_rxon.dev_type) || - (priv->staging_rxon.channel != priv->active_rxon.channel) || - (priv->staging_rxon.air_propagation != - priv->active_rxon.air_propagation) || - (priv->staging_rxon.ofdm_ht_single_stream_basic_rates != - priv->active_rxon.ofdm_ht_single_stream_basic_rates) || - (priv->staging_rxon.ofdm_ht_dual_stream_basic_rates != - priv->active_rxon.ofdm_ht_dual_stream_basic_rates) || - (priv->staging_rxon.ofdm_ht_triple_stream_basic_rates != - priv->active_rxon.ofdm_ht_triple_stream_basic_rates) || - (priv->staging_rxon.assoc_id != priv->active_rxon.assoc_id)) - return 1; + CHK(!iwl_is_associated_ctx(ctx)); + CHK(compare_ether_addr(staging->bssid_addr, active->bssid_addr)); + CHK(compare_ether_addr(staging->node_addr, active->node_addr)); + CHK(compare_ether_addr(staging->wlap_bssid_addr, + active->wlap_bssid_addr)); + CHK_NEQ(staging->dev_type, active->dev_type); + CHK_NEQ(staging->channel, active->channel); + CHK_NEQ(staging->air_propagation, active->air_propagation); + CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, + active->ofdm_ht_single_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, + active->ofdm_ht_dual_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_triple_stream_basic_rates, + active->ofdm_ht_triple_stream_basic_rates); + CHK_NEQ(staging->assoc_id, active->assoc_id); /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can * be updated with the RXON_ASSOC command -- however only some * flag transitions are allowed using RXON_ASSOC */ /* Check if we are not switching bands */ - if ((priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) != - (priv->active_rxon.flags & RXON_FLG_BAND_24G_MSK)) - return 1; + CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, + active->flags & RXON_FLG_BAND_24G_MSK); /* Check if we are switching association toggle */ - if ((priv->staging_rxon.filter_flags & RXON_FILTER_ASSOC_MSK) != - (priv->active_rxon.filter_flags & RXON_FILTER_ASSOC_MSK)) - return 1; + CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, + active->filter_flags & RXON_FILTER_ASSOC_MSK); + +#undef CHK +#undef CHK_NEQ return 0; } EXPORT_SYMBOL(iwl_full_rxon_required); -u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv) +u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) { /* * Assign the lowest rate -- should really get this from * the beacon skb from mac80211. */ - if (priv->staging_rxon.flags & RXON_FLG_BAND_24G_MSK) + if (ctx->staging.flags & RXON_FLG_BAND_24G_MSK) return IWL_RATE_1M_PLCP; else return IWL_RATE_6M_PLCP; } EXPORT_SYMBOL(iwl_rate_get_lowest_plcp); -void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) +static void _iwl_set_rxon_ht(struct iwl_priv *priv, + struct iwl_ht_config *ht_conf, + struct iwl_rxon_context *ctx) { - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + struct iwl_rxon_cmd *rxon = &ctx->staging; - if (!ht_conf->is_ht) { + if (!ctx->ht.enabled) { rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | RXON_FLG_HT40_PROT_MSK | @@ -703,22 +734,22 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) return; } - /* FIXME: if the definition of ht_protection changed, the "translation" + /* FIXME: if the definition of ht.protection changed, the "translation" * will be needed for rxon->flags */ - rxon->flags |= cpu_to_le32(ht_conf->ht_protection << RXON_FLG_HT_OPERATING_MODE_POS); + rxon->flags |= cpu_to_le32(ctx->ht.protection << RXON_FLG_HT_OPERATING_MODE_POS); /* Set up channel bandwidth: * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ /* clear the HT channel mode before set the mode */ rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); - if (iwl_is_ht40_tx_allowed(priv, NULL)) { + if (iwl_is_ht40_tx_allowed(priv, ctx, NULL)) { /* pure ht40 */ - if (ht_conf->ht_protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { + if (ctx->ht.protection == IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; /* Note: control channel is opposite of extension channel */ - switch (ht_conf->extension_chan_offset) { + switch (ctx->ht.extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; break; @@ -728,7 +759,7 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) } } else { /* Note: control channel is opposite of extension channel */ - switch (ht_conf->extension_chan_offset) { + switch (ctx->ht.extension_chan_offset) { case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: rxon->flags &= ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; @@ -749,12 +780,20 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) } if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv); + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " "extension channel offset 0x%x\n", - le32_to_cpu(rxon->flags), ht_conf->ht_protection, - ht_conf->extension_chan_offset); + le32_to_cpu(rxon->flags), ctx->ht.protection, + ctx->ht.extension_chan_offset); +} + +void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) +{ + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) + _iwl_set_rxon_ht(priv, ht_conf, ctx); } EXPORT_SYMBOL(iwl_set_rxon_ht); @@ -775,6 +814,14 @@ EXPORT_SYMBOL(iwl_set_rxon_ht); */ static int iwl_get_active_rx_chain_count(struct iwl_priv *priv) { + if (priv->cfg->advanced_bt_coexist && (priv->bt_full_concurrent || + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { + /* + * only use chain 'A' in bt high traffic load or + * full concurrency mode + */ + return IWL_NUM_RX_CHAINS_SINGLE; + } /* # of Rx chains to use when expecting MIMO. */ if (is_single_rx_stream(priv)) return IWL_NUM_RX_CHAINS_SINGLE; @@ -819,7 +866,7 @@ static u8 iwl_count_chain_bitmap(u32 chain_bitmap) * Selects how many and which Rx receivers/antennas/chains to use. * This should not be used for scan command ... it puts data in wrong place. */ -void iwl_set_rxon_chain(struct iwl_priv *priv) +void iwl_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { bool is_single = is_single_rx_stream(priv); bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); @@ -831,11 +878,20 @@ void iwl_set_rxon_chain(struct iwl_priv *priv) * Before first association, we assume all antennas are connected. * Just after first association, iwl_chain_noise_calibration() * checks which antennas actually *are* connected. */ - if (priv->chain_noise_data.active_chains) + if (priv->chain_noise_data.active_chains) active_chains = priv->chain_noise_data.active_chains; else active_chains = priv->hw_params.valid_rx_ant; + if (priv->cfg->advanced_bt_coexist && (priv->bt_full_concurrent || + priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { + /* + * only use chain 'A' in bt high traffic load or + * full concurrency mode + */ + active_chains = first_antenna(active_chains); + } + rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; /* How many receivers should we use? */ @@ -856,15 +912,15 @@ void iwl_set_rxon_chain(struct iwl_priv *priv) rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; - priv->staging_rxon.rx_chain = cpu_to_le16(rx_chain); + ctx->staging.rx_chain = cpu_to_le16(rx_chain); if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) - priv->staging_rxon.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; + ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; else - priv->staging_rxon.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; + ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", - priv->staging_rxon.rx_chain, + ctx->staging.rx_chain, active_rx_cnt, idle_rx_cnt); WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || @@ -872,39 +928,41 @@ void iwl_set_rxon_chain(struct iwl_priv *priv) } EXPORT_SYMBOL(iwl_set_rxon_chain); -/* Return valid channel */ +/* Return valid, unused, channel for a passive scan to reset the RF */ u8 iwl_get_single_channel_number(struct iwl_priv *priv, - enum ieee80211_band band) + enum ieee80211_band band) { const struct iwl_channel_info *ch_info; int i; u8 channel = 0; + u8 min, max; + struct iwl_rxon_context *ctx; - /* only scan single channel, good enough to reset the RF */ - /* pick the first valid not in-use channel */ if (band == IEEE80211_BAND_5GHZ) { - for (i = 14; i < priv->channel_count; i++) { - if (priv->channel_info[i].channel != - le16_to_cpu(priv->staging_rxon.channel)) { - channel = priv->channel_info[i].channel; - ch_info = iwl_get_channel_info(priv, - band, channel); - if (is_channel_valid(ch_info)) - break; - } - } + min = 14; + max = priv->channel_count; } else { - for (i = 0; i < 14; i++) { - if (priv->channel_info[i].channel != - le16_to_cpu(priv->staging_rxon.channel)) { - channel = - priv->channel_info[i].channel; - ch_info = iwl_get_channel_info(priv, - band, channel); - if (is_channel_valid(ch_info)) - break; - } + min = 0; + max = 14; + } + + for (i = min; i < max; i++) { + bool busy = false; + + for_each_context(priv, ctx) { + busy = priv->channel_info[i].channel == + le16_to_cpu(ctx->staging.channel); + if (busy) + break; } + + if (busy) + continue; + + channel = priv->channel_info[i].channel; + ch_info = iwl_get_channel_info(priv, band, channel); + if (is_channel_valid(ch_info)) + break; } return channel; @@ -915,25 +973,24 @@ EXPORT_SYMBOL(iwl_get_single_channel_number); * iwl_set_rxon_channel - Set the band and channel values in staging RXON * @ch: requested channel as a pointer to struct ieee80211_channel - * In addition to setting the staging RXON, priv->band is also set. - * * NOTE: Does not commit to the hardware; it sets appropriate bit fields * in the staging RXON flag structure based on the ch->band */ -int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch) +int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx) { enum ieee80211_band band = ch->band; u16 channel = ch->hw_value; - if ((le16_to_cpu(priv->staging_rxon.channel) == channel) && + if ((le16_to_cpu(ctx->staging.channel) == channel) && (priv->band == band)) return 0; - priv->staging_rxon.channel = cpu_to_le16(channel); + ctx->staging.channel = cpu_to_le16(channel); if (band == IEEE80211_BAND_5GHZ) - priv->staging_rxon.flags &= ~RXON_FLG_BAND_24G_MSK; + ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; else - priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK; + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; priv->band = band; @@ -944,24 +1001,25 @@ int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch) EXPORT_SYMBOL(iwl_set_rxon_channel); void iwl_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, enum ieee80211_band band, struct ieee80211_vif *vif) { if (band == IEEE80211_BAND_5GHZ) { - priv->staging_rxon.flags &= + ctx->staging.flags &= ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_CCK_MSK); - priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; } else { /* Copied from iwl_post_associate() */ if (vif && vif->bss_conf.use_short_slot) - priv->staging_rxon.flags |= RXON_FLG_SHORT_SLOT_MSK; + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; else - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; - priv->staging_rxon.flags |= RXON_FLG_BAND_24G_MSK; - priv->staging_rxon.flags |= RXON_FLG_AUTO_DETECT_MSK; - priv->staging_rxon.flags &= ~RXON_FLG_CCK_MSK; + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; + ctx->staging.flags &= ~RXON_FLG_CCK_MSK; } } EXPORT_SYMBOL(iwl_set_flags_for_band); @@ -970,35 +1028,34 @@ EXPORT_SYMBOL(iwl_set_flags_for_band); * initialize rxon structure with default values from eeprom */ void iwl_connection_init_rx_config(struct iwl_priv *priv, - struct ieee80211_vif *vif) + struct iwl_rxon_context *ctx) { const struct iwl_channel_info *ch_info; - enum nl80211_iftype type = NL80211_IFTYPE_STATION; - if (vif) - type = vif->type; + memset(&ctx->staging, 0, sizeof(ctx->staging)); - memset(&priv->staging_rxon, 0, sizeof(priv->staging_rxon)); - - switch (type) { + if (!ctx->vif) { + ctx->staging.dev_type = ctx->unused_devtype; + } else switch (ctx->vif->type) { case NL80211_IFTYPE_AP: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_AP; + ctx->staging.dev_type = ctx->ap_devtype; break; case NL80211_IFTYPE_STATION: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_ESS; - priv->staging_rxon.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; + ctx->staging.dev_type = ctx->station_devtype; + ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; break; case NL80211_IFTYPE_ADHOC: - priv->staging_rxon.dev_type = RXON_DEV_TYPE_IBSS; - priv->staging_rxon.flags = RXON_FLG_SHORT_PREAMBLE_MSK; - priv->staging_rxon.filter_flags = RXON_FILTER_BCON_AWARE_MSK | + ctx->staging.dev_type = ctx->ibss_devtype; + ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | RXON_FILTER_ACCEPT_GRP_MSK; break; default: - IWL_ERR(priv, "Unsupported interface type %d\n", type); + IWL_ERR(priv, "Unsupported interface type %d\n", + ctx->vif->type); break; } @@ -1006,37 +1063,36 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, /* TODO: Figure out when short_preamble would be set and cache from * that */ if (!hw_to_local(priv->hw)->short_preamble) - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; else - priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; #endif ch_info = iwl_get_channel_info(priv, priv->band, - le16_to_cpu(priv->active_rxon.channel)); + le16_to_cpu(ctx->active.channel)); if (!ch_info) ch_info = &priv->channel_info[0]; - priv->staging_rxon.channel = cpu_to_le16(ch_info->channel); + ctx->staging.channel = cpu_to_le16(ch_info->channel); priv->band = ch_info->band; - iwl_set_flags_for_band(priv, priv->band, vif); + iwl_set_flags_for_band(priv, ctx, priv->band, ctx->vif); - priv->staging_rxon.ofdm_basic_rates = + ctx->staging.ofdm_basic_rates = (IWL_OFDM_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; - priv->staging_rxon.cck_basic_rates = + ctx->staging.cck_basic_rates = (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; /* clear both MIX and PURE40 mode flag */ - priv->staging_rxon.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | + ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | RXON_FLG_CHANNEL_MODE_PURE_40); + if (ctx->vif) + memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); - if (vif) - memcpy(priv->staging_rxon.node_addr, vif->addr, ETH_ALEN); - - priv->staging_rxon.ofdm_ht_single_stream_basic_rates = 0xff; - priv->staging_rxon.ofdm_ht_dual_stream_basic_rates = 0xff; - priv->staging_rxon.ofdm_ht_triple_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; } EXPORT_SYMBOL(iwl_connection_init_rx_config); @@ -1044,6 +1100,7 @@ void iwl_set_rate(struct iwl_priv *priv) { const struct ieee80211_supported_band *hw = NULL; struct ieee80211_rate *rate; + struct iwl_rxon_context *ctx; int i; hw = iwl_get_hw_mode(priv, priv->band); @@ -1062,21 +1119,29 @@ void iwl_set_rate(struct iwl_priv *priv) IWL_DEBUG_RATE(priv, "Set active_rate = %0x\n", priv->active_rate); - priv->staging_rxon.cck_basic_rates = - (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; + for_each_context(priv, ctx) { + ctx->staging.cck_basic_rates = + (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; - priv->staging_rxon.ofdm_basic_rates = - (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; + ctx->staging.ofdm_basic_rates = + (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; + } } EXPORT_SYMBOL(iwl_set_rate); void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) { + /* + * MULTI-FIXME + * See iwl_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; if (priv->switch_rxon.switch_in_progress) { - ieee80211_chswitch_done(priv->vif, is_success); + ieee80211_chswitch_done(ctx->vif, is_success); mutex_lock(&priv->mutex); priv->switch_rxon.switch_in_progress = false; mutex_unlock(&priv->mutex); @@ -1087,14 +1152,19 @@ EXPORT_SYMBOL(iwl_chswitch_done); void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_rxon_cmd *rxon = (void *)&priv->active_rxon; struct iwl_csa_notification *csa = &(pkt->u.csa_notif); + /* + * MULTI-FIXME + * See iwl_mac_channel_switch. + */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_rxon_cmd *rxon = (void *)&ctx->active; if (priv->switch_rxon.switch_in_progress) { if (!le32_to_cpu(csa->status) && (csa->channel == priv->switch_rxon.channel)) { rxon->channel = csa->channel; - priv->staging_rxon.channel = csa->channel; + ctx->staging.channel = csa->channel; IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", le16_to_cpu(csa->channel)); iwl_chswitch_done(priv, true); @@ -1108,9 +1178,10 @@ void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) EXPORT_SYMBOL(iwl_rx_csa); #ifdef CONFIG_IWLWIFI_DEBUG -void iwl_print_rx_config_cmd(struct iwl_priv *priv) +void iwl_print_rx_config_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) { - struct iwl_rxon_cmd *rxon = &priv->staging_rxon; + struct iwl_rxon_cmd *rxon = &ctx->staging; IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); @@ -1150,7 +1221,8 @@ void iwl_irq_handle_error(struct iwl_priv *priv) priv->cfg->ops->lib->dump_nic_event_log(priv, false, NULL, false); #ifdef CONFIG_IWLWIFI_DEBUG if (iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) - iwl_print_rx_config_cmd(priv); + iwl_print_rx_config_cmd(priv, + &priv->contexts[IWL_RXON_CTX_BSS]); #endif wake_up_interruptible(&priv->wait_command_queue); @@ -1518,6 +1590,7 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, 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; @@ -1537,13 +1610,21 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, spin_lock_irqsave(&priv->lock, flags); - priv->qos_data.def_qos_parm.ac[q].cw_min = cpu_to_le16(params->cw_min); - priv->qos_data.def_qos_parm.ac[q].cw_max = cpu_to_le16(params->cw_max); - priv->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; - priv->qos_data.def_qos_parm.ac[q].edca_txop = - cpu_to_le16((params->txop * 32)); + /* + * 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)); - priv->qos_data.def_qos_parm.ac[q].reserved1 = 0; + ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; + } spin_unlock_irqrestore(&priv->lock, flags); @@ -1566,15 +1647,16 @@ static void iwl_ht_conf(struct iwl_priv *priv, struct iwl_ht_config *ht_conf = &priv->current_ht_config; struct ieee80211_sta *sta; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); IWL_DEBUG_MAC80211(priv, "enter:\n"); - if (!ht_conf->is_ht) + if (!ctx->ht.enabled) return; - ht_conf->ht_protection = + ctx->ht.protection = bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; - ht_conf->non_GF_STA_present = + ctx->ht.non_gf_sta_present = !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); ht_conf->single_chain_sufficient = false; @@ -1618,18 +1700,20 @@ static void iwl_ht_conf(struct iwl_priv *priv, IWL_DEBUG_MAC80211(priv, "leave\n"); } -static inline void iwl_set_no_assoc(struct iwl_priv *priv) +static inline void iwl_set_no_assoc(struct iwl_priv *priv, + struct ieee80211_vif *vif) { + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + iwl_led_disassociate(priv); /* * inform the ucode that there is no longer an * association and that no more packets should be * sent */ - priv->staging_rxon.filter_flags &= - ~RXON_FILTER_ASSOC_MSK; - priv->staging_rxon.assoc_id = 0; - iwlcore_commit_rxon(priv); + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + ctx->staging.assoc_id = 0; + iwlcore_commit_rxon(priv, ctx); } static int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) @@ -1640,6 +1724,14 @@ static int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) IWL_DEBUG_MAC80211(priv, "enter\n"); + lockdep_assert_held(&priv->mutex); + + if (!priv->beacon_ctx) { + IWL_ERR(priv, "update beacon but no beacon context!\n"); + dev_kfree_skb(skb); + return -EINVAL; + } + if (!iwl_is_ready_rf(priv)) { IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); return -EIO; @@ -1658,7 +1750,7 @@ static int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb) IWL_DEBUG_MAC80211(priv, "leave\n"); spin_unlock_irqrestore(&priv->lock, flags); - priv->cfg->ops->lib->post_associate(priv, priv->vif); + priv->cfg->ops->lib->post_associate(priv, priv->beacon_ctx->vif); return 0; } @@ -1669,6 +1761,7 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, u32 changes) { struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); int ret; IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes); @@ -1682,11 +1775,23 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - priv->qos_data.qos_active = bss_conf->qos; - iwl_update_qos(priv); + ctx->qos_data.qos_active = bss_conf->qos; + iwl_update_qos(priv, ctx); spin_unlock_irqrestore(&priv->lock, flags); } + if (changes & BSS_CHANGED_BEACON_ENABLED) { + /* + * the add_interface code must make sure we only ever + * have a single interface that could be beaconing at + * any time. + */ + if (vif->bss_conf.enable_beacon) + priv->beacon_ctx = ctx; + else + priv->beacon_ctx = NULL; + } + if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_AP) { dev_kfree_skb(priv->ibss_beacon); priv->ibss_beacon = ieee80211_beacon_get(hw, vif); @@ -1713,13 +1818,13 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, /* mac80211 only sets assoc when in STATION mode */ if (vif->type == NL80211_IFTYPE_ADHOC || bss_conf->assoc) { - memcpy(priv->staging_rxon.bssid_addr, + memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); /* currently needed in a few places */ memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); } else { - priv->staging_rxon.filter_flags &= + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; } @@ -1742,21 +1847,21 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n", bss_conf->use_short_preamble); if (bss_conf->use_short_preamble) - priv->staging_rxon.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; else - priv->staging_rxon.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; } if (changes & BSS_CHANGED_ERP_CTS_PROT) { IWL_DEBUG_MAC80211(priv, "ERP_CTS %d\n", bss_conf->use_cts_prot); if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) - priv->staging_rxon.flags |= RXON_FLG_TGG_PROTECT_MSK; + ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; else - priv->staging_rxon.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; if (bss_conf->use_cts_prot) - priv->staging_rxon.flags |= RXON_FLG_SELF_CTS_EN; + ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; else - priv->staging_rxon.flags &= ~RXON_FLG_SELF_CTS_EN; + ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; } if (changes & BSS_CHANGED_BASIC_RATES) { @@ -1766,12 +1871,12 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, * like this here: * if (A-band) - priv->staging_rxon.ofdm_basic_rates = + ctx->staging.ofdm_basic_rates = bss_conf->basic_rates; else - priv->staging_rxon.ofdm_basic_rates = + ctx->staging.ofdm_basic_rates = bss_conf->basic_rates >> 4; - priv->staging_rxon.cck_basic_rates = + ctx->staging.cck_basic_rates = bss_conf->basic_rates & 0xF; */ } @@ -1780,7 +1885,7 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, iwl_ht_conf(priv, vif); if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv); + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); } if (changes & BSS_CHANGED_ASSOC) { @@ -1793,29 +1898,29 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, if (!iwl_is_rfkill(priv)) priv->cfg->ops->lib->post_associate(priv, vif); } else - iwl_set_no_assoc(priv); + iwl_set_no_assoc(priv, vif); } - if (changes && iwl_is_associated(priv) && bss_conf->aid) { + if (changes && iwl_is_associated_ctx(ctx) && bss_conf->aid) { IWL_DEBUG_MAC80211(priv, "Changes (%#x) while associated\n", changes); - ret = iwl_send_rxon_assoc(priv); + ret = iwl_send_rxon_assoc(priv, ctx); if (!ret) { /* Sync active_rxon with latest change. */ - memcpy((void *)&priv->active_rxon, - &priv->staging_rxon, + memcpy((void *)&ctx->active, + &ctx->staging, sizeof(struct iwl_rxon_cmd)); } } if (changes & BSS_CHANGED_BEACON_ENABLED) { if (vif->bss_conf.enable_beacon) { - memcpy(priv->staging_rxon.bssid_addr, + memcpy(ctx->staging.bssid_addr, bss_conf->bssid, ETH_ALEN); memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); iwlcore_config_ap(priv, vif); } else - iwl_set_no_assoc(priv); + iwl_set_no_assoc(priv, vif); } if (changes & BSS_CHANGED_IBSS) { @@ -1827,6 +1932,12 @@ void iwl_bss_info_changed(struct ieee80211_hw *hw, bss_conf->bssid); } + if (changes & BSS_CHANGED_IDLE && + priv->cfg->ops->hcmd->set_pan_params) { + if (priv->cfg->ops->hcmd->set_pan_params(priv)) + IWL_ERR(priv, "failed to update PAN params\n"); + } + mutex_unlock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "leave\n"); @@ -1835,17 +1946,21 @@ EXPORT_SYMBOL(iwl_bss_info_changed); static int iwl_set_mode(struct iwl_priv *priv, struct ieee80211_vif *vif) { - iwl_connection_init_rx_config(priv, vif); + struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); + + iwl_connection_init_rx_config(priv, ctx); if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv); + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); - return iwlcore_commit_rxon(priv); + return iwlcore_commit_rxon(priv, ctx); } int iwl_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 = 0; IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", @@ -1858,23 +1973,60 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) goto out; } - if (priv->vif) { - IWL_DEBUG_MAC80211(priv, "leave - vif != NULL\n"); + 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(vif->type))) + continue; + + /* have maybe usable context w/o interface */ + ctx = tmp; + break; + } + + if (!ctx) { err = -EOPNOTSUPP; goto out; } - priv->vif = vif; + vif_priv->ctx = ctx; + ctx->vif = vif; + /* + * 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; err = iwl_set_mode(priv, vif); if (err) goto out_err; + if (priv->cfg->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; + } + goto out; out_err: - priv->vif = NULL; + ctx->vif = NULL; priv->iw_mode = NL80211_IFTYPE_STATION; out: mutex_unlock(&priv->mutex); @@ -1888,26 +2040,36 @@ void iwl_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); bool scan_completed = false; IWL_DEBUG_MAC80211(priv, "enter\n"); mutex_lock(&priv->mutex); - if (iwl_is_ready_rf(priv)) { - iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv); - } - if (priv->vif == vif) { - priv->vif = NULL; - if (priv->scan_vif == vif) { - scan_completed = true; - priv->scan_vif = NULL; - priv->scan_request = NULL; - } - memset(priv->bssid, 0, ETH_ALEN); + WARN_ON(ctx->vif != vif); + ctx->vif = NULL; + + iwl_scan_cancel_timeout(priv, 100); + iwl_set_mode(priv, vif); + + if (priv->scan_vif == vif) { + scan_completed = true; + priv->scan_vif = NULL; + priv->scan_request = NULL; } + + /* + * 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->notif_bt_traffic_load; + + memset(priv->bssid, 0, ETH_ALEN); mutex_unlock(&priv->mutex); if (scan_completed) @@ -1928,6 +2090,7 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) struct ieee80211_conf *conf = &hw->conf; struct ieee80211_channel *channel = conf->channel; struct iwl_ht_config *ht_conf = &priv->current_ht_config; + struct iwl_rxon_context *ctx; unsigned long flags = 0; int ret = 0; u16 ch; @@ -1957,7 +2120,8 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) * configured. */ if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv); + for_each_context(priv, ctx) + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); } /* during scanning mac80211 will delay channel setting until @@ -1977,39 +2141,49 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) spin_lock_irqsave(&priv->lock, flags); - /* Configure HT40 channels */ - ht_conf->is_ht = conf_is_ht(conf); - if (ht_conf->is_ht) { - if (conf_is_ht40_minus(conf)) { - ht_conf->extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ht_conf->is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - ht_conf->extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ht_conf->is_40mhz = true; - } else { - ht_conf->extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - ht_conf->is_40mhz = false; - } - } else - ht_conf->is_40mhz = false; - /* Default to no protection. Protection mode will later be set - * from BSS config in iwl_ht_conf */ - ht_conf->ht_protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + for_each_context(priv, ctx) { + /* 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(priv->staging_rxon.channel) != ch)) - priv->staging_rxon.flags = 0; + /* + * Default to no protection. Protection mode will + * later be set from BSS config in iwl_ht_conf + */ + ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + 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_rxon_channel(priv, channel); - iwl_set_rxon_ht(priv, ht_conf); + iwl_set_flags_for_band(priv, ctx, channel->band, + ctx->vif); + } - iwl_set_flags_for_band(priv, channel->band, priv->vif); spin_unlock_irqrestore(&priv->lock, flags); - if (priv->cfg->ops->lib->update_bcast_station) - ret = priv->cfg->ops->lib->update_bcast_station(priv); + if (priv->cfg->ops->lib->update_bcast_stations) + ret = priv->cfg->ops->lib->update_bcast_stations(priv); set_ch_out: /* The list of supported rates and rate mask can be different @@ -2040,12 +2214,13 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed) if (scan_active) goto out; - if (memcmp(&priv->active_rxon, - &priv->staging_rxon, sizeof(priv->staging_rxon))) - iwlcore_commit_rxon(priv); - else - IWL_DEBUG_INFO(priv, "Not re-sending same RXON configuration.\n"); - + for_each_context(priv, ctx) { + if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging))) + iwlcore_commit_rxon(priv, ctx); + else + IWL_DEBUG_INFO(priv, + "Not re-sending same RXON configuration.\n"); + } out: IWL_DEBUG_MAC80211(priv, "leave\n"); @@ -2058,6 +2233,8 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) { struct iwl_priv *priv = hw->priv; unsigned long flags; + /* IBSS can only be the IWL_RXON_CTX_BSS context */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; mutex_lock(&priv->mutex); IWL_DEBUG_MAC80211(priv, "enter\n"); @@ -2088,8 +2265,8 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw) * clear RXON_FILTER_ASSOC_MSK bit */ iwl_scan_cancel_timeout(priv, 100); - priv->staging_rxon.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv); + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwlcore_commit_rxon(priv, ctx); iwl_set_rate(priv); @@ -2498,7 +2675,7 @@ static void iwl_force_rf_reset(struct iwl_priv *priv) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - if (!iwl_is_associated(priv)) { + if (!iwl_is_any_associated(priv)) { IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); return; } @@ -2624,10 +2801,14 @@ static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt) "queue %d, not read %d time\n", q->id, q->repeat_same_read_ptr); - mod_timer(&priv->monitor_recover, jiffies + - msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS)); + if (!priv->cfg->advanced_bt_coexist) { + mod_timer(&priv->monitor_recover, + jiffies + msecs_to_jiffies( + IWL_ONE_HUNDRED_MSECS)); + return 1; + } } - return 1; + return 0; } else { q->last_read_ptr = q->read_ptr; q->repeat_same_read_ptr = 0; @@ -2645,25 +2826,27 @@ void iwl_bg_monitor_recover(unsigned long data) return; /* monitor and check for stuck cmd queue */ - if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM)) + if (iwl_check_stuck_queue(priv, priv->cmd_queue)) return; /* monitor and check for other stuck queues */ - if (iwl_is_associated(priv)) { + if (iwl_is_any_associated(priv)) { for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { /* skip as we already checked the command queue */ - if (cnt == IWL_CMD_QUEUE_NUM) + if (cnt == priv->cmd_queue) continue; if (iwl_check_stuck_queue(priv, cnt)) return; } } - /* - * Reschedule the timer to occur in - * priv->cfg->monitor_recover_period - */ - mod_timer(&priv->monitor_recover, - jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period)); + if (priv->cfg->monitor_recover_period) { + /* + * Reschedule the timer to occur in + * priv->cfg->monitor_recover_period + */ + mod_timer(&priv->monitor_recover, jiffies + msecs_to_jiffies( + priv->cfg->monitor_recover_period)); + } } EXPORT_SYMBOL(iwl_bg_monitor_recover); |