Path: blob/main/sys/contrib/dev/iwlwifi/mld/mld.c
107074 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include <linux/rtnetlink.h>5#include <net/mac80211.h>67#include "fw/api/rx.h"8#include "fw/api/datapath.h"9#include "fw/api/commands.h"10#include "fw/api/offload.h"11#include "fw/api/coex.h"12#include "fw/dbg.h"13#include "fw/uefi.h"1415#include "mld.h"16#include "mlo.h"17#include "mac80211.h"18#include "led.h"19#include "scan.h"20#include "tx.h"21#include "sta.h"22#include "regulatory.h"23#include "thermal.h"24#include "low_latency.h"25#include "hcmd.h"26#include "fw/api/location.h"2728#include "iwl-nvm-parse.h"2930#define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux"31MODULE_DESCRIPTION(DRV_DESCRIPTION);32MODULE_LICENSE("GPL");33MODULE_IMPORT_NS("IWLWIFI");3435static const struct iwl_op_mode_ops iwl_mld_ops;3637static int __init iwl_mld_init(void)38{39int ret = iwl_opmode_register("iwlmld", &iwl_mld_ops);4041if (ret)42pr_err("Unable to register MLD op_mode: %d\n", ret);4344return ret;45}46module_init(iwl_mld_init);4748static void __exit iwl_mld_exit(void)49{50iwl_opmode_deregister("iwlmld");51}52module_exit(iwl_mld_exit);5354static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld)55{56struct wiphy *wiphy = mld->wiphy;5758wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED;59wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR;60}6162VISIBLE_IF_IWLWIFI_KUNIT63void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans,64const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw,65struct ieee80211_hw *hw, struct dentry *dbgfs_dir)66{67mld->dev = trans->dev;68mld->trans = trans;69mld->cfg = cfg;70mld->fw = fw;71mld->hw = hw;72mld->wiphy = hw->wiphy;73mld->debugfs_dir = dbgfs_dir;7475iwl_notification_wait_init(&mld->notif_wait);7677/* Setup async RX handling */78spin_lock_init(&mld->async_handlers_lock);79INIT_LIST_HEAD(&mld->async_handlers_list);80wiphy_work_init(&mld->async_handlers_wk,81iwl_mld_async_handlers_wk);8283/* Dynamic Queue Allocation */84spin_lock_init(&mld->add_txqs_lock);85INIT_LIST_HEAD(&mld->txqs_to_add);86wiphy_work_init(&mld->add_txqs_wk, iwl_mld_add_txqs_wk);8788/* Setup RX queues sync wait queue */89init_waitqueue_head(&mld->rxq_sync.waitq);90}91EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_construct_mld);9293static void __acquires(&mld->wiphy->mtx)94iwl_mld_fwrt_dump_start(void *ctx)95{96struct iwl_mld *mld = ctx;9798wiphy_lock(mld->wiphy);99}100101static void __releases(&mld->wiphy->mtx)102iwl_mld_fwrt_dump_end(void *ctx)103{104struct iwl_mld *mld = ctx;105106wiphy_unlock(mld->wiphy);107}108109static bool iwl_mld_d3_debug_enable(void *ctx)110{111return IWL_MLD_D3_DEBUG;112}113114static int iwl_mld_fwrt_send_hcmd(void *ctx, struct iwl_host_cmd *host_cmd)115{116struct iwl_mld *mld = (struct iwl_mld *)ctx;117int ret;118119wiphy_lock(mld->wiphy);120ret = iwl_mld_send_cmd(mld, host_cmd);121wiphy_unlock(mld->wiphy);122123return ret;124}125126static const struct iwl_fw_runtime_ops iwl_mld_fwrt_ops = {127.dump_start = iwl_mld_fwrt_dump_start,128.dump_end = iwl_mld_fwrt_dump_end,129.send_hcmd = iwl_mld_fwrt_send_hcmd,130.d3_debug_enable = iwl_mld_d3_debug_enable,131};132133static void134iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans,135const struct iwl_fw *fw,136struct dentry *debugfs_dir)137{138iwl_fw_runtime_init(&mld->fwrt, trans, fw, &iwl_mld_fwrt_ops, mld,139NULL, NULL, debugfs_dir);140141iwl_fw_set_current_image(&mld->fwrt, IWL_UCODE_REGULAR);142}143144/* Please keep this array *SORTED* by hex value.145* Access is done through binary search146*/147static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {148HCMD_NAME(UCODE_ALIVE_NTFY),149HCMD_NAME(INIT_COMPLETE_NOTIF),150HCMD_NAME(PHY_CONTEXT_CMD),151HCMD_NAME(SCAN_CFG_CMD),152HCMD_NAME(SCAN_REQ_UMAC),153HCMD_NAME(SCAN_ABORT_UMAC),154HCMD_NAME(SCAN_COMPLETE_UMAC),155HCMD_NAME(TX_CMD),156HCMD_NAME(TXPATH_FLUSH),157HCMD_NAME(LEDS_CMD),158HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION),159HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION),160HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD),161HCMD_NAME(POWER_TABLE_CMD),162HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION),163HCMD_NAME(BEACON_NOTIFICATION),164HCMD_NAME(BEACON_TEMPLATE_CMD),165HCMD_NAME(TX_ANT_CONFIGURATION_CMD),166HCMD_NAME(REDUCE_TX_POWER_CMD),167HCMD_NAME(MISSED_BEACONS_NOTIFICATION),168HCMD_NAME(MAC_PM_POWER_TABLE),169HCMD_NAME(MFUART_LOAD_NOTIFICATION),170HCMD_NAME(RSS_CONFIG_CMD),171HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC),172HCMD_NAME(REPLY_RX_MPDU_CMD),173HCMD_NAME(BA_NOTIF),174HCMD_NAME(MCC_UPDATE_CMD),175HCMD_NAME(MCC_CHUB_UPDATE_CMD),176HCMD_NAME(MCAST_FILTER_CMD),177HCMD_NAME(REPLY_BEACON_FILTERING_CMD),178HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD),179HCMD_NAME(MATCH_FOUND_NOTIFICATION),180HCMD_NAME(WOWLAN_PATTERNS),181HCMD_NAME(WOWLAN_CONFIGURATION),182HCMD_NAME(WOWLAN_TSC_RSC_PARAM),183HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL),184HCMD_NAME(DEBUG_HOST_COMMAND),185HCMD_NAME(LDBG_CONFIG_CMD),186};187188/* Please keep this array *SORTED* by hex value.189* Access is done through binary search190*/191static const struct iwl_hcmd_names iwl_mld_system_names[] = {192HCMD_NAME(SHARED_MEM_CFG_CMD),193HCMD_NAME(SOC_CONFIGURATION_CMD),194HCMD_NAME(INIT_EXTENDED_CFG_CMD),195HCMD_NAME(FW_ERROR_RECOVERY_CMD),196HCMD_NAME(RFI_CONFIG_CMD),197HCMD_NAME(RFI_GET_FREQ_TABLE_CMD),198HCMD_NAME(SYSTEM_STATISTICS_CMD),199HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF),200};201202/* Please keep this array *SORTED* by hex value.203* Access is done through binary search204*/205static const struct iwl_hcmd_names iwl_mld_reg_and_nvm_names[] = {206HCMD_NAME(LARI_CONFIG_CHANGE),207HCMD_NAME(NVM_GET_INFO),208HCMD_NAME(TAS_CONFIG),209HCMD_NAME(SAR_OFFSET_MAPPING_TABLE_CMD),210HCMD_NAME(MCC_ALLOWED_AP_TYPE_CMD),211};212213/* Please keep this array *SORTED* by hex value.214* Access is done through binary search215*/216static const struct iwl_hcmd_names iwl_mld_debug_names[] = {217HCMD_NAME(HOST_EVENT_CFG),218HCMD_NAME(DBGC_SUSPEND_RESUME),219};220221/* Please keep this array *SORTED* by hex value.222* Access is done through binary search223*/224static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = {225HCMD_NAME(LOW_LATENCY_CMD),226HCMD_NAME(SESSION_PROTECTION_CMD),227HCMD_NAME(MAC_CONFIG_CMD),228HCMD_NAME(LINK_CONFIG_CMD),229HCMD_NAME(STA_CONFIG_CMD),230HCMD_NAME(AUX_STA_CMD),231HCMD_NAME(STA_REMOVE_CMD),232HCMD_NAME(ROC_CMD),233HCMD_NAME(MISSED_BEACONS_NOTIF),234HCMD_NAME(EMLSR_TRANS_FAIL_NOTIF),235HCMD_NAME(ROC_NOTIF),236HCMD_NAME(CHANNEL_SWITCH_ERROR_NOTIF),237HCMD_NAME(SESSION_PROTECTION_NOTIF),238HCMD_NAME(PROBE_RESPONSE_DATA_NOTIF),239HCMD_NAME(CHANNEL_SWITCH_START_NOTIF),240};241242/* Please keep this array *SORTED* by hex value.243* Access is done through binary search244*/245static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {246HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD),247HCMD_NAME(WNM_PLATFORM_PTM_REQUEST_CMD),248HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD),249HCMD_NAME(RFH_QUEUE_CONFIG_CMD),250HCMD_NAME(TLC_MNG_CONFIG_CMD),251HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD),252HCMD_NAME(SCD_QUEUE_CONFIG_CMD),253HCMD_NAME(ESR_MODE_NOTIF),254HCMD_NAME(MONITOR_NOTIF),255HCMD_NAME(TLC_MNG_UPDATE_NOTIF),256HCMD_NAME(BEACON_FILTER_IN_NOTIF),257HCMD_NAME(MU_GROUP_MGMT_NOTIF),258};259260/* Please keep this array *SORTED* by hex value.261* Access is done through binary search262*/263static const struct iwl_hcmd_names iwl_mld_scan_names[] = {264HCMD_NAME(CHANNEL_SURVEY_NOTIF),265};266267/* Please keep this array *SORTED* by hex value.268* Access is done through binary search269*/270static const struct iwl_hcmd_names iwl_mld_location_names[] = {271HCMD_NAME(TOF_RANGE_REQ_CMD),272HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF),273};274275/* Please keep this array *SORTED* by hex value.276* Access is done through binary search277*/278static const struct iwl_hcmd_names iwl_mld_phy_names[] = {279HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE),280HCMD_NAME(CTDP_CONFIG_CMD),281HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD),282HCMD_NAME(PER_CHAIN_LIMIT_OFFSET_CMD),283HCMD_NAME(CT_KILL_NOTIFICATION),284HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),285};286287/* Please keep this array *SORTED* by hex value.288* Access is done through binary search289*/290static const struct iwl_hcmd_names iwl_mld_statistics_names[] = {291HCMD_NAME(STATISTICS_OPER_NOTIF),292HCMD_NAME(STATISTICS_OPER_PART1_NOTIF),293};294295/* Please keep this array *SORTED* by hex value.296* Access is done through binary search297*/298static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = {299HCMD_NAME(WOWLAN_WAKE_PKT_NOTIFICATION),300HCMD_NAME(WOWLAN_INFO_NOTIFICATION),301HCMD_NAME(D3_END_NOTIFICATION),302};303304/* Please keep this array *SORTED* by hex value.305* Access is done through binary search306*/307static const struct iwl_hcmd_names iwl_mld_coex_names[] = {308HCMD_NAME(PROFILE_NOTIF),309};310311VISIBLE_IF_IWLWIFI_KUNIT312const struct iwl_hcmd_arr iwl_mld_groups[] = {313[LEGACY_GROUP] = HCMD_ARR(iwl_mld_legacy_names),314[LONG_GROUP] = HCMD_ARR(iwl_mld_legacy_names),315[SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names),316[MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names),317[DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names),318[SCAN_GROUP] = HCMD_ARR(iwl_mld_scan_names),319[LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names),320[REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names),321[DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names),322[PHY_OPS_GROUP] = HCMD_ARR(iwl_mld_phy_names),323[STATISTICS_GROUP] = HCMD_ARR(iwl_mld_statistics_names),324[PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mld_prot_offload_names),325[BT_COEX_GROUP] = HCMD_ARR(iwl_mld_coex_names),326};327EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_groups);328329#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)330const unsigned int global_iwl_mld_goups_size = ARRAY_SIZE(iwl_mld_groups);331EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size);332#endif333334static void335iwl_mld_configure_trans(struct iwl_op_mode *op_mode)336{337struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);338static const u8 no_reclaim_cmds[] = {TX_CMD};339struct iwl_trans *trans = mld->trans;340u32 eckv_value;341342iwl_bios_setup_step(trans, &mld->fwrt);343iwl_uefi_get_step_table(trans);344345if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value))346IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n");347else348trans->conf.ext_32khz_clock_valid = !!eckv_value;349350trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size();351trans->conf.command_groups = iwl_mld_groups;352trans->conf.command_groups_size = ARRAY_SIZE(iwl_mld_groups);353trans->conf.fw_reset_handshake = true;354trans->conf.queue_alloc_cmd_ver =355iwl_fw_lookup_cmd_ver(mld->fw, WIDE_ID(DATA_PATH_GROUP,356SCD_QUEUE_CONFIG_CMD),3570);358trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info,359driver_data[2]);360BUILD_BUG_ON(sizeof(no_reclaim_cmds) >361sizeof(trans->conf.no_reclaim_cmds));362memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds,363sizeof(no_reclaim_cmds));364trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds);365366trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD;367trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_desc);368trans->conf.wide_cmd_header = true;369370iwl_trans_op_mode_enter(trans, op_mode);371}372373/*374*****************************************************375* op mode ops functions376*****************************************************377*/378379#define NUM_FW_LOAD_RETRIES 3380static struct iwl_op_mode *381iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,382const struct iwl_fw *fw, struct dentry *dbgfs_dir)383{384struct ieee80211_hw *hw;385struct iwl_op_mode *op_mode;386struct iwl_mld *mld;387int ret;388389/* Allocate and initialize a new hardware device */390hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) +391sizeof(struct iwl_mld),392&iwl_mld_hw_ops);393if (!hw)394return ERR_PTR(-ENOMEM);395396op_mode = hw->priv;397398op_mode->ops = &iwl_mld_ops;399400mld = IWL_OP_MODE_GET_MLD(op_mode);401402iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir);403404/* we'll verify later it matches between commands */405mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11;406407iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir);408409iwl_mld_get_bios_tables(mld);410iwl_uefi_get_sgom_table(trans, &mld->fwrt);411mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt);412413iwl_mld_hw_set_regulatory(mld);414415/* Configure transport layer with the opmode specific params */416iwl_mld_configure_trans(op_mode);417418/* needed for regulatory init */419rtnl_lock();420/* Needed for sending commands */421wiphy_lock(mld->wiphy);422423for (int i = 0; i < NUM_FW_LOAD_RETRIES; i++) {424ret = iwl_mld_load_fw(mld);425if (!ret)426break;427}428429if (!ret) {430mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0);431if (IS_ERR(mld->nvm_data)) {432IWL_ERR(mld, "Failed to read NVM: %d\n", ret);433ret = PTR_ERR(mld->nvm_data);434}435}436437if (ret) {438wiphy_unlock(mld->wiphy);439rtnl_unlock();440goto err;441}442443/* We are about to stop the FW. Notifications may require an444* operational FW, so handle them all here before we stop.445*/446wiphy_work_flush(mld->wiphy, &mld->async_handlers_wk);447448iwl_mld_stop_fw(mld);449450wiphy_unlock(mld->wiphy);451rtnl_unlock();452453ret = iwl_mld_leds_init(mld);454if (ret)455goto free_nvm;456457ret = iwl_mld_alloc_scan_cmd(mld);458if (ret)459goto leds_exit;460461ret = iwl_mld_low_latency_init(mld);462if (ret)463goto free_scan_cmd;464465ret = iwl_mld_register_hw(mld);466if (ret)467goto low_latency_free;468469iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant);470471iwl_mld_add_debugfs_files(mld, dbgfs_dir);472iwl_mld_thermal_initialize(mld);473474iwl_mld_ptp_init(mld);475476return op_mode;477478low_latency_free:479iwl_mld_low_latency_free(mld);480free_scan_cmd:481kfree(mld->scan.cmd);482leds_exit:483iwl_mld_leds_exit(mld);484free_nvm:485kfree(mld->nvm_data);486err:487iwl_trans_op_mode_leave(mld->trans);488ieee80211_free_hw(mld->hw);489return ERR_PTR(ret);490}491492static void493iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode)494{495struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);496497iwl_mld_ptp_remove(mld);498iwl_mld_leds_exit(mld);499500iwl_mld_thermal_exit(mld);501502wiphy_lock(mld->wiphy);503iwl_mld_low_latency_stop(mld);504iwl_mld_deinit_time_sync(mld);505wiphy_unlock(mld->wiphy);506507ieee80211_unregister_hw(mld->hw);508509iwl_fw_runtime_free(&mld->fwrt);510iwl_mld_low_latency_free(mld);511512iwl_trans_op_mode_leave(mld->trans);513514kfree(mld->nvm_data);515kfree(mld->scan.cmd);516kfree(mld->channel_survey);517kfree(mld->error_recovery_buf);518kfree(mld->mcast_filter_cmd);519520ieee80211_free_hw(mld->hw);521}522523static void iwl_mld_queue_state_change(struct iwl_op_mode *op_mode,524int hw_queue, bool queue_full)525{526struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);527struct ieee80211_txq *txq;528struct iwl_mld_sta *mld_sta;529struct iwl_mld_txq *mld_txq;530531rcu_read_lock();532533txq = rcu_dereference(mld->fw_id_to_txq[hw_queue]);534if (!txq) {535rcu_read_unlock();536537if (queue_full) {538/* An internal queue is not expected to become full */539IWL_WARN(mld,540"Internal hw_queue %d is full! stopping all queues\n",541hw_queue);542/* Stop all queues, as an internal queue is not543* mapped to a mac80211 one544*/545ieee80211_stop_queues(mld->hw);546} else {547ieee80211_wake_queues(mld->hw);548}549550return;551}552553mld_txq = iwl_mld_txq_from_mac80211(txq);554mld_sta = txq->sta ? iwl_mld_sta_from_mac80211(txq->sta) : NULL;555556mld_txq->status.stop_full = queue_full;557558if (!queue_full && mld_sta &&559mld_sta->sta_state != IEEE80211_STA_NOTEXIST) {560local_bh_disable();561iwl_mld_tx_from_txq(mld, txq);562local_bh_enable();563}564565rcu_read_unlock();566}567568static void569iwl_mld_queue_full(struct iwl_op_mode *op_mode, int hw_queue)570{571iwl_mld_queue_state_change(op_mode, hw_queue, true);572}573574static void575iwl_mld_queue_not_full(struct iwl_op_mode *op_mode, int hw_queue)576{577iwl_mld_queue_state_change(op_mode, hw_queue, false);578}579580static bool581iwl_mld_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state)582{583struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);584585iwl_mld_set_hwkill(mld, state);586587return false;588}589590static void591iwl_mld_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb)592{593struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);594struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);595596iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]);597ieee80211_free_txskb(mld->hw, skb);598}599600static void iwl_mld_read_error_recovery_buffer(struct iwl_mld *mld)601{602u32 src_size = mld->fw->ucode_capa.error_log_size;603u32 src_addr = mld->fw->ucode_capa.error_log_addr;604u8 *recovery_buf;605int ret;606607/* no recovery buffer size defined in a TLV */608if (!src_size)609return;610611recovery_buf = kzalloc(src_size, GFP_ATOMIC);612if (!recovery_buf)613return;614615ret = iwl_trans_read_mem_bytes(mld->trans, src_addr,616recovery_buf, src_size);617if (ret) {618IWL_ERR(mld, "Failed to read error recovery buffer (%d)\n",619ret);620kfree(recovery_buf);621return;622}623624mld->error_recovery_buf = recovery_buf;625}626627static void iwl_mld_restart_nic(struct iwl_mld *mld)628{629iwl_mld_read_error_recovery_buffer(mld);630631mld->fwrt.trans->dbg.restart_required = false;632633ieee80211_restart_hw(mld->hw);634}635636static void637iwl_mld_nic_error(struct iwl_op_mode *op_mode,638enum iwl_fw_error_type type)639{640struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);641bool trans_dead = iwl_trans_is_dead(mld->trans);642643if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL)644IWL_ERR(mld, "Command queue full!\n");645else if (!trans_dead && !mld->fw_status.do_not_dump_once)646iwl_fwrt_dump_error_logs(&mld->fwrt);647648mld->fw_status.do_not_dump_once = false;649650/* It is necessary to abort any os scan here because mac80211 requires651* having the scan cleared before restarting.652* We'll reset the scan_status to NONE in restart cleanup in653* the next drv_start() call from mac80211. If ieee80211_hw_restart654* isn't called scan status will stay busy.655*/656iwl_mld_report_scan_aborted(mld);657658/*659* This should be first thing before trying to collect any660* data to avoid endless loops if any HW error happens while661* collecting debug data.662* It might not actually be true that we'll restart, but the663* setting doesn't matter if we're going to be unbound either.664*/665if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT &&666mld->fw_status.running)667mld->fw_status.in_hw_restart = true;668}669670static void iwl_mld_dump_error(struct iwl_op_mode *op_mode,671struct iwl_fw_error_dump_mode *mode)672{673struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);674675/* if we come in from opmode we have the mutex held */676if (mode->context == IWL_ERR_CONTEXT_FROM_OPMODE) {677lockdep_assert_wiphy(mld->wiphy);678iwl_fw_error_collect(&mld->fwrt);679} else {680wiphy_lock(mld->wiphy);681if (mode->context != IWL_ERR_CONTEXT_ABORT)682iwl_fw_error_collect(&mld->fwrt);683wiphy_unlock(mld->wiphy);684}685}686687static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode,688enum iwl_fw_error_type type)689{690struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);691692/* SW reset can happen for TOP error w/o NIC error, so693* also abort scan here and set in_hw_restart, when we694* had a NIC error both were already done.695*/696iwl_mld_report_scan_aborted(mld);697mld->fw_status.in_hw_restart = true;698699/* Do restart only in the following conditions are met:700* - we consider the FW as running701* - The trigger that brought us here is defined as one that requires702* a restart (in the debug TLVs)703*/704if (!mld->fw_status.running || !mld->fwrt.trans->dbg.restart_required)705return false;706707iwl_mld_restart_nic(mld);708return true;709}710711static void712iwl_mld_time_point(struct iwl_op_mode *op_mode,713enum iwl_fw_ini_time_point tp_id,714union iwl_dbg_tlv_tp_data *tp_data)715{716struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);717718iwl_dbg_tlv_time_point(&mld->fwrt, tp_id, tp_data);719}720721#ifdef CONFIG_PM_SLEEP722static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode)723{724struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);725726wiphy_lock(mld->wiphy);727iwl_mld_stop_fw(mld);728mld->fw_status.in_d3 = false;729wiphy_unlock(mld->wiphy);730}731#else732static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode)733{}734#endif735736static void iwl_mld_dump(struct iwl_op_mode *op_mode)737{738struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode);739struct iwl_fw_runtime *fwrt = &mld->fwrt;740741if (!iwl_trans_fw_running(fwrt->trans))742return;743744iwl_dbg_tlv_time_point(fwrt, IWL_FW_INI_TIME_POINT_USER_TRIGGER, NULL);745}746747static const struct iwl_op_mode_ops iwl_mld_ops = {748.start = iwl_op_mode_mld_start,749.stop = iwl_op_mode_mld_stop,750.rx = iwl_mld_rx,751.rx_rss = iwl_mld_rx_rss,752.queue_full = iwl_mld_queue_full,753.queue_not_full = iwl_mld_queue_not_full,754.hw_rf_kill = iwl_mld_set_hw_rfkill_state,755.free_skb = iwl_mld_free_skb,756.nic_error = iwl_mld_nic_error,757.dump_error = iwl_mld_dump_error,758.sw_reset = iwl_mld_sw_reset,759.time_point = iwl_mld_time_point,760.device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off),761.dump = iwl_mld_dump,762};763764struct iwl_mld_mod_params iwlmld_mod_params = {765.power_scheme = IWL_POWER_SCHEME_BPS,766};767768module_param_named(power_scheme, iwlmld_mod_params.power_scheme, int, 0444);769MODULE_PARM_DESC(power_scheme,770"power management scheme: 1-active, 2-balanced, default: 2");771772773