|
@@ -593,6 +593,7 @@ static void iwl_dealloc_ucode(struct iwl_priv *priv)
|
|
{
|
|
{
|
|
iwl_free_fw_img(priv, &priv->ucode_rt);
|
|
iwl_free_fw_img(priv, &priv->ucode_rt);
|
|
iwl_free_fw_img(priv, &priv->ucode_init);
|
|
iwl_free_fw_img(priv, &priv->ucode_init);
|
|
|
|
+ iwl_free_fw_img(priv, &priv->ucode_wowlan);
|
|
}
|
|
}
|
|
|
|
|
|
static int iwl_alloc_fw_desc(struct iwl_priv *priv, struct fw_desc *desc,
|
|
static int iwl_alloc_fw_desc(struct iwl_priv *priv, struct fw_desc *desc,
|
|
@@ -662,8 +663,9 @@ static int __must_check iwl_request_firmware(struct iwl_priv *priv, bool first)
|
|
}
|
|
}
|
|
|
|
|
|
struct iwlagn_firmware_pieces {
|
|
struct iwlagn_firmware_pieces {
|
|
- const void *inst, *data, *init, *init_data;
|
|
|
|
- size_t inst_size, data_size, init_size, init_data_size;
|
|
|
|
|
|
+ const void *inst, *data, *init, *init_data, *wowlan_inst, *wowlan_data;
|
|
|
|
+ size_t inst_size, data_size, init_size, init_data_size,
|
|
|
|
+ wowlan_inst_size, wowlan_data_size;
|
|
|
|
|
|
u32 build;
|
|
u32 build;
|
|
|
|
|
|
@@ -902,6 +904,14 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
|
|
goto invalid_tlv_len;
|
|
goto invalid_tlv_len;
|
|
priv->enhance_sensitivity_table = true;
|
|
priv->enhance_sensitivity_table = true;
|
|
break;
|
|
break;
|
|
|
|
+ case IWL_UCODE_TLV_WOWLAN_INST:
|
|
|
|
+ pieces->wowlan_inst = tlv_data;
|
|
|
|
+ pieces->wowlan_inst_size = tlv_len;
|
|
|
|
+ break;
|
|
|
|
+ case IWL_UCODE_TLV_WOWLAN_DATA:
|
|
|
|
+ pieces->wowlan_data = tlv_data;
|
|
|
|
+ pieces->wowlan_data_size = tlv_len;
|
|
|
|
+ break;
|
|
case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE:
|
|
case IWL_UCODE_TLV_PHY_CALIBRATION_SIZE:
|
|
if (tlv_len != sizeof(u32))
|
|
if (tlv_len != sizeof(u32))
|
|
goto invalid_tlv_len;
|
|
goto invalid_tlv_len;
|
|
@@ -1096,6 +1106,18 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
|
|
goto err_pci_alloc;
|
|
goto err_pci_alloc;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* WoWLAN instructions and data */
|
|
|
|
+ if (pieces.wowlan_inst_size && pieces.wowlan_data_size) {
|
|
|
|
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_wowlan.code,
|
|
|
|
+ pieces.wowlan_inst,
|
|
|
|
+ pieces.wowlan_inst_size))
|
|
|
|
+ goto err_pci_alloc;
|
|
|
|
+ if (iwl_alloc_fw_desc(priv, &priv->ucode_wowlan.data,
|
|
|
|
+ pieces.wowlan_data,
|
|
|
|
+ pieces.wowlan_data_size))
|
|
|
|
+ goto err_pci_alloc;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Now that we can no longer fail, copy information */
|
|
/* Now that we can no longer fail, copy information */
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1698,7 +1720,7 @@ int iwl_alive_start(struct iwl_priv *priv)
|
|
/* Configure Tx antenna selection based on H/W config */
|
|
/* Configure Tx antenna selection based on H/W config */
|
|
iwlagn_send_tx_ant_config(priv, priv->cfg->valid_tx_ant);
|
|
iwlagn_send_tx_ant_config(priv, priv->cfg->valid_tx_ant);
|
|
|
|
|
|
- if (iwl_is_associated_ctx(ctx)) {
|
|
|
|
|
|
+ if (iwl_is_associated_ctx(ctx) && !priv->wowlan) {
|
|
struct iwl_rxon_cmd *active_rxon =
|
|
struct iwl_rxon_cmd *active_rxon =
|
|
(struct iwl_rxon_cmd *)&ctx->active;
|
|
(struct iwl_rxon_cmd *)&ctx->active;
|
|
/* apply any changes in staging */
|
|
/* apply any changes in staging */
|
|
@@ -1713,7 +1735,10 @@ int iwl_alive_start(struct iwl_priv *priv)
|
|
iwlagn_set_rxon_chain(priv, ctx);
|
|
iwlagn_set_rxon_chain(priv, ctx);
|
|
}
|
|
}
|
|
|
|
|
|
- iwl_reset_run_time_calib(priv);
|
|
|
|
|
|
+ if (!priv->wowlan) {
|
|
|
|
+ /* WoWLAN ucode will not reply in the same way, skip it */
|
|
|
|
+ iwl_reset_run_time_calib(priv);
|
|
|
|
+ }
|
|
|
|
|
|
set_bit(STATUS_READY, &priv->status);
|
|
set_bit(STATUS_READY, &priv->status);
|
|
|
|
|
|
@@ -2153,6 +2178,23 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
|
|
WIPHY_FLAG_DISABLE_BEACON_HINTS |
|
|
WIPHY_FLAG_DISABLE_BEACON_HINTS |
|
|
WIPHY_FLAG_IBSS_RSN;
|
|
WIPHY_FLAG_IBSS_RSN;
|
|
|
|
|
|
|
|
+ if (priv->ucode_wowlan.code.len && device_can_wakeup(priv->bus->dev)) {
|
|
|
|
+ hw->wiphy->wowlan.flags = WIPHY_WOWLAN_MAGIC_PKT |
|
|
|
|
+ WIPHY_WOWLAN_DISCONNECT |
|
|
|
|
+ WIPHY_WOWLAN_EAP_IDENTITY_REQ |
|
|
|
|
+ WIPHY_WOWLAN_RFKILL_RELEASE;
|
|
|
|
+ if (!iwlagn_mod_params.sw_crypto)
|
|
|
|
+ hw->wiphy->wowlan.flags |=
|
|
|
|
+ WIPHY_WOWLAN_SUPPORTS_GTK_REKEY |
|
|
|
|
+ WIPHY_WOWLAN_GTK_REKEY_FAILURE;
|
|
|
|
+
|
|
|
|
+ hw->wiphy->wowlan.n_patterns = IWLAGN_WOWLAN_MAX_PATTERNS;
|
|
|
|
+ hw->wiphy->wowlan.pattern_min_len =
|
|
|
|
+ IWLAGN_WOWLAN_MIN_PATTERN_LEN;
|
|
|
|
+ hw->wiphy->wowlan.pattern_max_len =
|
|
|
|
+ IWLAGN_WOWLAN_MAX_PATTERN_LEN;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (iwlagn_mod_params.power_save)
|
|
if (iwlagn_mod_params.power_save)
|
|
hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
|
|
hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
|
|
else
|
|
else
|
|
@@ -2237,6 +2279,467 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw)
|
|
IWL_DEBUG_MAC80211(priv, "leave\n");
|
|
IWL_DEBUG_MAC80211(priv, "leave\n");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int iwlagn_send_patterns(struct iwl_priv *priv,
|
|
|
|
+ struct cfg80211_wowlan *wowlan)
|
|
|
|
+{
|
|
|
|
+ struct iwlagn_wowlan_patterns_cmd *pattern_cmd;
|
|
|
|
+ struct iwl_host_cmd cmd = {
|
|
|
|
+ .id = REPLY_WOWLAN_PATTERNS,
|
|
|
|
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
|
|
|
|
+ .flags = CMD_SYNC,
|
|
|
|
+ };
|
|
|
|
+ int i, err;
|
|
|
|
+
|
|
|
|
+ if (!wowlan->n_patterns)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ cmd.len[0] = sizeof(*pattern_cmd) +
|
|
|
|
+ wowlan->n_patterns * sizeof(struct iwlagn_wowlan_pattern);
|
|
|
|
+
|
|
|
|
+ pattern_cmd = kmalloc(cmd.len[0], GFP_KERNEL);
|
|
|
|
+ if (!pattern_cmd)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ pattern_cmd->n_patterns = cpu_to_le32(wowlan->n_patterns);
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < wowlan->n_patterns; i++) {
|
|
|
|
+ int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8);
|
|
|
|
+
|
|
|
|
+ memcpy(&pattern_cmd->patterns[i].mask,
|
|
|
|
+ wowlan->patterns[i].mask, mask_len);
|
|
|
|
+ memcpy(&pattern_cmd->patterns[i].pattern,
|
|
|
|
+ wowlan->patterns[i].pattern,
|
|
|
|
+ wowlan->patterns[i].pattern_len);
|
|
|
|
+ pattern_cmd->patterns[i].mask_size = mask_len;
|
|
|
|
+ pattern_cmd->patterns[i].pattern_size =
|
|
|
|
+ wowlan->patterns[i].pattern_len;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cmd.data[0] = pattern_cmd;
|
|
|
|
+ err = trans_send_cmd(&priv->trans, &cmd);
|
|
|
|
+ kfree(pattern_cmd);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void iwlagn_mac_set_rekey_data(struct ieee80211_hw *hw,
|
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
|
+ struct cfg80211_gtk_rekey_data *data)
|
|
|
|
+{
|
|
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
|
|
+
|
|
|
|
+ if (iwlagn_mod_params.sw_crypto)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&priv->mutex);
|
|
|
|
+
|
|
|
|
+ if (priv->contexts[IWL_RXON_CTX_BSS].vif != vif)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ memcpy(priv->kek, data->kek, NL80211_KEK_LEN);
|
|
|
|
+ memcpy(priv->kck, data->kck, NL80211_KCK_LEN);
|
|
|
|
+ priv->replay_ctr = cpu_to_le64(be64_to_cpup((__be64 *)&data->replay_ctr));
|
|
|
|
+ priv->have_rekey_data = true;
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ mutex_unlock(&priv->mutex);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct wowlan_key_data {
|
|
|
|
+ struct iwl_rxon_context *ctx;
|
|
|
|
+ struct iwlagn_wowlan_rsc_tsc_params_cmd *rsc_tsc;
|
|
|
|
+ struct iwlagn_wowlan_tkip_params_cmd *tkip;
|
|
|
|
+ const u8 *bssid;
|
|
|
|
+ bool error, use_rsc_tsc, use_tkip;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static void iwlagn_convert_p1k(u16 *p1k, __le16 *out)
|
|
|
|
+{
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < IWLAGN_P1K_SIZE; i++)
|
|
|
|
+ out[i] = cpu_to_le16(p1k[i]);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void iwlagn_wowlan_program_keys(struct ieee80211_hw *hw,
|
|
|
|
+ struct ieee80211_vif *vif,
|
|
|
|
+ struct ieee80211_sta *sta,
|
|
|
|
+ struct ieee80211_key_conf *key,
|
|
|
|
+ void *_data)
|
|
|
|
+{
|
|
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
|
|
+ struct wowlan_key_data *data = _data;
|
|
|
|
+ struct iwl_rxon_context *ctx = data->ctx;
|
|
|
|
+ struct aes_sc *aes_sc, *aes_tx_sc = NULL;
|
|
|
|
+ struct tkip_sc *tkip_sc, *tkip_tx_sc = NULL;
|
|
|
|
+ struct iwlagn_p1k_cache *rx_p1ks;
|
|
|
|
+ u8 *rx_mic_key;
|
|
|
|
+ struct ieee80211_key_seq seq;
|
|
|
|
+ u32 cur_rx_iv32 = 0;
|
|
|
|
+ u16 p1k[IWLAGN_P1K_SIZE];
|
|
|
|
+ int ret, i;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&priv->mutex);
|
|
|
|
+
|
|
|
|
+ if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 ||
|
|
|
|
+ key->cipher == WLAN_CIPHER_SUITE_WEP104) &&
|
|
|
|
+ !sta && !ctx->key_mapping_keys)
|
|
|
|
+ ret = iwl_set_default_wep_key(priv, ctx, key);
|
|
|
|
+ else
|
|
|
|
+ ret = iwl_set_dynamic_key(priv, ctx, key, sta);
|
|
|
|
+
|
|
|
|
+ if (ret) {
|
|
|
|
+ IWL_ERR(priv, "Error setting key during suspend!\n");
|
|
|
|
+ data->error = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (key->cipher) {
|
|
|
|
+ case WLAN_CIPHER_SUITE_TKIP:
|
|
|
|
+ if (sta) {
|
|
|
|
+ tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
|
|
|
|
+ tkip_tx_sc = &data->rsc_tsc->all_tsc_rsc.tkip.tsc;
|
|
|
|
+
|
|
|
|
+ rx_p1ks = data->tkip->rx_uni;
|
|
|
|
+
|
|
|
|
+ ieee80211_get_key_tx_seq(key, &seq);
|
|
|
|
+ tkip_tx_sc->iv16 = cpu_to_le16(seq.tkip.iv16);
|
|
|
|
+ tkip_tx_sc->iv32 = cpu_to_le32(seq.tkip.iv32);
|
|
|
|
+
|
|
|
|
+ ieee80211_get_tkip_p1k_iv(key, seq.tkip.iv32, p1k);
|
|
|
|
+ iwlagn_convert_p1k(p1k, data->tkip->tx.p1k);
|
|
|
|
+
|
|
|
|
+ memcpy(data->tkip->mic_keys.tx,
|
|
|
|
+ &key->key[NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY],
|
|
|
|
+ IWLAGN_MIC_KEY_SIZE);
|
|
|
|
+
|
|
|
|
+ rx_mic_key = data->tkip->mic_keys.rx_unicast;
|
|
|
|
+ } else {
|
|
|
|
+ tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.multicast_rsc;
|
|
|
|
+ rx_p1ks = data->tkip->rx_multi;
|
|
|
|
+ rx_mic_key = data->tkip->mic_keys.rx_mcast;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For non-QoS this relies on the fact that both the uCode and
|
|
|
|
+ * mac80211 use TID 0 (as they need to to avoid replay attacks)
|
|
|
|
+ * for checking the IV in the frames.
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < IWLAGN_NUM_RSC; i++) {
|
|
|
|
+ ieee80211_get_key_rx_seq(key, i, &seq);
|
|
|
|
+ tkip_sc[i].iv16 = cpu_to_le16(seq.tkip.iv16);
|
|
|
|
+ tkip_sc[i].iv32 = cpu_to_le32(seq.tkip.iv32);
|
|
|
|
+ /* wrapping isn't allowed, AP must rekey */
|
|
|
|
+ if (seq.tkip.iv32 > cur_rx_iv32)
|
|
|
|
+ cur_rx_iv32 = seq.tkip.iv32;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ieee80211_get_tkip_rx_p1k(key, data->bssid, cur_rx_iv32, p1k);
|
|
|
|
+ iwlagn_convert_p1k(p1k, rx_p1ks[0].p1k);
|
|
|
|
+ ieee80211_get_tkip_rx_p1k(key, data->bssid,
|
|
|
|
+ cur_rx_iv32 + 1, p1k);
|
|
|
|
+ iwlagn_convert_p1k(p1k, rx_p1ks[1].p1k);
|
|
|
|
+
|
|
|
|
+ memcpy(rx_mic_key,
|
|
|
|
+ &key->key[NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY],
|
|
|
|
+ IWLAGN_MIC_KEY_SIZE);
|
|
|
|
+
|
|
|
|
+ data->use_tkip = true;
|
|
|
|
+ data->use_rsc_tsc = true;
|
|
|
|
+ break;
|
|
|
|
+ case WLAN_CIPHER_SUITE_CCMP:
|
|
|
|
+ if (sta) {
|
|
|
|
+ u8 *pn = seq.ccmp.pn;
|
|
|
|
+
|
|
|
|
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.unicast_rsc;
|
|
|
|
+ aes_tx_sc = &data->rsc_tsc->all_tsc_rsc.aes.tsc;
|
|
|
|
+
|
|
|
|
+ ieee80211_get_key_tx_seq(key, &seq);
|
|
|
|
+ aes_tx_sc->pn = cpu_to_le64(
|
|
|
|
+ (u64)pn[5] |
|
|
|
|
+ ((u64)pn[4] << 8) |
|
|
|
|
+ ((u64)pn[3] << 16) |
|
|
|
|
+ ((u64)pn[2] << 24) |
|
|
|
|
+ ((u64)pn[1] << 32) |
|
|
|
|
+ ((u64)pn[0] << 40));
|
|
|
|
+ } else
|
|
|
|
+ aes_sc = data->rsc_tsc->all_tsc_rsc.aes.multicast_rsc;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For non-QoS this relies on the fact that both the uCode and
|
|
|
|
+ * mac80211 use TID 0 for checking the IV in the frames.
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < IWLAGN_NUM_RSC; i++) {
|
|
|
|
+ u8 *pn = seq.ccmp.pn;
|
|
|
|
+
|
|
|
|
+ ieee80211_get_key_rx_seq(key, i, &seq);
|
|
|
|
+ aes_sc->pn = cpu_to_le64(
|
|
|
|
+ (u64)pn[5] |
|
|
|
|
+ ((u64)pn[4] << 8) |
|
|
|
|
+ ((u64)pn[3] << 16) |
|
|
|
|
+ ((u64)pn[2] << 24) |
|
|
|
|
+ ((u64)pn[1] << 32) |
|
|
|
|
+ ((u64)pn[0] << 40));
|
|
|
|
+ }
|
|
|
|
+ data->use_rsc_tsc = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&priv->mutex);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
|
|
|
|
+ struct cfg80211_wowlan *wowlan)
|
|
|
|
+{
|
|
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
|
|
+ struct iwlagn_wowlan_wakeup_filter_cmd wakeup_filter_cmd;
|
|
|
|
+ struct iwl_rxon_cmd rxon;
|
|
|
|
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
|
|
|
|
+ struct iwlagn_wowlan_kek_kck_material_cmd kek_kck_cmd;
|
|
|
|
+ struct iwlagn_wowlan_tkip_params_cmd tkip_cmd = {};
|
|
|
|
+ struct wowlan_key_data key_data = {
|
|
|
|
+ .ctx = ctx,
|
|
|
|
+ .bssid = ctx->active.bssid_addr,
|
|
|
|
+ .use_rsc_tsc = false,
|
|
|
|
+ .tkip = &tkip_cmd,
|
|
|
|
+ .use_tkip = false,
|
|
|
|
+ };
|
|
|
|
+ int ret, i;
|
|
|
|
+ u16 seq;
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(!wowlan))
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&priv->mutex);
|
|
|
|
+
|
|
|
|
+ /* Don't attempt WoWLAN when not associated, tear down instead. */
|
|
|
|
+ if (!ctx->vif || ctx->vif->type != NL80211_IFTYPE_STATION ||
|
|
|
|
+ !iwl_is_associated_ctx(ctx)) {
|
|
|
|
+ ret = 1;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
|
|
|
|
+ if (!key_data.rsc_tsc) {
|
|
|
|
+ ret = -ENOMEM;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memset(&wakeup_filter_cmd, 0, sizeof(wakeup_filter_cmd));
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * We know the last used seqno, and the uCode expects to know that
|
|
|
|
+ * one, it will increment before TX.
|
|
|
|
+ */
|
|
|
|
+ seq = le16_to_cpu(priv->last_seq_ctl) & IEEE80211_SCTL_SEQ;
|
|
|
|
+ wakeup_filter_cmd.non_qos_seq = cpu_to_le16(seq);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * For QoS counters, we store the one to use next, so subtract 0x10
|
|
|
|
+ * since the uCode will add 0x10 before using the value.
|
|
|
|
+ */
|
|
|
|
+ for (i = 0; i < 8; i++) {
|
|
|
|
+ seq = priv->stations[IWL_AP_ID].tid[i].seq_number;
|
|
|
|
+ seq -= 0x10;
|
|
|
|
+ wakeup_filter_cmd.qos_seq[i] = cpu_to_le16(seq);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (wowlan->disconnect)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
|
|
|
|
+ IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE);
|
|
|
|
+ if (wowlan->magic_pkt)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET);
|
|
|
|
+ if (wowlan->gtk_rekey_failure)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
|
|
|
|
+ if (wowlan->eap_identity_req)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ);
|
|
|
|
+ if (wowlan->four_way_handshake)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
|
|
|
|
+ if (wowlan->rfkill_release)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_RFKILL);
|
|
|
|
+ if (wowlan->n_patterns)
|
|
|
|
+ wakeup_filter_cmd.enabled |=
|
|
|
|
+ cpu_to_le32(IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH);
|
|
|
|
+
|
|
|
|
+ iwl_scan_cancel_timeout(priv, 200);
|
|
|
|
+
|
|
|
|
+ memcpy(&rxon, &ctx->active, sizeof(rxon));
|
|
|
|
+
|
|
|
|
+ trans_stop_device(&priv->trans);
|
|
|
|
+
|
|
|
|
+ priv->wowlan = true;
|
|
|
|
+
|
|
|
|
+ ret = iwlagn_load_ucode_wait_alive(priv, &priv->ucode_wowlan,
|
|
|
|
+ IWL_UCODE_WOWLAN);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ /* now configure WoWLAN ucode */
|
|
|
|
+ ret = iwl_alive_start(priv);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ memcpy(&ctx->staging, &rxon, sizeof(rxon));
|
|
|
|
+ ret = iwlagn_commit_rxon(priv, ctx);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ ret = iwl_power_update_mode(priv, true);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ if (!iwlagn_mod_params.sw_crypto) {
|
|
|
|
+ /* mark all keys clear */
|
|
|
|
+ priv->ucode_key_table = 0;
|
|
|
|
+ ctx->key_mapping_keys = 0;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * This needs to be unlocked due to lock ordering
|
|
|
|
+ * constraints. Since we're in the suspend path
|
|
|
|
+ * that isn't really a problem though.
|
|
|
|
+ */
|
|
|
|
+ mutex_unlock(&priv->mutex);
|
|
|
|
+ ieee80211_iter_keys(priv->hw, ctx->vif,
|
|
|
|
+ iwlagn_wowlan_program_keys,
|
|
|
|
+ &key_data);
|
|
|
|
+ mutex_lock(&priv->mutex);
|
|
|
|
+ if (key_data.error) {
|
|
|
|
+ ret = -EIO;
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (key_data.use_rsc_tsc) {
|
|
|
|
+ struct iwl_host_cmd rsc_tsc_cmd = {
|
|
|
|
+ .id = REPLY_WOWLAN_TSC_RSC_PARAMS,
|
|
|
|
+ .flags = CMD_SYNC,
|
|
|
|
+ .data[0] = key_data.rsc_tsc,
|
|
|
|
+ .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
|
|
|
|
+ .len[0] = sizeof(*key_data.rsc_tsc),
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ ret = trans_send_cmd(&priv->trans, &rsc_tsc_cmd);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (key_data.use_tkip) {
|
|
|
|
+ ret = trans_send_cmd_pdu(&priv->trans,
|
|
|
|
+ REPLY_WOWLAN_TKIP_PARAMS,
|
|
|
|
+ CMD_SYNC, sizeof(tkip_cmd),
|
|
|
|
+ &tkip_cmd);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (priv->have_rekey_data) {
|
|
|
|
+ memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
|
|
|
|
+ memcpy(kek_kck_cmd.kck, priv->kck, NL80211_KCK_LEN);
|
|
|
|
+ kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
|
|
|
|
+ memcpy(kek_kck_cmd.kek, priv->kek, NL80211_KEK_LEN);
|
|
|
|
+ kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
|
|
|
|
+ kek_kck_cmd.replay_ctr = priv->replay_ctr;
|
|
|
|
+
|
|
|
|
+ ret = trans_send_cmd_pdu(&priv->trans,
|
|
|
|
+ REPLY_WOWLAN_KEK_KCK_MATERIAL,
|
|
|
|
+ CMD_SYNC, sizeof(kek_kck_cmd),
|
|
|
|
+ &kek_kck_cmd);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = trans_send_cmd_pdu(&priv->trans, REPLY_WOWLAN_WAKEUP_FILTER,
|
|
|
|
+ CMD_SYNC, sizeof(wakeup_filter_cmd),
|
|
|
|
+ &wakeup_filter_cmd);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ ret = iwlagn_send_patterns(priv, wowlan);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto error;
|
|
|
|
+
|
|
|
|
+ device_set_wakeup_enable(priv->bus->dev, true);
|
|
|
|
+
|
|
|
|
+ /* Now let the ucode operate on its own */
|
|
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_SET,
|
|
|
|
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
|
|
|
|
+
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ error:
|
|
|
|
+ priv->wowlan = false;
|
|
|
|
+ iwlagn_prepare_restart(priv);
|
|
|
|
+ ieee80211_restart_hw(priv->hw);
|
|
|
|
+ out:
|
|
|
|
+ mutex_unlock(&priv->mutex);
|
|
|
|
+ kfree(key_data.rsc_tsc);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int iwlagn_mac_resume(struct ieee80211_hw *hw)
|
|
|
|
+{
|
|
|
|
+ struct iwl_priv *priv = hw->priv;
|
|
|
|
+ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS];
|
|
|
|
+ struct ieee80211_vif *vif;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ u32 base, status = 0xffffffff;
|
|
|
|
+ int ret = -EIO;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&priv->mutex);
|
|
|
|
+
|
|
|
|
+ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR,
|
|
|
|
+ CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
|
|
|
|
+
|
|
|
|
+ base = priv->device_pointers.error_event_table;
|
|
|
|
+ if (iwlagn_hw_valid_rtc_data_addr(base)) {
|
|
|
|
+ spin_lock_irqsave(&priv->reg_lock, flags);
|
|
|
|
+ ret = iwl_grab_nic_access_silent(priv);
|
|
|
|
+ if (ret == 0) {
|
|
|
|
+ iwl_write32(priv, HBUS_TARG_MEM_RADDR, base);
|
|
|
|
+ status = iwl_read32(priv, HBUS_TARG_MEM_RDAT);
|
|
|
|
+ iwl_release_nic_access(priv);
|
|
|
|
+ }
|
|
|
|
+ spin_unlock_irqrestore(&priv->reg_lock, flags);
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_IWLWIFI_DEBUGFS
|
|
|
|
+ if (ret == 0) {
|
|
|
|
+ if (!priv->wowlan_sram)
|
|
|
|
+ priv->wowlan_sram =
|
|
|
|
+ kzalloc(priv->ucode_wowlan.data.len,
|
|
|
|
+ GFP_KERNEL);
|
|
|
|
+
|
|
|
|
+ if (priv->wowlan_sram)
|
|
|
|
+ _iwl_read_targ_mem_words(
|
|
|
|
+ priv, 0x800000, priv->wowlan_sram,
|
|
|
|
+ priv->ucode_wowlan.data.len / 4);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* we'll clear ctx->vif during iwlagn_prepare_restart() */
|
|
|
|
+ vif = ctx->vif;
|
|
|
|
+
|
|
|
|
+ priv->wowlan = false;
|
|
|
|
+
|
|
|
|
+ device_set_wakeup_enable(priv->bus->dev, false);
|
|
|
|
+
|
|
|
|
+ iwlagn_prepare_restart(priv);
|
|
|
|
+
|
|
|
|
+ memset((void *)&ctx->active, 0, sizeof(ctx->active));
|
|
|
|
+ iwl_connection_init_rx_config(priv, ctx);
|
|
|
|
+ iwlagn_set_rxon_chain(priv, ctx);
|
|
|
|
+
|
|
|
|
+ mutex_unlock(&priv->mutex);
|
|
|
|
+
|
|
|
|
+ ieee80211_resume_disconnect(vif);
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|
static void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
|
|
{
|
|
{
|
|
struct iwl_priv *priv = hw->priv;
|
|
struct iwl_priv *priv = hw->priv;
|
|
@@ -2926,6 +3429,9 @@ static void iwl_uninit_drv(struct iwl_priv *priv)
|
|
iwl_free_channel_map(priv);
|
|
iwl_free_channel_map(priv);
|
|
kfree(priv->scan_cmd);
|
|
kfree(priv->scan_cmd);
|
|
kfree(priv->beacon_cmd);
|
|
kfree(priv->beacon_cmd);
|
|
|
|
+#ifdef CONFIG_IWLWIFI_DEBUGFS
|
|
|
|
+ kfree(priv->wowlan_sram);
|
|
|
|
+#endif
|
|
}
|
|
}
|
|
|
|
|
|
static void iwl_mac_rssi_callback(struct ieee80211_hw *hw,
|
|
static void iwl_mac_rssi_callback(struct ieee80211_hw *hw,
|
|
@@ -2955,6 +3461,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
|
|
.tx = iwlagn_mac_tx,
|
|
.tx = iwlagn_mac_tx,
|
|
.start = iwlagn_mac_start,
|
|
.start = iwlagn_mac_start,
|
|
.stop = iwlagn_mac_stop,
|
|
.stop = iwlagn_mac_stop,
|
|
|
|
+ .suspend = iwlagn_mac_suspend,
|
|
|
|
+ .resume = iwlagn_mac_resume,
|
|
.add_interface = iwl_mac_add_interface,
|
|
.add_interface = iwl_mac_add_interface,
|
|
.remove_interface = iwl_mac_remove_interface,
|
|
.remove_interface = iwl_mac_remove_interface,
|
|
.change_interface = iwl_mac_change_interface,
|
|
.change_interface = iwl_mac_change_interface,
|
|
@@ -2962,6 +3470,7 @@ struct ieee80211_ops iwlagn_hw_ops = {
|
|
.configure_filter = iwlagn_configure_filter,
|
|
.configure_filter = iwlagn_configure_filter,
|
|
.set_key = iwlagn_mac_set_key,
|
|
.set_key = iwlagn_mac_set_key,
|
|
.update_tkip_key = iwlagn_mac_update_tkip_key,
|
|
.update_tkip_key = iwlagn_mac_update_tkip_key,
|
|
|
|
+ .set_rekey_data = iwlagn_mac_set_rekey_data,
|
|
.conf_tx = iwl_mac_conf_tx,
|
|
.conf_tx = iwl_mac_conf_tx,
|
|
.bss_info_changed = iwlagn_bss_info_changed,
|
|
.bss_info_changed = iwlagn_bss_info_changed,
|
|
.ampdu_action = iwlagn_mac_ampdu_action,
|
|
.ampdu_action = iwlagn_mac_ampdu_action,
|