static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
				    struct nlattr *attr,
				    struct cfg80211_wowlan *trig)
{
	struct nlattr *tb[NUM_NL80211_WOWLAN_TCP];
	struct cfg80211_wowlan_tcp *cfg;
	struct nl80211_wowlan_tcp_data_token *tok = NULL;
	struct nl80211_wowlan_tcp_data_seq *seq = NULL;
	u32 size;
	u32 data_size, wake_size, tokens_size = 0, wake_mask_size;
	int err, port;

	if (!rdev->wiphy.wowlan->tcp)
		return -EINVAL;

	err = nla_parse_nested_deprecated(tb, MAX_NL80211_WOWLAN_TCP, attr,
					  nl80211_wowlan_tcp_policy, NULL);
	if (err)
		return err;

	if (!tb[NL80211_WOWLAN_TCP_SRC_IPV4] ||
	    !tb[NL80211_WOWLAN_TCP_DST_IPV4] ||
	    !tb[NL80211_WOWLAN_TCP_DST_MAC] ||
	    !tb[NL80211_WOWLAN_TCP_DST_PORT] ||
	    !tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD] ||
	    !tb[NL80211_WOWLAN_TCP_DATA_INTERVAL] ||
	    !tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD] ||
	    !tb[NL80211_WOWLAN_TCP_WAKE_MASK])
		return -EINVAL;

	data_size = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]);
	if (data_size > rdev->wiphy.wowlan->tcp->data_payload_max)
		return -EINVAL;

	if (nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) >
			rdev->wiphy.wowlan->tcp->data_interval_max ||
	    nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]) == 0)
		return -EINVAL;

	wake_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]);
	if (wake_size > rdev->wiphy.wowlan->tcp->wake_payload_max)
		return -EINVAL;

	wake_mask_size = nla_len(tb[NL80211_WOWLAN_TCP_WAKE_MASK]);
	if (wake_mask_size != DIV_ROUND_UP(wake_size, 8))
		return -EINVAL;

	if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]) {
		u32 tokln = nla_len(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);

		tok = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN]);
		tokens_size = tokln - sizeof(*tok);

		if (!tok->len || tokens_size % tok->len)
			return -EINVAL;
		if (!rdev->wiphy.wowlan->tcp->tok)
			return -EINVAL;
		if (tok->len > rdev->wiphy.wowlan->tcp->tok->max_len)
			return -EINVAL;
		if (tok->len < rdev->wiphy.wowlan->tcp->tok->min_len)
			return -EINVAL;
		if (tokens_size > rdev->wiphy.wowlan->tcp->tok->bufsize)
			return -EINVAL;
		if (tok->offset + tok->len > data_size)
			return -EINVAL;
	}

	if (tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]) {
		seq = nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ]);
		if (!rdev->wiphy.wowlan->tcp->seq)
			return -EINVAL;
		if (seq->len == 0 || seq->len > 4)
			return -EINVAL;
		if (seq->len + seq->offset > data_size)
			return -EINVAL;
	}

	size = sizeof(*cfg);
	size += data_size;
	size += wake_size + wake_mask_size;
	size += tokens_size;

	cfg = kzalloc(size, GFP_KERNEL);
	if (!cfg)
		return -ENOMEM;
	cfg->src = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
	cfg->dst = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
	memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
	       ETH_ALEN);
	if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
		port = nla_get_u16(tb[NL80211_WOWLAN_TCP_SRC_PORT]);
	else
		port = 0;
#ifdef CONFIG_INET
	/* allocate a socket and port for it and use it */
	err = __sock_create(wiphy_net(&rdev->wiphy), PF_INET, SOCK_STREAM,
			    IPPROTO_TCP, &cfg->sock, 1);
	if (err) {
		kfree(cfg);
		return err;
	}
	if (inet_csk_get_port(cfg->sock->sk, port)) {
		sock_release(cfg->sock);
		kfree(cfg);
		return -EADDRINUSE;
	}
	cfg->src_port = inet_sk(cfg->sock->sk)->inet_num;
#else
	if (!port) {
		kfree(cfg);
		return -EINVAL;
	}
	cfg->src_port = port;
#endif

	cfg->dst_port = nla_get_u16(tb[NL80211_WOWLAN_TCP_DST_PORT]);
	cfg->payload_len = data_size;
	cfg->payload = (u8 *)cfg + sizeof(*cfg) + tokens_size;
	memcpy((void *)cfg->payload,
	       nla_data(tb[NL80211_WOWLAN_TCP_DATA_PAYLOAD]),
	       data_size);
	if (seq)
		cfg->payload_seq = *seq;
	cfg->data_interval = nla_get_u32(tb[NL80211_WOWLAN_TCP_DATA_INTERVAL]);
	cfg->wake_len = wake_size;
	cfg->wake_data = (u8 *)cfg + sizeof(*cfg) + tokens_size + data_size;
	memcpy((void *)cfg->wake_data,
	       nla_data(tb[NL80211_WOWLAN_TCP_WAKE_PAYLOAD]),
	       wake_size);
	cfg->wake_mask = (u8 *)cfg + sizeof(*cfg) + tokens_size +
			 data_size + wake_size;
	memcpy((void *)cfg->wake_mask,
	       nla_data(tb[NL80211_WOWLAN_TCP_WAKE_MASK]),
	       wake_mask_size);
	if (tok) {
		cfg->tokens_size = tokens_size;
		memcpy(&cfg->payload_tok, tok, sizeof(*tok) + tokens_size);
	}

	trig->tcp = cfg;

	return 0;
}