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

Commit 5171f7a0 authored by John W. Linville's avatar John W. Linville
Browse files
Conflicts:
	drivers/net/wireless/iwlwifi/mvm/mac80211.c
parents c88d0dc1 36eed56a
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -3897,6 +3897,24 @@ struct iwlagn_wowlan_kek_kck_material_cmd {
	__le64	replay_ctr;
} __packed;

#define RF_KILL_INDICATOR_FOR_WOWLAN	0x87

/*
 * REPLY_WOWLAN_GET_STATUS = 0xe5
 */
struct iwlagn_wowlan_status {
	__le64 replay_ctr;
	__le32 rekey_status;
	__le32 wakeup_reason;
	u8 pattern_number;
	u8 reserved1;
	__le16 qos_seq_ctr[8];
	__le16 non_qos_seq_ctr;
	__le16 reserved2;
	union iwlagn_all_tsc_rsc tsc_rsc;
	__le16 reserved3;
} __packed;

/*
 * REPLY_WIPAN_PARAMS = 0xb2 (Commands and Notification)
 */
+132 −29
Original line number Diff line number Diff line
@@ -441,52 +441,154 @@ static int iwlagn_mac_suspend(struct ieee80211_hw *hw,
	return ret;
}

struct iwl_resume_data {
	struct iwl_priv *priv;
	struct iwlagn_wowlan_status *cmd;
	bool valid;
};

static bool iwl_resume_status_fn(struct iwl_notif_wait_data *notif_wait,
				 struct iwl_rx_packet *pkt, void *data)
{
	struct iwl_resume_data *resume_data = data;
	struct iwl_priv *priv = resume_data->priv;
	u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;

	if (len - 4 != sizeof(*resume_data->cmd)) {
		IWL_ERR(priv, "rx wrong size data\n");
		return true;
	}
	memcpy(resume_data->cmd, pkt->data, sizeof(*resume_data->cmd));
	resume_data->valid = true;

	return true;
}

static int iwlagn_mac_resume(struct ieee80211_hw *hw)
{
	struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw);
	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;
	u32 base;
	int ret;
	enum iwl_d3_status d3_status;
	struct error_table_start {
		/* cf. struct iwl_error_event_table */
		u32 valid;
		u32 error_id;
	} err_info;
	struct iwl_notification_wait status_wait;
	static const u8 status_cmd[] = {
		REPLY_WOWLAN_GET_STATUS,
	};
	struct iwlagn_wowlan_status status_data = {};
	struct iwl_resume_data resume_data = {
		.priv = priv,
		.cmd = &status_data,
		.valid = false,
	};
	struct cfg80211_wowlan_wakeup wakeup = {
		.pattern_idx = -1,
	};
#ifdef CONFIG_IWLWIFI_DEBUGFS
	const struct fw_img *img;
#endif

	IWL_DEBUG_MAC80211(priv, "enter\n");
	mutex_lock(&priv->mutex);

	iwl_write32(priv->trans, CSR_UCODE_DRV_GP1_CLR,
			  CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE);
	/* we'll clear ctx->vif during iwlagn_prepare_restart() */
	vif = ctx->vif;

	ret = iwl_trans_d3_resume(priv->trans, &d3_status);
	if (ret)
		goto out_unlock;

	if (d3_status != IWL_D3_STATUS_ALIVE) {
		IWL_INFO(priv, "Device was reset during suspend\n");
		goto out_unlock;
	}

	base = priv->device_pointers.error_event_table;
	if (iwlagn_hw_valid_rtc_data_addr(base)) {
		if (iwl_trans_grab_nic_access(priv->trans, true, &flags)) {
			iwl_write32(priv->trans, HBUS_TARG_MEM_RADDR, base);
			status = iwl_read32(priv->trans, HBUS_TARG_MEM_RDAT);
			iwl_trans_release_nic_access(priv->trans, &flags);
			ret = 0;
	if (!iwlagn_hw_valid_rtc_data_addr(base)) {
		IWL_WARN(priv, "Invalid error table during resume!\n");
		goto out_unlock;
	}

#ifdef CONFIG_IWLWIFI_DEBUGFS
		if (ret == 0) {
			const struct fw_img *img;
	iwl_trans_read_mem_bytes(priv->trans, base,
				 &err_info, sizeof(err_info));

			img = &(priv->fw->img[IWL_UCODE_WOWLAN]);
			if (!priv->wowlan_sram) {
	if (err_info.valid) {
		IWL_INFO(priv, "error table is valid (%d, 0x%x)\n",
			 err_info.valid, err_info.error_id);
		if (err_info.error_id == RF_KILL_INDICATOR_FOR_WOWLAN) {
			wakeup.rfkill_release = true;
			ieee80211_report_wowlan_wakeup(vif, &wakeup,
						       GFP_KERNEL);
		}
		goto out_unlock;
	}

#ifdef CONFIG_IWLWIFI_DEBUGFS
	img = &priv->fw->img[IWL_UCODE_WOWLAN];
	if (!priv->wowlan_sram)
		priv->wowlan_sram =
			kzalloc(img->sec[IWL_UCODE_SECTION_DATA].len,
				GFP_KERNEL);
			}

	if (priv->wowlan_sram)
				iwl_trans_read_mem(
				      priv->trans, 0x800000,
		iwl_trans_read_mem(priv->trans, 0x800000,
				   priv->wowlan_sram,
				   img->sec[IWL_UCODE_SECTION_DATA].len / 4);
		}
#endif

	/*
	 * This is very strange. The GET_STATUS command is sent but the device
	 * doesn't reply properly, it seems it doesn't close the RBD so one is
	 * always left open ... As a result, we need to send another command
	 * and have to reset the driver afterwards. As we need to switch to
	 * runtime firmware again that'll happen.
	 */

	iwl_init_notification_wait(&priv->notif_wait, &status_wait, status_cmd,
				   ARRAY_SIZE(status_cmd), iwl_resume_status_fn,
				   &resume_data);

	iwl_dvm_send_cmd_pdu(priv, REPLY_WOWLAN_GET_STATUS, CMD_ASYNC, 0, NULL);
	iwl_dvm_send_cmd_pdu(priv, REPLY_ECHO, CMD_ASYNC, 0, NULL);
	/* an RBD is left open in the firmware now! */

	ret = iwl_wait_notification(&priv->notif_wait, &status_wait, HZ/5);
	if (ret)
		goto out_unlock;

	if (resume_data.valid && priv->contexts[IWL_RXON_CTX_BSS].vif) {
		u32 reasons = le32_to_cpu(status_data.wakeup_reason);
		struct cfg80211_wowlan_wakeup *wakeup_report;

		IWL_INFO(priv, "WoWLAN wakeup reason(s): 0x%.8x\n", reasons);

		if (reasons) {
			if (reasons & IWLAGN_WOWLAN_WAKEUP_MAGIC_PACKET)
				wakeup.magic_pkt = true;
			if (reasons & IWLAGN_WOWLAN_WAKEUP_PATTERN_MATCH)
				wakeup.pattern_idx = status_data.pattern_number;
			if (reasons & (IWLAGN_WOWLAN_WAKEUP_BEACON_MISS |
				       IWLAGN_WOWLAN_WAKEUP_LINK_CHANGE))
				wakeup.disconnect = true;
			if (reasons & IWLAGN_WOWLAN_WAKEUP_GTK_REKEY_FAIL)
				wakeup.gtk_rekey_failure = true;
			if (reasons & IWLAGN_WOWLAN_WAKEUP_EAP_IDENT_REQ)
				wakeup.eap_identity_req = true;
			if (reasons & IWLAGN_WOWLAN_WAKEUP_4WAY_HANDSHAKE)
				wakeup.four_way_handshake = true;
			wakeup_report = &wakeup;
		} else {
			wakeup_report = NULL;
		}

	/* we'll clear ctx->vif during iwlagn_prepare_restart() */
	vif = ctx->vif;
		ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
	}

	priv->wowlan = false;

@@ -496,6 +598,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw)
	iwl_connection_init_rx_config(priv, ctx);
	iwlagn_set_rxon_chain(priv, ctx);

 out_unlock:
	mutex_unlock(&priv->mutex);
	IWL_DEBUG_MAC80211(priv, "leave\n");

+1 −1
Original line number Diff line number Diff line
@@ -790,7 +790,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,

	memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));

	ieee80211_rx(priv->hw, skb);
	ieee80211_rx_ni(priv->hw, skb);
}

static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in)
+2 −3
Original line number Diff line number Diff line
@@ -1545,10 +1545,9 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
				bss_conf->bssid);
	}

	if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_ADHOC &&
	    priv->beacon_ctx) {
	if (changes & BSS_CHANGED_BEACON && priv->beacon_ctx == ctx) {
		if (iwlagn_update_beacon(priv, vif))
			IWL_ERR(priv, "Error sending IBSS beacon\n");
			IWL_ERR(priv, "Error updating beacon\n");
	}

	mutex_unlock(&priv->mutex);
+2 −2
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@ static int iwl_process_add_sta_resp(struct iwl_priv *priv,
	IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n",
		       sta_id);

	spin_lock(&priv->sta_lock);
	spin_lock_bh(&priv->sta_lock);

	switch (add_sta_resp->status) {
	case ADD_STA_SUCCESS_MSK:
@@ -119,7 +119,7 @@ static int iwl_process_add_sta_resp(struct iwl_priv *priv,
		       priv->stations[sta_id].sta.mode ==
		       STA_CONTROL_MODIFY_MSK ? "Modified" : "Added",
		       addsta->sta.addr);
	spin_unlock(&priv->sta_lock);
	spin_unlock_bh(&priv->sta_lock);

	return ret;
}
Loading