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

Commit 1a95c8df authored by Eliad Peller's avatar Eliad Peller Committed by Emmanuel Grumbach
Browse files

iwlwifi: mvm: configure seq_num to D0i3



Configure the QoS counters when entering D0i3.

The fw might use them later when performing protocol
offloading (we'll update the the counters back on
d0i3 exit in a following patch).

Non-QoS counter is handled internally in the fw, so
no need to configure it.

Also, add support for a new version of WOWLAN_CONFIG_CMD

Signed-off-by: default avatarEliad Peller <eliad@wizery.com>
Reviewed-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarEmmanuel Grumbach <emmanuel.grumbach@intel.com>
parent c63722cf
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -125,6 +125,14 @@ enum iwl_ucode_tlv_flag {
	IWL_UCODE_TLV_FLAGS_GO_UAPSD		= BIT(30),
};

/**
 * enum iwl_ucode_tlv_api - ucode api
 * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field.
 */
enum iwl_ucode_tlv_api {
	IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID	= BIT(0),
};

/**
 * enum iwl_ucode_tlv_capa - ucode capabilities
 * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o
iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
iwlmvm-y += scan.o time-event.o rs.o
iwlmvm-y += power.o coex.o
iwlmvm-y += led.o tt.o
iwlmvm-y += led.o tt.o offloading.o
iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
iwlmvm-$(CONFIG_PM_SLEEP) += d3.o

+30 −159
Original line number Diff line number Diff line
@@ -376,139 +376,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
	return err;
}

static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
				      struct ieee80211_vif *vif)
{
	union {
		struct iwl_proto_offload_cmd_v1 v1;
		struct iwl_proto_offload_cmd_v2 v2;
		struct iwl_proto_offload_cmd_v3_small v3s;
		struct iwl_proto_offload_cmd_v3_large v3l;
	} cmd = {};
	struct iwl_host_cmd hcmd = {
		.id = PROT_OFFLOAD_CONFIG_CMD,
		.flags = CMD_SYNC,
		.data[0] = &cmd,
		.dataflags[0] = IWL_HCMD_DFL_DUP,
	};
	struct iwl_proto_offload_cmd_common *common;
	u32 enabled = 0, size;
	u32 capa_flags = mvm->fw->ucode_capa.flags;
#if IS_ENABLED(CONFIG_IPV6)
	struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
	int i;

	if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
	    capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
		struct iwl_ns_config *nsc;
		struct iwl_targ_addr *addrs;
		int n_nsc, n_addrs;
		int c;

		if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
			nsc = cmd.v3s.ns_config;
			n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
			addrs = cmd.v3s.targ_addrs;
			n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
		} else {
			nsc = cmd.v3l.ns_config;
			n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
			addrs = cmd.v3l.targ_addrs;
			n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
		}

		if (mvmvif->num_target_ipv6_addrs)
			enabled |= IWL_D3_PROTO_OFFLOAD_NS;

		/*
		 * For each address we have (and that will fit) fill a target
		 * address struct and combine for NS offload structs with the
		 * solicited node addresses.
		 */
		for (i = 0, c = 0;
		     i < mvmvif->num_target_ipv6_addrs &&
		     i < n_addrs && c < n_nsc; i++) {
			struct in6_addr solicited_addr;
			int j;

			addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
						  &solicited_addr);
			for (j = 0; j < c; j++)
				if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
						  &solicited_addr) == 0)
					break;
			if (j == c)
				c++;
			addrs[i].addr = mvmvif->target_ipv6_addrs[i];
			addrs[i].config_num = cpu_to_le32(j);
			nsc[j].dest_ipv6_addr = solicited_addr;
			memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
		}

		if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
			cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
		else
			cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
		if (mvmvif->num_target_ipv6_addrs) {
			enabled |= IWL_D3_PROTO_OFFLOAD_NS;
			memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN);
		}

		BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) !=
			     sizeof(mvmvif->target_ipv6_addrs[0]));

		for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
				    IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++)
			memcpy(cmd.v2.target_ipv6_addr[i],
			       &mvmvif->target_ipv6_addrs[i],
			       sizeof(cmd.v2.target_ipv6_addr[i]));
	} else {
		if (mvmvif->num_target_ipv6_addrs) {
			enabled |= IWL_D3_PROTO_OFFLOAD_NS;
			memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN);
		}

		BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) !=
			     sizeof(mvmvif->target_ipv6_addrs[0]));

		for (i = 0; i < min(mvmvif->num_target_ipv6_addrs,
				    IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++)
			memcpy(cmd.v1.target_ipv6_addr[i],
			       &mvmvif->target_ipv6_addrs[i],
			       sizeof(cmd.v1.target_ipv6_addr[i]));
	}
#endif

	if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
		common = &cmd.v3s.common;
		size = sizeof(cmd.v3s);
	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
		common = &cmd.v3l.common;
		size = sizeof(cmd.v3l);
	} else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
		common = &cmd.v2.common;
		size = sizeof(cmd.v2);
	} else {
		common = &cmd.v1.common;
		size = sizeof(cmd.v1);
	}

	if (vif->bss_conf.arp_addr_cnt) {
		enabled |= IWL_D3_PROTO_OFFLOAD_ARP;
		common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0];
		memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN);
	}

	if (!enabled)
		return 0;

	common->enabled = cpu_to_le32(enabled);

	hcmd.len[0] = size;
	return iwl_mvm_send_cmd(mvm, &hcmd);
}

enum iwl_mvm_tcp_packet_type {
	MVM_TCP_TX_SYN,
	MVM_TCP_RX_SYNACK,
@@ -927,6 +794,20 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
		IWL_ERR(mvm, "failed to set non-QoS seqno\n");
}

static int
iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm,
			       const struct iwl_wowlan_config_cmd_v3 *cmd)
{
	/* start only with the v2 part of the command */
	u16 cmd_len = sizeof(cmd->common);

	if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID)
		cmd_len = sizeof(*cmd);

	return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC,
				    cmd_len, cmd);
}

static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
			     struct cfg80211_wowlan *wowlan,
			     bool test)
@@ -939,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
	struct iwl_mvm_vif *mvmvif;
	struct ieee80211_sta *ap_sta;
	struct iwl_mvm_sta *mvm_ap_sta;
	struct iwl_wowlan_config_cmd wowlan_config_cmd = {};
	struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {};
	struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
	struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
	struct iwl_d3_manager_config d3_cfg_cmd_data = {
@@ -961,7 +842,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
		.tkip = &tkip_cmd,
		.use_tkip = false,
	};
	int ret, i;
	int ret;
	int len __maybe_unused;

	if (!wowlan) {
@@ -1002,49 +883,41 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,

	mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv;

	/* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */
	/* TODO: wowlan_config_cmd.common.wowlan_ba_teardown_tids */

	wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported;
	wowlan_config_cmd.common.is_11n_connection =
					ap_sta->ht_cap.ht_supported;

	/* Query the last used seqno and set it */
	ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
	if (ret < 0)
		goto out_noreset;
	wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret);
	wowlan_config_cmd.common.non_qos_seq = cpu_to_le16(ret);

	/*
	 * For QoS counters, we store the one to use next, so subtract 0x10
	 * since the uCode will add 0x10 *before* using the value while we
	 * increment after using the value (i.e. store the next value to use).
	 */
	for (i = 0; i < IWL_MAX_TID_COUNT; i++) {
		u16 seq = mvm_ap_sta->tid_data[i].seq_number;
		seq -= 0x10;
		wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq);
	}
	iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd.common);

	if (wowlan->disconnect)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS |
				    IWL_WOWLAN_WAKEUP_LINK_CHANGE);
	if (wowlan->magic_pkt)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET);
	if (wowlan->gtk_rekey_failure)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL);
	if (wowlan->eap_identity_req)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ);
	if (wowlan->four_way_handshake)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE);
	if (wowlan->n_patterns)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH);

	if (wowlan->rfkill_release)
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT);

	if (wowlan->tcp) {
@@ -1052,7 +925,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
		 * Set the "link change" (really "link lost") flag as well
		 * since that implies losing the TCP connection.
		 */
		wowlan_config_cmd.wakeup_filter |=
		wowlan_config_cmd.common.wakeup_filter |=
			cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS |
				    IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE |
				    IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET |
@@ -1150,9 +1023,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
		}
	}

	ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION,
				   CMD_SYNC, sizeof(wowlan_config_cmd),
				   &wowlan_config_cmd);
	ret = iwl_mvm_send_wowlan_config_cmd(mvm, &wowlan_config_cmd);
	if (ret)
		goto out;

+7 −1
Original line number Diff line number Diff line
@@ -239,7 +239,7 @@ enum iwl_wowlan_wakeup_filters {
	IWL_WOWLAN_WAKEUP_BCN_FILTERING			= BIT(16),
}; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */

struct iwl_wowlan_config_cmd {
struct iwl_wowlan_config_cmd_v2 {
	__le32 wakeup_filter;
	__le16 non_qos_seq;
	__le16 qos_seq[8];
@@ -247,6 +247,12 @@ struct iwl_wowlan_config_cmd {
	u8 is_11n_connection;
} __packed; /* WOWLAN_CONFIG_API_S_VER_2 */

struct iwl_wowlan_config_cmd_v3 {
	struct iwl_wowlan_config_cmd_v2 common;
	u8 offloading_tid;
	u8 reserved[3];
} __packed; /* WOWLAN_CONFIG_API_S_VER_3 */

/*
 * WOWLAN_TSC_RSC_PARAMS
 */
+4 −1
Original line number Diff line number Diff line
@@ -317,13 +317,13 @@ struct iwl_mvm_vif {

	bool seqno_valid;
	u16 seqno;
#endif

#if IS_ENABLED(CONFIG_IPV6)
	/* IPv6 addresses for WoWLAN */
	struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX];
	int num_target_ipv6_addrs;
#endif
#endif

#ifdef CONFIG_IWLWIFI_DEBUGFS
	struct iwl_mvm *mvm;
@@ -896,6 +896,9 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
{
}
#endif
void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
				struct iwl_wowlan_config_cmd_v2 *cmd);
int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif);

/* D0i3 */
void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type);
Loading