Commit a437b3d9 authored by Kalle Valo's avatar Kalle Valo

Merge tag 'iwlwifi-next-for-kalle-2015-03-01' of...

Merge tag 'iwlwifi-next-for-kalle-2015-03-01' of https://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/iwlwifi-next

* add triggers for firmware dump collection
* remove support for -9.ucode
* new statitics API
* rate control improvements
parents 9b6cc9a8 190f1029
...@@ -1549,7 +1549,7 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv) ...@@ -1549,7 +1549,7 @@ static void iwl_dump_nic_error_log(struct iwl_priv *priv)
table.blink1, table.blink2, table.ilink1, table.blink1, table.blink2, table.ilink1,
table.ilink2, table.bcon_time, table.gp1, table.ilink2, table.bcon_time, table.gp1,
table.gp2, table.gp3, table.ucode_ver, table.gp2, table.gp3, table.ucode_ver,
table.hw_ver, table.brd_ver); table.hw_ver, 0, table.brd_ver);
IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id, IWL_ERR(priv, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id)); desc_lookup(table.error_id));
IWL_ERR(priv, "0x%08X | uPc\n", table.pc); IWL_ERR(priv, "0x%08X | uPc\n", table.pc);
......
...@@ -77,8 +77,8 @@ ...@@ -77,8 +77,8 @@
#define IWL3160_UCODE_API_OK 10 #define IWL3160_UCODE_API_OK 10
/* Lowest firmware API version supported */ /* Lowest firmware API version supported */
#define IWL7260_UCODE_API_MIN 9 #define IWL7260_UCODE_API_MIN 10
#define IWL3160_UCODE_API_MIN 9 #define IWL3160_UCODE_API_MIN 10
/* NVM versions */ /* NVM versions */
#define IWL7260_NVM_VERSION 0x0a1d #define IWL7260_NVM_VERSION 0x0a1d
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
#define IWL8000_UCODE_API_OK 10 #define IWL8000_UCODE_API_OK 10
/* Lowest firmware API version supported */ /* Lowest firmware API version supported */
#define IWL8000_UCODE_API_MIN 9 #define IWL8000_UCODE_API_MIN 10
/* NVM versions */ /* NVM versions */
#define IWL8000_NVM_VERSION 0x0a1d #define IWL8000_NVM_VERSION 0x0a1d
......
...@@ -431,11 +431,11 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, ...@@ -431,11 +431,11 @@ TRACE_EVENT(iwlwifi_dev_ucode_error,
TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low, TP_PROTO(const struct device *dev, u32 desc, u32 tsf_low,
u32 data1, u32 data2, u32 line, u32 blink1, u32 data1, u32 data2, u32 line, u32 blink1,
u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time, u32 blink2, u32 ilink1, u32 ilink2, u32 bcon_time,
u32 gp1, u32 gp2, u32 gp3, u32 ucode_ver, u32 hw_ver, u32 gp1, u32 gp2, u32 gp3, u32 major, u32 minor, u32 hw_ver,
u32 brd_ver), u32 brd_ver),
TP_ARGS(dev, desc, tsf_low, data1, data2, line, TP_ARGS(dev, desc, tsf_low, data1, data2, line,
blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2, blink1, blink2, ilink1, ilink2, bcon_time, gp1, gp2,
gp3, ucode_ver, hw_ver, brd_ver), gp3, major, minor, hw_ver, brd_ver),
TP_STRUCT__entry( TP_STRUCT__entry(
DEV_ENTRY DEV_ENTRY
__field(u32, desc) __field(u32, desc)
...@@ -451,7 +451,8 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, ...@@ -451,7 +451,8 @@ TRACE_EVENT(iwlwifi_dev_ucode_error,
__field(u32, gp1) __field(u32, gp1)
__field(u32, gp2) __field(u32, gp2)
__field(u32, gp3) __field(u32, gp3)
__field(u32, ucode_ver) __field(u32, major)
__field(u32, minor)
__field(u32, hw_ver) __field(u32, hw_ver)
__field(u32, brd_ver) __field(u32, brd_ver)
), ),
...@@ -470,21 +471,22 @@ TRACE_EVENT(iwlwifi_dev_ucode_error, ...@@ -470,21 +471,22 @@ TRACE_EVENT(iwlwifi_dev_ucode_error,
__entry->gp1 = gp1; __entry->gp1 = gp1;
__entry->gp2 = gp2; __entry->gp2 = gp2;
__entry->gp3 = gp3; __entry->gp3 = gp3;
__entry->ucode_ver = ucode_ver; __entry->major = major;
__entry->minor = minor;
__entry->hw_ver = hw_ver; __entry->hw_ver = hw_ver;
__entry->brd_ver = brd_ver; __entry->brd_ver = brd_ver;
), ),
TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, " TP_printk("[%s] #%02d %010u data 0x%08X 0x%08X line %u, "
"blink 0x%05X 0x%05X ilink 0x%05X 0x%05X " "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X "
"bcon_tm %010u gp 0x%08X 0x%08X 0x%08X uCode 0x%08X " "bcon_tm %010u gp 0x%08X 0x%08X 0x%08X major 0x%08X "
"hw 0x%08X brd 0x%08X", "minor 0x%08X hw 0x%08X brd 0x%08X",
__get_str(dev), __entry->desc, __entry->tsf_low, __get_str(dev), __entry->desc, __entry->tsf_low,
__entry->data1, __entry->data1,
__entry->data2, __entry->line, __entry->blink1, __entry->data2, __entry->line, __entry->blink1,
__entry->blink2, __entry->ilink1, __entry->ilink2, __entry->blink2, __entry->ilink1, __entry->ilink2,
__entry->bcon_time, __entry->gp1, __entry->gp2, __entry->bcon_time, __entry->gp1, __entry->gp2,
__entry->gp3, __entry->ucode_ver, __entry->hw_ver, __entry->gp3, __entry->major, __entry->minor,
__entry->brd_ver) __entry->hw_ver, __entry->brd_ver)
); );
TRACE_EVENT(iwlwifi_dev_ucode_event, TRACE_EVENT(iwlwifi_dev_ucode_event,
......
...@@ -175,6 +175,8 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv) ...@@ -175,6 +175,8 @@ static void iwl_dealloc_ucode(struct iwl_drv *drv)
kfree(drv->fw.dbg_dest_tlv); kfree(drv->fw.dbg_dest_tlv);
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++) for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_conf_tlv); i++)
kfree(drv->fw.dbg_conf_tlv[i]); kfree(drv->fw.dbg_conf_tlv[i]);
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++)
kfree(drv->fw.dbg_trigger_tlv[i]);
for (i = 0; i < IWL_UCODE_TYPE_MAX; i++) for (i = 0; i < IWL_UCODE_TYPE_MAX; i++)
iwl_free_fw_img(drv, drv->fw.img + i); iwl_free_fw_img(drv, drv->fw.img + i);
...@@ -293,8 +295,10 @@ struct iwl_firmware_pieces { ...@@ -293,8 +295,10 @@ struct iwl_firmware_pieces {
/* FW debug data parsed for driver usage */ /* FW debug data parsed for driver usage */
struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
size_t dbg_conf_tlv_len[FW_DBG_MAX]; size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
}; };
/* /*
...@@ -842,6 +846,23 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, ...@@ -842,6 +846,23 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
capa->n_scan_channels = capa->n_scan_channels =
le32_to_cpup((__le32 *)tlv_data); le32_to_cpup((__le32 *)tlv_data);
break; break;
case IWL_UCODE_TLV_FW_VERSION: {
__le32 *ptr = (void *)tlv_data;
u32 major, minor;
u8 local_comp;
if (tlv_len != sizeof(u32) * 3)
goto invalid_tlv_len;
major = le32_to_cpup(ptr++);
minor = le32_to_cpup(ptr++);
local_comp = le32_to_cpup(ptr);
snprintf(drv->fw.fw_version,
sizeof(drv->fw.fw_version), "%u.%u.%u",
major, minor, local_comp);
break;
}
case IWL_UCODE_TLV_FW_DBG_DEST: { case IWL_UCODE_TLV_FW_DBG_DEST: {
struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data; struct iwl_fw_dbg_dest_tlv *dest = (void *)tlv_data;
...@@ -897,6 +918,31 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, ...@@ -897,6 +918,31 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
pieces->dbg_conf_tlv_len[conf->id] = tlv_len; pieces->dbg_conf_tlv_len[conf->id] = tlv_len;
break; break;
} }
case IWL_UCODE_TLV_FW_DBG_TRIGGER: {
struct iwl_fw_dbg_trigger_tlv *trigger =
(void *)tlv_data;
u32 trigger_id = le32_to_cpu(trigger->id);
if (trigger_id >= ARRAY_SIZE(drv->fw.dbg_trigger_tlv)) {
IWL_ERR(drv,
"Skip unknown trigger: %u\n",
trigger->id);
break;
}
if (pieces->dbg_trigger_tlv[trigger_id]) {
IWL_ERR(drv,
"Ignore duplicate dbg trigger %u\n",
trigger->id);
break;
}
IWL_INFO(drv, "Found debug trigger: %u\n", trigger->id);
pieces->dbg_trigger_tlv[trigger_id] = trigger;
pieces->dbg_trigger_tlv_len[trigger_id] = tlv_len;
break;
}
case IWL_UCODE_TLV_SEC_RT_USNIFFER: case IWL_UCODE_TLV_SEC_RT_USNIFFER:
usniffer_images = true; usniffer_images = true;
iwl_store_ucode_sec(pieces, tlv_data, iwl_store_ucode_sec(pieces, tlv_data,
...@@ -1107,6 +1153,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) ...@@ -1107,6 +1153,9 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
if (err) if (err)
goto try_again; goto try_again;
if (drv->fw.ucode_capa.api[0] & IWL_UCODE_TLV_API_NEW_VERSION)
api_ver = drv->fw.ucode_ver;
else
api_ver = IWL_UCODE_API(drv->fw.ucode_ver); api_ver = IWL_UCODE_API(drv->fw.ucode_ver);
/* /*
...@@ -1178,6 +1227,19 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) ...@@ -1178,6 +1227,19 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
} }
} }
for (i = 0; i < ARRAY_SIZE(drv->fw.dbg_trigger_tlv); i++) {
if (pieces->dbg_trigger_tlv[i]) {
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 */ /* Now that we can no longer fail, copy information */
/* /*
......
...@@ -82,6 +82,8 @@ ...@@ -82,6 +82,8 @@
* sections like this in a single file. * sections like this in a single file.
* @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers * @IWL_FW_ERROR_DUMP_FH_REGS: range of FH registers
* @IWL_FW_ERROR_DUMP_MEM: chunk of memory * @IWL_FW_ERROR_DUMP_MEM: chunk of memory
* @IWL_FW_ERROR_DUMP_ERROR_INFO: description of what triggered this dump.
* Structured as &struct iwl_fw_error_dump_trigger_desc.
*/ */
enum iwl_fw_error_dump_type { enum iwl_fw_error_dump_type {
/* 0 is deprecated */ /* 0 is deprecated */
...@@ -94,6 +96,7 @@ enum iwl_fw_error_dump_type { ...@@ -94,6 +96,7 @@ enum iwl_fw_error_dump_type {
IWL_FW_ERROR_DUMP_TXF = 7, IWL_FW_ERROR_DUMP_TXF = 7,
IWL_FW_ERROR_DUMP_FH_REGS = 8, IWL_FW_ERROR_DUMP_FH_REGS = 8,
IWL_FW_ERROR_DUMP_MEM = 9, IWL_FW_ERROR_DUMP_MEM = 9,
IWL_FW_ERROR_DUMP_ERROR_INFO = 10,
IWL_FW_ERROR_DUMP_MAX, IWL_FW_ERROR_DUMP_MAX,
}; };
...@@ -230,4 +233,47 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) ...@@ -230,4 +233,47 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data)
return (void *)(data->data + le32_to_cpu(data->len)); return (void *)(data->data + le32_to_cpu(data->len));
} }
/**
* enum iwl_fw_dbg_trigger - triggers available
*
* @FW_DBG_TRIGGER_USER: trigger log collection by user
* This should not be defined as a trigger to the driver, but a value the
* driver should set to indicate that the trigger was initiated by the
* user.
* @FW_DBG_TRIGGER_FW_ASSERT: trigger log collection when the firmware asserts
* @FW_DBG_TRIGGER_MISSED_BEACONS: trigger log collection when beacons are
* missed.
* @FW_DBG_TRIGGER_CHANNEL_SWITCH: trigger log collection upon channel switch.
* @FW_DBG_TRIGGER_FW_NOTIF: trigger log collection when the firmware sends a
* command response or a notification.
* @FW_DB_TRIGGER_RESERVED: reserved
* @FW_DBG_TRIGGER_STATS: trigger log collection upon statistics threshold.
* @FW_DBG_TRIGGER_RSSI: trigger log collection when the rssi of the beacon
* goes below a threshold.
*/
enum iwl_fw_dbg_trigger {
FW_DBG_TRIGGER_INVALID = 0,
FW_DBG_TRIGGER_USER,
FW_DBG_TRIGGER_FW_ASSERT,
FW_DBG_TRIGGER_MISSED_BEACONS,
FW_DBG_TRIGGER_CHANNEL_SWITCH,
FW_DBG_TRIGGER_FW_NOTIF,
FW_DB_TRIGGER_RESERVED,
FW_DBG_TRIGGER_STATS,
FW_DBG_TRIGGER_RSSI,
/* must be last */
FW_DBG_TRIGGER_MAX,
};
/**
* struct iwl_fw_error_dump_trigger_desc - describes the trigger condition
* @type: %enum iwl_fw_dbg_trigger
* @data: raw data about what happened
*/
struct iwl_fw_error_dump_trigger_desc {
__le32 type;
u8 data[];
};
#endif /* __fw_error_dump_h__ */ #endif /* __fw_error_dump_h__ */
...@@ -66,6 +66,7 @@ ...@@ -66,6 +66,7 @@
#define __iwl_fw_file_h__ #define __iwl_fw_file_h__
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/nl80211.h>
/* v1/v2 uCode file layout */ /* v1/v2 uCode file layout */
struct iwl_ucode_header { struct iwl_ucode_header {
...@@ -133,8 +134,10 @@ enum iwl_ucode_tlv_type { ...@@ -133,8 +134,10 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_N_SCAN_CHANNELS = 31, IWL_UCODE_TLV_N_SCAN_CHANNELS = 31,
IWL_UCODE_TLV_SEC_RT_USNIFFER = 34, IWL_UCODE_TLV_SEC_RT_USNIFFER = 34,
IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35, IWL_UCODE_TLV_SDIO_ADMA_ADDR = 35,
IWL_UCODE_TLV_FW_VERSION = 36,
IWL_UCODE_TLV_FW_DBG_DEST = 38, IWL_UCODE_TLV_FW_DBG_DEST = 38,
IWL_UCODE_TLV_FW_DBG_CONF = 39, IWL_UCODE_TLV_FW_DBG_CONF = 39,
IWL_UCODE_TLV_FW_DBG_TRIGGER = 40,
}; };
struct iwl_ucode_tlv { struct iwl_ucode_tlv {
...@@ -156,7 +159,8 @@ struct iwl_tlv_ucode_header { ...@@ -156,7 +159,8 @@ struct iwl_tlv_ucode_header {
__le32 zero; __le32 zero;
__le32 magic; __le32 magic;
u8 human_readable[FW_VER_HUMAN_READABLE_SZ]; u8 human_readable[FW_VER_HUMAN_READABLE_SZ];
__le32 ver; /* major/minor/API/serial */ /* major/minor/API/serial or major in new format */
__le32 ver;
__le32 build; __le32 build;
__le64 ignore; __le64 ignore;
/* /*
...@@ -237,7 +241,6 @@ enum iwl_ucode_tlv_flag { ...@@ -237,7 +241,6 @@ enum iwl_ucode_tlv_flag {
* enum iwl_ucode_tlv_api - ucode api * enum iwl_ucode_tlv_api - ucode api
* @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex * @IWL_UCODE_TLV_API_BT_COEX_SPLIT: new API for BT Coex
* @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit. * @IWL_UCODE_TLV_API_DISABLE_STA_TX: ucode supports tx_disable bit.
* @IWL_UCODE_TLV_API_LMAC_SCAN: This ucode uses LMAC unified scan API.
* @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif. * @IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF: ucode supports disabling dummy notif.
* @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time * @IWL_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time
* longer than the passive one, which is essential for fragmented scan. * longer than the passive one, which is essential for fragmented scan.
...@@ -250,11 +253,12 @@ enum iwl_ucode_tlv_flag { ...@@ -250,11 +253,12 @@ enum iwl_ucode_tlv_flag {
* @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too. * @IWL_UCODE_TLV_API_SINGLE_SCAN_EBS: EBS is supported for single scans too.
* @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported. * @IWL_UCODE_TLV_API_ASYNC_DTM: Async temperature notifications are supported.
* @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params * @IWL_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params
* @IWL_UCODE_TLV_API_STATS_V10: uCode supports/uses statistics API version 10
* @IWL_UCODE_TLV_API_NEW_VERSION: new versioning format
*/ */
enum iwl_ucode_tlv_api { enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3), IWL_UCODE_TLV_API_BT_COEX_SPLIT = BIT(3),
IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5), IWL_UCODE_TLV_API_DISABLE_STA_TX = BIT(5),
IWL_UCODE_TLV_API_LMAC_SCAN = BIT(6),
IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7), IWL_UCODE_TLV_API_SF_NO_DUMMY_NOTIF = BIT(7),
IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8), IWL_UCODE_TLV_API_FRAGMENTED_SCAN = BIT(8),
IWL_UCODE_TLV_API_HDC_PHASE_0 = BIT(10), IWL_UCODE_TLV_API_HDC_PHASE_0 = BIT(10),
...@@ -263,6 +267,8 @@ enum iwl_ucode_tlv_api { ...@@ -263,6 +267,8 @@ enum iwl_ucode_tlv_api {
IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = BIT(16), IWL_UCODE_TLV_API_SINGLE_SCAN_EBS = BIT(16),
IWL_UCODE_TLV_API_ASYNC_DTM = BIT(17), IWL_UCODE_TLV_API_ASYNC_DTM = BIT(17),
IWL_UCODE_TLV_API_LQ_SS_PARAMS = BIT(18), IWL_UCODE_TLV_API_LQ_SS_PARAMS = BIT(18),
IWL_UCODE_TLV_API_STATS_V10 = BIT(19),
IWL_UCODE_TLV_API_NEW_VERSION = BIT(20),
}; };
/** /**
...@@ -284,6 +290,8 @@ enum iwl_ucode_tlv_api { ...@@ -284,6 +290,8 @@ enum iwl_ucode_tlv_api {
* which also implies support for the scheduler configuration command * which also implies support for the scheduler configuration command
* @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching * @IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching
* @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command * @IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command
* @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics
* @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running
*/ */
enum iwl_ucode_tlv_capa { enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0), IWL_UCODE_TLV_CAPA_D0I3_SUPPORT = BIT(0),
...@@ -298,6 +306,8 @@ enum iwl_ucode_tlv_capa { ...@@ -298,6 +306,8 @@ enum iwl_ucode_tlv_capa {
IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12), IWL_UCODE_TLV_CAPA_DQA_SUPPORT = BIT(12),
IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = BIT(13), IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = BIT(13),
IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18), IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = BIT(18),
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = BIT(22),
IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = BIT(28),
}; };
/* The default calibrate table size if not specified by firmware file */ /* The default calibrate table size if not specified by firmware file */
...@@ -450,44 +460,129 @@ struct iwl_fw_dbg_conf_hcmd { ...@@ -450,44 +460,129 @@ struct iwl_fw_dbg_conf_hcmd {
} __packed; } __packed;
/** /**
* struct iwl_fw_dbg_trigger - a TLV that describes a debug configuration * enum iwl_fw_dbg_trigger_mode - triggers functionalities
* *
* @enabled: is this trigger enabled * @IWL_FW_DBG_TRIGGER_START: when trigger occurs re-conf the dbg mechanism
* @reserved: * @IWL_FW_DBG_TRIGGER_STOP: when trigger occurs pull the dbg data
* @len: length, in bytes, of the %trigger field
* @trigger: pointer to a trigger struct
*/ */
struct iwl_fw_dbg_trigger { enum iwl_fw_dbg_trigger_mode {
u8 enabled; IWL_FW_DBG_TRIGGER_START = BIT(0),
u8 reserved; IWL_FW_DBG_TRIGGER_STOP = BIT(1),
u8 len; };
u8 trigger[0];
/**
* enum iwl_fw_dbg_trigger_vif_type - define the VIF type for a trigger
* @IWL_FW_DBG_CONF_VIF_ANY: any vif type
* @IWL_FW_DBG_CONF_VIF_IBSS: IBSS mode
* @IWL_FW_DBG_CONF_VIF_STATION: BSS mode
* @IWL_FW_DBG_CONF_VIF_AP: AP mode
* @IWL_FW_DBG_CONF_VIF_P2P_CLIENT: P2P Client mode
* @IWL_FW_DBG_CONF_VIF_P2P_GO: P2P GO mode
* @IWL_FW_DBG_CONF_VIF_P2P_DEVICE: P2P device
*/
enum iwl_fw_dbg_trigger_vif_type {
IWL_FW_DBG_CONF_VIF_ANY = NL80211_IFTYPE_UNSPECIFIED,
IWL_FW_DBG_CONF_VIF_IBSS = NL80211_IFTYPE_ADHOC,
IWL_FW_DBG_CONF_VIF_STATION = NL80211_IFTYPE_STATION,
IWL_FW_DBG_CONF_VIF_AP = NL80211_IFTYPE_AP,
IWL_FW_DBG_CONF_VIF_P2P_CLIENT = NL80211_IFTYPE_P2P_CLIENT,
IWL_FW_DBG_CONF_VIF_P2P_GO = NL80211_IFTYPE_P2P_GO,
IWL_FW_DBG_CONF_VIF_P2P_DEVICE = NL80211_IFTYPE_P2P_DEVICE,
};
/**
* struct iwl_fw_dbg_trigger_tlv - a TLV that describes the trigger
* @id: %enum iwl_fw_dbg_trigger
* @vif_type: %enum iwl_fw_dbg_trigger_vif_type
* @stop_conf_ids: bitmap of configurations this trigger relates to.
* if the mode is %IWL_FW_DBG_TRIGGER_STOP, then if the bit corresponding
* to the currently running configuration is set, the data should be
* collected.
* @stop_delay: how many milliseconds to wait before collecting the data
* after the STOP trigger fires.
* @mode: %enum iwl_fw_dbg_trigger_mode - can be stop / start of both
* @start_conf_id: if mode is %IWL_FW_DBG_TRIGGER_START, this defines what
* configuration should be applied when the triggers kicks in.
* @occurrences: number of occurrences. 0 means the trigger will never fire.
*/
struct iwl_fw_dbg_trigger_tlv {
__le32 id;
__le32 vif_type;
__le32 stop_conf_ids;
__le32 stop_delay;
u8 mode;
u8 start_conf_id;
__le16 occurrences;
__le32 reserved[2];
u8 data[0];
} __packed; } __packed;
#define FW_DBG_START_FROM_ALIVE 0
#define FW_DBG_CONF_MAX 32
#define FW_DBG_INVALID 0xff
/** /**
* enum iwl_fw_dbg_conf - configurations available * struct iwl_fw_dbg_trigger_missed_bcon - configures trigger for missed beacons
* * @stop_consec_missed_bcon: stop recording if threshold is crossed.
* @FW_DBG_CUSTOM: take this configuration from alive * @stop_consec_missed_bcon_since_rx: stop recording if threshold is crossed.
* Note that the trigger is NO-OP for this configuration * @start_consec_missed_bcon: start recording if threshold is crossed.
* @start_consec_missed_bcon_since_rx: start recording if threshold is crossed.
* @reserved1: reserved
* @reserved2: reserved
*/ */
enum iwl_fw_dbg_conf { struct iwl_fw_dbg_trigger_missed_bcon {
FW_DBG_CUSTOM = 0, __le32 stop_consec_missed_bcon;
__le32 stop_consec_missed_bcon_since_rx;
__le32 reserved2[2];
__le32 start_consec_missed_bcon;
__le32 start_consec_missed_bcon_since_rx;
__le32 reserved1[2];
} __packed;
/* must be last */ /**
FW_DBG_MAX, * struct iwl_fw_dbg_trigger_cmd - configures trigger for messages from FW.
FW_DBG_INVALID = 0xff, * cmds: the list of commands to trigger the collection on
}; */
struct iwl_fw_dbg_trigger_cmd {
struct cmd {
u8 cmd_id;
u8 group_id;
} __packed cmds[16];
} __packed;
/** /**
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration * iwl_fw_dbg_trigger_stats - configures trigger for statistics
* * @stop_offset: the offset of the value to be monitored
* @id: %enum iwl_fw_dbg_conf * @stop_threshold: the threshold above which to collect
* @start_offset: the offset of the value to be monitored
* @start_threshold: the threshold above which to start recording
*/
struct iwl_fw_dbg_trigger_stats {
__le32 stop_offset;
__le32 stop_threshold;
__le32 start_offset;
__le32 start_threshold;
} __packed;
/**
* struct iwl_fw_dbg_trigger_low_rssi - trigger for low beacon RSSI
* @rssi: RSSI value to trigger at
*/
struct iwl_fw_dbg_trigger_low_rssi {
__le32 rssi;
} __packed;
/**
* struct iwl_fw_dbg_conf_tlv - a TLV that describes a debug configuration.
* @id: conf id
* @usniffer: should the uSniffer image be used * @usniffer: should the uSniffer image be used
* @num_of_hcmds: how many HCMDs to send are present here * @num_of_hcmds: how many HCMDs to send are present here
* @hcmd: a variable length host command to be sent to apply the configuration. * @hcmd: a variable length host command to be sent to apply the configuration.
* If there is more than one HCMD to send, they will appear one after the * If there is more than one HCMD to send, they will appear one after the
* other and be sent in the order that they appear in. * other and be sent in the order that they appear in.
* This parses IWL_UCODE_TLV_FW_DBG_CONF * This parses IWL_UCODE_TLV_FW_DBG_CONF. The user can add up-to
* %FW_DBG_CONF_MAX configuration per run.
*/ */
struct iwl_fw_dbg_conf_tlv { struct iwl_fw_dbg_conf_tlv {
u8 id; u8 id;
...@@ -495,8 +590,6 @@ struct iwl_fw_dbg_conf_tlv { ...@@ -495,8 +590,6 @@ struct iwl_fw_dbg_conf_tlv {
u8 reserved; u8 reserved;
u8 num_of_hcmds; u8 num_of_hcmds;
struct iwl_fw_dbg_conf_hcmd hcmd; struct iwl_fw_dbg_conf_hcmd hcmd;
/* struct iwl_fw_dbg_trigger sits after all variable length hcmds */
} __packed; } __packed;
#endif /* __iwl_fw_file_h__ */ #endif /* __iwl_fw_file_h__ */
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#include <net/mac80211.h> #include <net/mac80211.h>
#include "iwl-fw-file.h" #include "iwl-fw-file.h"
#include "iwl-fw-error-dump.h"
/** /**
* enum iwl_ucode_type * enum iwl_ucode_type
...@@ -157,6 +158,8 @@ struct iwl_fw_cscheme_list { ...@@ -157,6 +158,8 @@ struct iwl_fw_cscheme_list {
* @dbg_dest_tlv: points to the destination TLV for debug * @dbg_dest_tlv: points to the destination TLV for debug
* @dbg_conf_tlv: array of pointers to configuration TLVs for debug * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
* @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries * @dbg_conf_tlv_len: lengths of the @dbg_conf_tlv entries
* @dbg_trigger_tlv: array of pointers to triggers TLVs
* @dbg_trigger_tlv_len: lengths of the @dbg_trigger_tlv entries
* @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
*/ */
struct iwl_fw { struct iwl_fw {
...@@ -186,9 +189,10 @@ struct iwl_fw { ...@@ -186,9 +189,10 @@ struct iwl_fw {
u32 sdio_adma_addr; u32 sdio_adma_addr;
struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
size_t dbg_conf_tlv_len[FW_DBG_MAX]; size_t dbg_conf_tlv_len[FW_DBG_CONF_MAX];
struct iwl_fw_dbg_trigger_tlv *dbg_trigger_tlv[FW_DBG_TRIGGER_MAX];
size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX];
u8 dbg_dest_reg_num; u8 dbg_dest_reg_num;
}; };
...@@ -206,46 +210,29 @@ static inline const char *get_fw_dbg_mode_string(int mode) ...@@ -206,46 +210,29 @@ static inline const char *get_fw_dbg_mode_string(int mode)
} }
} }
static inline const struct iwl_fw_dbg_trigger * static inline bool
iwl_fw_dbg_conf_get_trigger(const struct iwl_fw *fw, u8 id) iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id)
{ {
const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id]; const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id];
u8 *ptr;
int i;
if (!conf_tlv) if (!conf_tlv)
return NULL;
ptr = (void *)&conf_tlv->hcmd;
for (i = 0; i < conf_tlv->num_of_hcmds; i++) {
ptr += sizeof(conf_tlv->hcmd);
ptr += le16_to_cpu(conf_tlv->hcmd.len);
}
return (const struct iwl_fw_dbg_trigger *)ptr;
}
static inline bool
iwl_fw_dbg_conf_enabled(const struct iwl_fw *fw, u8 id)
{
const struct iwl_fw_dbg_trigger *trigger =
iwl_fw_dbg_conf_get_trigger(fw, id);
if (!trigger)
return false; return false;
return trigger->enabled; return conf_tlv->usniffer;
} }
static inline bool #define iwl_fw_dbg_trigger_enabled(fw, id) ({ \
iwl_fw_dbg_conf_usniffer(const struct iwl_fw *fw, u8 id) void *__dbg_trigger = (fw)->dbg_trigger_tlv[(id)]; \
{ unlikely(__dbg_trigger); \
const struct iwl_fw_dbg_conf_tlv *conf_tlv = fw->dbg_conf_tlv[id]; })
if (!conf_tlv) static inline struct iwl_fw_dbg_trigger_tlv*
return false; iwl_fw_dbg_get_trigger(const struct iwl_fw *fw, u8 id)
{
if (WARN_ON(id >= ARRAY_SIZE(fw->dbg_trigger_tlv)))
return NULL;
return conf_tlv->usniffer; return fw->dbg_trigger_tlv[id];
} }
#endif /* __iwl_fw_h__ */ #endif /* __iwl_fw_h__ */
...@@ -72,7 +72,7 @@ ...@@ -72,7 +72,7 @@
#include "iwl-trans.h" #include "iwl-trans.h"
#define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */ #define CHANNEL_NUM_SIZE 4 /* num of channels in calib_ch size */
#define IWL_NUM_PAPD_CH_GROUPS 7 #define IWL_NUM_PAPD_CH_GROUPS 9
#define IWL_NUM_TXP_CH_GROUPS 9 #define IWL_NUM_TXP_CH_GROUPS 9
struct iwl_phy_db_entry { struct iwl_phy_db_entry {
......
...@@ -370,7 +370,6 @@ enum secure_load_status_reg { ...@@ -370,7 +370,6 @@ enum secure_load_status_reg {
#define MON_BUFF_CYCLE_CNT (0xa03c48) #define MON_BUFF_CYCLE_CNT (0xa03c48)
#define DBGC_IN_SAMPLE (0xa03c00) #define DBGC_IN_SAMPLE (0xa03c00)
#define DBGC_OUT_CTRL (0xa03c0c)
/* FW chicken bits */ /* FW chicken bits */
#define LMPM_CHICK 0xA01FF8 #define LMPM_CHICK 0xA01FF8
......
...@@ -595,6 +595,7 @@ enum iwl_d0i3_mode { ...@@ -595,6 +595,7 @@ enum iwl_d0i3_mode {
* @dflt_pwr_limit: default power limit fetched from the platform (ACPI) * @dflt_pwr_limit: default power limit fetched from the platform (ACPI)
* @dbg_dest_tlv: points to the destination TLV for debug * @dbg_dest_tlv: points to the destination TLV for debug
* @dbg_conf_tlv: array of pointers to configuration TLVs for debug * @dbg_conf_tlv: array of pointers to configuration TLVs for debug
* @dbg_trigger_tlv: array of pointers to triggers TLVs for debug
* @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv * @dbg_dest_reg_num: num of reg_ops in %dbg_dest_tlv
*/ */
struct iwl_trans { struct iwl_trans {
...@@ -628,7 +629,8 @@ struct iwl_trans { ...@@ -628,7 +629,8 @@ struct iwl_trans {
u64 dflt_pwr_limit; u64 dflt_pwr_limit;
const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv; const struct iwl_fw_dbg_dest_tlv *dbg_dest_tlv;
const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_MAX]; const struct iwl_fw_dbg_conf_tlv *dbg_conf_tlv[FW_DBG_CONF_MAX];
struct iwl_fw_dbg_trigger_tlv * const *dbg_trigger_tlv;
u8 dbg_dest_reg_num; u8 dbg_dest_reg_num;
enum iwl_d0i3_mode d0i3_mode; enum iwl_d0i3_mode d0i3_mode;
......
...@@ -611,7 +611,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm) ...@@ -611,7 +611,7 @@ int iwl_send_bt_init_conf(struct iwl_mvm *mvm)
bt_cmd->enabled_modules |= bt_cmd->enabled_modules |=
cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED); cpu_to_le32(BT_COEX_SYNC2SCO_ENABLED);
if (IWL_MVM_BT_COEX_CORUNNING) if (iwl_mvm_bt_is_plcr_supported(mvm))
bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED); bt_cmd->enabled_modules |= cpu_to_le32(BT_COEX_CORUN_ENABLED);
if (IWL_MVM_BT_COEX_MPLUT) { if (IWL_MVM_BT_COEX_MPLUT) {
...@@ -1234,7 +1234,7 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm, ...@@ -1234,7 +1234,7 @@ int iwl_mvm_rx_ant_coupling_notif(struct iwl_mvm *mvm,
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT)) if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_BT_COEX_SPLIT))
return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd); return iwl_mvm_rx_ant_coupling_notif_old(mvm, rxb, dev_cmd);
if (!IWL_MVM_BT_COEX_CORUNNING) if (!iwl_mvm_bt_is_plcr_supported(mvm))
return 0; return 0;
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
......
...@@ -619,7 +619,7 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm) ...@@ -619,7 +619,7 @@ int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm)
if (IWL_MVM_BT_COEX_SYNC2SCO) if (IWL_MVM_BT_COEX_SYNC2SCO)
bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO); bt_cmd->flags |= cpu_to_le32(BT_COEX_SYNC2SCO);
if (IWL_MVM_BT_COEX_CORUNNING) { if (iwl_mvm_bt_is_plcr_supported(mvm)) {
bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 | bt_cmd->valid_bit_msk |= cpu_to_le32(BT_VALID_CORUN_LUT_20 |
BT_VALID_CORUN_LUT_40); BT_VALID_CORUN_LUT_40);
bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING); bt_cmd->flags |= cpu_to_le32(BT_COEX_CORUNNING);
...@@ -1167,16 +1167,10 @@ bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm, ...@@ -1167,16 +1167,10 @@ bool iwl_mvm_bt_coex_is_mimo_allowed_old(struct iwl_mvm *mvm,
return lut_type != BT_COEX_LOOSE_LUT; return lut_type != BT_COEX_LOOSE_LUT;
} }
bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant)
{
u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
return ag < BT_HIGH_TRAFFIC;
}
bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm) bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm)
{ {
u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading); u32 ag = le32_to_cpu(mvm->last_bt_notif_old.bt_activity_grading);
return ag == BT_OFF; return ag < BT_HIGH_TRAFFIC;
} }
bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm, bool iwl_mvm_bt_coex_is_tpc_allowed_old(struct iwl_mvm *mvm,
...@@ -1213,7 +1207,7 @@ int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm, ...@@ -1213,7 +1207,7 @@ int iwl_mvm_rx_ant_coupling_notif_old(struct iwl_mvm *mvm,
.dataflags = { IWL_HCMD_DFL_NOCOPY, }, .dataflags = { IWL_HCMD_DFL_NOCOPY, },
}; };
if (!IWL_MVM_BT_COEX_CORUNNING) if (!iwl_mvm_bt_is_plcr_supported(mvm))
return 0; return 0;
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
......
...@@ -1876,25 +1876,28 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) ...@@ -1876,25 +1876,28 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test)
if (mvm->net_detect) { if (mvm->net_detect) {
iwl_mvm_query_netdetect_reasons(mvm, vif); iwl_mvm_query_netdetect_reasons(mvm, vif);
/* has unlocked the mutex, so skip that */
goto out;
} else { } else {
keep = iwl_mvm_query_wakeup_reasons(mvm, vif); keep = iwl_mvm_query_wakeup_reasons(mvm, vif);
#ifdef CONFIG_IWLWIFI_DEBUGFS #ifdef CONFIG_IWLWIFI_DEBUGFS
if (keep) if (keep)
mvm->keep_vif = vif; mvm->keep_vif = vif;
/* has unlocked the mutex, so skip that */
goto out_iterate;
#endif #endif
} }
/* has unlocked the mutex, so skip that */
goto out;
out_unlock: out_unlock:
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
out: out_iterate:
if (!test) if (!test)
ieee80211_iterate_active_interfaces_rtnl(mvm->hw, ieee80211_iterate_active_interfaces_rtnl(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); iwl_mvm_d3_disconnect_iter, keep ? vif : NULL);
out:
/* return 1 to reconfigure the device */ /* return 1 to reconfigure the device */
set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status); set_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status);
......
...@@ -545,6 +545,57 @@ static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif, ...@@ -545,6 +545,57 @@ static ssize_t iwl_dbgfs_uapsd_misbehaving_write(struct ieee80211_vif *vif,
return ret ? count : -EINVAL; return ret ? count : -EINVAL;
} }
static ssize_t iwl_dbgfs_rx_phyinfo_write(struct ieee80211_vif *vif, char *buf,
size_t count, loff_t *ppos)
{
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->mvm;
struct ieee80211_chanctx_conf *chanctx_conf;
struct iwl_mvm_phy_ctxt *phy_ctxt;
u16 value;
int ret;
ret = kstrtou16(buf, 0, &value);
if (ret)
return ret;
mutex_lock(&mvm->mutex);
rcu_read_lock();
chanctx_conf = rcu_dereference(vif->chanctx_conf);
/* make sure the channel context is assigned */
if (!chanctx_conf) {
rcu_read_unlock();
mutex_unlock(&mvm->mutex);
return -EINVAL;
}
phy_ctxt = &mvm->phy_ctxts[*(u16 *)chanctx_conf->drv_priv];
rcu_read_unlock();
mvm->dbgfs_rx_phyinfo = value;
ret = iwl_mvm_phy_ctxt_changed(mvm, phy_ctxt, &chanctx_conf->min_def,
chanctx_conf->rx_chains_static,
chanctx_conf->rx_chains_dynamic);
mutex_unlock(&mvm->mutex);
return ret ?: count;
}
static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct ieee80211_vif *vif = file->private_data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
char buf[8];
snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo);
return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf));
}
#define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ #define MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \
_MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif) _MVM_DEBUGFS_WRITE_FILE_OPS(name, bufsz, struct ieee80211_vif)
#define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ #define MVM_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \
...@@ -560,6 +611,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32); ...@@ -560,6 +611,7 @@ MVM_DEBUGFS_READ_WRITE_FILE_OPS(pm_params, 32);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256); MVM_DEBUGFS_READ_WRITE_FILE_OPS(bf_params, 256);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10); MVM_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 10);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20); MVM_DEBUGFS_READ_WRITE_FILE_OPS(uapsd_misbehaving, 20);
MVM_DEBUGFS_READ_WRITE_FILE_OPS(rx_phyinfo, 10);
void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{ {
...@@ -595,6 +647,8 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif) ...@@ -595,6 +647,8 @@ void iwl_mvm_vif_dbgfs_register(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
S_IRUSR | S_IWUSR); S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir, MVM_DEBUGFS_ADD_FILE_VIF(uapsd_misbehaving, mvmvif->dbgfs_dir,
S_IRUSR | S_IWUSR); S_IRUSR | S_IWUSR);
MVM_DEBUGFS_ADD_FILE_VIF(rx_phyinfo, mvmvif->dbgfs_dir,
S_IRUSR | S_IWUSR);
if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p &&
mvmvif == mvm->bf_allowed_vif) mvmvif == mvm->bf_allowed_vif)
......
...@@ -942,7 +942,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file, ...@@ -942,7 +942,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_read(struct file *file,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct iwl_mvm *mvm = file->private_data; struct iwl_mvm *mvm = file->private_data;
enum iwl_fw_dbg_conf conf; int conf;
char buf[8]; char buf[8];
const size_t bufsz = sizeof(buf); const size_t bufsz = sizeof(buf);
int pos = 0; int pos = 0;
...@@ -966,7 +966,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm, ...@@ -966,7 +966,7 @@ static ssize_t iwl_dbgfs_fw_dbg_conf_write(struct iwl_mvm *mvm,
if (ret) if (ret)
return ret; return ret;
if (WARN_ON(conf_id >= FW_DBG_MAX)) if (WARN_ON(conf_id >= FW_DBG_CONF_MAX))
return -EINVAL; return -EINVAL;
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
...@@ -985,7 +985,7 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm, ...@@ -985,7 +985,7 @@ static ssize_t iwl_dbgfs_fw_dbg_collect_write(struct iwl_mvm *mvm,
if (ret) if (ret)
return ret; return ret;
iwl_mvm_fw_dbg_collect(mvm); iwl_mvm_fw_dbg_collect(mvm, FW_DBG_TRIGGER_USER, NULL, 0, 0);
iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE); iwl_mvm_unref(mvm, IWL_MVM_REF_PRPH_WRITE);
......
...@@ -70,6 +70,7 @@ ...@@ -70,6 +70,7 @@
#define MAC_INDEX_AUX 4 #define MAC_INDEX_AUX 4
#define MAC_INDEX_MIN_DRIVER 0 #define MAC_INDEX_MIN_DRIVER 0
#define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX #define NUM_MAC_INDEX_DRIVER MAC_INDEX_AUX
#define NUM_MAC_INDEX (MAC_INDEX_AUX + 1)
enum iwl_ac { enum iwl_ac {
AC_BK, AC_BK,
......
...@@ -70,54 +70,9 @@ ...@@ -70,54 +70,9 @@
/* Scan Commands, Responses, Notifications */ /* Scan Commands, Responses, Notifications */
/* Masks for iwl_scan_channel.type flags */
#define SCAN_CHANNEL_TYPE_ACTIVE BIT(0)
#define SCAN_CHANNEL_NARROW_BAND BIT(22)
/* Max number of IEs for direct SSID scans in a command */ /* Max number of IEs for direct SSID scans in a command */
#define PROBE_OPTION_MAX 20 #define PROBE_OPTION_MAX 20
/**
* struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table
* @channel: band is selected by iwl_scan_cmd "flags" field
* @tx_gain: gain for analog radio
* @dsp_atten: gain for DSP
* @active_dwell: dwell time for active scan in TU, typically 5-50
* @passive_dwell: dwell time for passive scan in TU, typically 20-500
* @type: type is broken down to these bits:
* bit 0: 0 = passive, 1 = active
* bits 1-20: SSID direct bit map. If any of these bits is set then
* the corresponding SSID IE is transmitted in probe request
* (bit i adds IE in position i to the probe request)
* bit 22: channel width, 0 = regular, 1 = TGj narrow channel
*
* @iteration_count:
* @iteration_interval:
* This struct is used once for each channel in the scan list.
* Each channel can independently select:
* 1) SSID for directed active scans
* 2) Txpower setting (for rate specified within Tx command)
* 3) How long to stay on-channel (behavior may be modified by quiet_time,
* quiet_plcp_th, good_CRC_th)
*
* To avoid uCode errors, make sure the following are true (see comments
* under struct iwl_scan_cmd about max_out_time and quiet_time):
* 1) If using passive_dwell (i.e. passive_dwell != 0):
* active_dwell <= passive_dwell (< max_out_time if max_out_time != 0)
* 2) quiet_time <= active_dwell
* 3) If restricting off-channel time (i.e. max_out_time !=0):
* passive_dwell < max_out_time
* active_dwell < max_out_time
*/
struct iwl_scan_channel {
__le32 type;
__le16 channel;
__le16 iteration_count;
__le32 iteration_interval;
__le16 active_dwell;
__le16 passive_dwell;
} __packed; /* SCAN_CHANNEL_CONTROL_API_S_VER_1 */
/** /**
* struct iwl_ssid_ie - directed scan network information element * struct iwl_ssid_ie - directed scan network information element
* *
...@@ -132,152 +87,6 @@ struct iwl_ssid_ie { ...@@ -132,152 +87,6 @@ struct iwl_ssid_ie {
u8 ssid[IEEE80211_MAX_SSID_LEN]; u8 ssid[IEEE80211_MAX_SSID_LEN];
} __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */ } __packed; /* SCAN_DIRECT_SSID_IE_API_S_VER_1 */
/**
* iwl_scan_flags - masks for scan command flags
*@SCAN_FLAGS_PERIODIC_SCAN:
*@SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX:
*@SCAN_FLAGS_DELAYED_SCAN_LOWBAND:
*@SCAN_FLAGS_DELAYED_SCAN_HIGHBAND:
*@SCAN_FLAGS_FRAGMENTED_SCAN:
*@SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active
* in the past hour, even if they are marked as passive.
*/
enum iwl_scan_flags {
SCAN_FLAGS_PERIODIC_SCAN = BIT(0),
SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX = BIT(1),
SCAN_FLAGS_DELAYED_SCAN_LOWBAND = BIT(2),
SCAN_FLAGS_DELAYED_SCAN_HIGHBAND = BIT(3),
SCAN_FLAGS_FRAGMENTED_SCAN = BIT(4),
SCAN_FLAGS_PASSIVE2ACTIVE = BIT(5),
};
/**
* enum iwl_scan_type - Scan types for scan command
* @SCAN_TYPE_FORCED:
* @SCAN_TYPE_BACKGROUND:
* @SCAN_TYPE_OS:
* @SCAN_TYPE_ROAMING:
* @SCAN_TYPE_ACTION:
* @SCAN_TYPE_DISCOVERY:
* @SCAN_TYPE_DISCOVERY_FORCED:
*/
enum iwl_scan_type {
SCAN_TYPE_FORCED = 0,
SCAN_TYPE_BACKGROUND = 1,
SCAN_TYPE_OS = 2,
SCAN_TYPE_ROAMING = 3,
SCAN_TYPE_ACTION = 4,
SCAN_TYPE_DISCOVERY = 5,
SCAN_TYPE_DISCOVERY_FORCED = 6,
}; /* SCAN_ACTIVITY_TYPE_E_VER_1 */
/**
* struct iwl_scan_cmd - scan request command
* ( SCAN_REQUEST_CMD = 0x80 )
* @len: command length in bytes
* @scan_flags: scan flags from SCAN_FLAGS_*
* @channel_count: num of channels in channel list
* (1 - ucode_capa.n_scan_channels)
* @quiet_time: in msecs, dwell this time for active scan on quiet channels
* @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than
* this number of packets were received (typically 1)
* @passive2active: is auto switching from passive to active during scan allowed
* @rxchain_sel_flags: RXON_RX_CHAIN_*
* @max_out_time: in TUs, max out of serving channel time
* @suspend_time: how long to pause scan when returning to service channel:
* bits 0-19: beacon interal in TUs (suspend before executing)
* bits 20-23: reserved
* bits 24-31: number of beacons (suspend between channels)
* @rxon_flags: RXON_FLG_*
* @filter_flags: RXON_FILTER_*
* @tx_cmd: for active scans (zero for passive), w/o payload,
* no RS so specify TX rate
* @direct_scan: direct scan SSIDs
* @type: one of SCAN_TYPE_*
* @repeats: how many time to repeat the scan
*/
struct iwl_scan_cmd {
__le16 len;
u8 scan_flags;
u8 channel_count;
__le16 quiet_time;
__le16 quiet_plcp_th;
__le16 passive2active;
__le16 rxchain_sel_flags;
__le32 max_out_time;
__le32 suspend_time;
/* RX_ON_FLAGS_API_S_VER_1 */
__le32 rxon_flags;
__le32 filter_flags;
struct iwl_tx_cmd tx_cmd;
struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX];
__le32 type;
__le32 repeats;
/*
* Probe request frame, followed by channel list.
*
* Size of probe request frame is specified by byte count in tx_cmd.
* Channel list follows immediately after probe request frame.
* Number of channels in list is specified by channel_count.
* Each channel in list is of type:
*
* struct iwl_scan_channel channels[0];
*
* NOTE: Only one band of channels can be scanned per pass. You
* must not mix 2.4GHz channels and 5.2GHz channels, and you must wait
* for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION)
* before requesting another scan.
*/
u8 data[0];
} __packed; /* SCAN_REQUEST_FIXED_PART_API_S_VER_5 */
/* Response to scan request contains only status with one of these values */
#define SCAN_RESPONSE_OK 0x1
#define SCAN_RESPONSE_ERROR 0x2
/*
* SCAN_ABORT_CMD = 0x81
* When scan abort is requested, the command has no fields except the common
* header. The response contains only a status with one of these values.
*/
#define SCAN_ABORT_POSSIBLE 0x1
#define SCAN_ABORT_IGNORED 0x2 /* no pending scans */
/* TODO: complete documentation */
#define SCAN_OWNER_STATUS 0x1
#define MEASURE_OWNER_STATUS 0x2
/**
* struct iwl_scan_start_notif - notifies start of scan in the device
* ( SCAN_START_NOTIFICATION = 0x82 )
* @tsf_low: TSF timer (lower half) in usecs
* @tsf_high: TSF timer (higher half) in usecs
* @beacon_timer: structured as follows:
* bits 0:19 - beacon interval in usecs
* bits 20:23 - reserved (0)
* bits 24:31 - number of beacons
* @channel: which channel is scanned
* @band: 0 for 5.2 GHz, 1 for 2.4 GHz
* @status: one of *_OWNER_STATUS
*/
struct iwl_scan_start_notif {
__le32 tsf_low;
__le32 tsf_high;
__le32 beacon_timer;
u8 channel;
u8 band;
u8 reserved[2];
__le32 status;
} __packed; /* SCAN_START_NTF_API_S_VER_1 */
/* scan results probe_status first bit indicates success */
#define SCAN_PROBE_STATUS_OK 0
#define SCAN_PROBE_STATUS_TX_FAILED BIT(0)
/* error statuses combined with TX_FAILED */
#define SCAN_PROBE_STATUS_FAIL_TTL BIT(1)
#define SCAN_PROBE_STATUS_FAIL_BT BIT(2)
/* How many statistics are gathered for each channel */ /* How many statistics are gathered for each channel */
#define SCAN_RESULTS_STATISTICS 1 #define SCAN_RESULTS_STATISTICS 1
......
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
#ifndef __fw_api_stats_h__ #ifndef __fw_api_stats_h__
#define __fw_api_stats_h__ #define __fw_api_stats_h__
#include "fw-api-mac.h"
struct mvm_statistics_dbg { struct mvm_statistics_dbg {
__le32 burst_check; __le32 burst_check;
...@@ -218,7 +219,7 @@ struct mvm_statistics_bt_activity { ...@@ -218,7 +219,7 @@ struct mvm_statistics_bt_activity {
__le32 lo_priority_rx_denied_cnt; __le32 lo_priority_rx_denied_cnt;
} __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */ } __packed; /* STATISTICS_BT_ACTIVITY_API_S_VER_1 */
struct mvm_statistics_general { struct mvm_statistics_general_v5 {
__le32 radio_temperature; __le32 radio_temperature;
__le32 radio_voltage; __le32 radio_voltage;
struct mvm_statistics_dbg dbg; struct mvm_statistics_dbg dbg;
...@@ -244,6 +245,39 @@ struct mvm_statistics_general { ...@@ -244,6 +245,39 @@ struct mvm_statistics_general {
struct mvm_statistics_bt_activity bt_activity; struct mvm_statistics_bt_activity bt_activity;
} __packed; /* STATISTICS_GENERAL_API_S_VER_5 */ } __packed; /* STATISTICS_GENERAL_API_S_VER_5 */
struct mvm_statistics_general_v8 {
__le32 radio_temperature;
__le32 radio_voltage;
struct mvm_statistics_dbg dbg;
__le32 sleep_time;
__le32 slots_out;
__le32 slots_idle;
__le32 ttl_timestamp;
struct mvm_statistics_div slow_div;
__le32 rx_enable_counter;
/*
* num_of_sos_states:
* count the number of times we have to re-tune
* in order to get out of bad PHY status
*/
__le32 num_of_sos_states;
__le32 beacon_filtered;
__le32 missed_beacons;
__s8 beacon_filter_average_energy;
__s8 beacon_filter_reason;
__s8 beacon_filter_current_energy;
__s8 beacon_filter_reserved;
__le32 beacon_filter_delta_time;
struct mvm_statistics_bt_activity bt_activity;
__le64 rx_time;
__le64 on_time_rf;
__le64 on_time_scan;
__le64 tx_time;
__le32 beacon_counter[NUM_MAC_INDEX];
u8 beacon_average_energy[NUM_MAC_INDEX];
u8 reserved[4 - (NUM_MAC_INDEX % 4)];
} __packed; /* STATISTICS_GENERAL_API_S_VER_8 */
struct mvm_statistics_rx { struct mvm_statistics_rx {
struct mvm_statistics_rx_phy ofdm; struct mvm_statistics_rx_phy ofdm;
struct mvm_statistics_rx_phy cck; struct mvm_statistics_rx_phy cck;
...@@ -256,22 +290,28 @@ struct mvm_statistics_rx { ...@@ -256,22 +290,28 @@ struct mvm_statistics_rx {
* *
* By default, uCode issues this notification after receiving a beacon * By default, uCode issues this notification after receiving a beacon
* while associated. To disable this behavior, set DISABLE_NOTIF flag in the * while associated. To disable this behavior, set DISABLE_NOTIF flag in the
* REPLY_STATISTICS_CMD 0x9c, above. * STATISTICS_CMD (0x9c), below.
*
* Statistics counters continue to increment beacon after beacon, but are
* cleared when changing channels or when driver issues REPLY_STATISTICS_CMD
* 0x9c with CLEAR_STATS bit set (see above).
*
* uCode also issues this notification during scans. uCode clears statistics
* appropriately so that each notification contains statistics for only the
* one channel that has just been scanned.
*/ */
struct iwl_notif_statistics { struct iwl_notif_statistics_v8 {
__le32 flag; __le32 flag;
struct mvm_statistics_rx rx; struct mvm_statistics_rx rx;
struct mvm_statistics_tx tx; struct mvm_statistics_tx tx;
struct mvm_statistics_general general; struct mvm_statistics_general_v5 general;
} __packed; /* STATISTICS_NTFY_API_S_VER_8 */ } __packed; /* STATISTICS_NTFY_API_S_VER_8 */
struct iwl_notif_statistics_v10 {
__le32 flag;
struct mvm_statistics_rx rx;
struct mvm_statistics_tx tx;
struct mvm_statistics_general_v8 general;
} __packed; /* STATISTICS_NTFY_API_S_VER_10 */
#define IWL_STATISTICS_FLG_CLEAR 0x1
#define IWL_STATISTICS_FLG_DISABLE_NOTIF 0x2
struct iwl_statistics_cmd {
__le32 flags;
} __packed; /* STATISTICS_CMD_API_S_VER_1 */
#endif /* __fw_api_stats_h__ */ #endif /* __fw_api_stats_h__ */
...@@ -192,6 +192,7 @@ enum { ...@@ -192,6 +192,7 @@ enum {
BEACON_NOTIFICATION = 0x90, BEACON_NOTIFICATION = 0x90,
BEACON_TEMPLATE_CMD = 0x91, BEACON_TEMPLATE_CMD = 0x91,
TX_ANT_CONFIGURATION_CMD = 0x98, TX_ANT_CONFIGURATION_CMD = 0x98,
STATISTICS_CMD = 0x9c,
STATISTICS_NOTIFICATION = 0x9d, STATISTICS_NOTIFICATION = 0x9d,
EOSP_NOTIFICATION = 0x9e, EOSP_NOTIFICATION = 0x9e,
REDUCE_TX_POWER_CMD = 0x9f, REDUCE_TX_POWER_CMD = 0x9f,
...@@ -431,7 +432,7 @@ enum { ...@@ -431,7 +432,7 @@ enum {
#define IWL_ALIVE_FLG_RFKILL BIT(0) #define IWL_ALIVE_FLG_RFKILL BIT(0)
struct mvm_alive_resp { struct mvm_alive_resp_ver1 {
__le16 status; __le16 status;
__le16 flags; __le16 flags;
u8 ucode_minor; u8 ucode_minor;
...@@ -482,6 +483,30 @@ struct mvm_alive_resp_ver2 { ...@@ -482,6 +483,30 @@ struct mvm_alive_resp_ver2 {
__le32 dbg_print_buff_addr; __le32 dbg_print_buff_addr;
} __packed; /* ALIVE_RES_API_S_VER_2 */ } __packed; /* ALIVE_RES_API_S_VER_2 */
struct mvm_alive_resp {
__le16 status;
__le16 flags;
__le32 ucode_minor;
__le32 ucode_major;
u8 ver_subtype;
u8 ver_type;
u8 mac;
u8 opt;
__le32 timestamp;
__le32 error_event_table_ptr; /* SRAM address for error log */
__le32 log_event_table_ptr; /* SRAM address for LMAC event log */
__le32 cpu_register_ptr;
__le32 dbgm_config_ptr;
__le32 alive_counter_ptr;
__le32 scd_base_ptr; /* SRAM address for SCD */
__le32 st_fwrd_addr; /* pointer to Store and forward */
__le32 st_fwrd_size;
__le32 umac_minor; /* UMAC version: minor */
__le32 umac_major; /* UMAC version: major */
__le32 error_info_addr; /* SRAM address for UMAC error log */
__le32 dbg_print_buff_addr;
} __packed; /* ALIVE_RES_API_S_VER_3 */
/* Error response/notification */ /* Error response/notification */
enum { enum {
FW_ERR_UNKNOWN_CMD = 0x0, FW_ERR_UNKNOWN_CMD = 0x0,
......
...@@ -112,25 +112,27 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, ...@@ -112,25 +112,27 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
struct iwl_mvm *mvm = struct iwl_mvm *mvm =
container_of(notif_wait, struct iwl_mvm, notif_wait); container_of(notif_wait, struct iwl_mvm, notif_wait);
struct iwl_mvm_alive_data *alive_data = data; struct iwl_mvm_alive_data *alive_data = data;
struct mvm_alive_resp *palive; struct mvm_alive_resp_ver1 *palive1;
struct mvm_alive_resp_ver2 *palive2; struct mvm_alive_resp_ver2 *palive2;
struct mvm_alive_resp *palive;
if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) { if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive1)) {
palive = (void *)pkt->data; palive1 = (void *)pkt->data;
mvm->support_umac_log = false; mvm->support_umac_log = false;
mvm->error_event_table = mvm->error_event_table =
le32_to_cpu(palive->error_event_table_ptr); le32_to_cpu(palive1->error_event_table_ptr);
mvm->log_event_table = le32_to_cpu(palive->log_event_table_ptr); mvm->log_event_table =
alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr); le32_to_cpu(palive1->log_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive1->scd_base_ptr);
alive_data->valid = le16_to_cpu(palive->status) == alive_data->valid = le16_to_cpu(palive1->status) ==
IWL_ALIVE_STATUS_OK; IWL_ALIVE_STATUS_OK;
IWL_DEBUG_FW(mvm, IWL_DEBUG_FW(mvm,
"Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive->status), palive->ver_type, le16_to_cpu(palive1->status), palive1->ver_type,
palive->ver_subtype, palive->flags); palive1->ver_subtype, palive1->flags);
} else { } else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive2)) {
palive2 = (void *)pkt->data; palive2 = (void *)pkt->data;
mvm->error_event_table = mvm->error_event_table =
...@@ -156,6 +158,33 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, ...@@ -156,6 +158,33 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait,
IWL_DEBUG_FW(mvm, IWL_DEBUG_FW(mvm,
"UMAC version: Major - 0x%x, Minor - 0x%x\n", "UMAC version: Major - 0x%x, Minor - 0x%x\n",
palive2->umac_major, palive2->umac_minor); palive2->umac_major, palive2->umac_minor);
} else if (iwl_rx_packet_payload_len(pkt) == sizeof(*palive)) {
palive = (void *)pkt->data;
mvm->error_event_table =
le32_to_cpu(palive->error_event_table_ptr);
mvm->log_event_table =
le32_to_cpu(palive->log_event_table_ptr);
alive_data->scd_base_addr = le32_to_cpu(palive->scd_base_ptr);
mvm->umac_error_event_table =
le32_to_cpu(palive->error_info_addr);
mvm->sf_space.addr = le32_to_cpu(palive->st_fwrd_addr);
mvm->sf_space.size = le32_to_cpu(palive->st_fwrd_size);
alive_data->valid = le16_to_cpu(palive->status) ==
IWL_ALIVE_STATUS_OK;
if (mvm->umac_error_event_table)
mvm->support_umac_log = true;
IWL_DEBUG_FW(mvm,
"Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n",
le16_to_cpu(palive->status), palive->ver_type,
palive->ver_subtype, palive->flags);
IWL_DEBUG_FW(mvm,
"UMAC version: Major - 0x%x, Minor - 0x%x\n",
le32_to_cpu(palive->umac_major),
le32_to_cpu(palive->umac_minor));
} }
return true; return true;
...@@ -188,8 +217,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, ...@@ -188,8 +217,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm,
struct iwl_sf_region st_fwrd_space; struct iwl_sf_region st_fwrd_space;
if (ucode_type == IWL_UCODE_REGULAR && if (ucode_type == IWL_UCODE_REGULAR &&
iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_CUSTOM) && iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE))
iwl_fw_dbg_conf_enabled(mvm->fw, FW_DBG_CUSTOM))
fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER); fw = iwl_get_ucode_image(mvm, IWL_UCODE_REGULAR_USNIFFER);
else else
fw = iwl_get_ucode_image(mvm, ucode_type); fw = iwl_get_ucode_image(mvm, ucode_type);
...@@ -451,20 +479,80 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm) ...@@ -451,20 +479,80 @@ static void iwl_mvm_get_shared_mem_conf(struct iwl_mvm *mvm)
iwl_free_resp(&cmd); iwl_free_resp(&cmd);
} }
void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm) int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
struct iwl_mvm_dump_desc *desc,
unsigned int delay)
{ {
if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status))
return -EBUSY;
if (WARN_ON(mvm->fw_dump_desc))
iwl_mvm_free_fw_dump_desc(mvm);
IWL_WARN(mvm, "Collecting data: trigger %d fired.\n",
le32_to_cpu(desc->trig_desc.type));
mvm->fw_dump_desc = desc;
/* stop recording */ /* stop recording */
if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) { if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000) {
iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
} else { } else {
iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0); iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 0);
iwl_write_prph(mvm->trans, DBGC_OUT_CTRL, 0); /* wait before we collect the data till the DBGC stop */
udelay(100);
} }
schedule_work(&mvm->fw_error_dump_wk); queue_delayed_work(system_wq, &mvm->fw_dump_wk, delay);
return 0;
}
int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
const char *str, size_t len, unsigned int delay)
{
struct iwl_mvm_dump_desc *desc;
desc = kzalloc(sizeof(*desc) + len, GFP_ATOMIC);
if (!desc)
return -ENOMEM;
desc->len = len;
desc->trig_desc.type = cpu_to_le32(trig);
memcpy(desc->trig_desc.data, str, len);
return iwl_mvm_fw_dbg_collect_desc(mvm, desc, delay);
}
int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
struct iwl_fw_dbg_trigger_tlv *trigger,
const char *str, size_t len)
{
unsigned int delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay));
u16 occurrences = le16_to_cpu(trigger->occurrences);
int ret;
if (!occurrences)
return 0;
ret = iwl_mvm_fw_dbg_collect(mvm, le32_to_cpu(trigger->id), str,
len, delay);
if (ret)
return ret;
trigger->occurrences = cpu_to_le16(occurrences - 1);
return 0;
} }
int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id) static inline void iwl_mvm_restart_early_start(struct iwl_mvm *mvm)
{
if (mvm->cfg->device_family == IWL_DEVICE_FAMILY_7000)
iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100);
else
iwl_write_prph(mvm->trans, DBGC_IN_SAMPLE, 1);
}
int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 conf_id)
{ {
u8 *ptr; u8 *ptr;
int ret; int ret;
...@@ -474,6 +562,14 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id) ...@@ -474,6 +562,14 @@ int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf conf_id)
"Invalid configuration %d\n", conf_id)) "Invalid configuration %d\n", conf_id))
return -EINVAL; return -EINVAL;
/* EARLY START - firmware's configuration is hard coded */
if ((!mvm->fw->dbg_conf_tlv[conf_id] ||
!mvm->fw->dbg_conf_tlv[conf_id]->num_of_hcmds) &&
conf_id == FW_DBG_START_FROM_ALIVE) {
iwl_mvm_restart_early_start(mvm);
return 0;
}
if (!mvm->fw->dbg_conf_tlv[conf_id]) if (!mvm->fw->dbg_conf_tlv[conf_id])
return -EINVAL; return -EINVAL;
...@@ -583,7 +679,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ...@@ -583,7 +679,10 @@ int iwl_mvm_up(struct iwl_mvm *mvm)
IWL_ERR(mvm, "Failed to initialize Smart Fifo\n"); IWL_ERR(mvm, "Failed to initialize Smart Fifo\n");
mvm->fw_dbg_conf = FW_DBG_INVALID; mvm->fw_dbg_conf = FW_DBG_INVALID;
iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_CUSTOM); /* if we have a destination, assume EARLY START */
if (mvm->fw->dbg_dest_tlv)
mvm->fw_dbg_conf = FW_DBG_START_FROM_ALIVE;
iwl_mvm_start_fw_dbg_conf(mvm, FW_DBG_START_FROM_ALIVE);
ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm)); ret = iwl_send_tx_ant_cfg(mvm, iwl_mvm_get_valid_tx_ant(mvm));
if (ret) if (ret)
......
...@@ -244,6 +244,7 @@ static void iwl_mvm_mac_sta_hw_queues_iter(void *_data, ...@@ -244,6 +244,7 @@ static void iwl_mvm_mac_sta_hw_queues_iter(void *_data,
unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
struct ieee80211_vif *exclude_vif) struct ieee80211_vif *exclude_vif)
{ {
u8 sta_id;
struct iwl_mvm_hw_queues_iface_iterator_data data = { struct iwl_mvm_hw_queues_iface_iterator_data data = {
.exclude_vif = exclude_vif, .exclude_vif = exclude_vif,
.used_hw_queues = .used_hw_queues =
...@@ -264,6 +265,13 @@ unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, ...@@ -264,6 +265,13 @@ unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
iwl_mvm_mac_sta_hw_queues_iter, iwl_mvm_mac_sta_hw_queues_iter,
&data); &data);
/*
* Some TDLS stations may be removed but are in the process of being
* drained. Don't touch their queues.
*/
for_each_set_bit(sta_id, mvm->sta_drained, IWL_MVM_STATION_COUNT)
data.used_hw_queues |= mvm->tfd_drained[sta_id];
return data.used_hw_queues; return data.used_hw_queues;
} }
...@@ -1367,10 +1375,18 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, ...@@ -1367,10 +1375,18 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
{ {
struct iwl_missed_beacons_notif *missed_beacons = _data; struct iwl_missed_beacons_notif *missed_beacons = _data;
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm *mvm = mvmvif->mvm;
struct iwl_fw_dbg_trigger_missed_bcon *bcon_trig;
struct iwl_fw_dbg_trigger_tlv *trigger;
u32 stop_trig_missed_bcon, stop_trig_missed_bcon_since_rx;
u32 rx_missed_bcon, rx_missed_bcon_since_rx;
if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id)) if (mvmvif->id != (u16)le32_to_cpu(missed_beacons->mac_id))
return; return;
rx_missed_bcon = le32_to_cpu(missed_beacons->consec_missed_beacons);
rx_missed_bcon_since_rx =
le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx);
/* /*
* TODO: the threshold should be adjusted based on latency conditions, * TODO: the threshold should be adjusted based on latency conditions,
* and/or in case of a CS flow on one of the other AP vifs. * and/or in case of a CS flow on one of the other AP vifs.
...@@ -1378,6 +1394,26 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac, ...@@ -1378,6 +1394,26 @@ static void iwl_mvm_beacon_loss_iterator(void *_data, u8 *mac,
if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) > if (le32_to_cpu(missed_beacons->consec_missed_beacons_since_last_rx) >
IWL_MVM_MISSED_BEACONS_THRESHOLD) IWL_MVM_MISSED_BEACONS_THRESHOLD)
ieee80211_beacon_loss(vif); ieee80211_beacon_loss(vif);
if (!iwl_fw_dbg_trigger_enabled(mvm->fw,
FW_DBG_TRIGGER_MISSED_BEACONS))
return;
trigger = iwl_fw_dbg_get_trigger(mvm->fw,
FW_DBG_TRIGGER_MISSED_BEACONS);
bcon_trig = (void *)trigger->data;
stop_trig_missed_bcon = le32_to_cpu(bcon_trig->stop_consec_missed_bcon);
stop_trig_missed_bcon_since_rx =
le32_to_cpu(bcon_trig->stop_consec_missed_bcon_since_rx);
/* TODO: implement start trigger */
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
return;
if (rx_missed_bcon_since_rx >= stop_trig_missed_bcon_since_rx ||
rx_missed_bcon >= stop_trig_missed_bcon)
iwl_mvm_fw_dbg_collect_trig(mvm, trigger, NULL, 0);
} }
int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm, int iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
......
...@@ -339,13 +339,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) ...@@ -339,13 +339,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
!iwlwifi_mod_params.sw_crypto) !iwlwifi_mod_params.sw_crypto)
hw->flags |= IEEE80211_HW_MFP_CAPABLE; hw->flags |= IEEE80211_HW_MFP_CAPABLE;
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN ||
mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS; hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
hw->wiphy->features |= hw->wiphy->features |=
NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
}
hw->sta_data_size = sizeof(struct iwl_mvm_sta); hw->sta_data_size = sizeof(struct iwl_mvm_sta);
hw->vif_data_size = sizeof(struct iwl_mvm_vif); hw->vif_data_size = sizeof(struct iwl_mvm_vif);
...@@ -889,12 +886,23 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, ...@@ -889,12 +886,23 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm,
iwl_trans_release_nic_access(mvm->trans, &flags); iwl_trans_release_nic_access(mvm->trans, &flags);
} }
void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm)
{
if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert ||
!mvm->fw_dump_desc)
return;
kfree(mvm->fw_dump_desc);
mvm->fw_dump_desc = NULL;
}
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
{ {
struct iwl_fw_error_dump_file *dump_file; struct iwl_fw_error_dump_file *dump_file;
struct iwl_fw_error_dump_data *dump_data; struct iwl_fw_error_dump_data *dump_data;
struct iwl_fw_error_dump_info *dump_info; struct iwl_fw_error_dump_info *dump_info;
struct iwl_fw_error_dump_mem *dump_mem; struct iwl_fw_error_dump_mem *dump_mem;
struct iwl_fw_error_dump_trigger_desc *dump_trig;
struct iwl_mvm_dump_ptrs *fw_error_dump; struct iwl_mvm_dump_ptrs *fw_error_dump;
u32 sram_len, sram_ofs; u32 sram_len, sram_ofs;
u32 file_len, fifo_data_len = 0; u32 file_len, fifo_data_len = 0;
...@@ -964,6 +972,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -964,6 +972,10 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
fifo_data_len + fifo_data_len +
sizeof(*dump_info); sizeof(*dump_info);
if (mvm->fw_dump_desc)
file_len += sizeof(*dump_data) + sizeof(*dump_trig) +
mvm->fw_dump_desc->len;
/* Make room for the SMEM, if it exists */ /* Make room for the SMEM, if it exists */
if (smem_len) if (smem_len)
file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len; file_len += sizeof(*dump_data) + sizeof(*dump_mem) + smem_len;
...@@ -975,6 +987,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -975,6 +987,7 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
dump_file = vzalloc(file_len); dump_file = vzalloc(file_len);
if (!dump_file) { if (!dump_file) {
kfree(fw_error_dump); kfree(fw_error_dump);
iwl_mvm_free_fw_dump_desc(mvm);
return; return;
} }
...@@ -1003,6 +1016,19 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -1003,6 +1016,19 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
if (test_bit(STATUS_FW_ERROR, &mvm->trans->status)) if (test_bit(STATUS_FW_ERROR, &mvm->trans->status))
iwl_mvm_dump_fifos(mvm, &dump_data); iwl_mvm_dump_fifos(mvm, &dump_data);
if (mvm->fw_dump_desc) {
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_ERROR_INFO);
dump_data->len = cpu_to_le32(sizeof(*dump_trig) +
mvm->fw_dump_desc->len);
dump_trig = (void *)dump_data->data;
memcpy(dump_trig, &mvm->fw_dump_desc->trig_desc,
sizeof(*dump_trig) + mvm->fw_dump_desc->len);
/* now we can free this copy */
iwl_mvm_free_fw_dump_desc(mvm);
dump_data = iwl_fw_error_next_data(dump_data);
}
dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM);
dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem)); dump_data->len = cpu_to_le32(sram_len + sizeof(*dump_mem));
dump_mem = (void *)dump_data->data; dump_mem = (void *)dump_data->data;
...@@ -1041,16 +1067,26 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm) ...@@ -1041,16 +1067,26 @@ void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm)
dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0, dev_coredumpm(mvm->trans->dev, THIS_MODULE, fw_error_dump, 0,
GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump); GFP_KERNEL, iwl_mvm_read_coredump, iwl_mvm_free_coredump);
clear_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status);
} }
struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert = {
.trig_desc = {
.type = cpu_to_le32(FW_DBG_TRIGGER_FW_ASSERT),
},
};
static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
{ {
/* clear the D3 reconfig, we only need it to avoid dumping a /* clear the D3 reconfig, we only need it to avoid dumping a
* firmware coredump on reconfiguration, we shouldn't do that * firmware coredump on reconfiguration, we shouldn't do that
* on D3->D0 transition * on D3->D0 transition
*/ */
if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) if (!test_and_clear_bit(IWL_MVM_STATUS_D3_RECONFIG, &mvm->status)) {
mvm->fw_dump_desc = &iwl_mvm_dump_desc_assert;
iwl_mvm_fw_error_dump(mvm); iwl_mvm_fw_error_dump(mvm);
}
/* cleanup all stale references (scan, roc), but keep the /* cleanup all stale references (scan, roc), but keep the
* ucode_down ref until reconfig is complete * ucode_down ref until reconfig is complete
...@@ -1091,6 +1127,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm) ...@@ -1091,6 +1127,10 @@ static void iwl_mvm_restart_cleanup(struct iwl_mvm *mvm)
mvm->vif_count = 0; mvm->vif_count = 0;
mvm->rx_ba_sessions = 0; mvm->rx_ba_sessions = 0;
mvm->fw_dbg_conf = FW_DBG_INVALID;
/* keep statistics ticking */
iwl_mvm_accu_radio_stats(mvm);
} }
int __iwl_mvm_mac_start(struct iwl_mvm *mvm) int __iwl_mvm_mac_start(struct iwl_mvm *mvm)
...@@ -1213,6 +1253,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm) ...@@ -1213,6 +1253,11 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm)
{ {
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
/* firmware counters are obviously reset now, but we shouldn't
* partially track so also clear the fw_reset_accu counters.
*/
memset(&mvm->accu_radio_stats, 0, sizeof(mvm->accu_radio_stats));
/* /*
* Disallow low power states when the FW is down by taking * Disallow low power states when the FW is down by taking
* the UCODE_DOWN ref. in case of ongoing hw restart the * the UCODE_DOWN ref. in case of ongoing hw restart the
...@@ -1252,7 +1297,8 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) ...@@ -1252,7 +1297,8 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw)
flush_work(&mvm->d0i3_exit_work); flush_work(&mvm->d0i3_exit_work);
flush_work(&mvm->async_handlers_wk); flush_work(&mvm->async_handlers_wk);
flush_work(&mvm->fw_error_dump_wk); cancel_delayed_work_sync(&mvm->fw_dump_wk);
iwl_mvm_free_fw_dump_desc(mvm);
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
__iwl_mvm_mac_stop(mvm); __iwl_mvm_mac_stop(mvm);
...@@ -1317,6 +1363,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw, ...@@ -1317,6 +1363,11 @@ static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
/* make sure that beacon statistics don't go backwards with FW reset */
if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
mvmvif->beacon_stats.accu_num_beacons +=
mvmvif->beacon_stats.num_beacons;
/* Allocate resources for the MAC context, and add it to the fw */ /* Allocate resources for the MAC context, and add it to the fw */
ret = iwl_mvm_mac_ctxt_init(mvm, vif); ret = iwl_mvm_mac_ctxt_init(mvm, vif);
if (ret) if (ret)
...@@ -1810,6 +1861,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, ...@@ -1810,6 +1861,11 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm,
if (changes & BSS_CHANGED_ASSOC) { if (changes & BSS_CHANGED_ASSOC) {
if (bss_conf->assoc) { if (bss_conf->assoc) {
/* clear statistics to get clean beacon counter */
iwl_mvm_request_statistics(mvm, true);
memset(&mvmvif->beacon_stats, 0,
sizeof(mvmvif->beacon_stats));
/* add quota for this interface */ /* add quota for this interface */
ret = iwl_mvm_update_quotas(mvm, NULL); ret = iwl_mvm_update_quotas(mvm, NULL);
if (ret) { if (ret) {
...@@ -2196,10 +2252,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw, ...@@ -2196,10 +2252,8 @@ static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
ret = iwl_mvm_scan_umac(mvm, vif, hw_req); ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
else if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN)
ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
else else
ret = iwl_mvm_scan_request(mvm, vif, req); ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
if (ret) if (ret)
iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN); iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
...@@ -2527,16 +2581,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw, ...@@ -2527,16 +2581,10 @@ static int iwl_mvm_mac_sched_scan_start(struct ieee80211_hw *hw,
mutex_lock(&mvm->mutex); mutex_lock(&mvm->mutex);
/* Newest FW fixes sched scan while connected on another interface */
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_LMAC_SCAN) {
if (!vif->bss_conf.idle) { if (!vif->bss_conf.idle) {
ret = -EBUSY; ret = -EBUSY;
goto out; goto out;
} }
} else if (!iwl_mvm_is_idle(mvm)) {
ret = -EBUSY;
goto out;
}
if (mvm->scan_status != IWL_MVM_SCAN_NONE) { if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
ret = -EBUSY; ret = -EBUSY;
...@@ -3433,6 +3481,9 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, ...@@ -3433,6 +3481,9 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw,
IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n", IWL_DEBUG_MAC80211(mvm, "pre CSA to freq %d\n",
chsw->chandef.center_freq1); chsw->chandef.center_freq1);
iwl_fw_dbg_trigger_simple_stop(mvm, vif, FW_DBG_TRIGGER_CHANNEL_SWITCH,
NULL, 0);
switch (vif->type) { switch (vif->type) {
case NL80211_IFTYPE_AP: case NL80211_IFTYPE_AP:
csa_vif = csa_vif =
...@@ -3581,6 +3632,95 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw, ...@@ -3581,6 +3632,95 @@ static void iwl_mvm_mac_flush(struct ieee80211_hw *hw,
} }
} }
static int iwl_mvm_mac_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
int ret;
memset(survey, 0, sizeof(*survey));
/* only support global statistics right now */
if (idx != 0)
return -ENOENT;
if (!(mvm->fw->ucode_capa.capa[0] &
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return -ENOENT;
mutex_lock(&mvm->mutex);
if (mvm->ucode_loaded) {
ret = iwl_mvm_request_statistics(mvm, false);
if (ret)
goto out;
}
survey->filled = SURVEY_INFO_TIME |
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_TX |
SURVEY_INFO_TIME_SCAN;
survey->time = mvm->accu_radio_stats.on_time_rf +
mvm->radio_stats.on_time_rf;
do_div(survey->time, USEC_PER_MSEC);
survey->time_rx = mvm->accu_radio_stats.rx_time +
mvm->radio_stats.rx_time;
do_div(survey->time_rx, USEC_PER_MSEC);
survey->time_tx = mvm->accu_radio_stats.tx_time +
mvm->radio_stats.tx_time;
do_div(survey->time_tx, USEC_PER_MSEC);
survey->time_scan = mvm->accu_radio_stats.on_time_scan +
mvm->radio_stats.on_time_scan;
do_div(survey->time_scan, USEC_PER_MSEC);
out:
mutex_unlock(&mvm->mutex);
return ret;
}
static void iwl_mvm_mac_sta_statistics(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
struct station_info *sinfo)
{
struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
if (!(mvm->fw->ucode_capa.capa[0] &
IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
return;
/* if beacon filtering isn't on mac80211 does it anyway */
if (!(vif->driver_flags & IEEE80211_VIF_BEACON_FILTER))
return;
if (!vif->bss_conf.assoc)
return;
mutex_lock(&mvm->mutex);
if (mvmvif->ap_sta_id != mvmsta->sta_id)
goto unlock;
if (iwl_mvm_request_statistics(mvm, false))
goto unlock;
sinfo->rx_beacon = mvmvif->beacon_stats.num_beacons +
mvmvif->beacon_stats.accu_num_beacons;
sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_RX);
if (mvmvif->beacon_stats.avg_signal) {
/* firmware only reports a value after RXing a few beacons */
sinfo->rx_beacon_signal_avg = mvmvif->beacon_stats.avg_signal;
sinfo->filled |= BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
}
unlock:
mutex_unlock(&mvm->mutex);
}
const struct ieee80211_ops iwl_mvm_hw_ops = { const struct ieee80211_ops iwl_mvm_hw_ops = {
.tx = iwl_mvm_mac_tx, .tx = iwl_mvm_mac_tx,
.ampdu_action = iwl_mvm_mac_ampdu_action, .ampdu_action = iwl_mvm_mac_ampdu_action,
...@@ -3647,4 +3787,6 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { ...@@ -3647,4 +3787,6 @@ const struct ieee80211_ops iwl_mvm_hw_ops = {
#endif #endif
.set_default_unicast_key = iwl_mvm_set_default_unicast_key, .set_default_unicast_key = iwl_mvm_set_default_unicast_key,
#endif #endif
.get_survey = iwl_mvm_mac_get_survey,
.sta_statistics = iwl_mvm_mac_sta_statistics,
}; };
...@@ -75,6 +75,7 @@ ...@@ -75,6 +75,7 @@
#include "iwl-trans.h" #include "iwl-trans.h"
#include "iwl-notif-wait.h" #include "iwl-notif-wait.h"
#include "iwl-eeprom-parse.h" #include "iwl-eeprom-parse.h"
#include "iwl-fw-file.h"
#include "sta.h" #include "sta.h"
#include "fw-api.h" #include "fw-api.h"
#include "constants.h" #include "constants.h"
...@@ -145,6 +146,19 @@ struct iwl_mvm_dump_ptrs { ...@@ -145,6 +146,19 @@ struct iwl_mvm_dump_ptrs {
u32 op_mode_len; u32 op_mode_len;
}; };
/**
* struct iwl_mvm_dump_desc - describes the dump
* @len: length of trig_desc->data
* @trig_desc: the description of the dump
*/
struct iwl_mvm_dump_desc {
size_t len;
/* must be last */
struct iwl_fw_error_dump_trigger_desc trig_desc;
};
extern struct iwl_mvm_dump_desc iwl_mvm_dump_desc_assert;
struct iwl_mvm_phy_ctxt { struct iwl_mvm_phy_ctxt {
u16 id; u16 id;
u16 color; u16 color;
...@@ -337,6 +351,9 @@ struct iwl_mvm_vif_bf_data { ...@@ -337,6 +351,9 @@ struct iwl_mvm_vif_bf_data {
* @beacon_skb: the skb used to hold the AP/GO beacon template * @beacon_skb: the skb used to hold the AP/GO beacon template
* @smps_requests: the SMPS requests of differents parts of the driver, * @smps_requests: the SMPS requests of differents parts of the driver,
* combined on update to yield the overall request to mac80211. * combined on update to yield the overall request to mac80211.
* @beacon_stats: beacon statistics, containing the # of received beacons,
* # of received beacons accumulated over FW restart, and the current
* average signal of beacons retrieved from the firmware
*/ */
struct iwl_mvm_vif { struct iwl_mvm_vif {
u16 id; u16 id;
...@@ -354,6 +371,11 @@ struct iwl_mvm_vif { ...@@ -354,6 +371,11 @@ struct iwl_mvm_vif {
bool ps_disabled; bool ps_disabled;
struct iwl_mvm_vif_bf_data bf_data; struct iwl_mvm_vif_bf_data bf_data;
struct {
u32 num_beacons, accu_num_beacons;
u8 avg_signal;
} beacon_stats;
u32 ap_beacon_time; u32 ap_beacon_time;
enum iwl_tsf_id tsf_id; enum iwl_tsf_id tsf_id;
...@@ -593,6 +615,13 @@ struct iwl_mvm { ...@@ -593,6 +615,13 @@ struct iwl_mvm {
struct mvm_statistics_rx rx_stats; struct mvm_statistics_rx rx_stats;
struct {
u64 rx_time;
u64 tx_time;
u64 on_time_rf;
u64 on_time_scan;
} radio_stats, accu_radio_stats;
u8 queue_to_mac80211[IWL_MAX_HW_QUEUES]; u8 queue_to_mac80211[IWL_MAX_HW_QUEUES];
atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES]; atomic_t mac80211_queue_stop_count[IEEE80211_MAX_QUEUES];
...@@ -666,6 +695,7 @@ struct iwl_mvm { ...@@ -666,6 +695,7 @@ struct iwl_mvm {
struct iwl_mvm_frame_stats drv_rx_stats; struct iwl_mvm_frame_stats drv_rx_stats;
spinlock_t drv_stats_lock; spinlock_t drv_stats_lock;
u16 dbgfs_rx_phyinfo;
#endif #endif
struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX]; struct iwl_mvm_phy_ctxt phy_ctxts[NUM_PHY_CTX];
...@@ -687,8 +717,9 @@ struct iwl_mvm { ...@@ -687,8 +717,9 @@ struct iwl_mvm {
/* -1 for always, 0 for never, >0 for that many times */ /* -1 for always, 0 for never, >0 for that many times */
s8 restart_fw; s8 restart_fw;
struct work_struct fw_error_dump_wk; u8 fw_dbg_conf;
enum iwl_fw_dbg_conf fw_dbg_conf; struct delayed_work fw_dump_wk;
struct iwl_mvm_dump_desc *fw_dump_desc;
#ifdef CONFIG_IWLWIFI_LEDS #ifdef CONFIG_IWLWIFI_LEDS
struct led_classdev led; struct led_classdev led;
...@@ -824,6 +855,7 @@ enum iwl_mvm_status { ...@@ -824,6 +855,7 @@ enum iwl_mvm_status {
IWL_MVM_STATUS_IN_D0I3, IWL_MVM_STATUS_IN_D0I3,
IWL_MVM_STATUS_ROC_AUX_RUNNING, IWL_MVM_STATUS_ROC_AUX_RUNNING,
IWL_MVM_STATUS_D3_RECONFIG, IWL_MVM_STATUS_D3_RECONFIG,
IWL_MVM_STATUS_DUMPING_FW_LOG,
}; };
static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_radio_killed(struct iwl_mvm *mvm)
...@@ -883,6 +915,12 @@ static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm) ...@@ -883,6 +915,12 @@ static inline bool iwl_mvm_is_scd_cfg_supported(struct iwl_mvm *mvm)
return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG; return mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_SCD_CFG;
} }
static inline bool iwl_mvm_bt_is_plcr_supported(struct iwl_mvm *mvm)
{
return (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_BT_COEX_PLCR) &&
IWL_MVM_BT_COEX_CORUNNING;
}
extern const u8 iwl_mvm_ac_to_tx_fifo[]; extern const u8 iwl_mvm_ac_to_tx_fifo[];
struct iwl_rate_info { struct iwl_rate_info {
...@@ -951,12 +989,13 @@ static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm) ...@@ -951,12 +989,13 @@ static inline void iwl_mvm_wait_for_async_handlers(struct iwl_mvm *mvm)
} }
/* Statistics */ /* Statistics */
int iwl_mvm_rx_reply_statistics(struct iwl_mvm *mvm, void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_packet *pkt);
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm, int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear);
void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm);
/* NVM */ /* NVM */
int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic); int iwl_nvm_init(struct iwl_mvm *mvm, bool read_nvm_from_nic);
...@@ -1072,13 +1111,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, ...@@ -1072,13 +1111,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm,
/* Scanning */ /* Scanning */
int iwl_mvm_scan_size(struct iwl_mvm *mvm); int iwl_mvm_scan_size(struct iwl_mvm *mvm);
int iwl_mvm_scan_request(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_scan_request *req);
int iwl_mvm_rx_scan_response(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_rx_scan_complete(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd);
int iwl_mvm_cancel_scan(struct iwl_mvm *mvm); int iwl_mvm_cancel_scan(struct iwl_mvm *mvm);
int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan); int iwl_mvm_max_scan_ie_len(struct iwl_mvm *mvm, bool is_sched_scan);
...@@ -1089,14 +1121,8 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm, ...@@ -1089,14 +1121,8 @@ int iwl_mvm_rx_scan_offload_complete_notif(struct iwl_mvm *mvm,
int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm, int iwl_mvm_rx_scan_offload_iter_complete_notif(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd); struct iwl_device_cmd *cmd);
int iwl_mvm_config_sched_scan(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req,
struct ieee80211_scan_ies *ies);
int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm, int iwl_mvm_config_sched_scan_profiles(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req); struct cfg80211_sched_scan_request *req);
int iwl_mvm_sched_scan_start(struct iwl_mvm *mvm,
struct cfg80211_sched_scan_request *req);
int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm, int iwl_mvm_scan_offload_start(struct iwl_mvm *mvm,
struct ieee80211_vif *vif, struct ieee80211_vif *vif,
struct cfg80211_sched_scan_request *req, struct cfg80211_sched_scan_request *req,
...@@ -1238,7 +1264,6 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm, ...@@ -1238,7 +1264,6 @@ bool iwl_mvm_bt_coex_is_tpc_allowed(struct iwl_mvm *mvm,
u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, u8 iwl_mvm_bt_coex_tx_prio(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr,
struct ieee80211_tx_info *info, u8 ac); struct ieee80211_tx_info *info, u8 ac);
bool iwl_mvm_bt_coex_is_ant_avail_old(struct iwl_mvm *mvm, u8 ant);
bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm); bool iwl_mvm_bt_coex_is_shared_ant_avail_old(struct iwl_mvm *mvm);
void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm); void iwl_mvm_bt_coex_vif_change_old(struct iwl_mvm *mvm);
int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm); int iwl_send_bt_init_conf_old(struct iwl_mvm *mvm);
...@@ -1352,9 +1377,6 @@ static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue, ...@@ -1352,9 +1377,6 @@ static inline void iwl_mvm_enable_agg_txq(struct iwl_mvm *mvm, int queue,
iwl_mvm_enable_txq(mvm, queue, ssn, &cfg, wdg_timeout); iwl_mvm_enable_txq(mvm, queue, ssn, &cfg, wdg_timeout);
} }
/* Assoc status */
bool iwl_mvm_is_idle(struct iwl_mvm *mvm);
/* Thermal management and CT-kill */ /* Thermal management and CT-kill */
void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff); void iwl_mvm_tt_tx_backoff(struct iwl_mvm *mvm, u32 backoff);
void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp); void iwl_mvm_tt_temp_changed(struct iwl_mvm *mvm, u32 temp);
...@@ -1405,7 +1427,62 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm); ...@@ -1405,7 +1427,62 @@ struct ieee80211_vif *iwl_mvm_get_bss_vif(struct iwl_mvm *mvm);
void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error); void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error);
void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm); void iwl_mvm_fw_error_dump(struct iwl_mvm *mvm);
int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, enum iwl_fw_dbg_conf id); int iwl_mvm_start_fw_dbg_conf(struct iwl_mvm *mvm, u8 id);
void iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm); int iwl_mvm_fw_dbg_collect(struct iwl_mvm *mvm, enum iwl_fw_dbg_trigger trig,
const char *str, size_t len, unsigned int delay);
int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm,
struct iwl_mvm_dump_desc *desc,
unsigned int delay);
void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm);
int iwl_mvm_fw_dbg_collect_trig(struct iwl_mvm *mvm,
struct iwl_fw_dbg_trigger_tlv *trigger,
const char *str, size_t len);
static inline bool
iwl_fw_dbg_trigger_vif_match(struct iwl_fw_dbg_trigger_tlv *trig,
struct ieee80211_vif *vif)
{
u32 trig_vif = le32_to_cpu(trig->vif_type);
return trig_vif == IWL_FW_DBG_CONF_VIF_ANY || vif->type == trig_vif;
}
static inline bool
iwl_fw_dbg_trigger_stop_conf_match(struct iwl_mvm *mvm,
struct iwl_fw_dbg_trigger_tlv *trig)
{
return ((trig->mode & IWL_FW_DBG_TRIGGER_STOP) &&
(mvm->fw_dbg_conf == FW_DBG_INVALID ||
(BIT(mvm->fw_dbg_conf) & le32_to_cpu(trig->stop_conf_ids))));
}
static inline bool
iwl_fw_dbg_trigger_check_stop(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_fw_dbg_trigger_tlv *trig)
{
if (vif && !iwl_fw_dbg_trigger_vif_match(trig, vif))
return false;
return iwl_fw_dbg_trigger_stop_conf_match(mvm, trig);
}
static inline void
iwl_fw_dbg_trigger_simple_stop(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
enum iwl_fw_dbg_trigger trig,
const char *str, size_t len)
{
struct iwl_fw_dbg_trigger_tlv *trigger;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, trig))
return;
trigger = iwl_fw_dbg_get_trigger(mvm->fw, trig);
if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trigger))
return;
iwl_mvm_fw_dbg_collect_trig(mvm, trigger, str, len);
}
#endif /* __IWL_MVM_H__ */ #endif /* __IWL_MVM_H__ */
...@@ -237,8 +237,6 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { ...@@ -237,8 +237,6 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false), RX_HANDLER(EOSP_NOTIFICATION, iwl_mvm_rx_eosp_notif, false),
RX_HANDLER(SCAN_REQUEST_CMD, iwl_mvm_rx_scan_response, false),
RX_HANDLER(SCAN_COMPLETE_NOTIFICATION, iwl_mvm_rx_scan_complete, true),
RX_HANDLER(SCAN_ITERATION_COMPLETE, RX_HANDLER(SCAN_ITERATION_COMPLETE,
iwl_mvm_rx_scan_offload_iter_complete_notif, false), iwl_mvm_rx_scan_offload_iter_complete_notif, false),
RX_HANDLER(SCAN_OFFLOAD_COMPLETE, RX_HANDLER(SCAN_OFFLOAD_COMPLETE,
...@@ -311,6 +309,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = { ...@@ -311,6 +309,7 @@ static const char *const iwl_mvm_cmd_strings[REPLY_MAX] = {
CMD(REPLY_RX_MPDU_CMD), CMD(REPLY_RX_MPDU_CMD),
CMD(BEACON_NOTIFICATION), CMD(BEACON_NOTIFICATION),
CMD(BEACON_TEMPLATE_CMD), CMD(BEACON_TEMPLATE_CMD),
CMD(STATISTICS_CMD),
CMD(STATISTICS_NOTIFICATION), CMD(STATISTICS_NOTIFICATION),
CMD(EOSP_NOTIFICATION), CMD(EOSP_NOTIFICATION),
CMD(REDUCE_TX_POWER_CMD), CMD(REDUCE_TX_POWER_CMD),
...@@ -456,7 +455,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ...@@ -456,7 +455,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
INIT_WORK(&mvm->roc_done_wk, iwl_mvm_roc_done_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->sta_drained_wk, iwl_mvm_sta_drained_wk);
INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work); INIT_WORK(&mvm->d0i3_exit_work, iwl_mvm_d0i3_exit_work);
INIT_WORK(&mvm->fw_error_dump_wk, iwl_mvm_fw_error_dump_wk); 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); INIT_DELAYED_WORK(&mvm->tdls_cs.dwork, iwl_mvm_tdls_ch_switch_work);
spin_lock_init(&mvm->d0i3_tx_lock); spin_lock_init(&mvm->d0i3_tx_lock);
...@@ -504,6 +503,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, ...@@ -504,6 +503,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg,
trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num; trans->dbg_dest_reg_num = mvm->fw->dbg_dest_reg_num;
memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv, memcpy(trans->dbg_conf_tlv, mvm->fw->dbg_conf_tlv,
sizeof(trans->dbg_conf_tlv)); sizeof(trans->dbg_conf_tlv));
trans->dbg_trigger_tlv = mvm->fw->dbg_trigger_tlv;
/* set up notification wait support */ /* set up notification wait support */
iwl_notification_wait_init(&mvm->notif_wait); iwl_notification_wait_init(&mvm->notif_wait);
...@@ -685,6 +685,38 @@ static void iwl_mvm_async_handlers_wk(struct work_struct *wk) ...@@ -685,6 +685,38 @@ static void iwl_mvm_async_handlers_wk(struct work_struct *wk)
mutex_unlock(&mvm->mutex); mutex_unlock(&mvm->mutex);
} }
static inline void iwl_mvm_rx_check_trigger(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_cmd *cmds_trig;
char buf[32];
int i;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF))
return;
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_FW_NOTIF);
cmds_trig = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
return;
for (i = 0; i < ARRAY_SIZE(cmds_trig->cmds); i++) {
/* don't collect on CMD 0 */
if (!cmds_trig->cmds[i].cmd_id)
break;
if (cmds_trig->cmds[i].cmd_id != pkt->hdr.cmd)
continue;
memset(buf, 0, sizeof(buf));
snprintf(buf, sizeof(buf), "CMD 0x%02x received", pkt->hdr.cmd);
iwl_mvm_fw_dbg_collect_trig(mvm, trig, buf, sizeof(buf));
break;
}
}
static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
struct iwl_rx_cmd_buffer *rxb, struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd) struct iwl_device_cmd *cmd)
...@@ -693,6 +725,8 @@ static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode, ...@@ -693,6 +725,8 @@ static int iwl_mvm_rx_dispatch(struct iwl_op_mode *op_mode,
struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode);
u8 i; u8 i;
iwl_mvm_rx_check_trigger(mvm, pkt);
/* /*
* Do the notification wait before RX handlers so * Do the notification wait before RX handlers so
* even if the RX handler consumes the RXB we have * even if the RX handler consumes the RXB we have
...@@ -827,7 +861,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk) ...@@ -827,7 +861,7 @@ static void iwl_mvm_reprobe_wk(struct work_struct *wk)
static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) static void iwl_mvm_fw_error_dump_wk(struct work_struct *work)
{ {
struct iwl_mvm *mvm = struct iwl_mvm *mvm =
container_of(work, struct iwl_mvm, fw_error_dump_wk); container_of(work, struct iwl_mvm, fw_dump_wk.work);
if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT)) if (iwl_mvm_ref_sync(mvm, IWL_MVM_REF_FW_DBG_COLLECT))
return; return;
...@@ -879,7 +913,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error) ...@@ -879,7 +913,7 @@ void iwl_mvm_nic_restart(struct iwl_mvm *mvm, bool fw_error)
* can't recover this since we're already half suspended. * can't recover this since we're already half suspended.
*/ */
if (!mvm->restart_fw && fw_error) { if (!mvm->restart_fw && fw_error) {
schedule_work(&mvm->fw_error_dump_wk); iwl_mvm_fw_dbg_collect_desc(mvm, &iwl_mvm_dump_desc_assert, 0);
} else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART, } else if (test_and_set_bit(IWL_MVM_STATUS_IN_HW_RESTART,
&mvm->status)) { &mvm->status)) {
struct iwl_mvm_reprobe *reprobe; struct iwl_mvm_reprobe *reprobe;
......
...@@ -175,6 +175,8 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm, ...@@ -175,6 +175,8 @@ static void iwl_mvm_phy_ctxt_cmd_data(struct iwl_mvm *mvm,
cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS); cmd->rxchain_info |= cpu_to_le32(idle_cnt << PHY_RX_CHAIN_CNT_POS);
cmd->rxchain_info |= cpu_to_le32(active_cnt << cmd->rxchain_info |= cpu_to_le32(active_cnt <<
PHY_RX_CHAIN_MIMO_CNT_POS); PHY_RX_CHAIN_MIMO_CNT_POS);
if (unlikely(mvm->dbgfs_rx_phyinfo))
cmd->rxchain_info = cpu_to_le32(mvm->dbgfs_rx_phyinfo);
cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm)); cmd->txchain_info = cpu_to_le32(iwl_mvm_get_valid_tx_ant(mvm));
} }
......
This diff is collapsed.
...@@ -170,6 +170,7 @@ struct rs_rate { ...@@ -170,6 +170,7 @@ struct rs_rate {
bool sgi; bool sgi;
bool ldpc; bool ldpc;
bool stbc; bool stbc;
bool bfer;
}; };
...@@ -331,14 +332,14 @@ struct iwl_lq_sta { ...@@ -331,14 +332,14 @@ struct iwl_lq_sta {
/* tx power reduce for this sta */ /* tx power reduce for this sta */
int tpc_reduce; int tpc_reduce;
/* force STBC/BFER/SISO for testing */
enum rs_ss_force_opt ss_force;
/* persistent fields - initialized only once - keep last! */ /* persistent fields - initialized only once - keep last! */
struct lq_sta_pers { struct lq_sta_pers {
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
u32 dbg_fixed_rate; u32 dbg_fixed_rate;
u8 dbg_fixed_txp_reduction; u8 dbg_fixed_txp_reduction;
/* force STBC/BFER/SISO for testing */
enum rs_ss_force_opt ss_force;
#endif #endif
u8 chains; u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS]; s8 chain_signal[IEEE80211_MAX_CHAINS];
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* GPL LICENSE SUMMARY * GPL LICENSE SUMMARY
* *
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of version 2 of the GNU General Public License as * it under the terms of version 2 of the GNU General Public License as
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
* BSD LICENSE * BSD LICENSE
* *
* Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
* Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -345,6 +345,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, ...@@ -345,6 +345,25 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
struct iwl_mvm_sta *mvmsta; struct iwl_mvm_sta *mvmsta;
mvmsta = iwl_mvm_sta_from_mac80211(sta); mvmsta = iwl_mvm_sta_from_mac80211(sta);
rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status);
if (iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_RSSI) &&
ieee80211_is_beacon(hdr->frame_control)) {
struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_fw_dbg_trigger_low_rssi *rssi_trig;
bool trig_check;
s32 rssi;
trig = iwl_fw_dbg_get_trigger(mvm->fw,
FW_DBG_TRIGGER_RSSI);
rssi_trig = (void *)trig->data;
rssi = le32_to_cpu(rssi_trig->rssi);
trig_check =
iwl_fw_dbg_trigger_check_stop(mvm, mvmsta->vif,
trig);
if (trig_check && rx_status->signal < rssi)
iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0);
}
} }
rcu_read_unlock(); rcu_read_unlock();
...@@ -416,35 +435,43 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, ...@@ -416,35 +435,43 @@ int iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
} }
static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm, static void iwl_mvm_update_rx_statistics(struct iwl_mvm *mvm,
struct iwl_notif_statistics *stats) struct mvm_statistics_rx *rx_stats)
{ {
/*
* NOTE FW aggregates the statistics - BUT the statistics are cleared
* when the driver issues REPLY_STATISTICS_CMD 0x9c with CLEAR_STATS
* bit set.
*/
lockdep_assert_held(&mvm->mutex); lockdep_assert_held(&mvm->mutex);
memcpy(&mvm->rx_stats, &stats->rx, sizeof(struct mvm_statistics_rx));
mvm->rx_stats = *rx_stats;
} }
struct iwl_mvm_stat_data { struct iwl_mvm_stat_data {
struct iwl_notif_statistics *stats;
struct iwl_mvm *mvm; struct iwl_mvm *mvm;
__le32 mac_id;
__s8 beacon_filter_average_energy;
struct mvm_statistics_general_v8 *general;
}; };
static void iwl_mvm_stat_iterator(void *_data, u8 *mac, static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
struct iwl_mvm_stat_data *data = _data; struct iwl_mvm_stat_data *data = _data;
struct iwl_notif_statistics *stats = data->stats;
struct iwl_mvm *mvm = data->mvm; struct iwl_mvm *mvm = data->mvm;
int sig = -stats->general.beacon_filter_average_energy; int sig = -data->beacon_filter_average_energy;
int last_event; int last_event;
int thold = vif->bss_conf.cqm_rssi_thold; int thold = vif->bss_conf.cqm_rssi_thold;
int hyst = vif->bss_conf.cqm_rssi_hyst; int hyst = vif->bss_conf.cqm_rssi_hyst;
u16 id = le32_to_cpu(stats->rx.general.mac_id); u16 id = le32_to_cpu(data->mac_id);
struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
/* This doesn't need the MAC ID check since it's not taking the
* data copied into the "data" struct, but rather the data from
* the notification directly.
*/
if (data->general) {
mvmvif->beacon_stats.num_beacons =
le32_to_cpu(data->general->beacon_counter[mvmvif->id]);
mvmvif->beacon_stats.avg_signal =
-data->general->beacon_average_energy[mvmvif->id];
}
if (mvmvif->id != id) if (mvmvif->id != id)
return; return;
...@@ -500,34 +527,101 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac, ...@@ -500,34 +527,101 @@ static void iwl_mvm_stat_iterator(void *_data, u8 *mac,
} }
} }
/* static inline void
* iwl_mvm_rx_statistics - STATISTICS_NOTIFICATION handler iwl_mvm_rx_stats_check_trigger(struct iwl_mvm *mvm, struct iwl_rx_packet *pkt)
*
* TODO: This handler is implemented partially.
*/
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{ {
struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_fw_dbg_trigger_tlv *trig;
struct iwl_notif_statistics *stats = (void *)&pkt->data; struct iwl_fw_dbg_trigger_stats *trig_stats;
u32 trig_offset, trig_thold;
if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_STATS))
return;
trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_STATS);
trig_stats = (void *)trig->data;
if (!iwl_fw_dbg_trigger_check_stop(mvm, NULL, trig))
return;
trig_offset = le32_to_cpu(trig_stats->stop_offset);
trig_thold = le32_to_cpu(trig_stats->stop_threshold);
if (WARN_ON_ONCE(trig_offset >= iwl_rx_packet_payload_len(pkt)))
return;
if (le32_to_cpup((__le32 *) (pkt->data + trig_offset)) < trig_thold)
return;
iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL, 0);
}
void iwl_mvm_handle_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_packet *pkt)
{
size_t v8_len = sizeof(struct iwl_notif_statistics_v8);
size_t v10_len = sizeof(struct iwl_notif_statistics_v10);
struct iwl_mvm_stat_data data = { struct iwl_mvm_stat_data data = {
.stats = stats,
.mvm = mvm, .mvm = mvm,
}; };
u32 temperature;
if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_STATS_V10) {
struct iwl_notif_statistics_v10 *stats = (void *)&pkt->data;
if (iwl_rx_packet_payload_len(pkt) != v10_len)
goto invalid;
temperature = le32_to_cpu(stats->general.radio_temperature);
data.mac_id = stats->rx.general.mac_id;
data.beacon_filter_average_energy =
stats->general.beacon_filter_average_energy;
iwl_mvm_update_rx_statistics(mvm, &stats->rx);
mvm->radio_stats.rx_time = le64_to_cpu(stats->general.rx_time);
mvm->radio_stats.tx_time = le64_to_cpu(stats->general.tx_time);
mvm->radio_stats.on_time_rf =
le64_to_cpu(stats->general.on_time_rf);
mvm->radio_stats.on_time_scan =
le64_to_cpu(stats->general.on_time_scan);
data.general = &stats->general;
} else {
struct iwl_notif_statistics_v8 *stats = (void *)&pkt->data;
if (iwl_rx_packet_payload_len(pkt) != v8_len)
goto invalid;
temperature = le32_to_cpu(stats->general.radio_temperature);
data.mac_id = stats->rx.general.mac_id;
data.beacon_filter_average_energy =
stats->general.beacon_filter_average_energy;
iwl_mvm_update_rx_statistics(mvm, &stats->rx);
}
iwl_mvm_rx_stats_check_trigger(mvm, pkt);
/* Only handle rx statistics temperature changes if async temp /* Only handle rx statistics temperature changes if async temp
* notifications are not supported * notifications are not supported
*/ */
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_ASYNC_DTM)) if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_ASYNC_DTM))
iwl_mvm_tt_temp_changed(mvm, iwl_mvm_tt_temp_changed(mvm, temperature);
le32_to_cpu(stats->general.radio_temperature));
iwl_mvm_update_rx_statistics(mvm, stats);
ieee80211_iterate_active_interfaces(mvm->hw, ieee80211_iterate_active_interfaces(mvm->hw,
IEEE80211_IFACE_ITER_NORMAL, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_stat_iterator, iwl_mvm_stat_iterator,
&data); &data);
return;
invalid:
IWL_ERR(mvm, "received invalid statistics size (%d)!\n",
iwl_rx_packet_payload_len(pkt));
}
int iwl_mvm_rx_statistics(struct iwl_mvm *mvm,
struct iwl_rx_cmd_buffer *rxb,
struct iwl_device_cmd *cmd)
{
iwl_mvm_handle_rx_statistics(mvm, rxb_addr(rxb));
return 0; return 0;
} }
This diff is collapsed.
...@@ -664,6 +664,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, ...@@ -664,6 +664,8 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm,
info->status.rates[0].count = tx_resp->failure_frame + 1; info->status.rates[0].count = tx_resp->failure_frame + 1;
iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate), iwl_mvm_hwrate_to_tx_status(le32_to_cpu(tx_resp->initial_rate),
info); info);
info->status.status_driver_data[1] =
(void *)(uintptr_t)le32_to_cpu(tx_resp->initial_rate);
/* Single frame failure in an AMPDU queue => send BAR */ /* Single frame failure in an AMPDU queue => send BAR */
if (txq_id >= mvm->first_agg_queue && if (txq_id >= mvm->first_agg_queue &&
...@@ -909,6 +911,8 @@ static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info, ...@@ -909,6 +911,8 @@ static void iwl_mvm_tx_info_from_ba_notif(struct ieee80211_tx_info *info,
info->status.tx_time = tid_data->tx_time; info->status.tx_time = tid_data->tx_time;
info->status.status_driver_data[0] = info->status.status_driver_data[0] =
(void *)(uintptr_t)tid_data->reduced_tpc; (void *)(uintptr_t)tid_data->reduced_tpc;
info->status.status_driver_data[1] =
(void *)(uintptr_t)tid_data->rate_n_flags;
} }
int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, int iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
......
...@@ -332,7 +332,7 @@ static const char *desc_lookup(u32 num) ...@@ -332,7 +332,7 @@ static const char *desc_lookup(u32 num)
* read with u32-sized accesses, any members with a different size * read with u32-sized accesses, any members with a different size
* need to be ordered correctly though! * need to be ordered correctly though!
*/ */
struct iwl_error_event_table { struct iwl_error_event_table_v1 {
u32 valid; /* (nonzero) valid, (0) log is empty */ u32 valid; /* (nonzero) valid, (0) log is empty */
u32 error_id; /* type of error */ u32 error_id; /* type of error */
u32 pc; /* program counter */ u32 pc; /* program counter */
...@@ -377,7 +377,55 @@ struct iwl_error_event_table { ...@@ -377,7 +377,55 @@ struct iwl_error_event_table {
u32 u_timestamp; /* indicate when the date and time of the u32 u_timestamp; /* indicate when the date and time of the
* compilation */ * compilation */
u32 flow_handler; /* FH read/write pointers, RX credit */ u32 flow_handler; /* FH read/write pointers, RX credit */
} __packed; } __packed /* LOG_ERROR_TABLE_API_S_VER_1 */;
struct iwl_error_event_table {
u32 valid; /* (nonzero) valid, (0) log is empty */
u32 error_id; /* type of error */
u32 pc; /* program counter */
u32 blink1; /* branch link */
u32 blink2; /* branch link */
u32 ilink1; /* interrupt link */
u32 ilink2; /* interrupt link */
u32 data1; /* error-specific data */
u32 data2; /* error-specific data */
u32 data3; /* error-specific data */
u32 bcon_time; /* beacon timer */
u32 tsf_low; /* network timestamp function timer */
u32 tsf_hi; /* network timestamp function timer */
u32 gp1; /* GP1 timer register */
u32 gp2; /* GP2 timer register */
u32 gp3; /* GP3 timer register */
u32 major; /* uCode version major */
u32 minor; /* uCode version minor */
u32 hw_ver; /* HW Silicon version */
u32 brd_ver; /* HW board version */
u32 log_pc; /* log program counter */
u32 frame_ptr; /* frame pointer */
u32 stack_ptr; /* stack pointer */
u32 hcmd; /* last host command header */
u32 isr0; /* isr status register LMPM_NIC_ISR0:
* rxtx_flag */
u32 isr1; /* isr status register LMPM_NIC_ISR1:
* host_flag */
u32 isr2; /* isr status register LMPM_NIC_ISR2:
* enc_flag */
u32 isr3; /* isr status register LMPM_NIC_ISR3:
* time_flag */
u32 isr4; /* isr status register LMPM_NIC_ISR4:
* wico interrupt */
u32 isr_pref; /* isr status register LMPM_NIC_PREF_STAT */
u32 wait_event; /* wait event() caller address */
u32 l2p_control; /* L2pControlField */
u32 l2p_duration; /* L2pDurationField */
u32 l2p_mhvalid; /* L2pMhValidBits */
u32 l2p_addr_match; /* L2pAddrMatchStat */
u32 lmpm_pmg_sel; /* indicate which clocks are turned on
* (LMPM_PMG_SEL) */
u32 u_timestamp; /* indicate when the date and time of the
* compilation */
u32 flow_handler; /* FH read/write pointers, RX credit */
} __packed /* LOG_ERROR_TABLE_API_S_VER_2 */;
/* /*
* UMAC error struct - relevant starting from family 8000 chip. * UMAC error struct - relevant starting from family 8000 chip.
...@@ -396,8 +444,8 @@ struct iwl_umac_error_event_table { ...@@ -396,8 +444,8 @@ struct iwl_umac_error_event_table {
u32 data1; /* error-specific data */ u32 data1; /* error-specific data */
u32 data2; /* error-specific data */ u32 data2; /* error-specific data */
u32 data3; /* error-specific data */ u32 data3; /* error-specific data */
u32 umac_fw_ver; /* UMAC version */ u32 umac_major;
u32 umac_fw_api_ver; /* UMAC FW API ver */ u32 umac_minor;
u32 frame_pointer; /* core register 27*/ u32 frame_pointer; /* core register 27*/
u32 stack_pointer; /* core register 28 */ u32 stack_pointer; /* core register 28 */
u32 cmd_header; /* latest host cmd sent to UMAC */ u32 cmd_header; /* latest host cmd sent to UMAC */
...@@ -441,18 +489,18 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm) ...@@ -441,18 +489,18 @@ static void iwl_mvm_dump_umac_error_log(struct iwl_mvm *mvm)
IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1); IWL_ERR(mvm, "0x%08X | umac data1\n", table.data1);
IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2); IWL_ERR(mvm, "0x%08X | umac data2\n", table.data2);
IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3); IWL_ERR(mvm, "0x%08X | umac data3\n", table.data3);
IWL_ERR(mvm, "0x%08X | umac version\n", table.umac_fw_ver); IWL_ERR(mvm, "0x%08X | umac major\n", table.umac_major);
IWL_ERR(mvm, "0x%08X | umac api version\n", table.umac_fw_api_ver); IWL_ERR(mvm, "0x%08X | umac minor\n", table.umac_minor);
IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer); IWL_ERR(mvm, "0x%08X | frame pointer\n", table.frame_pointer);
IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer); IWL_ERR(mvm, "0x%08X | stack pointer\n", table.stack_pointer);
IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header); IWL_ERR(mvm, "0x%08X | last host cmd\n", table.cmd_header);
IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref); IWL_ERR(mvm, "0x%08X | isr status reg\n", table.nic_isr_pref);
} }
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) static void iwl_mvm_dump_nic_error_log_old(struct iwl_mvm *mvm)
{ {
struct iwl_trans *trans = mvm->trans; struct iwl_trans *trans = mvm->trans;
struct iwl_error_event_table table; struct iwl_error_event_table_v1 table;
u32 base; u32 base;
base = mvm->error_event_table; base = mvm->error_event_table;
...@@ -489,7 +537,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) ...@@ -489,7 +537,7 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
table.data1, table.data2, table.data3, table.data1, table.data2, table.data3,
table.blink1, table.blink2, table.ilink1, table.blink1, table.blink2, table.ilink1,
table.ilink2, table.bcon_time, table.gp1, table.ilink2, table.bcon_time, table.gp1,
table.gp2, table.gp3, table.ucode_ver, table.gp2, table.gp3, table.ucode_ver, 0,
table.hw_ver, table.brd_ver); table.hw_ver, table.brd_ver);
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id, IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id)); desc_lookup(table.error_id));
...@@ -530,6 +578,92 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm) ...@@ -530,6 +578,92 @@ void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
iwl_mvm_dump_umac_error_log(mvm); iwl_mvm_dump_umac_error_log(mvm);
} }
void iwl_mvm_dump_nic_error_log(struct iwl_mvm *mvm)
{
struct iwl_trans *trans = mvm->trans;
struct iwl_error_event_table table;
u32 base;
if (!(mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_NEW_VERSION)) {
iwl_mvm_dump_nic_error_log_old(mvm);
return;
}
base = mvm->error_event_table;
if (mvm->cur_ucode == IWL_UCODE_INIT) {
if (!base)
base = mvm->fw->init_errlog_ptr;
} else {
if (!base)
base = mvm->fw->inst_errlog_ptr;
}
if (base < 0x800000) {
IWL_ERR(mvm,
"Not valid error log pointer 0x%08X for %s uCode\n",
base,
(mvm->cur_ucode == IWL_UCODE_INIT)
? "Init" : "RT");
return;
}
iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table));
if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) {
IWL_ERR(trans, "Start IWL Error Log Dump:\n");
IWL_ERR(trans, "Status: 0x%08lX, count: %d\n",
mvm->status, table.valid);
}
/* Do not change this output - scripts rely on it */
IWL_ERR(mvm, "Loaded firmware version: %s\n", mvm->fw->fw_version);
trace_iwlwifi_dev_ucode_error(trans->dev, table.error_id, table.tsf_low,
table.data1, table.data2, table.data3,
table.blink1, table.blink2, table.ilink1,
table.ilink2, table.bcon_time, table.gp1,
table.gp2, table.gp3, table.major,
table.minor, table.hw_ver, table.brd_ver);
IWL_ERR(mvm, "0x%08X | %-28s\n", table.error_id,
desc_lookup(table.error_id));
IWL_ERR(mvm, "0x%08X | uPc\n", table.pc);
IWL_ERR(mvm, "0x%08X | branchlink1\n", table.blink1);
IWL_ERR(mvm, "0x%08X | branchlink2\n", table.blink2);
IWL_ERR(mvm, "0x%08X | interruptlink1\n", table.ilink1);
IWL_ERR(mvm, "0x%08X | interruptlink2\n", table.ilink2);
IWL_ERR(mvm, "0x%08X | data1\n", table.data1);
IWL_ERR(mvm, "0x%08X | data2\n", table.data2);
IWL_ERR(mvm, "0x%08X | data3\n", table.data3);
IWL_ERR(mvm, "0x%08X | beacon time\n", table.bcon_time);
IWL_ERR(mvm, "0x%08X | tsf low\n", table.tsf_low);
IWL_ERR(mvm, "0x%08X | tsf hi\n", table.tsf_hi);
IWL_ERR(mvm, "0x%08X | time gp1\n", table.gp1);
IWL_ERR(mvm, "0x%08X | time gp2\n", table.gp2);
IWL_ERR(mvm, "0x%08X | time gp3\n", table.gp3);
IWL_ERR(mvm, "0x%08X | uCode version major\n", table.major);
IWL_ERR(mvm, "0x%08X | uCode version minor\n", table.minor);
IWL_ERR(mvm, "0x%08X | hw version\n", table.hw_ver);
IWL_ERR(mvm, "0x%08X | board version\n", table.brd_ver);
IWL_ERR(mvm, "0x%08X | hcmd\n", table.hcmd);
IWL_ERR(mvm, "0x%08X | isr0\n", table.isr0);
IWL_ERR(mvm, "0x%08X | isr1\n", table.isr1);
IWL_ERR(mvm, "0x%08X | isr2\n", table.isr2);
IWL_ERR(mvm, "0x%08X | isr3\n", table.isr3);
IWL_ERR(mvm, "0x%08X | isr4\n", table.isr4);
IWL_ERR(mvm, "0x%08X | isr_pref\n", table.isr_pref);
IWL_ERR(mvm, "0x%08X | wait_event\n", table.wait_event);
IWL_ERR(mvm, "0x%08X | l2p_control\n", table.l2p_control);
IWL_ERR(mvm, "0x%08X | l2p_duration\n", table.l2p_duration);
IWL_ERR(mvm, "0x%08X | l2p_mhvalid\n", table.l2p_mhvalid);
IWL_ERR(mvm, "0x%08X | l2p_addr_match\n", table.l2p_addr_match);
IWL_ERR(mvm, "0x%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel);
IWL_ERR(mvm, "0x%08X | timestamp\n", table.u_timestamp);
IWL_ERR(mvm, "0x%08X | flow_handler\n", table.flow_handler);
if (mvm->support_umac_log)
iwl_mvm_dump_umac_error_log(mvm);
}
void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn, void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, u16 ssn,
const struct iwl_trans_txq_scd_cfg *cfg, const struct iwl_trans_txq_scd_cfg *cfg,
unsigned int wdg_timeout) unsigned int wdg_timeout)
...@@ -643,6 +777,40 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif, ...@@ -643,6 +777,40 @@ void iwl_mvm_update_smps(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
ieee80211_request_smps(vif, smps_mode); ieee80211_request_smps(vif, smps_mode);
} }
int iwl_mvm_request_statistics(struct iwl_mvm *mvm, bool clear)
{
struct iwl_statistics_cmd scmd = {
.flags = clear ? cpu_to_le32(IWL_STATISTICS_FLG_CLEAR) : 0,
};
struct iwl_host_cmd cmd = {
.id = STATISTICS_CMD,
.len[0] = sizeof(scmd),
.data[0] = &scmd,
.flags = CMD_WANT_SKB,
};
int ret;
ret = iwl_mvm_send_cmd(mvm, &cmd);
if (ret)
return ret;
iwl_mvm_handle_rx_statistics(mvm, cmd.resp_pkt);
iwl_free_resp(&cmd);
if (clear)
iwl_mvm_accu_radio_stats(mvm);
return 0;
}
void iwl_mvm_accu_radio_stats(struct iwl_mvm *mvm)
{
mvm->accu_radio_stats.rx_time += mvm->radio_stats.rx_time;
mvm->accu_radio_stats.tx_time += mvm->radio_stats.tx_time;
mvm->accu_radio_stats.on_time_rf += mvm->radio_stats.on_time_rf;
mvm->accu_radio_stats.on_time_scan += mvm->radio_stats.on_time_scan;
}
static void iwl_mvm_diversity_iter(void *_data, u8 *mac, static void iwl_mvm_diversity_iter(void *_data, u8 *mac,
struct ieee80211_vif *vif) struct ieee80211_vif *vif)
{ {
...@@ -717,25 +885,6 @@ bool iwl_mvm_low_latency(struct iwl_mvm *mvm) ...@@ -717,25 +885,6 @@ bool iwl_mvm_low_latency(struct iwl_mvm *mvm)
return result; return result;
} }
static void iwl_mvm_idle_iter(void *_data, u8 *mac, struct ieee80211_vif *vif)
{
bool *idle = _data;
if (!vif->bss_conf.idle)
*idle = false;
}
bool iwl_mvm_is_idle(struct iwl_mvm *mvm)
{
bool idle = true;
ieee80211_iterate_active_interfaces_atomic(
mvm->hw, IEEE80211_IFACE_ITER_NORMAL,
iwl_mvm_idle_iter, &idle);
return idle;
}
struct iwl_bss_iter_data { struct iwl_bss_iter_data {
struct ieee80211_vif *vif; struct ieee80211_vif *vif;
bool error; bool error;
......
...@@ -898,6 +898,9 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, ...@@ -898,6 +898,9 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
IWL_DEBUG_FW(trans, "working with %s CPU\n", IWL_DEBUG_FW(trans, "working with %s CPU\n",
image->is_dual_cpus ? "Dual" : "Single"); image->is_dual_cpus ? "Dual" : "Single");
if (trans->dbg_dest_tlv)
iwl_pcie_apply_destination(trans);
/* configure the ucode to be ready to get the secured image */ /* configure the ucode to be ready to get the secured image */
/* release CPU reset */ /* release CPU reset */
iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT); iwl_write_prph(trans, RELEASE_CPU_RESET, RELEASE_CPU_RESET_BIT);
...@@ -914,9 +917,6 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans, ...@@ -914,9 +917,6 @@ static int iwl_pcie_load_given_ucode_8000b(struct iwl_trans *trans,
if (ret) if (ret)
return ret; return ret;
if (trans->dbg_dest_tlv)
iwl_pcie_apply_destination(trans);
/* wait for image verification to complete */ /* wait for image verification to complete */
ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0, ret = iwl_poll_prph_bit(trans, LMPM_SECURE_BOOT_CPU1_STATUS_ADDR_B0,
LMPM_SECURE_BOOT_STATUS_SUCCESS, LMPM_SECURE_BOOT_STATUS_SUCCESS,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment