|
@@ -184,6 +184,11 @@ static void iwl_free_fw_img(struct iwl_drv *drv, struct fw_img *img)
|
|
|
static void iwl_dealloc_ucode(struct iwl_drv *drv)
|
|
|
{
|
|
|
int i;
|
|
|
+
|
|
|
+ kfree(drv->fw.dbg_dest_tlv);
|
|
|
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
|
|
|
+ kfree(drv->fw.dbg_conf_tlv[i]);
|
|
|
+
|
|
|
for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
|
|
|
iwl_free_fw_img(drv, drv->fw.img + i);
|
|
|
}
|
|
@@ -308,6 +313,11 @@ struct iwl_firmware_pieces {
|
|
|
|
|
|
u32 init_evtlog_ptr, init_evtlog_size, init_errlog_ptr;
|
|
|
u32 inst_evtlog_ptr, inst_evtlog_size, inst_errlog_ptr;
|
|
|
+
|
|
|
+ /* FW debug data parsed for driver usage */
|
|
|
+ struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
|
|
|
+ struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX];
|
|
|
+ size_t dbg_conf_tlv_len[FW_DBG_MAX];
|
|
|
};
|
|
|
|
|
|
/*
|
|
@@ -853,6 +863,58 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
|
|
|
capa->n_scan_channels =
|
|
|
le32_to_cpup((__le32 *)tlv_data);
|
|
|
break;
|
|
|
+ case IWL_UCODE_TLV_FW_DBG_DEST: {
|
|
|
+ struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
|
|
|
+
|
|
|
+ if (pieces->dbg_dest_tlv) {
|
|
|
+ IWL_ERR(drv,
|
|
|
+ "dbg destination ignored, already exists\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ pieces->dbg_dest_tlv = dest;
|
|
|
+ IWL_INFO(drv, "Found debug destination: %s\n",
|
|
|
+ get_fw_dbg_mode_string(dest->monitor_mode));
|
|
|
+
|
|
|
+ drv->fw.dbg_dest_reg_num =
|
|
|
+ tlv_len - offsetof(struct iwl_fw_dbg_dest_tlv,
|
|
|
+ reg_ops);
|
|
|
+ drv->fw.dbg_dest_reg_num /=
|
|
|
+ sizeof(drv->fw.dbg_dest_tlv->reg_ops[0]);
|
|
|
+
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case IWL_UCODE_TLV_FW_DBG_CONF: {
|
|
|
+ struct iwl_fw_dbg_conf_tlv *conf = (void *)tlv_data;
|
|
|
+
|
|
|
+ if (!pieces->dbg_dest_tlv) {
|
|
|
+ IWL_ERR(drv,
|
|
|
+ "Ignore dbg config %d - no destination configured\n",
|
|
|
+ conf->id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (conf->id >= ARRAY_SIZE(drv->fw.dbg_conf_tlv)) {
|
|
|
+ IWL_ERR(drv,
|
|
|
+ "Skip unknown configuration: %d\n",
|
|
|
+ conf->id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (pieces->dbg_conf_tlv[conf->id]) {
|
|
|
+ IWL_ERR(drv,
|
|
|
+ "Ignore duplicate dbg config %d\n",
|
|
|
+ conf->id);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ IWL_INFO(drv, "Found debug configuration: %d\n",
|
|
|
+ conf->id);
|
|
|
+
|
|
|
+ pieces->dbg_conf_tlv[conf->id] = conf;
|
|
|
+ pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
|
|
|
+ break;
|
|
|
+ }
|
|
|
default:
|
|
|
IWL_DEBUG_INFO(drv, "unknown TLV: %d\n", tlv_type);
|
|
|
break;
|
|
@@ -996,7 +1058,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
struct iwl_ucode_header *ucode;
|
|
|
struct iwlwifi_opmode_table *op;
|
|
|
int err;
|
|
|
- struct iwl_firmware_pieces pieces;
|
|
|
+ struct iwl_firmware_pieces *pieces;
|
|
|
const unsigned int api_max = drv->cfg->ucode_api_max;
|
|
|
unsigned int api_ok = drv->cfg->ucode_api_ok;
|
|
|
const unsigned int api_min = drv->cfg->ucode_api_min;
|
|
@@ -1013,7 +1075,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
if (!api_ok)
|
|
|
api_ok = api_max;
|
|
|
|
|
|
- memset(&pieces, 0, sizeof(pieces));
|
|
|
+ pieces = kzalloc(sizeof(*pieces), GFP_KERNEL);
|
|
|
+ if (!pieces)
|
|
|
+ return;
|
|
|
|
|
|
if (!ucode_raw) {
|
|
|
if (drv->fw_index <= api_ok)
|
|
@@ -1036,10 +1100,10 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
ucode = (struct iwl_ucode_header *)ucode_raw->data;
|
|
|
|
|
|
if (ucode->ver)
|
|
|
- err = iwl_parse_v1_v2_firmware(drv, ucode_raw, &pieces);
|
|
|
+ err = iwl_parse_v1_v2_firmware(drv, ucode_raw, pieces);
|
|
|
else
|
|
|
- err = iwl_parse_tlv_firmware(drv, ucode_raw, &pieces,
|
|
|
- &fw->ucode_capa);
|
|
|
+ err = iwl_parse_tlv_firmware(drv, ucode_raw, pieces,
|
|
|
+ &fw->ucode_capa);
|
|
|
|
|
|
if (err)
|
|
|
goto try_again;
|
|
@@ -1079,7 +1143,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
* In mvm uCode there is no difference between data and instructions
|
|
|
* sections.
|
|
|
*/
|
|
|
- if (!fw->mvm_fw && validate_sec_sizes(drv, &pieces, drv->cfg))
|
|
|
+ if (!fw->mvm_fw && validate_sec_sizes(drv, pieces, drv->cfg))
|
|
|
goto try_again;
|
|
|
|
|
|
/* Allocate ucode buffers for card's bus-master loading ... */
|
|
@@ -1088,9 +1152,33 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
* 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))
|
|
|
+ if (iwl_alloc_ucode(drv, pieces, i))
|
|
|
goto out_free_fw;
|
|
|
|
|
|
+ if (pieces->dbg_dest_tlv) {
|
|
|
+ drv->fw.dbg_dest_tlv =
|
|
|
+ kmemdup(pieces->dbg_dest_tlv,
|
|
|
+ sizeof(*pieces->dbg_dest_tlv) +
|
|
|
+ sizeof(pieces->dbg_dest_tlv->reg_ops[0]) *
|
|
|
+ drv->fw.dbg_dest_reg_num, GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!drv->fw.dbg_dest_tlv)
|
|
|
+ goto out_free_fw;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) {
|
|
|
+ if (pieces->dbg_conf_tlv[i]) {
|
|
|
+ drv->fw.dbg_conf_tlv_len[i] =
|
|
|
+ pieces->dbg_conf_tlv_len[i];
|
|
|
+ drv->fw.dbg_conf_tlv[i] =
|
|
|
+ kmemdup(pieces->dbg_conf_tlv[i],
|
|
|
+ drv->fw.dbg_conf_tlv_len[i],
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!drv->fw.dbg_conf_tlv[i])
|
|
|
+ goto out_free_fw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/* Now that we can no longer fail, copy information */
|
|
|
|
|
|
/*
|
|
@@ -1098,20 +1186,20 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
* 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;
|
|
|
+ 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->cfg->base_params->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;
|
|
|
+ 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->cfg->base_params->max_event_log_size;
|
|
|
- fw->inst_errlog_ptr = pieces.inst_errlog_ptr;
|
|
|
+ fw->inst_errlog_ptr = pieces->inst_errlog_ptr;
|
|
|
|
|
|
/*
|
|
|
* figure out the offset of chain noise reset and gain commands
|
|
@@ -1213,10 +1301,12 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
iwl_req_fw_callback(ucode_raw, context);
|
|
|
}
|
|
|
|
|
|
+ kfree(pieces);
|
|
|
return;
|
|
|
|
|
|
try_again:
|
|
|
/* try next, if any */
|
|
|
+ kfree(pieces);
|
|
|
release_firmware(ucode_raw);
|
|
|
if (iwl_request_firmware(drv, false))
|
|
|
goto out_unbind;
|
|
@@ -1227,6 +1317,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|
|
iwl_dealloc_ucode(drv);
|
|
|
release_firmware(ucode_raw);
|
|
|
out_unbind:
|
|
|
+ kfree(pieces);
|
|
|
complete(&drv->request_firmware_complete);
|
|
|
device_release_driver(drv->trans->dev);
|
|
|
}
|