/** * struct iwl_drv - drv common data * @list: list of drv structures using this opmode * @fw: the iwl_fw structure * @op_mode: the running op_mode * @trans: transport layer * @dev: for debug prints only * @fw_index: firmware revision to try loading * @firmware_name: composite filename of ucode file to load * @request_firmware_complete: the firmware has been obtained from user space * @dbgfs_drv: debugfs root directory entry * @dbgfs_trans: debugfs transport directory entry * @dbgfs_op_mode: debugfs op_mode directory entry
*/ struct iwl_drv { struct list_head list; struct iwl_fw fw;
/* Protects the table contents, i.e. the ops pointer & drv list */ static DEFINE_MUTEX(iwlwifi_opmode_table_mtx); staticstruct iwlwifi_opmode_table { constchar *name; /* name: iwldvm, iwlmvm, etc */ conststruct iwl_op_mode_ops *ops; /* pointer to op_mode ops */ struct list_head drv; /* list of devices using this op_mode */
} iwlwifi_opmode_table[] = { /* ops set when driver is initialized */
[DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL },
[MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, #if IS_ENABLED(CONFIG_IWLMLD)
[MLD_OP_MODE] = { .name = "iwlmld", .ops = NULL }, #endif
};
#define IWL_DEFAULT_SCAN_CHANNELS 40
/* * struct fw_sec: Just for the image parsing process. * For the fw storage we are using struct fw_desc.
*/ struct fw_sec { constvoid *data; /* the sec data */
size_t size; /* section size */
u32 offset; /* offset of writing in the device */
};
switch (CSR_HW_REV_TYPE(trans->info.hw_rev)) { case IWL_CFG_MAC_TYPE_PU:
mac = "9000-pu";
mac_step = 'b'; break; case IWL_CFG_MAC_TYPE_TH:
mac = "9260-th";
mac_step = 'b'; break; case IWL_CFG_MAC_TYPE_QU:
mac = "Qu"; break; case IWL_CFG_MAC_TYPE_CC: /* special case - no RF since it's fixed (discrete) */
scnprintf(buf, FW_NAME_PRE_BUFSIZE, "iwlwifi-cc-a0"); return buf; case IWL_CFG_MAC_TYPE_QUZ:
mac = "QuZ"; /* all QuZ use A0 firmware */
mac_step = 'a'; break; case IWL_CFG_MAC_TYPE_SO: case IWL_CFG_MAC_TYPE_SOF:
mac = "so";
mac_step = 'a'; break; case IWL_CFG_MAC_TYPE_TY:
mac = "ty";
mac_step = 'a'; break; case IWL_CFG_MAC_TYPE_MA:
mac = "ma"; break; case IWL_CFG_MAC_TYPE_BZ: case IWL_CFG_MAC_TYPE_BZ_W:
mac = "bz"; break; case IWL_CFG_MAC_TYPE_GL:
mac = "gl"; break; case IWL_CFG_MAC_TYPE_SC:
mac = "sc"; break; case IWL_CFG_MAC_TYPE_SC2: /* Uses the same firmware as SC2 */ case IWL_CFG_MAC_TYPE_SC2F:
mac = "sc2"; break; case IWL_CFG_MAC_TYPE_BR:
mac = "br"; break; case IWL_CFG_MAC_TYPE_DR:
mac = "dr"; break; default: return"unknown-mac";
}
/* if the MAC doesn't have range or if its range it higher than the RF's */ if (!base->ucode_api_max ||
(cfg->ucode_api_max && base->ucode_api_min > cfg->ucode_api_max)) {
*api_min = cfg->ucode_api_min;
*api_max = cfg->ucode_api_max; return;
}
/* if the RF doesn't have range or if its range it higher than the MAC's */ if (!cfg->ucode_api_max ||
(base->ucode_api_max && cfg->ucode_api_min > base->ucode_api_max)) {
*api_min = base->ucode_api_min;
*api_max = base->ucode_api_max; return;
}
if (drv->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000 &&
(drv->trans->info.hw_rev_step != SILICON_B_STEP &&
drv->trans->info.hw_rev_step != SILICON_C_STEP)) {
IWL_ERR(drv, "Only HW steps B and C are currently supported (0x%0x)\n",
drv->trans->info.hw_rev); return -EINVAL;
}
struct fw_img_parsing { struct fw_sec *sec; int sec_counter;
};
/* * struct fw_sec_parsing: to extract fw section and it's offset from tlv
*/ struct fw_sec_parsing {
__le32 offset; const u8 data[];
} __packed;
/** * struct iwl_tlv_calib_data - parse the default calib data from TLV * * @ucode_type: the uCode to which the following default calib relates. * @calib: default calibrations.
*/ struct iwl_tlv_calib_data {
__le32 ucode_type; struct iwl_tlv_calib_ctrl calib;
} __packed;
if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_API, 32)) {
IWL_WARN(drv, "api flags index %d larger than supported by driver\n",
api_index); return;
}
for (i = 0; i < 32; i++) { if (api_flags & BIT(i))
__set_bit(i + 32 * api_index, capa->_api);
}
}
if (api_index >= DIV_ROUND_UP(NUM_IWL_UCODE_TLV_CAPA, 32)) {
IWL_WARN(drv, "capa flags index %d larger than supported by driver\n",
api_index); return;
}
for (i = 0; i < 32; i++) { if (api_flags & BIT(i))
__set_bit(i + 32 * api_index, capa->_capa);
}
}
pieces->dbg_dest_ver = (const u8 *)tlv_data; if (*pieces->dbg_dest_ver == 1) {
dest = (constvoid *)tlv_data;
} elseif (*pieces->dbg_dest_ver == 0) {
dest_v1 = (constvoid *)tlv_data;
} else {
IWL_ERR(drv, "The version is %d, and it is invalid\n",
*pieces->dbg_dest_ver); break;
}
if (paging_mem_size > MAX_PAGING_IMAGE_SIZE) {
IWL_ERR(drv, "Paging: driver supports up to %lu bytes for paging image\n",
MAX_PAGING_IMAGE_SIZE); return -EINVAL;
}
if (tlv_len != sizeof(*fseq_ver)) goto invalid_tlv_len;
IWL_DEBUG_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
fseq_ver->version);
} break; case IWL_UCODE_TLV_FW_NUM_STATIONS: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; if (le32_to_cpup((const __le32 *)tlv_data) >
IWL_STATION_COUNT_MAX) {
IWL_ERR(drv, "%d is an invalid number of station\n",
le32_to_cpup((const __le32 *)tlv_data)); goto tlv_error;
}
capa->num_stations =
le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_FW_NUM_LINKS: if (tlv_len != sizeof(u32)) goto invalid_tlv_len; if (le32_to_cpup((const __le32 *)tlv_data) >
IWL_FW_MAX_LINK_ID + 1) {
IWL_ERR(drv, "%d is an invalid number of links\n",
le32_to_cpup((const __le32 *)tlv_data)); goto tlv_error;
}
capa->num_links =
le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_FW_NUM_BEACONS: if (tlv_len != sizeof(u32)) goto invalid_tlv_len;
capa->num_beacons =
le32_to_cpup((const __le32 *)tlv_data); break; case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: { conststruct iwl_umac_debug_addrs *dbg_ptrs =
(constvoid *)tlv_data;
if (tlv_len != sizeof(*dbg_ptrs)) goto invalid_tlv_len; if (drv->trans->mac_cfg->device_family <
IWL_DEVICE_FAMILY_22000) break;
drv->trans->dbg.umac_error_event_table =
le32_to_cpu(dbg_ptrs->error_info_addr) &
~FW_ADDR_CACHE_CONTROL;
drv->trans->dbg.error_event_table_tlv_status |=
IWL_ERROR_EVENT_TABLE_UMAC; break;
} case IWL_UCODE_TLV_LMAC_DEBUG_ADDRS: { conststruct iwl_lmac_debug_addrs *dbg_ptrs =
(constvoid *)tlv_data;
if (tlv_len != sizeof(*dbg_ptrs)) goto invalid_tlv_len; if (drv->trans->mac_cfg->device_family <
IWL_DEVICE_FAMILY_22000) break;
drv->trans->dbg.lmac_error_event_table[0] =
le32_to_cpu(dbg_ptrs->error_event_table_ptr) &
~FW_ADDR_CACHE_CONTROL;
drv->trans->dbg.error_event_table_tlv_status |=
IWL_ERROR_EVENT_TABLE_LMAC1; break;
} case IWL_UCODE_TLV_TYPE_REGIONS:
iwl_parse_dbg_tlv_assert_tables(drv, tlv);
fallthrough; case IWL_UCODE_TLV_TYPE_DEBUG_INFO: case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: case IWL_UCODE_TLV_TYPE_HCMD: case IWL_UCODE_TLV_TYPE_TRIGGERS: case IWL_UCODE_TLV_TYPE_CONF_SET: if (iwlwifi_mod_params.enable_ini)
iwl_dbg_tlv_alloc(drv->trans, tlv, false); break; case IWL_UCODE_TLV_CMD_VERSIONS: if (tlv_len % sizeof(struct iwl_fw_cmd_version)) {
IWL_ERR(drv, "Invalid length for command versions: %u\n",
tlv_len);
tlv_len /= sizeof(struct iwl_fw_cmd_version);
tlv_len *= sizeof(struct iwl_fw_cmd_version);
} if (WARN_ON(capa->cmd_versions)) return -EINVAL;
capa->cmd_versions = kmemdup(tlv_data, tlv_len,
GFP_KERNEL); if (!capa->cmd_versions) return -ENOMEM;
capa->n_cmd_versions =
tlv_len / sizeof(struct iwl_fw_cmd_version); break; case IWL_UCODE_TLV_PHY_INTEGRATION_VERSION: if (drv->fw.phy_integration_ver) {
IWL_ERR(drv, "phy integration str ignored, already exists\n"); break;
}
drv->fw.phy_integration_ver =
kmemdup(tlv_data, tlv_len, GFP_KERNEL); if (!drv->fw.phy_integration_ver) return -ENOMEM;
drv->fw.phy_integration_ver_len = tlv_len; break; case IWL_UCODE_TLV_SEC_TABLE_ADDR: case IWL_UCODE_TLV_D3_KEK_KCK_ADDR:
iwl_drv_set_dump_exclude(drv, tlv_type,
tlv_data, tlv_len); break; case IWL_UCODE_TLV_CURRENT_PC: if (tlv_len < sizeof(struct iwl_pc_data)) goto invalid_tlv_len;
drv->trans->dbg.pc_data =
kmemdup(tlv_data, tlv_len, GFP_KERNEL); if (!drv->trans->dbg.pc_data) return -ENOMEM;
drv->trans->dbg.num_pc =
tlv_len / sizeof(struct iwl_pc_data); break; case IWL_UCODE_TLV_PNVM_DATA: if (drv->fw.pnvm_data) break;
drv->fw.pnvm_data =
kvmemdup(tlv_data, tlv_len, GFP_KERNEL); if (!drv->fw.pnvm_data) return -ENOMEM;
drv->fw.pnvm_size = tlv_len; break; default:
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type); break;
}
}
if (!fw_has_capa(capa, IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED) &&
usniffer_req && !*usniffer_images) {
IWL_ERR(drv, "user selected to work with usniffer but usniffer image isn't available in ucode package\n"); return -EINVAL;
}
if (len) {
IWL_ERR(drv, "invalid TLV after parsing: %zd\n", len);
iwl_print_hex_dump(drv, IWL_DL_FW, data, len); return -EINVAL;
}
/* Verify that uCode images will fit in card's SRAM. */ if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_INST) >
cfg->max_inst_size) {
IWL_ERR(drv, "uCode instr len %zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
IWL_UCODE_SECTION_INST)); return -1;
}
if (get_sec_size(pieces, IWL_UCODE_REGULAR, IWL_UCODE_SECTION_DATA) >
cfg->max_data_size) {
IWL_ERR(drv, "uCode data len %zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
IWL_UCODE_SECTION_DATA)); return -1;
}
if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_INST) >
cfg->max_inst_size) {
IWL_ERR(drv, "uCode init instr len %zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_INIT,
IWL_UCODE_SECTION_INST)); return -1;
}
if (get_sec_size(pieces, IWL_UCODE_INIT, IWL_UCODE_SECTION_DATA) >
cfg->max_data_size) {
IWL_ERR(drv, "uCode init data len %zd too large to fit in\n",
get_sec_size(pieces, IWL_UCODE_REGULAR,
IWL_UCODE_SECTION_DATA)); return -1;
} return 0;
}
staticvoid _iwl_op_mode_stop(struct iwl_drv *drv)
{ /* also protects start/stop from racing against each other */
lockdep_assert_held(&iwlwifi_opmode_table_mtx);
/* op_mode can be NULL if its start failed */ if (drv->op_mode) {
iwl_op_mode_stop(drv->op_mode);
drv->op_mode = NULL;
if (fw_has_api(&drv->fw.ucode_capa, IWL_UCODE_TLV_API_NEW_VERSION))
api_ver = drv->fw.ucode_ver; else
api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
/* * api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely * on the API version read from firmware header from here on forward
*/ if (api_ver < api_min || api_ver > api_max) {
IWL_ERR(drv, "Driver unable to support your firmware API. " "Driver supports v%u, firmware is v%u.\n",
api_max, api_ver); goto try_again;
}
/* * In mvm uCode there is no difference between data and instructions * sections.
*/ if (fw->type == IWL_FW_DVM && validate_sec_sizes(drv, pieces,
drv->trans->cfg)) goto try_again;
/* Allocate ucode buffers for card's bus-master loading ... */
/* Runtime instructions and 2 copies of data: * 1) unmodified from disk * 2) backup cache for save/restore during power-downs
*/ for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) if (iwl_alloc_ucode(drv, pieces, i)) goto out_free_fw;
/* In version 1 of the destination tlv, which is * relevant for internal buffer exclusively, * the base address is part of given with the length * of the buffer, and the size shift is give instead of * end shift. We now store these values in base_reg, * and end shift, and when dumping the data we'll * manipulate it for extracting both the length and
* base address */
dest_tlv->base_reg = pieces->dbg_dest_tlv->cfg_reg;
dest_tlv->end_shift =
pieces->dbg_dest_tlv->size_shift;
}
}
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg.conf_tlv); i++) { if (pieces->dbg_conf_tlv[i]) {
drv->fw.dbg.conf_tlv[i] =
kmemdup(pieces->dbg_conf_tlv[i],
pieces->dbg_conf_tlv_len[i],
GFP_KERNEL); if (!drv->fw.dbg.conf_tlv[i]) goto out_free_fw;
}
}
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg.trigger_tlv); i++) { if (pieces->dbg_trigger_tlv[i]) { /* * If the trigger isn't long enough, WARN and exit. * Someone is trying to debug something and he won't * be able to catch the bug he is trying to chase. * We'd better be noisy to be sure he knows what's * going on.
*/ if (WARN_ON(pieces->dbg_trigger_tlv_len[i] <
(trigger_tlv_sz[i] + sizeof(struct iwl_fw_dbg_trigger_tlv)))) goto out_free_fw;
drv->fw.dbg.trigger_tlv_len[i] =
pieces->dbg_trigger_tlv_len[i];
drv->fw.dbg.trigger_tlv[i] =
kmemdup(pieces->dbg_trigger_tlv[i],
drv->fw.dbg.trigger_tlv_len[i],
GFP_KERNEL); if (!drv->fw.dbg.trigger_tlv[i]) goto out_free_fw;
}
}
/* Now that we can no longer fail, copy information */
/* * The (size - 16) / 12 formula is based on the information recorded * for each event, which is of mode 1 (including timestamp) for all * new microcodes that include this information.
*/
fw->init_evtlog_ptr = pieces->init_evtlog_ptr; if (pieces->init_evtlog_size)
fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12; else
fw->init_evtlog_size =
drv->trans->mac_cfg->base->max_event_log_size;
fw->init_errlog_ptr = pieces->init_errlog_ptr;
fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr; if (pieces->inst_evtlog_size)
fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12; else
fw->inst_evtlog_size =
drv->trans->mac_cfg->base->max_event_log_size;
fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
/* * figure out the offset of chain noise reset and gain commands * base on the size of standard phy calibration commands table size
*/ if (fw->ucode_capa.standard_phy_calibration_size >
IWL_MAX_PHY_CALIBRATE_TBL_SIZE)
fw->ucode_capa.standard_phy_calibration_size =
IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE;
/* We have our copies now, allow OS release its copies */
release_firmware(ucode_raw);
mutex_lock(&iwlwifi_opmode_table_mtx); switch (fw->type) { case IWL_FW_DVM:
op = &iwlwifi_opmode_table[DVM_OP_MODE]; break; default:
WARN(1, "Invalid fw type %d\n", fw->type);
fallthrough; case IWL_FW_MVM:
op = &iwlwifi_opmode_table[MVM_OP_MODE]; break;
}
#if IS_ENABLED(CONFIG_IWLMLD) if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION &&
iwl_drv_is_wifi7_supported(drv->trans))
op = &iwlwifi_opmode_table[MLD_OP_MODE]; #else if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION &&
iwl_drv_is_wifi7_supported(drv->trans)) {
IWL_ERR(drv, "IWLMLD needs to be compiled to support this firmware\n");
mutex_unlock(&iwlwifi_opmode_table_mtx); goto out_unbind;
} #endif
IWL_INFO(drv, "loaded firmware version %s op_mode %s\n",
drv->fw.fw_version, op->name);
/* add this device to the list of devices using this op_mode */
list_add_tail(&drv->list, &op->drv);
if (op->ops) {
drv->op_mode = _iwl_op_mode_start(drv, op);
try_again: /* try next, if any */
release_firmware(ucode_raw); if (iwl_request_firmware(drv, false)) goto out_unbind; goto free;
out_free_fw:
release_firmware(ucode_raw);
out_unbind:
complete(&drv->request_firmware_complete);
device_release_driver(drv->trans->dev); /* drv has just been freed by the release */
failure = false;
free: if (failure)
iwl_dealloc_ucode(drv);
if (pieces) { for (i = 0; i < ARRAY_SIZE(pieces->img); i++)
kfree(pieces->img[i].sec);
kfree(pieces->dbg_mem_tlv);
kfree(pieces);
}
}
/* Create transport layer debugfs dir */
drv->trans->dbgfs_dir = debugfs_create_dir("trans", drv->dbgfs_drv); #endif
drv->trans->dbg.domains_bitmap = IWL_TRANS_FW_DBG_DOMAIN(drv->trans); if (iwlwifi_mod_params.enable_ini != ENABLE_INI) { /* We have a non-default value in the module parameter, * take its value
*/
drv->trans->dbg.domains_bitmap &= 0xffff; if (iwlwifi_mod_params.enable_ini != IWL_FW_INI_PRESET_DISABLE) { if (iwlwifi_mod_params.enable_ini > ENABLE_INI) {
IWL_ERR(trans, "invalid enable_ini module parameter value: max = %d, using 0 instead\n",
ENABLE_INI);
iwlwifi_mod_params.enable_ini = 0;
}
drv->trans->dbg.domains_bitmap =
BIT(IWL_FW_DBG_DOMAIN_POS + iwlwifi_mod_params.enable_ini);
}
}
ret = iwl_request_firmware(drv, true); if (ret) {
IWL_ERR(trans, "Couldn't request the fw\n"); goto err_fw;
}
/* * List is empty (this item wasn't added) * when firmware loading failed -- in that * case we can't remove it from any list.
*/ if (!list_empty(&drv->list))
list_del(&drv->list);
mutex_unlock(&iwlwifi_opmode_table_mtx);
int iwl_opmode_register(constchar *name, conststruct iwl_op_mode_ops *ops)
{ int i; struct iwl_drv *drv; struct iwlwifi_opmode_table *op;
mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) {
op = &iwlwifi_opmode_table[i]; if (strcmp(op->name, name)) continue;
op->ops = ops; /* TODO: need to handle exceptional case */
list_for_each_entry(drv, &op->drv, list)
drv->op_mode = _iwl_op_mode_start(drv, op);
void iwl_opmode_deregister(constchar *name)
{ int i; struct iwl_drv *drv;
mutex_lock(&iwlwifi_opmode_table_mtx); for (i = 0; i < ARRAY_SIZE(iwlwifi_opmode_table); i++) { if (strcmp(iwlwifi_opmode_table[i].name, name)) continue;
iwlwifi_opmode_table[i].ops = NULL;
/* call the stop routine for all devices */
list_for_each_entry(drv, &iwlwifi_opmode_table[i].drv, list)
_iwl_op_mode_stop(drv);
module_param_named(enable_ini, iwlwifi_mod_params.enable_ini, uint, 0444);
MODULE_PARM_DESC(enable_ini, "0:disable, 1-15:FW_DBG_PRESET Values, 16:enabled without preset value defined," "Debug INI TLV FW debug infrastructure (default: 16)");
/* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the * priority line in the PCIx). * set bt_coex_active to false, uCode will ignore the BT activity and * perform the normal operation * * User might experience transmit issue on some platform due to WiFi/BT * co-exist problem. The possible behaviors are: * Able to scan and finding all the available AP * Not able to associate with any AP * On those platforms, WiFi communication can be restored by set * "bt_coex_active" module parameter to "false" * * default: bt_coex_active = true (BT_COEX_ENABLE)
*/
module_param_named(bt_coex_active, iwlwifi_mod_params.bt_coex_active, bool, 0444);
MODULE_PARM_DESC(bt_coex_active, "enable wifi/bt co-exist (default: enable)");
module_param_named(remove_when_gone,
iwlwifi_mod_params.remove_when_gone, bool,
0444);
MODULE_PARM_DESC(remove_when_gone, "Remove dev from PCIe bus if it is deemed inaccessible (default: false)");
module_param_named(disable_11ax, iwlwifi_mod_params.disable_11ax, bool,
S_IRUGO);
MODULE_PARM_DESC(disable_11ax, "Disable HE capabilities (default: false)");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.