Commit c8ac61cf authored by Johannes Berg's avatar Johannes Berg Committed by Wey-Yi Guy

iwlagn: implement WoWLAN

Implement WoWLAN support in iwlagn. The device
supports a number of wakeup triggers and can do
GTK rekeying when asleep (if HW crypto is used).
Unfortunately, we need to disconnect from the AP
after resume since we can't yet get all the info
out of the wowlan uCode to stay connected safely.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarWey-Yi Guy <wey-yi.w.guy@intel.com>
parent 5a3d9882
...@@ -400,6 +400,7 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) ...@@ -400,6 +400,7 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
struct iwl_tx_queue *txq = &priv->txq[txq_id]; struct iwl_tx_queue *txq = &priv->txq[txq_id];
struct ieee80211_tx_info *info; struct ieee80211_tx_info *info;
struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
struct ieee80211_hdr *hdr;
struct iwl_tx_info *txb; struct iwl_tx_info *txb;
u32 status = le16_to_cpu(tx_resp->status.status); u32 status = le16_to_cpu(tx_resp->status.status);
int tid; int tid;
...@@ -426,6 +427,11 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) ...@@ -426,6 +427,11 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
IWLAGN_TX_RES_RA_POS; IWLAGN_TX_RES_RA_POS;
spin_lock_irqsave(&priv->sta_lock, flags); spin_lock_irqsave(&priv->sta_lock, flags);
hdr = (void *)txb->skb->data;
if (!ieee80211_is_data_qos(hdr->frame_control))
priv->last_seq_ctl = tx_resp->seq_ctl;
if (txq->sched_retry) { if (txq->sched_retry) {
const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp); const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp);
struct iwl_ht_agg *agg; struct iwl_ht_agg *agg;
......
...@@ -855,6 +855,9 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw, ...@@ -855,6 +855,9 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
iwl_wake_any_queue(priv, ctx); iwl_wake_any_queue(priv, ctx);
} }
ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK;
if (ctx->ctxid == IWL_RXON_CTX_BSS)
priv->have_rekey_data = false;
} }
iwlagn_bt_coex_rssi_monitor(priv); iwlagn_bt_coex_rssi_monitor(priv);
......
...@@ -513,6 +513,12 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, ...@@ -513,6 +513,12 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
return -EIO; return -EIO;
} }
/*
* This step takes a long time (60-80ms!!) and
* WoWLAN image should be loaded quickly, so
* skip it for WoWLAN.
*/
if (ucode_type != IWL_UCODE_WOWLAN) {
ret = iwl_verify_ucode(priv, image); ret = iwl_verify_ucode(priv, image);
if (ret) { if (ret) {
priv->ucode_type = old_type; priv->ucode_type = old_type;
...@@ -521,6 +527,7 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv, ...@@ -521,6 +527,7 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
/* delay a bit to give rfkill time to run */ /* delay a bit to give rfkill time to run */
msleep(5); msleep(5);
}
ret = iwlagn_alive_notify(priv); ret = iwlagn_alive_notify(priv);
if (ret) { if (ret) {
......
This diff is collapsed.
...@@ -188,6 +188,13 @@ enum { ...@@ -188,6 +188,13 @@ enum {
REPLY_WIPAN_NOA_NOTIFICATION = 0xbc, REPLY_WIPAN_NOA_NOTIFICATION = 0xbc,
REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd, REPLY_WIPAN_DEACTIVATION_COMPLETE = 0xbd,
REPLY_WOWLAN_PATTERNS = 0xe0,
REPLY_WOWLAN_WAKEUP_FILTER = 0xe1,
REPLY_WOWLAN_TSC_RSC_PARAMS = 0xe2,
REPLY_WOWLAN_TKIP_PARAMS = 0xe3,
REPLY_WOWLAN_KEK_KCK_MATERIAL = 0xe4,
REPLY_WOWLAN_GET_STATUS = 0xe5,
REPLY_MAX = 0xff REPLY_MAX = 0xff
}; };
...@@ -3764,6 +3771,127 @@ struct iwl_bt_coex_prot_env_cmd { ...@@ -3764,6 +3771,127 @@ struct iwl_bt_coex_prot_env_cmd {
u8 reserved[2]; u8 reserved[2];
} __attribute__((packed)); } __attribute__((packed));
/*
* REPLY_WOWLAN_PATTERNS
*/
#define IWLAGN_WOWLAN_MIN_PATTERN_LEN 16
#define IWLAGN_WOWLAN_MAX_PATTERN_LEN 128
struct iwlagn_wowlan_pattern {
u8 mask[IWLAGN_WOWLAN_MAX_PATTERN_LEN / 8];
u8 pattern[IWLAGN_WOWLAN_MAX_PATTERN_LEN];
u8 mask_size;
u8 pattern_size;
__le16 reserved;
} __packed;
#define IWLAGN_WOWLAN_MAX_PATTERNS 20
struct iwlagn_wowlan_patterns_cmd {
__le32 n_patterns;
struct iwlagn_wowlan_pattern patterns[];
} __packed;
/*
* REPLY_WOWLAN_WAKEUP_FILTER
*/
enum iwlagn_wowlan_wakeup_filters {
IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET = BIT(0),
IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH = BIT(1),
IWLAGN_WOWLAN_WAKEUP_BEACON_MISS = BIT(2),
IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE = BIT(3),
IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL = BIT(4),
IWLAGN_WOWLAN_WAKEUP_RFKILL = BIT(5),
IWLAGN_WOWLAN_WAKEUP_UCODE_ERROR = BIT(6),
IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ = BIT(7),
IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE = BIT(8),
IWLAGN_WOWLAN_WAKEUP_ALWAYS = BIT(9),
IWLAGN_WOWLAN_WAKEUP_ENABLE_NET_DETECT = BIT(10),
};
struct iwlagn_wowlan_wakeup_filter_cmd {
__le32 enabled;
__le16 non_qos_seq;
u8 min_sleep_seconds;
u8 reserved;
__le16 qos_seq[8];
};
/*
* REPLY_WOWLAN_TSC_RSC_PARAMS
*/
#define IWLAGN_NUM_RSC 16
struct tkip_sc {
__le16 iv16;
__le16 pad;
__le32 iv32;
} __packed;
struct iwlagn_tkip_rsc_tsc {
struct tkip_sc unicast_rsc[IWLAGN_NUM_RSC];
struct tkip_sc multicast_rsc[IWLAGN_NUM_RSC];
struct tkip_sc tsc;
} __packed;
struct aes_sc {
__le64 pn;
} __packed;
struct iwlagn_aes_rsc_tsc {
struct aes_sc unicast_rsc[IWLAGN_NUM_RSC];
struct aes_sc multicast_rsc[IWLAGN_NUM_RSC];
struct aes_sc tsc;
} __packed;
union iwlagn_all_tsc_rsc {
struct iwlagn_tkip_rsc_tsc tkip;
struct iwlagn_aes_rsc_tsc aes;
};
struct iwlagn_wowlan_rsc_tsc_params_cmd {
union iwlagn_all_tsc_rsc all_tsc_rsc;
} __packed;
/*
* REPLY_WOWLAN_TKIP_PARAMS
*/
#define IWLAGN_MIC_KEY_SIZE 8
#define IWLAGN_P1K_SIZE 5
struct iwlagn_mic_keys {
u8 tx[IWLAGN_MIC_KEY_SIZE];
u8 rx_unicast[IWLAGN_MIC_KEY_SIZE];
u8 rx_mcast[IWLAGN_MIC_KEY_SIZE];
} __packed;
struct iwlagn_p1k_cache {
__le16 p1k[IWLAGN_P1K_SIZE];
} __packed;
#define IWLAGN_NUM_RX_P1K_CACHE 2
struct iwlagn_wowlan_tkip_params_cmd {
struct iwlagn_mic_keys mic_keys;
struct iwlagn_p1k_cache tx;
struct iwlagn_p1k_cache rx_uni[IWLAGN_NUM_RX_P1K_CACHE];
struct iwlagn_p1k_cache rx_multi[IWLAGN_NUM_RX_P1K_CACHE];
} __packed;
/*
* REPLY_WOWLAN_KEK_KCK_MATERIAL
*/
#define IWLAGN_KCK_MAX_SIZE 32
#define IWLAGN_KEK_MAX_SIZE 32
struct iwlagn_wowlan_kek_kck_material_cmd {
u8 kck[IWLAGN_KCK_MAX_SIZE];
u8 kek[IWLAGN_KEK_MAX_SIZE];
__le16 kck_len;
__le16 kek_len;
__le64 replay_ctr;
} __packed;
/****************************************************************************** /******************************************************************************
* (13) * (13)
* Union of all expected notifications/responses: * Union of all expected notifications/responses:
......
...@@ -1903,7 +1903,11 @@ int iwl_suspend(struct iwl_priv *priv) ...@@ -1903,7 +1903,11 @@ int iwl_suspend(struct iwl_priv *priv)
* first but since iwl_mac_stop() has no knowledge of who the caller is, * first but since iwl_mac_stop() has no knowledge of who the caller is,
* it will not call apm_ops.stop() to stop the DMA operation. * it will not call apm_ops.stop() to stop the DMA operation.
* Calling apm_ops.stop here to make sure we stop the DMA. * Calling apm_ops.stop here to make sure we stop the DMA.
*
* But of course ... if we have configured WoWLAN then we did other
* things already :-)
*/ */
if (!priv->wowlan)
iwl_apm_stop(priv); iwl_apm_stop(priv);
return 0; return 0;
......
...@@ -351,6 +351,7 @@ ...@@ -351,6 +351,7 @@
#define CSR_UCODE_SW_BIT_RFKILL (0x00000002) #define CSR_UCODE_SW_BIT_RFKILL (0x00000002)
#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) #define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004)
#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) #define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008)
#define CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020)
/* GP Driver */ /* GP Driver */
#define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) #define CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003)
......
...@@ -322,6 +322,19 @@ static ssize_t iwl_dbgfs_sram_write(struct file *file, ...@@ -322,6 +322,19 @@ static ssize_t iwl_dbgfs_sram_write(struct file *file,
return count; return count;
} }
static ssize_t iwl_dbgfs_wowlan_sram_read(struct file *file,
char __user *user_buf,
size_t count, loff_t *ppos)
{
struct iwl_priv *priv = file->private_data;
if (!priv->wowlan_sram)
return -ENODATA;
return simple_read_from_buffer(user_buf, count, ppos,
priv->wowlan_sram,
priv->ucode_wowlan.data.len);
}
static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
...@@ -856,6 +869,7 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file, ...@@ -856,6 +869,7 @@ static ssize_t iwl_dbgfs_current_sleep_command_read(struct file *file,
} }
DEBUGFS_READ_WRITE_FILE_OPS(sram); DEBUGFS_READ_WRITE_FILE_OPS(sram);
DEBUGFS_READ_FILE_OPS(wowlan_sram);
DEBUGFS_READ_WRITE_FILE_OPS(log_event); DEBUGFS_READ_WRITE_FILE_OPS(log_event);
DEBUGFS_READ_FILE_OPS(nvm); DEBUGFS_READ_FILE_OPS(nvm);
DEBUGFS_READ_FILE_OPS(stations); DEBUGFS_READ_FILE_OPS(stations);
...@@ -2667,6 +2681,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name) ...@@ -2667,6 +2681,7 @@ int iwl_dbgfs_register(struct iwl_priv *priv, const char *name)
DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(wowlan_sram, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(log_event, dir_data, S_IWUSR | S_IRUSR); DEBUGFS_ADD_FILE(log_event, dir_data, S_IWUSR | S_IRUSR);
DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR);
DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR);
......
...@@ -551,7 +551,8 @@ enum iwl_ucode_tlv_type { ...@@ -551,7 +551,8 @@ enum iwl_ucode_tlv_type {
IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13, IWL_UCODE_TLV_INIT_ERRLOG_PTR = 13,
IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14, IWL_UCODE_TLV_ENHANCE_SENS_TBL = 14,
IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15, IWL_UCODE_TLV_PHY_CALIBRATION_SIZE = 15,
/* 16 and 17 reserved for future use */ IWL_UCODE_TLV_WOWLAN_INST = 16,
IWL_UCODE_TLV_WOWLAN_DATA = 17,
IWL_UCODE_TLV_FLAGS = 18, IWL_UCODE_TLV_FLAGS = 18,
}; };
...@@ -1284,6 +1285,7 @@ struct iwl_priv { ...@@ -1284,6 +1285,7 @@ struct iwl_priv {
struct fw_img ucode_rt; struct fw_img ucode_rt;
struct fw_img ucode_init; struct fw_img ucode_init;
struct fw_img ucode_wowlan;
enum iwlagn_ucode_type ucode_type; enum iwlagn_ucode_type ucode_type;
u8 ucode_write_complete; /* the image write is complete */ u8 ucode_write_complete; /* the image write is complete */
...@@ -1356,6 +1358,8 @@ struct iwl_priv { ...@@ -1356,6 +1358,8 @@ struct iwl_priv {
u8 mac80211_registered; u8 mac80211_registered;
bool wowlan;
/* eeprom -- this is in the card's little endian byte order */ /* eeprom -- this is in the card's little endian byte order */
u8 *eeprom; u8 *eeprom;
int nvm_device_type; int nvm_device_type;
...@@ -1508,6 +1512,7 @@ struct iwl_priv { ...@@ -1508,6 +1512,7 @@ struct iwl_priv {
struct dentry *debugfs_dir; struct dentry *debugfs_dir;
u32 dbgfs_sram_offset, dbgfs_sram_len; u32 dbgfs_sram_offset, dbgfs_sram_len;
bool disable_ht40; bool disable_ht40;
void *wowlan_sram;
#endif /* CONFIG_IWLWIFI_DEBUGFS */ #endif /* CONFIG_IWLWIFI_DEBUGFS */
struct work_struct txpower_work; struct work_struct txpower_work;
...@@ -1528,6 +1533,11 @@ struct iwl_priv { ...@@ -1528,6 +1533,11 @@ struct iwl_priv {
u32 tm_fixed_rate; u32 tm_fixed_rate;
#endif #endif
/* WoWLAN GTK rekey data */
u8 kck[NL80211_KCK_LEN], kek[NL80211_KEK_LEN];
__le64 replay_ctr;
__le16 last_seq_ctl;
bool have_rekey_data;
}; /*iwl_priv */ }; /*iwl_priv */
static inline void iwl_txq_ctx_activate(struct iwl_priv *priv, int txq_id) static inline void iwl_txq_ctx_activate(struct iwl_priv *priv, int txq_id)
......
...@@ -347,7 +347,9 @@ static void iwl_power_build_cmd(struct iwl_priv *priv, ...@@ -347,7 +347,9 @@ static void iwl_power_build_cmd(struct iwl_priv *priv,
dtimper = priv->hw->conf.ps_dtim_period ?: 1; dtimper = priv->hw->conf.ps_dtim_period ?: 1;
if (priv->hw->conf.flags & IEEE80211_CONF_IDLE) if (priv->wowlan)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, dtimper);
else if (priv->hw->conf.flags & IEEE80211_CONF_IDLE)
iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20); iwl_static_sleep_cmd(priv, cmd, IWL_POWER_INDEX_5, 20);
else if (iwl_tt_is_low_power_state(priv)) { else if (iwl_tt_is_low_power_state(priv)) {
/* in thermal throttling low power state */ /* in thermal throttling low power state */
......
...@@ -866,6 +866,12 @@ const char *get_cmd_string(u8 cmd) ...@@ -866,6 +866,12 @@ const char *get_cmd_string(u8 cmd)
IWL_CMD(REPLY_WIPAN_P2P_CHANNEL_SWITCH); IWL_CMD(REPLY_WIPAN_P2P_CHANNEL_SWITCH);
IWL_CMD(REPLY_WIPAN_NOA_NOTIFICATION); IWL_CMD(REPLY_WIPAN_NOA_NOTIFICATION);
IWL_CMD(REPLY_WIPAN_DEACTIVATION_COMPLETE); IWL_CMD(REPLY_WIPAN_DEACTIVATION_COMPLETE);
IWL_CMD(REPLY_WOWLAN_PATTERNS);
IWL_CMD(REPLY_WOWLAN_WAKEUP_FILTER);
IWL_CMD(REPLY_WOWLAN_TSC_RSC_PARAMS);
IWL_CMD(REPLY_WOWLAN_TKIP_PARAMS);
IWL_CMD(REPLY_WOWLAN_KEK_KCK_MATERIAL);
IWL_CMD(REPLY_WOWLAN_GET_STATUS);
default: default:
return "UNKNOWN"; return "UNKNOWN";
......
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