162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/****************************************************************************** 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright(c) 2008 - 2014, 2022 Intel Corporation. All rights reserved. 562306a36Sopenharmony_ci *****************************************************************************/ 662306a36Sopenharmony_ci#include <linux/etherdevice.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/sched.h> 1062306a36Sopenharmony_ci#include <net/mac80211.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "iwl-io.h" 1362306a36Sopenharmony_ci#include "iwl-agn-hw.h" 1462306a36Sopenharmony_ci#include "iwl-trans.h" 1562306a36Sopenharmony_ci#include "iwl-modparams.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "dev.h" 1862306a36Sopenharmony_ci#include "agn.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ciint iwlagn_hw_valid_rtc_data_addr(u32 addr) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && 2362306a36Sopenharmony_ci (addr < IWLAGN_RTC_DATA_UPPER_BOUND); 2462306a36Sopenharmony_ci} 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciint iwlagn_send_tx_power(struct iwl_priv *priv) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci struct iwlagn_tx_power_dbm_cmd tx_power_cmd; 2962306a36Sopenharmony_ci u8 tx_ant_cfg_cmd; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), 3262306a36Sopenharmony_ci "TX Power requested while scanning!\n")) 3362306a36Sopenharmony_ci return -EAGAIN; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci /* half dBm need to multiply */ 3662306a36Sopenharmony_ci tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) { 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * For the newer devices which using enhanced/extend tx power 4162306a36Sopenharmony_ci * table in EEPROM, the format is in half dBm. driver need to 4262306a36Sopenharmony_ci * convert to dBm format before report to mac80211. 4362306a36Sopenharmony_ci * By doing so, there is a possibility of 1/2 dBm resolution 4462306a36Sopenharmony_ci * lost. driver will perform "round-up" operation before 4562306a36Sopenharmony_ci * reporting, but it will cause 1/2 dBm tx power over the 4662306a36Sopenharmony_ci * regulatory limit. Perform the checking here, if the 4762306a36Sopenharmony_ci * "tx_power_user_lmt" is higher than EEPROM value (in 4862306a36Sopenharmony_ci * half-dBm format), lower the tx power based on EEPROM 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci tx_power_cmd.global_lmt = 5162306a36Sopenharmony_ci priv->nvm_data->max_tx_pwr_half_dbm; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; 5462306a36Sopenharmony_ci tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) 5762306a36Sopenharmony_ci tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; 5862306a36Sopenharmony_ci else 5962306a36Sopenharmony_ci tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0, 6262306a36Sopenharmony_ci sizeof(tx_power_cmd), &tx_power_cmd); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_civoid iwlagn_temperature(struct iwl_priv *priv) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci lockdep_assert_held(&priv->statistics.lock); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* store temperature from correct statistics (in Celsius) */ 7062306a36Sopenharmony_ci priv->temperature = le32_to_cpu(priv->statistics.common.temperature); 7162306a36Sopenharmony_ci iwl_tt_handler(priv); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ciint iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int idx = 0; 7762306a36Sopenharmony_ci int band_offset = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* HT rate format: mac80211 wants an MCS number, which is just LSB */ 8062306a36Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 8162306a36Sopenharmony_ci idx = (rate_n_flags & 0xff); 8262306a36Sopenharmony_ci return idx; 8362306a36Sopenharmony_ci /* Legacy rate format, search for match in table */ 8462306a36Sopenharmony_ci } else { 8562306a36Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 8662306a36Sopenharmony_ci band_offset = IWL_FIRST_OFDM_RATE; 8762306a36Sopenharmony_ci for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) 8862306a36Sopenharmony_ci if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) 8962306a36Sopenharmony_ci return idx - band_offset; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci return -1; 9362306a36Sopenharmony_ci} 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ciint iwlagn_manage_ibss_station(struct iwl_priv *priv, 9662306a36Sopenharmony_ci struct ieee80211_vif *vif, bool add) 9762306a36Sopenharmony_ci{ 9862306a36Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (add) 10162306a36Sopenharmony_ci return iwlagn_add_bssid_station(priv, vif_priv->ctx, 10262306a36Sopenharmony_ci vif->bss_conf.bssid, 10362306a36Sopenharmony_ci &vif_priv->ibss_bssid_sta_id); 10462306a36Sopenharmony_ci return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, 10562306a36Sopenharmony_ci vif->bss_conf.bssid); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * pre-requirements: 11262306a36Sopenharmony_ci * 1. acquire mutex before calling 11362306a36Sopenharmony_ci * 2. make sure rf is on and not in exit state 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ciint iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = { 11862306a36Sopenharmony_ci .flush_control = cpu_to_le16(IWL_DROP_ALL), 11962306a36Sopenharmony_ci }; 12062306a36Sopenharmony_ci struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = { 12162306a36Sopenharmony_ci .flush_control = cpu_to_le16(IWL_DROP_ALL), 12262306a36Sopenharmony_ci }; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | 12562306a36Sopenharmony_ci IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) 12862306a36Sopenharmony_ci queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK | 12962306a36Sopenharmony_ci IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK | 13062306a36Sopenharmony_ci IWL_PAN_SCD_MGMT_MSK | 13162306a36Sopenharmony_ci IWL_PAN_SCD_MULTICAST_MSK; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (priv->nvm_data->sku_cap_11n_enable) 13462306a36Sopenharmony_ci queue_control |= IWL_AGG_TX_QUEUE_MSK; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (scd_q_msk) 13762306a36Sopenharmony_ci queue_control = scd_q_msk; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control); 14062306a36Sopenharmony_ci flush_cmd_v3.queue_control = cpu_to_le32(queue_control); 14162306a36Sopenharmony_ci flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (IWL_UCODE_API(priv->fw->ucode_ver) > 2) 14462306a36Sopenharmony_ci return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, 14562306a36Sopenharmony_ci sizeof(flush_cmd_v3), 14662306a36Sopenharmony_ci &flush_cmd_v3); 14762306a36Sopenharmony_ci return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, 14862306a36Sopenharmony_ci sizeof(flush_cmd_v2), &flush_cmd_v2); 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_civoid iwlagn_dev_txfifo_flush(struct iwl_priv *priv) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci mutex_lock(&priv->mutex); 15462306a36Sopenharmony_ci ieee80211_stop_queues(priv->hw); 15562306a36Sopenharmony_ci if (iwlagn_txfifo_flush(priv, 0)) { 15662306a36Sopenharmony_ci IWL_ERR(priv, "flush request fail\n"); 15762306a36Sopenharmony_ci goto done; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); 16062306a36Sopenharmony_ci iwl_trans_wait_tx_queues_empty(priv->trans, 0xffffffff); 16162306a36Sopenharmony_cidone: 16262306a36Sopenharmony_ci ieee80211_wake_queues(priv->hw); 16362306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* 16762306a36Sopenharmony_ci * BT coex 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci/* Notmal TDM */ 17062306a36Sopenharmony_cistatic const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { 17162306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 17262306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 17362306a36Sopenharmony_ci cpu_to_le32(0xaeaaaaaa), 17462306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 17562306a36Sopenharmony_ci cpu_to_le32(0xcc00ff28), 17662306a36Sopenharmony_ci cpu_to_le32(0x0000aaaa), 17762306a36Sopenharmony_ci cpu_to_le32(0xcc00aaaa), 17862306a36Sopenharmony_ci cpu_to_le32(0x0000aaaa), 17962306a36Sopenharmony_ci cpu_to_le32(0xc0004000), 18062306a36Sopenharmony_ci cpu_to_le32(0x00004000), 18162306a36Sopenharmony_ci cpu_to_le32(0xf0005000), 18262306a36Sopenharmony_ci cpu_to_le32(0xf0005000), 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* Full concurrency */ 18662306a36Sopenharmony_cistatic const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { 18762306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 18862306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 18962306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 19062306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 19162306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 19262306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 19362306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 19462306a36Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 19562306a36Sopenharmony_ci cpu_to_le32(0x00000000), 19662306a36Sopenharmony_ci cpu_to_le32(0x00000000), 19762306a36Sopenharmony_ci cpu_to_le32(0x00000000), 19862306a36Sopenharmony_ci cpu_to_le32(0x00000000), 19962306a36Sopenharmony_ci}; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_civoid iwlagn_send_advance_bt_config(struct iwl_priv *priv) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct iwl_basic_bt_cmd basic = { 20462306a36Sopenharmony_ci .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, 20562306a36Sopenharmony_ci .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, 20662306a36Sopenharmony_ci .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, 20762306a36Sopenharmony_ci .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, 20862306a36Sopenharmony_ci }; 20962306a36Sopenharmony_ci struct iwl_bt_cmd_v1 bt_cmd_v1; 21062306a36Sopenharmony_ci struct iwl_bt_cmd_v2 bt_cmd_v2; 21162306a36Sopenharmony_ci int ret; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != 21462306a36Sopenharmony_ci sizeof(basic.bt3_lookup_table)); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (priv->lib->bt_params) { 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * newer generation of devices (2000 series and newer) 21962306a36Sopenharmony_ci * use the version 2 of the bt command 22062306a36Sopenharmony_ci * we need to make sure sending the host command 22162306a36Sopenharmony_ci * with correct data structure to avoid uCode assert 22262306a36Sopenharmony_ci */ 22362306a36Sopenharmony_ci if (priv->lib->bt_params->bt_session_2) { 22462306a36Sopenharmony_ci bt_cmd_v2.prio_boost = cpu_to_le32( 22562306a36Sopenharmony_ci priv->lib->bt_params->bt_prio_boost); 22662306a36Sopenharmony_ci bt_cmd_v2.tx_prio_boost = 0; 22762306a36Sopenharmony_ci bt_cmd_v2.rx_prio_boost = 0; 22862306a36Sopenharmony_ci } else { 22962306a36Sopenharmony_ci /* older version only has 8 bits */ 23062306a36Sopenharmony_ci WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); 23162306a36Sopenharmony_ci bt_cmd_v1.prio_boost = 23262306a36Sopenharmony_ci priv->lib->bt_params->bt_prio_boost; 23362306a36Sopenharmony_ci bt_cmd_v1.tx_prio_boost = 0; 23462306a36Sopenharmony_ci bt_cmd_v1.rx_prio_boost = 0; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci } else { 23762306a36Sopenharmony_ci IWL_ERR(priv, "failed to construct BT Coex Config\n"); 23862306a36Sopenharmony_ci return; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* 24262306a36Sopenharmony_ci * Possible situations when BT needs to take over for receive, 24362306a36Sopenharmony_ci * at the same time where STA needs to response to AP's frame(s), 24462306a36Sopenharmony_ci * reduce the tx power of the required response frames, by that, 24562306a36Sopenharmony_ci * allow the concurrent BT receive & WiFi transmit 24662306a36Sopenharmony_ci * (BT - ANT A, WiFi -ANT B), without interference to one another 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * Reduced tx power apply to control frames only (ACK/Back/CTS) 24962306a36Sopenharmony_ci * when indicated by the BT config command 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci basic.kill_ack_mask = priv->kill_ack_mask; 25262306a36Sopenharmony_ci basic.kill_cts_mask = priv->kill_cts_mask; 25362306a36Sopenharmony_ci if (priv->reduced_txpower) 25462306a36Sopenharmony_ci basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; 25562306a36Sopenharmony_ci basic.valid = priv->bt_valid; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Configure BT coex mode to "no coexistence" when the 25962306a36Sopenharmony_ci * user disabled BT coexistence, we have no interface 26062306a36Sopenharmony_ci * (might be in monitor mode), or the interface is in 26162306a36Sopenharmony_ci * IBSS mode (no proper uCode support for coex then). 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci if (!iwlwifi_mod_params.bt_coex_active || 26462306a36Sopenharmony_ci priv->iw_mode == NL80211_IFTYPE_ADHOC) { 26562306a36Sopenharmony_ci basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << 26862306a36Sopenharmony_ci IWLAGN_BT_FLAG_COEX_MODE_SHIFT; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (!priv->bt_enable_pspoll) 27162306a36Sopenharmony_ci basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; 27262306a36Sopenharmony_ci else 27362306a36Sopenharmony_ci basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (priv->bt_ch_announce) 27662306a36Sopenharmony_ci basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; 27762306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci priv->bt_enable_flag = basic.flags; 28062306a36Sopenharmony_ci if (priv->bt_full_concurrent) 28162306a36Sopenharmony_ci memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, 28262306a36Sopenharmony_ci sizeof(iwlagn_concurrent_lookup)); 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, 28562306a36Sopenharmony_ci sizeof(iwlagn_def_3w_lookup)); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", 28862306a36Sopenharmony_ci basic.flags ? "active" : "disabled", 28962306a36Sopenharmony_ci priv->bt_full_concurrent ? 29062306a36Sopenharmony_ci "full concurrency" : "3-wire"); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (priv->lib->bt_params->bt_session_2) { 29362306a36Sopenharmony_ci memcpy(&bt_cmd_v2.basic, &basic, 29462306a36Sopenharmony_ci sizeof(basic)); 29562306a36Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, 29662306a36Sopenharmony_ci 0, sizeof(bt_cmd_v2), &bt_cmd_v2); 29762306a36Sopenharmony_ci } else { 29862306a36Sopenharmony_ci memcpy(&bt_cmd_v1.basic, &basic, 29962306a36Sopenharmony_ci sizeof(basic)); 30062306a36Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, 30162306a36Sopenharmony_ci 0, sizeof(bt_cmd_v1), &bt_cmd_v1); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci if (ret) 30462306a36Sopenharmony_ci IWL_ERR(priv, "failed to send BT Coex Config\n"); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_civoid iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci struct iwl_rxon_context *ctx, *found_ctx = NULL; 31162306a36Sopenharmony_ci bool found_ap = false; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* Check whether AP or GO mode is active. */ 31662306a36Sopenharmony_ci if (rssi_ena) { 31762306a36Sopenharmony_ci for_each_context(priv, ctx) { 31862306a36Sopenharmony_ci if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && 31962306a36Sopenharmony_ci iwl_is_associated_ctx(ctx)) { 32062306a36Sopenharmony_ci found_ap = true; 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* 32762306a36Sopenharmony_ci * If disable was received or If GO/AP mode, disable RSSI 32862306a36Sopenharmony_ci * measurements. 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_ci if (!rssi_ena || found_ap) { 33162306a36Sopenharmony_ci if (priv->cur_rssi_ctx) { 33262306a36Sopenharmony_ci ctx = priv->cur_rssi_ctx; 33362306a36Sopenharmony_ci ieee80211_disable_rssi_reports(ctx->vif); 33462306a36Sopenharmony_ci priv->cur_rssi_ctx = NULL; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci return; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* 34062306a36Sopenharmony_ci * If rssi measurements need to be enabled, consider all cases now. 34162306a36Sopenharmony_ci * Figure out how many contexts are active. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci for_each_context(priv, ctx) { 34462306a36Sopenharmony_ci if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && 34562306a36Sopenharmony_ci iwl_is_associated_ctx(ctx)) { 34662306a36Sopenharmony_ci found_ctx = ctx; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * rssi monitor already enabled for the correct interface...nothing 35362306a36Sopenharmony_ci * to do. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci if (found_ctx == priv->cur_rssi_ctx) 35662306a36Sopenharmony_ci return; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* 35962306a36Sopenharmony_ci * Figure out if rssi monitor is currently enabled, and needs 36062306a36Sopenharmony_ci * to be changed. If rssi monitor is already enabled, disable 36162306a36Sopenharmony_ci * it first else just enable rssi measurements on the 36262306a36Sopenharmony_ci * interface found above. 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_ci if (priv->cur_rssi_ctx) { 36562306a36Sopenharmony_ci ctx = priv->cur_rssi_ctx; 36662306a36Sopenharmony_ci if (ctx->vif) 36762306a36Sopenharmony_ci ieee80211_disable_rssi_reports(ctx->vif); 36862306a36Sopenharmony_ci } 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci priv->cur_rssi_ctx = found_ctx; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (!found_ctx) 37362306a36Sopenharmony_ci return; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ieee80211_enable_rssi_reports(found_ctx->vif, 37662306a36Sopenharmony_ci IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, 37762306a36Sopenharmony_ci IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> 38362306a36Sopenharmony_ci BT_UART_MSG_FRAME3SCOESCO_POS; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic void iwlagn_bt_traffic_change_work(struct work_struct *work) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct iwl_priv *priv = 38962306a36Sopenharmony_ci container_of(work, struct iwl_priv, bt_traffic_change_work); 39062306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 39162306a36Sopenharmony_ci int smps_request = -1; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { 39462306a36Sopenharmony_ci /* bt coex disabled */ 39562306a36Sopenharmony_ci return; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * Note: bt_traffic_load can be overridden by scan complete and 40062306a36Sopenharmony_ci * coex profile notifications. Ignore that since only bad consequence 40162306a36Sopenharmony_ci * can be not matching debug print with actual state. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", 40462306a36Sopenharmony_ci priv->bt_traffic_load); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci switch (priv->bt_traffic_load) { 40762306a36Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_NONE: 40862306a36Sopenharmony_ci if (priv->bt_status) 40962306a36Sopenharmony_ci smps_request = IEEE80211_SMPS_DYNAMIC; 41062306a36Sopenharmony_ci else 41162306a36Sopenharmony_ci smps_request = IEEE80211_SMPS_AUTOMATIC; 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_LOW: 41462306a36Sopenharmony_ci smps_request = IEEE80211_SMPS_DYNAMIC; 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: 41762306a36Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: 41862306a36Sopenharmony_ci smps_request = IEEE80211_SMPS_STATIC; 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci default: 42162306a36Sopenharmony_ci IWL_ERR(priv, "Invalid BT traffic load: %d\n", 42262306a36Sopenharmony_ci priv->bt_traffic_load); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mutex_lock(&priv->mutex); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* 42962306a36Sopenharmony_ci * We can not send command to firmware while scanning. When the scan 43062306a36Sopenharmony_ci * complete we will schedule this work again. We do check with mutex 43162306a36Sopenharmony_ci * locked to prevent new scan request to arrive. We do not check 43262306a36Sopenharmony_ci * STATUS_SCANNING to avoid race when queue_work two times from 43362306a36Sopenharmony_ci * different notifications, but quit and not perform any work at all. 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci if (test_bit(STATUS_SCAN_HW, &priv->status)) 43662306a36Sopenharmony_ci goto out; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci iwl_update_chain_flags(priv); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (smps_request != -1) { 44162306a36Sopenharmony_ci priv->current_ht_config.smps = smps_request; 44262306a36Sopenharmony_ci for_each_context(priv, ctx) { 44362306a36Sopenharmony_ci if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) 44462306a36Sopenharmony_ci ieee80211_request_smps(ctx->vif, 0, smps_request); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* 44962306a36Sopenharmony_ci * Dynamic PS poll related functionality. Adjust RSSI measurements if 45062306a36Sopenharmony_ci * necessary. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci iwlagn_bt_coex_rssi_monitor(priv); 45362306a36Sopenharmony_ciout: 45462306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci/* 45862306a36Sopenharmony_ci * If BT sco traffic, and RSSI monitor is enabled, move measurements to the 45962306a36Sopenharmony_ci * correct interface or disable it if this is the last interface to be 46062306a36Sopenharmony_ci * removed. 46162306a36Sopenharmony_ci */ 46262306a36Sopenharmony_civoid iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci if (priv->bt_is_sco && 46562306a36Sopenharmony_ci priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) 46662306a36Sopenharmony_ci iwlagn_bt_adjust_rssi_monitor(priv, true); 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci iwlagn_bt_adjust_rssi_monitor(priv, false); 46962306a36Sopenharmony_ci} 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic void iwlagn_print_uartmsg(struct iwl_priv *priv, 47262306a36Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " 47562306a36Sopenharmony_ci "Update Req = 0x%X\n", 47662306a36Sopenharmony_ci (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> 47762306a36Sopenharmony_ci BT_UART_MSG_FRAME1MSGTYPE_POS, 47862306a36Sopenharmony_ci (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> 47962306a36Sopenharmony_ci BT_UART_MSG_FRAME1SSN_POS, 48062306a36Sopenharmony_ci (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> 48162306a36Sopenharmony_ci BT_UART_MSG_FRAME1UPDATEREQ_POS); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " 48462306a36Sopenharmony_ci "Chl_SeqN = 0x%X, In band = 0x%X\n", 48562306a36Sopenharmony_ci (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> 48662306a36Sopenharmony_ci BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, 48762306a36Sopenharmony_ci (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> 48862306a36Sopenharmony_ci BT_UART_MSG_FRAME2TRAFFICLOAD_POS, 48962306a36Sopenharmony_ci (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> 49062306a36Sopenharmony_ci BT_UART_MSG_FRAME2CHLSEQN_POS, 49162306a36Sopenharmony_ci (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> 49262306a36Sopenharmony_ci BT_UART_MSG_FRAME2INBAND_POS); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " 49562306a36Sopenharmony_ci "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", 49662306a36Sopenharmony_ci (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> 49762306a36Sopenharmony_ci BT_UART_MSG_FRAME3SCOESCO_POS, 49862306a36Sopenharmony_ci (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> 49962306a36Sopenharmony_ci BT_UART_MSG_FRAME3SNIFF_POS, 50062306a36Sopenharmony_ci (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> 50162306a36Sopenharmony_ci BT_UART_MSG_FRAME3A2DP_POS, 50262306a36Sopenharmony_ci (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> 50362306a36Sopenharmony_ci BT_UART_MSG_FRAME3ACL_POS, 50462306a36Sopenharmony_ci (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> 50562306a36Sopenharmony_ci BT_UART_MSG_FRAME3MASTER_POS, 50662306a36Sopenharmony_ci (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> 50762306a36Sopenharmony_ci BT_UART_MSG_FRAME3OBEX_POS); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", 51062306a36Sopenharmony_ci (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> 51162306a36Sopenharmony_ci BT_UART_MSG_FRAME4IDLEDURATION_POS); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " 51462306a36Sopenharmony_ci "eSCO Retransmissions = 0x%X\n", 51562306a36Sopenharmony_ci (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> 51662306a36Sopenharmony_ci BT_UART_MSG_FRAME5TXACTIVITY_POS, 51762306a36Sopenharmony_ci (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> 51862306a36Sopenharmony_ci BT_UART_MSG_FRAME5RXACTIVITY_POS, 51962306a36Sopenharmony_ci (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> 52062306a36Sopenharmony_ci BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", 52362306a36Sopenharmony_ci (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> 52462306a36Sopenharmony_ci BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, 52562306a36Sopenharmony_ci (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> 52662306a36Sopenharmony_ci BT_UART_MSG_FRAME6DISCOVERABLE_POS); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " 52962306a36Sopenharmony_ci "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", 53062306a36Sopenharmony_ci (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> 53162306a36Sopenharmony_ci BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, 53262306a36Sopenharmony_ci (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> 53362306a36Sopenharmony_ci BT_UART_MSG_FRAME7PAGE_POS, 53462306a36Sopenharmony_ci (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> 53562306a36Sopenharmony_ci BT_UART_MSG_FRAME7INQUIRY_POS, 53662306a36Sopenharmony_ci (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> 53762306a36Sopenharmony_ci BT_UART_MSG_FRAME7CONNECTABLE_POS); 53862306a36Sopenharmony_ci} 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic bool iwlagn_set_kill_msk(struct iwl_priv *priv, 54162306a36Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg) 54262306a36Sopenharmony_ci{ 54362306a36Sopenharmony_ci bool need_update = false; 54462306a36Sopenharmony_ci u8 kill_msk = IWL_BT_KILL_REDUCE; 54562306a36Sopenharmony_ci static const __le32 bt_kill_ack_msg[3] = { 54662306a36Sopenharmony_ci IWLAGN_BT_KILL_ACK_MASK_DEFAULT, 54762306a36Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, 54862306a36Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; 54962306a36Sopenharmony_ci static const __le32 bt_kill_cts_msg[3] = { 55062306a36Sopenharmony_ci IWLAGN_BT_KILL_CTS_MASK_DEFAULT, 55162306a36Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, 55262306a36Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci if (!priv->reduced_txpower) 55562306a36Sopenharmony_ci kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) 55662306a36Sopenharmony_ci ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; 55762306a36Sopenharmony_ci if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || 55862306a36Sopenharmony_ci priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { 55962306a36Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; 56062306a36Sopenharmony_ci priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; 56162306a36Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; 56262306a36Sopenharmony_ci priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; 56362306a36Sopenharmony_ci need_update = true; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci return need_update; 56662306a36Sopenharmony_ci} 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci/* 56962306a36Sopenharmony_ci * Upon RSSI changes, sends a bt config command with following changes 57062306a36Sopenharmony_ci * 1. enable/disable "reduced control frames tx power 57162306a36Sopenharmony_ci * 2. update the "kill)ack_mask" and "kill_cts_mask" 57262306a36Sopenharmony_ci * 57362306a36Sopenharmony_ci * If "reduced tx power" is enabled, uCode shall 57462306a36Sopenharmony_ci * 1. ACK/Back/CTS rate shall reduced to 6Mbps 57562306a36Sopenharmony_ci * 2. not use duplciate 20/40MHz mode 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_cistatic bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, 57862306a36Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci bool need_update = false; 58162306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 58262306a36Sopenharmony_ci int ave_rssi; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { 58562306a36Sopenharmony_ci IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); 58662306a36Sopenharmony_ci return false; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ave_rssi = ieee80211_ave_rssi(ctx->vif); 59062306a36Sopenharmony_ci if (!ave_rssi) { 59162306a36Sopenharmony_ci /* no rssi data, no changes to reduce tx power */ 59262306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "no rssi data available\n"); 59362306a36Sopenharmony_ci return need_update; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci if (!priv->reduced_txpower && 59662306a36Sopenharmony_ci !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && 59762306a36Sopenharmony_ci (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && 59862306a36Sopenharmony_ci (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | 59962306a36Sopenharmony_ci BT_UART_MSG_FRAME3OBEX_MSK)) && 60062306a36Sopenharmony_ci !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | 60162306a36Sopenharmony_ci BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { 60262306a36Sopenharmony_ci /* enabling reduced tx power */ 60362306a36Sopenharmony_ci priv->reduced_txpower = true; 60462306a36Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; 60562306a36Sopenharmony_ci need_update = true; 60662306a36Sopenharmony_ci } else if (priv->reduced_txpower && 60762306a36Sopenharmony_ci (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || 60862306a36Sopenharmony_ci (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || 60962306a36Sopenharmony_ci (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | 61062306a36Sopenharmony_ci BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || 61162306a36Sopenharmony_ci !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | 61262306a36Sopenharmony_ci BT_UART_MSG_FRAME3OBEX_MSK)))) { 61362306a36Sopenharmony_ci /* disable reduced tx power */ 61462306a36Sopenharmony_ci priv->reduced_txpower = false; 61562306a36Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; 61662306a36Sopenharmony_ci need_update = true; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return need_update; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, 62362306a36Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 62662306a36Sopenharmony_ci struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; 62762306a36Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { 63062306a36Sopenharmony_ci /* bt coex disabled */ 63162306a36Sopenharmony_ci return; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); 63562306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); 63662306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); 63762306a36Sopenharmony_ci IWL_DEBUG_COEX(priv, " CI compliance: %d\n", 63862306a36Sopenharmony_ci coex->bt_ci_compliance); 63962306a36Sopenharmony_ci iwlagn_print_uartmsg(priv, uart_msg); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci priv->last_bt_traffic_load = priv->bt_traffic_load; 64262306a36Sopenharmony_ci priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { 64562306a36Sopenharmony_ci if (priv->bt_status != coex->bt_status || 64662306a36Sopenharmony_ci priv->last_bt_traffic_load != coex->bt_traffic_load) { 64762306a36Sopenharmony_ci if (coex->bt_status) { 64862306a36Sopenharmony_ci /* BT on */ 64962306a36Sopenharmony_ci if (!priv->bt_ch_announce) 65062306a36Sopenharmony_ci priv->bt_traffic_load = 65162306a36Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_HIGH; 65262306a36Sopenharmony_ci else 65362306a36Sopenharmony_ci priv->bt_traffic_load = 65462306a36Sopenharmony_ci coex->bt_traffic_load; 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci /* BT off */ 65762306a36Sopenharmony_ci priv->bt_traffic_load = 65862306a36Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_NONE; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci priv->bt_status = coex->bt_status; 66162306a36Sopenharmony_ci queue_work(priv->workqueue, 66262306a36Sopenharmony_ci &priv->bt_traffic_change_work); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* schedule to send runtime bt_config */ 66762306a36Sopenharmony_ci /* check reduce power before change ack/cts kill mask */ 66862306a36Sopenharmony_ci if (iwlagn_fill_txpower_mode(priv, uart_msg) || 66962306a36Sopenharmony_ci iwlagn_set_kill_msk(priv, uart_msg)) 67062306a36Sopenharmony_ci queue_work(priv->workqueue, &priv->bt_runtime_config); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* FIXME: based on notification, adjust the prio_boost */ 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci priv->bt_ci_compliance = coex->bt_ci_compliance; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_civoid iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) 67962306a36Sopenharmony_ci{ 68062306a36Sopenharmony_ci priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = 68162306a36Sopenharmony_ci iwlagn_bt_coex_profile_notif; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_civoid iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci INIT_WORK(&priv->bt_traffic_change_work, 68762306a36Sopenharmony_ci iwlagn_bt_traffic_change_work); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_civoid iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci cancel_work_sync(&priv->bt_traffic_change_work); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic bool is_single_rx_stream(struct iwl_priv *priv) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || 69862306a36Sopenharmony_ci priv->current_ht_config.single_chain_sufficient; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci#define IWL_NUM_RX_CHAINS_MULTIPLE 3 70262306a36Sopenharmony_ci#define IWL_NUM_RX_CHAINS_SINGLE 2 70362306a36Sopenharmony_ci#define IWL_NUM_IDLE_CHAINS_DUAL 2 70462306a36Sopenharmony_ci#define IWL_NUM_IDLE_CHAINS_SINGLE 1 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci/* 70762306a36Sopenharmony_ci * Determine how many receiver/antenna chains to use. 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * More provides better reception via diversity. Fewer saves power 71062306a36Sopenharmony_ci * at the expense of throughput, but only when not in powersave to 71162306a36Sopenharmony_ci * start with. 71262306a36Sopenharmony_ci * 71362306a36Sopenharmony_ci * MIMO (dual stream) requires at least 2, but works better with 3. 71462306a36Sopenharmony_ci * This does not determine *which* chains to use, just how many. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_cistatic int iwl_get_active_rx_chain_count(struct iwl_priv *priv) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci if (priv->lib->bt_params && 71962306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 72062306a36Sopenharmony_ci (priv->bt_full_concurrent || 72162306a36Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { 72262306a36Sopenharmony_ci /* 72362306a36Sopenharmony_ci * only use chain 'A' in bt high traffic load or 72462306a36Sopenharmony_ci * full concurrency mode 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci return IWL_NUM_RX_CHAINS_SINGLE; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci /* # of Rx chains to use when expecting MIMO. */ 72962306a36Sopenharmony_ci if (is_single_rx_stream(priv)) 73062306a36Sopenharmony_ci return IWL_NUM_RX_CHAINS_SINGLE; 73162306a36Sopenharmony_ci else 73262306a36Sopenharmony_ci return IWL_NUM_RX_CHAINS_MULTIPLE; 73362306a36Sopenharmony_ci} 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci/* 73662306a36Sopenharmony_ci * When we are in power saving mode, unless device support spatial 73762306a36Sopenharmony_ci * multiplexing power save, use the active count for rx chain count. 73862306a36Sopenharmony_ci */ 73962306a36Sopenharmony_cistatic int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci /* # Rx chains when idling, depending on SMPS mode */ 74262306a36Sopenharmony_ci switch (priv->current_ht_config.smps) { 74362306a36Sopenharmony_ci case IEEE80211_SMPS_STATIC: 74462306a36Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 74562306a36Sopenharmony_ci return IWL_NUM_IDLE_CHAINS_SINGLE; 74662306a36Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 74762306a36Sopenharmony_ci case IEEE80211_SMPS_OFF: 74862306a36Sopenharmony_ci return active_cnt; 74962306a36Sopenharmony_ci default: 75062306a36Sopenharmony_ci WARN(1, "invalid SMPS mode %d", 75162306a36Sopenharmony_ci priv->current_ht_config.smps); 75262306a36Sopenharmony_ci return active_cnt; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* up to 4 chains */ 75762306a36Sopenharmony_cistatic u8 iwl_count_chain_bitmap(u32 chain_bitmap) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci u8 res; 76062306a36Sopenharmony_ci res = (chain_bitmap & BIT(0)) >> 0; 76162306a36Sopenharmony_ci res += (chain_bitmap & BIT(1)) >> 1; 76262306a36Sopenharmony_ci res += (chain_bitmap & BIT(2)) >> 2; 76362306a36Sopenharmony_ci res += (chain_bitmap & BIT(3)) >> 3; 76462306a36Sopenharmony_ci return res; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci/* 76862306a36Sopenharmony_ci * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image 76962306a36Sopenharmony_ci * 77062306a36Sopenharmony_ci * Selects how many and which Rx receivers/antennas/chains to use. 77162306a36Sopenharmony_ci * This should not be used for scan command ... it puts data in wrong place. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_civoid iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci bool is_single = is_single_rx_stream(priv); 77662306a36Sopenharmony_ci bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); 77762306a36Sopenharmony_ci u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; 77862306a36Sopenharmony_ci u32 active_chains; 77962306a36Sopenharmony_ci u16 rx_chain; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* Tell uCode which antennas are actually connected. 78262306a36Sopenharmony_ci * Before first association, we assume all antennas are connected. 78362306a36Sopenharmony_ci * Just after first association, iwl_chain_noise_calibration() 78462306a36Sopenharmony_ci * checks which antennas actually *are* connected. */ 78562306a36Sopenharmony_ci if (priv->chain_noise_data.active_chains) 78662306a36Sopenharmony_ci active_chains = priv->chain_noise_data.active_chains; 78762306a36Sopenharmony_ci else 78862306a36Sopenharmony_ci active_chains = priv->nvm_data->valid_rx_ant; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (priv->lib->bt_params && 79162306a36Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 79262306a36Sopenharmony_ci (priv->bt_full_concurrent || 79362306a36Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { 79462306a36Sopenharmony_ci /* 79562306a36Sopenharmony_ci * only use chain 'A' in bt high traffic load or 79662306a36Sopenharmony_ci * full concurrency mode 79762306a36Sopenharmony_ci */ 79862306a36Sopenharmony_ci active_chains = first_antenna(active_chains); 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* How many receivers should we use? */ 80462306a36Sopenharmony_ci active_rx_cnt = iwl_get_active_rx_chain_count(priv); 80562306a36Sopenharmony_ci idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* correct rx chain count according hw settings 80962306a36Sopenharmony_ci * and chain noise calibration 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_ci valid_rx_cnt = iwl_count_chain_bitmap(active_chains); 81262306a36Sopenharmony_ci if (valid_rx_cnt < active_rx_cnt) 81362306a36Sopenharmony_ci active_rx_cnt = valid_rx_cnt; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (valid_rx_cnt < idle_rx_cnt) 81662306a36Sopenharmony_ci idle_rx_cnt = valid_rx_cnt; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; 81962306a36Sopenharmony_ci rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci ctx->staging.rx_chain = cpu_to_le16(rx_chain); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) 82462306a36Sopenharmony_ci ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", 82962306a36Sopenharmony_ci ctx->staging.rx_chain, 83062306a36Sopenharmony_ci active_rx_cnt, idle_rx_cnt); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || 83362306a36Sopenharmony_ci active_rx_cnt < idle_rx_cnt); 83462306a36Sopenharmony_ci} 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ciu8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci int i; 83962306a36Sopenharmony_ci u8 ind = ant; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (priv->band == NL80211_BAND_2GHZ && 84262306a36Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) 84362306a36Sopenharmony_ci return 0; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci for (i = 0; i < RATE_ANT_NUM - 1; i++) { 84662306a36Sopenharmony_ci ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; 84762306a36Sopenharmony_ci if (valid & BIT(ind)) 84862306a36Sopenharmony_ci return ind; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci return ant; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 85462306a36Sopenharmony_cistatic void iwlagn_convert_p1k(u16 *p1k, __le16 *out) 85562306a36Sopenharmony_ci{ 85662306a36Sopenharmony_ci int i; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci for (i = 0; i < IWLAGN_P1K_SIZE; i++) 85962306a36Sopenharmony_ci out[i] = cpu_to_le16(p1k[i]); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistruct wowlan_key_data { 86362306a36Sopenharmony_ci struct iwl_rxon_context *ctx; 86462306a36Sopenharmony_ci struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; 86562306a36Sopenharmony_ci struct iwlagn_wowlan_tkip_params_cmd *tkip; 86662306a36Sopenharmony_ci const u8 *bssid; 86762306a36Sopenharmony_ci bool error, use_rsc_tsc, use_tkip; 86862306a36Sopenharmony_ci}; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, 87262306a36Sopenharmony_ci struct ieee80211_vif *vif, 87362306a36Sopenharmony_ci struct ieee80211_sta *sta, 87462306a36Sopenharmony_ci struct ieee80211_key_conf *key, 87562306a36Sopenharmony_ci void *_data) 87662306a36Sopenharmony_ci{ 87762306a36Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 87862306a36Sopenharmony_ci struct wowlan_key_data *data = _data; 87962306a36Sopenharmony_ci struct iwl_rxon_context *ctx = data->ctx; 88062306a36Sopenharmony_ci struct aes_sc *aes_sc, *aes_tx_sc = NULL; 88162306a36Sopenharmony_ci struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; 88262306a36Sopenharmony_ci struct iwlagn_p1k_cache *rx_p1ks; 88362306a36Sopenharmony_ci u8 *rx_mic_key; 88462306a36Sopenharmony_ci struct ieee80211_key_seq seq; 88562306a36Sopenharmony_ci u32 cur_rx_iv32 = 0; 88662306a36Sopenharmony_ci u16 p1k[IWLAGN_P1K_SIZE]; 88762306a36Sopenharmony_ci int ret, i; 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci mutex_lock(&priv->mutex); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || 89262306a36Sopenharmony_ci key->cipher == WLAN_CIPHER_SUITE_WEP104) && 89362306a36Sopenharmony_ci !sta && !ctx->key_mapping_keys) 89462306a36Sopenharmony_ci ret = iwl_set_default_wep_key(priv, ctx, key); 89562306a36Sopenharmony_ci else 89662306a36Sopenharmony_ci ret = iwl_set_dynamic_key(priv, ctx, key, sta); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (ret) { 89962306a36Sopenharmony_ci IWL_ERR(priv, "Error setting key during suspend!\n"); 90062306a36Sopenharmony_ci data->error = true; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci switch (key->cipher) { 90462306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 90562306a36Sopenharmony_ci if (sta) { 90662306a36Sopenharmony_ci u64 pn64; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; 90962306a36Sopenharmony_ci tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci rx_p1ks = data->tkip->rx_uni; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci pn64 = atomic64_read(&key->tx_pn); 91462306a36Sopenharmony_ci tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64)); 91562306a36Sopenharmony_ci tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64)); 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); 91862306a36Sopenharmony_ci iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci memcpy(data->tkip->mic_keys.tx, 92162306a36Sopenharmony_ci &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], 92262306a36Sopenharmony_ci IWLAGN_MIC_KEY_SIZE); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci rx_mic_key = data->tkip->mic_keys.rx_unicast; 92562306a36Sopenharmony_ci } else { 92662306a36Sopenharmony_ci tkip_sc = 92762306a36Sopenharmony_ci data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; 92862306a36Sopenharmony_ci rx_p1ks = data->tkip->rx_multi; 92962306a36Sopenharmony_ci rx_mic_key = data->tkip->mic_keys.rx_mcast; 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* 93362306a36Sopenharmony_ci * For non-QoS this relies on the fact that both the uCode and 93462306a36Sopenharmony_ci * mac80211 use TID 0 (as they need to to avoid replay attacks) 93562306a36Sopenharmony_ci * for checking the IV in the frames. 93662306a36Sopenharmony_ci */ 93762306a36Sopenharmony_ci for (i = 0; i < IWLAGN_NUM_RSC; i++) { 93862306a36Sopenharmony_ci ieee80211_get_key_rx_seq(key, i, &seq); 93962306a36Sopenharmony_ci tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); 94062306a36Sopenharmony_ci tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); 94162306a36Sopenharmony_ci /* wrapping isn't allowed, AP must rekey */ 94262306a36Sopenharmony_ci if (seq.tkip.iv32 > cur_rx_iv32) 94362306a36Sopenharmony_ci cur_rx_iv32 = seq.tkip.iv32; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); 94762306a36Sopenharmony_ci iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); 94862306a36Sopenharmony_ci ieee80211_get_tkip_rx_p1k(key, data->bssid, 94962306a36Sopenharmony_ci cur_rx_iv32 + 1, p1k); 95062306a36Sopenharmony_ci iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci memcpy(rx_mic_key, 95362306a36Sopenharmony_ci &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], 95462306a36Sopenharmony_ci IWLAGN_MIC_KEY_SIZE); 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci data->use_tkip = true; 95762306a36Sopenharmony_ci data->use_rsc_tsc = true; 95862306a36Sopenharmony_ci break; 95962306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 96062306a36Sopenharmony_ci if (sta) { 96162306a36Sopenharmony_ci u64 pn64; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; 96462306a36Sopenharmony_ci aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci pn64 = atomic64_read(&key->tx_pn); 96762306a36Sopenharmony_ci aes_tx_sc->pn = cpu_to_le64(pn64); 96862306a36Sopenharmony_ci } else 96962306a36Sopenharmony_ci aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* 97262306a36Sopenharmony_ci * For non-QoS this relies on the fact that both the uCode and 97362306a36Sopenharmony_ci * mac80211 use TID 0 for checking the IV in the frames. 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_ci for (i = 0; i < IWLAGN_NUM_RSC; i++) { 97662306a36Sopenharmony_ci u8 *pn = seq.ccmp.pn; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ieee80211_get_key_rx_seq(key, i, &seq); 97962306a36Sopenharmony_ci aes_sc[i].pn = cpu_to_le64( 98062306a36Sopenharmony_ci (u64)pn[5] | 98162306a36Sopenharmony_ci ((u64)pn[4] << 8) | 98262306a36Sopenharmony_ci ((u64)pn[3] << 16) | 98362306a36Sopenharmony_ci ((u64)pn[2] << 24) | 98462306a36Sopenharmony_ci ((u64)pn[1] << 32) | 98562306a36Sopenharmony_ci ((u64)pn[0] << 40)); 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci data->use_rsc_tsc = true; 98862306a36Sopenharmony_ci break; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ciint iwlagn_send_patterns(struct iwl_priv *priv, 99562306a36Sopenharmony_ci struct cfg80211_wowlan *wowlan) 99662306a36Sopenharmony_ci{ 99762306a36Sopenharmony_ci struct iwlagn_wowlan_patterns_cmd *pattern_cmd; 99862306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 99962306a36Sopenharmony_ci .id = REPLY_WOWLAN_PATTERNS, 100062306a36Sopenharmony_ci .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 100162306a36Sopenharmony_ci }; 100262306a36Sopenharmony_ci int i, err; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (!wowlan->n_patterns) 100562306a36Sopenharmony_ci return 0; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); 101062306a36Sopenharmony_ci if (!pattern_cmd) 101162306a36Sopenharmony_ci return -ENOMEM; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci for (i = 0; i < wowlan->n_patterns; i++) { 101662306a36Sopenharmony_ci int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci memcpy(&pattern_cmd->patterns[i].mask, 101962306a36Sopenharmony_ci wowlan->patterns[i].mask, mask_len); 102062306a36Sopenharmony_ci memcpy(&pattern_cmd->patterns[i].pattern, 102162306a36Sopenharmony_ci wowlan->patterns[i].pattern, 102262306a36Sopenharmony_ci wowlan->patterns[i].pattern_len); 102362306a36Sopenharmony_ci pattern_cmd->patterns[i].mask_size = mask_len; 102462306a36Sopenharmony_ci pattern_cmd->patterns[i].pattern_size = 102562306a36Sopenharmony_ci wowlan->patterns[i].pattern_len; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci cmd.data[0] = pattern_cmd; 102962306a36Sopenharmony_ci err = iwl_dvm_send_cmd(priv, &cmd); 103062306a36Sopenharmony_ci kfree(pattern_cmd); 103162306a36Sopenharmony_ci return err; 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ciint iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; 103762306a36Sopenharmony_ci struct iwl_rxon_cmd rxon; 103862306a36Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 103962306a36Sopenharmony_ci struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; 104062306a36Sopenharmony_ci struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; 104162306a36Sopenharmony_ci struct iwlagn_d3_config_cmd d3_cfg_cmd = { 104262306a36Sopenharmony_ci /* 104362306a36Sopenharmony_ci * Program the minimum sleep time to 10 seconds, as many 104462306a36Sopenharmony_ci * platforms have issues processing a wakeup signal while 104562306a36Sopenharmony_ci * still being in the process of suspending. 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_ci .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), 104862306a36Sopenharmony_ci }; 104962306a36Sopenharmony_ci struct wowlan_key_data key_data = { 105062306a36Sopenharmony_ci .ctx = ctx, 105162306a36Sopenharmony_ci .bssid = ctx->active.bssid_addr, 105262306a36Sopenharmony_ci .use_rsc_tsc = false, 105362306a36Sopenharmony_ci .tkip = &tkip_cmd, 105462306a36Sopenharmony_ci .use_tkip = false, 105562306a36Sopenharmony_ci }; 105662306a36Sopenharmony_ci int ret, i; 105762306a36Sopenharmony_ci u16 seq; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); 106062306a36Sopenharmony_ci if (!key_data.rsc_tsc) 106162306a36Sopenharmony_ci return -ENOMEM; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci /* 106662306a36Sopenharmony_ci * We know the last used seqno, and the uCode expects to know that 106762306a36Sopenharmony_ci * one, it will increment before TX. 106862306a36Sopenharmony_ci */ 106962306a36Sopenharmony_ci seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; 107062306a36Sopenharmony_ci wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* 107362306a36Sopenharmony_ci * For QoS counters, we store the one to use next, so subtract 0x10 107462306a36Sopenharmony_ci * since the uCode will add 0x10 before using the value. 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 107762306a36Sopenharmony_ci seq = priv->tid_data[IWL_AP_ID][i].seq_number; 107862306a36Sopenharmony_ci seq -= 0x10; 107962306a36Sopenharmony_ci wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci if (wowlan->disconnect) 108362306a36Sopenharmony_ci wakeup_filter_cmd.enabled |= 108462306a36Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | 108562306a36Sopenharmony_ci IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); 108662306a36Sopenharmony_ci if (wowlan->magic_pkt) 108762306a36Sopenharmony_ci wakeup_filter_cmd.enabled |= 108862306a36Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); 108962306a36Sopenharmony_ci if (wowlan->gtk_rekey_failure) 109062306a36Sopenharmony_ci wakeup_filter_cmd.enabled |= 109162306a36Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); 109262306a36Sopenharmony_ci if (wowlan->eap_identity_req) 109362306a36Sopenharmony_ci wakeup_filter_cmd.enabled |= 109462306a36Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); 109562306a36Sopenharmony_ci if (wowlan->four_way_handshake) 109662306a36Sopenharmony_ci wakeup_filter_cmd.enabled |= 109762306a36Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); 109862306a36Sopenharmony_ci if (wowlan->n_patterns) 109962306a36Sopenharmony_ci wakeup_filter_cmd.enabled |= 110062306a36Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci if (wowlan->rfkill_release) 110362306a36Sopenharmony_ci d3_cfg_cmd.wakeup_flags |= 110462306a36Sopenharmony_ci cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci iwl_scan_cancel_timeout(priv, 200); 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci memcpy(&rxon, &ctx->active, sizeof(rxon)); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci priv->ucode_loaded = false; 111162306a36Sopenharmony_ci iwl_trans_stop_device(priv->trans); 111262306a36Sopenharmony_ci ret = iwl_trans_start_hw(priv->trans); 111362306a36Sopenharmony_ci if (ret) 111462306a36Sopenharmony_ci goto out; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci priv->wowlan = true; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); 111962306a36Sopenharmony_ci if (ret) 112062306a36Sopenharmony_ci goto out; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* now configure WoWLAN ucode */ 112362306a36Sopenharmony_ci ret = iwl_alive_start(priv); 112462306a36Sopenharmony_ci if (ret) 112562306a36Sopenharmony_ci goto out; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci memcpy(&ctx->staging, &rxon, sizeof(rxon)); 112862306a36Sopenharmony_ci ret = iwlagn_commit_rxon(priv, ctx); 112962306a36Sopenharmony_ci if (ret) 113062306a36Sopenharmony_ci goto out; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci ret = iwl_power_update_mode(priv, true); 113362306a36Sopenharmony_ci if (ret) 113462306a36Sopenharmony_ci goto out; 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci if (!iwlwifi_mod_params.swcrypto) { 113762306a36Sopenharmony_ci /* mark all keys clear */ 113862306a36Sopenharmony_ci priv->ucode_key_table = 0; 113962306a36Sopenharmony_ci ctx->key_mapping_keys = 0; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* 114262306a36Sopenharmony_ci * This needs to be unlocked due to lock ordering 114362306a36Sopenharmony_ci * constraints. Since we're in the suspend path 114462306a36Sopenharmony_ci * that isn't really a problem though. 114562306a36Sopenharmony_ci */ 114662306a36Sopenharmony_ci mutex_unlock(&priv->mutex); 114762306a36Sopenharmony_ci ieee80211_iter_keys(priv->hw, ctx->vif, 114862306a36Sopenharmony_ci iwlagn_wowlan_program_keys, 114962306a36Sopenharmony_ci &key_data); 115062306a36Sopenharmony_ci mutex_lock(&priv->mutex); 115162306a36Sopenharmony_ci if (key_data.error) { 115262306a36Sopenharmony_ci ret = -EIO; 115362306a36Sopenharmony_ci goto out; 115462306a36Sopenharmony_ci } 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (key_data.use_rsc_tsc) { 115762306a36Sopenharmony_ci struct iwl_host_cmd rsc_tsc_cmd = { 115862306a36Sopenharmony_ci .id = REPLY_WOWLAN_TSC_RSC_PARAMS, 115962306a36Sopenharmony_ci .data[0] = key_data.rsc_tsc, 116062306a36Sopenharmony_ci .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 116162306a36Sopenharmony_ci .len[0] = sizeof(*key_data.rsc_tsc), 116262306a36Sopenharmony_ci }; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); 116562306a36Sopenharmony_ci if (ret) 116662306a36Sopenharmony_ci goto out; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci if (key_data.use_tkip) { 117062306a36Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, 117162306a36Sopenharmony_ci REPLY_WOWLAN_TKIP_PARAMS, 117262306a36Sopenharmony_ci 0, sizeof(tkip_cmd), 117362306a36Sopenharmony_ci &tkip_cmd); 117462306a36Sopenharmony_ci if (ret) 117562306a36Sopenharmony_ci goto out; 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci if (priv->have_rekey_data) { 117962306a36Sopenharmony_ci memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); 118062306a36Sopenharmony_ci memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); 118162306a36Sopenharmony_ci kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); 118262306a36Sopenharmony_ci memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); 118362306a36Sopenharmony_ci kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); 118462306a36Sopenharmony_ci kek_kck_cmd.replay_ctr = priv->replay_ctr; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, 118762306a36Sopenharmony_ci REPLY_WOWLAN_KEK_KCK_MATERIAL, 118862306a36Sopenharmony_ci 0, sizeof(kek_kck_cmd), 118962306a36Sopenharmony_ci &kek_kck_cmd); 119062306a36Sopenharmony_ci if (ret) 119162306a36Sopenharmony_ci goto out; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci } 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0, 119662306a36Sopenharmony_ci sizeof(d3_cfg_cmd), &d3_cfg_cmd); 119762306a36Sopenharmony_ci if (ret) 119862306a36Sopenharmony_ci goto out; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, 120162306a36Sopenharmony_ci 0, sizeof(wakeup_filter_cmd), 120262306a36Sopenharmony_ci &wakeup_filter_cmd); 120362306a36Sopenharmony_ci if (ret) 120462306a36Sopenharmony_ci goto out; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci ret = iwlagn_send_patterns(priv, wowlan); 120762306a36Sopenharmony_ci out: 120862306a36Sopenharmony_ci kfree(key_data.rsc_tsc); 120962306a36Sopenharmony_ci return ret; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci#endif 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ciint iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { 121662306a36Sopenharmony_ci IWL_WARN(priv, "Not sending command - %s KILL\n", 121762306a36Sopenharmony_ci iwl_is_rfkill(priv) ? "RF" : "CT"); 121862306a36Sopenharmony_ci return -EIO; 121962306a36Sopenharmony_ci } 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci if (test_bit(STATUS_FW_ERROR, &priv->status)) { 122262306a36Sopenharmony_ci IWL_ERR(priv, "Command %s failed: FW Error\n", 122362306a36Sopenharmony_ci iwl_get_cmd_string(priv->trans, cmd->id)); 122462306a36Sopenharmony_ci return -EIO; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* 122862306a36Sopenharmony_ci * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag 122962306a36Sopenharmony_ci * in iwl_down but cancel the workers only later. 123062306a36Sopenharmony_ci */ 123162306a36Sopenharmony_ci if (!priv->ucode_loaded) { 123262306a36Sopenharmony_ci IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id); 123362306a36Sopenharmony_ci return -EIO; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* 123762306a36Sopenharmony_ci * Synchronous commands from this op-mode must hold 123862306a36Sopenharmony_ci * the mutex, this ensures we don't try to send two 123962306a36Sopenharmony_ci * (or more) synchronous commands at a time. 124062306a36Sopenharmony_ci */ 124162306a36Sopenharmony_ci if (!(cmd->flags & CMD_ASYNC)) 124262306a36Sopenharmony_ci lockdep_assert_held(&priv->mutex); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci return iwl_trans_send_cmd(priv->trans, cmd); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ciint iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, 124862306a36Sopenharmony_ci u32 flags, u16 len, const void *data) 124962306a36Sopenharmony_ci{ 125062306a36Sopenharmony_ci struct iwl_host_cmd cmd = { 125162306a36Sopenharmony_ci .id = id, 125262306a36Sopenharmony_ci .len = { len, }, 125362306a36Sopenharmony_ci .data = { data, }, 125462306a36Sopenharmony_ci .flags = flags, 125562306a36Sopenharmony_ci }; 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd); 125862306a36Sopenharmony_ci} 1259