aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-01-29 07:58:06 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2015-03-02 01:20:28 -0500
commitd2709ad723ff2ae22013978ed6ec29cf1b9b8332 (patch)
tree057dadfcb68f72846b90f2112bfe3bee205c24f9
parentef17708e174fdc1ed5d10887ec2f3fc6f6c98d69 (diff)
iwlwifi: mvm: add framework for triggers for fw dump
Most of the time, the issues we want to debug with the firmware dump mechanism are transient. It is then very hard to stop the recording on time and get meaningful data. In order to solve this, I add here an infrastucture of triggers. The user will supply a list of triggers that will start / stop the recording. We have two types of triggers: start and stop. Start triggers can start a specific configuration. The stop triggers will be able to kick the collection of the data with the currently running configuration. These triggers are given to the driver by the .ucode file - just like the configuration. In the next patches, I'll add triggers in the code. Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-drv.c46
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h18
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw-file.h88
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-fw.h53
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h4
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/debugfs.c6
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw.c37
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mac80211.c5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h59
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c7
10 files changed, 243 insertions, 80 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-drv.c b/drivers/net/wireless/iwlwifi/iwl-drv.c
index b608114b6b9e..141331d41abf 100644
--- a/drivers/net/wireless/iwlwifi/iwl-drv.c
+++ b/drivers/net/wireless/iwlwifi/iwl-drv.c
@@ -175,6 +175,8 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
175 kfree(drv->fw.dbg_dest_tlv); 175 kfree(drv->fw.dbg_dest_tlv);
176 for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) 176 for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
177 kfree(drv->fw.dbg_conf_tlv[i]); 177 kfree(drv->fw.dbg_conf_tlv[i]);
178 for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++)
179 kfree(drv->fw.dbg_trigger_tlv[i]);
178 180
179 for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) 181 for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
180 iwl_free_fw_img(drv, drv->fw.img + i); 182 iwl_free_fw_img(drv, drv->fw.img + i);
@@ -293,8 +295,10 @@ struct iwl_firmware_pieces {
293 295
294 /* FW debug data parsed for driver usage */ 296 /* FW debug data parsed for driver usage */
295 struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; 297 struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
296 struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; 298 struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
297 size_t dbg_conf_tlv_len[FW_DBG_MAX]; 299 size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
300 struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
301 size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
298}; 302};
299 303
300/* 304/*
@@ -914,6 +918,31 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
914 pieces->dbg_conf_tlv_len[conf->id] = tlv_len; 918 pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
915 break; 919 break;
916 } 920 }
921 case IWL_UCODE_TLV_FW_DBG_TRIGGER: {
922 struct iwl_fw_dbg_trigger_tlv *trigger =
923 (void *)tlv_data;
924 u32 trigger_id = le32_to_cpu(trigger->id);
925
926 if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) {
927 IWL_ERR(drv,
928 "Skip unknown trigger: %u\n",
929 trigger->id);
930 break;
931 }
932
933 if (pieces->dbg_trigger_tlv[trigger_id]) {
934 IWL_ERR(drv,
935 "Ignore duplicate dbg trigger %u\n",
936 trigger->id);
937 break;
938 }
939
940 IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id);
941
942 pieces->dbg_trigger_tlv[trigger_id] = trigger;
943 pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len;
944 break;
945 }
917 case IWL_UCODE_TLV_SEC_RT_USNIFFER: 946 case IWL_UCODE_TLV_SEC_RT_USNIFFER:
918 usniffer_images = true; 947 usniffer_images = true;
919 iwl_store_ucode_sec(pieces, tlv_data, 948 iwl_store_ucode_sec(pieces, tlv_data,
@@ -1198,6 +1227,19 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
1198 } 1227 }
1199 } 1228 }
1200 1229
1230 for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
1231 if (pieces->dbg_trigger_tlv[i]) {
1232 drv->fw.dbg_trigger_tlv_len[i] =
1233 pieces->dbg_trigger_tlv_len[i];
1234 drv->fw.dbg_trigger_tlv[i] =
1235 kmemdup(pieces->dbg_trigger_tlv[i],
1236 drv->fw.dbg_trigger_tlv_len[i],
1237 GFP_KERNEL);
1238 if (!drv->fw.dbg_trigger_tlv[i])
1239 goto out_free_fw;
1240 }
1241 }
1242
1201 /* Now that we can no longer fail, copy information */ 1243 /* Now that we can no longer fail, copy information */
1202 1244
1203 /* 1245 /*
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
index 919a2548a92c..fabddd8ecd89 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-error-dump.h
@@ -230,4 +230,22 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
230 return (void *)(data->data + le32_to_cpu(data->len)); 230 return (void *)(data->data + le32_to_cpu(data->len));
231} 231}
232 232
233/**
234 * enum iwl_fw_dbg_trigger - triggers available
235 *
236 * @FW_DBG_TRIGGER_USER: trigger log collection by user
237 * This should not be defined as a trigger to the driver, but a value the
238 * driver should set to indicate that the trigger was initiated by the
239 * user.
240 * @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts
241 */
242enum iwl_fw_dbg_trigger {
243 FW_DBG_TRIGGER_INVALID = 0,
244 FW_DBG_TRIGGER_USER,
245 FW_DBG_TRIGGER_FW_ASSERT,
246
247 /* must be last */
248 FW_DBG_TRIGGER_MAX,
249};
250
233#endif /* __fw_error_dump_h__ */ 251#endif /* __fw_error_dump_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
index 59706821830f..d2c4d2121a79 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw-file.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw-file.h
@@ -66,6 +66,7 @@
66#define __iwl_fw_file_h__ 66#define __iwl_fw_file_h__
67 67
68#include <linux/netdevice.h> 68#include <linux/netdevice.h>
69#include <linux/nl80211.h>
69 70
70/* v1/v2 uCode file layout */ 71/* v1/v2 uCode file layout */
71struct iwl_ucode_header { 72struct iwl_ucode_header {
@@ -136,6 +137,7 @@ enum iwl_ucode_tlv_type {
136 IWL_UCODE_TLV_FW_VERSION = 36, 137 IWL_UCODE_TLV_FW_VERSION = 36,
137 IWL_UCODE_TLV_FW_DBG_DEST = 38, 138 IWL_UCODE_TLV_FW_DBG_DEST = 38,
138 IWL_UCODE_TLV_FW_DBG_CONF = 39, 139 IWL_UCODE_TLV_FW_DBG_CONF = 39,
140 IWL_UCODE_TLV_FW_DBG_TRIGGER = 40,
139}; 141};
140 142
141struct iwl_ucode_tlv { 143struct iwl_ucode_tlv {
@@ -458,44 +460,78 @@ struct iwl_fw_dbg_conf_hcmd {
458} __packed; 460} __packed;
459 461
460/** 462/**
461 * struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration 463 * enum iwl_fw_dbg_trigger_mode - triggers functionalities
462 * 464 *
463 * @enabled: is this trigger enabled 465 * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism
464 * @reserved: 466 * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data
465 * @len: length, in bytes, of the %trigger field
466 * @trigger: pointer to a trigger struct
467 */ 467 */
468struct iwl_fw_dbg_trigger { 468enum iwl_fw_dbg_trigger_mode {
469 u8 enabled; 469 IWL_FW_DBG_TRIGGER_START = BIT(0),
470 u8 reserved; 470 IWL_FW_DBG_TRIGGER_STOP = BIT(1),
471 u8 len; 471};
472 u8 trigger[0];
473} __packed;
474 472
475/** 473/**
476 * enum iwl_fw_dbg_conf - configurations available 474 * enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger
477 * 475 * @IWL_FW_DBG_CONF_VIF_ANY: any vif type
478 * @FW_DBG_CUSTOM: take this configuration from alive 476 * @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode
479 * Note that the trigger is NO-OP for this configuration 477 * @IWL_FW_DBG_CONF_VIF_STATION: BSS mode
478 * @IWL_FW_DBG_CONF_VIF_AP: AP mode
479 * @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode
480 * @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode
481 * @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device
480 */ 482 */
481enum iwl_fw_dbg_conf { 483enum iwl_fw_dbg_trigger_vif_type {
482 FW_DBG_CUSTOM = 0, 484 IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED,
483 485 IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC,
484 /* must be last */ 486 IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION,
485 FW_DBG_MAX, 487 IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP,
486 FW_DBG_INVALID = 0xff, 488 IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT,
489 IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO,
490 IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE,
487}; 491};
488 492
489/** 493/**
490 * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration 494 * struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger
491 * 495 * @id: %enum iwl_fw_dbg_trigger
492 * @id: %enum iwl_fw_dbg_conf 496 * @vif_type: %enum iwl_fw_dbg_trigger_vif_type
497 * @stop_conf_ids: bitmap of configurations this trigger relates to.
498 * if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding
499 * to the currently running configuration is set, the data should be
500 * collected.
501 * @stop_delay: how many milliseconds to wait before collecting the data
502 * after the STOP trigger fires.
503 * @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both
504 * @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what
505 * configuration should be applied when the triggers kicks in.
506 * @occurrences: number of occurrences. 0 means the trigger will never fire.
507 */
508struct iwl_fw_dbg_trigger_tlv {
509 __le32 id;
510 __le32 vif_type;
511 __le32 stop_conf_ids;
512 __le32 stop_delay;
513 u8 mode;
514 u8 start_conf_id;
515 __le16 occurrences;
516 __le32 reserved[2];
517
518 u8 data[0];
519} __packed;
520
521#define FW_DBG_START_FROM_ALIVE 0
522#define FW_DBG_CONF_MAX 32
523#define FW_DBG_INVALID 0xff
524
525/**
526 * struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
527 * @id: conf id
493 * @usniffer: should the uSniffer image be used 528 * @usniffer: should the uSniffer image be used
494 * @num_of_hcmds: how many HCMDs to send are present here 529 * @num_of_hcmds: how many HCMDs to send are present here
495 * @hcmd: a variable length host command to be sent to apply the configuration. 530 * @hcmd: a variable length host command to be sent to apply the configuration.
496 * If there is more than one HCMD to send, they will appear one after the 531 * If there is more than one HCMD to send, they will appear one after the
497 * other and be sent in the order that they appear in. 532 * other and be sent in the order that they appear in.
498 * This parses IWL_UCODE_TLV_FW_DBG_CONF 533 * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to
534 * %FW_DBG_CONF_MAX configuration per run.
499 */ 535 */
500struct iwl_fw_dbg_conf_tlv { 536struct iwl_fw_dbg_conf_tlv {
501 u8 id; 537 u8 id;
@@ -503,8 +539,6 @@ struct iwl_fw_dbg_conf_tlv {
503 u8 reserved; 539 u8 reserved;
504 u8 num_of_hcmds; 540 u8 num_of_hcmds;
505 struct iwl_fw_dbg_conf_hcmd hcmd; 541 struct iwl_fw_dbg_conf_hcmd hcmd;
506
507 /* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
508} __packed; 542} __packed;
509 543
510#endif /* __iwl_fw_file_h__ */ 544#endif /* __iwl_fw_file_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h
index ffd785cc67d6..cf75bafae51d 100644
--- a/drivers/net/wireless/iwlwifi/iwl-fw.h
+++ b/drivers/net/wireless/iwlwifi/iwl-fw.h
@@ -68,6 +68,7 @@
68#include <net/mac80211.h> 68#include <net/mac80211.h>
69 69
70#include "iwl-fw-file.h" 70#include "iwl-fw-file.h"
71#include "iwl-fw-error-dump.h"
71 72
72/** 73/**
73 * enum iwl_ucode_type 74 * enum iwl_ucode_type
@@ -157,6 +158,8 @@ struct iwl_fw_cscheme_list {
157 * @dbg_dest_tlv: points to the destination TLV for debug 158 * @dbg_dest_tlv: points to the destination TLV for debug
158 * @dbg_conf_tlv: array of pointers to configuration TLVs for debug 159 * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
159 * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries 160 * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
161 * @dbg_trigger_tlv: array of pointers to triggers TLVs
162 * @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries
160 * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv 163 * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
161 */ 164 */
162struct iwl_fw { 165struct iwl_fw {
@@ -186,9 +189,10 @@ struct iwl_fw {
186 u32 sdio_adma_addr; 189 u32 sdio_adma_addr;
187 190
188 struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; 191 struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
189 struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; 192 struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
190 size_t dbg_conf_tlv_len[FW_DBG_MAX]; 193 size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
191 194 struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
195 size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
192 u8 dbg_dest_reg_num; 196 u8 dbg_dest_reg_num;
193}; 197};
194 198
@@ -206,46 +210,29 @@ static inline const char *get_fw_dbg_mode_string(int mode)
206 } 210 }
207} 211}
208 212
209static inline const struct iwl_fw_dbg_trigger * 213static inline bool
210iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id) 214iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
211{ 215{
212 const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id]; 216 const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
213 u8 *ptr;
214 int i;
215 217
216 if (!conf_tlv) 218 if (!conf_tlv)
217 return NULL;
218
219 ptr = (void *)&conf_tlv->hcmd;
220 for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
221 ptr += sizeof(conf_tlv->hcmd);
222 ptr += le16_to_cpu(conf_tlv->hcmd.len);
223 }
224
225 return (const struct iwl_fw_dbg_trigger *)ptr;
226}
227
228static inline bool
229iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
230{
231 const struct iwl_fw_dbg_trigger *trigger =
232 iwl_fw_dbg_conf_get_trigger(fw, id);
233
234 if (!trigger)
235 return false; 219 return false;
236 220
237 return trigger->enabled; 221 return conf_tlv->usniffer;
238} 222}
239 223
240static inline bool 224#define iwl_fw_dbg_trigger_enabled(fw, id) ({ \
241iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) 225 void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \
242{ 226 unlikely(__dbg_trigger); \
243 const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id]; 227})
244 228
245 if (!conf_tlv) 229static inline struct iwl_fw_dbg_trigger_tlv*
246 return false; 230iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id)
231{
232 if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv)))
233 return NULL;
247 234
248 return conf_tlv->usniffer; 235 return fw->dbg_trigger_tlv[id];
249} 236}
250 237
251#endif /* __iwl_fw_h__ */ 238#endif /* __iwl_fw_h__ */
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index a96bd8db6ceb..542a6810c81c 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -595,6 +595,7 @@ enum iwl_d0i3_mode {
595 * @dflt_pwr_limit: default power limit fetched from the platform (ACPI) 595 * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
596 * @dbg_dest_tlv: points to the destination TLV for debug 596 * @dbg_dest_tlv: points to the destination TLV for debug
597 * @dbg_conf_tlv: array of pointers to configuration TLVs for debug 597 * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
598 * @dbg_trigger_tlv: array of pointers to triggers TLVs for debug
598 * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv 599 * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
599 */ 600 */
600struct iwl_trans { 601struct iwl_trans {
@@ -628,7 +629,8 @@ struct iwl_trans {
628 u64 dflt_pwr_limit; 629 u64 dflt_pwr_limit;
629 630
630 const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; 631 const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
631 const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; 632 const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
633 struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
632 u8 dbg_dest_reg_num; 634 u8 dbg_dest_reg_num;
633 635
634 enum iwl_d0i3_mode d0i3_mode; 636 enum iwl_d0i3_mode d0i3_mode;
diff --git a/drivers/net/wireless/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
index 82c09d86af8c..f890d5e4673f 100644
--- a/drivers/net/wireless/iwlwifi/mvm/debugfs.c
+++ b/drivers/net/wireless/iwlwifi/mvm/debugfs.c
@@ -942,7 +942,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
942 size_t count, loff_t *ppos) 942 size_t count, loff_t *ppos)
943{ 943{
944 struct iwl_mvm *mvm = file->private_data; 944 struct iwl_mvm *mvm = file->private_data;
945 enum iwl_fw_dbg_conf conf; 945 int conf;
946 char buf[8]; 946 char buf[8];
947 const size_t bufsz = sizeof(buf); 947 const size_t bufsz = sizeof(buf);
948 int pos = 0; 948 int pos = 0;
@@ -966,7 +966,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
966 if (ret) 966 if (ret)
967 return ret; 967 return ret;
968 968
969 if (WARN_ON(conf_id >= FW_DBG_MAX)) 969 if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))
970 return -EINVAL; 970 return -EINVAL;
971 971
972 mutex_lock(&mvm->mutex); 972 mutex_lock(&mvm->mutex);
@@ -985,7 +985,7 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
985 if (ret) 985 if (ret)
986 return ret; 986 return ret;
987 987
988 iwl_mvm_fw_dbg_collect(mvm); 988 iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, 0);
989 989
990 iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); 990 iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
991 991
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw.c b/drivers/net/wireless/iwlwifi/mvm/fw.c
index 7426bb0811be..8d684d5da964 100644
--- a/drivers/net/wireless/iwlwifi/mvm/fw.c
+++ b/drivers/net/wireless/iwlwifi/mvm/fw.c
@@ -217,8 +217,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
217 struct iwl_sf_region st_fwrd_space; 217 struct iwl_sf_region st_fwrd_space;
218 218
219 if (ucode_type == IWL_UCODE_REGULAR && 219 if (ucode_type == IWL_UCODE_REGULAR &&
220 iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) && 220 iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
221 iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
222 fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER); 221 fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
223 else 222 else
224 fw = iwl_get_ucode_image(mvm, ucode_type); 223 fw = iwl_get_ucode_image(mvm, ucode_type);
@@ -480,8 +479,14 @@ exit:
480 iwl_free_resp(&cmd); 479 iwl_free_resp(&cmd);
481} 480}
482 481
483void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm) 482int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
483 unsigned int delay)
484{ 484{
485 if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
486 return -EBUSY;
487
488 IWL_WARN(mvm, "Collecting data: trigger %d fired.\n", trig);
489
485 /* stop recording */ 490 /* stop recording */
486 if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { 491 if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
487 iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); 492 iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
@@ -491,10 +496,30 @@ void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm)
491 udelay(100); 496 udelay(100);
492 } 497 }
493 498
494 schedule_work(&mvm->fw_error_dump_wk); 499 queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
500
501 return 0;
502}
503
504int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
505 struct iwl_fw_dbg_trigger_tlv *trigger)
506{
507 unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
508 u16 occurrences = le16_to_cpu(trigger->occurrences);
509 int ret;
510
511 if (!occurrences)
512 return 0;
513
514 ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), delay);
515 if (ret)
516 return ret;
517
518 trigger->occurrences = cpu_to_le16(occurrences - 1);
519 return 0;
495} 520}
496 521
497int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id) 522int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
498{ 523{
499 u8 *ptr; 524 u8 *ptr;
500 int ret; 525 int ret;
@@ -613,7 +638,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
613 IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); 638 IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
614 639
615 mvm->fw_dbg_conf = FW_DBG_INVALID; 640 mvm->fw_dbg_conf = FW_DBG_INVALID;
616 iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM); 641 iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);
617 642
618 ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); 643 ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
619 if (ret) 644 if (ret)
diff --git a/drivers/net/wireless/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
index ce5a5ff06d0f..a5261a4e7e11 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mac80211.c
+++ b/drivers/net/wireless/iwlwifi/mvm/mac80211.c
@@ -1038,6 +1038,8 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
1038 1038
1039 dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, 1039 dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
1040 GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); 1040 GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
1041
1042 clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
1041} 1043}
1042 1044
1043static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) 1045static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
@@ -1088,6 +1090,7 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
1088 1090
1089 mvm->vif_count = 0; 1091 mvm->vif_count = 0;
1090 mvm->rx_ba_sessions = 0; 1092 mvm->rx_ba_sessions = 0;
1093 mvm->fw_dbg_conf = FW_DBG_INVALID;
1091 1094
1092 /* keep statistics ticking */ 1095 /* keep statistics ticking */
1093 iwl_mvm_accu_radio_stats(mvm); 1096 iwl_mvm_accu_radio_stats(mvm);
@@ -1257,7 +1260,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
1257 1260
1258 flush_work(&mvm->d0i3_exit_work); 1261 flush_work(&mvm->d0i3_exit_work);
1259 flush_work(&mvm->async_handlers_wk); 1262 flush_work(&mvm->async_handlers_wk);
1260 flush_work(&mvm->fw_error_dump_wk); 1263 cancel_delayed_work_sync(&mvm->fw_dump_wk);
1261 1264
1262 mutex_lock(&mvm->mutex); 1265 mutex_lock(&mvm->mutex);
1263 __iwl_mvm_mac_stop(mvm); 1266 __iwl_mvm_mac_stop(mvm);
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h
index 885d09692fb6..4068139efb54 100644
--- a/drivers/net/wireless/iwlwifi/mvm/mvm.h
+++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h
@@ -75,6 +75,7 @@
75#include "iwl-trans.h" 75#include "iwl-trans.h"
76#include "iwl-notif-wait.h" 76#include "iwl-notif-wait.h"
77#include "iwl-eeprom-parse.h" 77#include "iwl-eeprom-parse.h"
78#include "iwl-fw-file.h"
78#include "sta.h" 79#include "sta.h"
79#include "fw-api.h" 80#include "fw-api.h"
80#include "constants.h" 81#include "constants.h"
@@ -703,8 +704,8 @@ struct iwl_mvm {
703 704
704 /* -1 for always, 0 for never, >0 for that many times */ 705 /* -1 for always, 0 for never, >0 for that many times */
705 s8 restart_fw; 706 s8 restart_fw;
706 struct work_struct fw_error_dump_wk; 707 u8 fw_dbg_conf;
707 enum iwl_fw_dbg_conf fw_dbg_conf; 708 struct delayed_work fw_dump_wk;
708 709
709#ifdef CONFIG_IWLWIFI_LEDS 710#ifdef CONFIG_IWLWIFI_LEDS
710 struct led_classdev led; 711 struct led_classdev led;
@@ -840,6 +841,7 @@ enum iwl_mvm_status {
840 IWL_MVM_STATUS_IN_D0I3, 841 IWL_MVM_STATUS_IN_D0I3,
841 IWL_MVM_STATUS_ROC_AUX_RUNNING, 842 IWL_MVM_STATUS_ROC_AUX_RUNNING,
842 IWL_MVM_STATUS_D3_RECONFIG, 843 IWL_MVM_STATUS_D3_RECONFIG,
844 IWL_MVM_STATUS_DUMPING_FW_LOG,
843}; 845};
844 846
845static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) 847static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
@@ -1411,7 +1413,56 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
1411void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); 1413void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
1412void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); 1414void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
1413 1415
1414int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf id); 1416int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
1415void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm); 1417int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
1418 unsigned int delay);
1419int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
1420 struct iwl_fw_dbg_trigger_tlv *trigger);
1421
1422static inline bool
1423iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
1424 struct ieee80211_vif *vif)
1425{
1426 u32 trig_vif = le32_to_cpu(trig->vif_type);
1427
1428 return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
1429}
1430
1431static inline bool
1432iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
1433 struct iwl_fw_dbg_trigger_tlv *trig)
1434{
1435 return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
1436 (mvm->fw_dbg_conf == FW_DBG_INVALID ||
1437 (BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
1438}
1439
1440static inline bool
1441iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
1442 struct ieee80211_vif *vif,
1443 struct iwl_fw_dbg_trigger_tlv *trig)
1444{
1445 if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
1446 return false;
1447
1448 return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
1449}
1450
1451static inline void
1452iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
1453 struct ieee80211_vif *vif,
1454 enum iwl_fw_dbg_trigger trig)
1455{
1456 struct iwl_fw_dbg_trigger_tlv *trigger;
1457
1458 if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig))
1459 return;
1460
1461 trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig);
1462 if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
1463 return;
1464
1465 iwl_mvm_fw_dbg_collect_trig(mvm, trigger);
1466}
1416 1467
1417#endif /* __IWL_MVM_H__ */ 1468#endif /* __IWL_MVM_H__ */
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c
index 1f64d23e7590..96a4a154a42c 100644
--- a/drivers/net/wireless/iwlwifi/mvm/ops.c
+++ b/drivers/net/wireless/iwlwifi/mvm/ops.c
@@ -455,7 +455,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
455 INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk); 455 INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_wk);
456 INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk); 456 INIT_WORK(&mvm->sta_drained_wk, iwl_mvm_sta_drained_wk);
457 INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); 457 INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
458 INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk); 458 INIT_DELAYED_WORK(&mvm->fw_dump_wk, iwl_mvm_fw_error_dump_wk);
459 INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work); 459 INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
460 460
461 spin_lock_init(&mvm->d0i3_tx_lock); 461 spin_lock_init(&mvm->d0i3_tx_lock);
@@ -503,6 +503,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
503 trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; 503 trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
504 memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, 504 memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
505 sizeof(trans->dbg_conf_tlv)); 505 sizeof(trans->dbg_conf_tlv));
506 trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv;
506 507
507 /* set up notification wait support */ 508 /* set up notification wait support */
508 iwl_notification_wait_init(&mvm->notif_wait); 509 iwl_notification_wait_init(&mvm->notif_wait);
@@ -826,7 +827,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk)
826static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) 827static void iwl_mvm_fw_error_dump_wk(struct work_struct *work)
827{ 828{
828 struct iwl_mvm *mvm = 829 struct iwl_mvm *mvm =
829 container_of(work, struct iwl_mvm, fw_error_dump_wk); 830 container_of(work, struct iwl_mvm, fw_dump_wk.work);
830 831
831 if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) 832 if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT))
832 return; 833 return;
@@ -878,7 +879,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
878 * can't recover this since we're already half suspended. 879 * can't recover this since we're already half suspended.
879 */ 880 */
880 if (!mvm->restart_fw && fw_error) { 881 if (!mvm->restart_fw && fw_error) {
881 schedule_work(&mvm->fw_error_dump_wk); 882 iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_FW_ASSERT, 0);
882 } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, 883 } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART,
883 &mvm->status)) { 884 &mvm->status)) {
884 struct iwl_mvm_reprobe *reprobe; 885 struct iwl_mvm_reprobe *reprobe;