18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/****************************************************************************** 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Contact Information: 78c2ecf20Sopenharmony_ci * Intel Linux Wireless <linuxwifi@intel.com> 88c2ecf20Sopenharmony_ci * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci *****************************************************************************/ 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/sched.h> 158c2ecf20Sopenharmony_ci#include <net/mac80211.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "iwl-io.h" 188c2ecf20Sopenharmony_ci#include "iwl-agn-hw.h" 198c2ecf20Sopenharmony_ci#include "iwl-trans.h" 208c2ecf20Sopenharmony_ci#include "iwl-modparams.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "dev.h" 238c2ecf20Sopenharmony_ci#include "agn.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ciint iwlagn_hw_valid_rtc_data_addr(u32 addr) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci return (addr >= IWLAGN_RTC_DATA_LOWER_BOUND) && 288c2ecf20Sopenharmony_ci (addr < IWLAGN_RTC_DATA_UPPER_BOUND); 298c2ecf20Sopenharmony_ci} 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ciint iwlagn_send_tx_power(struct iwl_priv *priv) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci struct iwlagn_tx_power_dbm_cmd tx_power_cmd; 348c2ecf20Sopenharmony_ci u8 tx_ant_cfg_cmd; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci if (WARN_ONCE(test_bit(STATUS_SCAN_HW, &priv->status), 378c2ecf20Sopenharmony_ci "TX Power requested while scanning!\n")) 388c2ecf20Sopenharmony_ci return -EAGAIN; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci /* half dBm need to multiply */ 418c2ecf20Sopenharmony_ci tx_power_cmd.global_lmt = (s8)(2 * priv->tx_power_user_lmt); 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci if (tx_power_cmd.global_lmt > priv->nvm_data->max_tx_pwr_half_dbm) { 448c2ecf20Sopenharmony_ci /* 458c2ecf20Sopenharmony_ci * For the newer devices which using enhanced/extend tx power 468c2ecf20Sopenharmony_ci * table in EEPROM, the format is in half dBm. driver need to 478c2ecf20Sopenharmony_ci * convert to dBm format before report to mac80211. 488c2ecf20Sopenharmony_ci * By doing so, there is a possibility of 1/2 dBm resolution 498c2ecf20Sopenharmony_ci * lost. driver will perform "round-up" operation before 508c2ecf20Sopenharmony_ci * reporting, but it will cause 1/2 dBm tx power over the 518c2ecf20Sopenharmony_ci * regulatory limit. Perform the checking here, if the 528c2ecf20Sopenharmony_ci * "tx_power_user_lmt" is higher than EEPROM value (in 538c2ecf20Sopenharmony_ci * half-dBm format), lower the tx power based on EEPROM 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_ci tx_power_cmd.global_lmt = 568c2ecf20Sopenharmony_ci priv->nvm_data->max_tx_pwr_half_dbm; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci tx_power_cmd.flags = IWLAGN_TX_POWER_NO_CLOSED; 598c2ecf20Sopenharmony_ci tx_power_cmd.srv_chan_lmt = IWLAGN_TX_POWER_AUTO; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (IWL_UCODE_API(priv->fw->ucode_ver) == 1) 628c2ecf20Sopenharmony_ci tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD_V1; 638c2ecf20Sopenharmony_ci else 648c2ecf20Sopenharmony_ci tx_ant_cfg_cmd = REPLY_TX_POWER_DBM_CMD; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return iwl_dvm_send_cmd_pdu(priv, tx_ant_cfg_cmd, 0, 678c2ecf20Sopenharmony_ci sizeof(tx_power_cmd), &tx_power_cmd); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_civoid iwlagn_temperature(struct iwl_priv *priv) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->statistics.lock); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* store temperature from correct statistics (in Celsius) */ 758c2ecf20Sopenharmony_ci priv->temperature = le32_to_cpu(priv->statistics.common.temperature); 768c2ecf20Sopenharmony_ci iwl_tt_handler(priv); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciint iwlagn_hwrate_to_mac80211_idx(u32 rate_n_flags, enum nl80211_band band) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci int idx = 0; 828c2ecf20Sopenharmony_ci int band_offset = 0; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci /* HT rate format: mac80211 wants an MCS number, which is just LSB */ 858c2ecf20Sopenharmony_ci if (rate_n_flags & RATE_MCS_HT_MSK) { 868c2ecf20Sopenharmony_ci idx = (rate_n_flags & 0xff); 878c2ecf20Sopenharmony_ci return idx; 888c2ecf20Sopenharmony_ci /* Legacy rate format, search for match in table */ 898c2ecf20Sopenharmony_ci } else { 908c2ecf20Sopenharmony_ci if (band == NL80211_BAND_5GHZ) 918c2ecf20Sopenharmony_ci band_offset = IWL_FIRST_OFDM_RATE; 928c2ecf20Sopenharmony_ci for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) 938c2ecf20Sopenharmony_ci if (iwl_rates[idx].plcp == (rate_n_flags & 0xFF)) 948c2ecf20Sopenharmony_ci return idx - band_offset; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci return -1; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ciint iwlagn_manage_ibss_station(struct iwl_priv *priv, 1018c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, bool add) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (add) 1068c2ecf20Sopenharmony_ci return iwlagn_add_bssid_station(priv, vif_priv->ctx, 1078c2ecf20Sopenharmony_ci vif->bss_conf.bssid, 1088c2ecf20Sopenharmony_ci &vif_priv->ibss_bssid_sta_id); 1098c2ecf20Sopenharmony_ci return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, 1108c2ecf20Sopenharmony_ci vif->bss_conf.bssid); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * iwlagn_txfifo_flush: send REPLY_TXFIFO_FLUSH command to uCode 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * pre-requirements: 1178c2ecf20Sopenharmony_ci * 1. acquire mutex before calling 1188c2ecf20Sopenharmony_ci * 2. make sure rf is on and not in exit state 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ciint iwlagn_txfifo_flush(struct iwl_priv *priv, u32 scd_q_msk) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct iwl_txfifo_flush_cmd_v3 flush_cmd_v3 = { 1238c2ecf20Sopenharmony_ci .flush_control = cpu_to_le16(IWL_DROP_ALL), 1248c2ecf20Sopenharmony_ci }; 1258c2ecf20Sopenharmony_ci struct iwl_txfifo_flush_cmd_v2 flush_cmd_v2 = { 1268c2ecf20Sopenharmony_ci .flush_control = cpu_to_le16(IWL_DROP_ALL), 1278c2ecf20Sopenharmony_ci }; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci u32 queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | 1308c2ecf20Sopenharmony_ci IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | IWL_SCD_MGMT_MSK; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) 1338c2ecf20Sopenharmony_ci queue_control |= IWL_PAN_SCD_VO_MSK | IWL_PAN_SCD_VI_MSK | 1348c2ecf20Sopenharmony_ci IWL_PAN_SCD_BE_MSK | IWL_PAN_SCD_BK_MSK | 1358c2ecf20Sopenharmony_ci IWL_PAN_SCD_MGMT_MSK | 1368c2ecf20Sopenharmony_ci IWL_PAN_SCD_MULTICAST_MSK; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (priv->nvm_data->sku_cap_11n_enable) 1398c2ecf20Sopenharmony_ci queue_control |= IWL_AGG_TX_QUEUE_MSK; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci if (scd_q_msk) 1428c2ecf20Sopenharmony_ci queue_control = scd_q_msk; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", queue_control); 1458c2ecf20Sopenharmony_ci flush_cmd_v3.queue_control = cpu_to_le32(queue_control); 1468c2ecf20Sopenharmony_ci flush_cmd_v2.queue_control = cpu_to_le16((u16)queue_control); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (IWL_UCODE_API(priv->fw->ucode_ver) > 2) 1498c2ecf20Sopenharmony_ci return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, 1508c2ecf20Sopenharmony_ci sizeof(flush_cmd_v3), 1518c2ecf20Sopenharmony_ci &flush_cmd_v3); 1528c2ecf20Sopenharmony_ci return iwl_dvm_send_cmd_pdu(priv, REPLY_TXFIFO_FLUSH, 0, 1538c2ecf20Sopenharmony_ci sizeof(flush_cmd_v2), &flush_cmd_v2); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_civoid iwlagn_dev_txfifo_flush(struct iwl_priv *priv) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci mutex_lock(&priv->mutex); 1598c2ecf20Sopenharmony_ci ieee80211_stop_queues(priv->hw); 1608c2ecf20Sopenharmony_ci if (iwlagn_txfifo_flush(priv, 0)) { 1618c2ecf20Sopenharmony_ci IWL_ERR(priv, "flush request fail\n"); 1628c2ecf20Sopenharmony_ci goto done; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "wait transmit/flush all frames\n"); 1658c2ecf20Sopenharmony_ci iwl_trans_wait_tx_queues_empty(priv->trans, 0xffffffff); 1668c2ecf20Sopenharmony_cidone: 1678c2ecf20Sopenharmony_ci ieee80211_wake_queues(priv->hw); 1688c2ecf20Sopenharmony_ci mutex_unlock(&priv->mutex); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci/* 1728c2ecf20Sopenharmony_ci * BT coex 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci/* Notmal TDM */ 1758c2ecf20Sopenharmony_cistatic const __le32 iwlagn_def_3w_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { 1768c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1778c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1788c2ecf20Sopenharmony_ci cpu_to_le32(0xaeaaaaaa), 1798c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1808c2ecf20Sopenharmony_ci cpu_to_le32(0xcc00ff28), 1818c2ecf20Sopenharmony_ci cpu_to_le32(0x0000aaaa), 1828c2ecf20Sopenharmony_ci cpu_to_le32(0xcc00aaaa), 1838c2ecf20Sopenharmony_ci cpu_to_le32(0x0000aaaa), 1848c2ecf20Sopenharmony_ci cpu_to_le32(0xc0004000), 1858c2ecf20Sopenharmony_ci cpu_to_le32(0x00004000), 1868c2ecf20Sopenharmony_ci cpu_to_le32(0xf0005000), 1878c2ecf20Sopenharmony_ci cpu_to_le32(0xf0005000), 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* Full concurrency */ 1918c2ecf20Sopenharmony_cistatic const __le32 iwlagn_concurrent_lookup[IWLAGN_BT_DECISION_LUT_SIZE] = { 1928c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1938c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1948c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1958c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1968c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1978c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1988c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 1998c2ecf20Sopenharmony_ci cpu_to_le32(0xaaaaaaaa), 2008c2ecf20Sopenharmony_ci cpu_to_le32(0x00000000), 2018c2ecf20Sopenharmony_ci cpu_to_le32(0x00000000), 2028c2ecf20Sopenharmony_ci cpu_to_le32(0x00000000), 2038c2ecf20Sopenharmony_ci cpu_to_le32(0x00000000), 2048c2ecf20Sopenharmony_ci}; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_civoid iwlagn_send_advance_bt_config(struct iwl_priv *priv) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct iwl_basic_bt_cmd basic = { 2098c2ecf20Sopenharmony_ci .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, 2108c2ecf20Sopenharmony_ci .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, 2118c2ecf20Sopenharmony_ci .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, 2128c2ecf20Sopenharmony_ci .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, 2138c2ecf20Sopenharmony_ci }; 2148c2ecf20Sopenharmony_ci struct iwl_bt_cmd_v1 bt_cmd_v1; 2158c2ecf20Sopenharmony_ci struct iwl_bt_cmd_v2 bt_cmd_v2; 2168c2ecf20Sopenharmony_ci int ret; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != 2198c2ecf20Sopenharmony_ci sizeof(basic.bt3_lookup_table)); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci if (priv->lib->bt_params) { 2228c2ecf20Sopenharmony_ci /* 2238c2ecf20Sopenharmony_ci * newer generation of devices (2000 series and newer) 2248c2ecf20Sopenharmony_ci * use the version 2 of the bt command 2258c2ecf20Sopenharmony_ci * we need to make sure sending the host command 2268c2ecf20Sopenharmony_ci * with correct data structure to avoid uCode assert 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_ci if (priv->lib->bt_params->bt_session_2) { 2298c2ecf20Sopenharmony_ci bt_cmd_v2.prio_boost = cpu_to_le32( 2308c2ecf20Sopenharmony_ci priv->lib->bt_params->bt_prio_boost); 2318c2ecf20Sopenharmony_ci bt_cmd_v2.tx_prio_boost = 0; 2328c2ecf20Sopenharmony_ci bt_cmd_v2.rx_prio_boost = 0; 2338c2ecf20Sopenharmony_ci } else { 2348c2ecf20Sopenharmony_ci /* older version only has 8 bits */ 2358c2ecf20Sopenharmony_ci WARN_ON(priv->lib->bt_params->bt_prio_boost & ~0xFF); 2368c2ecf20Sopenharmony_ci bt_cmd_v1.prio_boost = 2378c2ecf20Sopenharmony_ci priv->lib->bt_params->bt_prio_boost; 2388c2ecf20Sopenharmony_ci bt_cmd_v1.tx_prio_boost = 0; 2398c2ecf20Sopenharmony_ci bt_cmd_v1.rx_prio_boost = 0; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci IWL_ERR(priv, "failed to construct BT Coex Config\n"); 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * Possible situations when BT needs to take over for receive, 2488c2ecf20Sopenharmony_ci * at the same time where STA needs to response to AP's frame(s), 2498c2ecf20Sopenharmony_ci * reduce the tx power of the required response frames, by that, 2508c2ecf20Sopenharmony_ci * allow the concurrent BT receive & WiFi transmit 2518c2ecf20Sopenharmony_ci * (BT - ANT A, WiFi -ANT B), without interference to one another 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * Reduced tx power apply to control frames only (ACK/Back/CTS) 2548c2ecf20Sopenharmony_ci * when indicated by the BT config command 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci basic.kill_ack_mask = priv->kill_ack_mask; 2578c2ecf20Sopenharmony_ci basic.kill_cts_mask = priv->kill_cts_mask; 2588c2ecf20Sopenharmony_ci if (priv->reduced_txpower) 2598c2ecf20Sopenharmony_ci basic.reduce_txpower = IWLAGN_BT_REDUCED_TX_PWR; 2608c2ecf20Sopenharmony_ci basic.valid = priv->bt_valid; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * Configure BT coex mode to "no coexistence" when the 2648c2ecf20Sopenharmony_ci * user disabled BT coexistence, we have no interface 2658c2ecf20Sopenharmony_ci * (might be in monitor mode), or the interface is in 2668c2ecf20Sopenharmony_ci * IBSS mode (no proper uCode support for coex then). 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_ci if (!iwlwifi_mod_params.bt_coex_active || 2698c2ecf20Sopenharmony_ci priv->iw_mode == NL80211_IFTYPE_ADHOC) { 2708c2ecf20Sopenharmony_ci basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; 2718c2ecf20Sopenharmony_ci } else { 2728c2ecf20Sopenharmony_ci basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << 2738c2ecf20Sopenharmony_ci IWLAGN_BT_FLAG_COEX_MODE_SHIFT; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (!priv->bt_enable_pspoll) 2768c2ecf20Sopenharmony_ci basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; 2778c2ecf20Sopenharmony_ci else 2788c2ecf20Sopenharmony_ci basic.flags &= ~IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (priv->bt_ch_announce) 2818c2ecf20Sopenharmony_ci basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; 2828c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT coex flag: 0X%x\n", basic.flags); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci priv->bt_enable_flag = basic.flags; 2858c2ecf20Sopenharmony_ci if (priv->bt_full_concurrent) 2868c2ecf20Sopenharmony_ci memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, 2878c2ecf20Sopenharmony_ci sizeof(iwlagn_concurrent_lookup)); 2888c2ecf20Sopenharmony_ci else 2898c2ecf20Sopenharmony_ci memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, 2908c2ecf20Sopenharmony_ci sizeof(iwlagn_def_3w_lookup)); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT coex %s in %s mode\n", 2938c2ecf20Sopenharmony_ci basic.flags ? "active" : "disabled", 2948c2ecf20Sopenharmony_ci priv->bt_full_concurrent ? 2958c2ecf20Sopenharmony_ci "full concurrency" : "3-wire"); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (priv->lib->bt_params->bt_session_2) { 2988c2ecf20Sopenharmony_ci memcpy(&bt_cmd_v2.basic, &basic, 2998c2ecf20Sopenharmony_ci sizeof(basic)); 3008c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, 3018c2ecf20Sopenharmony_ci 0, sizeof(bt_cmd_v2), &bt_cmd_v2); 3028c2ecf20Sopenharmony_ci } else { 3038c2ecf20Sopenharmony_ci memcpy(&bt_cmd_v1.basic, &basic, 3048c2ecf20Sopenharmony_ci sizeof(basic)); 3058c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_BT_CONFIG, 3068c2ecf20Sopenharmony_ci 0, sizeof(bt_cmd_v1), &bt_cmd_v1); 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci if (ret) 3098c2ecf20Sopenharmony_ci IWL_ERR(priv, "failed to send BT Coex Config\n"); 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_civoid iwlagn_bt_adjust_rssi_monitor(struct iwl_priv *priv, bool rssi_ena) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx, *found_ctx = NULL; 3168c2ecf20Sopenharmony_ci bool found_ap = false; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci /* Check whether AP or GO mode is active. */ 3218c2ecf20Sopenharmony_ci if (rssi_ena) { 3228c2ecf20Sopenharmony_ci for_each_context(priv, ctx) { 3238c2ecf20Sopenharmony_ci if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_AP && 3248c2ecf20Sopenharmony_ci iwl_is_associated_ctx(ctx)) { 3258c2ecf20Sopenharmony_ci found_ap = true; 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* 3328c2ecf20Sopenharmony_ci * If disable was received or If GO/AP mode, disable RSSI 3338c2ecf20Sopenharmony_ci * measurements. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ci if (!rssi_ena || found_ap) { 3368c2ecf20Sopenharmony_ci if (priv->cur_rssi_ctx) { 3378c2ecf20Sopenharmony_ci ctx = priv->cur_rssi_ctx; 3388c2ecf20Sopenharmony_ci ieee80211_disable_rssi_reports(ctx->vif); 3398c2ecf20Sopenharmony_ci priv->cur_rssi_ctx = NULL; 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci return; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* 3458c2ecf20Sopenharmony_ci * If rssi measurements need to be enabled, consider all cases now. 3468c2ecf20Sopenharmony_ci * Figure out how many contexts are active. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci for_each_context(priv, ctx) { 3498c2ecf20Sopenharmony_ci if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION && 3508c2ecf20Sopenharmony_ci iwl_is_associated_ctx(ctx)) { 3518c2ecf20Sopenharmony_ci found_ctx = ctx; 3528c2ecf20Sopenharmony_ci break; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* 3578c2ecf20Sopenharmony_ci * rssi monitor already enabled for the correct interface...nothing 3588c2ecf20Sopenharmony_ci * to do. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci if (found_ctx == priv->cur_rssi_ctx) 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* 3648c2ecf20Sopenharmony_ci * Figure out if rssi monitor is currently enabled, and needs 3658c2ecf20Sopenharmony_ci * to be changed. If rssi monitor is already enabled, disable 3668c2ecf20Sopenharmony_ci * it first else just enable rssi measurements on the 3678c2ecf20Sopenharmony_ci * interface found above. 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_ci if (priv->cur_rssi_ctx) { 3708c2ecf20Sopenharmony_ci ctx = priv->cur_rssi_ctx; 3718c2ecf20Sopenharmony_ci if (ctx->vif) 3728c2ecf20Sopenharmony_ci ieee80211_disable_rssi_reports(ctx->vif); 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci priv->cur_rssi_ctx = found_ctx; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (!found_ctx) 3788c2ecf20Sopenharmony_ci return; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci ieee80211_enable_rssi_reports(found_ctx->vif, 3818c2ecf20Sopenharmony_ci IWLAGN_BT_PSP_MIN_RSSI_THRESHOLD, 3828c2ecf20Sopenharmony_ci IWLAGN_BT_PSP_MAX_RSSI_THRESHOLD); 3838c2ecf20Sopenharmony_ci} 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_cistatic bool iwlagn_bt_traffic_is_sco(struct iwl_bt_uart_msg *uart_msg) 3868c2ecf20Sopenharmony_ci{ 3878c2ecf20Sopenharmony_ci return (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> 3888c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3SCOESCO_POS; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic void iwlagn_bt_traffic_change_work(struct work_struct *work) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct iwl_priv *priv = 3948c2ecf20Sopenharmony_ci container_of(work, struct iwl_priv, bt_traffic_change_work); 3958c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx; 3968c2ecf20Sopenharmony_ci int smps_request = -1; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { 3998c2ecf20Sopenharmony_ci /* bt coex disabled */ 4008c2ecf20Sopenharmony_ci return; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* 4048c2ecf20Sopenharmony_ci * Note: bt_traffic_load can be overridden by scan complete and 4058c2ecf20Sopenharmony_ci * coex profile notifications. Ignore that since only bad consequence 4068c2ecf20Sopenharmony_ci * can be not matching debug print with actual state. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT traffic load changes: %d\n", 4098c2ecf20Sopenharmony_ci priv->bt_traffic_load); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci switch (priv->bt_traffic_load) { 4128c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_NONE: 4138c2ecf20Sopenharmony_ci if (priv->bt_status) 4148c2ecf20Sopenharmony_ci smps_request = IEEE80211_SMPS_DYNAMIC; 4158c2ecf20Sopenharmony_ci else 4168c2ecf20Sopenharmony_ci smps_request = IEEE80211_SMPS_AUTOMATIC; 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_LOW: 4198c2ecf20Sopenharmony_ci smps_request = IEEE80211_SMPS_DYNAMIC; 4208c2ecf20Sopenharmony_ci break; 4218c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_HIGH: 4228c2ecf20Sopenharmony_ci case IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS: 4238c2ecf20Sopenharmony_ci smps_request = IEEE80211_SMPS_STATIC; 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci default: 4268c2ecf20Sopenharmony_ci IWL_ERR(priv, "Invalid BT traffic load: %d\n", 4278c2ecf20Sopenharmony_ci priv->bt_traffic_load); 4288c2ecf20Sopenharmony_ci break; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci mutex_lock(&priv->mutex); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* 4348c2ecf20Sopenharmony_ci * We can not send command to firmware while scanning. When the scan 4358c2ecf20Sopenharmony_ci * complete we will schedule this work again. We do check with mutex 4368c2ecf20Sopenharmony_ci * locked to prevent new scan request to arrive. We do not check 4378c2ecf20Sopenharmony_ci * STATUS_SCANNING to avoid race when queue_work two times from 4388c2ecf20Sopenharmony_ci * different notifications, but quit and not perform any work at all. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci if (test_bit(STATUS_SCAN_HW, &priv->status)) 4418c2ecf20Sopenharmony_ci goto out; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci iwl_update_chain_flags(priv); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (smps_request != -1) { 4468c2ecf20Sopenharmony_ci priv->current_ht_config.smps = smps_request; 4478c2ecf20Sopenharmony_ci for_each_context(priv, ctx) { 4488c2ecf20Sopenharmony_ci if (ctx->vif && ctx->vif->type == NL80211_IFTYPE_STATION) 4498c2ecf20Sopenharmony_ci ieee80211_request_smps(ctx->vif, smps_request); 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci /* 4548c2ecf20Sopenharmony_ci * Dynamic PS poll related functionality. Adjust RSSI measurements if 4558c2ecf20Sopenharmony_ci * necessary. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci iwlagn_bt_coex_rssi_monitor(priv); 4588c2ecf20Sopenharmony_ciout: 4598c2ecf20Sopenharmony_ci mutex_unlock(&priv->mutex); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/* 4638c2ecf20Sopenharmony_ci * If BT sco traffic, and RSSI monitor is enabled, move measurements to the 4648c2ecf20Sopenharmony_ci * correct interface or disable it if this is the last interface to be 4658c2ecf20Sopenharmony_ci * removed. 4668c2ecf20Sopenharmony_ci */ 4678c2ecf20Sopenharmony_civoid iwlagn_bt_coex_rssi_monitor(struct iwl_priv *priv) 4688c2ecf20Sopenharmony_ci{ 4698c2ecf20Sopenharmony_ci if (priv->bt_is_sco && 4708c2ecf20Sopenharmony_ci priv->bt_traffic_load == IWL_BT_COEX_TRAFFIC_LOAD_CONTINUOUS) 4718c2ecf20Sopenharmony_ci iwlagn_bt_adjust_rssi_monitor(priv, true); 4728c2ecf20Sopenharmony_ci else 4738c2ecf20Sopenharmony_ci iwlagn_bt_adjust_rssi_monitor(priv, false); 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic void iwlagn_print_uartmsg(struct iwl_priv *priv, 4778c2ecf20Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "Message Type = 0x%X, SSN = 0x%X, " 4808c2ecf20Sopenharmony_ci "Update Req = 0x%X\n", 4818c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME1MSGTYPE_MSK & uart_msg->frame1) >> 4828c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME1MSGTYPE_POS, 4838c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME1SSN_MSK & uart_msg->frame1) >> 4848c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME1SSN_POS, 4858c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME1UPDATEREQ_MSK & uart_msg->frame1) >> 4868c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME1UPDATEREQ_POS); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "Open connections = 0x%X, Traffic load = 0x%X, " 4898c2ecf20Sopenharmony_ci "Chl_SeqN = 0x%X, In band = 0x%X\n", 4908c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME2OPENCONNECTIONS_MSK & uart_msg->frame2) >> 4918c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME2OPENCONNECTIONS_POS, 4928c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME2TRAFFICLOAD_MSK & uart_msg->frame2) >> 4938c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME2TRAFFICLOAD_POS, 4948c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME2CHLSEQN_MSK & uart_msg->frame2) >> 4958c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME2CHLSEQN_POS, 4968c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME2INBAND_MSK & uart_msg->frame2) >> 4978c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME2INBAND_POS); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "SCO/eSCO = 0x%X, Sniff = 0x%X, A2DP = 0x%X, " 5008c2ecf20Sopenharmony_ci "ACL = 0x%X, Master = 0x%X, OBEX = 0x%X\n", 5018c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) >> 5028c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3SCOESCO_POS, 5038c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME3SNIFF_MSK & uart_msg->frame3) >> 5048c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3SNIFF_POS, 5058c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME3A2DP_MSK & uart_msg->frame3) >> 5068c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3A2DP_POS, 5078c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME3ACL_MSK & uart_msg->frame3) >> 5088c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3ACL_POS, 5098c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME3MASTER_MSK & uart_msg->frame3) >> 5108c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3MASTER_POS, 5118c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME3OBEX_MSK & uart_msg->frame3) >> 5128c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3OBEX_POS); 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "Idle duration = 0x%X\n", 5158c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME4IDLEDURATION_MSK & uart_msg->frame4) >> 5168c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME4IDLEDURATION_POS); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "Tx Activity = 0x%X, Rx Activity = 0x%X, " 5198c2ecf20Sopenharmony_ci "eSCO Retransmissions = 0x%X\n", 5208c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME5TXACTIVITY_MSK & uart_msg->frame5) >> 5218c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME5TXACTIVITY_POS, 5228c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME5RXACTIVITY_MSK & uart_msg->frame5) >> 5238c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME5RXACTIVITY_POS, 5248c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME5ESCORETRANSMIT_MSK & uart_msg->frame5) >> 5258c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME5ESCORETRANSMIT_POS); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "Sniff Interval = 0x%X, Discoverable = 0x%X\n", 5288c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME6SNIFFINTERVAL_MSK & uart_msg->frame6) >> 5298c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME6SNIFFINTERVAL_POS, 5308c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> 5318c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME6DISCOVERABLE_POS); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "Sniff Activity = 0x%X, Page = " 5348c2ecf20Sopenharmony_ci "0x%X, Inquiry = 0x%X, Connectable = 0x%X\n", 5358c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> 5368c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, 5378c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> 5388c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME7PAGE_POS, 5398c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> 5408c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME7INQUIRY_POS, 5418c2ecf20Sopenharmony_ci (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> 5428c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME7CONNECTABLE_POS); 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic bool iwlagn_set_kill_msk(struct iwl_priv *priv, 5468c2ecf20Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci bool need_update = false; 5498c2ecf20Sopenharmony_ci u8 kill_msk = IWL_BT_KILL_REDUCE; 5508c2ecf20Sopenharmony_ci static const __le32 bt_kill_ack_msg[3] = { 5518c2ecf20Sopenharmony_ci IWLAGN_BT_KILL_ACK_MASK_DEFAULT, 5528c2ecf20Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, 5538c2ecf20Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; 5548c2ecf20Sopenharmony_ci static const __le32 bt_kill_cts_msg[3] = { 5558c2ecf20Sopenharmony_ci IWLAGN_BT_KILL_CTS_MASK_DEFAULT, 5568c2ecf20Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_SCO, 5578c2ecf20Sopenharmony_ci IWLAGN_BT_KILL_ACK_CTS_MASK_REDUCE}; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!priv->reduced_txpower) 5608c2ecf20Sopenharmony_ci kill_msk = (BT_UART_MSG_FRAME3SCOESCO_MSK & uart_msg->frame3) 5618c2ecf20Sopenharmony_ci ? IWL_BT_KILL_OVERRIDE : IWL_BT_KILL_DEFAULT; 5628c2ecf20Sopenharmony_ci if (priv->kill_ack_mask != bt_kill_ack_msg[kill_msk] || 5638c2ecf20Sopenharmony_ci priv->kill_cts_mask != bt_kill_cts_msg[kill_msk]) { 5648c2ecf20Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_KILL_ACK_MASK; 5658c2ecf20Sopenharmony_ci priv->kill_ack_mask = bt_kill_ack_msg[kill_msk]; 5668c2ecf20Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_KILL_CTS_MASK; 5678c2ecf20Sopenharmony_ci priv->kill_cts_mask = bt_kill_cts_msg[kill_msk]; 5688c2ecf20Sopenharmony_ci need_update = true; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci return need_update; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci/* 5748c2ecf20Sopenharmony_ci * Upon RSSI changes, sends a bt config command with following changes 5758c2ecf20Sopenharmony_ci * 1. enable/disable "reduced control frames tx power 5768c2ecf20Sopenharmony_ci * 2. update the "kill)ack_mask" and "kill_cts_mask" 5778c2ecf20Sopenharmony_ci * 5788c2ecf20Sopenharmony_ci * If "reduced tx power" is enabled, uCode shall 5798c2ecf20Sopenharmony_ci * 1. ACK/Back/CTS rate shall reduced to 6Mbps 5808c2ecf20Sopenharmony_ci * 2. not use duplciate 20/40MHz mode 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_cistatic bool iwlagn_fill_txpower_mode(struct iwl_priv *priv, 5838c2ecf20Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg) 5848c2ecf20Sopenharmony_ci{ 5858c2ecf20Sopenharmony_ci bool need_update = false; 5868c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 5878c2ecf20Sopenharmony_ci int ave_rssi; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci if (!ctx->vif || (ctx->vif->type != NL80211_IFTYPE_STATION)) { 5908c2ecf20Sopenharmony_ci IWL_DEBUG_INFO(priv, "BSS ctx not active or not in sta mode\n"); 5918c2ecf20Sopenharmony_ci return false; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci ave_rssi = ieee80211_ave_rssi(ctx->vif); 5958c2ecf20Sopenharmony_ci if (!ave_rssi) { 5968c2ecf20Sopenharmony_ci /* no rssi data, no changes to reduce tx power */ 5978c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "no rssi data available\n"); 5988c2ecf20Sopenharmony_ci return need_update; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci if (!priv->reduced_txpower && 6018c2ecf20Sopenharmony_ci !iwl_is_associated(priv, IWL_RXON_CTX_PAN) && 6028c2ecf20Sopenharmony_ci (ave_rssi > BT_ENABLE_REDUCED_TXPOWER_THRESHOLD) && 6038c2ecf20Sopenharmony_ci (uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | 6048c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3OBEX_MSK)) && 6058c2ecf20Sopenharmony_ci !(uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | 6068c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK))) { 6078c2ecf20Sopenharmony_ci /* enabling reduced tx power */ 6088c2ecf20Sopenharmony_ci priv->reduced_txpower = true; 6098c2ecf20Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; 6108c2ecf20Sopenharmony_ci need_update = true; 6118c2ecf20Sopenharmony_ci } else if (priv->reduced_txpower && 6128c2ecf20Sopenharmony_ci (iwl_is_associated(priv, IWL_RXON_CTX_PAN) || 6138c2ecf20Sopenharmony_ci (ave_rssi < BT_DISABLE_REDUCED_TXPOWER_THRESHOLD) || 6148c2ecf20Sopenharmony_ci (uart_msg->frame3 & (BT_UART_MSG_FRAME3SCOESCO_MSK | 6158c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3SNIFF_MSK | BT_UART_MSG_FRAME3A2DP_MSK)) || 6168c2ecf20Sopenharmony_ci !(uart_msg->frame3 & (BT_UART_MSG_FRAME3ACL_MSK | 6178c2ecf20Sopenharmony_ci BT_UART_MSG_FRAME3OBEX_MSK)))) { 6188c2ecf20Sopenharmony_ci /* disable reduced tx power */ 6198c2ecf20Sopenharmony_ci priv->reduced_txpower = false; 6208c2ecf20Sopenharmony_ci priv->bt_valid |= IWLAGN_BT_VALID_REDUCED_TX_PWR; 6218c2ecf20Sopenharmony_ci need_update = true; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return need_update; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void iwlagn_bt_coex_profile_notif(struct iwl_priv *priv, 6288c2ecf20Sopenharmony_ci struct iwl_rx_cmd_buffer *rxb) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct iwl_rx_packet *pkt = rxb_addr(rxb); 6318c2ecf20Sopenharmony_ci struct iwl_bt_coex_profile_notif *coex = (void *)pkt->data; 6328c2ecf20Sopenharmony_ci struct iwl_bt_uart_msg *uart_msg = &coex->last_bt_uart_msg; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (priv->bt_enable_flag == IWLAGN_BT_FLAG_COEX_MODE_DISABLED) { 6358c2ecf20Sopenharmony_ci /* bt coex disabled */ 6368c2ecf20Sopenharmony_ci return; 6378c2ecf20Sopenharmony_ci } 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, "BT Coex notification:\n"); 6408c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, " status: %d\n", coex->bt_status); 6418c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, " traffic load: %d\n", coex->bt_traffic_load); 6428c2ecf20Sopenharmony_ci IWL_DEBUG_COEX(priv, " CI compliance: %d\n", 6438c2ecf20Sopenharmony_ci coex->bt_ci_compliance); 6448c2ecf20Sopenharmony_ci iwlagn_print_uartmsg(priv, uart_msg); 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci priv->last_bt_traffic_load = priv->bt_traffic_load; 6478c2ecf20Sopenharmony_ci priv->bt_is_sco = iwlagn_bt_traffic_is_sco(uart_msg); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci if (priv->iw_mode != NL80211_IFTYPE_ADHOC) { 6508c2ecf20Sopenharmony_ci if (priv->bt_status != coex->bt_status || 6518c2ecf20Sopenharmony_ci priv->last_bt_traffic_load != coex->bt_traffic_load) { 6528c2ecf20Sopenharmony_ci if (coex->bt_status) { 6538c2ecf20Sopenharmony_ci /* BT on */ 6548c2ecf20Sopenharmony_ci if (!priv->bt_ch_announce) 6558c2ecf20Sopenharmony_ci priv->bt_traffic_load = 6568c2ecf20Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_HIGH; 6578c2ecf20Sopenharmony_ci else 6588c2ecf20Sopenharmony_ci priv->bt_traffic_load = 6598c2ecf20Sopenharmony_ci coex->bt_traffic_load; 6608c2ecf20Sopenharmony_ci } else { 6618c2ecf20Sopenharmony_ci /* BT off */ 6628c2ecf20Sopenharmony_ci priv->bt_traffic_load = 6638c2ecf20Sopenharmony_ci IWL_BT_COEX_TRAFFIC_LOAD_NONE; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci priv->bt_status = coex->bt_status; 6668c2ecf20Sopenharmony_ci queue_work(priv->workqueue, 6678c2ecf20Sopenharmony_ci &priv->bt_traffic_change_work); 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci } 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* schedule to send runtime bt_config */ 6728c2ecf20Sopenharmony_ci /* check reduce power before change ack/cts kill mask */ 6738c2ecf20Sopenharmony_ci if (iwlagn_fill_txpower_mode(priv, uart_msg) || 6748c2ecf20Sopenharmony_ci iwlagn_set_kill_msk(priv, uart_msg)) 6758c2ecf20Sopenharmony_ci queue_work(priv->workqueue, &priv->bt_runtime_config); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci /* FIXME: based on notification, adjust the prio_boost */ 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci priv->bt_ci_compliance = coex->bt_ci_compliance; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_civoid iwlagn_bt_rx_handler_setup(struct iwl_priv *priv) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci priv->rx_handlers[REPLY_BT_COEX_PROFILE_NOTIF] = 6868c2ecf20Sopenharmony_ci iwlagn_bt_coex_profile_notif; 6878c2ecf20Sopenharmony_ci} 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_civoid iwlagn_bt_setup_deferred_work(struct iwl_priv *priv) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci INIT_WORK(&priv->bt_traffic_change_work, 6928c2ecf20Sopenharmony_ci iwlagn_bt_traffic_change_work); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_civoid iwlagn_bt_cancel_deferred_work(struct iwl_priv *priv) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci cancel_work_sync(&priv->bt_traffic_change_work); 6988c2ecf20Sopenharmony_ci} 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic bool is_single_rx_stream(struct iwl_priv *priv) 7018c2ecf20Sopenharmony_ci{ 7028c2ecf20Sopenharmony_ci return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || 7038c2ecf20Sopenharmony_ci priv->current_ht_config.single_chain_sufficient; 7048c2ecf20Sopenharmony_ci} 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci#define IWL_NUM_RX_CHAINS_MULTIPLE 3 7078c2ecf20Sopenharmony_ci#define IWL_NUM_RX_CHAINS_SINGLE 2 7088c2ecf20Sopenharmony_ci#define IWL_NUM_IDLE_CHAINS_DUAL 2 7098c2ecf20Sopenharmony_ci#define IWL_NUM_IDLE_CHAINS_SINGLE 1 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci/* 7128c2ecf20Sopenharmony_ci * Determine how many receiver/antenna chains to use. 7138c2ecf20Sopenharmony_ci * 7148c2ecf20Sopenharmony_ci * More provides better reception via diversity. Fewer saves power 7158c2ecf20Sopenharmony_ci * at the expense of throughput, but only when not in powersave to 7168c2ecf20Sopenharmony_ci * start with. 7178c2ecf20Sopenharmony_ci * 7188c2ecf20Sopenharmony_ci * MIMO (dual stream) requires at least 2, but works better with 3. 7198c2ecf20Sopenharmony_ci * This does not determine *which* chains to use, just how many. 7208c2ecf20Sopenharmony_ci */ 7218c2ecf20Sopenharmony_cistatic int iwl_get_active_rx_chain_count(struct iwl_priv *priv) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci if (priv->lib->bt_params && 7248c2ecf20Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 7258c2ecf20Sopenharmony_ci (priv->bt_full_concurrent || 7268c2ecf20Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { 7278c2ecf20Sopenharmony_ci /* 7288c2ecf20Sopenharmony_ci * only use chain 'A' in bt high traffic load or 7298c2ecf20Sopenharmony_ci * full concurrency mode 7308c2ecf20Sopenharmony_ci */ 7318c2ecf20Sopenharmony_ci return IWL_NUM_RX_CHAINS_SINGLE; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci /* # of Rx chains to use when expecting MIMO. */ 7348c2ecf20Sopenharmony_ci if (is_single_rx_stream(priv)) 7358c2ecf20Sopenharmony_ci return IWL_NUM_RX_CHAINS_SINGLE; 7368c2ecf20Sopenharmony_ci else 7378c2ecf20Sopenharmony_ci return IWL_NUM_RX_CHAINS_MULTIPLE; 7388c2ecf20Sopenharmony_ci} 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci/* 7418c2ecf20Sopenharmony_ci * When we are in power saving mode, unless device support spatial 7428c2ecf20Sopenharmony_ci * multiplexing power save, use the active count for rx chain count. 7438c2ecf20Sopenharmony_ci */ 7448c2ecf20Sopenharmony_cistatic int iwl_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci /* # Rx chains when idling, depending on SMPS mode */ 7478c2ecf20Sopenharmony_ci switch (priv->current_ht_config.smps) { 7488c2ecf20Sopenharmony_ci case IEEE80211_SMPS_STATIC: 7498c2ecf20Sopenharmony_ci case IEEE80211_SMPS_DYNAMIC: 7508c2ecf20Sopenharmony_ci return IWL_NUM_IDLE_CHAINS_SINGLE; 7518c2ecf20Sopenharmony_ci case IEEE80211_SMPS_AUTOMATIC: 7528c2ecf20Sopenharmony_ci case IEEE80211_SMPS_OFF: 7538c2ecf20Sopenharmony_ci return active_cnt; 7548c2ecf20Sopenharmony_ci default: 7558c2ecf20Sopenharmony_ci WARN(1, "invalid SMPS mode %d", 7568c2ecf20Sopenharmony_ci priv->current_ht_config.smps); 7578c2ecf20Sopenharmony_ci return active_cnt; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci/* up to 4 chains */ 7628c2ecf20Sopenharmony_cistatic u8 iwl_count_chain_bitmap(u32 chain_bitmap) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci u8 res; 7658c2ecf20Sopenharmony_ci res = (chain_bitmap & BIT(0)) >> 0; 7668c2ecf20Sopenharmony_ci res += (chain_bitmap & BIT(1)) >> 1; 7678c2ecf20Sopenharmony_ci res += (chain_bitmap & BIT(2)) >> 2; 7688c2ecf20Sopenharmony_ci res += (chain_bitmap & BIT(3)) >> 3; 7698c2ecf20Sopenharmony_ci return res; 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci/* 7738c2ecf20Sopenharmony_ci * iwlagn_set_rxon_chain - Set up Rx chain usage in "staging" RXON image 7748c2ecf20Sopenharmony_ci * 7758c2ecf20Sopenharmony_ci * Selects how many and which Rx receivers/antennas/chains to use. 7768c2ecf20Sopenharmony_ci * This should not be used for scan command ... it puts data in wrong place. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_civoid iwlagn_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) 7798c2ecf20Sopenharmony_ci{ 7808c2ecf20Sopenharmony_ci bool is_single = is_single_rx_stream(priv); 7818c2ecf20Sopenharmony_ci bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); 7828c2ecf20Sopenharmony_ci u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; 7838c2ecf20Sopenharmony_ci u32 active_chains; 7848c2ecf20Sopenharmony_ci u16 rx_chain; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci /* Tell uCode which antennas are actually connected. 7878c2ecf20Sopenharmony_ci * Before first association, we assume all antennas are connected. 7888c2ecf20Sopenharmony_ci * Just after first association, iwl_chain_noise_calibration() 7898c2ecf20Sopenharmony_ci * checks which antennas actually *are* connected. */ 7908c2ecf20Sopenharmony_ci if (priv->chain_noise_data.active_chains) 7918c2ecf20Sopenharmony_ci active_chains = priv->chain_noise_data.active_chains; 7928c2ecf20Sopenharmony_ci else 7938c2ecf20Sopenharmony_ci active_chains = priv->nvm_data->valid_rx_ant; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (priv->lib->bt_params && 7968c2ecf20Sopenharmony_ci priv->lib->bt_params->advanced_bt_coexist && 7978c2ecf20Sopenharmony_ci (priv->bt_full_concurrent || 7988c2ecf20Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH)) { 7998c2ecf20Sopenharmony_ci /* 8008c2ecf20Sopenharmony_ci * only use chain 'A' in bt high traffic load or 8018c2ecf20Sopenharmony_ci * full concurrency mode 8028c2ecf20Sopenharmony_ci */ 8038c2ecf20Sopenharmony_ci active_chains = first_antenna(active_chains); 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* How many receivers should we use? */ 8098c2ecf20Sopenharmony_ci active_rx_cnt = iwl_get_active_rx_chain_count(priv); 8108c2ecf20Sopenharmony_ci idle_rx_cnt = iwl_get_idle_rx_chain_count(priv, active_rx_cnt); 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci /* correct rx chain count according hw settings 8148c2ecf20Sopenharmony_ci * and chain noise calibration 8158c2ecf20Sopenharmony_ci */ 8168c2ecf20Sopenharmony_ci valid_rx_cnt = iwl_count_chain_bitmap(active_chains); 8178c2ecf20Sopenharmony_ci if (valid_rx_cnt < active_rx_cnt) 8188c2ecf20Sopenharmony_ci active_rx_cnt = valid_rx_cnt; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci if (valid_rx_cnt < idle_rx_cnt) 8218c2ecf20Sopenharmony_ci idle_rx_cnt = valid_rx_cnt; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; 8248c2ecf20Sopenharmony_ci rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci ctx->staging.rx_chain = cpu_to_le16(rx_chain); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) 8298c2ecf20Sopenharmony_ci ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; 8308c2ecf20Sopenharmony_ci else 8318c2ecf20Sopenharmony_ci ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", 8348c2ecf20Sopenharmony_ci ctx->staging.rx_chain, 8358c2ecf20Sopenharmony_ci active_rx_cnt, idle_rx_cnt); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || 8388c2ecf20Sopenharmony_ci active_rx_cnt < idle_rx_cnt); 8398c2ecf20Sopenharmony_ci} 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ciu8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) 8428c2ecf20Sopenharmony_ci{ 8438c2ecf20Sopenharmony_ci int i; 8448c2ecf20Sopenharmony_ci u8 ind = ant; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (priv->band == NL80211_BAND_2GHZ && 8478c2ecf20Sopenharmony_ci priv->bt_traffic_load >= IWL_BT_COEX_TRAFFIC_LOAD_HIGH) 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci for (i = 0; i < RATE_ANT_NUM - 1; i++) { 8518c2ecf20Sopenharmony_ci ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; 8528c2ecf20Sopenharmony_ci if (valid & BIT(ind)) 8538c2ecf20Sopenharmony_ci return ind; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci return ant; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 8598c2ecf20Sopenharmony_cistatic void iwlagn_convert_p1k(u16 *p1k, __le16 *out) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci int i; 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_P1K_SIZE; i++) 8648c2ecf20Sopenharmony_ci out[i] = cpu_to_le16(p1k[i]); 8658c2ecf20Sopenharmony_ci} 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistruct wowlan_key_data { 8688c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx; 8698c2ecf20Sopenharmony_ci struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc; 8708c2ecf20Sopenharmony_ci struct iwlagn_wowlan_tkip_params_cmd *tkip; 8718c2ecf20Sopenharmony_ci const u8 *bssid; 8728c2ecf20Sopenharmony_ci bool error, use_rsc_tsc, use_tkip; 8738c2ecf20Sopenharmony_ci}; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw, 8778c2ecf20Sopenharmony_ci struct ieee80211_vif *vif, 8788c2ecf20Sopenharmony_ci struct ieee80211_sta *sta, 8798c2ecf20Sopenharmony_ci struct ieee80211_key_conf *key, 8808c2ecf20Sopenharmony_ci void *_data) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); 8838c2ecf20Sopenharmony_ci struct wowlan_key_data *data = _data; 8848c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = data->ctx; 8858c2ecf20Sopenharmony_ci struct aes_sc *aes_sc, *aes_tx_sc = NULL; 8868c2ecf20Sopenharmony_ci struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL; 8878c2ecf20Sopenharmony_ci struct iwlagn_p1k_cache *rx_p1ks; 8888c2ecf20Sopenharmony_ci u8 *rx_mic_key; 8898c2ecf20Sopenharmony_ci struct ieee80211_key_seq seq; 8908c2ecf20Sopenharmony_ci u32 cur_rx_iv32 = 0; 8918c2ecf20Sopenharmony_ci u16 p1k[IWLAGN_P1K_SIZE]; 8928c2ecf20Sopenharmony_ci int ret, i; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci mutex_lock(&priv->mutex); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || 8978c2ecf20Sopenharmony_ci key->cipher == WLAN_CIPHER_SUITE_WEP104) && 8988c2ecf20Sopenharmony_ci !sta && !ctx->key_mapping_keys) 8998c2ecf20Sopenharmony_ci ret = iwl_set_default_wep_key(priv, ctx, key); 9008c2ecf20Sopenharmony_ci else 9018c2ecf20Sopenharmony_ci ret = iwl_set_dynamic_key(priv, ctx, key, sta); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (ret) { 9048c2ecf20Sopenharmony_ci IWL_ERR(priv, "Error setting key during suspend!\n"); 9058c2ecf20Sopenharmony_ci data->error = true; 9068c2ecf20Sopenharmony_ci } 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci switch (key->cipher) { 9098c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 9108c2ecf20Sopenharmony_ci if (sta) { 9118c2ecf20Sopenharmony_ci u64 pn64; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc; 9148c2ecf20Sopenharmony_ci tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc; 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci rx_p1ks = data->tkip->rx_uni; 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci pn64 = atomic64_read(&key->tx_pn); 9198c2ecf20Sopenharmony_ci tkip_tx_sc->iv16 = cpu_to_le16(TKIP_PN_TO_IV16(pn64)); 9208c2ecf20Sopenharmony_ci tkip_tx_sc->iv32 = cpu_to_le32(TKIP_PN_TO_IV32(pn64)); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k); 9238c2ecf20Sopenharmony_ci iwlagn_convert_p1k(p1k, data->tkip->tx.p1k); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci memcpy(data->tkip->mic_keys.tx, 9268c2ecf20Sopenharmony_ci &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY], 9278c2ecf20Sopenharmony_ci IWLAGN_MIC_KEY_SIZE); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci rx_mic_key = data->tkip->mic_keys.rx_unicast; 9308c2ecf20Sopenharmony_ci } else { 9318c2ecf20Sopenharmony_ci tkip_sc = 9328c2ecf20Sopenharmony_ci data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc; 9338c2ecf20Sopenharmony_ci rx_p1ks = data->tkip->rx_multi; 9348c2ecf20Sopenharmony_ci rx_mic_key = data->tkip->mic_keys.rx_mcast; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci /* 9388c2ecf20Sopenharmony_ci * For non-QoS this relies on the fact that both the uCode and 9398c2ecf20Sopenharmony_ci * mac80211 use TID 0 (as they need to to avoid replay attacks) 9408c2ecf20Sopenharmony_ci * for checking the IV in the frames. 9418c2ecf20Sopenharmony_ci */ 9428c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_NUM_RSC; i++) { 9438c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(key, i, &seq); 9448c2ecf20Sopenharmony_ci tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16); 9458c2ecf20Sopenharmony_ci tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32); 9468c2ecf20Sopenharmony_ci /* wrapping isn't allowed, AP must rekey */ 9478c2ecf20Sopenharmony_ci if (seq.tkip.iv32 > cur_rx_iv32) 9488c2ecf20Sopenharmony_ci cur_rx_iv32 = seq.tkip.iv32; 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k); 9528c2ecf20Sopenharmony_ci iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k); 9538c2ecf20Sopenharmony_ci ieee80211_get_tkip_rx_p1k(key, data->bssid, 9548c2ecf20Sopenharmony_ci cur_rx_iv32 + 1, p1k); 9558c2ecf20Sopenharmony_ci iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k); 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci memcpy(rx_mic_key, 9588c2ecf20Sopenharmony_ci &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY], 9598c2ecf20Sopenharmony_ci IWLAGN_MIC_KEY_SIZE); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci data->use_tkip = true; 9628c2ecf20Sopenharmony_ci data->use_rsc_tsc = true; 9638c2ecf20Sopenharmony_ci break; 9648c2ecf20Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 9658c2ecf20Sopenharmony_ci if (sta) { 9668c2ecf20Sopenharmony_ci u64 pn64; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc; 9698c2ecf20Sopenharmony_ci aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci pn64 = atomic64_read(&key->tx_pn); 9728c2ecf20Sopenharmony_ci aes_tx_sc->pn = cpu_to_le64(pn64); 9738c2ecf20Sopenharmony_ci } else 9748c2ecf20Sopenharmony_ci aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* 9778c2ecf20Sopenharmony_ci * For non-QoS this relies on the fact that both the uCode and 9788c2ecf20Sopenharmony_ci * mac80211 use TID 0 for checking the IV in the frames. 9798c2ecf20Sopenharmony_ci */ 9808c2ecf20Sopenharmony_ci for (i = 0; i < IWLAGN_NUM_RSC; i++) { 9818c2ecf20Sopenharmony_ci u8 *pn = seq.ccmp.pn; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci ieee80211_get_key_rx_seq(key, i, &seq); 9848c2ecf20Sopenharmony_ci aes_sc[i].pn = cpu_to_le64( 9858c2ecf20Sopenharmony_ci (u64)pn[5] | 9868c2ecf20Sopenharmony_ci ((u64)pn[4] << 8) | 9878c2ecf20Sopenharmony_ci ((u64)pn[3] << 16) | 9888c2ecf20Sopenharmony_ci ((u64)pn[2] << 24) | 9898c2ecf20Sopenharmony_ci ((u64)pn[1] << 32) | 9908c2ecf20Sopenharmony_ci ((u64)pn[0] << 40)); 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci data->use_rsc_tsc = true; 9938c2ecf20Sopenharmony_ci break; 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci mutex_unlock(&priv->mutex); 9978c2ecf20Sopenharmony_ci} 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ciint iwlagn_send_patterns(struct iwl_priv *priv, 10008c2ecf20Sopenharmony_ci struct cfg80211_wowlan *wowlan) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci struct iwlagn_wowlan_patterns_cmd *pattern_cmd; 10038c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 10048c2ecf20Sopenharmony_ci .id = REPLY_WOWLAN_PATTERNS, 10058c2ecf20Sopenharmony_ci .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 10068c2ecf20Sopenharmony_ci }; 10078c2ecf20Sopenharmony_ci int i, err; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci if (!wowlan->n_patterns) 10108c2ecf20Sopenharmony_ci return 0; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL); 10158c2ecf20Sopenharmony_ci if (!pattern_cmd) 10168c2ecf20Sopenharmony_ci return -ENOMEM; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci for (i = 0; i < wowlan->n_patterns; i++) { 10218c2ecf20Sopenharmony_ci int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci memcpy(&pattern_cmd->patterns[i].mask, 10248c2ecf20Sopenharmony_ci wowlan->patterns[i].mask, mask_len); 10258c2ecf20Sopenharmony_ci memcpy(&pattern_cmd->patterns[i].pattern, 10268c2ecf20Sopenharmony_ci wowlan->patterns[i].pattern, 10278c2ecf20Sopenharmony_ci wowlan->patterns[i].pattern_len); 10288c2ecf20Sopenharmony_ci pattern_cmd->patterns[i].mask_size = mask_len; 10298c2ecf20Sopenharmony_ci pattern_cmd->patterns[i].pattern_size = 10308c2ecf20Sopenharmony_ci wowlan->patterns[i].pattern_len; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci cmd.data[0] = pattern_cmd; 10348c2ecf20Sopenharmony_ci err = iwl_dvm_send_cmd(priv, &cmd); 10358c2ecf20Sopenharmony_ci kfree(pattern_cmd); 10368c2ecf20Sopenharmony_ci return err; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ciint iwlagn_suspend(struct iwl_priv *priv, struct cfg80211_wowlan *wowlan) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd; 10428c2ecf20Sopenharmony_ci struct iwl_rxon_cmd rxon; 10438c2ecf20Sopenharmony_ci struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; 10448c2ecf20Sopenharmony_ci struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd; 10458c2ecf20Sopenharmony_ci struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {}; 10468c2ecf20Sopenharmony_ci struct iwlagn_d3_config_cmd d3_cfg_cmd = { 10478c2ecf20Sopenharmony_ci /* 10488c2ecf20Sopenharmony_ci * Program the minimum sleep time to 10 seconds, as many 10498c2ecf20Sopenharmony_ci * platforms have issues processing a wakeup signal while 10508c2ecf20Sopenharmony_ci * still being in the process of suspending. 10518c2ecf20Sopenharmony_ci */ 10528c2ecf20Sopenharmony_ci .min_sleep_time = cpu_to_le32(10 * 1000 * 1000), 10538c2ecf20Sopenharmony_ci }; 10548c2ecf20Sopenharmony_ci struct wowlan_key_data key_data = { 10558c2ecf20Sopenharmony_ci .ctx = ctx, 10568c2ecf20Sopenharmony_ci .bssid = ctx->active.bssid_addr, 10578c2ecf20Sopenharmony_ci .use_rsc_tsc = false, 10588c2ecf20Sopenharmony_ci .tkip = &tkip_cmd, 10598c2ecf20Sopenharmony_ci .use_tkip = false, 10608c2ecf20Sopenharmony_ci }; 10618c2ecf20Sopenharmony_ci int ret, i; 10628c2ecf20Sopenharmony_ci u16 seq; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL); 10658c2ecf20Sopenharmony_ci if (!key_data.rsc_tsc) 10668c2ecf20Sopenharmony_ci return -ENOMEM; 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd)); 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* 10718c2ecf20Sopenharmony_ci * We know the last used seqno, and the uCode expects to know that 10728c2ecf20Sopenharmony_ci * one, it will increment before TX. 10738c2ecf20Sopenharmony_ci */ 10748c2ecf20Sopenharmony_ci seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ; 10758c2ecf20Sopenharmony_ci wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci /* 10788c2ecf20Sopenharmony_ci * For QoS counters, we store the one to use next, so subtract 0x10 10798c2ecf20Sopenharmony_ci * since the uCode will add 0x10 before using the value. 10808c2ecf20Sopenharmony_ci */ 10818c2ecf20Sopenharmony_ci for (i = 0; i < IWL_MAX_TID_COUNT; i++) { 10828c2ecf20Sopenharmony_ci seq = priv->tid_data[IWL_AP_ID][i].seq_number; 10838c2ecf20Sopenharmony_ci seq -= 0x10; 10848c2ecf20Sopenharmony_ci wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq); 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci if (wowlan->disconnect) 10888c2ecf20Sopenharmony_ci wakeup_filter_cmd.enabled |= 10898c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS | 10908c2ecf20Sopenharmony_ci IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE); 10918c2ecf20Sopenharmony_ci if (wowlan->magic_pkt) 10928c2ecf20Sopenharmony_ci wakeup_filter_cmd.enabled |= 10938c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET); 10948c2ecf20Sopenharmony_ci if (wowlan->gtk_rekey_failure) 10958c2ecf20Sopenharmony_ci wakeup_filter_cmd.enabled |= 10968c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL); 10978c2ecf20Sopenharmony_ci if (wowlan->eap_identity_req) 10988c2ecf20Sopenharmony_ci wakeup_filter_cmd.enabled |= 10998c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ); 11008c2ecf20Sopenharmony_ci if (wowlan->four_way_handshake) 11018c2ecf20Sopenharmony_ci wakeup_filter_cmd.enabled |= 11028c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE); 11038c2ecf20Sopenharmony_ci if (wowlan->n_patterns) 11048c2ecf20Sopenharmony_ci wakeup_filter_cmd.enabled |= 11058c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH); 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci if (wowlan->rfkill_release) 11088c2ecf20Sopenharmony_ci d3_cfg_cmd.wakeup_flags |= 11098c2ecf20Sopenharmony_ci cpu_to_le32(IWLAGN_D3_WAKEUP_RFKILL); 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci iwl_scan_cancel_timeout(priv, 200); 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci memcpy(&rxon, &ctx->active, sizeof(rxon)); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci priv->ucode_loaded = false; 11168c2ecf20Sopenharmony_ci iwl_trans_stop_device(priv->trans); 11178c2ecf20Sopenharmony_ci ret = iwl_trans_start_hw(priv->trans); 11188c2ecf20Sopenharmony_ci if (ret) 11198c2ecf20Sopenharmony_ci goto out; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci priv->wowlan = true; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci ret = iwl_load_ucode_wait_alive(priv, IWL_UCODE_WOWLAN); 11248c2ecf20Sopenharmony_ci if (ret) 11258c2ecf20Sopenharmony_ci goto out; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* now configure WoWLAN ucode */ 11288c2ecf20Sopenharmony_ci ret = iwl_alive_start(priv); 11298c2ecf20Sopenharmony_ci if (ret) 11308c2ecf20Sopenharmony_ci goto out; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci memcpy(&ctx->staging, &rxon, sizeof(rxon)); 11338c2ecf20Sopenharmony_ci ret = iwlagn_commit_rxon(priv, ctx); 11348c2ecf20Sopenharmony_ci if (ret) 11358c2ecf20Sopenharmony_ci goto out; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci ret = iwl_power_update_mode(priv, true); 11388c2ecf20Sopenharmony_ci if (ret) 11398c2ecf20Sopenharmony_ci goto out; 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci if (!iwlwifi_mod_params.swcrypto) { 11428c2ecf20Sopenharmony_ci /* mark all keys clear */ 11438c2ecf20Sopenharmony_ci priv->ucode_key_table = 0; 11448c2ecf20Sopenharmony_ci ctx->key_mapping_keys = 0; 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* 11478c2ecf20Sopenharmony_ci * This needs to be unlocked due to lock ordering 11488c2ecf20Sopenharmony_ci * constraints. Since we're in the suspend path 11498c2ecf20Sopenharmony_ci * that isn't really a problem though. 11508c2ecf20Sopenharmony_ci */ 11518c2ecf20Sopenharmony_ci mutex_unlock(&priv->mutex); 11528c2ecf20Sopenharmony_ci ieee80211_iter_keys(priv->hw, ctx->vif, 11538c2ecf20Sopenharmony_ci iwlagn_wowlan_program_keys, 11548c2ecf20Sopenharmony_ci &key_data); 11558c2ecf20Sopenharmony_ci mutex_lock(&priv->mutex); 11568c2ecf20Sopenharmony_ci if (key_data.error) { 11578c2ecf20Sopenharmony_ci ret = -EIO; 11588c2ecf20Sopenharmony_ci goto out; 11598c2ecf20Sopenharmony_ci } 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (key_data.use_rsc_tsc) { 11628c2ecf20Sopenharmony_ci struct iwl_host_cmd rsc_tsc_cmd = { 11638c2ecf20Sopenharmony_ci .id = REPLY_WOWLAN_TSC_RSC_PARAMS, 11648c2ecf20Sopenharmony_ci .data[0] = key_data.rsc_tsc, 11658c2ecf20Sopenharmony_ci .dataflags[0] = IWL_HCMD_DFL_NOCOPY, 11668c2ecf20Sopenharmony_ci .len[0] = sizeof(*key_data.rsc_tsc), 11678c2ecf20Sopenharmony_ci }; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd(priv, &rsc_tsc_cmd); 11708c2ecf20Sopenharmony_ci if (ret) 11718c2ecf20Sopenharmony_ci goto out; 11728c2ecf20Sopenharmony_ci } 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (key_data.use_tkip) { 11758c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, 11768c2ecf20Sopenharmony_ci REPLY_WOWLAN_TKIP_PARAMS, 11778c2ecf20Sopenharmony_ci 0, sizeof(tkip_cmd), 11788c2ecf20Sopenharmony_ci &tkip_cmd); 11798c2ecf20Sopenharmony_ci if (ret) 11808c2ecf20Sopenharmony_ci goto out; 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci if (priv->have_rekey_data) { 11848c2ecf20Sopenharmony_ci memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd)); 11858c2ecf20Sopenharmony_ci memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN); 11868c2ecf20Sopenharmony_ci kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN); 11878c2ecf20Sopenharmony_ci memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN); 11888c2ecf20Sopenharmony_ci kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN); 11898c2ecf20Sopenharmony_ci kek_kck_cmd.replay_ctr = priv->replay_ctr; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, 11928c2ecf20Sopenharmony_ci REPLY_WOWLAN_KEK_KCK_MATERIAL, 11938c2ecf20Sopenharmony_ci 0, sizeof(kek_kck_cmd), 11948c2ecf20Sopenharmony_ci &kek_kck_cmd); 11958c2ecf20Sopenharmony_ci if (ret) 11968c2ecf20Sopenharmony_ci goto out; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_D3_CONFIG, 0, 12018c2ecf20Sopenharmony_ci sizeof(d3_cfg_cmd), &d3_cfg_cmd); 12028c2ecf20Sopenharmony_ci if (ret) 12038c2ecf20Sopenharmony_ci goto out; 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci ret = iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_WAKEUP_FILTER, 12068c2ecf20Sopenharmony_ci 0, sizeof(wakeup_filter_cmd), 12078c2ecf20Sopenharmony_ci &wakeup_filter_cmd); 12088c2ecf20Sopenharmony_ci if (ret) 12098c2ecf20Sopenharmony_ci goto out; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci ret = iwlagn_send_patterns(priv, wowlan); 12128c2ecf20Sopenharmony_ci out: 12138c2ecf20Sopenharmony_ci kfree(key_data.rsc_tsc); 12148c2ecf20Sopenharmony_ci return ret; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci#endif 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ciint iwl_dvm_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) 12198c2ecf20Sopenharmony_ci{ 12208c2ecf20Sopenharmony_ci if (iwl_is_rfkill(priv) || iwl_is_ctkill(priv)) { 12218c2ecf20Sopenharmony_ci IWL_WARN(priv, "Not sending command - %s KILL\n", 12228c2ecf20Sopenharmony_ci iwl_is_rfkill(priv) ? "RF" : "CT"); 12238c2ecf20Sopenharmony_ci return -EIO; 12248c2ecf20Sopenharmony_ci } 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci if (test_bit(STATUS_FW_ERROR, &priv->status)) { 12278c2ecf20Sopenharmony_ci IWL_ERR(priv, "Command %s failed: FW Error\n", 12288c2ecf20Sopenharmony_ci iwl_get_cmd_string(priv->trans, cmd->id)); 12298c2ecf20Sopenharmony_ci return -EIO; 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* 12338c2ecf20Sopenharmony_ci * This can happen upon FW ASSERT: we clear the STATUS_FW_ERROR flag 12348c2ecf20Sopenharmony_ci * in iwl_down but cancel the workers only later. 12358c2ecf20Sopenharmony_ci */ 12368c2ecf20Sopenharmony_ci if (!priv->ucode_loaded) { 12378c2ecf20Sopenharmony_ci IWL_ERR(priv, "Fw not loaded - dropping CMD: %x\n", cmd->id); 12388c2ecf20Sopenharmony_ci return -EIO; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* 12428c2ecf20Sopenharmony_ci * Synchronous commands from this op-mode must hold 12438c2ecf20Sopenharmony_ci * the mutex, this ensures we don't try to send two 12448c2ecf20Sopenharmony_ci * (or more) synchronous commands at a time. 12458c2ecf20Sopenharmony_ci */ 12468c2ecf20Sopenharmony_ci if (!(cmd->flags & CMD_ASYNC)) 12478c2ecf20Sopenharmony_ci lockdep_assert_held(&priv->mutex); 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci return iwl_trans_send_cmd(priv->trans, cmd); 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ciint iwl_dvm_send_cmd_pdu(struct iwl_priv *priv, u8 id, 12538c2ecf20Sopenharmony_ci u32 flags, u16 len, const void *data) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct iwl_host_cmd cmd = { 12568c2ecf20Sopenharmony_ci .id = id, 12578c2ecf20Sopenharmony_ci .len = { len, }, 12588c2ecf20Sopenharmony_ci .data = { data, }, 12598c2ecf20Sopenharmony_ci .flags = flags, 12608c2ecf20Sopenharmony_ci }; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci return iwl_dvm_send_cmd(priv, &cmd); 12638c2ecf20Sopenharmony_ci} 1264