struct sk_buff *iwl_mvm_testmode_alloc_event(struct iwl_op_mode *op_mode, int len) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); return cfg80211_testmode_alloc_event_skb(mvm->hw->wiphy, len, GFP_ATOMIC); }
static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); int i; iwl_mvm_leds_exit(mvm); iwl_mvm_tt_exit(mvm); ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); vfree(mvm->fw_error_dump); kfree(mvm->fw_error_sram); kfree(mvm->fw_error_rxf); kfree(mvm->mcast_filter_cmd); mvm->mcast_filter_cmd = NULL; #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_IWLWIFI_DEBUGFS) kfree(mvm->d3_resume_sram); #endif iwl_trans_op_mode_leave(mvm->trans); iwl_phy_db_free(mvm->phy_db); mvm->phy_db = NULL; iwl_free_nvm_data(mvm->nvm_data); for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) kfree(mvm->nvm_sections[i].data); ieee80211_free_hw(mvm->hw); }
static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; u32 reg_val = 0; radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >> FW_PHY_CFG_RADIO_TYPE_POS; radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >> FW_PHY_CFG_RADIO_STEP_POS; radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >> FW_PHY_CFG_RADIO_DASH_POS; /* SKU control */ reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; /* radio configuration */ reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); /* * TODO: Bits 7-8 of CSR in 8000 HW family set the ADC sampling, and * shouldn't be set to any non-zero value. The same is supposed to be * true of the other HW, but unsetting them (such as the 7260) causes * automatic tests to fail on seemingly unrelated errors. Need to * further investigate this, but for now we'll separate cases. */ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, reg_val); IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, radio_cfg_step, radio_cfg_dash); /* * W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); }
static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; u32 reg_val = 0; radio_cfg_type = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_TYPE) >> FW_PHY_CFG_RADIO_TYPE_POS; radio_cfg_step = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_STEP) >> FW_PHY_CFG_RADIO_STEP_POS; radio_cfg_dash = (mvm->fw->phy_config & FW_PHY_CFG_RADIO_DASH) >> FW_PHY_CFG_RADIO_DASH_POS; /* SKU control */ reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; /* radio configuration */ reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); /* silicon bits */ reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, reg_val); IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, radio_cfg_step, radio_cfg_dash); /* * W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ if (mvm->trans->cfg->device_family != IWL_DEVICE_FAMILY_8000) iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); }
static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); int i; iwl_mvm_leds_exit(mvm); ieee80211_unregister_hw(mvm->hw); kfree(mvm->scan_cmd); iwl_trans_stop_hw(mvm->trans, true); iwl_phy_db_free(mvm->phy_db); mvm->phy_db = NULL; kfree(mvm->eeprom_blob); iwl_free_nvm_data(mvm->nvm_data); for (i = 0; i < NVM_NUM_OF_SECTIONS; i++) kfree(mvm->nvm_sections[i].data); ieee80211_free_hw(mvm->hw); }
static struct iwl_op_mode * iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; struct iwl_mvm *mvm; struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { TX_CMD, }; int err, scan_size; u32 min_backoff; /* * We use IWL_MVM_STATION_COUNT to check the validity of the station * index all over the driver - check that its value corresponds to the * array size. */ BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); /******************************** * 1. Allocating and configuring HW data ********************************/ hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + sizeof(struct iwl_mvm), &iwl_mvm_hw_ops); if (!hw) return NULL; op_mode = hw->priv; op_mode->ops = &iwl_mvm_ops; mvm = IWL_OP_MODE_GET_MVM(op_mode); mvm->dev = trans->dev; mvm->trans = trans; mvm->cfg = cfg; mvm->fw = fw; mvm->hw = hw; mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; mvm->aux_queue = 15; mvm->first_agg_queue = 16; mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; if (mvm->cfg->base_params->num_of_queues == 16) { mvm->aux_queue = 11; mvm->first_agg_queue = 12; } mvm->sf_state = SF_UNINIT; mutex_init(&mvm->mutex); mutex_init(&mvm->d0i3_suspend_mutex); spin_lock_init(&mvm->async_handlers_lock); INIT_LIST_HEAD(&mvm->time_event_list); INIT_LIST_HEAD(&mvm->async_handlers_list); spin_lock_init(&mvm->time_event_lock); INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); spin_lock_init(&mvm->d0i3_tx_lock); skb_queue_head_init(&mvm->d0i3_tx); init_waitqueue_head(&mvm->d0i3_exit_waitq); SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); /* * Populate the state variables that the transport layer needs * to know about. */ trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; if (!iwlwifi_mod_params.wd_disable) trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout; else trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_mvm_cmd_strings; trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; trans_cfg.cmd_fifo = IWL_MVM_CMD_FIFO; snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%s", fw->fw_version); /* Configure transport layer */ iwl_trans_configure(mvm->trans, &trans_cfg); trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); /* set up notification wait support */ iwl_notification_wait_init(&mvm->notif_wait); /* Init phy db */ mvm->phy_db = iwl_phy_db_init(trans); if (!mvm->phy_db) { IWL_ERR(mvm, "Cannot init phy_db\n"); goto out_free; } IWL_INFO(mvm, "Detected %s, REV=0x%X\n", mvm->cfg->name, mvm->trans->hw_rev); min_backoff = calc_min_backoff(trans, cfg); iwl_mvm_tt_initialize(mvm, min_backoff); /* set the nvm_file_name according to priority */ if (iwlwifi_mod_params.nvm_file) mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; else mvm->nvm_file_name = mvm->cfg->default_nvm_file; if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, "not allowing power-up and not having nvm_file\n")) goto out_free; /* * Even if nvm exists in the nvm_file driver should read agin the nvm * from the nic because there might be entries that exist in the OTP * and not in the file. * for nics with no_power_up_nic_in_init: rely completley on nvm_file */ if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { err = iwl_nvm_init(mvm, false); if (err) goto out_free; } else { err = iwl_trans_start_hw(mvm->trans); if (err) goto out_free; mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); iwl_trans_stop_device(trans); mutex_unlock(&mvm->mutex); /* returns 0 if successful, 1 if success but in rfkill */ if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } } scan_size = sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)); mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); if (!mvm->scan_cmd) goto out_free; err = iwl_mvm_mac_setup_register(mvm); if (err) goto out_free; err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); if (err) goto out_unregister; memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); /* rpm starts with a taken ref. only set the appropriate bit here. */ set_bit(IWL_MVM_REF_UCODE_DOWN, mvm->ref_bitmap); return op_mode; out_unregister: ieee80211_unregister_hw(mvm->hw); iwl_mvm_leds_exit(mvm); out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) iwl_trans_op_mode_leave(trans); ieee80211_free_hw(mvm->hw); return NULL; }
static struct iwl_op_mode * iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; struct iwl_mvm *mvm; struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { TX_CMD, }; int err, scan_size; /******************************** * 1. Allocating and configuring HW data ********************************/ hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + sizeof(struct iwl_mvm), &iwl_mvm_hw_ops); if (!hw) return NULL; op_mode = hw->priv; op_mode->ops = &iwl_mvm_ops; op_mode->trans = trans; mvm = IWL_OP_MODE_GET_MVM(op_mode); mvm->dev = trans->dev; mvm->trans = trans; mvm->cfg = cfg; mvm->fw = fw; mvm->hw = hw; mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; mutex_init(&mvm->mutex); spin_lock_init(&mvm->async_handlers_lock); INIT_LIST_HEAD(&mvm->time_event_list); INIT_LIST_HEAD(&mvm->async_handlers_list); spin_lock_init(&mvm->time_event_lock); INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); /* * Populate the state variables that the transport layer needs * to know about. */ trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; if (!iwlwifi_mod_params.wd_disable) trans_cfg.queue_watchdog_timeout = cfg->base_params->wd_timeout; else trans_cfg.queue_watchdog_timeout = IWL_WATCHDOG_DISABLED; trans_cfg.command_names = iwl_mvm_cmd_strings; trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; trans_cfg.cmd_fifo = IWL_MVM_CMD_FIFO; snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%s", fw->fw_version); /* Configure transport layer */ iwl_trans_configure(mvm->trans, &trans_cfg); trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); /* set up notification wait support */ iwl_notification_wait_init(&mvm->notif_wait); /* Init phy db */ mvm->phy_db = iwl_phy_db_init(trans); if (!mvm->phy_db) { IWL_ERR(mvm, "Cannot init phy_db\n"); goto out_free; } IWL_INFO(mvm, "Detected %s, REV=0x%X\n", mvm->cfg->name, mvm->trans->hw_rev); err = iwl_trans_start_hw(mvm->trans); if (err) goto out_free; iwl_mvm_tt_initialize(mvm); mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); mutex_unlock(&mvm->mutex); /* returns 0 if successful, 1 if success but in rfkill */ if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } /* Stop the hw after the ALIVE and NVM has been read */ if (!iwlmvm_mod_params.init_dbg) iwl_trans_stop_hw(mvm->trans, false); scan_size = sizeof(struct iwl_scan_cmd) + mvm->fw->ucode_capa.max_probe_length + (MAX_NUM_SCAN_CHANNELS * sizeof(struct iwl_scan_channel)); mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); if (!mvm->scan_cmd) goto out_free; err = iwl_mvm_mac_setup_register(mvm); if (err) goto out_free; err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); if (err) goto out_unregister; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD) mvm->pm_ops = &pm_mac_ops; else mvm->pm_ops = &pm_legacy_ops; memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); return op_mode; out_unregister: ieee80211_unregister_hw(mvm->hw); out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); iwl_trans_stop_hw(trans, true); ieee80211_free_hw(mvm->hw); return NULL; }
u32 iwl_mvm_testmode_get_fw_ver(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); return mvm->fw->ucode_ver; }
int iwl_mvm_testmode_send_cmd(struct iwl_op_mode *op_mode, struct iwl_host_cmd *cmd) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); return iwl_mvm_send_cmd(mvm, cmd); }
static int iwl_mvm_tm_send_hcmd(struct iwl_mvm *mvm, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_tm_cmd_request *hcmd_req = data_in->data; struct iwl_tm_cmd_request *cmd_resp; u32 reply_len, resp_size; struct iwl_rx_packet *pkt; struct iwl_host_cmd host_cmd = { .id = hcmd_req->id, .data[0] = hcmd_req->data, .len[0] = hcmd_req->len, .dataflags[0] = IWL_HCMD_DFL_NOCOPY, .flags = CMD_SYNC, }; int ret; if (hcmd_req->want_resp) host_cmd.flags |= CMD_WANT_SKB; mutex_lock(&mvm->mutex); ret = iwl_mvm_send_cmd(mvm, &host_cmd); mutex_unlock(&mvm->mutex); if (ret) return ret; /* if no reply is required, we are done */ if (!(host_cmd.flags & CMD_WANT_SKB)) return 0; /* Retrieve response packet */ pkt = host_cmd.resp_pkt; if (!pkt) { IWL_ERR(mvm->trans, "HCMD received a null response packet\n"); return -ENOMSG; } reply_len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; /* Set response data */ resp_size = sizeof(struct iwl_tm_cmd_request) + reply_len; cmd_resp = kzalloc(resp_size, GFP_KERNEL); if (!cmd_resp) { iwl_free_resp(&host_cmd); return -ENOMEM; } cmd_resp->id = hcmd_req->id; cmd_resp->len = reply_len; memcpy(cmd_resp->data, &(pkt->hdr), reply_len); iwl_free_resp(&host_cmd); data_out->data = cmd_resp; data_out->len = resp_size; return 0; } static void iwl_mvm_tm_execute_reg_ops(struct iwl_trans *trans, struct iwl_tm_regs_request *request, struct iwl_tm_regs_request *result) { struct iwl_tm_reg_op *cur_op; u32 idx, read_idx; for (idx = 0, read_idx = 0; idx < request->num; idx++) { cur_op = &request->reg_ops[idx]; if (cur_op->op_type == IWL_TM_REG_OP_READ) { cur_op->value = iwl_read32(trans, cur_op->address); memcpy(&result->reg_ops[read_idx], cur_op, sizeof(*cur_op)); read_idx++; } else { /* IWL_TM_REG_OP_WRITE is the only possible option */ iwl_write32(trans, cur_op->address, cur_op->value); } } } static int iwl_mvm_tm_reg_ops(struct iwl_trans *trans, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_tm_reg_op *cur_op; struct iwl_tm_regs_request *request = data_in->data; struct iwl_tm_regs_request *result; u32 result_size; u32 idx, read_idx; bool is_grab_nic_access_required = true; unsigned long flags; /* Calculate result size (result is returned only for read ops) */ for (idx = 0, read_idx = 0; idx < request->num; idx++) { if (request->reg_ops[idx].op_type == IWL_TM_REG_OP_READ) read_idx++; /* check if there is an operation that it is not */ /* in the CSR range (0x00000000 - 0x000003FF) */ /* and not in the AL range */ cur_op = &request->reg_ops[idx]; if (IS_AL_ADDR(cur_op->address) || (cur_op->address < HBUS_BASE)) is_grab_nic_access_required = false; } result_size = sizeof(struct iwl_tm_regs_request) + read_idx*sizeof(struct iwl_tm_reg_op); result = kzalloc(result_size, GFP_KERNEL); if (!result) return -ENOMEM; result->num = read_idx; if (is_grab_nic_access_required) { if (!iwl_trans_grab_nic_access(trans, false, &flags)) { kfree(result); return -EBUSY; } iwl_mvm_tm_execute_reg_ops(trans, request, result); iwl_trans_release_nic_access(trans, &flags); } else { iwl_mvm_tm_execute_reg_ops(trans, request, result); } data_out->data = result; data_out->len = result_size; return 0; } static int iwl_tm_get_dev_info(struct iwl_mvm *mvm, struct iwl_tm_data *data_out) { struct iwl_tm_dev_info *dev_info; const u8 driver_ver[] = IWLWIFI_VERSION; if (!mvm->nvm_data) return -EINVAL; dev_info = kzalloc(sizeof(struct iwl_tm_dev_info) + (strlen(driver_ver)+1)*sizeof(u8), GFP_KERNEL); if (!dev_info) return -ENOMEM; dev_info->dev_id = mvm->trans->hw_id; dev_info->fw_ver = mvm->fw->ucode_ver; dev_info->vendor_id = PCI_VENDOR_ID_INTEL; dev_info->silicon_step = mvm->nvm_data->radio_cfg_step; /* TODO: Assign real value when feature is implemented */ dev_info->build_ver = 0x00; strcpy(dev_info->driver_ver, driver_ver); data_out->data = dev_info; data_out->len = sizeof(*dev_info); return 0; } static int iwl_tm_indirect_read(struct iwl_mvm *mvm, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_trans *trans = mvm->trans; struct iwl_tm_sram_read_request *cmd_in = data_in->data; u32 addr = cmd_in->offset; u32 size = cmd_in->length; u32 *buf32, size32, i; unsigned long flags; if (size & (sizeof(u32)-1)) return -EINVAL; data_out->data = kmalloc(size, GFP_KERNEL); if (!data_out->data) return -ENOMEM; data_out->len = size; size32 = size / sizeof(u32); buf32 = data_out->data; mutex_lock(&mvm->mutex); /* Hard-coded periphery absolute address */ if (IWL_ABS_PRPH_START <= addr && addr < IWL_ABS_PRPH_START + PRPH_END) { if (!iwl_trans_grab_nic_access(trans, false, &flags)) { mutex_unlock(&mvm->mutex); return -EBUSY; } for (i = 0; i < size32; i++) buf32[i] = iwl_trans_read_prph(trans, addr + i * sizeof(u32)); iwl_trans_release_nic_access(trans, &flags); } else { /* target memory (SRAM) */ iwl_trans_read_mem(trans, addr, buf32, size32); } mutex_unlock(&mvm->mutex); return 0; } static int iwl_tm_indirect_write(struct iwl_mvm *mvm, struct iwl_tm_data *data_in) { struct iwl_trans *trans = mvm->trans; struct iwl_tm_sram_write_request *cmd_in = data_in->data; u32 addr = cmd_in->offset; u32 size = cmd_in->len; u8 *buf = cmd_in->buffer; u32 *buf32 = (u32 *)buf, size32 = size / sizeof(u32); unsigned long flags; u32 val, i; mutex_lock(&mvm->mutex); if (IWL_ABS_PRPH_START <= addr && addr < IWL_ABS_PRPH_START + PRPH_END) { /* Periphery writes can be 1-3 bytes long, or DWORDs */ if (size < 4) { memcpy(&val, buf, size); if (!iwl_trans_grab_nic_access(trans, false, &flags)) { mutex_unlock(&mvm->mutex); return -EBUSY; } iwl_write32(trans, HBUS_TARG_PRPH_WADDR, (addr & 0x000FFFFF) | ((size - 1) << 24)); iwl_write32(trans, HBUS_TARG_PRPH_WDAT, val); iwl_trans_release_nic_access(trans, &flags); } else { if (size % sizeof(u32)) { mutex_unlock(&mvm->mutex); return -EINVAL; } for (i = 0; i < size32; i++) iwl_write_prph(trans, addr + i*sizeof(u32), buf32[i]); } } else { iwl_trans_write_mem(trans, addr, buf32, size32); } mutex_unlock(&mvm->mutex); return 0; } /** * iwl_mvm_tm_cmd_execute - Implementation of test command executor callback * @op_mode: Specific device's operation mode * @cmd: User space command's index * @data_in: Input data. "data" field is to be casted to relevant * data structure. All verification must be done in the * caller function, therefor assuming that input data * length is valid. * @data_out: Will be allocated inside, freeing is in the caller's * responsibility */ int iwl_mvm_tm_cmd_execute(struct iwl_op_mode *op_mode, u32 cmd, struct iwl_tm_data *data_in, struct iwl_tm_data *data_out) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); if (WARN_ON_ONCE(!op_mode || !data_in)) return -EINVAL; switch (cmd) { case IWL_TM_USER_CMD_HCMD: return iwl_mvm_tm_send_hcmd(mvm, data_in, data_out); case IWL_TM_USER_CMD_REG_ACCESS: return iwl_mvm_tm_reg_ops(mvm->trans, data_in, data_out); case IWL_TM_USER_CMD_SRAM_WRITE: return iwl_tm_indirect_write(mvm, data_in); case IWL_TM_USER_CMD_SRAM_READ: return iwl_tm_indirect_read(mvm, data_in, data_out); case IWL_TM_USER_CMD_GET_DEVICE_INFO: return iwl_tm_get_dev_info(mvm, data_out); default: break; } return -EOPNOTSUPP; } /** * iwl_tm_mvm_send_rx() - Send a spontaneous rx message to user * @mvm: mvm opmode pointer * @rxb: Contains rx packet to be sent */ void iwl_tm_mvm_send_rx(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt; int length; pkt = rxb_addr(rxb); length = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; /* the length doesn't include len_n_flags field, so add it manually */ length += sizeof(__le32); iwl_tm_gnl_send_msg(mvm->trans, IWL_TM_USER_CMD_NOTIF_UCODE_RX_PKT, true, (void *)pkt, length, GFP_ATOMIC); }
struct sk_buff *iwl_mvm_testmode_alloc_reply(struct iwl_op_mode *op_mode, int len) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); return cfg80211_testmode_alloc_reply_skb(mvm->hw->wiphy, len); }
static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) { struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); u8 radio_cfg_type, radio_cfg_step, radio_cfg_dash; u32 reg_val = 0; /* * We can't upload the correct value to the INIT image * as we don't have nvm_data by that time. * * TODO: Figure out what we should do here */ if (mvm->nvm_data) { radio_cfg_type = mvm->nvm_data->radio_cfg_type; radio_cfg_step = mvm->nvm_data->radio_cfg_step; radio_cfg_dash = mvm->nvm_data->radio_cfg_dash; } else { radio_cfg_type = 0; radio_cfg_step = 0; radio_cfg_dash = 0; } /* SKU control */ reg_val |= CSR_HW_REV_STEP(mvm->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; reg_val |= CSR_HW_REV_DASH(mvm->trans->hw_rev) << CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; /* radio configuration */ reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; reg_val |= radio_cfg_step << CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; reg_val |= radio_cfg_dash << CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; WARN_ON((radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE) & ~CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE); /* silicon bits */ reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; reg_val |= CSR_HW_IF_CONFIG_REG_BIT_MAC_SI; iwl_trans_set_bits_mask(mvm->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH | CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE | CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP | CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH | CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI, reg_val); IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, radio_cfg_step, radio_cfg_dash); /* * W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); }
static struct iwl_op_mode * iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; struct iwl_mvm *mvm; struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { TX_CMD, }; int err, scan_size; u32 min_backoff; /* * We use IWL_MVM_STATION_COUNT to check the validity of the station * index all over the driver - check that its value corresponds to the * array size. */ BUILD_BUG_ON(ARRAY_SIZE(mvm->fw_id_to_mac_id) != IWL_MVM_STATION_COUNT); /******************************** * 1. Allocating and configuring HW data ********************************/ hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + sizeof(struct iwl_mvm), &iwl_mvm_hw_ops); if (!hw) return NULL; if (cfg->max_rx_agg_size) hw->max_rx_aggregation_subframes = cfg->max_rx_agg_size; if (cfg->max_tx_agg_size) hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; op_mode = hw->priv; op_mode->ops = &iwl_mvm_ops; mvm = IWL_OP_MODE_GET_MVM(op_mode); mvm->dev = trans->dev; mvm->trans = trans; mvm->cfg = cfg; mvm->fw = fw; mvm->hw = hw; mvm->restart_fw = iwlwifi_mod_params.restart_fw ? -1 : 0; mvm->aux_queue = 15; mvm->first_agg_queue = 16; mvm->last_agg_queue = mvm->cfg->base_params->num_of_queues - 1; if (mvm->cfg->base_params->num_of_queues == 16) { mvm->aux_queue = 11; mvm->first_agg_queue = 12; } mvm->sf_state = SF_UNINIT; mvm->low_latency_agg_frame_limit = 6; mvm->cur_ucode = IWL_UCODE_INIT; mutex_init(&mvm->mutex); mutex_init(&mvm->d0i3_suspend_mutex); spin_lock_init(&mvm->async_handlers_lock); INIT_LIST_HEAD(&mvm->time_event_list); INIT_LIST_HEAD(&mvm->aux_roc_te_list); INIT_LIST_HEAD(&mvm->async_handlers_list); spin_lock_init(&mvm->time_event_lock); mvm->workqueue = create_singlethread_workqueue(DRV_NAME); if (!mvm->workqueue) { ieee80211_free_hw(mvm->hw); return NULL; } INIT_WORK(&mvm->async_handlers_wk, iwl_mvm_async_handlers_wk); INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk); INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); spin_lock_init(&mvm->d0i3_tx_lock); spin_lock_init(&mvm->refs_lock); skb_queue_head_init(&mvm->d0i3_tx); init_waitqueue_head(&mvm->d0i3_exit_waitq); SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); /* * Populate the state variables that the transport layer needs * to know about. */ trans_cfg.op_mode = op_mode; trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); trans_cfg.rx_buf_size_8k = iwlwifi_mod_params.amsdu_size_8K; if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_DW_BC_TABLE) trans_cfg.bc_table_dword = true; trans_cfg.command_names = iwl_mvm_cmd_strings; trans_cfg.cmd_queue = IWL_MVM_CMD_QUEUE; trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; trans_cfg.scd_set_active = true; trans_cfg.sdio_adma_addr = fw->sdio_adma_addr; /* Set a short watchdog for the command queue */ trans_cfg.cmd_q_wdg_timeout = iwl_mvm_get_wd_timeout(mvm, NULL, false, true); snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%s", fw->fw_version); /* Configure transport layer */ iwl_trans_configure(mvm->trans, &trans_cfg); trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); trans->dbg_dest_tlv = mvm->fw->dbg_dest_tlv; trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, sizeof(trans->dbg_conf_tlv)); trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv; /* set up notification wait support */ iwl_notification_wait_init(&mvm->notif_wait); /* Init phy db */ mvm->phy_db = iwl_phy_db_init(trans); if (!mvm->phy_db) { IWL_ERR(mvm, "Cannot init phy_db\n"); goto out_free; } IWL_INFO(mvm, "Detected %s, REV=0x%X\n", mvm->cfg->name, mvm->trans->hw_rev); min_backoff = calc_min_backoff(trans, cfg); iwl_mvm_tt_initialize(mvm, min_backoff); /* set the nvm_file_name according to priority */ if (iwlwifi_mod_params.nvm_file) { mvm->nvm_file_name = iwlwifi_mod_params.nvm_file; } else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { if (CSR_HW_REV_STEP(trans->hw_rev) == SILICON_B_STEP) mvm->nvm_file_name = mvm->cfg->default_nvm_file_B_step; else mvm->nvm_file_name = mvm->cfg->default_nvm_file_C_step; } if (WARN(cfg->no_power_up_nic_in_init && !mvm->nvm_file_name, "not allowing power-up and not having nvm_file\n")) goto out_free; /* * Even if nvm exists in the nvm_file driver should read again the nvm * from the nic because there might be entries that exist in the OTP * and not in the file. * for nics with no_power_up_nic_in_init: rely completley on nvm_file */ if (cfg->no_power_up_nic_in_init && mvm->nvm_file_name) { err = iwl_nvm_init(mvm, false); if (err) goto out_free; } else { err = iwl_trans_start_hw(mvm->trans); if (err) goto out_free; mutex_lock(&mvm->mutex); err = iwl_run_init_mvm_ucode(mvm, true); if (!err || !iwlmvm_mod_params.init_dbg) iwl_trans_stop_device(trans); mutex_unlock(&mvm->mutex); /* returns 0 if successful, 1 if success but in rfkill */ if (err < 0 && !iwlmvm_mod_params.init_dbg) { IWL_ERR(mvm, "Failed to run INIT ucode: %d\n", err); goto out_free; } } scan_size = iwl_mvm_scan_size(mvm); mvm->scan_cmd = kmalloc(scan_size, GFP_KERNEL); if (!mvm->scan_cmd) goto out_free; /* Set EBS as successful as long as not stated otherwise by the FW. */ mvm->last_ebs_successful = true; err = iwl_mvm_mac_setup_register(mvm); if (err) goto out_free; err = iwl_mvm_dbgfs_register(mvm, dbgfs_dir); if (err) goto out_unregister; memset(&mvm->rx_stats, 0, sizeof(struct mvm_statistics_rx)); /* rpm starts with a taken ref. only set the appropriate bit here. */ mvm->refs[IWL_MVM_REF_UCODE_DOWN] = 1; return op_mode; out_unregister: ieee80211_unregister_hw(mvm->hw); iwl_mvm_leds_exit(mvm); out_free: iwl_phy_db_free(mvm->phy_db); kfree(mvm->scan_cmd); if (!cfg->no_power_up_nic_in_init || !mvm->nvm_file_name) iwl_trans_op_mode_leave(trans); destroy_workqueue(mvm->workqueue); ieee80211_free_hw(mvm->hw); return NULL; }