Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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 ieee80211_tx_info *info;
	struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0];
	struct ieee80211_hdr *hdr;
	struct iwl_tx_info *txb;
	u32 status = le16_to_cpu(tx_resp->status.status);
	int tid;
@@ -426,6 +427,11 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb)
		IWLAGN_TX_RES_RA_POS;

	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) {
		const u32 scd_ssn = iwlagn_get_scd_ssn(tx_resp);
		struct iwl_ht_agg *agg;
+3 −0
Original line number Diff line number Diff line
@@ -855,6 +855,9 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
				iwl_wake_any_queue(priv, ctx);
			}
			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);
+14 −7
Original line number Diff line number Diff line
@@ -513,6 +513,12 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,
		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);
		if (ret) {
			priv->ucode_type = old_type;
@@ -521,6 +527,7 @@ int iwlagn_load_ucode_wait_alive(struct iwl_priv *priv,

		/* delay a bit to give rfkill time to run */
		msleep(5);
	}

	ret = iwlagn_alive_notify(priv);
	if (ret) {
+513 −4
Original line number Diff line number Diff line
@@ -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_init);
	iwl_free_fw_img(priv, &priv->ucode_wowlan);
}

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 {
	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;

@@ -902,6 +904,14 @@ static int iwlagn_load_firmware(struct iwl_priv *priv,
				goto invalid_tlv_len;
			priv->enhance_sensitivity_table = true;
			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:
			if (tlv_len != sizeof(u32))
				goto invalid_tlv_len;
@@ -1096,6 +1106,18 @@ static void iwl_ucode_callback(const struct firmware *ucode_raw, void *context)
			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 */

	/*
@@ -1698,7 +1720,7 @@ int iwl_alive_start(struct iwl_priv *priv)
	/* Configure Tx antenna selection based on H/W config */
	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 *)&ctx->active;
		/* apply any changes in staging */
@@ -1713,7 +1735,10 @@ int iwl_alive_start(struct iwl_priv *priv)
		iwlagn_set_rxon_chain(priv, ctx);
	}

	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);

@@ -2153,6 +2178,23 @@ static int iwl_mac_setup_register(struct iwl_priv *priv,
			    WIPHY_FLAG_DISABLE_BEACON_HINTS |
			    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)
		hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;
	else
@@ -2237,6 +2279,467 @@ static void iwlagn_mac_stop(struct ieee80211_hw *hw)
	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)
{
	struct iwl_priv *priv = hw->priv;
@@ -2926,6 +3429,9 @@ static void iwl_uninit_drv(struct iwl_priv *priv)
	iwl_free_channel_map(priv);
	kfree(priv->scan_cmd);
	kfree(priv->beacon_cmd);
#ifdef CONFIG_IWLWIFI_DEBUGFS
	kfree(priv->wowlan_sram);
#endif
}

static void iwl_mac_rssi_callback(struct ieee80211_hw *hw,
@@ -2955,6 +3461,8 @@ struct ieee80211_ops iwlagn_hw_ops = {
	.tx = iwlagn_mac_tx,
	.start = iwlagn_mac_start,
	.stop = iwlagn_mac_stop,
	.suspend = iwlagn_mac_suspend,
	.resume = iwlagn_mac_resume,
	.add_interface = iwl_mac_add_interface,
	.remove_interface = iwl_mac_remove_interface,
	.change_interface = iwl_mac_change_interface,
@@ -2962,6 +3470,7 @@ struct ieee80211_ops iwlagn_hw_ops = {
	.configure_filter = iwlagn_configure_filter,
	.set_key = iwlagn_mac_set_key,
	.update_tkip_key = iwlagn_mac_update_tkip_key,
	.set_rekey_data = iwlagn_mac_set_rekey_data,
	.conf_tx = iwl_mac_conf_tx,
	.bss_info_changed = iwlagn_bss_info_changed,
	.ampdu_action = iwlagn_mac_ampdu_action,
+128 −0
Original line number Diff line number Diff line
@@ -188,6 +188,13 @@ enum {
	REPLY_WIPAN_NOA_NOTIFICATION = 0xbc,
	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
};

@@ -3764,6 +3771,127 @@ struct iwl_bt_coex_prot_env_cmd {
	u8 reserved[2];
} __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)
 * Union of all expected notifications/responses:
Loading